diff options
author | Michael Drake <tlsa@netsurf-browser.org> | 2022-02-26 14:40:35 +0000 |
---|---|---|
committer | Michael Drake <tlsa@netsurf-browser.org> | 2022-02-26 14:40:35 +0000 |
commit | 370c2a783b67350143c18a07463835887d2b1847 (patch) | |
tree | 10ac9c1104edd13b986b7c59ab51c767c2f86ed7 | |
parent | 84a9edb126121cd145295e48a5f0a98de4aab708 (diff) | |
download | libnsgif-370c2a783b67350143c18a07463835887d2b1847.tar.gz libnsgif-370c2a783b67350143c18a07463835887d2b1847.tar.bz2 |
API: Add public access function for frame information.
-rw-r--r-- | include/nsgif.h | 126 | ||||
-rw-r--r-- | src/gif.c | 121 |
2 files changed, 160 insertions, 87 deletions
diff --git a/include/nsgif.h b/include/nsgif.h index 550fea3..b36af8a 100644 --- a/include/nsgif.h +++ b/include/nsgif.h @@ -20,22 +20,29 @@ #include <stdbool.h> #include <inttypes.h> +/** Representation of infinity. */ #define NSGIF_INFINITE (UINT32_MAX) typedef struct nsgif nsgif; -typedef struct nsgif_info { - /** width of GIF (may increase during decoding) */ - uint32_t width; - /** height of GIF (may increase during decoding) */ - uint32_t height; - /** number of frames decoded */ - uint32_t frame_count; - /** number of times to loop animation */ - int loop_max; - /** number of animation loops so far */ - int loop_count; -} nsgif_info_t; +/** + * GIF rectangle structure. + * + * * Top left coordinate is `(x0, y0)`. + * * Width is `x1 - x0`. + * * Height is `y1 - y0`. + * * Units are pixels. + */ +typedef struct nsgif_rect { + /** x co-ordinate of redraw rectangle, left */ + uint32_t x0; + /** y co-ordinate of redraw rectangle, top */ + uint32_t y0; + /** x co-ordinate of redraw rectangle, right */ + uint32_t x1; + /** y co-ordinate of redraw rectangle, bottom */ + uint32_t y1; +} nsgif_rect; /** * NSGIF return codes. @@ -94,25 +101,6 @@ typedef enum { } nsgif_error; /** - * GIF rectangle structure. - * - * * Top left coordinate is `(x0, y0)`. - * * Width is `x1 - x0`. - * * Height is `y1 - y0`. - * * Units are pixels. - */ -typedef struct nsgif_rect { - /** x co-ordinate of redraw rectangle, left */ - uint32_t x0; - /** y co-ordinate of redraw rectangle, top */ - uint32_t y0; - /** x co-ordinate of redraw rectangle, right */ - uint32_t x1; - /** y co-ordinate of redraw rectangle, bottom */ - uint32_t y1; -} nsgif_rect; - -/** * Client bitmap type. * * These are client-created and destroyed, via the \ref bitmap callbacks, @@ -193,6 +181,13 @@ const char *nsgif_strerror(nsgif_error err); nsgif_error nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif **gif_out); /** + * Free a NSGIF object. + * + * \param[in] gif The NSGIF to free. + */ +void nsgif_destroy(nsgif *gif); + +/** * Scan the source image data. * * This is used to feed the source data into LibNSGIF. This must be called @@ -224,6 +219,10 @@ nsgif_error nsgif_data_scan( /** * Prepare to show a frame. * + * If this is the last frame of an animation with a finite loop count, the + * returned `delay_cs` will be \ref NSGIF_INFINITE, indicating that the frame + * should be shown forever. + * * \param[in] gif The NSGIF object. * \param[out] area The area in pixels that must be redrawn. * \param[out] delay_cs Time to wait after frame_new before next frame in cs. @@ -268,6 +267,60 @@ nsgif_error nsgif_reset( nsgif *gif); /** + * Information about a GIF. + */ +typedef struct nsgif_info { + /** width of GIF (may increase during decoding) */ + uint32_t width; + /** height of GIF (may increase during decoding) */ + uint32_t height; + /** number of frames decoded */ + uint32_t frame_count; + /** number of times to loop animation */ + int loop_max; + /** number of animation loops so far */ + int loop_count; +} nsgif_info_t; + +/** + * Frame disposal method. + * + * Clients do not need to know about this, it is provided purely for dumping + * raw information about GIF frames. + */ +enum nsgif_disposal { + NSGIF_DISPOSAL_UNSPECIFIED, /**< No disposal method specified. */ + NSGIF_DISPOSAL_NONE, /**< Frame remains. */ + NSGIF_DISPOSAL_RESTORE_BG, /**< Clear frame to background colour. */ + NSGIF_DISPOSAL_RESTORE_PREV, /**< Restore previous frame. */ + NSGIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for NSGIF_DISPOSAL_RESTORE_PREV. */ +}; + +/** + * Convert a disposal method to a string. + * + * \param[in] disposal The disposal method to convert. + * \return String representation of given disposal method. + */ +const char *nsgif_str_disposal(enum nsgif_disposal disposal); + +/** + * Information about a GIF frame. + */ +typedef struct nsgif_frame_info { + /** whether the frame should be displayed/animated */ + bool display; + + /** Disposal method for previous frame; affects plotting */ + uint8_t disposal; + /** delay (in cs) before animating the frame */ + uint32_t delay; + + /** Frame's redraw rectangle. */ + nsgif_rect rect; +} nsgif_frame_info_t; + +/** * Get information about a GIF from an NSGIF object. * * \param[in] gif The NSGIF object to get info for. @@ -277,10 +330,15 @@ nsgif_error nsgif_reset( const nsgif_info_t *nsgif_get_info(const nsgif *gif); /** - * Free a NSGIF object. + * Get information about a GIF from an NSGIF object. * - * \param[in] gif The NSGIF to free. + * \param[in] gif The NSGIF object to get frame info for. + * \param[in] frame The frame number to get info for. + * + * \return The gif frame info, or NULL on error. */ -void nsgif_destroy(nsgif *gif); +const nsgif_frame_info_t *nsgif_get_frame_info( + const nsgif *gif, + uint32_t frame); #endif @@ -22,12 +22,7 @@ /** GIF frame data */ typedef struct nsgif_frame { - /** whether the frame should be displayed/animated */ - bool display; - /** delay (in cs) before animating the frame */ - uint32_t frame_delay; - - /* Internal members are listed below */ + struct nsgif_frame_info info; /** offset (in bytes) to the GIF frame data */ uint32_t frame_pointer; @@ -37,17 +32,14 @@ typedef struct nsgif_frame { bool opaque; /** whether a full image redraw is required */ bool redraw_required; - /** how the previous frame should be disposed; affects plotting */ - uint8_t disposal_method; + /** whether we acknowledge transparency */ bool transparency; /** the index designating a transparent pixel */ uint32_t transparency_index; + /* Frame flags */ uint32_t flags; - - /** Frame's redraw rectangle. */ - nsgif_rect redraw; } nsgif_frame; /** GIF animation data */ @@ -137,14 +129,6 @@ struct nsgif { /** No transparency */ #define NSGIF_NO_TRANSPARENCY (0xFFFFFFFFu) -enum nsgif_disposal { - NSGIF_DISPOSAL_UNSPECIFIED, - NSGIF_DISPOSAL_NONE, - NSGIF_DISPOSAL_RESTORE_BG, - NSGIF_DISPOSAL_RESTORE_PREV, - NSGIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for NSGIF_DISPOSAL_RESTORE_PREV. */ -}; - /* GIF Flags */ #define NSGIF_COLOUR_TABLE_MASK 0x80 #define NSGIF_COLOUR_TABLE_SIZE_MASK 0x07 @@ -578,10 +562,10 @@ static inline nsgif_error nsgif__decode( }; nsgif_error ret; - uint32_t width = frame->redraw.x1 - frame->redraw.x0; - uint32_t height = frame->redraw.y1 - frame->redraw.y0; - uint32_t offset_x = frame->redraw.x0; - uint32_t offset_y = frame->redraw.y0; + uint32_t width = frame->info.rect.x1 - frame->info.rect.x0; + uint32_t height = frame->info.rect.y1 - frame->info.rect.y0; + uint32_t offset_x = frame->info.rect.x0; + uint32_t offset_y = frame->info.rect.y0; uint32_t interlace = frame->flags & GIF_MASK_INTERLACE; uint32_t transparency_index = frame->transparency_index; uint32_t *restrict colour_table = gif->colour_table; @@ -616,15 +600,15 @@ static void nsgif__restore_bg( memset(bitmap, NSGIF_TRANSPARENT_COLOUR, gif->info.width * gif->info.height * sizeof(*bitmap)); } else { - uint32_t width = frame->redraw.x1 - frame->redraw.x0; - uint32_t height = frame->redraw.y1 - frame->redraw.y0; - uint32_t offset_x = frame->redraw.x0; - uint32_t offset_y = frame->redraw.y0; + uint32_t width = frame->info.rect.x1 - frame->info.rect.x0; + uint32_t height = frame->info.rect.y1 - frame->info.rect.y0; + uint32_t offset_x = frame->info.rect.x0; + uint32_t offset_y = frame->info.rect.y0; width -= gif__clip(offset_x, width, gif->info.width); height -= gif__clip(offset_y, height, gif->info.height); - if (frame->display == false || width == 0) { + if (frame->info.display == false || width == 0) { return; } @@ -671,10 +655,10 @@ static nsgif_error nsgif__update_bitmap( } else { struct nsgif_frame *prev = &gif->frames[frame_idx - 1]; - if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_BG) { + if (prev->info.disposal == NSGIF_DISPOSAL_RESTORE_BG) { nsgif__restore_bg(gif, prev, bitmap); - } else if (prev->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) { + } else if (prev->info.disposal == NSGIF_DISPOSAL_RESTORE_PREV) { ret = nsgif__recover_frame(gif, bitmap); if (ret != NSGIF_OK) { nsgif__restore_bg(gif, prev, bitmap); @@ -682,7 +666,7 @@ static nsgif_error nsgif__update_bitmap( } } - if (frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV) { + if (frame->info.disposal == NSGIF_DISPOSAL_RESTORE_PREV) { /* Store the previous frame for later restoration */ nsgif__record_frame(gif, bitmap); } @@ -737,9 +721,9 @@ static nsgif_error nsgif__parse_extension_graphic_control( return NSGIF_ERR_END_OF_DATA; } - frame->frame_delay = data[3] | (data[4] << 8); - if (frame->frame_delay < gif->delay_min) { - frame->frame_delay = gif->delay_default; + frame->info.delay = data[3] | (data[4] << 8); + if (frame->info.delay < gif->delay_min) { + frame->info.delay = gif->delay_default; } if (data[2] & GIF_MASK_TRANSPARENCY) { @@ -747,22 +731,22 @@ static nsgif_error nsgif__parse_extension_graphic_control( frame->transparency_index = data[5]; } - frame->disposal_method = ((data[2] & GIF_MASK_DISPOSAL) >> 2); + frame->info.disposal = ((data[2] & GIF_MASK_DISPOSAL) >> 2); /* I have encountered documentation and GIFs in the * wild that use 0x04 to restore the previous frame, * rather than the officially documented 0x03. I * believe some (older?) software may even actually * export this way. We handle this as a type of * "quirks" mode. */ - if (frame->disposal_method == NSGIF_DISPOSAL_RESTORE_QUIRK) { - frame->disposal_method = NSGIF_DISPOSAL_RESTORE_PREV; + if (frame->info.disposal == NSGIF_DISPOSAL_RESTORE_QUIRK) { + frame->info.disposal = NSGIF_DISPOSAL_RESTORE_PREV; } /* if we are clearing the background then we need to * redraw enough to cover the previous frame too. */ frame->redraw_required = - frame->disposal_method == NSGIF_DISPOSAL_RESTORE_BG || - frame->disposal_method == NSGIF_DISPOSAL_RESTORE_PREV; + frame->info.disposal == NSGIF_DISPOSAL_RESTORE_BG || + frame->info.disposal == NSGIF_DISPOSAL_RESTORE_PREV; return NSGIF_OK; } @@ -964,10 +948,10 @@ static nsgif_error nsgif__parse_image_descriptor( h = data[7] | (data[8] << 8); frame->flags = data[9]; - frame->redraw.x0 = x; - frame->redraw.y0 = y; - frame->redraw.x1 = x + w; - frame->redraw.y1 = y + h; + frame->info.rect.x0 = x; + frame->info.rect.y0 = y; + frame->info.rect.x1 = x + w; + frame->info.rect.y1 = y + h; /* Allow first frame to grow image dimensions. */ if (gif->info.frame_count == 0) { @@ -1143,7 +1127,7 @@ static nsgif_error nsgif__parse_image_data( *pos = data; gif->info.frame_count = frame_idx + 1; - gif->frames[frame_idx].display = true; + gif->frames[frame_idx].info.display = true; return NSGIF_OK; } @@ -1177,9 +1161,9 @@ static struct nsgif_frame *nsgif__get_frame( frame->transparency_index = NSGIF_NO_TRANSPARENCY; frame->frame_pointer = gif->buf_pos; frame->redraw_required = false; - frame->disposal_method = 0; - frame->frame_delay = 100; - frame->display = false; + frame->info.display = false; + frame->info.disposal = 0; + frame->info.delay = 10; frame->decoded = false; } @@ -1215,7 +1199,7 @@ static nsgif_error nsgif__process_frame( pos = gif->buf + frame->frame_pointer; /* Ensure this frame is supposed to be decoded */ - if (frame->display == false) { + if (frame->info.display == false) { return NSGIF_OK; } @@ -1592,10 +1576,10 @@ static nsgif_error nsgif__next_displayable_frame( } if (delay != NULL) { - *delay += gif->frames[next].frame_delay; + *delay += gif->frames[next].info.delay; } - } while (gif->frames[next].display == false); + } while (gif->frames[next].info.display == false); *frame = next; return NSGIF_OK; @@ -1637,8 +1621,8 @@ nsgif_error nsgif_frame_prepare( if (gif->frame != NSGIF_FRAME_INVALID && gif->frame < gif->info.frame_count && - gif->frames[gif->frame].display) { - rect = gif->frames[gif->frame].redraw; + gif->frames[gif->frame].info.display) { + rect = gif->frames[gif->frame].info.rect; } if (nsgif__animation_complete( @@ -1671,7 +1655,7 @@ nsgif_error nsgif_frame_prepare( } gif->frame = frame; - nsgif__redraw_rect_extend(&gif->frames[frame].redraw, &rect); + nsgif__redraw_rect_extend(&gif->frames[frame].info.rect, &rect); *frame_new = gif->frame; *delay_cs = delay; @@ -1717,12 +1701,25 @@ nsgif_error nsgif_frame_decode( return ret; } +/* exported function documented in nsgif.h */ const nsgif_info_t *nsgif_get_info(const nsgif *gif) { return &gif->info; } /* exported function documented in nsgif.h */ +const nsgif_frame_info_t *nsgif_get_frame_info( + const nsgif *gif, + uint32_t frame) +{ + if (frame > gif->info.frame_count) { + return NULL; + } + + return &gif->frames[frame].info; +} + +/* exported function documented in nsgif.h */ const char *nsgif_strerror(nsgif_error err) { static const char *const str[] = { @@ -1744,3 +1741,21 @@ const char *nsgif_strerror(nsgif_error err) return str[err]; } + +/* exported function documented in nsgif.h */ +const char *nsgif_str_disposal(enum nsgif_disposal disposal) +{ + static const char *const str[] = { + [NSGIF_DISPOSAL_UNSPECIFIED] = "Unspecified", + [NSGIF_DISPOSAL_NONE] = "None", + [NSGIF_DISPOSAL_RESTORE_BG] = "Restore background", + [NSGIF_DISPOSAL_RESTORE_PREV] = "Restore previous", + [NSGIF_DISPOSAL_RESTORE_QUIRK] = "Restore quirk", + }; + + if (disposal >= NSGIF_ARRAY_LEN(str) || str[disposal] == NULL) { + return "Unspecified"; + } + + return str[disposal]; +} |