Make animation loop more understandable.

Change-Id: I71215e8f36abe82e591ec35880962628827a4b6f

BUG=10292
TEST=ran the program

Review URL: http://codereview.chromium.org/6019001
diff --git a/src/ply-image.c b/src/ply-image.c
index 58e1dae..3395c92 100644
--- a/src/ply-image.c
+++ b/src/ply-image.c
@@ -489,33 +489,27 @@
   ply_image_free (image);
 }
 
-/*
- * Compute the difference in nanoseconds between two timevals.  Return 0 if the
- * clock is not reliable.
- */
-int64_t difference_nsec(struct timespec x, struct timespec y)
+
+int64_t timespec_to_nsec(struct timespec ts)
 {
-  int64_t z;
-
-  if (bad_clock)
-    {
-      return 0;
-    }
-
-  z = x.tv_sec - y.tv_sec;
-  z *= BILLION;
-  z += x.tv_nsec - y.tv_nsec;
-  return z;
+  int64_t nsec;
+  nsec = ts.tv_sec;
+  nsec *= BILLION;
+  nsec += ts.tv_nsec;
+  return nsec;
 }
 
 
-void gettime_check(struct timespec *ts)
+int64_t gettime_check(void)
 {
-  int r = clock_gettime(CLOCK_MONOTONIC, ts);
+  struct timespec ts;
+  int r = clock_gettime(CLOCK_MONOTONIC, &ts);
   if (r)
     {
       bad_clock = 1;
+      return 0;
     }
+  return timespec_to_nsec(ts);
 }
 
 
@@ -584,9 +578,8 @@
           /*
            * Animate frames.
            */
-          struct timespec then, now;
-          int64_t error_nsec;
-          int64_t interval_nsec;
+          int64_t draw_time_nsec, frame_period_nsec;
+          int64_t sleep_time_nsec, now_nsec, then_nsec;
 
           xoff = strtol (argv[2], &endptr, 10);
           if (endptr == argv[2] || *endptr != '\0')
@@ -603,33 +596,39 @@
            * Begin animation.
            *
            * The first time around, we don't know how long it takes to draw a
-           * frame, so we assume it's instantaneous.  After that, we assume
-           * that the drawing time for the previous cycle is a good estimate
-           * for the next cycle.  Errors may be introduced by different frame
-           * size and complexity (PNG decoding).
+           * frame, so we approximate it as 0.  After that, we assume that the
+           * drawing time for the previous cycle is a good estimate for the
+           * next cycle.  Errors may be introduced by different frame size and
+           * complexity (PNG decoding) and by the CPU load.
            */
-          error_nsec = 0;
-          interval_nsec = BILLION / frame_rate;
-          gettime_check (&now);
+          draw_time_nsec = 0;
+          frame_period_nsec = BILLION / frame_rate;
+          now_nsec = gettime_check ();
 
           for (i = 4; i < argc; i++)
             {
               /*
-               * Before displaying the next frame, sleep for the inter-frame
-               * interval, adjusted by the error in the previous cycle.
-               * Positive error means the last cycle took longer than desired.
+               * Before displaying the next frame, sleep for the right amount
+               * of time so that the achieved period approximates the desired
+               * period.
                */
-              if (interval_nsec - error_nsec > 0)
+              sleep_time_nsec = frame_period_nsec - draw_time_nsec;
+              if (sleep_time_nsec > 0)
                 {
                   struct timespec req;
-                  req.tv_sec = (interval_nsec - error_nsec) / BILLION;
-                  req.tv_nsec = (interval_nsec - error_nsec) % BILLION;
+                  req.tv_sec = sleep_time_nsec / BILLION;
+                  req.tv_nsec = sleep_time_nsec % BILLION;
                   nanosleep (&req, NULL);
                 }
               ply_frame_buffer_show_file_at_xy (buffer, argv[i], xoff, yoff);
-              then = now;
-              gettime_check (&now);
-              error_nsec = difference_nsec (now, then) - interval_nsec;
+              then_nsec = now_nsec;
+              now_nsec = gettime_check ();
+              /*
+               * If the clock is bad (unlikely), assume draw time is
+               * instantaneous as a rough guess.
+               */
+              draw_time_nsec = bad_clock ? 0 :
+                now_nsec - then_nsec - sleep_time_nsec;
             }
         }
     }