LCOV - code coverage report
Current view: top level - gifdec - gifdec.c (source / functions) Coverage Total Hit
Test: gifread.info Lines: 92.4 % 317 293
Test Date: 2024-03-08 00:28:24 Functions: 100.0 % 21 21

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

Generated by: LCOV version 2.0-1