LCOV - code coverage report
Current view: top level - gifdec - gifdec.c (source / functions) Coverage Total Hit
Test: gifread_nocode.info Lines: 38.2 % 317 121
Test Date: 2024-03-08 11:11:14 Functions: 61.9 % 21 13

            Line data    Source code
       1              : #include "gifdec.h"
       2              : 
       3              : #include <stdio.h>
       4              : #include <stdlib.h>
       5              : #include <string.h>
       6              : 
       7              : #include <sys/types.h>
       8              : #include <sys/stat.h>
       9              : #include <fcntl.h>
      10              : #ifdef _WIN32
      11              : #include <io.h>
      12              : #else
      13              : #include <unistd.h>
      14              : #endif
      15              : 
      16              : #define MIN(A, B) ((A) < (B) ? (A) : (B))
      17              : #define MAX(A, B) ((A) > (B) ? (A) : (B))
      18              : 
      19              : typedef struct Entry {
      20              :     uint16_t length;
      21              :     uint16_t prefix;
      22              :     uint8_t  suffix;
      23              : } Entry;
      24              : 
      25              : typedef struct Table {
      26              :     int bulk;
      27              :     int nentries;
      28              :     Entry *entries;
      29              : } Table;
      30              : 
      31              : static uint16_t
      32         2009 : read_num(int fd)
      33              : {
      34              :     uint8_t bytes[2];
      35              : 
      36         2009 :     read(fd, bytes, 2);
      37         2009 :     return bytes[0] + (((uint16_t) bytes[1]) << 8);
      38              : }
      39              : 
      40              : gd_GIF *
      41         1000 : gd_open_gif(const char *fname)
      42              : {
      43              :     int fd;
      44              :     uint8_t sigver[3];
      45              :     uint16_t width, height, depth;
      46              :     uint8_t fdsz, bgidx, aspect;
      47              :     int i;
      48              :     uint8_t *bgcolor;
      49              :     int gct_sz;
      50              :     gd_GIF *gif;
      51              : 
      52         1000 :     fd = open(fname, O_RDONLY);
      53         1000 :     if (fd == -1) return NULL;
      54              : #ifdef _WIN32
      55              :     setmode(fd, O_BINARY);
      56              : #endif
      57              :     /* Header */
      58         1000 :     read(fd, sigver, 3);
      59         1000 :     if (memcmp(sigver, "GIF", 3) != 0) {
      60            0 :         fprintf(stderr, "invalid signature\n");
      61            0 :         goto fail;
      62              :     }
      63              :     /* Version */
      64         1000 :     read(fd, sigver, 3);
      65         1000 :     if (memcmp(sigver, "89a", 3) != 0) {
      66            0 :         fprintf(stderr, "invalid version\n");
      67            0 :         goto fail;
      68              :     }
      69              :     /* Width x Height */
      70         1000 :     width  = read_num(fd);
      71         1000 :     height = read_num(fd);
      72              :     /* FDSZ */
      73         1000 :     read(fd, &fdsz, 1);
      74              :     /* Presence of GCT */
      75         1000 :     if (!(fdsz & 0x80)) {
      76          486 :         fprintf(stderr, "no global color table\n");
      77          486 :         goto fail;
      78              :     }
      79              :     /* Color Space's Depth */
      80          514 :     depth = ((fdsz >> 4) & 7) + 1;
      81              :     /* Ignore Sort Flag. */
      82              :     /* GCT Size */
      83          514 :     gct_sz = 1 << ((fdsz & 0x07) + 1);
      84              :     /* Background Color Index */
      85          514 :     read(fd, &bgidx, 1);
      86              :     /* Aspect Ratio */
      87          514 :     read(fd, &aspect, 1);
      88              :     /* Create gd_GIF Structure. */
      89          514 :     gif = calloc(1, sizeof(*gif));
      90          514 :     if (!gif) goto fail;
      91          514 :     gif->fd = fd;
      92          514 :     gif->width  = width;
      93          514 :     gif->height = height;
      94          514 :     gif->depth  = depth;
      95              :     /* Read GCT */
      96          514 :     gif->gct.size = gct_sz;
      97          514 :     read(fd, gif->gct.colors, 3 * gif->gct.size);
      98          514 :     gif->palette = &gif->gct;
      99          514 :     gif->bgindex = bgidx;
     100          514 :     gif->frame = calloc(4, width * height);
     101          514 :     if (!gif->frame) {
     102            0 :         free(gif);
     103            0 :         goto fail;
     104              :     }
     105          514 :     gif->canvas = &gif->frame[width * height];
     106          514 :     if (gif->bgindex)
     107          514 :         memset(gif->frame, gif->bgindex, gif->width * gif->height);
     108          514 :     bgcolor = &gif->palette->colors[gif->bgindex*3];
     109          514 :     if (bgcolor[0] || bgcolor[1] || bgcolor [2])
     110     30852060 :         for (i = 0; i < gif->width * gif->height; i++)
     111     30852060 :             memcpy(&gif->canvas[i*3], bgcolor, 3);
     112          514 :     gif->anim_start = lseek(fd, 0, SEEK_CUR);
     113          514 :     goto ok;
     114              : fail:
     115          486 :     close(fd);
     116          486 :     return 0;
     117              : ok:
     118          514 :     return gif;
     119         1000 : }
     120              : 
     121              : static void
     122            0 : discard_sub_blocks(gd_GIF *gif)
     123              : {
     124              :     uint8_t size;
     125              : 
     126            0 :     do {
     127            0 :         read(gif->fd, &size, 1);
     128            0 :         lseek(gif->fd, size, SEEK_CUR);
     129            0 :     } while (size);
     130            0 : }
     131              : 
     132              : static void
     133            0 : read_plain_text_ext(gd_GIF *gif)
     134              : {
     135            0 :     if (gif->plain_text) {
     136              :         uint16_t tx, ty, tw, th;
     137              :         uint8_t cw, ch, fg, bg;
     138              :         off_t sub_block;
     139            0 :         lseek(gif->fd, 1, SEEK_CUR); /* block size = 12 */
     140            0 :         tx = read_num(gif->fd);
     141            0 :         ty = read_num(gif->fd);
     142            0 :         tw = read_num(gif->fd);
     143            0 :         th = read_num(gif->fd);
     144            0 :         read(gif->fd, &cw, 1);
     145            0 :         read(gif->fd, &ch, 1);
     146            0 :         read(gif->fd, &fg, 1);
     147            0 :         read(gif->fd, &bg, 1);
     148            0 :         sub_block = lseek(gif->fd, 0, SEEK_CUR);
     149            0 :         gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
     150            0 :         lseek(gif->fd, sub_block, SEEK_SET);
     151            0 :     } else {
     152              :         /* Discard plain text metadata. */
     153            0 :         lseek(gif->fd, 13, SEEK_CUR);
     154              :     }
     155              :     /* Discard plain text sub-blocks. */
     156            0 :     discard_sub_blocks(gif);
     157            0 : }
     158              : 
     159              : static void
     160            1 : read_graphic_control_ext(gd_GIF *gif)
     161              : {
     162              :     uint8_t rdit;
     163              : 
     164              :     /* Discard block size (always 0x04). */
     165            1 :     lseek(gif->fd, 1, SEEK_CUR);
     166            1 :     read(gif->fd, &rdit, 1);
     167            1 :     gif->gce.disposal = (rdit >> 2) & 3;
     168            1 :     gif->gce.input = rdit & 2;
     169            1 :     gif->gce.transparency = rdit & 1;
     170            1 :     gif->gce.delay = read_num(gif->fd);
     171            1 :     read(gif->fd, &gif->gce.tindex, 1);
     172              :     /* Skip block terminator. */
     173            1 :     lseek(gif->fd, 1, SEEK_CUR);
     174            1 : }
     175              : 
     176              : static void
     177            0 : read_comment_ext(gd_GIF *gif)
     178              : {
     179            0 :     if (gif->comment) {
     180            0 :         off_t sub_block = lseek(gif->fd, 0, SEEK_CUR);
     181            0 :         gif->comment(gif);
     182            0 :         lseek(gif->fd, sub_block, SEEK_SET);
     183            0 :     }
     184              :     /* Discard comment sub-blocks. */
     185            0 :     discard_sub_blocks(gif);
     186            0 : }
     187              : 
     188              : static void
     189            0 : read_application_ext(gd_GIF *gif)
     190              : {
     191              :     char app_id[8];
     192              :     char app_auth_code[3];
     193              : 
     194              :     /* Discard block size (always 0x0B). */
     195            0 :     lseek(gif->fd, 1, SEEK_CUR);
     196              :     /* Application Identifier. */
     197            0 :     read(gif->fd, app_id, 8);
     198              :     /* Application Authentication Code. */
     199            0 :     read(gif->fd, app_auth_code, 3);
     200            0 :     if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
     201              :         /* Discard block size (0x03) and constant byte (0x01). */
     202            0 :         lseek(gif->fd, 2, SEEK_CUR);
     203            0 :         gif->loop_count = read_num(gif->fd);
     204              :         /* Skip block terminator. */
     205            0 :         lseek(gif->fd, 1, SEEK_CUR);
     206            0 :     } else if (gif->application) {
     207            0 :         off_t sub_block = lseek(gif->fd, 0, SEEK_CUR);
     208            0 :         gif->application(gif, app_id, app_auth_code);
     209            0 :         lseek(gif->fd, sub_block, SEEK_SET);
     210            0 :         discard_sub_blocks(gif);
     211            0 :     } else {
     212            0 :         discard_sub_blocks(gif);
     213              :     }
     214            0 : }
     215              : 
     216              : static void
     217            1 : read_ext(gd_GIF *gif)
     218              : {
     219              :     uint8_t label;
     220              : 
     221            1 :     read(gif->fd, &label, 1);
     222            1 :     switch (label) {
     223              :     case 0x01:
     224            0 :         read_plain_text_ext(gif);
     225            0 :         break;
     226              :     case 0xF9:
     227            1 :         read_graphic_control_ext(gif);
     228            1 :         break;
     229              :     case 0xFE:
     230            0 :         read_comment_ext(gif);
     231            0 :         break;
     232              :     case 0xFF:
     233            0 :         read_application_ext(gif);
     234            0 :         break;
     235              :     default:
     236            0 :         fprintf(stderr, "unknown extension: %02X\n", label);
     237            0 :     }
     238            1 : }
     239              : 
     240              : static Table *
     241            0 : new_table(int key_size)
     242              : {
     243              :     int key;
     244            0 :     int init_bulk = MAX(1 << (key_size + 1), 0x100);
     245            0 :     Table *table = malloc(sizeof(*table) + sizeof(Entry) * init_bulk);
     246            0 :     if (table) {
     247            0 :         table->bulk = init_bulk;
     248            0 :         table->nentries = (1 << key_size) + 2;
     249            0 :         table->entries = (Entry *) &table[1];
     250            0 :         for (key = 0; key < (1 << key_size); key++)
     251            0 :             table->entries[key] = (Entry) {1, 0xFFF, key};
     252            0 :     }
     253            0 :     return table;
     254              : }
     255              : 
     256              : /* Add table entry. Return value:
     257              :  *  0 on success
     258              :  *  +1 if key size must be incremented after this addition
     259              :  *  -1 if could not realloc table */
     260              : static int
     261            0 : add_entry(Table **tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
     262              : {
     263            0 :     Table *table = *tablep;
     264            0 :     if (table->nentries == table->bulk) {
     265            0 :         table->bulk *= 2;
     266            0 :         table = realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
     267            0 :         if (!table) return -1;
     268            0 :         table->entries = (Entry *) &table[1];
     269            0 :         *tablep = table;
     270            0 :     }
     271            0 :     table->entries[table->nentries] = (Entry) {length, prefix, suffix};
     272            0 :     table->nentries++;
     273            0 :     if ((table->nentries & (table->nentries - 1)) == 0)
     274            0 :         return 1;
     275            0 :     return 0;
     276            0 : }
     277              : 
     278              : static uint16_t
     279            0 : get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
     280              : {
     281              :     int bits_read;
     282              :     int rpad;
     283              :     int frag_size;
     284              :     uint16_t key;
     285              : 
     286            0 :     key = 0;
     287            0 :     for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
     288            0 :         rpad = (*shift + bits_read) % 8;
     289            0 :         if (rpad == 0) {
     290              :             /* Update byte. */
     291            0 :             if (*sub_len == 0) {
     292            0 :                 read(gif->fd, sub_len, 1); /* Must be nonzero! */
     293            0 :                 if (*sub_len == 0)
     294            0 :                     return 0x1000;
     295            0 :             }
     296            0 :             read(gif->fd, byte, 1);
     297            0 :             (*sub_len)--;
     298            0 :         }
     299            0 :         frag_size = MIN(key_size - bits_read, 8 - rpad);
     300            0 :         key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
     301            0 :     }
     302              :     /* Clear extra bits to the left. */
     303            0 :     key &= (1 << key_size) - 1;
     304            0 :     *shift = (*shift + key_size) % 8;
     305            0 :     return key;
     306            0 : }
     307              : 
     308              : /* Compute output index of y-th input line, in frame of height h. */
     309              : static int
     310            0 : interlaced_line_index(int h, int y)
     311              : {
     312              :     int p; /* number of lines in current pass */
     313              : 
     314            0 :     p = (h - 1) / 8 + 1;
     315            0 :     if (y < p) /* pass 1 */
     316            0 :         return y * 8;
     317            0 :     y -= p;
     318            0 :     p = (h - 5) / 8 + 1;
     319            0 :     if (y < p) /* pass 2 */
     320            0 :         return y * 8 + 4;
     321            0 :     y -= p;
     322            0 :     p = (h - 3) / 4 + 1;
     323            0 :     if (y < p) /* pass 3 */
     324            0 :         return y * 4 + 2;
     325            0 :     y -= p;
     326              :     /* pass 4 */
     327            0 :     return y * 2 + 1;
     328            0 : }
     329              : 
     330              : /* Decompress image pixels.
     331              :  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
     332              : static int
     333            1 : read_image_data(gd_GIF *gif, int interlace)
     334              : {
     335              :     uint8_t sub_len, shift, byte;
     336              :     int init_key_size, key_size, table_is_full;
     337              :     int frm_off, frm_size, str_len, i, p, x, y;
     338              :     uint16_t key, clear, stop;
     339              :     int ret;
     340              :     Table *table;
     341              :     Entry entry;
     342              :     off_t start, end;
     343              : 
     344            1 :     read(gif->fd, &byte, 1);
     345            1 :     key_size = (int) byte;
     346            1 :     if (key_size < 2 || key_size > 8)
     347            1 :         return -1;
     348              :     
     349            0 :     start = lseek(gif->fd, 0, SEEK_CUR);
     350            0 :     discard_sub_blocks(gif);
     351            0 :     end = lseek(gif->fd, 0, SEEK_CUR);
     352            0 :     lseek(gif->fd, start, SEEK_SET);
     353            0 :     clear = 1 << key_size;
     354            0 :     stop = clear + 1;
     355            0 :     table = new_table(key_size);
     356            0 :     key_size++;
     357            0 :     init_key_size = key_size;
     358            0 :     sub_len = shift = 0;
     359            0 :     key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
     360            0 :     frm_off = 0;
     361            0 :     ret = 0;
     362            0 :     frm_size = gif->fw*gif->fh;
     363            0 :     while (frm_off < frm_size) {
     364            0 :         if (key == clear) {
     365            0 :             key_size = init_key_size;
     366            0 :             table->nentries = (1 << (key_size - 1)) + 2;
     367            0 :             table_is_full = 0;
     368            0 :         } else if (!table_is_full) {
     369            0 :             ret = add_entry(&table, str_len + 1, key, entry.suffix);
     370            0 :             if (ret == -1) {
     371            0 :                 free(table);
     372            0 :                 return -1;
     373              :             }
     374            0 :             if (table->nentries == 0x1000) {
     375            0 :                 ret = 0;
     376            0 :                 table_is_full = 1;
     377            0 :             }
     378            0 :         }
     379            0 :         key = get_key(gif, key_size, &sub_len, &shift, &byte);
     380            0 :         if (key == clear) continue;
     381            0 :         if (key == stop || key == 0x1000) break;
     382            0 :         if (ret == 1) key_size++;
     383            0 :         entry = table->entries[key];
     384            0 :         str_len = entry.length;
     385            0 :         for (i = 0; i < str_len; i++) {
     386            0 :             p = frm_off + entry.length - 1;
     387            0 :             x = p % gif->fw;
     388            0 :             y = p / gif->fw;
     389            0 :             if (interlace)
     390            0 :                 y = interlaced_line_index((int) gif->fh, y);
     391            0 :             gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
     392            0 :             if (entry.prefix == 0xFFF)
     393            0 :                 break;
     394              :             else
     395            0 :                 entry = table->entries[entry.prefix];
     396            0 :         }
     397            0 :         frm_off += str_len;
     398            0 :         if (key < table->nentries - 1 && !table_is_full)
     399            0 :             table->entries[table->nentries - 1].suffix = entry.suffix;
     400              :     }
     401            0 :     free(table);
     402            0 :     if (key == stop)
     403            0 :         read(gif->fd, &sub_len, 1); /* Must be zero! */
     404            0 :     lseek(gif->fd, end, SEEK_SET);
     405            0 :     return 0;
     406            1 : }
     407              : 
     408              : /* Read image.
     409              :  * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
     410              : static int
     411            3 : read_image(gd_GIF *gif)
     412              : {
     413              :     uint8_t fisrz;
     414              :     int interlace;
     415              : 
     416              :     /* Image Descriptor. */
     417            3 :     gif->fx = read_num(gif->fd);
     418            3 :     gif->fy = read_num(gif->fd);
     419              :     
     420            3 :     if (gif->fx >= gif->width || gif->fy >= gif->height)
     421            2 :         return -1;
     422              :     
     423            1 :     gif->fw = read_num(gif->fd);
     424            1 :     gif->fh = read_num(gif->fd);
     425              :     
     426            1 :     gif->fw = MIN(gif->fw, gif->width - gif->fx);
     427            1 :     gif->fh = MIN(gif->fh, gif->height - gif->fy);
     428              :     
     429            1 :     read(gif->fd, &fisrz, 1);
     430            1 :     interlace = fisrz & 0x40;
     431              :     /* Ignore Sort Flag. */
     432              :     /* Local Color Table? */
     433            1 :     if (fisrz & 0x80) {
     434              :         /* Read LCT */
     435            1 :         gif->lct.size = 1 << ((fisrz & 0x07) + 1);
     436            1 :         read(gif->fd, gif->lct.colors, 3 * gif->lct.size);
     437            1 :         gif->palette = &gif->lct;
     438            1 :     } else
     439            0 :         gif->palette = &gif->gct;
     440              :     /* Image Data. */
     441            1 :     return read_image_data(gif, interlace);
     442            3 : }
     443              : 
     444              : static void
     445          516 : render_frame_rect(gd_GIF *gif, uint8_t *buffer)
     446              : {
     447              :     int i, j, k;
     448              :     uint8_t index, *color;
     449          516 :     i = gif->fy * gif->width + gif->fx;
     450          516 :     for (j = 0; j < gif->fh; j++) {
     451            0 :         for (k = 0; k < gif->fw; k++) {
     452            0 :             index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
     453            0 :             color = &gif->palette->colors[index*3];
     454            0 :             if (!gif->gce.transparency || index != gif->gce.tindex)
     455            0 :                 memcpy(&buffer[(i+k)*3], color, 3);
     456            0 :         }
     457            0 :         i += gif->width;
     458            0 :     }
     459          516 : }
     460              : 
     461              : static void
     462          514 : dispose(gd_GIF *gif)
     463              : {
     464              :     int i, j, k;
     465              :     uint8_t *bgcolor;
     466          514 :     switch (gif->gce.disposal) {
     467              :     case 2: /* Restore to background color. */
     468            0 :         bgcolor = &gif->palette->colors[gif->bgindex*3];
     469            0 :         i = gif->fy * gif->width + gif->fx;
     470            0 :         for (j = 0; j < gif->fh; j++) {
     471            0 :             for (k = 0; k < gif->fw; k++)
     472            0 :                 memcpy(&gif->canvas[(i+k)*3], bgcolor, 3);
     473            0 :             i += gif->width;
     474            0 :         }
     475            0 :         break;
     476              :     case 3: /* Restore to previous, i.e., don't update canvas.*/
     477            0 :         break;
     478              :     default:
     479              :         /* Add frame non-transparent pixels to canvas. */
     480          514 :         render_frame_rect(gif, gif->canvas);
     481          514 :     }
     482          514 : }
     483              : 
     484              : /* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
     485              : int
     486          514 : gd_get_frame(gd_GIF *gif)
     487              : {
     488              :     char sep;
     489              : 
     490          514 :     dispose(gif);
     491          514 :     read(gif->fd, &sep, 1);
     492          515 :     while (sep != ',') {
     493          512 :         if (sep == ';')
     494            2 :             return 0;
     495          510 :         if (sep == '!')
     496            1 :             read_ext(gif);
     497          509 :         else return -1;
     498            1 :         read(gif->fd, &sep, 1);
     499              :     }
     500            3 :     if (read_image(gif) == -1)
     501            3 :         return -1;
     502            0 :     return 1;
     503          514 : }
     504              : 
     505              : void
     506            2 : gd_render_frame(gd_GIF *gif, uint8_t *buffer)
     507              : {
     508            2 :     memcpy(buffer, gif->canvas, gif->width * gif->height * 3);
     509            2 :     render_frame_rect(gif, buffer);
     510            2 : }
     511              : 
     512              : int
     513       116199 : gd_is_bgcolor(gd_GIF *gif, uint8_t color[3])
     514              : {
     515       116199 :     return !memcmp(&gif->palette->colors[gif->bgindex*3], color, 3);
     516              : }
     517              : 
     518              : void
     519            2 : gd_rewind(gd_GIF *gif)
     520              : {
     521            2 :     lseek(gif->fd, gif->anim_start, SEEK_SET);
     522            2 : }
     523              : 
     524              : void
     525          514 : gd_close_gif(gd_GIF *gif)
     526              : {
     527          514 :     close(gif->fd);
     528          514 :     free(gif->frame);    
     529          514 :     free(gif);
     530          514 : }
        

Generated by: LCOV version 2.0-1