2017-11-20 13:59:06 (UTC-02:00)
Marcel Rodrigues <marcelgmr@gmail.com>
Implement RGB canvas framework. Definitions: palette: array of unique 24-bit RGB colors LCT: local color table (palette with 1-frame scope) GCT: global color table (palette apllied to all frames lacking LCT) frame: image array of 8-bit indices into palette entries canvas: image array of 24-bit colors Initially, gifdec only stored the GCT, 1 LCT and 1 frame. In order to implement the disposal method "restore to previous" (RTP), a "back frame" was added. However, the back frame only stored color indices, and those indices could be referencing previous LCTs. With this commit, gifdec will store GCT, 1 LCT, 1 frame and 1 canvas. Every frame that isn't marked for RTP is rendered to the canvas after it has been used, apllying the current valid palette -- whether it's GCT or the frame's LCT. This way, the canvas will store pixel colors that could otherwise be lost with the old approach.
diff --git a/gifdec.c b/gifdec.c index fe7bb8a..f207b27 100644 --- a/gifdec.c +++ b/gifdec.c @@ -80,7 +80,7 @@ gd_open_gif(const char *fname) /* Aspect Ratio */ read(fd, &aspect, 1); /* Create gd_GIF Structure. */ - gif = calloc(1, sizeof(*gif) + 2 * width * height); + gif = calloc(1, sizeof(*gif) + 4 * width * height); if (!gif) goto fail; gif->fd = fd; gif->width = width; @@ -89,11 +89,11 @@ gd_open_gif(const char *fname) gif->gct.size = gct_sz; read(fd, gif->gct.colors, 3 * gif->gct.size); gif->palette = &gif->gct; - gif->bgcolor = bgidx; - gif->frame = (uint8_t *) &gif[1]; - gif->back = &gif->frame[gif->width * gif->height]; - if (gif->bgcolor) - memset(gif->frame, gif->bgcolor, gif->width * gif->height); + gif->bgindex = bgidx; + gif->canvas = (uint8_t *) &gif[1]; + gif->frame = &gif->canvas[3 * width * height]; + if (gif->bgindex) + memset(gif->frame, gif->bgindex, gif->width * gif->height); goto ok; fail: close(fd); @@ -288,7 +288,7 @@ get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *by /* Decompress image pixels. * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */ static int -read_image_data(gd_GIF *gif, uint16_t x, uint16_t y, uint16_t w) +read_image_data(gd_GIF *gif) { uint8_t sub_len, shift, byte; int init_key_size, key_size, table_is_full; @@ -333,8 +333,7 @@ read_image_data(gd_GIF *gif, uint16_t x, uint16_t y, uint16_t w) str_len = entry.length; while (1) { p = frm_off + entry.length - 1; - if (!gif->gce.transparency || entry.suffix != gif->gce.tindex) - gif->frame[(y + p / w) * gif->width + x + p % w] = entry.suffix; + gif->frame[(gif->fy + p / gif->fw) * gif->width + gif->fx + p % gif->fw] = entry.suffix; if (entry.prefix == 0xFFF) break; else @@ -354,15 +353,14 @@ read_image_data(gd_GIF *gif, uint16_t x, uint16_t y, uint16_t w) static int read_image(gd_GIF *gif) { - uint16_t x, y, w, h, i, j; uint8_t fisrz; int interlace; /* Image Descriptor. */ - x = read_num(gif->fd); - y = read_num(gif->fd); - w = read_num(gif->fd); - h = read_num(gif->fd); + gif->fx = read_num(gif->fd); + gif->fy = read_num(gif->fd); + gif->fw = read_num(gif->fd); + gif->fh = read_num(gif->fd); read(gif->fd, &fisrz, 1); interlace = fisrz & 0x40; /* Ignore Sort Flag. */ @@ -374,33 +372,41 @@ read_image(gd_GIF *gif) gif->palette = &gif->lct; } else gif->palette = &gif->gct; - if (gif->prev_disposal == 3) { - j = gif->prev_y * gif->width + gif->prev_x; - for (i = 0; i < gif->prev_h; i++) { - memcpy(&gif->frame[j], &gif->back[j], gif->prev_w); - j += gif->width; - } - } else if (gif->prev_disposal == 2) { - j = gif->prev_y * gif->width + gif->prev_x; - for (i = 0; i < gif->prev_h; i++) { - memset(&gif->frame[j], gif->bgcolor, gif->prev_w); - j += gif->width; + /* Image Data. */ + return read_image_data(gif); +} + +void +dispose(gd_GIF *gif) +{ + int i, j, k; + uint8_t index, *color; + switch (gif->gce.disposal) { + case 2: /* Restore to background color. */ + color = &gif->palette->colors[gif->bgindex*3]; + i = (gif->fy * gif->width + gif->fx) * 3; + for (j = 0; j < gif->fh; j++) { + for (k = 0; k < gif->fw; k++) { + memcpy(&gif->canvas[i+k*3], color, 3); + } + i += gif->width * 3; } - } - if (gif->gce.disposal == 3) { - j = y * gif->width + x; - for (i = 0; i < h; i++) { - memcpy(&gif->back[j], &gif->frame[j], w); - j += gif->width; + break; + case 3: /* Restore to previous, i.e., don't update canvas.*/ + break; + default: + /* Add frame non-transparent pixels to canvas. */ + i = (gif->fy * gif->width + gif->fx) * 3; + for (j = 0; j < gif->fh; j++) { + for (k = 0; k < gif->fw; k++) { + index = gif->frame[j * gif->fw + k]; + color = &gif->palette->colors[index]; + if (!gif->gce.transparency || index != gif->gce.tindex) + memcpy(&gif->canvas[i+k*3], color, 3); + } + i += gif->width * 3; } - gif->prev_x = x; - gif->prev_y = y; - gif->prev_w = w; - gif->prev_h = h; } - gif->prev_disposal = gif->gce.disposal; - /* Image Data. */ - return read_image_data(gif, x, y, w); } /* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */ @@ -409,6 +415,7 @@ gd_get_frame(gd_GIF *gif) { char sep; + dispose(gif); read(gif->fd, &sep, 1); while (sep != ',') { if (sep == ';') diff --git a/gifdec.h b/gifdec.h index 44b1883..68e86f9 100644 --- a/gifdec.h +++ b/gifdec.h @@ -30,10 +30,9 @@ typedef struct gd_GIF { ); void (*comment)(struct gd_GIF *gif); void (*application)(struct gd_GIF *gif, char id[8], char auth[3]); - uint16_t prev_x, prev_y, prev_w, prev_h; - uint8_t prev_disposal; - uint8_t bgcolor; - uint8_t *frame, *back; + uint16_t fx, fy, fw, fh; + uint8_t bgindex; + uint8_t *canvas, *frame; } gd_GIF; gd_GIF *gd_open_gif(const char *fname);