1 /* load a GIF with libnsgif
2 *
3 * 6/10/18
4 * - from gifload.c
5 */
6
7 /*
8
9 This file is part of VIPS.
10
11 VIPS is free software; you can redistribute it and/or modify
12 it under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Lesser General Public License for more details.
20
21 You should have received a copy of the GNU Lesser General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 02110-1301 USA
25
26 */
27
28 /*
29
30 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31
32 */
33
34 /*
35 #define VERBOSE
36 #define VIPS_DEBUG
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif /*HAVE_CONFIG_H*/
42 #include <vips/intl.h>
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <ctype.h>
49
50 #include <vips/vips.h>
51 #include <vips/buf.h>
52 #include <vips/internal.h>
53 #include <vips/debug.h>
54
55 /* TODO:
56 *
57 * - libnsgif does not seem to support comment metadata
58 *
59 * - it always loads the entire source file into memory
60 *
61 * Notes:
62 *
63 * - hard to detect mono images -- local_colour_table in libnsgif is only set
64 * when we decode a frame, so we can't tell just from init whether any
65 * frames have colour info
66 *
67 * - don't bother detecting alpha -- if we can't detect RGB, alpha won't help
68 * much
69 *
70 */
71
72 #ifdef HAVE_NSGIF
73
74 #include <libnsgif/libnsgif.h>
75
76 #define VIPS_TYPE_FOREIGN_LOAD_GIF (vips_foreign_load_nsgif_get_type())
77 #define VIPS_FOREIGN_LOAD_GIF( obj ) \
78 (G_TYPE_CHECK_INSTANCE_CAST( (obj), \
79 VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadNsgif ))
80 #define VIPS_FOREIGN_LOAD_GIF_CLASS( klass ) \
81 (G_TYPE_CHECK_CLASS_CAST( (klass), \
82 VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadNsgifClass))
83 #define VIPS_IS_FOREIGN_LOAD_GIF( obj ) \
84 (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FOREIGN_LOAD_GIF ))
85 #define VIPS_IS_FOREIGN_LOAD_GIF_CLASS( klass ) \
86 (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FOREIGN_LOAD_GIF ))
87 #define VIPS_FOREIGN_LOAD_GIF_GET_CLASS( obj ) \
88 (G_TYPE_INSTANCE_GET_CLASS( (obj), \
89 VIPS_TYPE_FOREIGN_LOAD_GIF, VipsForeignLoadNsgifClass ))
90
91 typedef struct _VipsForeignLoadNsgif {
92 VipsForeignLoad parent_object;
93
94 /* Load this page (frame number).
95 */
96 int page;
97
98 /* Load this many pages.
99 */
100 int n;
101
102 /* Load from this source (set by subclasses).
103 */
104 VipsSource *source;
105
106 /* The animation created by libnsgif.
107 */
108 gif_animation *anim;
109
110 /* The data/size pair we pass to libnsgif.
111 */
112 unsigned char *data;
113 size_t size;
114
115 /* The frame_count, after we have removed undisplayable frames.
116 */
117 int frame_count_displayable;
118
119 /* Delays between frames (in milliseconds). Array of length
120 * @frame_count_displayable.
121 */
122 int *delay;
123
124 /* A single centisecond value for compatibility.
125 */
126 int gif_delay;
127
128 /* If the GIF contains any frames with transparent elements.
129 */
130 gboolean has_transparency;
131
132 } VipsForeignLoadNsgif;
133
134 typedef VipsForeignLoadClass VipsForeignLoadNsgifClass;
135
136 G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadNsgif, vips_foreign_load_nsgif,
137 VIPS_TYPE_FOREIGN_LOAD );
138
139 static const char *
vips_foreign_load_nsgif_errstr(gif_result result)140 vips_foreign_load_nsgif_errstr( gif_result result )
141 {
142 switch( result ) {
143 case GIF_WORKING:
144 return( _( "Working" ) );
145
146 case GIF_OK:
147 return( _( "OK" ) );
148
149 case GIF_INSUFFICIENT_FRAME_DATA:
150 return( _( "Insufficient data to complete frame" ) );
151
152 case GIF_FRAME_DATA_ERROR:
153 return( _( "GIF frame data error" ) );
154
155 case GIF_INSUFFICIENT_DATA:
156 return( _( "Insufficient data to do anything" ) );
157
158 case GIF_DATA_ERROR:
159 return( _( "GIF header data error" ) );
160
161 case GIF_INSUFFICIENT_MEMORY:
162 return( _( "Insuficient memory to process" ) );
163
164 case GIF_FRAME_NO_DISPLAY:
165 return( _( "No display" ) );
166
167 case GIF_END_OF_FRAME:
168 return( _( "At end of frame" ) );
169
170 default:
171 return( _( "Unknown error" ) );
172 }
173 }
174
175 static void
vips_foreign_load_nsgif_error(VipsForeignLoadNsgif * gif,gif_result result)176 vips_foreign_load_nsgif_error( VipsForeignLoadNsgif *gif, gif_result result )
177 {
178 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif );
179
180 vips_error( class->nickname, "%s",
181 vips_foreign_load_nsgif_errstr( result ) );
182 }
183
184 static void
vips_foreign_load_nsgif_dispose(GObject * gobject)185 vips_foreign_load_nsgif_dispose( GObject *gobject )
186 {
187 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) gobject;
188
189 VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_dispose:\n" );
190
191 if( gif->anim ) {
192 gif_finalise( gif->anim );
193 VIPS_FREE( gif->anim );
194 }
195 VIPS_UNREF( gif->source );
196 VIPS_FREE( gif->delay );
197
198 G_OBJECT_CLASS( vips_foreign_load_nsgif_parent_class )->
199 dispose( gobject );
200 }
201
202 static VipsForeignFlags
vips_foreign_load_nsgif_get_flags_filename(const char * filename)203 vips_foreign_load_nsgif_get_flags_filename( const char *filename )
204 {
205 return( VIPS_FOREIGN_SEQUENTIAL );
206 }
207
208 static VipsForeignFlags
vips_foreign_load_nsgif_get_flags(VipsForeignLoad * load)209 vips_foreign_load_nsgif_get_flags( VipsForeignLoad *load )
210 {
211 return( VIPS_FOREIGN_SEQUENTIAL );
212 }
213
214 static gboolean
vips_foreign_load_nsgif_is_a_source(VipsSource * source)215 vips_foreign_load_nsgif_is_a_source( VipsSource *source )
216 {
217 const unsigned char *data;
218
219 if( (data = vips_source_sniff( source, 4 )) &&
220 data[0] == 'G' &&
221 data[1] == 'I' &&
222 data[2] == 'F' &&
223 data[3] == '8' )
224 return( TRUE );
225
226 return( FALSE );
227 }
228
229 #ifdef VERBOSE
230 static const char *
dispose_name(int restore)231 dispose_name( int restore )
232 {
233 switch( restore ) {
234 case 1: return( "combine" );
235 case 2: return( "clear" );
236 case 3: return( "restore" );
237 case 4: return( "quirk restore" );
238 default: return( "none" );
239 }
240 }
241
242 static void
print_frame(gif_frame * frame)243 print_frame( gif_frame *frame )
244 {
245 printf( "frame:\n" );
246 printf( " display = %d\n", frame->display );
247 printf( " frame_delay = %d\n", frame->frame_delay );
248 printf( " virgin = %d\n", frame->virgin );
249 printf( " opaque = %d\n", frame->opaque );
250 printf( " redraw_required = %d\n", frame->redraw_required );
251 printf( " disposal_method = %d (%s)\n",
252 frame->disposal_method,
253 dispose_name( frame->disposal_method ) );
254 printf( " transparency = %d\n", frame->transparency );
255 printf( " transparency_index = %d\n", frame->transparency_index );
256 printf( " redraw_x = %d\n", frame->redraw_x );
257 printf( " redraw_y = %d\n", frame->redraw_y );
258 printf( " redraw_width = %d\n", frame->redraw_width );
259 printf( " redraw_height = %d\n", frame->redraw_height );
260 }
261
262 static void
print_animation(gif_animation * anim)263 print_animation( gif_animation *anim )
264 {
265 int i;
266
267 printf( "animation:\n" );
268 printf( " width = %d\n", anim->width );
269 printf( " height = %d\n", anim->height );
270 printf( " frame_count = %d\n", anim->frame_count );
271 printf( " frame_count_partial = %d\n", anim->frame_count_partial );
272 printf( " decoded_frame = %d\n", anim->decoded_frame );
273 printf( " frame_image = %p\n", anim->frame_image );
274 printf( " loop_count = %d\n", anim->loop_count );
275 printf( " frame_holders = %d\n", anim->frame_holders );
276 printf( " background_index = %d\n", anim->background_index );
277 printf( " colour_table_size = %d\n", anim->colour_table_size );
278 printf( " global_colours = %d\n", anim->global_colours );
279 printf( " global_colour_table = %p\n", anim->global_colour_table );
280 printf( " local_colour_table = %p\n", anim->local_colour_table );
281
282 for( i = 0; i < anim->frame_holders; i++ ) {
283 printf( "%d ", i );
284 print_frame( &anim->frames[i] );
285 }
286 }
287 #endif /*VERBOSE*/
288
289 static int
vips_foreign_load_nsgif_set_header(VipsForeignLoadNsgif * gif,VipsImage * image)290 vips_foreign_load_nsgif_set_header( VipsForeignLoadNsgif *gif,
291 VipsImage *image )
292 {
293 VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_set_header:\n" );
294
295 vips_image_init_fields( image,
296 gif->anim->width, gif->anim->height * gif->n,
297 gif->has_transparency ? 4 : 3,
298 VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
299 VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
300 vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
301
302 /* Only set page-height if we have more than one page, or this could
303 * accidentally turn into an animated image later.
304 */
305 if( gif->n > 1 )
306 vips_image_set_int( image,
307 VIPS_META_PAGE_HEIGHT, gif->anim->height );
308 vips_image_set_int( image, VIPS_META_N_PAGES,
309 gif->frame_count_displayable );
310 vips_image_set_int( image, "loop", gif->anim->loop_count );
311 vips_image_set_array_int( image, "delay",
312 gif->delay, gif->frame_count_displayable );
313
314 if( gif->anim->global_colours &&
315 gif->anim->global_colour_table &&
316 gif->anim->background_index >= 0 &&
317 gif->anim->background_index < gif->anim->colour_table_size ) {
318 int index = gif->anim->background_index;
319 unsigned char *entry = (unsigned char *)
320 &gif->anim->global_colour_table[index];
321
322 double array[3];
323
324 array[0] = entry[0];
325 array[1] = entry[1];
326 array[2] = entry[2];
327
328 vips_image_set_array_double( image, "background", array, 3 );
329 }
330
331 VIPS_SETSTR( image->filename,
332 vips_connection_filename( VIPS_CONNECTION( gif->source ) ) );
333
334 /* DEPRECATED "gif-loop"
335 *
336 * Not the correct behavior as loop=1 became gif-loop=0
337 * but we want to keep the old behavior untouched!
338 */
339 vips_image_set_int( image,
340 "gif-loop", gif->anim->loop_count == 0 ?
341 0 : gif->anim->loop_count - 1 );
342
343 /* The deprecated gif-delay field is in centiseconds.
344 */
345 vips_image_set_int( image, "gif-delay", gif->gif_delay );
346
347 return( 0 );
348 }
349
350 /* Scan the GIF as quickly as we can and extract transparency, bands, pages,
351 * etc.
352 *
353 * Don't flag any errors unless we have to: we want to work for corrupt or
354 * malformed GIFs.
355 *
356 * Close as soon as we can to free up the fd.
357 */
358 static int
vips_foreign_load_nsgif_header(VipsForeignLoad * load)359 vips_foreign_load_nsgif_header( VipsForeignLoad *load )
360 {
361 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
362 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) load;
363
364 const void *data;
365 size_t size;
366 gif_result result;
367 int i;
368
369 VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_header:\n" );
370
371 /* We map in the image, then minimise to close any underlying file
372 * object. This won't unmap.
373 */
374 if( !(data = vips_source_map( gif->source, &size )) )
375 return( -1 );
376 vips_source_minimise( gif->source );
377
378 result = gif_initialise( gif->anim, size, (void *) data );
379 VIPS_DEBUG_MSG( "gif_initialise() = %d\n", result );
380 #ifdef VERBOSE
381 print_animation( gif->anim );
382 #endif /*VERBOSE*/
383 if( result != GIF_OK &&
384 result != GIF_WORKING &&
385 result != GIF_INSUFFICIENT_FRAME_DATA ) {
386 vips_foreign_load_nsgif_error( gif, result );
387 return( -1 );
388 }
389 else if( result == GIF_INSUFFICIENT_FRAME_DATA &&
390 load->fail_on >= VIPS_FAIL_ON_TRUNCATED ) {
391 vips_error( class->nickname, "%s", _( "truncated GIF" ) );
392 return( -1 );
393 }
394
395 /* Many GIFs have dead frames at the end. Remove these from our count.
396 */
397 for( i = gif->anim->frame_count - 1;
398 i >= 0 && !gif->anim->frames[i].display; i-- )
399 ;
400 gif->frame_count_displayable = i + 1;
401 #ifdef VERBOSE
402 if( gif->frame_count_displayable != gif->anim->frame_count )
403 printf( "vips_foreign_load_nsgif_open: "
404 "removed %d undisplayable frames\n",
405 gif->anim->frame_count - gif->frame_count_displayable );
406 #endif /*VERBOSE*/
407
408 if( !gif->frame_count_displayable ) {
409 vips_error( class->nickname, "%s", _( "no frames in GIF" ) );
410 return( -1 );
411 }
412
413 /* Check for any transparency.
414 */
415 for( i = 0; i < gif->frame_count_displayable; i++ )
416 if( gif->anim->frames[i].transparency ) {
417 gif->has_transparency = TRUE;
418 break;
419 }
420
421 if( gif->n == -1 )
422 gif->n = gif->frame_count_displayable - gif->page;
423
424 if( gif->page < 0 ||
425 gif->n <= 0 ||
426 gif->page + gif->n > gif->frame_count_displayable ) {
427 vips_error( class->nickname, "%s", _( "bad page number" ) );
428 return( -1 );
429 }
430
431 /* In ms, frame_delay in cs.
432 */
433 VIPS_FREE( gif->delay );
434 if( !(gif->delay = VIPS_ARRAY( NULL,
435 gif->frame_count_displayable, int )) )
436 return( -1 );
437 for( i = 0; i < gif->frame_count_displayable; i++ )
438 gif->delay[i] = 10 * gif->anim->frames[i].frame_delay;
439
440 gif->gif_delay = gif->anim->frames[0].frame_delay;
441
442 vips_foreign_load_nsgif_set_header( gif, load->out );
443
444 return( 0 );
445 }
446
447 static int
vips_foreign_load_nsgif_generate(VipsRegion * or,void * seq,void * a,void * b,gboolean * stop)448 vips_foreign_load_nsgif_generate( VipsRegion *or,
449 void *seq, void *a, void *b, gboolean *stop )
450 {
451 VipsRect *r = &or->valid;
452 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) a;
453
454 int y;
455
456 #ifdef VERBOSE
457 VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_generate: "
458 "top = %d, height = %d\n", r->top, r->height );
459 #endif /*VERBOSE*/
460
461 for( y = 0; y < r->height; y++ ) {
462 /* The page for this output line, and the line number in page.
463 */
464 int page = (r->top + y) / gif->anim->height + gif->page;
465 int line = (r->top + y) % gif->anim->height;
466
467 gif_result result;
468 VipsPel *p, *q;
469
470 g_assert( line >= 0 && line < gif->anim->height );
471 g_assert( page >= 0 && page < gif->frame_count_displayable );
472
473 if( gif->anim->decoded_frame != page ) {
474 result = gif_decode_frame( gif->anim, page );
475 VIPS_DEBUG_MSG( " gif_decode_frame(%d) = %d\n",
476 page, result );
477 if( result != GIF_OK ) {
478 vips_foreign_load_nsgif_error( gif, result );
479 return( -1 );
480 }
481 #ifdef VERBOSE
482 print_animation( gif->anim );
483 #endif /*VERBOSE*/
484 }
485
486 p = gif->anim->frame_image +
487 line * gif->anim->width * sizeof( int );
488 q = VIPS_REGION_ADDR( or, 0, r->top + y );
489 if( gif->has_transparency )
490 memcpy( q, p, VIPS_REGION_SIZEOF_LINE( or ) );
491 else {
492 int i;
493
494 for( i = 0; i < r->width; i++ ) {
495 q[0] = p[0];
496 q[1] = p[1];
497 q[2] = p[2];
498
499 q += 3;
500 p += 4;
501 }
502 }
503 }
504
505 return( 0 );
506 }
507
508 static int
vips_foreign_load_nsgif_load(VipsForeignLoad * load)509 vips_foreign_load_nsgif_load( VipsForeignLoad *load )
510 {
511 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) load;
512 VipsImage **t = (VipsImage **)
513 vips_object_local_array( VIPS_OBJECT( load ), 4 );
514
515 VIPS_DEBUG_MSG( "vips_foreign_load_nsgif_load:\n" );
516
517 /* Make the output pipeline.
518 */
519 t[0] = vips_image_new();
520 if( vips_foreign_load_nsgif_set_header( gif, t[0] ) )
521 return( -1 );
522
523 /* Strips 8 pixels high to avoid too many tiny regions.
524 */
525 if( vips_image_generate( t[0],
526 NULL, vips_foreign_load_nsgif_generate, NULL, gif, NULL ) ||
527 vips_sequential( t[0], &t[1],
528 "tile_height", VIPS__FATSTRIP_HEIGHT,
529 NULL ) ||
530 vips_image_write( t[1], load->real ) )
531 return( -1 );
532
533 return( 0 );
534 }
535
536 static void
vips_foreign_load_nsgif_class_init(VipsForeignLoadNsgifClass * class)537 vips_foreign_load_nsgif_class_init( VipsForeignLoadNsgifClass *class )
538 {
539 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
540 VipsObjectClass *object_class = (VipsObjectClass *) class;
541 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
542 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
543
544 gobject_class->dispose = vips_foreign_load_nsgif_dispose;
545 gobject_class->set_property = vips_object_set_property;
546 gobject_class->get_property = vips_object_get_property;
547
548 object_class->nickname = "gifload_base";
549 object_class->description = _( "load GIF with libnsgif" );
550
551 /* High priority, so that we handle vipsheader etc.
552 */
553 foreign_class->priority = 50;
554
555 load_class->get_flags_filename =
556 vips_foreign_load_nsgif_get_flags_filename;
557 load_class->get_flags = vips_foreign_load_nsgif_get_flags;
558 load_class->header = vips_foreign_load_nsgif_header;
559 load_class->load = vips_foreign_load_nsgif_load;
560
561 VIPS_ARG_INT( class, "page", 10,
562 _( "Page" ),
563 _( "Load this page from the file" ),
564 VIPS_ARGUMENT_OPTIONAL_INPUT,
565 G_STRUCT_OFFSET( VipsForeignLoadNsgif, page ),
566 0, 100000, 0 );
567
568 VIPS_ARG_INT( class, "n", 6,
569 _( "n" ),
570 _( "Load this many pages" ),
571 VIPS_ARGUMENT_OPTIONAL_INPUT,
572 G_STRUCT_OFFSET( VipsForeignLoadNsgif, n ),
573 -1, 100000, 1 );
574
575 }
576
577 static void *
vips_foreign_load_nsgif_bitmap_create(int width,int height)578 vips_foreign_load_nsgif_bitmap_create( int width, int height )
579 {
580 /* Enforce max GIF dimensions of 16383 (0x7FFF). This should be enough
581 * for anyone, and will prevent the worst GIF bombs.
582 */
583 if( width <= 0 ||
584 width > 16383 ||
585 height <= 0 ||
586 height > 16383 ) {
587 vips_error( "gifload",
588 "%s", _( "bad image dimensions") );
589 return( NULL );
590 }
591
592 return g_malloc0( (gsize) width * height * 4 );
593 }
594
595 static void
vips_foreign_load_nsgif_bitmap_set_opaque(void * bitmap,bool opaque)596 vips_foreign_load_nsgif_bitmap_set_opaque( void *bitmap, bool opaque )
597 {
598 (void) opaque; /* unused */
599 (void) bitmap; /* unused */
600 g_assert( bitmap );
601 }
602
603 static bool
vips_foreign_load_nsgif_bitmap_test_opaque(void * bitmap)604 vips_foreign_load_nsgif_bitmap_test_opaque( void *bitmap )
605 {
606 (void) bitmap; /* unused */
607 g_assert( bitmap );
608
609 return( false );
610 }
611
612 static unsigned char *
vips_foreign_load_nsgif_bitmap_get_buffer(void * bitmap)613 vips_foreign_load_nsgif_bitmap_get_buffer( void *bitmap )
614 {
615 g_assert( bitmap );
616
617 return( bitmap );
618 }
619
620 static void
vips_foreign_load_nsgif_bitmap_destroy(void * bitmap)621 vips_foreign_load_nsgif_bitmap_destroy( void *bitmap )
622 {
623 g_assert( bitmap );
624 g_free( bitmap );
625 }
626
627 static void
vips_foreign_load_nsgif_bitmap_modified(void * bitmap)628 vips_foreign_load_nsgif_bitmap_modified( void *bitmap )
629 {
630 (void) bitmap; /* unused */
631 g_assert( bitmap );
632
633 return;
634 }
635
636 static gif_bitmap_callback_vt vips_foreign_load_nsgif_bitmap_callbacks = {
637 vips_foreign_load_nsgif_bitmap_create,
638 vips_foreign_load_nsgif_bitmap_destroy,
639 vips_foreign_load_nsgif_bitmap_get_buffer,
640 vips_foreign_load_nsgif_bitmap_set_opaque,
641 vips_foreign_load_nsgif_bitmap_test_opaque,
642 vips_foreign_load_nsgif_bitmap_modified
643 };
644
645 static void
vips_foreign_load_nsgif_init(VipsForeignLoadNsgif * gif)646 vips_foreign_load_nsgif_init( VipsForeignLoadNsgif *gif )
647 {
648 gif->anim = g_new0( gif_animation, 1 );
649 gif_create( gif->anim, &vips_foreign_load_nsgif_bitmap_callbacks );
650 gif->n = 1;
651 }
652
653 typedef struct _VipsForeignLoadNsgifFile {
654 VipsForeignLoadNsgif parent_object;
655
656 /* Filename for load.
657 */
658 char *filename;
659
660 } VipsForeignLoadNsgifFile;
661
662 typedef VipsForeignLoadNsgifClass VipsForeignLoadNsgifFileClass;
663
664 G_DEFINE_TYPE( VipsForeignLoadNsgifFile, vips_foreign_load_nsgif_file,
665 vips_foreign_load_nsgif_get_type() );
666
667 static int
vips_foreign_load_gif_file_build(VipsObject * object)668 vips_foreign_load_gif_file_build( VipsObject *object )
669 {
670 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) object;
671 VipsForeignLoadNsgifFile *file = (VipsForeignLoadNsgifFile *) object;
672
673 if( file->filename )
674 if( !(gif->source =
675 vips_source_new_from_file( file->filename )) )
676 return( -1 );
677
678 if( VIPS_OBJECT_CLASS( vips_foreign_load_nsgif_file_parent_class )->
679 build( object ) )
680 return( -1 );
681
682 return( 0 );
683 }
684
685 static const char *vips_foreign_nsgif_suffs[] = {
686 ".gif",
687 NULL
688 };
689
690 static gboolean
vips_foreign_load_nsgif_file_is_a(const char * filename)691 vips_foreign_load_nsgif_file_is_a( const char *filename )
692 {
693 VipsSource *source;
694 gboolean result;
695
696 if( !(source = vips_source_new_from_file( filename )) )
697 return( FALSE );
698 result = vips_foreign_load_nsgif_is_a_source( source );
699 VIPS_UNREF( source );
700
701 return( result );
702 }
703
704 static void
vips_foreign_load_nsgif_file_class_init(VipsForeignLoadNsgifFileClass * class)705 vips_foreign_load_nsgif_file_class_init(
706 VipsForeignLoadNsgifFileClass *class )
707 {
708 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
709 VipsObjectClass *object_class = (VipsObjectClass *) class;
710 VipsForeignClass *foreign_class = (VipsForeignClass *) class;
711 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
712
713 gobject_class->set_property = vips_object_set_property;
714 gobject_class->get_property = vips_object_get_property;
715
716 object_class->nickname = "gifload";
717 object_class->description = _( "load GIF with libnsgif" );
718 object_class->build = vips_foreign_load_gif_file_build;
719
720 foreign_class->suffs = vips_foreign_nsgif_suffs;
721
722 load_class->is_a = vips_foreign_load_nsgif_file_is_a;
723
724 VIPS_ARG_STRING( class, "filename", 1,
725 _( "Filename" ),
726 _( "Filename to load from" ),
727 VIPS_ARGUMENT_REQUIRED_INPUT,
728 G_STRUCT_OFFSET( VipsForeignLoadNsgifFile, filename ),
729 NULL );
730
731 }
732
733 static void
vips_foreign_load_nsgif_file_init(VipsForeignLoadNsgifFile * file)734 vips_foreign_load_nsgif_file_init( VipsForeignLoadNsgifFile *file )
735 {
736 }
737
738 typedef struct _VipsForeignLoadNsgifBuffer {
739 VipsForeignLoadNsgif parent_object;
740
741 /* Load from a buffer.
742 */
743 VipsArea *blob;
744
745 } VipsForeignLoadNsgifBuffer;
746
747 typedef VipsForeignLoadNsgifClass VipsForeignLoadNsgifBufferClass;
748
749 G_DEFINE_TYPE( VipsForeignLoadNsgifBuffer, vips_foreign_load_nsgif_buffer,
750 vips_foreign_load_nsgif_get_type() );
751
752 static int
vips_foreign_load_nsgif_buffer_build(VipsObject * object)753 vips_foreign_load_nsgif_buffer_build( VipsObject *object )
754 {
755 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) object;
756 VipsForeignLoadNsgifBuffer *buffer =
757 (VipsForeignLoadNsgifBuffer *) object;
758
759 if( buffer->blob &&
760 !(gif->source = vips_source_new_from_memory(
761 buffer->blob->data,
762 buffer->blob->length )) )
763 return( -1 );
764
765 if( VIPS_OBJECT_CLASS( vips_foreign_load_nsgif_buffer_parent_class )->
766 build( object ) )
767 return( -1 );
768
769 return( 0 );
770 }
771
772 static gboolean
vips_foreign_load_nsgif_buffer_is_a_buffer(const void * buf,size_t len)773 vips_foreign_load_nsgif_buffer_is_a_buffer( const void *buf, size_t len )
774 {
775 VipsSource *source;
776 gboolean result;
777
778 if( !(source = vips_source_new_from_memory( buf, len )) )
779 return( FALSE );
780 result = vips_foreign_load_nsgif_is_a_source( source );
781 VIPS_UNREF( source );
782
783 return( result );
784 }
785
786 static void
vips_foreign_load_nsgif_buffer_class_init(VipsForeignLoadNsgifBufferClass * class)787 vips_foreign_load_nsgif_buffer_class_init(
788 VipsForeignLoadNsgifBufferClass *class )
789 {
790 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
791 VipsObjectClass *object_class = (VipsObjectClass *) class;
792 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
793
794 gobject_class->set_property = vips_object_set_property;
795 gobject_class->get_property = vips_object_get_property;
796
797 object_class->nickname = "gifload_buffer";
798 object_class->description = _( "load GIF with libnsgif" );
799 object_class->build = vips_foreign_load_nsgif_buffer_build;
800
801 load_class->is_a_buffer = vips_foreign_load_nsgif_buffer_is_a_buffer;
802
803 VIPS_ARG_BOXED( class, "buffer", 1,
804 _( "Buffer" ),
805 _( "Buffer to load from" ),
806 VIPS_ARGUMENT_REQUIRED_INPUT,
807 G_STRUCT_OFFSET( VipsForeignLoadNsgifBuffer, blob ),
808 VIPS_TYPE_BLOB );
809
810 }
811
812 static void
vips_foreign_load_nsgif_buffer_init(VipsForeignLoadNsgifBuffer * buffer)813 vips_foreign_load_nsgif_buffer_init( VipsForeignLoadNsgifBuffer *buffer )
814 {
815 }
816
817 typedef struct _VipsForeignLoadNsgifSource {
818 VipsForeignLoadNsgif parent_object;
819
820 /* Load from a source.
821 */
822 VipsSource *source;
823
824 } VipsForeignLoadNsgifSource;
825
826 typedef VipsForeignLoadClass VipsForeignLoadNsgifSourceClass;
827
828 G_DEFINE_TYPE( VipsForeignLoadNsgifSource, vips_foreign_load_nsgif_source,
829 vips_foreign_load_nsgif_get_type() );
830
831 static int
vips_foreign_load_nsgif_source_build(VipsObject * object)832 vips_foreign_load_nsgif_source_build( VipsObject *object )
833 {
834 VipsForeignLoadNsgif *gif = (VipsForeignLoadNsgif *) object;
835 VipsForeignLoadNsgifSource *source =
836 (VipsForeignLoadNsgifSource *) object;
837
838 if( source->source ) {
839 gif->source = source->source;
840 g_object_ref( gif->source );
841 }
842
843 if( VIPS_OBJECT_CLASS( vips_foreign_load_nsgif_source_parent_class )->
844 build( object ) )
845 return( -1 );
846
847 return( 0 );
848 }
849
850 static void
vips_foreign_load_nsgif_source_class_init(VipsForeignLoadNsgifSourceClass * class)851 vips_foreign_load_nsgif_source_class_init(
852 VipsForeignLoadNsgifSourceClass *class )
853 {
854 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
855 VipsObjectClass *object_class = (VipsObjectClass *) class;
856 VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
857 VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
858
859 gobject_class->set_property = vips_object_set_property;
860 gobject_class->get_property = vips_object_get_property;
861
862 object_class->nickname = "gifload_source";
863 object_class->description = _( "load gif from source" );
864 object_class->build = vips_foreign_load_nsgif_source_build;
865
866 operation_class->flags = VIPS_OPERATION_NOCACHE;
867
868 load_class->is_a_source = vips_foreign_load_nsgif_is_a_source;
869
870 VIPS_ARG_OBJECT( class, "source", 1,
871 _( "Source" ),
872 _( "Source to load from" ),
873 VIPS_ARGUMENT_REQUIRED_INPUT,
874 G_STRUCT_OFFSET( VipsForeignLoadNsgifSource, source ),
875 VIPS_TYPE_SOURCE );
876
877 }
878
879 static void
vips_foreign_load_nsgif_source_init(VipsForeignLoadNsgifSource * source)880 vips_foreign_load_nsgif_source_init( VipsForeignLoadNsgifSource *source )
881 {
882 }
883
884 #endif /*HAVE_NSGIF*/
885
886 /**
887 * vips_gifload:
888 * @filename: file to load
889 * @out: (out): output image
890 * @...: %NULL-terminated list of optional named arguments
891 *
892 * Optional arguments:
893 *
894 * * @page: %gint, page (frame) to read
895 * * @n: %gint, load this many pages
896 * * @fail_on: #VipsFailOn, types of read error to fail on
897 *
898 * Read a GIF file into a libvips image.
899 *
900 * Use @page to select a page to render, numbering from zero.
901 *
902 * Use @n to select the number of pages to render. The default is 1. Pages are
903 * rendered in a vertical column. Set to -1 to mean "until the end of the
904 * document". Use vips_grid() to change page layout.
905 *
906 * Use @fail_on to set the type of error that will cause load to fail. By
907 * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE.
908 *
909 * The output image is RGBA for GIFs containing transparent elements, RGB
910 * otherwise.
911 *
912 * See also: vips_image_new_from_file().
913 *
914 * Returns: 0 on success, -1 on error.
915 */
916 int
vips_gifload(const char * filename,VipsImage ** out,...)917 vips_gifload( const char *filename, VipsImage **out, ... )
918 {
919 va_list ap;
920 int result;
921
922 va_start( ap, out );
923 result = vips_call_split( "gifload", ap, filename, out );
924 va_end( ap );
925
926 return( result );
927 }
928
929 /**
930 * vips_gifload_buffer:
931 * @buf: (array length=len) (element-type guint8): memory area to load
932 * @len: (type gsize): size of memory area
933 * @out: (out): image to write
934 * @...: %NULL-terminated list of optional named arguments
935 *
936 * Optional arguments:
937 *
938 * * @page: %gint, page (frame) to read
939 * * @n: %gint, load this many pages
940 * * @fail_on: #VipsFailOn, types of read error to fail on
941 *
942 * Exactly as vips_gifload(), but read from a memory buffer.
943 *
944 * You must not free the buffer while @out is active. The
945 * #VipsObject::postclose signal on @out is a good place to free.
946 *
947 * See also: vips_gifload().
948 *
949 * Returns: 0 on success, -1 on error.
950 */
951 int
vips_gifload_buffer(void * buf,size_t len,VipsImage ** out,...)952 vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... )
953 {
954 va_list ap;
955 VipsBlob *blob;
956 int result;
957
958 /* We don't take a copy of the data or free it.
959 */
960 blob = vips_blob_new( NULL, buf, len );
961
962 va_start( ap, out );
963 result = vips_call_split( "gifload_buffer", ap, blob, out );
964 va_end( ap );
965
966 vips_area_unref( VIPS_AREA( blob ) );
967
968 return( result );
969 }
970
971 /**
972 * vips_gifload_source:
973 * @source: source to load
974 * @out: (out): image to write
975 * @...: %NULL-terminated list of optional named arguments
976 *
977 * Optional arguments:
978 *
979 * * @page: %gint, page (frame) to read
980 * * @n: %gint, load this many pages
981 * * @fail_on: #VipsFailOn, types of read error to fail on
982 *
983 * Exactly as vips_gifload(), but read from a source.
984 *
985 * See also: vips_gifload().
986 *
987 * Returns: 0 on success, -1 on error.
988 */
989 int
vips_gifload_source(VipsSource * source,VipsImage ** out,...)990 vips_gifload_source( VipsSource *source, VipsImage **out, ... )
991 {
992 va_list ap;
993 int result;
994
995 va_start( ap, out );
996 result = vips_call_split( "gifload_source", ap, source, out );
997 va_end( ap );
998
999 return( result );
1000 }
1001