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

Generated by: LCOV version 2.0-1