update libnsgif from git master

though unfortunately libnsgif seems to have broken DISPOSE_PREVIOUS
handling again
This commit is contained in:
John Cupitt 2021-02-28 14:29:27 +00:00
parent 48d9c5d0d4
commit 68332663fb
6 changed files with 152 additions and 30 deletions

View File

@ -1,6 +1,8 @@
EXTRA_DIST = \
README-ns \
README.md \
patches \
update.sh \
utils
noinst_LTLIBRARIES = libnsgif.la

View File

@ -1,18 +1,16 @@
# libnsgif
This is [libnsgif](https://www.netsurf-browser.org/projects/libnsgif/),
but within the libviops build system.
Based on libnsgif-0.2.1, with one patch to prevent it modifying the input
buffer on error.
but within the libvips build system.
# To update
When netsurf release a new version:
Run `./update.sh` to update this copy of libnsgif from the upstream repo. It
will also patch libnsgif.c to prevent it modifying the input.
* copy in sources
* reapply any patches from git, eg. no input modification
Last updated 28 Feb 2021.
# To do
No attempt made to run tests or build docs.
No attempt made to run tests or build docs. Though the gif loader is tested as
part of the libvips test suite.

View File

@ -440,7 +440,7 @@ static gif_result gif_initialise_frame(gif_animation *gif)
* There was code here to set a TRAILER tag. But this
* wrote to the input buffer, which will not work for
* libvips, where buffers can be mmaped read only files.
*
*
* Instead, just signal insufficient frame data.
*/
return GIF_INSUFFICIENT_FRAME_DATA;
@ -565,6 +565,73 @@ static gif_result gif_error_from_lzw(lzw_result l_res)
return g_res[l_res];
}
static void gif__record_previous_frame(gif_animation *gif)
{
bool need_alloc = gif->prev_frame == NULL;
const uint32_t *frame_data;
uint32_t *prev_frame;
if (gif->decoded_frame == GIF_INVALID_FRAME ||
gif->decoded_frame == gif->prev_index) {
/* No frame to copy, or already have this frame recorded. */
return;
}
assert(gif->bitmap_callbacks.bitmap_get_buffer);
frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
if (!frame_data) {
return;
}
if (gif->prev_frame != NULL &&
gif->width * gif->height > gif->prev_width * gif->prev_height) {
need_alloc = true;
}
if (need_alloc) {
prev_frame = realloc(gif->prev_frame,
gif->width * gif->height * 4);
if (prev_frame == NULL) {
return;
}
} else {
prev_frame = gif->prev_frame;
}
memcpy(prev_frame, frame_data, gif->width * gif->height * 4);
gif->prev_frame = prev_frame;
gif->prev_width = gif->width;
gif->prev_height = gif->height;
gif->prev_index = gif->decoded_frame;
}
static gif_result gif__recover_previous_frame(const gif_animation *gif)
{
const uint32_t *prev_frame = gif->prev_frame;
unsigned height = gif->height < gif->prev_height ? gif->height : gif->prev_height;
unsigned width = gif->width < gif->prev_width ? gif->width : gif->prev_width;
uint32_t *frame_data;
if (prev_frame == NULL) {
return GIF_FRAME_DATA_ERROR;
}
assert(gif->bitmap_callbacks.bitmap_get_buffer);
frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
if (!frame_data) {
return GIF_INSUFFICIENT_MEMORY;
}
for (unsigned y = 0; y < height; y++) {
memcpy(frame_data, prev_frame, width * 4);
frame_data += gif->width;
prev_frame += gif->prev_width;
}
return GIF_OK;
}
/**
* decode a gif frame
@ -578,6 +645,7 @@ gif_internal_decode_frame(gif_animation *gif,
unsigned int frame,
bool clear_image)
{
gif_result err;
unsigned int index = 0;
unsigned char *gif_data, *gif_end;
int gif_bytes;
@ -607,6 +675,11 @@ gif_internal_decode_frame(gif_animation *gif,
return GIF_OK;
}
if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) {
/* Store the previous frame for later restoration */
gif__record_previous_frame(gif);
}
/* Get the start of our frame data and the end of the GIF data */
gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
gif_end = gif->gif_data + gif->buffer_size;
@ -783,34 +856,16 @@ gif_internal_decode_frame(gif_animation *gif,
(gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
/*
* If the previous frame's disposal method requires we
* restore the previous image, find the last image set
* to "do not dispose" and get that frame data
* restore the previous image, restore our saved image.
*/
int last_undisposed_frame = frame - 2;
while ((last_undisposed_frame >= 0) &&
(gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) {
last_undisposed_frame--;
}
/* If we don't find one, clear the frame data */
if (last_undisposed_frame == -1) {
err = gif__recover_previous_frame(gif);
if (err != GIF_OK) {
/* see notes above on transparency
* vs. background color
*/
memset((char*)frame_data,
GIF_TRANSPARENT_COLOUR,
gif->width * gif->height * sizeof(int));
} else {
return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false);
if (return_value != GIF_OK) {
goto gif_decode_frame_exit;
}
/* Get this frame's data */
assert(gif->bitmap_callbacks.bitmap_get_buffer);
frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
if (!frame_data) {
return GIF_INSUFFICIENT_MEMORY;
}
}
}
gif->decoded_frame = frame;
@ -918,6 +973,7 @@ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
memset(gif, 0, sizeof(gif_animation));
gif->bitmap_callbacks = *bitmap_callbacks;
gif->decoded_frame = GIF_INVALID_FRAME;
gif->prev_index = GIF_INVALID_FRAME;
}
@ -1159,6 +1215,9 @@ void gif_finalise(gif_animation *gif)
free(gif->global_colour_table);
gif->global_colour_table = NULL;
free(gif->prev_frame);
gif->prev_frame = NULL;
lzw_context_destroy(gif->lzw_ctx);
gif->lzw_ctx = NULL;
}

View File

@ -136,6 +136,15 @@ typedef struct gif_animation {
unsigned int *global_colour_table;
/** local colour table */
unsigned int *local_colour_table;
/** previous frame for GIF_FRAME_RESTORE */
void *prev_frame;
/** previous frame index */
int prev_index;
/** previous frame width */
unsigned prev_width;
/** previous frame height */
unsigned prev_height;
} gif_animation;
/**

View File

@ -0,0 +1,32 @@
--- libnsgif-orig.c 2021-02-28 14:10:41.818557190 +0000
+++ libnsgif.c 2021-02-28 14:11:55.942285930 +0000
@@ -435,20 +435,15 @@
block_size = gif_data[0] + 1;
/* Check if the frame data runs off the end of the file */
if ((int)(gif_bytes - block_size) < 0) {
- /* Try to recover by signaling the end of the gif.
- * Once we get garbage data, there is no logical way to
- * determine where the next frame is. It's probably
- * better to partially load the gif than not at all.
- */
- if (gif_bytes >= 2) {
- gif_data[0] = 0;
- gif_data[1] = GIF_TRAILER;
- gif_bytes = 1;
- ++gif_data;
- break;
- } else {
- return GIF_INSUFFICIENT_FRAME_DATA;
- }
+ /* jcupitt 15/9/19
+ *
+ * There was code here to set a TRAILER tag. But this
+ * wrote to the input buffer, which will not work for
+ * libvips, where buffers can be mmaped read only files.
+ *
+ * Instead, just signal insufficient frame data.
+ */
+ return GIF_INSUFFICIENT_FRAME_DATA;
} else {
gif_bytes -= block_size;
gif_data += block_size;

View File

@ -0,0 +1,22 @@
#!/bin/bash
# attempt to update our copy of libnsgif from the upstream repo
set -e
git clone git://git.netsurf-browser.org/libnsgif.git
echo copying out source files ...
cp libnsgif/src/libnsgif.c .
cp libnsgif/include/libnsgif.h .
cp libnsgif/src/lzw.[ch] .
cp libnsgif/src/utils/log.h utils
echo applying patches ...
for patch in patches/*.patch; do
patch -p0 <$patch
done
echo cleaning up ...
rm -rf libnsgif