NetSurf
gif.c
Go to the documentation of this file.
1/*
2 * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
3 * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
4 * Copyright 2008 Sean Fox <dyntryx@gmail.com>
5 *
6 * This file is part of NetSurf, http://www.netsurf-browser.org/
7 *
8 * NetSurf is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * NetSurf is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * \file
23 *
24 * Content for image/gif implementation
25 *
26 * All GIFs are dynamically decompressed using the routines that gifread.c
27 * provides. Whilst this allows support for progressive decoding, it is
28 * not implemented here as NetSurf currently does not provide such support.
29 *
30 * [rjw] - Sun 4th April 2004
31 */
32
33#include <assert.h>
34#include <string.h>
35#include <stdbool.h>
36#include <stdlib.h>
37
38#include <nsutils/assert.h>
39
40#include <nsgif.h>
41
42#include "utils/log.h"
43#include "utils/utils.h"
44#include "utils/messages.h"
45#include "utils/nsoption.h"
46#include "netsurf/misc.h"
47#include "netsurf/bitmap.h"
48#include "netsurf/content.h"
49#include "content/llcache.h"
50#include "content/content.h"
54#include "desktop/bitmap.h"
55
56#include "image/image.h"
57#include "image/gif.h"
58
59typedef struct gif_content {
60 struct content base;
61
62 nsgif_t *gif; /**< GIF animation data */
63 uint32_t current_frame; /**< current frame to display [0...(max-1)] */
65
66static inline nserror gif__nsgif_error_to_ns(nsgif_error gif_res)
67{
68 nserror err;
69
70 switch (gif_res) {
71 case NSGIF_ERR_OOM:
72 err = NSERROR_NOMEM;
73 break;
74 default:
76 break;
77 }
78
79 return err;
80}
81
82/**
83 * Callback for libnsgif; forwards the call to bitmap_create()
84 *
85 * \param width width of image in pixels
86 * \param height width of image in pixels
87 * \return an opaque struct bitmap, or NULL on memory exhaustion
88 */
89static void *gif_bitmap_create(int width, int height)
90{
92}
93
94/**
95 * Convert client bitmap format to a LibNSGIF format specifier.
96 */
97static nsgif_bitmap_fmt_t nsgif__get_bitmap_format(void)
98{
99 ns_static_assert((int)BITMAP_LAYOUT_R8G8B8A8 == (int)NSGIF_BITMAP_FMT_R8G8B8A8);
100 ns_static_assert((int)BITMAP_LAYOUT_B8G8R8A8 == (int)NSGIF_BITMAP_FMT_B8G8R8A8);
101 ns_static_assert((int)BITMAP_LAYOUT_A8R8G8B8 == (int)NSGIF_BITMAP_FMT_A8R8G8B8);
102 ns_static_assert((int)BITMAP_LAYOUT_A8B8G8R8 == (int)NSGIF_BITMAP_FMT_A8B8G8R8);
103 ns_static_assert((int)BITMAP_LAYOUT_RGBA8888 == (int)NSGIF_BITMAP_FMT_RGBA8888);
104 ns_static_assert((int)BITMAP_LAYOUT_BGRA8888 == (int)NSGIF_BITMAP_FMT_BGRA8888);
105 ns_static_assert((int)BITMAP_LAYOUT_ARGB8888 == (int)NSGIF_BITMAP_FMT_ARGB8888);
106 ns_static_assert((int)BITMAP_LAYOUT_ABGR8888 == (int)NSGIF_BITMAP_FMT_ABGR8888);
107
108 return (nsgif_bitmap_fmt_t)bitmap_fmt.layout;
109}
110
112{
113 nsgif_error gif_res;
114 const nsgif_bitmap_cb_vt gif_bitmap_callbacks = {
115 .create = gif_bitmap_create,
116 .destroy = guit->bitmap->destroy,
117 .get_buffer = guit->bitmap->get_buffer,
118 .set_opaque = guit->bitmap->set_opaque,
119 .test_opaque = bitmap_test_opaque,
120 .modified = guit->bitmap->modified,
121 };
122
123 gif_res = nsgif_create(&gif_bitmap_callbacks,
125 if (gif_res != NSGIF_OK) {
126 nserror err = gif__nsgif_error_to_ns(gif_res);
127 content_broadcast_error(&c->base, err, NULL);
128 return err;
129 }
130
131 return NSERROR_OK;
132}
133
135 lwc_string *imime_type, const struct http_parameter *params,
137 bool quirks, struct content **c)
138{
140 nserror error;
141
142 result = calloc(1, sizeof(gif_content));
143 if (result == NULL)
144 return NSERROR_NOMEM;
145
146 error = content__init(&result->base, handler, imime_type, params,
148 if (error != NSERROR_OK) {
149 free(result);
150 return error;
151 }
152
154 if (error != NSERROR_OK) {
155 free(result);
156 return error;
157 }
158
159 *c = (struct content *) result;
160
161 return NSERROR_OK;
162}
163
164/**
165 * Scheduler callback. Performs any necessary animation.
166 *
167 * \param p The content to animate
168*/
169static void gif_animate_cb(void *p);
170
171/**
172 * Performs any necessary animation.
173 *
174 * \param p The content to animate
175*/
176static nserror gif__animate(gif_content *gif, bool redraw)
177{
178 nsgif_error gif_res;
179 nsgif_rect_t rect;
180 uint32_t delay;
181 uint32_t f;
182
183 gif_res = nsgif_frame_prepare(gif->gif, &rect, &delay, &f);
184 if (gif_res != NSGIF_OK) {
185 return gif__nsgif_error_to_ns(gif_res);
186 }
187
188 gif->current_frame = f;
189
190 /* Continue animating if we should */
191 if (nsoption_bool(animate_images) && delay != NSGIF_INFINITE) {
192 guit->misc->schedule(delay * 10, gif_animate_cb, gif);
193 }
194
195 if (redraw) {
196 union content_msg_data data;
197
198 /* area within gif to redraw */
199 data.redraw.x = rect.x0;
200 data.redraw.y = rect.y0;
201 data.redraw.width = rect.x1 - rect.x0;
202 data.redraw.height = rect.y1 - rect.y0;
203
205 }
206
207 return NSERROR_OK;
208}
209
210static void gif_animate_cb(void *p)
211{
212 gif_content *gif = p;
213
214 gif__animate(gif, true);
215}
216
217static bool gif_convert(struct content *c)
218{
219 gif_content *gif = (gif_content *) c;
220 const nsgif_info_t *gif_info;
221 const uint8_t *data;
222 nsgif_error gif_err;
223 nserror err;
224 size_t size;
225 char *title;
226
227 /* Get the animation */
228 data = content__get_source_data(c, &size);
229
230 /* Initialise the GIF */
231 gif_err = nsgif_data_scan(gif->gif, size, data);
232 if (gif_err != NSGIF_OK) {
233 NSLOG(netsurf, INFO, "nsgif scan: %s", nsgif_strerror(gif_err));
234 /* Not fatal unless we have no frames. */
235 }
236
237 nsgif_data_complete(gif->gif);
238
239 gif_info = nsgif_get_info(gif->gif);
240 assert(gif_info != NULL);
241
242 /* Abort on bad GIFs */
243 if (gif_info->frame_count == 0) {
244 err = gif__nsgif_error_to_ns(gif_err);
245 content_broadcast_error(c, err, "GIF with no frames.");
246 return false;
247 } else if (gif_info->width == 0 || gif_info->height == 0) {
248 err = gif__nsgif_error_to_ns(gif_err);
249 content_broadcast_error(c, err, "Zero size image.");
250 return false;
251 }
252
253 /* Store our content width, height and calculate size */
254 c->width = gif_info->width;
255 c->height = gif_info->height;
256 c->size += (gif_info->width * gif_info->height * 4) + 16 + 44;
257
258 /* set title text */
259 title = messages_get_buff("GIFTitle",
261 c->width, c->height);
262 if (title != NULL) {
264 free(title);
265 }
266
267 err = gif__animate(gif, false);
268 if (err != NSERROR_OK) {
270 return false;
271 }
272
273 /* Exit as a success */
276
277 /* Done: update status bar */
278 content_set_status(c, "");
279 return true;
280}
281
282/**
283 * Updates the GIF bitmap to display the current frame
284 *
285 * \param gif The gif context to update.
286 * \return NSGIF_OK on success else apropriate error code.
287 */
288static nsgif_error gif_get_frame(gif_content *gif,
289 nsgif_bitmap_t **bitmap)
290{
291 uint32_t current_frame = gif->current_frame;
292 if (!nsoption_bool(animate_images)) {
293 current_frame = 0;
294 }
295
296 return nsgif_frame_decode(gif->gif, current_frame, bitmap);
297}
298
299static bool gif_redraw(struct content *c, struct content_redraw_data *data,
300 const struct rect *clip, const struct redraw_context *ctx)
301{
302 gif_content *gif = (gif_content *) c;
303 nsgif_bitmap_t *bitmap;
304
305 if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
306 return false;
307 }
308
309 return image_bitmap_plot(bitmap, data, clip, ctx);
310}
311
312static void gif_destroy(struct content *c)
313{
314 gif_content *gif = (gif_content *) c;
315
316 /* Free all the associated memory buffers */
318 nsgif_destroy(gif->gif);
319}
320
321static nserror gif_clone(const struct content *old, struct content **newc)
322{
323 gif_content *gif;
324 nserror error;
325
326 gif = calloc(1, sizeof(gif_content));
327 if (gif == NULL)
328 return NSERROR_NOMEM;
329
330 error = content__clone(old, &gif->base);
331 if (error != NSERROR_OK) {
332 content_destroy(&gif->base);
333 return error;
334 }
335
336 /* Simply replay creation and conversion of content */
337 error = gif_create_gif_data(gif);
338 if (error != NSERROR_OK) {
339 content_destroy(&gif->base);
340 return error;
341 }
342
343 if (old->status == CONTENT_STATUS_READY ||
344 old->status == CONTENT_STATUS_DONE) {
345 if (gif_convert(&gif->base) == false) {
346 content_destroy(&gif->base);
348 }
349 }
350
351 *newc = (struct content *) gif;
352
353 return NSERROR_OK;
354}
355
356static void gif_add_user(struct content *c)
357{
358 gif_content *gif = (gif_content *) c;
359
360 /* Ensure this content has already been converted.
361 * If it hasn't, the animation will start at the conversion phase instead. */
362 if (gif->gif == NULL) return;
363
364 if (content_count_users(c) == 1) {
365 /* First user, and content already converted, so start the animation. */
366 if (nsgif_reset(gif->gif) == NSGIF_OK) {
367 gif__animate(gif, true);
368 }
369 }
370}
371
372static void gif_remove_user(struct content *c)
373{
374 if (content_count_users(c) == 1) {
375 /* Last user is about to be removed from this content, so stop the animation. */
377 }
378}
379
380static nsgif_bitmap_t *gif_get_bitmap(
381 const struct content *c, void *context)
382{
383 gif_content *gif = (gif_content *) c;
384 nsgif_bitmap_t *bitmap;
385
386 if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
387 return NULL;
388 }
389
390 return bitmap;
391}
392
394{
395 return CONTENT_IMAGE;
396}
397
398static bool gif_content_is_opaque(struct content *c)
399{
400 gif_content *gif = (gif_content *) c;
401 nsgif_bitmap_t *bitmap;
402
403 if (gif_get_frame(gif, &bitmap) != NSGIF_OK) {
404 return false;
405 }
406
407 return guit->bitmap->get_opaque(bitmap);
408}
409
412 .data_complete = gif_convert,
413 .destroy = gif_destroy,
414 .redraw = gif_redraw,
415 .clone = gif_clone,
416 .add_user = gif_add_user,
417 .remove_user = gif_remove_user,
418 .get_internal = gif_get_bitmap,
419 .type = gif_content_type,
420 .is_opaque = gif_content_is_opaque,
421 .no_share = false,
422};
423
424static const char *gif_types[] = {
425 "image/gif"
426};
427
STATIC char result[100]
Definition: arexx.c:77
Content handling interface.
bool image_bitmap_plot(struct bitmap *bitmap, struct content_redraw_data *data, const struct rect *clip, const struct redraw_context *ctx)
Common image content handler bitmap plot call.
Definition: image.c:116
Initialisation/finalisation of image handlers.
void content_destroy(struct content *c)
Destroy and free a content.
Definition: content.c:354
void content_broadcast(struct content *c, content_msg msg, const union content_msg_data *data)
Send a message to all users.
Definition: content.c:752
void content_set_done(struct content *c)
Put a content in status CONTENT_STATUS_DONE.
Definition: content.c:299
uint32_t content_count_users(struct content *c)
Count users for the content.
Definition: content.c:718
bool content__set_title(struct content *c, const char *title)
Set title associated with content.
Definition: content.c:1090
nserror content__init(struct content *c, const content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks)
Definition: content.c:190
const uint8_t * content__get_source_data(struct content *c, size_t *size)
Retrieve source of content.
Definition: content.c:1216
nserror content__clone(const struct content *c, struct content *nc)
Clone a content's data members.
Definition: content.c:1382
void content_set_ready(struct content *c)
Put a content in status CONTENT_STATUS_READY and unlock the content.
Definition: content.c:285
void content_set_status(struct content *c, const char *status_message)
Updates content with new status.
Definition: content.c:270
void content_broadcast_error(struct content *c, nserror errorcode, const char *msg)
Send an error message to all users.
Definition: content.c:769
Protected interface to Content handling.
@ CONTENT_STATUS_READY
Some parts of content still being loaded, but can be displayed.
Definition: content_type.h:92
@ CONTENT_STATUS_DONE
Content has completed all processing.
Definition: content_type.h:95
content_type
The type of a content.
Definition: content_type.h:53
@ CONTENT_IMAGE
All images.
Definition: content_type.h:67
@ CONTENT_MSG_REDRAW
needs redraw (eg.
Definition: content_type.h:134
Internal core bitmap interface.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_GIF_ERROR
A GIF error occurred.
Definition: errors.h:40
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_CLONE_FAILED
Failed to clone handle.
Definition: errors.h:37
@ NSERROR_OK
No error.
Definition: errors.h:30
CONTENT_FACTORY_REGISTER_TYPES(nsgif, gif_types, gif_content_handler)
static const content_handler gif_content_handler
Definition: gif.c:410
static void gif_remove_user(struct content *c)
Definition: gif.c:372
static nserror gif__animate(gif_content *gif, bool redraw)
Performs any necessary animation.
Definition: gif.c:176
static bool gif_convert(struct content *c)
Definition: gif.c:217
static void * gif_bitmap_create(int width, int height)
Callback for libnsgif; forwards the call to bitmap_create()
Definition: gif.c:89
static content_type gif_content_type(void)
Definition: gif.c:393
static nserror gif_create(const content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c)
Definition: gif.c:134
static void gif_destroy(struct content *c)
Definition: gif.c:312
static const char * gif_types[]
Definition: gif.c:424
static void gif_animate_cb(void *p)
Scheduler callback.
Definition: gif.c:210
struct gif_content gif_content
static nsgif_bitmap_t * gif_get_bitmap(const struct content *c, void *context)
Definition: gif.c:380
static void gif_add_user(struct content *c)
Definition: gif.c:356
static nserror gif_create_gif_data(gif_content *c)
Definition: gif.c:111
static nsgif_error gif_get_frame(gif_content *gif, nsgif_bitmap_t **bitmap)
Updates the GIF bitmap to display the current frame.
Definition: gif.c:288
static nserror gif__nsgif_error_to_ns(nsgif_error gif_res)
Definition: gif.c:66
static bool gif_redraw(struct content *c, struct content_redraw_data *data, const struct rect *clip, const struct redraw_context *ctx)
Definition: gif.c:299
static bool gif_content_is_opaque(struct content *c)
Definition: gif.c:398
static nsgif_bitmap_fmt_t nsgif__get_bitmap_format(void)
Convert client bitmap format to a LibNSGIF format specifier.
Definition: gif.c:97
static nserror gif_clone(const struct content *old, struct content **newc)
Definition: gif.c:321
Content for image/gif (interface).
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
Generic bitmap handling interface.
bool bitmap_test_opaque(void *bitmap)
Test whether a bitmap is completely opaque (no transparency).
Definition: bitmap.c:316
@ BITMAP_NONE
Definition: bitmap.h:37
@ BITMAP_LAYOUT_ABGR8888
32-bit BGRA (0xAABBGGRR).
Definition: bitmap.h:91
@ BITMAP_LAYOUT_BGRA8888
32-bit BGRA (0xBBGGRRAA).
Definition: bitmap.h:75
@ BITMAP_LAYOUT_ARGB8888
32-bit ARGB (0xAARRGGBB).
Definition: bitmap.h:83
@ BITMAP_LAYOUT_B8G8R8A8
Bite-wise BGRA: Byte order: 0xBB, 0xGG, 0xRR, 0xAA.
Definition: bitmap.h:53
@ BITMAP_LAYOUT_R8G8B8A8
Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA.
Definition: bitmap.h:50
@ BITMAP_LAYOUT_A8B8G8R8
Bite-wise ABGR: Byte order: 0xAA, 0xBB, 0xGG, 0xRR.
Definition: bitmap.h:59
@ BITMAP_LAYOUT_A8R8G8B8
Bite-wise ARGB: Byte order: 0xAA, 0xRR, 0xGG, 0xBB.
Definition: bitmap.h:56
@ BITMAP_LAYOUT_RGBA8888
32-bit RGBA (0xRRGGBBAA).
Definition: bitmap.h:67
Public content interface.
Interface to platform-specific miscellaneous browser operation table.
static struct llcache_s * llcache
low level cache state
Definition: llcache.c:267
nsurl * llcache_handle_get_url(const llcache_handle *handle)
Retrieve the post-redirect URL of a low-level cache object.
Definition: llcache.c:4195
Low-level resource cache (interface)
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
char * messages_get_buff(const char *key,...)
Formatted message from a key in the global message hash.
Definition: messages.c:205
Localised message support (interface).
const char * nsurl_access_leaf(const nsurl *url)
Access a URL's path leaf as a string.
int width
Definition: gui.c:160
int height
Definition: gui.c:161
Interface to utility string handling.
Bitmap format specifier.
Definition: bitmap.h:95
enum bitmap_layout layout
Colour component layout.
Definition: bitmap.h:96
RISC OS wimp toolkit bitmap.
Definition: bitmap.c:68
Content operation function table.
nserror(* create)(const struct content_handler *handler, lwc_string *imime_type, const struct http_parameter *params, struct llcache_handle *llcache, const char *fallback_charset, bool quirks, struct content **c)
parameters to content redraw
Definition: content.h:40
Content which corresponds to a single URL.
bool quirks
Content is in quirks mode.
int height
Height dimension, if applicable.
char * fallback_charset
Fallback charset, or NULL.
int width
Width dimension, if applicable.
const struct content_handler * handler
Handler for content.
struct llcache_handle * llcache
Low-level cache object.
struct textsearch_context * context
content_status status
Current status.
unsigned int size
Estimated size of all data associated with this content.
nsgif_t * gif
GIF animation data.
Definition: gif.c:62
struct content base
Definition: gif.c:60
uint32_t current_frame
current frame to display [0...(max-1)]
Definition: gif.c:63
void(* set_opaque)(void *bitmap, bool opaque)
Set the opacity of a bitmap.
Definition: bitmap.h:151
void(* destroy)(void *bitmap)
Destroy a bitmap.
Definition: bitmap.h:143
void *(* create)(int width, int height, enum gui_bitmap_flags flags)
Create a new bitmap.
Definition: bitmap.h:136
bool(* get_opaque)(void *bitmap)
Get the opacity of a bitmap.
Definition: bitmap.h:159
void(* modified)(void *bitmap)
Marks a bitmap as modified.
Definition: bitmap.h:200
unsigned char *(* get_buffer)(void *bitmap)
Get the image buffer from a bitmap.
Definition: bitmap.h:169
nserror(* schedule)(int t, void(*callback)(void *p), void *p)
Schedule a callback.
Definition: misc.h:58
Representation of an HTTP parameter.
Definition: parameter.c:31
Handle to low-level cache object.
Definition: llcache.c:76
struct gui_misc_table * misc
Browser table.
Definition: gui_table.h:57
struct gui_bitmap_table * bitmap
Bitmap table.
Definition: gui_table.h:153
Rectangle coordinates.
Definition: types.h:40
int x0
Definition: types.h:41
int y0
Top left.
Definition: types.h:41
int x1
Definition: types.h:42
int y1
Bottom right.
Definition: types.h:42
Redraw context.
Definition: plotters.h:51
struct rect rect
Rectangle coordinates.
Extra data for some content_msg messages.
Definition: content.h:60
int x
Carret x-coord.
Definition: content.h:110
int height
Carret height.
Definition: content.h:110
const char * title
Definition: content.h:186
int y
Carret y-coord.
Definition: content.h:110
void * ctx
context passed to browser_window_search()
Definition: content.h:277
struct content_msg_data::@101 redraw
CONTENT_MSG_REDRAW - Area of content which needs redrawing.
Option reading and saving interface.
#define nsoption_bool(OPTION)
Get the value of a boolean option.
Definition: nsoption.h:304
Interface to a number of general purpose functionality.
static nserror bitmap(const struct redraw_context *ctx, struct bitmap *bitmap, int x, int y, int width, int height, colour bg, bitmap_flags_t flags)
Plot a bitmap.
Definition: plot.c:857
static nserror clip(const struct redraw_context *ctx, const struct rect *clip)
Sets a clip rectangle for subsequent plot operations.
Definition: plot.c:357