1 
2 #include "config.h"
3 #include "mvi.h"
4 
5 #ifndef NO_LZO
6 # include "minilzo/minilzo.h"
7 # define IF_LZO(x) x
8 #else
9 # define IF_LZO(x) ((void)0)
10 #endif
11 
12 /* ======================================================================== */
13 /*  Movie format is little endian on all targets.  All multibyte fields     */
14 /*  get sent LSbyte first.  All bitmaps are ls-bit first.  Packed pixels    */
15 /*  fill LSnibble before MSnibble.                                          */
16 /*                                                                          */
17 /*  FILE HEADER:                                                            */
18 /*      4 bytes     Signature: 0x4A 0x5A 0x6A 0x7A                          */
19 /*      2 bytes     X resolution  (always 0xA0 0x00 (160) for now)          */
20 /*      2 bytes     Y resolution  (always 0xC8 0x00 (200) for now)          */
21 /*                                                                          */
22 /*  Note that the file header can optionally appear after any frame.        */
23 /*  This makes it easy to concatenate two movies together.  The decoder     */
24 /*  should look for a file header when decoding any frame.  It's up to      */
25 /*  the decoder how it handles two movies w/ different resolutions.         */
26 /*                                                                          */
27 /*  FRAME FORMAT:                                                           */
28 /*      4 bytes     Total frame length (incl. header)                       */
29 /*      3 bytes     Frame number                                            */
30 /*      1 byte      Flags                                                   */
31 /*                  Bit 0:  Frame sent (0 == skip, 1 == present)            */
32 /*                          Absent frames assumed to be repeat of prev      */
33 /*                  Bit 1:  Bounding boxes sent (0 == skip, 1 == present)   */
34 /*                          Absent bounding boxes assumed to be repeat.     */
35 /*                  Bit 2:  RLE compressed                                  */
36 /*                  Bit 3:  In-frame row-repeat map (1 == present)          */
37 /*                  Bit 4:  Frame-to-frame row delta map (1 == present)     */
38 /*                  Bit 5:  LZO compressed (0 == no, 1 == yes)              */
39 /*                  Bit 6..7:  Reserved                                     */
40 /*                                                                          */
41 /*      (Note:  Remainder of frame is LZO compressed if LZO = 1)            */
42 /*                                                                          */
43 /*      32 bytes    MOB bounding boxes in 8 4-byte records (if sent.)       */
44 /*                  1 byte each for x0, y0, x1, y1                          */
45 /*      N bytes     Row Delta Map (if sent.)                                */
46 /*      N bytes     Row Repeat Map (if sent.)                               */
47 /*      N bytes     Image data  (if sent.)                                  */
48 /*                                                                          */
49 /*  ROW DELTA MAP                                                           */
50 /*                                                                          */
51 /*  If present, the row-delta map indicates which rows are exact repeats    */
52 /*  of the previous frame.  This eliminates rows from the raster scan       */
53 /*  that get sent.  Row-delta compression happens before row-repeat, RLE or */
54 /*  copy-through.  The row-delta map is 1-bit-per-row bitmap that           */
55 /*  indicates which rows are copies of the ones from the previous frame.    */
56 /*  Rows are packed in LSB first, in a byte-oriented bitmap.  The           */
57 /*  bitmap length is always rounded to a whole number of bytes.             */
58 /*                                                                          */
59 /*  The length of the row-delta map is implied by the Y dimension of        */
60 /*  the screen.                                                             */
61 /*                                                                          */
62 /*  ROW REPEAT MAP                                                          */
63 /*                                                                          */
64 /*  If present, the row-repeat map indicates which rows are repeats         */
65 /*  of the rows above them.  This eliminates rows from the raster scan      */
66 /*  that get sent.  Row-repeat compression happens before RLE or            */
67 /*  copy-through.  The row-repeat map is 1-bit-per-row bitmap that          */
68 /*  indicates which rows are copies of the ones above it.  Copies are       */
69 /*  recursive, so the same row can get copied down multiple times.          */
70 /*  Rows are packed in LSB first, in a byte-oriented bitmap.  The           */
71 /*  bitmap length is always rounded to a whole number of bytes.             */
72 /*                                                                          */
73 /*  The length of the row-repeat map is implied by the Y dimension of       */
74 /*  the screen, minus the rows compressed away by the row-delta map.        */
75 /*                                                                          */
76 /*  IMAGE DATA FORMAT:                                                      */
77 /*      N bytes     Pixel data                                              */
78 /*                                                                          */
79 /*  RLE COMPRESSED IMAGE                                                    */
80 /*                                                                          */
81 /*  For RLE-compressed images, image is composed of "copy runs" and         */
82 /*  "fill runs".  All image pixels are contained in a run and so an         */
83 /*  image consists of "run records."  Runs are always even length.          */
84 /*  The RLE compressor scans from upper left to lower right in normal       */
85 /*  raster order, and concatenates rows.                                    */
86 /*                                                                          */
87 /*      COPY RUN:  2 pixels to 256 pixels, always even length.              */
88 /*                                                                          */
89 /*          Header byte:                                                    */
90 /*          Bit 0:      0 (indicates "copy run")                            */
91 /*          Bit 7..1:   Bits 7..1 of "run_length - 2".                      */
92 /*                                                                          */
93 /*          Data byte(s):                                                   */
94 /*          Bit 7..4:   Second pixel                                        */
95 /*          Bit 3..0:   First pixel                                         */
96 /*                                                                          */
97 /*      FILL RUN:  2 pixels to 526 pixels.                                  */
98 /*                                                                          */
99 /*          Header byte:                                                    */
100 /*          Bit 0:      1 (indicates "fill run")                            */
101 /*          Bit 3..1:   Run length (2..14), or 000 to indicate overflow.    */
102 /*          Bit 7..4:   Fill color.                                         */
103 /*                                                                          */
104 /*          If encoded run length = 000, a second header byte provides      */
105 /*          an 8-bit run length in the range 0..510, to which 16 gets       */
106 /*          added for the actual run length.  Thus, the value stored        */
107 /*          in the byte is (run_length - 16) / 2.                           */
108 /*                                                                          */
109 /*  UNCOMPRESSED IMAGE                                                      */
110 /*                                                                          */
111 /*  For uncompressed images, there's simply a x_dim * y_dim / 2 byte        */
112 /*  record of packed pixels.  Pixels are packed into bytes with the         */
113 /*  first pixel in bits 3..0, and the second pixel in bits 7..4.            */
114 /* ======================================================================== */
115 
116 
117 #define FLG_FRSENT ( 1)
118 #define FLG_BBSENT ( 2)
119 #define FLG_RLECMP ( 4)
120 #define FLG_RPTMAP ( 8)
121 #define FLG_DLTMAP (16)
122 #define FLG_LZOCMP (32)
123 
124 LOCAL uint8_t *enc_vid = NULL, *enc_buf = NULL, *enc_vid2;
125 #ifndef NO_LZO
126 LOCAL uint8_t *lzo_wrk = NULL, *lzo_tmp = NULL;
127 #endif
128 LOCAL uint32_t rowrpt[MVI_MAX_Y >> 5];
129 LOCAL uint32_t rowdlt[MVI_MAX_Y >> 5];
130 
131 #define ENC_BUF_SZ (MVI_MAX_X * MVI_MAX_Y + 128)
132 
133 
134 /* ======================================================================== */
135 /*  MVI_INIT:  Initialize movie tracking structure and scratch areas        */
136 /* ======================================================================== */
mvi_init(mvi_t * movie,int x_dim,int y_dim)137 void mvi_init(mvi_t *movie, int x_dim, int y_dim)
138 {
139     int i, j;
140 
141     if (x_dim > MVI_MAX_X || y_dim > MVI_MAX_Y)
142     {
143         fprintf(stderr, "MVI_INIT: %dx%d movie dimensions too large\n",
144                 x_dim, y_dim);
145         exit(1);
146     }
147 
148     movie->x_dim = x_dim;
149     movie->y_dim = y_dim;
150 
151     for (i = 0; i < 8; i++)
152         for (j = 0; j < 4; j++)
153             movie->bbox[i][j] = 0;
154 
155     movie->vid = CALLOC(uint8_t, x_dim*y_dim); /* 1 byte/pixel, just like gfx */
156     movie->fr  = 0;
157     movie->f   = NULL;
158 
159     if (!enc_buf ) enc_buf  = CALLOC(uint8_t, ENC_BUF_SZ);
160     if (!enc_vid ) enc_vid  = CALLOC(uint8_t, MVI_MAX_X * MVI_MAX_Y);
161     if (!enc_vid2) enc_vid2 = CALLOC(uint8_t, MVI_MAX_X * MVI_MAX_Y);
162 
163 #ifndef NO_LZO
164     if (!lzo_wrk) lzo_wrk = CALLOC(uint8_t, LZO1X_MEM_COMPRESS);
165     if (!lzo_tmp) lzo_tmp = CALLOC(uint8_t, ENC_BUF_SZ);
166 #endif
167 
168     if (!enc_buf || !enc_vid || !enc_vid2 || !movie->vid)
169     {
170         perror("MVI_INIT");
171         exit(1);
172     }
173 }
174 
175 /* ======================================================================== */
176 /*  MVI_WR_FRAME:  Encode and write a movie frame to the movie file.        */
177 /* ======================================================================== */
mvi_wr_frame(mvi_t * movie,uint8_t * vid,uint8_t bbox[8][4])178 void mvi_wr_frame(mvi_t *movie, uint8_t *vid, uint8_t bbox[8][4])
179 {
180     int send_bbox = 0, send_frame = 0, send_rle = 0;
181     int send_rowrpt = 0, lzo_comp = 0;
182     int enc_height_rd = movie->y_dim, enc_height = movie->y_dim;
183     int send_rowdlt = 0, rowdlt_ok = 0;
184     int yi, yo;
185     uint8_t header_byte = 0;
186     int rle_max_len;
187     uint8_t *enc_hdr;
188     uint8_t *enc_ptr = enc_buf;
189     uint8_t *evid = vid;
190     int i, j;
191 
192     /* -------------------------------------------------------------------- */
193     /*  If this is frame #0, send a file header.                            */
194     /* -------------------------------------------------------------------- */
195     if (movie->fr == 0)
196     {
197         /* signature */
198         *enc_ptr++ = 0x4A;
199         *enc_ptr++ = 0x5A;
200         *enc_ptr++ = 0x6A;
201         *enc_ptr++ = 0x7A;
202         /* dimensions */
203         *enc_ptr++ = (movie->x_dim >> 0) & 0xFF;
204         *enc_ptr++ = (movie->x_dim >> 8) & 0xFF;
205         *enc_ptr++ = (movie->y_dim >> 0) & 0xFF;
206         *enc_ptr++ = (movie->y_dim >> 8) & 0xFF;
207 
208         movie->rpt_frames = 0;
209         movie->rpt_rows   = 0;
210         movie->tot_bytes  = 0;
211 #ifndef NO_LZO
212         movie->tot_lzosave= 0;
213 #endif
214         memset(movie->vid, 0xFF, movie->x_dim * movie->y_dim); /* force 1st */
215     }
216 
217     enc_hdr  = enc_ptr;
218     enc_ptr += 8;
219 
220     /* -------------------------------------------------------------------- */
221     /*  Decide whether to send bounding box and frame.                      */
222     /* -------------------------------------------------------------------- */
223     send_frame = memcmp(movie->vid, vid, movie->x_dim * movie->y_dim);
224     send_bbox  = memcmp((void*)movie->bbox, bbox, sizeof(movie->bbox));
225 
226     if (!send_frame) movie->rpt_frames++;
227 
228     if (send_frame &&                             /* only on frames we send */
229         movie->fr > 0 &&                          /* never the first frame  */
230         ((movie->fr - movie->rpt_frames) & 15) != 0)  /* force no 1 ever 16 */
231         rowdlt_ok = 1;
232 
233 
234     /* -------------------------------------------------------------------- */
235     /*  If we're sending the bounding boxes, plug them in now.              */
236     /* -------------------------------------------------------------------- */
237     if (send_bbox)
238     {
239         for (i = 0; i < 8; i++)
240             for (j = 0; j < 4; j++)
241                 *enc_ptr++ = bbox[i][j];
242 
243         memcpy(movie->bbox, bbox, sizeof(movie->bbox));
244     }
245 
246     /* -------------------------------------------------------------------- */
247     /*  If we can send a row-delta map, go find which rows are repeats      */
248     /*  from the previous frame.                                            */
249     /* -------------------------------------------------------------------- */
250     if (send_frame && rowdlt_ok)
251     {
252         memset(rowdlt, 0, sizeof(rowdlt));      /* zero out repeat bitmap.  */
253         for (yi = yo = 0; yi < movie->y_dim; yi++)
254         {
255             if (memcmp(movie->vid + (yi*movie->x_dim),
256                               vid + (yi*movie->x_dim), movie->x_dim) == 0)
257             {
258                 rowdlt[yi >> 5] |= 1u << (yi & 0x1F);
259                 enc_height_rd--;
260                 movie->rpt_rows++;
261             } else
262             {
263                 memcpy(enc_vid2 + yo * movie->x_dim,
264                            vid  + yi * movie->x_dim, movie->x_dim);
265                 yo++;
266             }
267         }
268         assert( !send_rowdlt || yo <  yi );
269         assert(!(send_rowdlt && yo == yi));
270         assert(yo > 0);
271         assert(enc_height_rd > 0);
272         assert(yo == enc_height_rd);
273 
274         /* ---------------------------------------------------------------- */
275         /*  Only send rowdelta if at least 2 rows are repeats.              */
276         /* ---------------------------------------------------------------- */
277         if (enc_height - enc_height_rd > 2)
278         {
279             send_rowdlt = 1;
280             evid        = enc_vid2;
281             enc_height  = enc_height_rd;
282 /*printf("Frame %-6d %.8X %.8X %.8X %.8X %.8X\n", movie->fr, rowdlt[0], rowdlt[1], rowdlt[2], rowdlt[3], rowdlt[4]);*/
283 
284             for (i = 0; i < movie->y_dim; i += 8)
285             {
286                 j = 8 * ((i >> 3) & 3);
287                 *enc_ptr++ = (rowdlt[i >> 5] >> j) & 0xFF;
288             }
289         } else
290         {
291             send_rowdlt   = 0;
292             enc_height_rd = movie->y_dim;
293         }
294     }
295 
296     /* -------------------------------------------------------------------- */
297     /*  At this point we don't need the previous frame any more, so update  */
298     /* -------------------------------------------------------------------- */
299     if (send_frame)
300         memcpy(movie->vid, vid, movie->x_dim * movie->y_dim);
301 
302     /* -------------------------------------------------------------------- */
303     /*  If we're sending the frame, copy it to our encoding buffer.  Look   */
304     /*  for redundant rows as we go.                                        */
305     /* -------------------------------------------------------------------- */
306     if (send_frame && enc_height_rd > 1)
307     {
308         memset(rowrpt, 0, sizeof(rowrpt));      /* zero out repeat bitmap.  */
309         memcpy(enc_vid, evid, movie->x_dim);    /* copy first row           */
310         for (yi = 1, yo = 1; yi < enc_height_rd; yi++)
311         {
312             if (!memcmp(evid + (yi-1) * movie->x_dim,
313                         evid +  yi    * movie->x_dim, movie->x_dim))
314             {
315                 rowrpt[yi >> 5] |= 1u << (yi & 0x1F);
316 
317                 send_rowrpt = 1;
318                 enc_height--;
319                 movie->rpt_rows++;
320             } else
321             {
322                 memcpy(enc_vid + yo * movie->x_dim,
323                           evid + yi * movie->x_dim, movie->x_dim);
324                 yo++;
325             }
326         }
327         assert( !send_rowrpt || yo <  yi );
328         assert(!(send_rowrpt && yo == yi));
329 
330         if (send_rowrpt)
331         {
332             for (i = 0; i < enc_height_rd; i += 8)
333             {
334                 j = 8 * ((i >> 3) & 3);
335                 *enc_ptr++ = (rowrpt[i >> 5] >> j) & 0xFF;
336             }
337         }
338     } else if (send_frame && enc_height_rd <= 1)
339     {
340         memcpy(enc_vid, enc_vid2, movie->x_dim * enc_height);
341     }
342 
343 /*if (send_frame) printf("frame %.6X enc_height=%-3d enc_height_rd=%-3d\n", movie->fr, enc_height, enc_height_rd);*/
344 
345     /* -------------------------------------------------------------------- */
346     /*  If we're sending an image, try to RLE-compress it.  If RLE "blows   */
347     /*  up" (expands the data) we'll send uncompressed.                     */
348     /* -------------------------------------------------------------------- */
349     if (send_frame)
350     {
351         uint8_t *rle_ptr = enc_ptr;
352         uint8_t *vid_ptr = enc_vid;
353         uint8_t *rle_end;
354         uint8_t *vid_end;
355         uint8_t *cpstart = enc_vid;
356         uint8_t p0, p1, p2, p3;
357 
358         rle_max_len = (movie->x_dim * enc_height) >> 1;
359 
360         vid_end     = enc_vid + movie->x_dim * enc_height;
361         rle_end     = enc_ptr + rle_max_len - 1;
362 
363         /* ---------------------------------------------------------------- */
364         /*  Keep encoding RLE until we either run out of pixels or we       */
365         /*  overflow the encoding buffer.  We'll send uncompressed if RLE   */
366         /*  expands the image.                                              */
367         /* ---------------------------------------------------------------- */
368         while (rle_ptr < rle_end && vid_ptr < vid_end)
369         {
370             int fl_len;
371             int fl_cur;
372             int remain = vid_end - vid_ptr;
373 
374             /* ------------------------------------------------------------ */
375             /*  Grab the next two pixels.                                   */
376             /*  If they're not equal append to a copy run.                  */
377             /* ------------------------------------------------------------ */
378             p0 = vid_ptr[0] & 0xF;
379             p1 = vid_ptr[1] & 0xF;
380 
381             if (p0 != p1)
382             {
383                 vid_ptr += 2;
384                 continue;
385             }
386 
387             /* ------------------------------------------------------------ */
388             /*  See how long the run is.                                    */
389             /* ------------------------------------------------------------ */
390             for (i = 2; i < remain; i += 2)
391             {
392                 p2 = vid_ptr[i+0] & 0xF;
393                 p3 = vid_ptr[i+1] & 0xF;
394 
395                 if (p2 != p0 || p3 != p0)
396                     break;
397             }
398 
399             fl_len = i >> 1;  /* always even number, so div-by-2 */
400 
401             /* ------------------------------------------------------------ */
402             /*  If it's not a long enough run, just append them to the      */
403             /*  copy run and continue.                                      */
404             /* ------------------------------------------------------------ */
405             if (fl_len == 1 && remain > 2)
406             {
407                 vid_ptr += fl_len << 1;
408                 continue;
409             }
410 
411             /* ------------------------------------------------------------ */
412             /*  Flush any existing copy run first.                          */
413             /* ------------------------------------------------------------ */
414             if (cpstart < vid_ptr)
415             {
416                 int cp_len = vid_ptr - cpstart;
417                 int cp_cur;
418 
419                 assert((cp_len & 1) == 0);
420 
421                 /* -------------------------------------------------------- */
422                 /*  Don't let copy run overflow buffer!                     */
423                 /* -------------------------------------------------------- */
424                 if (rle_ptr + 2 + (cp_len>>8) + (cp_len>>1) >= rle_end)
425                     goto no_rle;
426 
427                 /* -------------------------------------------------------- */
428                 /*  Output the copy run.                                    */
429                 /* -------------------------------------------------------- */
430                 while (cp_len > 0)
431                 {
432                     int p;
433                     cp_cur  = cp_len > 256 ? 256 : cp_len;
434                     cp_len -= cp_cur;
435 
436                     *rle_ptr++ = cp_cur - 2;
437                     for (j = 0; j < cp_cur; j += 2)
438                     {
439                         p = (cpstart[0] & 0xF) | ((cpstart[1]&0xF)<<4);
440                         *rle_ptr++ = p;
441                         cpstart   += 2;
442                     }
443                 }
444                 assert(cpstart == vid_ptr);
445             }
446 
447             /* ------------------------------------------------------------ */
448             /*  Advance the input video pointer beyond fill run.  That      */
449             /*  location also marks the beginning of the next copy run.     */
450             /* ------------------------------------------------------------ */
451             vid_ptr += fl_len << 1;
452             cpstart  = vid_ptr;
453 
454             /* ------------------------------------------------------------ */
455             /*  Now output the RLE run.                                     */
456             /* ------------------------------------------------------------ */
457             while (fl_len > 0)
458             {
459                 fl_cur  = fl_len > 263 ? 263 : fl_len;
460                 fl_len -= fl_cur;
461 
462                 if (fl_cur < 8)
463                 {
464                     if (rle_ptr >= rle_end)
465                         goto no_rle;
466                     *rle_ptr++ = 1 | (fl_cur << 1) | (p0 << 4);
467                 } else
468                 {
469                     if (rle_ptr + 1 >= rle_end)
470                         goto no_rle;
471                     *rle_ptr++ = 1 | (p0 << 4);
472                     *rle_ptr++ = fl_cur - 8;
473                 }
474             }
475         }
476 
477         if (rle_ptr >= rle_end)
478             goto no_rle;
479 
480         /* ---------------------------------------------------------------- */
481         /*  Flush any copy run at end.                                      */
482         /* ---------------------------------------------------------------- */
483         if (cpstart < vid_ptr)
484         {
485             int cp_len = vid_ptr - cpstart;
486             int cp_cur;
487 
488             assert((cp_len & 1) == 0);
489 
490             /* ------------------------------------------------------------ */
491             /*  Don't let copy run overflow buffer!                         */
492             /* ------------------------------------------------------------ */
493             if (rle_ptr + 2 + (cp_len>>8) + (cp_len>>1) >= rle_end)
494                 goto no_rle;
495 
496             /* ------------------------------------------------------------ */
497             /*  Output the copy run.                                        */
498             /* ------------------------------------------------------------ */
499             while (cp_len > 0)
500             {
501                 int p;
502                 cp_cur  = cp_len > 256 ? 256 : cp_len;
503                 cp_len -= cp_cur;
504 
505                 *rle_ptr++ = cp_cur - 2;
506                 for (j = 0; j < cp_cur; j += 2)
507                 {
508                     p = (cpstart[0] & 0xF) | ((cpstart[1]&0xF)<<4);
509                     *rle_ptr++ = p;
510                     cpstart   += 2;
511                 }
512             }
513             assert(cpstart == vid_ptr);
514         }
515 
516         /* ---------------------------------------------------------------- */
517         /*  If we make it to here w/out overflowing the RLE buf, send RLE.  */
518         /* ---------------------------------------------------------------- */
519         if (rle_ptr <= rle_end)
520         {
521             send_rle = 1;
522             enc_ptr  = rle_ptr;
523         }
524     } /* end of RLE encode */
525 
526 no_rle:
527 
528     /* -------------------------------------------------------------------- */
529     /*  If we're sending the frame and RLE didn't work out, send packed.    */
530     /* -------------------------------------------------------------------- */
531     if (send_frame && !send_rle)
532     {
533         int total = movie->x_dim * enc_height;
534 
535         for (i = 0; i < total; i += 2)
536         {
537             *enc_ptr++ =  (enc_vid[i + 0] & 0xF)
538                        | ((enc_vid[i + 1] & 0xF) << 4);
539         }
540     }
541 
542 #ifndef NO_LZO
543     /* -------------------------------------------------------------------- */
544     /*  Try to compress the encoded frame with LZO.                         */
545     /* -------------------------------------------------------------------- */
546     if (lzo_wrk && lzo_tmp && enc_ptr > enc_hdr + 8)
547     {
548         int r;
549         lzo_uint lzo_len = 0, data_len = enc_ptr - enc_hdr - 8;
550 
551         r = lzo1x_1_compress(enc_hdr + 8, data_len,
552                              lzo_tmp, &lzo_len, (lzo_voidp)lzo_wrk);
553 
554         if (r == LZO_E_OK && lzo_len < data_len && lzo_len > 0)
555         {
556             memcpy(enc_hdr + 8, lzo_tmp, lzo_len);
557             enc_ptr = enc_hdr + 8 + lzo_len;
558             lzo_comp = 1;
559             movie->tot_lzosave += data_len - lzo_len;
560         }
561     }
562 #endif
563 
564     /* -------------------------------------------------------------------- */
565     /*  Generate the header byte.                                           */
566     /* -------------------------------------------------------------------- */
567     if ( send_frame ) header_byte |= FLG_FRSENT;
568     if ( send_bbox  ) header_byte |= FLG_BBSENT;
569     if ( send_rowrpt) header_byte |= FLG_RPTMAP;
570     if ( send_rle   ) header_byte |= FLG_RLECMP;
571     if ( send_rowdlt) header_byte |= FLG_DLTMAP;
572     if ( lzo_comp   ) header_byte |= FLG_LZOCMP;
573 
574     /* -------------------------------------------------------------------- */
575     /*  Prepend the header:  Frame # and flag byte.                         */
576     /* -------------------------------------------------------------------- */
577     enc_hdr[0] = ((enc_ptr - enc_hdr) >>  0) & 0xFF;
578     enc_hdr[1] = ((enc_ptr - enc_hdr) >>  8) & 0xFF;
579     enc_hdr[2] = ((enc_ptr - enc_hdr) >> 16) & 0xFF;
580     enc_hdr[3] = ((enc_ptr - enc_hdr) >> 24) & 0xFF;
581     enc_hdr[4] = (movie->fr >>  0) & 0xFF;
582     enc_hdr[5] = (movie->fr >>  8) & 0xFF;
583     enc_hdr[6] = (movie->fr >> 16) & 0xFF;
584     enc_hdr[7] = header_byte;
585 
586     /* -------------------------------------------------------------------- */
587     /*  Send it all in one big fwrite.                                      */
588     /* -------------------------------------------------------------------- */
589     fwrite(enc_buf, 1, enc_ptr - enc_buf, movie->f);
590     fflush(movie->f);
591     movie->tot_bytes += enc_ptr - enc_buf;
592 
593     movie->fr++;  /* increment frame count. */
594 }
595 
596 
597 /* ======================================================================== */
598 /*  MVI_RD_FRAME -- Reads a frame from a movie file and decodes it.         */
599 /* ======================================================================== */
mvi_rd_frame(mvi_t * movie,uint8_t * vid,uint8_t bbox[8][4])600 int  mvi_rd_frame(mvi_t *movie, uint8_t *vid, uint8_t bbox[8][4])
601 {
602     int got_frame  = 0;
603     int got_bbox   = 0;
604     int got_rowrpt = 0;
605     int got_rle    = 0;
606     int got_rowdlt = 0;
607     int lzo_comp   = 0;
608     uint8_t *dec_ptr = enc_buf, *dec_end;
609     uint8_t *dec_vid;
610     uint8_t *dvid;
611     size_t tot_rd;
612     uint32_t flags;
613     int i, j;
614     int enc_height, enc_height_rd;
615     uint32_t fr_len;
616 
617     /* -------------------------------------------------------------------- */
618     /*  Sanity check.                                                       */
619     /* -------------------------------------------------------------------- */
620     if (!movie->f) return -1; /* no current file? */
621 
622 again:
623     /* -------------------------------------------------------------------- */
624     /*  Read 8 bytes.  It's either a frame header or a file header.         */
625     /* -------------------------------------------------------------------- */
626     tot_rd = fread(enc_buf, 1, 8, movie->f);
627     if (tot_rd < 8)
628         return -1;      /* end of file or other error */
629 
630 
631     /* -------------------------------------------------------------------- */
632     /*  Check for a file header on this frame.                              */
633     /* -------------------------------------------------------------------- */
634     if (tot_rd == 8 &&
635         enc_buf[0] == 0x4A && enc_buf[1] == 0x5A &&
636         enc_buf[2] == 0x6A && enc_buf[3] == 0x7A)
637     {
638         int new_x_dim, new_y_dim;
639 
640         new_x_dim = (enc_buf[5] << 8) | enc_buf[4];
641         new_y_dim = (enc_buf[7] << 8) | enc_buf[6];
642 
643         if (new_x_dim != movie->x_dim ||
644             new_y_dim != movie->y_dim)
645         {
646             movie->x_dim = new_x_dim;
647             movie->y_dim = new_y_dim;
648             if (new_x_dim > MVI_MAX_X || new_y_dim > MVI_MAX_Y)
649             {
650                 fprintf(stderr, "MVI_RD: Movie size %dx%d too large!",
651                         new_x_dim, new_y_dim);
652                 return -1;
653             }
654         }
655 
656         goto again;  /* loop until we get a frame header */
657     }
658 
659     /* -------------------------------------------------------------------- */
660     /*  Pull apart the frame header.                                        */
661     /* -------------------------------------------------------------------- */
662     fr_len = (enc_buf[0] <<  0) |
663              (enc_buf[1] <<  8) |
664              (enc_buf[2] << 16) |
665              (enc_buf[3] << 24);
666 
667     if (fr_len > ENC_BUF_SZ)
668     {
669         fprintf(stderr,
670                 "MVI_RD: Frame size too large: %d bytes (%.8X bytes)\n",
671                 fr_len, fr_len);
672         exit(1);
673     }
674 
675     movie->last_fr = movie->fr;
676 
677     movie->fr = (enc_buf[4] <<  0) |
678                 (enc_buf[5] <<  8) |
679                 (enc_buf[6] << 16);
680 
681     flags = enc_buf[7];
682 
683 
684     got_frame  = flags & FLG_FRSENT;
685     got_bbox   = flags & FLG_BBSENT;
686     got_rowrpt = flags & FLG_RPTMAP;
687     got_rle    = flags & FLG_RLECMP;
688     got_rowdlt = flags & FLG_DLTMAP;
689     lzo_comp   = flags & FLG_LZOCMP;
690 
691     /* -------------------------------------------------------------------- */
692     /*  Now read the rest of the frame.                                     */
693     /* -------------------------------------------------------------------- */
694     tot_rd += fread(enc_buf, 1, fr_len - 8, movie->f);
695     dec_end = enc_buf + fr_len - 8;
696     dec_ptr = enc_buf;
697 
698     if (tot_rd != fr_len)
699     {
700         fprintf(stderr,
701                 "MVI_RD: Short read on frame %d (%d vs %d bytes)\n"
702                 "        Previous frame was frame %d\n",
703                 movie->fr, (int)tot_rd, fr_len, movie->last_fr);
704         exit(1);
705     }
706 
707 
708     /* -------------------------------------------------------------------- */
709     /*  Sanity check flags.                                                 */
710     /* -------------------------------------------------------------------- */
711     if ((flags & 0xC0) != 0 ||
712         (got_rle    != 0 && got_frame == 0) ||
713         (got_rowdlt != 0 && got_frame == 0) ||
714         (got_rowrpt != 0 && got_frame == 0))
715     {
716         fprintf(stderr, "MVI_RD: Bogus flags: %.2X\n", flags);
717         return -1;
718     }
719 
720     if (flags != 0 && (movie->x_dim == 0 || movie->y_dim == 0))
721     {
722         fprintf(stderr, "MVI_RD: Movie data before valid file header!\n");
723         return -1;
724     }
725 
726 
727     /* -------------------------------------------------------------------- */
728     /*  If the frame is LZO compressed, decompress it before proceeding.    */
729     /* -------------------------------------------------------------------- */
730 #ifdef NO_LZO
731     if (lzo_comp)
732     {
733         fprintf(stderr, "MVI_RD: This movie is LZO-compressed.  LZO "
734                         "compression support is not compiled in.\n");
735         return -1;
736     }
737 #else
738     if (lzo_comp)
739     {
740         int r;
741         lzo_uint lzo_len = 0;
742 
743         if (!lzo_tmp)
744         {
745             fprintf(stderr, "MVI_RD: This movie is LZO-compressed, but "
746                             "there is insufficient memory for decompresion\n");
747             return -1;
748         }
749 
750         r = lzo1x_decompress(dec_ptr, fr_len - 8, lzo_tmp,
751                              &lzo_len,
752                              (lzo_voidp)NULL);
753 
754         if (r == LZO_E_OK)
755         {
756             memcpy(dec_ptr, lzo_tmp, lzo_len);
757             fr_len  = 8 + lzo_len;
758             dec_end = dec_ptr + lzo_len;
759         } else
760         {
761             fprintf(stderr, "MVI_RD:  LZO error: %d\n", r);
762             return -1;
763         }
764     }
765 #endif
766 
767     /* -------------------------------------------------------------------- */
768     /*  If we have an updated set of bounding boxes, read them in.          */
769     /* -------------------------------------------------------------------- */
770     if (got_bbox)
771     {
772         for (i = 0; i < 8; i++)
773             for (j = 0; j < 4; j++)
774                 movie->bbox[i][j] = *dec_ptr++;
775     }
776 
777     /* -------------------------------------------------------------------- */
778     /*  If we have a row-delta map, read that in.                           */
779     /* -------------------------------------------------------------------- */
780     memset(rowdlt, 0, sizeof(rowdlt));
781     enc_height_rd = movie->y_dim;
782 
783     if (got_rowdlt)
784     {
785         for (i = 0; i < movie->y_dim; i += 8)
786             rowdlt[i >> 5] |= *dec_ptr++ << (8*((i>>3) & 3));
787 
788         if (dec_ptr >= dec_end)
789         {
790             fprintf(stderr, "MVI_RD:  Error reading row-repeat tbl\n");
791             return -1;
792         }
793 
794         for (i = 0; i < movie->y_dim; i += 32)
795         {
796             uint32_t tmp = rowdlt[i >> 5];
797 
798             while (tmp)
799             {
800                 tmp &= tmp - 1;
801                 enc_height_rd--;
802             }
803         }
804     }
805 
806     /* -------------------------------------------------------------------- */
807     /*  If we have a row-repeat map, read that in.                          */
808     /* -------------------------------------------------------------------- */
809     memset(rowrpt, 0, sizeof(rowrpt));
810     enc_height = enc_height_rd;
811 
812     if (got_rowrpt)
813     {
814         for (i = 0; i < enc_height_rd; i += 8)
815             rowrpt[i >> 5] |= *dec_ptr++ << (8*((i>>3) & 3));
816 
817         if (dec_ptr >= dec_end)
818         {
819             fprintf(stderr, "MVI_RD:  Error reading row-repeat tbl\n");
820             return -1;
821         }
822 
823         for (i = 0; i < enc_height_rd; i += 32)
824         {
825             uint32_t tmp = rowrpt[i >> 5];
826 
827             while (tmp)
828             {
829                 tmp &= tmp - 1;
830                 enc_height--;
831             }
832         }
833     }
834 
835     /* -------------------------------------------------------------------- */
836     /*  Now decode the image (prior to unpacking the row repeat map).       */
837     /* -------------------------------------------------------------------- */
838     dec_vid = got_rowrpt ? enc_vid  :
839               got_rowdlt ? enc_vid2 : vid;
840 
841     if (got_frame == 0)
842     {
843         memcpy(vid, movie->vid, movie->x_dim * movie->y_dim);
844     } else if (!got_rle)
845     {
846         for (i = 0; i < movie->x_dim * enc_height; i += 2)
847         {
848             *dec_vid++ = 0xF &  *dec_ptr;
849             *dec_vid++ = 0xF & (*dec_ptr >> 4);
850             dec_ptr++;
851         }
852     } else /* RLE data */
853     {
854         int run, len, p;
855         int remain = movie->x_dim * enc_height;
856 
857         /* ---------------------------------------------------------------- */
858         /*  Expand all the runs in the encoded image.  The ending comes     */
859         /*  implicitly when no images pixels remain.                        */
860         /* ---------------------------------------------------------------- */
861         while (remain > 0)
862         {
863             if (dec_ptr >= dec_end)
864             {
865                 fprintf(stderr, "MVI_RD:  Error in RLE data!\n");
866                 return -1;
867             }
868 
869             run = *dec_ptr++;
870 
871             /* ------------------------------------------------------------ */
872             /*  Handle copy runs by unpacking and copying the pixels to     */
873             /*  the output.                                                 */
874             /* ------------------------------------------------------------ */
875             if ((run & 1) == 0) /* copy run? */
876             {
877                 len = run + 2;
878                 if (len > remain || (dec_ptr + (len>>1)) > dec_end)
879                 {
880                     fprintf(stderr, "MVI_RD:  Error in RLE data!\n");
881                     return -1;
882                 }
883                 remain -= len;
884 
885                 for (i = 0; i < len; i += 2)
886                 {
887                     *dec_vid++ = 0xF &  *dec_ptr;
888                     *dec_vid++ = 0xF & (*dec_ptr >> 4);
889                     dec_ptr++;
890                 }
891 
892                 continue;
893             }
894 
895             /* ------------------------------------------------------------ */
896             /*  Handle fill runs by writing the color value over and over.  */
897             /* ------------------------------------------------------------ */
898             len = run & 0xE;
899             if (!len) len = 16 + (*dec_ptr++ << 1);
900 
901             if (len > remain || dec_ptr > dec_end)
902             {
903                 fprintf(stderr, "MVI_RD:  Error in RLE data!\n");
904                 return -1;
905             }
906             remain -= len;
907 
908             p = (run & 0xF0) >> 4;
909 
910             len >>= 1;
911 
912             while (len-->0) { *dec_vid++ = p; *dec_vid++ = p; }
913         }
914 
915         if (remain != 0)
916         {
917             fprintf(stderr, "MVI_RD:  Error in RLE data!\n");
918             return -1;
919         }
920     }
921 
922     /* -------------------------------------------------------------------- */
923     /*  If we have a row-repeat map, expand vertically-compressed image.    */
924     /* -------------------------------------------------------------------- */
925     if (got_rowrpt)
926     {
927         int yo, yi, xd = movie->x_dim;
928 
929         dvid = got_rowdlt ? enc_vid2 : vid;
930 
931         yi = enc_height - 1;
932         for (yo = enc_height_rd - 1; yo >= 0; yo--)
933         {
934             memcpy(dvid + yo*xd, enc_vid + yi*xd, xd);
935 
936             if ((1 & (rowrpt[yo >> 5] >> (yo & 0x1F))) == 0) /* not a rpt */
937                 yi--;
938         }
939 
940         assert(yi == -1);
941     }
942 
943     /* -------------------------------------------------------------------- */
944     /*  If we have a row-delta map, expand temporally-compressed image.     */
945     /* -------------------------------------------------------------------- */
946     if (got_rowdlt)
947     {
948         int yo, yi, xd = movie->x_dim;
949 
950         yi = enc_height_rd - 1;
951         for (yo = movie->y_dim - 1; yo >= 0; yo--)
952         {
953             if ((1 & (rowdlt[yo >> 5] >> (yo & 0x1F))) == 0) /* not a rpt */
954             {
955                 memcpy(vid + yo*xd, enc_vid2 + yi*xd, xd);
956                 yi--;
957             } else  /* copy from previous frame */
958             {
959                 memcpy(vid + yo*xd, movie->vid + yo*xd, xd);
960             }
961         }
962 
963         assert(yi == -1);
964     }
965 
966     /* -------------------------------------------------------------------- */
967     /*  Remember each frame we decode.                                      */
968     /* -------------------------------------------------------------------- */
969     if (got_frame)
970         memcpy(movie->vid, vid, movie->x_dim * movie->y_dim);
971 
972 
973     /* -------------------------------------------------------------------- */
974     /*  Copy over current bounding boxes.                                   */
975     /* -------------------------------------------------------------------- */
976     memcpy(bbox, movie->bbox, sizeof(movie->bbox));
977 
978     return (got_frame ? 0 : MVI_FR_SAME) |
979            (got_bbox  ? 0 : MVI_BB_SAME);
980 }
981 
982 /* ======================================================================== */
983 /*  This program is free software; you can redistribute it and/or modify    */
984 /*  it under the terms of the GNU General Public License as published by    */
985 /*  the Free Software Foundation; either version 2 of the License, or       */
986 /*  (at your option) any later version.                                     */
987 /*                                                                          */
988 /*  This program is distributed in the hope that it will be useful,         */
989 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
990 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
991 /*  General Public License for more details.                                */
992 /*                                                                          */
993 /*  You should have received a copy of the GNU General Public License along */
994 /*  with this program; if not, write to the Free Software Foundation, Inc., */
995 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
996 /* ======================================================================== */
997 /*                 Copyright (c) 1998-2005, Joseph Zbiciak                  */
998 /* ======================================================================== */
999 
1000