Update libnsgif (#2699)

* libnsgif: Rename upstream README with markdown extension.

* libnsgif: Drop default frame delay patch

* libnsgif: Remove unused utils / log.h header.

* libnsgif: Rename files to match upstream.

* libnsgif: Extend update script to update local README and test/

* libnsgif: Run update script.

* libnsgif: Update buildsystem for renamed files

* nsgifload: Switch to new library header.

* nsgifload: Remove optional and unused bitmap callbacks.

* nsgifload: Update to use new libnsgif API.
This commit is contained in:
Michael Drake 2022-03-03 10:02:36 +00:00 committed by GitHub
parent 716fbf75b1
commit 8e9fe72dea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 3550 additions and 2167 deletions

View File

@ -1,8 +1,8 @@
noinst_LTLIBRARIES = libnsgif.la
MY_SOURCES = \
libnsgif.h \
libnsgif.c \
nsgif.h \
gif.c \
lzw.c \
lzw.h
@ -11,11 +11,10 @@ libnsgif_la_SOURCES = $(MY_SOURCES)
endif
EXTRA_DIST = \
README-ns \
README-ns.md \
README.md \
patches \
update.sh \
utils
update.sh
if !ENABLE_NSGIF
EXTRA_DIST += \

View File

@ -1,36 +0,0 @@
libnsgif - Decoding GIF files
=============================
The functions provided by this library allow for efficient progressive
GIF decoding. Whilst the initialisation does not ensure that there is
sufficient image data to complete the entire frame, it does ensure
that the information provided is valid. Any subsequent attempts to
decode an initialised GIF are guaranteed to succeed, and any bytes of
the image not present are assumed to be totally transparent.
To begin decoding a GIF, the 'gif' structure must be initialised with
the 'gif_data' and 'buffer_size' set to their initial values. The
'buffer_position' should initially be 0, and will be internally
updated as the decoding commences. The caller should then repeatedly
call gif_initialise() with the structure until the function returns 1,
or no more data is avaliable.
Once the initialisation has begun, the decoder completes the variables
'frame_count' and 'frame_count_partial'. The former being the total
number of frames that have been successfully initialised, and the
latter being the number of frames that a partial amount of data is
available for. This assists the caller in managing the animation
whilst decoding is continuing.
To decode a frame, the caller must use gif_decode_frame() which
updates the current 'frame_image' to reflect the desired frame. The
required 'disposal_method' is also updated to reflect how the frame
should be plotted. The caller must not assume that the current
'frame_image' will be valid between calls if initialisation is still
occuring, and should either always request that the frame is decoded
(no processing will occur if the 'decoded_frame' has not been
invalidated by initialisation) or perform the check itself.
It should be noted that gif_finalise() should always be called, even
if no frames were initialised. Additionally, it is the responsibility
of the caller to free 'gif_data'.

View File

@ -0,0 +1,121 @@
LibNSGIF: NetSurf GIF decoder
=============================
LibNSGIF is a C library for decoding GIF format images and animations.
It is licenced under the MIT licence.
This library aims to provide a simple API for robust decoding of GIF files.
Details
-------
The GIF source data is scanned prior to decoding, allowing for efficient
decoding. The scanning phase will scan currently available data and will
resume from where it left off when called with additional data.
Only one frame is ever fully decoded to a bitmap at a time, reducing memory
usage for large GIFs.
Using
-----
LibNSGIF allows the client to allocate the bitmap into which the GIF is
decoded. The client can have an arbitrary bitmap structure, that is simply
a void pointer to LibNSGIF. The client must provide a callback table for
interacting with bitmaps. This table must include as a minimum functions to
create and destroy bitmaps, and a function to get a pointer to the bitmap's
pixel data buffer.
To load a GIF, first create an nsgif object with `nsgif_create()`.
```c
err = nsgif_create(&bitmap_callbacks, &gif);
if (err != NSGIF_OK) {
fprintf(stderr, "%s\n", nsgif_strerror(err));
// Handle error
}
```
Now you can load the GIF source data into the nsgif object with
`nsgif_data_scan()`:
```c
err = nsgif_data_scan(gif, size, data);
if (err != NSGIF_OK) {
fprintf(stderr, "%s\n", nsgif_strerror(err));
// Handle error
}
```
This scans the source data and decodes information about each frame, however
it doesn't decode any of the bitmap data for the frames. The client may call
`nsgif_data_scan()` multiple times as source data is fetched. Once the
function has returned `NSGIF_OK` it has enough data to display at least one
frame. The early frames can be decoded before the later frames are scanned.
Frames have to be scanned before they can be decoded.
> **Note**: The client must not free the data until after calling
> `nsgif_destroy()`. You can move the data, e.g. if you realloc to a bigger
> buffer. Just be sure to call `nsgif_data_scan()` again with the new pointer
> before making any other calls against that nsgif object.
To decode the frames, you can call `nsgif_get_info()` to get the frame_count,
and then call `nsgif_frame_decode()` for each frame, and manage the animation,
and non-displayable frames yourself, or you can use the helper function,
`nsgif_frame_prepare()`:
```c
err = nsgif_frame_prepare(gif, &area, &delay_cs, &frame_new);
if (err != NSGIF_OK) {
fprintf(stderr, "%s\n", nsgif_strerror(err));
// Handle error
}
// Update our bitmap to know it should be showing `frame_new` now.
// Trigger redraw of `area` of image.
if (delay_cs != NSGIF_INFINITE) {
// Schedule next frame in delay_cs.
}
```
This will return the number of the next frame to be decoded, the delay in cs
before the next frame should be decoded, and the area of the bitmap that needs
to be redrawn.
> **Note**: GIF frames may only occupy a portion of the overall bitmap, and only
> redrawing the area that has changed may be more efficient than redrawing the
> whole thing. The returned area comprises both any region that has been
> changed in the disposal of the previous frame and the new frame.
GIF files can limit the number of animation loops to a finite number or they
may only have one frame. In either of these cases, the returned delay is
`NSGIF_INFINITE` indicating that the animation is complete. Subsequent calls
to `nsgif_frame_prepare()` will return `NSGIF_ERR_ANIMATION_END`.
To force the repeat of an animation, call `nsgif_reset()`.
One reason for the two-step decoding of frames is that it enables deferred
decoding. You can call `nsgif_frame_prepare()` and cause a redraw of that
portion of your document. If the GIF is off screen (another tab, or scrolled
out of sight), there is no need to decode it at all.
Once the bitmap is needed for a redraw, you can decode the correct frame
on-demand with:
```c
err = nsgif_frame_decode(gif, frame_new, &bitmap);
if (err != NSGIF_OK) {
fprintf(stderr, "%s\n", nsgif_strerror(err));
// Handle error
}
```
Note that this will be a no-op if the requested frame already happens to be
the decoded frame.
Once you are done with the GIF, free up the nsgif object with:
```c
nsgif_destroy(gif);
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,194 +0,0 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
* Copyright 2013-2021 Michael Drake <tlsa@netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* \file
* Interface to progressive animated GIF file decoding.
*/
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
#include <stdint.h>
#include <stdbool.h>
/* Error return values */
typedef enum {
GIF_WORKING = 1,
GIF_OK = 0,
GIF_INSUFFICIENT_DATA = -1,
GIF_INSUFFICIENT_FRAME_DATA = GIF_INSUFFICIENT_DATA,
GIF_FRAME_DATA_ERROR = -2,
GIF_DATA_ERROR = -4,
GIF_INSUFFICIENT_MEMORY = -5,
GIF_FRAME_NO_DISPLAY = -6,
GIF_END_OF_FRAME = -7
} gif_result;
/** GIF frame data */
typedef struct gif_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 */
/** offset (in bytes) to the GIF frame data */
uint32_t frame_pointer;
/** whether the frame has previously been used */
bool virgin;
/** whether the frame is totally opaque */
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;
/** x co-ordinate of redraw rectangle */
uint32_t redraw_x;
/** y co-ordinate of redraw rectangle */
uint32_t redraw_y;
/** width of redraw rectangle */
uint32_t redraw_width;
/** height of redraw rectangle */
uint32_t redraw_height;
/* Frame flags */
uint32_t flags;
} gif_frame;
/* API for Bitmap callbacks */
typedef void* (*gif_bitmap_cb_create)(int width, int height);
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
typedef uint8_t* (*gif_bitmap_cb_get_buffer)(void *bitmap);
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/** Bitmap callbacks function table */
typedef struct gif_bitmap_callback_vt {
/** Create a bitmap. */
gif_bitmap_cb_create bitmap_create;
/** Free a bitmap. */
gif_bitmap_cb_destroy bitmap_destroy;
/** Return a pointer to the pixel data in a bitmap. */
gif_bitmap_cb_get_buffer bitmap_get_buffer;
/* Members below are optional */
/** Sets whether a bitmap should be plotted opaque. */
gif_bitmap_cb_set_opaque bitmap_set_opaque;
/** Tests whether a bitmap has an opaque alpha channel. */
gif_bitmap_cb_test_opaque bitmap_test_opaque;
/** The bitmap image has changed, so flush any persistent cache. */
gif_bitmap_cb_modified bitmap_modified;
} gif_bitmap_callback_vt;
/** GIF animation data */
typedef struct gif_animation {
/** LZW decode context */
void *lzw_ctx;
/** callbacks for bitmap functions */
gif_bitmap_callback_vt bitmap_callbacks;
/** pointer to GIF data */
const uint8_t *gif_data;
/** 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 frames partially decoded */
uint32_t frame_count_partial;
/** decoded frames */
gif_frame *frames;
/** current frame decoded to bitmap */
int decoded_frame;
/** currently decoded image; stored as bitmap from bitmap_create callback */
void *frame_image;
/** number of times to loop animation */
int loop_count;
/* Internal members are listed below */
/** current index into GIF data */
uint32_t buffer_position;
/** total number of bytes of GIF data available */
uint32_t buffer_size;
/** current number of frame holders */
uint32_t frame_holders;
/** background index */
uint32_t bg_index;
/** background colour */
uint32_t bg_colour;
/** image aspect ratio (ignored) */
uint32_t aspect_ratio;
/** size of colour table (in entries) */
uint32_t colour_table_size;
/** whether the GIF has a global colour table */
bool global_colours;
/** global colour table */
uint32_t *global_colour_table;
/** local colour table */
uint32_t *local_colour_table;
/** current colour table */
uint32_t *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;
/**
* Initialises necessary gif_animation members.
*/
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
/**
* Initialises any workspace held by the animation and attempts to decode
* any information that hasn't already been decoded.
* If an error occurs, all previously decoded frames are retained.
*
* \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for memory error
* - GIF_DATA_ERROR for GIF error
* - GIF_OK for successful decoding
* - GIF_WORKING for successful decoding if more frames are expected
*/
gif_result gif_initialise(gif_animation *gif, size_t size, const uint8_t *data);
/**
* Decodes a GIF frame.
*
* \return Error return value.
* - GIF_FRAME_DATA_ERROR for GIF frame data error
* - GIF_DATA_ERROR for GIF error (invalid frame header)
* - GIF_INSUFFICIENT_DATA reached unexpected end of source data
* - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
* - GIF_OK for successful decoding
*/
gif_result gif_decode_frame(gif_animation *gif, uint32_t frame);
/**
* Releases any workspace held by a gif
*/
void gif_finalise(gif_animation *gif);
#endif

View File

@ -1,6 +1,6 @@
nsgif_lib = static_library('nsgif',
'libnsgif.h',
'libnsgif.c',
'nsgif.h',
'gif.c',
'lzw.c',
'lzw.h',
)

View File

@ -0,0 +1,358 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
* Copyright 2013-2022 Michael Drake <tlsa@netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* \file
* Interface to progressive animated GIF file decoding.
*/
#ifndef NSNSGIF_H
#define NSNSGIF_H
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
/** Representation of infinity. */
#define NSGIF_INFINITE (UINT32_MAX)
/**
* Opaque type used by LibNSGIF to represent a GIF object in memory.
*/
typedef struct nsgif nsgif_t;
/**
* LibNSGIF 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_t;
/**
* LibNSGIF return codes.
*/
typedef enum {
/**
* Success.
*/
NSGIF_OK,
/**
* Out of memory error.
*/
NSGIF_ERR_OOM,
/**
* GIF source data is invalid, and no frames are recoverable.
*/
NSGIF_ERR_DATA,
/**
* Frame number is not valid.
*/
NSGIF_ERR_BAD_FRAME,
/**
* GIF source data contained an error in a frame.
*/
NSGIF_ERR_DATA_FRAME,
/**
* Too many frames.
*/
NSGIF_ERR_FRAME_COUNT,
/**
* GIF source data ended without one complete frame available.
*/
NSGIF_ERR_END_OF_DATA,
/**
* GIF source data ended with incomplete frame.
*/
NSGIF_ERR_END_OF_FRAME,
/**
* The current frame cannot be displayed.
*/
NSGIF_ERR_FRAME_DISPLAY,
/**
* Indicates an animation is complete, and \ref nsgif_reset must be
* called to restart the animation from the beginning.
*/
NSGIF_ERR_ANIMATION_END,
} nsgif_error;
/**
* Client bitmap type.
*
* These are client-created and destroyed, via the \ref bitmap callbacks,
* but they are owned by a \ref nsgif_t.
*
* The pixel buffer is is 32bpp, treated as individual bytes in the component
* order RR GG BB AA. For example, a 1x1 image with a single orange pixel would
* be encoded as the following sequence of bytes: 0xff, 0x88, 0x00, 0x00.
*/
typedef void nsgif_bitmap_t;
/** Bitmap callbacks function table */
typedef struct nsgif_bitmap_cb_vt {
/**
* Callback to create a bitmap with the given dimensions.
*
* \param[in] width Required bitmap width in pixels.
* \param[in] height Required bitmap height in pixels.
* \return pointer to client's bitmap structure or NULL on error.
*/
nsgif_bitmap_t* (*create)(int width, int height);
/**
* Callback to free a bitmap.
*
* \param[in] bitmap The bitmap to destroy.
*/
void (*destroy)(nsgif_bitmap_t *bitmap);
/**
* Get pointer to pixel buffer in a bitmap.
*
* The pixel buffer must be `width * height * sizeof(uint32_t)`.
* Note that the returned pointer to uint8_t must be 4-byte aligned.
*
* \param[in] bitmap The bitmap.
* \return pointer to bitmap's pixel buffer.
*/
uint8_t* (*get_buffer)(nsgif_bitmap_t *bitmap);
/* The following functions are optional. */
/**
* Set whether a bitmap can be plotted opaque.
*
* \param[in] bitmap The bitmap.
* \param[in] opaque Whether the current frame is opaque.
*/
void (*set_opaque)(nsgif_bitmap_t *bitmap, bool opaque);
/**
* Tests whether a bitmap has an opaque alpha channel.
*
* \param[in] bitmap The bitmap.
* \return true if the bitmap is opaque, false otherwise.
*/
bool (*test_opaque)(nsgif_bitmap_t *bitmap);
/**
* Bitmap modified notification.
*
* \param[in] bitmap The bitmap.
*/
void (*modified)(nsgif_bitmap_t *bitmap);
} nsgif_bitmap_cb_vt;
/**
* Convert an error code to a string.
*
* \param[in] err The error code to convert.
* \return String representation of given error code.
*/
const char *nsgif_strerror(nsgif_error err);
/**
* Create the NSGIF object.
*
* \param[in] bitmap_vt Bitmap operation functions v-table.
* \param[out] gif_out Return \ref nsgif_t object on success.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_create(
const nsgif_bitmap_cb_vt *bitmap_vt,
nsgif_t **gif_out);
/**
* Free a NSGIF object.
*
* \param[in] gif The NSGIF to free.
*/
void nsgif_destroy(nsgif_t *gif);
/**
* Scan the source image data.
*
* This is used to feed the source data into LibNSGIF. This must be called
* before calling \ref nsgif_frame_decode.
*
* It can be called multiple times with, with increasing sizes. If it is called
* several times, as more data is available (e.g. slow network fetch) the data
* already given to \ref nsgif_data_scan must be provided each time.
*
* For example, if you call \ref nsgif_data_scan with 25 bytes of data, and then
* fetch another 10 bytes, you would need to call \ref nsgif_data_scan with a
* size of 35 bytes, and the whole 35 bytes must be contiguous memory. It is
* safe to `realloc` the source buffer between calls to \ref nsgif_data_scan.
* (The actual data pointer is allowed to be different.)
*
* If an error occurs, all previously scanned frames are retained.
*
* \param[in] gif The \ref nsgif_t object.
* \param[in] size Number of bytes in data.
* \param[in] data Raw source GIF data.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_data_scan(
nsgif_t *gif,
size_t size,
const uint8_t *data);
/**
* 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 \ref nsgif_t 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.
* \param[out] frame_new The frame to decode.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_frame_prepare(
nsgif_t *gif,
nsgif_rect_t *area,
uint32_t *delay_cs,
uint32_t *frame_new);
/**
* Decodes a GIF frame.
*
* \param[in] gif The \ref nsgif_t object.
* \param[in] frame The frame number to decode.
* \param[out] bitmap On success, returns pointer to the client-allocated,
* nsgif-owned client bitmap structure.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_frame_decode(
nsgif_t *gif,
uint32_t frame,
nsgif_bitmap_t **bitmap);
/**
* Reset a GIF animation.
*
* Some animations are only meant to loop N times, and then show the
* final frame forever. This function resets the loop and frame counters,
* so that the animation can be replayed without the overhead of recreating
* the \ref nsgif_t object and rescanning the raw data.
*
* \param[in] gif A \ref nsgif_t object.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_reset(
nsgif_t *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;
/** background colour in same pixel format as \ref nsgif_bitmap_t. */
uint8_t background[4];
} 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;
/** whether the frame may have transparency */
bool transparency;
/** 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_t rect;
} nsgif_frame_info_t;
/**
* Get information about a GIF from an \ref nsgif_t object.
*
* \param[in] gif The \ref nsgif_t object to get info for.
*
* \return The gif info, or NULL on error.
*/
const nsgif_info_t *nsgif_get_info(const nsgif_t *gif);
/**
* Get information about a GIF from an \ref nsgif_t object.
*
* \param[in] gif The \ref nsgif_t 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.
*/
const nsgif_frame_info_t *nsgif_get_frame_info(
const nsgif_t *gif,
uint32_t frame);
#endif

View File

@ -1,11 +0,0 @@
--- libnsgif.orig.c 2021-12-18 17:58:43.829719505 +0000
+++ libnsgif.c 2021-12-18 17:58:47.773783438 +0000
@@ -1003,7 +1003,7 @@
frame->frame_pointer = gif->buffer_position;
frame->redraw_required = false;
frame->disposal_method = 0;
- frame->frame_delay = 100;
+ frame->frame_delay = 10;
frame->display = false;
frame->virgin = true;
}

View File

@ -0,0 +1,763 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (C) 2021 Michael Drake <tlsa@netsurf-browser.org>
*/
/**
* \file
* \brief Command line argument handling.
*/
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cli.h"
/**
* Check whether a CLI argument type should have a numerical value.
*
* \param[in] type An argument type.
* \return true if the argument needs a numerical value, or false otherwise.
*/
static inline bool cli__arg_is_numerical(enum cli_arg_type type)
{
return (type != CLI_STRING && type != CLI_BOOL);
}
/**
* Parse a signed integer value from an argument.
*
* \param[in] str String containing value to parse.
* \param[out] i Pointer to place to store parsed value.
* \param[in,out] pos Current position in str, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_value_int(
const char *str,
int64_t *i,
size_t *pos)
{
long long temp;
char *end = NULL;
str += *pos;
errno = 0;
temp = strtoll(str, &end, 0);
if (end == str || errno == ERANGE ||
temp > INT64_MAX || temp < INT64_MIN) {
fprintf(stderr, "Failed to parse integer from '%s'\n", str);
return false;
}
*i = (int64_t)temp;
*pos += (size_t)(end - str);
return true;
}
/**
* Parse an unsigned integer value from an argument.
*
* \param[in] str String containing value to parse.
* \param[out] u Pointer to place to store parsed value.
* \param[in,out] pos Current position in str, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_value_uint(
const char *str,
uint64_t *u,
size_t *pos)
{
unsigned long long temp;
char *end = NULL;
str += *pos;
errno = 0;
temp = strtoull(str, &end, 0);
if (end == str || errno == ERANGE || temp > UINT64_MAX) {
fprintf(stderr, "Failed to parse unsigned from '%s'\n", str);
return false;
}
*u = (uint64_t)temp;
*pos += (size_t)(end - str);
return true;
}
/**
* Parse an enum value from an argument.
*
* \param[in] str String containing value to parse.
* \param[out] e Enum details.
* \param[in,out] pos Current position in str, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_value_enum(
const char *str,
const struct cli_enum *e,
size_t *pos)
{
str += *pos;
*pos += strlen(str);
for (const struct cli_str_val *sv = e->desc; sv->str != NULL; sv++) {
if (strcmp(str, sv->str) == 0) {
*e->e = sv->val;
return true;
}
}
return false;
}
/**
* Parse a string value from an argument.
*
* \param[in] str String containing value to parse.
* \param[out] s Pointer to place to store parsed value.
* \param[in,out] pos Current position in str, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_value_string(
const char *str,
const char **s,
size_t *pos)
{
*s = str + *pos;
*pos += strlen(*s);
return true;
}
/**
* Parse a value from an argument.
*
* \param[in] entry Client command line interface argument specification.
* \param[in] arg Argument to parse a value from.
* \param[in,out] pos Current position in argument, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_value(
const struct cli_table_entry *entry,
const char *arg,
size_t *pos)
{
switch (entry->t) {
case CLI_CMD:
if (strcmp(arg + *pos, entry->l) == 0) {
*pos += strlen(arg);
return true;
}
return false;
case CLI_INT:
return cli__parse_value_int(arg, entry->v.i, pos);
case CLI_UINT:
return cli__parse_value_uint(arg, entry->v.u, pos);
case CLI_ENUM:
return cli__parse_value_enum(arg, &entry->v.e, pos);
case CLI_STRING:
return cli__parse_value_string(arg, entry->v.s, pos);
default:
fprintf(stderr, "Unexpected value for '%s': %s\n",
entry->l, arg);
break;
}
return false;
}
/**
* Parse a value from an argument.
*
* \param[in] entry Client command line interface argument specification.
* \param[in] argc Number of command line arguments.
* \param[in] argv String vector containing command line arguments.
* \param[in] arg_pos Current position in argv.
* \param[in,out] pos Current pos in current argument, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_argv_value(const struct cli_table_entry *entry,
int argc, const char **argv,
int arg_pos, size_t *pos)
{
const char *arg = argv[arg_pos];
if (arg_pos >= argc) {
fprintf(stderr, "Value not given for '%s'\n", entry->l);
return false;
}
return cli__parse_value(entry, arg, pos);
}
/**
* Check whether a CLI argument is a positional value.
*
* \param[in] entry Client command line interface argument specification.
* \return true if the argument is positional, or false otherwise.
*/
static inline bool cli__entry_is_positional(const struct cli_table_entry *entry)
{
return entry->p;
}
/**
* Look up a short argument flag.
*
* \param[in] cli Client command line interface specification.
* \param[in] s Argument flag to look up in client CLI spec.
* \return Client CLI spec entry on success, or NULL otherwise.
*/
static const struct cli_table_entry *cli__lookup_short(
const struct cli_table *cli, char s)
{
for (size_t i = 0; i < cli->count; i++) {
if (cli__entry_is_positional(&cli->entries[i])) {
continue;
}
if (cli->entries[i].s == s) {
return &cli->entries[i];
}
}
fprintf(stderr, "Unknown flag: '%c'\n", s);
return NULL;
}
/**
* Handle an argument with a type that requires a value.
*
* This can handle the value being in the current argument, optionally split by
* a separator, or in the next argument.
*
* \param[in] entry Client command line interface argument specification.
* \param[in] argc Number of command line arguments.
* \param[in] argv String vector containing command line arguments.
* \param[in,out] arg_pos Current position in argv, updated on exit.
* \param[in] pos Current position in current argument string.
* \param[in] sep Name/value separator character, or '\0' if none.
* \return true on success, or false otherwise.
*/
static bool cli__handle_arg_value(const struct cli_table_entry *entry,
int argc, const char **argv, int *arg_pos, size_t pos, char sep)
{
const char *arg = argv[*arg_pos];
size_t orig_pos;
bool ret;
if (arg[pos] == '\0') {
(*arg_pos)++;
pos = 0;
} else if (arg[pos] == sep) {
pos++;
} else if (cli__arg_is_numerical(entry->t) == false) {
fprintf(stderr, "Separator required for non-numerical value\n");
return false;
}
if (isspace(argv[*arg_pos][pos])) {
fprintf(stderr, "Unexpected white space in '%s' "
"for argument '%s'\n",
&argv[*arg_pos][pos], entry->l);
return false;
}
orig_pos = pos;
ret = cli__parse_argv_value(entry, argc, argv, *arg_pos, &pos);
if (ret != true) {
return ret;
}
if (argv[*arg_pos][pos] != '\0') {
fprintf(stderr, "Invalid value '%s' for argument '%s'\n",
&argv[*arg_pos][orig_pos], entry->l);
return false;
}
return true;
}
/**
* Parse a flags argument.
*
* \param[in] cli Client command line interface specification.
* \param[in] argc Number of command line arguments.
* \param[in] argv String vector containing command line arguments.
* \param[out] arg_pos Current position in argv, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_short(const struct cli_table *cli,
int argc, const char **argv, int *arg_pos)
{
const char *arg = argv[*arg_pos];
size_t pos = 1;
if (arg[0] != '-') {
return false;
}
while (arg[pos] != '\0') {
const struct cli_table_entry *entry;
entry = cli__lookup_short(cli, arg[pos]);
if (entry == NULL) {
return false;
}
if (entry->t == CLI_BOOL) {
*entry->v.b = true;
} else {
return cli__handle_arg_value(entry, argc, argv,
arg_pos, pos + 1, '\0');
}
pos++;
}
return true;
}
/**
* Look up a long argument name.
*
* \param[in] cli Client command line interface specification.
* \param[in] arg Argument name to look up in cli spec.
* \param[in,out] pos Current position in arg, updated on exit.
* \return Client CLI spec entry on success, or NULL otherwise.
*/
static const struct cli_table_entry *cli__lookup_long(
const struct cli_table *cli,
const char *arg,
size_t *pos)
{
arg += *pos;
for (size_t i = 0; i < cli->count; i++) {
if (cli__entry_is_positional(&cli->entries[i]) == false) {
const char *name = cli->entries[i].l;
size_t name_len = strlen(cli->entries[i].l);
if (strncmp(name, arg, name_len) == 0) {
if (arg[name_len] != '\0' &&
arg[name_len] != '=') {
continue;
}
*pos += name_len;
return &cli->entries[i];
}
}
}
fprintf(stderr, "Unknown argument: '%s'\n", arg);
return NULL;
}
/**
* Parse a long argument.
*
* \param[in] cli Client command line interface specification.
* \param[in] argc Number of command line arguments.
* \param[in] argv String vector containing command line arguments.
* \param[out] arg_pos Current position in argv, updated on exit.
* \return true on success, or false otherwise.
*/
static bool cli__parse_long(const struct cli_table *cli,
int argc, const char **argv, int *arg_pos)
{
const struct cli_table_entry *entry;
const char *arg = argv[*arg_pos];
size_t pos = 2;
if (arg[0] != '-' ||
arg[1] != '-') {
return false;
}
entry = cli__lookup_long(cli, arg, &pos);
if (entry == NULL) {
return false;
}
if (entry->t == CLI_BOOL) {
if (arg[pos] != '\0') {
fprintf(stderr, "Unexpected value for argument '%s'\n",
arg);
return false;
}
*entry->v.b = true;
} else {
bool ret;
ret = cli__handle_arg_value(entry, argc, argv,
arg_pos, pos, '=');
if (ret != true) {
return ret;
}
}
return true;
}
/**
* Parse a positional argument according to the given CLI spec entry.
*
* \param[in] entry Client command line interface argument specification.
* \param[in] arg Argument to parse.
* \return true on success, or false otherwise.
*/
static bool cli__parse_positional_entry(
const struct cli_table_entry *entry,
const char *arg)
{
size_t pos = 0;
bool ret;
ret = cli__parse_value(entry, arg, &pos);
if (ret != true) {
return ret;
} else if (arg[pos] != '\0') {
fprintf(stderr, "Failed to parse value '%s' for arg '%s'\n",
arg, entry->l);
return false;
}
return true;
}
/**
* Parse a positional argument.
*
* \param[in] cli Client command line interface specification.
* \param[in] arg Argument to parse.
* \param[in] count Number of positional arguments parsed already.
* \return true on success, or false otherwise.
*/
static bool cli__parse_positional(const struct cli_table *cli,
const char *arg, size_t count)
{
size_t positional = 0;
for (size_t i = 0; i < cli->count; i++) {
if (cli__entry_is_positional(&cli->entries[i])) {
if (positional == count) {
return cli__parse_positional_entry(
&cli->entries[i], arg);
}
positional++;
}
}
fprintf(stderr, "Unexpected positional argument: '%s'\n", arg);
return false;
}
/**
* Get the string to indicate type of value expected for an argument.
*
* \param[in] type The argument type.
* \return String for value type.
*/
static const char *cli__string_from_type(enum cli_arg_type type)
{
static const char *const strings[] = {
[CLI_BOOL] = "",
[CLI_INT] = "INT",
[CLI_UINT] = "UINT",
[CLI_ENUM] = "ENUM",
[CLI_STRING] = "STRING",
};
if (type >= CLI_ARRAY_LEN(strings) || strings[type] == NULL) {
return "";
}
return strings[type];
}
/**
* Helper to update a maximum adjusted string length if new values is greater.
*
* \param[in] str String to check.
* \param[in] adjustment Amount to modify length of string by (bytes).
* \param[out] len Returns the maximum of existing and this length.
*/
static void cli__max_len(const char *str, size_t adjustment, size_t *len)
{
size_t str_len = strlen(str) + adjustment;
if (str_len > *len) {
*len = str_len;
}
}
/**
* Count up various properties of the client CLI interface specification.
*
* \param[in] cli Client command line interface specification.
* \param[out] count Returns number of non-positional arguments.
* \param[out] pcount Returns number of positional arguments.
* \param[out] max_len Returns max string length of non-positional arguments.
* \param[out] pmax_len Returns max string length of positional arguments.
* \param[out] phas_desc Returns number of positional args with descriptions.
*/
static void cli__count(const struct cli_table *cli,
size_t *count,
size_t *pcount,
size_t *max_len,
size_t *pmax_len,
size_t *phas_desc)
{
if (count != NULL) *count = 0;
if (pcount != NULL) *pcount = 0;
if (max_len != NULL) *max_len = 0;
if (pmax_len != NULL) *pmax_len = 0;
if (phas_desc != NULL) *phas_desc = 0;
for (size_t i = 0; i < cli->count; i++) {
const struct cli_table_entry *entry = &cli->entries[i];
if (cli__entry_is_positional(entry)) {
if (pcount != NULL) {
(*pcount)++;
}
if (pmax_len != NULL) {
cli__max_len(entry->l, 0, pmax_len);
}
if (phas_desc != NULL) {
(*phas_desc)++;
}
} else {
if (count != NULL) {
(*count)++;
}
if (max_len != NULL) {
const char *type_str;
size_t type_len;
type_str = cli__string_from_type(entry->t);
type_len = strlen(type_str);
cli__max_len(entry->l, type_len, max_len);
}
}
}
}
static inline bool cli__is_negative(const char *arg)
{
int64_t i;
size_t pos = 0;
return cli__parse_value_int(arg, &i, &pos)
&& pos == strlen(arg)
&& i < 0;
}
/* Documented in cli.h */
bool cli_parse(const struct cli_table *cli, int argc, const char **argv)
{
size_t pos_count = 0;
enum {
ARG_PROG_NAME,
ARG_FIRST,
};
for (int i = ARG_FIRST; i < argc; i++) {
const char *arg = argv[i];
size_t pos_inc = 0;
bool ret;
if (arg[0] == '-') {
if (arg[1] == '-') {
ret = cli__parse_long(cli, argc, argv, &i);
} else {
ret = cli__parse_short(cli, argc, argv, &i);
if (ret != true) {
if (cli__is_negative(argv[i])) {
pos_inc = 1;
ret = cli__parse_positional(
cli, argv[i],
pos_count);
}
}
}
} else {
pos_inc = 1;
ret = cli__parse_positional(cli, argv[i], pos_count);
}
if (ret != true) {
return ret;
}
pos_count += pos_inc;
}
if (pos_count < cli->min_positional) {
fprintf(stderr, "Insufficient positional arguments found.\n");
return false;
}
return true;
}
/**
* Get terminal width.
*
* \return terminal width in characters.
*/
static size_t cli__terminal_width(void)
{
return 80;
}
/**
* Print an entry's description, with a given indent.
*
* The indent is assumed to already be applied for the first line of the
* output by the caller.
*
* \param[in] entry The entry to print the description for.
* \param[in] indent The number of spaces to pad the left margin with.
*/
static void cli__print_description(const struct cli_table_entry *entry,
size_t indent)
{
size_t terminal_width = cli__terminal_width();
size_t avail = (indent > terminal_width) ? 0 : terminal_width - indent;
size_t space = avail;
const char *desc = entry->d;
if (desc != NULL) {
while (*desc != '\0') {
size_t word_len = strcspn(desc, " \n\t");
if (word_len <= space || space == avail) {
fprintf(stderr, "%*.*s",
(int)word_len,
(int)word_len, desc);
desc += word_len;
if (word_len <= space) {
space -= word_len;
}
if (space > 0) {
fprintf(stderr, " ");
space--;
}
} else {
fprintf(stderr, "\n%*s", (int)indent, "");
space = avail;
}
desc += strspn(desc, " \n\t");
}
}
fprintf(stderr, "\n");
}
/* Documented in cli.h */
void cli_help(const struct cli_table *cli, const char *prog_name)
{
size_t count;
size_t pcount;
size_t max_len;
size_t pmax_len;
size_t phas_desc;
size_t required = 0;
enum {
ARG_PROG_NAME,
};
cli__count(cli, &count, &pcount, &max_len, &pmax_len, &phas_desc);
fprintf(stderr, "\nUsage: %s", prog_name);
if (pcount > 0) {
for (size_t i = 0; i < cli->count; i++) {
if (cli__entry_is_positional(&cli->entries[i])) {
const char *punctuation =
(required == cli->min_positional) ?
" [" : " ";
if (cli->entries[i].t == CLI_CMD) {
fprintf(stderr, "%s%s", punctuation,
cli->entries[i].l);
} else {
fprintf(stderr, "%s<%s>", punctuation,
cli->entries[i].l);
}
required++;
}
}
if (required == pcount && required > cli->min_positional) {
fprintf(stderr, "]");
}
}
if (count > 0) {
fprintf(stderr, " [options]");
}
fprintf(stderr, "\n\n");
if (phas_desc > 0) {
fprintf(stderr, "Where:\n\n");
for (size_t i = 0; i < cli->count; i++) {
const struct cli_table_entry *entry = &cli->entries[i];
if (entry->d == NULL) {
continue;
}
if (cli__entry_is_positional(entry)) {
fprintf(stderr, " %*.*s ",
(int)pmax_len,
(int)pmax_len,
entry->l);
cli__print_description(entry, pmax_len + 4);
fprintf(stderr, "\n");
}
}
}
if (count > 0) {
fprintf(stderr, "Options:\n\n");
for (size_t i = 0; i < cli->count; i++) {
const struct cli_table_entry *entry = &cli->entries[i];
const char *type_str;
size_t type_len;
size_t arg_len;
if (cli__entry_is_positional(entry)) {
continue;
}
if (entry->s != '\0') {
fprintf(stderr, " -%c", entry->s);
} else {
fprintf(stderr, " ");
}
type_str = cli__string_from_type(entry->t);
type_len = strlen(type_str);
arg_len = strlen(entry->l);
fprintf(stderr, " --%s %s%*.s ", entry->l, type_str,
(int)(max_len - arg_len - type_len),
"");
cli__print_description(entry, max_len + 11);
fprintf(stderr, "\n");
}
}
}

View File

@ -0,0 +1,94 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (C) 2021 Michael Drake <tlsa@netsurf-browser.org>
*/
/**
* \file
* \brief Command line argument handling API.
*/
#ifndef _PELTAR_CLI_H_
#define _PELTAR_CLI_H_
#include <stdint.h>
#include <stdbool.h>
/**
* Helper to get element count for an array,
*
* \param[in] _a Array to get number of elements for.
*/
#define CLI_ARRAY_LEN(_a) ((sizeof(_a))/(sizeof(*(_a))))
/**
* CLI argument type.
*/
enum cli_arg_type {
CLI_CMD, /**< A sub-command. Must match long argument name. */
CLI_BOOL, /**< Has no value; presence of flag indicates true. */
CLI_INT, /**< Has signed integer value. */
CLI_UINT, /**< Has unsigned integer value. */
CLI_ENUM, /**< Has enumeration value. */
CLI_STRING, /**< Has string value. */
};
struct cli_str_val {
const char *str;
int64_t val;
};
struct cli_enum {
const struct cli_str_val *desc;
int64_t *e; /**< Location to store \ref CLI_ENUM value. */
};
/**
* Client description for a command line argument.
*/
struct cli_table_entry {
const char *l; /**< Long argument name. */
const char s; /**< Short flag name. (Non-positional arguments.) */
bool p; /**< Whether the argument is a positional argument. */
enum cli_arg_type t; /**< Argument type. */
union {
bool *b; /**< Location to store \ref CLI_BOOL value. */
int64_t *i; /**< Location to store \ref CLI_INT value. */
uint64_t *u; /**< Location to store \ref CLI_UINT value. */
const char **s; /**< Location to store \ref CLI_STRING value. */
struct cli_enum e;
} v; /**< Where to store type-specific values. */
const char *d; /**< Description. */
};
/**
* Client command line interface specification.
*/
struct cli_table {
const struct cli_table_entry *entries;
size_t count;
size_t min_positional;
};
/**
* Parse the command line arguments.
*
* \param[in] cli Client command line interface specification.
* \param[in] argc Number of command line arguments.
* \param[in] argv String vector containing command line arguments.
* \return true on success, false on error.
*/
bool cli_parse(const struct cli_table *cli, int argc, const char **argv);
/**
* Print usage and help output.
*
* Note: Assumes non-Unicode. (One byte per character.)
*
* \param[in] cli Client command line interface specification.
* \param[in] prog_name Program name.
*/
void cli_help(const struct cli_table *cli, const char *prog_name);
#endif

View File

@ -1,258 +0,0 @@
/* under libvips, compile with:
*
* gcc -g -Wall decode_gif.c `pkg-config vips --cflags --libs`
*/
/*
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
* Copyright 2008 James Bursa <james@netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <vips/vips.h>
#include "../libnsgif.h"
#define BYTES_PER_PIXEL 4
#define MAX_IMAGE_BYTES (48 * 1024 * 1024)
static void *bitmap_create(int width, int height)
{
/* ensure a stupidly large bitmap is not created */
if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) {
return NULL;
}
return calloc(width * height, BYTES_PER_PIXEL);
}
static void bitmap_set_opaque(void *bitmap, bool opaque)
{
(void) opaque; /* unused */
(void) bitmap; /* unused */
assert(bitmap);
}
static bool bitmap_test_opaque(void *bitmap)
{
(void) bitmap; /* unused */
assert(bitmap);
return false;
}
static unsigned char *bitmap_get_buffer(void *bitmap)
{
assert(bitmap);
return bitmap;
}
static void bitmap_destroy(void *bitmap)
{
assert(bitmap);
free(bitmap);
}
static void bitmap_modified(void *bitmap)
{
(void) bitmap; /* unused */
assert(bitmap);
return;
}
static unsigned char *load_file(const char *path, size_t *data_size)
{
FILE *fd;
struct stat sb;
unsigned char *buffer;
size_t size;
size_t n;
fd = fopen(path, "rb");
if (!fd) {
perror(path);
exit(EXIT_FAILURE);
}
if (stat(path, &sb)) {
perror(path);
exit(EXIT_FAILURE);
}
size = sb.st_size;
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Unable to allocate %lld bytes\n",
(long long) size);
exit(EXIT_FAILURE);
}
n = fread(buffer, 1, size, fd);
if (n != size) {
perror(path);
exit(EXIT_FAILURE);
}
fclose(fd);
*data_size = size;
return buffer;
}
static void warning(const char *context, gif_result code)
{
fprintf(stderr, "%s failed: ", context);
switch (code)
{
case GIF_INSUFFICIENT_FRAME_DATA:
fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA");
break;
case GIF_FRAME_DATA_ERROR:
fprintf(stderr, "GIF_FRAME_DATA_ERROR");
break;
case GIF_INSUFFICIENT_DATA:
fprintf(stderr, "GIF_INSUFFICIENT_DATA");
break;
case GIF_DATA_ERROR:
fprintf(stderr, "GIF_DATA_ERROR");
break;
case GIF_INSUFFICIENT_MEMORY:
fprintf(stderr, "GIF_INSUFFICIENT_MEMORY");
break;
default:
fprintf(stderr, "unknown code %i", code);
break;
}
fprintf(stderr, "\n");
}
static void write_ppm(FILE* fh, const char *name, gif_animation *gif,
bool no_write)
{
unsigned int i;
gif_result code;
if (!no_write) {
fprintf(fh, "P3\n");
fprintf(fh, "# %s\n", name);
fprintf(fh, "# width %u \n", gif->width);
fprintf(fh, "# height %u \n", gif->height);
fprintf(fh, "# frame_count %u \n", gif->frame_count);
fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial);
fprintf(fh, "# loop_count %u \n", gif->loop_count);
fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count);
}
/* decode the frames */
for (i = 0; i != gif->frame_count; i++) {
unsigned int row, col;
unsigned char *image;
code = gif_decode_frame(gif, i);
if (code != GIF_OK)
warning("gif_decode_frame", code);
if (!no_write) {
fprintf(fh, "# frame %u:\n", i);
image = (unsigned char *) gif->frame_image;
for (row = 0; row != gif->height; row++) {
for (col = 0; col != gif->width; col++) {
size_t z = (row * gif->width + col) * 4;
fprintf(fh, "%u %u %u ",
(unsigned char) image[z],
(unsigned char) image[z + 1],
(unsigned char) image[z + 2]);
}
fprintf(fh, "\n");
}
}
}
}
int main(int argc, char *argv[])
{
gif_bitmap_callback_vt bitmap_callbacks = {
bitmap_create,
bitmap_destroy,
bitmap_get_buffer,
bitmap_set_opaque,
bitmap_test_opaque,
bitmap_modified
};
gif_animation gif;
size_t size;
gif_result code;
unsigned char *data;
FILE *outf = stdout;
bool no_write = false;
if (argc < 2) {
fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded "
"but not output.\n");
fprintf(stderr, "Otherwise [out] is an output filename.\n");
fprintf(stderr, "When [out] is unset, output is to stdout.\n");
return 1;
}
if (argc > 2) {
if (strcmp(argv[2], "NOWRITE") == 0) {
no_write = true;
} else {
outf = fopen(argv[2], "w+");
if (outf == NULL) {
fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
return 2;
}
}
}
/* create our gif animation */
gif_create(&gif, &bitmap_callbacks);
/* load file into memory */
data = load_file(argv[1], &size);
/* begin decoding */
do {
code = gif_initialise(&gif, size, data);
if (code != GIF_OK && code != GIF_WORKING) {
warning("gif_initialise", code);
gif_finalise(&gif);
free(data);
return 1;
}
} while (code != GIF_OK);
write_ppm(outf, argv[1], &gif, no_write);
if (argc > 2 && !no_write) {
fclose(outf);
}
/* clean up */
gif_finalise(&gif);
free(data);
return 0;
}

View File

@ -0,0 +1,310 @@
/*
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
* Copyright 2008 James Bursa <james@netsurf-browser.org>
* Copyright 2022 Michael Drake <tlsa@netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "../include/nsgif.h"
#include "cli.h"
#include "cli.c"
#define BYTES_PER_PIXEL 4
static struct nsgif_options {
const char *file;
const char *ppm;
uint64_t loops;
bool info;
} nsgif_options;
static const struct cli_table_entry cli_entries[] = {
{
.s = 'm',
.l = "ppm",
.t = CLI_STRING,
.v.s = &nsgif_options.ppm,
.d = "Convert frames to PPM image at given path."
},
{
.s = 'i',
.l = "info",
.t = CLI_BOOL,
.v.b = &nsgif_options.info,
.d = "Dump GIF info to stdout."
},
{
.s = 'l',
.l = "loops",
.t = CLI_UINT,
.v.u = &nsgif_options.loops,
.d = "Loop through decoding all frames N times. "
"The default is 1."
},
{
.p = true,
.l = "FILE",
.t = CLI_STRING,
.v.s = &nsgif_options.file,
.d = "Path to GIF file to load."
},
};
const struct cli_table cli = {
.entries = cli_entries,
.count = (sizeof(cli_entries))/(sizeof(*cli_entries)),
.min_positional = 1,
};
static void *bitmap_create(int width, int height)
{
/* Ensure a stupidly large bitmap is not created */
if (width > 4096 || height > 4096) {
return NULL;
}
return calloc(width * height, BYTES_PER_PIXEL);
}
static unsigned char *bitmap_get_buffer(void *bitmap)
{
return bitmap;
}
static void bitmap_destroy(void *bitmap)
{
free(bitmap);
}
static uint8_t *load_file(const char *path, size_t *data_size)
{
FILE *fd;
struct stat sb;
unsigned char *buffer;
size_t size;
size_t n;
fd = fopen(path, "rb");
if (!fd) {
perror(path);
exit(EXIT_FAILURE);
}
if (stat(path, &sb)) {
perror(path);
exit(EXIT_FAILURE);
}
size = sb.st_size;
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Unable to allocate %lld bytes\n",
(long long) size);
exit(EXIT_FAILURE);
}
n = fread(buffer, 1, size, fd);
if (n != size) {
perror(path);
exit(EXIT_FAILURE);
}
fclose(fd);
*data_size = size;
return buffer;
}
static void warning(const char *context, nsgif_error err)
{
fprintf(stderr, "%s failed: %s\n",
context, nsgif_strerror(err));
}
static void print_gif_info(const nsgif_info_t *info)
{
fprintf(stdout, "gif:\n");
fprintf(stdout, " width: %"PRIu32"\n", info->width);
fprintf(stdout, " height: %"PRIu32"\n", info->height);
fprintf(stdout, " max-loops: %"PRIu32"\n", info->loop_max);
fprintf(stdout, " frame-count: %"PRIu32"\n", info->frame_count);
fprintf(stdout, " background:\n");
fprintf(stdout, " red: 0x%"PRIx8"\n", info->background[0]);
fprintf(stdout, " green: 0x%"PRIx8"\n", info->background[1]);
fprintf(stdout, " blue: 0x%"PRIx8"\n", info->background[2]);
fprintf(stdout, " frames:\n");
}
static void print_gif_frame_info(const nsgif_frame_info_t *info)
{
const char *disposal = nsgif_str_disposal(info->disposal);
fprintf(stdout, " - disposal-method: %s\n", disposal);
fprintf(stdout, " transparency: %s\n", info->transparency ? "yes" : "no");
fprintf(stdout, " display: %s\n", info->display ? "yes" : "no");
fprintf(stdout, " delay: %"PRIu32"\n", info->delay);
fprintf(stdout, " rect:\n");
fprintf(stdout, " x: %"PRIu32"\n", info->rect.x0);
fprintf(stdout, " y: %"PRIu32"\n", info->rect.y0);
fprintf(stdout, " w: %"PRIu32"\n", info->rect.x1 - info->rect.x0);
fprintf(stdout, " h: %"PRIu32"\n", info->rect.y1 - info->rect.y0);
}
static void decode(FILE* ppm, const char *name, nsgif_t *gif)
{
nsgif_error err;
uint32_t frame_prev = 0;
const nsgif_info_t *info;
info = nsgif_get_info(gif);
if (ppm != NULL) {
fprintf(ppm, "P3\n");
fprintf(ppm, "# %s\n", name);
fprintf(ppm, "# width %u \n", info->width);
fprintf(ppm, "# height %u \n", info->height);
fprintf(ppm, "# frame_count %u \n", info->frame_count);
fprintf(ppm, "# loop_max %u \n", info->loop_max);
fprintf(ppm, "%u %u 256\n", info->width,
info->height * info->frame_count);
}
if (nsgif_options.info == true) {
print_gif_info(info);
}
/* decode the frames */
while (true) {
nsgif_bitmap_t *bitmap;
const uint8_t *image;
uint32_t frame_new;
uint32_t delay_cs;
nsgif_rect_t area;
err = nsgif_frame_prepare(gif, &area,
&delay_cs, &frame_new);
if (err != NSGIF_OK) {
warning("nsgif_frame_prepare", err);
return;
}
if (frame_new < frame_prev) {
/* Must be an animation that loops. We only care about
* decoding each frame once in this utility. */
return;
}
frame_prev = frame_new;
err = nsgif_frame_decode(gif, frame_new, &bitmap);
if (err != NSGIF_OK) {
warning("nsgif_decode_frame", err);
return;
}
if (nsgif_options.info == true) {
const nsgif_frame_info_t *f_info;
f_info = nsgif_get_frame_info(gif, frame_new);
assert(f_info != NULL);
print_gif_frame_info(f_info);
}
if (ppm != NULL) {
fprintf(ppm, "# frame %u:\n", frame_new);
image = (const uint8_t *) bitmap;
for (uint32_t y = 0; y != info->height; y++) {
for (uint32_t x = 0; x != info->width; x++) {
size_t z = (y * info->width + x) * 4;
fprintf(ppm, "%u %u %u ",
image[z],
image[z + 1],
image[z + 2]);
}
fprintf(ppm, "\n");
}
}
if (delay_cs == NSGIF_INFINITE) {
/** This frame is the last. */
return;
}
}
}
int main(int argc, char *argv[])
{
const nsgif_bitmap_cb_vt bitmap_callbacks = {
.create = bitmap_create,
.destroy = bitmap_destroy,
.get_buffer = bitmap_get_buffer,
};
size_t size;
nsgif_t *gif;
uint8_t *data;
nsgif_error err;
FILE *ppm = NULL;
/* Override default options with any command line args */
if (!cli_parse(&cli, argc, (void *)argv)) {
cli_help(&cli, argv[0]);
return EXIT_FAILURE;
}
if (nsgif_options.ppm != NULL) {
ppm = fopen(nsgif_options.ppm, "w+");
if (ppm == NULL) {
fprintf(stderr, "Unable to open %s for writing\n",
nsgif_options.ppm);
return EXIT_FAILURE;
}
}
/* create our gif animation */
err = nsgif_create(&bitmap_callbacks, &gif);
if (err != NSGIF_OK) {
warning("nsgif_create", err);
return EXIT_FAILURE;
}
/* load file into memory */
data = load_file(nsgif_options.file, &size);
/* Scan the raw data */
err = nsgif_data_scan(gif, size, data);
if (err != NSGIF_OK) {
warning("nsgif_data_scan", err);
nsgif_destroy(gif);
free(data);
return EXIT_FAILURE;
}
if (nsgif_options.loops == 0) {
nsgif_options.loops = 1;
}
for (uint64_t i = 0; i < nsgif_options.loops; i++) {
decode((i == 0) ? ppm : NULL, nsgif_options.file, gif);
}
if (ppm != NULL) {
fclose(ppm);
}
/* clean up */
nsgif_destroy(gif);
free(data);
return 0;
}

View File

@ -7,9 +7,15 @@ 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/README.md README-ns.md
cp libnsgif/include/nsgif.h .
cp libnsgif/src/lzw.[ch] .
cp libnsgif/src/gif.c .
cp libnsgif/test/cli.[ch] test/
cp libnsgif/test/nsgif.c test/
if [ -d "patches" ]
then

View File

@ -1,21 +0,0 @@
/*
* Copyright 2003 James Bursa <bursa@users.sourceforge.net>
* Copyright 2004 John Tytgat <John.Tytgat@aaug.net>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
#include <stdio.h>
#ifndef _LIBNSGIF_LOG_H_
#define _LIBNSGIF_LOG_H_
#ifdef NDEBUG
# define LOG(x) ((void) 0)
#else
# define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
#endif /* NDEBUG */
#endif /* _LIBNSGIF_LOG_H_ */

View File

@ -71,7 +71,7 @@
#ifdef HAVE_NSGIF
#include <libnsgif/libnsgif.h>
#include <libnsgif/nsgif.h>
#define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_nsgif_get_type())
#define VIPS_FOREIGN_LOAD_GIF( obj ) \
@ -105,19 +105,19 @@ typedef struct _VipsForeignLoadNsgif {
/* The animation created by libnsgif.
*/
gif_animation *anim;
nsgif_t *anim;
/* The data/size pair we pass to libnsgif.
*/
unsigned char *data;
size_t size;
/* The frame_count, after we have removed undisplayable frames.
/* Information about the current GIF.
*/
int frame_count_displayable;
const nsgif_info_t *info;
/* Delays between frames (in milliseconds). Array of length
* @frame_count_displayable.
* @info->frame_count.
*/
int *delay;
@ -137,40 +137,13 @@ G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadNsgif, vips_foreign_load_nsgif,
VIPS_TYPE_FOREIGN_LOAD );
static const char *
vips_foreign_load_nsgif_errstr( gif_result result )
vips_foreign_load_nsgif_errstr( nsgif_error result )
{
switch( result ) {
case GIF_WORKING:
return( _( "Working" ) );
case GIF_OK:
return( _( "OK" ) );
case GIF_FRAME_DATA_ERROR:
return( _( "GIF frame data error" ) );
case GIF_INSUFFICIENT_DATA:
return( _( "Incomplete data" ) );
case GIF_DATA_ERROR:
return( _( "GIF header data error" ) );
case GIF_INSUFFICIENT_MEMORY:
return( _( "Insuficient memory to process" ) );
case GIF_FRAME_NO_DISPLAY:
return( _( "No display" ) );
case GIF_END_OF_FRAME:
return( _( "At end of frame" ) );
default:
return( _( "Unknown error" ) );
}
return nsgif_strerror( result );
}
static void
vips_foreign_load_nsgif_error( VipsForeignLoadNsgif *gif, gif_result result )
vips_foreign_load_nsgif_error( VipsForeignLoadNsgif *gif, nsgif_error result )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
@ -186,8 +159,7 @@ vips_foreign_load_nsgif_dispose( GObject *gobject )
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_dispose:\n" );
if( gif->anim ) {
gif_finalise( gif->anim );
VIPS_FREE( gif->anim );
nsgif_destroy( gif->anim );
}
VIPS_UNREF( gif->source );
VIPS_FREE( gif->delay );
@ -224,60 +196,45 @@ vips_foreign_load_nsgif_is_a_source( VipsSource *source )
}
#ifdef VERBOSE
static const char *
dispose_name( int restore )
{
switch( restore ) {
case 1: return( "combine" );
case 2: return( "clear" );
case 3: return( "restore" );
case 4: return( "quirk restore" );
default: return( "none" );
}
}
static void
print_frame( gif_frame *frame )
print_frame( const nsgif_frame_info_t *frame )
{
if ( frame == NULL )
return;
printf( "frame:\n" );
printf( " display = %d\n", frame->display );
printf( " frame_delay = %d\n", frame->frame_delay );
printf( " virgin = %d\n", frame->virgin );
printf( " opaque = %d\n", frame->opaque );
printf( " redraw_required = %d\n", frame->redraw_required );
printf( " disposal_method = %d (%s)\n",
frame->disposal_method,
dispose_name( frame->disposal_method ) );
printf( " delay = %d\n", frame->delay );
// printf( " opaque = %d\n", frame->opaque );
printf( " disposal = %d (%s)\n",
frame->disposal, nsgif_str_disposal( frame->disposal ) );
printf( " transparency = %d\n", frame->transparency );
printf( " transparency_index = %d\n", frame->transparency_index );
printf( " redraw_x = %d\n", frame->redraw_x );
printf( " redraw_y = %d\n", frame->redraw_y );
printf( " redraw_width = %d\n", frame->redraw_width );
printf( " redraw_height = %d\n", frame->redraw_height );
// printf( " transparency_index = %d\n", frame->transparency_index );
printf( " rect.x0 = %u\n", frame->rect.x0 );
printf( " rect.y0 = %u\n", frame->rect.y0 );
printf( " rect.x1 = %u\n", frame->rect.x1 );
printf( " rect.y1 = %u\n", frame->rect.y1 );
}
static void
print_animation( gif_animation *anim )
print_animation( nsgif_t *anim, const nsgif_info_t *info )
{
int i;
printf( "animation:\n" );
printf( " width = %d\n", anim->width );
printf( " height = %d\n", anim->height );
printf( " frame_count = %d\n", anim->frame_count );
printf( " frame_count_partial = %d\n", anim->frame_count_partial );
printf( " decoded_frame = %d\n", anim->decoded_frame );
printf( " frame_image = %p\n", anim->frame_image );
printf( " loop_count = %d\n", anim->loop_count );
printf( " frame_holders = %d\n", anim->frame_holders );
printf( " colour_table_size = %d\n", anim->colour_table_size );
printf( " global_colours = %d\n", anim->global_colours );
printf( " global_colour_table = %p\n", anim->global_colour_table );
printf( " local_colour_table = %p\n", anim->local_colour_table );
printf( " width = %d\n", info->width );
printf( " height = %d\n", info->height );
printf( " frame_count = %d\n", info->frame_count );
printf( " loop_count = %d\n", info->loop_count );
// printf( " colour_table_size = %d\n", anim->colour_table_size );
// printf( " global_colours = %d\n", anim->global_colours );
// printf( " global_colour_table = %p\n", anim->global_colour_table );
// printf( " local_colour_table = %p\n", anim->local_colour_table );
for( i = 0; i < anim->frame_holders; i++ ) {
for( i = 0; i < info->frame_count; i++ ) {
printf( "%d ", i );
print_frame( &anim->frames[i] );
print_frame( nsgif_get_frame_info( anim, i ) );
}
}
#endif /*VERBOSE*/
@ -286,10 +243,12 @@ static int
vips_foreign_load_nsgif_set_header( VipsForeignLoadNsgif *gif,
VipsImage *image )
{
double array[3];
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_set_header:\n" );
vips_image_init_fields( image,
gif->anim->width, gif->anim->height * gif->n,
gif->info->width, gif->info->height * gif->n,
gif->has_transparency ? 4 : 3,
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
@ -300,29 +259,19 @@ vips_foreign_load_nsgif_set_header( VipsForeignLoadNsgif *gif,
*/
if( gif->n > 1 )
vips_image_set_int( image,
VIPS_META_PAGE_HEIGHT, gif->anim->height );
VIPS_META_PAGE_HEIGHT, gif->info->height );
vips_image_set_int( image, VIPS_META_N_PAGES,
gif->frame_count_displayable );
vips_image_set_int( image, "loop", gif->anim->loop_count );
gif->info->frame_count );
vips_image_set_int( image, "loop", gif->info->loop_max );
vips_image_set_array_int( image, "delay",
gif->delay, gif->frame_count_displayable );
gif->delay, gif->info->frame_count );
if( gif->anim->global_colours &&
gif->anim->global_colour_table &&
gif->anim->bg_index >= 0 &&
gif->anim->bg_index < gif->anim->colour_table_size ) {
int index = gif->anim->bg_index;
unsigned char *entry = (unsigned char *)
&gif->anim->global_colour_table[index];
array[0] = gif->info->background[0];
array[1] = gif->info->background[1];
array[2] = gif->info->background[2];
double array[3];
array[0] = entry[0];
array[1] = entry[1];
array[2] = entry[2];
vips_image_set_array_double( image, "background", array, 3 );
}
vips_image_set_array_double( image, "background", array, 3 );
VIPS_SETSTR( image->filename,
vips_connection_filename( VIPS_CONNECTION( gif->source ) ) );
@ -333,8 +282,8 @@ vips_foreign_load_nsgif_set_header( VipsForeignLoadNsgif *gif,
* but we want to keep the old behavior untouched!
*/
vips_image_set_int( image,
"gif-loop", gif->anim->loop_count == 0 ?
0 : gif->anim->loop_count - 1 );
"gif-loop", gif->info->loop_max == 0 ?
0 : gif->info->loop_max - 1 );
/* The deprecated gif-delay field is in centiseconds.
*/
@ -359,7 +308,7 @@ vips_foreign_load_nsgif_header( VipsForeignLoad *load )
const void *data;
size_t size;
gif_result result;
nsgif_error result;
int i;
VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_header:\n" );
@ -371,55 +320,40 @@ vips_foreign_load_nsgif_header( VipsForeignLoad *load )
return( -1 );
vips_source_minimise( gif->source );
result = gif_initialise( gif->anim, size, (void *) data );
VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result );
result = nsgif_data_scan( gif->anim, size, (void *) data );
VIPS_DEBUG_MSG( "nsgif_data_scan() = %s\n", nsgif_strerror( result ) );
gif->info = nsgif_get_info(gif->anim);
#ifdef VERBOSE
print_animation( gif->anim );
print_animation( gif->anim, gif->info );
#endif /*VERBOSE*/
if( result != GIF_OK &&
result != GIF_WORKING &&
result != GIF_INSUFFICIENT_FRAME_DATA ) {
if( result != NSGIF_OK ) {
vips_foreign_load_nsgif_error( gif, result );
return( -1 );
}
else if( result == GIF_INSUFFICIENT_FRAME_DATA &&
load->fail_on >= VIPS_FAIL_ON_TRUNCATED ) {
vips_error( class->nickname, "%s", _( "truncated GIF" ) );
return( -1 );
}
/* Many GIFs have dead frames at the end. Remove these from our count.
*/
for( i = gif->anim->frame_count - 1;
i >= 0 && !gif->anim->frames[i].display; i-- )
;
gif->frame_count_displayable = i + 1;
#ifdef VERBOSE
if( gif->frame_count_displayable != gif->anim->frame_count )
printf( "vips_foreign_load_nsgif_open: "
"removed %d undisplayable frames\n",
gif->anim->frame_count - gif->frame_count_displayable );
#endif /*VERBOSE*/
if( !gif->frame_count_displayable ) {
if( !gif->info->frame_count ) {
vips_error( class->nickname, "%s", _( "no frames in GIF" ) );
return( -1 );
}
/* Check for any transparency.
*/
for( i = 0; i < gif->frame_count_displayable; i++ )
if( gif->anim->frames[i].transparency ) {
for( i = 0; i < gif->info->frame_count; i++ ) {
const nsgif_frame_info_t *frame_info;
frame_info = nsgif_get_frame_info( gif->anim, i );
if( frame_info != NULL && frame_info->transparency ) {
gif->has_transparency = TRUE;
break;
}
}
if( gif->n == -1 )
gif->n = gif->frame_count_displayable - gif->page;
gif->n = gif->info->frame_count - gif->page;
if( gif->page < 0 ||
gif->n <= 0 ||
gif->page + gif->n > gif->frame_count_displayable ) {
gif->page + gif->n > gif->info->frame_count ) {
vips_error( class->nickname, "%s", _( "bad page number" ) );
return( -1 );
}
@ -428,12 +362,20 @@ vips_foreign_load_nsgif_header( VipsForeignLoad *load )
*/
VIPS_FREE( gif->delay );
if( !(gif->delay = VIPS_ARRAY( NULL,
gif->frame_count_displayable, int )) )
gif->info->frame_count, int )) )
return( -1 );
for( i = 0; i < gif->frame_count_displayable; i++ )
gif->delay[i] = 10 * gif->anim->frames[i].frame_delay;
for( i = 0; i < gif->info->frame_count; i++ ) {
const nsgif_frame_info_t *frame_info;
gif->gif_delay = gif->anim->frames[0].frame_delay;
frame_info = nsgif_get_frame_info( gif->anim, i );
if ( frame_info == NULL ) {
vips_error( class->nickname, "%s", _( "bad frame" ) );
return( -1 );
}
gif->delay[i] = 10 * frame_info->delay;
}
gif->gif_delay = gif->delay[0] / 10;
vips_foreign_load_nsgif_set_header( gif, load->out );
@ -444,7 +386,9 @@ static int
vips_foreign_load_nsgif_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsRect *r = &or->valid;
int prev_page = -1;
nsgif_bitmap_t *bitmap;
VipsRect *r = &or->valid;
VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) a;
int y;
@ -457,30 +401,30 @@ vips_foreign_load_nsgif_generate( VipsRegion *or,
for( y = 0; y < r->height; y++ ) {
/* The page for this output line, and the line number in page.
*/
int page = (r->top + y) / gif->anim->height + gif->page;
int line = (r->top + y) % gif->anim->height;
int page = (r->top + y) / gif->info->height + gif->page;
int line = (r->top + y) % gif->info->height;
gif_result result;
nsgif_error result;
VipsPel *p, *q;
g_assert( line >= 0 && line < gif->anim->height );
g_assert( page >= 0 && page < gif->frame_count_displayable );
g_assert( line >= 0 && line < gif->info->height );
g_assert( page >= 0 && page < gif->info->frame_count );
if( gif->anim->decoded_frame != page ) {
result = gif_decode_frame( gif->anim, page );
VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n",
if( prev_page != page ) {
result = nsgif_frame_decode( gif->anim, page, &bitmap );
VIPS_DEBUG_MSG( " nsgif_frame_decode(%d) = %d\n",
page, result );
if( result != GIF_OK ) {
vips_foreign_load_nsgif_error( gif, result );
if( result != NSGIF_OK ) {
vips_foreign_load_nsgif_error( gif, result );
return( -1 );
}
#ifdef VERBOSE
print_animation( gif->anim );
#endif /*VERBOSE*/
prev_page = page;
}
p = gif->anim->frame_image +
line * gif->anim->width * sizeof( int );
p = bitmap + line * gif->info->width * sizeof( int );
q = VIPS_REGION_ADDR( or, 0, r->top + y );
if( gif->has_transparency )
memcpy( q, p, VIPS_REGION_SIZEOF_LINE( or ) );
@ -587,23 +531,6 @@ vips_foreign_load_nsgif_bitmap_create( int width, int height )
return g_malloc0( (gsize) width * height * 4 );
}
static void
vips_foreign_load_nsgif_bitmap_set_opaque( void *bitmap, bool opaque )
{
(void) opaque; /* unused */
(void) bitmap; /* unused */
g_assert( bitmap );
}
static bool
vips_foreign_load_nsgif_bitmap_test_opaque( void *bitmap )
{
(void) bitmap; /* unused */
g_assert( bitmap );
return( false );
}
static unsigned char *
vips_foreign_load_nsgif_bitmap_get_buffer( void *bitmap )
{
@ -619,29 +546,24 @@ vips_foreign_load_nsgif_bitmap_destroy( void *bitmap )
g_free( bitmap );
}
static void
vips_foreign_load_nsgif_bitmap_modified( void *bitmap )
{
(void) bitmap; /* unused */
g_assert( bitmap );
return;
}
static gif_bitmap_callback_vt vips_foreign_load_nsgif_bitmap_callbacks = {
static nsgif_bitmap_cb_vt vips_foreign_load_nsgif_bitmap_callbacks = {
vips_foreign_load_nsgif_bitmap_create,
vips_foreign_load_nsgif_bitmap_destroy,
vips_foreign_load_nsgif_bitmap_get_buffer,
vips_foreign_load_nsgif_bitmap_set_opaque,
vips_foreign_load_nsgif_bitmap_test_opaque,
vips_foreign_load_nsgif_bitmap_modified
};
static void
vips_foreign_load_nsgif_init( VipsForeignLoadNsgif *gif )
{
gif->anim = g_new0( gif_animation, 1 );
gif_create( gif->anim, &vips_foreign_load_nsgif_bitmap_callbacks );
nsgif_error result = nsgif_create(
&vips_foreign_load_nsgif_bitmap_callbacks,
&gif->anim );
if (result != NSGIF_OK) {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
vips_error( class->nickname, "%s",
nsgif_strerror( result ) );
return;
}
gif->n = 1;
}