Commit message: fetch map after DGifGetImageDesc()

Earlier refactoring broke GIF map fetch.
CVE: CVE-2019-17534
CWE: CWE-416
Vulnerability description: vips_foreign_load_gif_scan_image in foreign/gifload.c in libvips before 8.8.2 tries to access a color map before a DGifGetImageDesc call, leading to a use-after-free.

Project: libvips
File: gifload.c

vips_foreign_load_gif_buffer_class_init( 
	VipsForeignLoadGifBufferClass *class )
{
	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
	VipsObjectClass *object_class = (VipsObjectClass *) class;
	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
	VipsForeignLoadGifClass *gif_class = (VipsForeignLoadGifClass *) class;

	gobject_class->set_property = vips_object_set_property;
	gobject_class->get_property = vips_object_get_property;

	object_class->nickname = "gifload_buffer";
	object_class->description = _( "load GIF with giflib" );

	load_class->is_a_buffer = vips_foreign_load_gif_is_a_buffer;

	gif_class->open = vips_foreign_load_gif_buffer_open;

	VIPS_ARG_BOXED( class, "buffer", 1, 
		_( "Buffer" ),
		_( "Buffer to load from" ),
		VIPS_ARGUMENT_REQUIRED_INPUT, 
		G_STRUCT_OFFSET( VipsForeignLoadGifBuffer, buf ),
		VIPS_TYPE_BLOB );

}

Project: libvips
File: gifload.c

vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
{
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_buffer_open( VipsForeignLoadGif *gif )
{
	VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) gif;

	vips_foreign_load_gif_close( gif );
	buffer->p = buffer->buf->data;
	buffer->bytes_to_go = buffer->buf->length;
	gif->read_func = vips_giflib_buffer_read;;

	return( VIPS_FOREIGN_LOAD_GIF_CLASS( 
		vips_foreign_load_gif_file_parent_class )->open( gif ) );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_build_cmap( VipsForeignLoadGif *gif )
{
	ColorMapObject *map = gif->file->Image.ColorMap ?
		gif->file->Image.ColorMap : gif->file->SColorMap;

	int v;

	for( v = 0; v < 256; v++ ) {
		VipsPel *q = (VipsPel *) &gif->cmap[v];

		if( map &&
			v < map->ColorCount ) {
			q[0] = map->Colors[v].Red;
			q[1] = map->Colors[v].Green;
			q[2] = map->Colors[v].Blue;
			q[3] = 255;
		}
		else {
			/* If there's no map, just save the index.
			 */
			q[0] = v;
			q[1] = v;
			q[2] = v;
			q[3] = 255;
		}
	}
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_close( VipsForeignLoadGif *gif )
{
#ifdef HAVE_GIFLIB_5
	if( gif->file ) {
		int error; 

		if( DGifCloseFile( gif->file, &error ) == GIF_ERROR ) 
			vips_foreign_load_gif_error_vips( gif, error );
		gif->file = NULL;
	}
#else 
	if( gif->file ) {
		if( DGifCloseFile( gif->file ) == GIF_ERROR ) 
			vips_foreign_load_gif_error_vips( gif, GifLastError() ); 
		gif->file = NULL;
	}
#endif
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif, 
	GifByteType **extension )
{
	if( DGifGetCodeNext( gif->file, extension ) == GIF_ERROR ) {
		vips_foreign_load_gif_error( gif ); 
		return( -1 ); 
	}

	if( *extension ) 
		VIPS_DEBUG_MSG( "gifload: CODE_NEXT\n" ); 

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_dispose( GObject *gobject )
{
	VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject;

	vips_foreign_load_gif_close( gif ); 

	VIPS_UNREF( gif->frame ); 
	VIPS_UNREF( gif->previous ); 
	VIPS_FREE( gif->comment ); 
	VIPS_FREE( gif->line ) 

	G_OBJECT_CLASS( vips_foreign_load_gif_parent_class )->
		dispose( gobject );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_error( VipsForeignLoadGif *gif )
{
	int error;

	error = 0;

#ifdef HAVE_GIFLIB_5
	if( gif->file ) 
		error = gif->file->Error;
#else 
	error = GifLastError(); 
#endif

	if( error ) 
		vips_foreign_load_gif_error_vips( gif, error ); 
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_errstr( int error_code )
{
#ifdef HAVE_GIFLIB_5
	return( GifErrorString( error_code ) ); 
#else /*!HAVE_GIFLIB_5*/
	switch( error_code ) {
	case D_GIF_ERR_OPEN_FAILED:
		return( _( "Failed to open given file" ) ); 

	case D_GIF_ERR_READ_FAILED:
		return( _( "Failed to read from given file" ) ); 

	case D_GIF_ERR_NOT_GIF_FILE:
		return( _( "Data is not a GIF file" ) ); 

	case D_GIF_ERR_NO_SCRN_DSCR:
		return( _( "No screen descriptor detected" ) ); 

	case D_GIF_ERR_NO_IMAG_DSCR:
		return( _( "No image descriptor detected" ) ); 

	case D_GIF_ERR_NO_COLOR_MAP:
		return( _( "Neither global nor local color map" ) ); 

	case D_GIF_ERR_WRONG_RECORD:
		return( _( "Wrong record type detected" ) ); 

	case D_GIF_ERR_DATA_TOO_BIG:
		return( _( "Number of pixels bigger than width * height" ) ); 

	case D_GIF_ERR_NOT_ENOUGH_MEM:
		return( _( "Failed to allocate required memory" ) ); 

	case D_GIF_ERR_CLOSE_FAILED:
		return( _( "Failed to close given file" ) ); 

	case D_GIF_ERR_NOT_READABLE:
		return( _( "Given file was not opened for read" ) ); 

	case D_GIF_ERR_IMAGE_DEFECT:
		return( _( "Image is defective, decoding aborted" ) ); 

	case D_GIF_ERR_EOF_TOO_SOON:
		return( _( "Image EOF detected, before image complete" ) ); 

	default:
		return( _( "Unknown error" ) ); 
	}
#endif /*HAVE_GIFLIB_5*/
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_extension( VipsForeignLoadGif *gif )
{
	GifByteType *extension;
	int ext_code;

	VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension:\n" ); 

	if( DGifGetExtension( gif->file, &ext_code, &extension ) == 
		GIF_ERROR ) {
		vips_foreign_load_gif_error( gif ); 
		return( -1 ); 
	}

	if( extension &&
		ext_code == GRAPHICS_EXT_FUNC_CODE &&
		extension[0] == 4 ) { 
		/* Bytes are flags, delay low, delay high,
		 * transparency. Flag bit 1 means transparency
		 * is being set.
		 */
		gif->transparency = -1;
		if( extension[1] & 0x1 ) 
			gif->transparency = extension[4];

		/* Set the current dispose mode. This is read during frame load
		 * to set the meaning of background and transparent pixels.
		 */
		gif->dispose = (extension[1] >> 2) & 0x7;
		VIPS_DEBUG_MSG( "vips_foreign_load_gif_extension: "
			"dispose = %d\n", gif->dispose ); 
	}

	while( extension != NULL ) 
		if( vips_foreign_load_gif_ext_next( gif, &extension ) )
			return( -1 ); 

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_file_class_init( 
	VipsForeignLoadGifFileClass *class )
{
	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
	VipsObjectClass *object_class = (VipsObjectClass *) class;
	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
	VipsForeignLoadGifClass *gif_class = (VipsForeignLoadGifClass *) class;

	gobject_class->dispose = vips_foreign_load_gif_file_dispose;
	gobject_class->set_property = vips_object_set_property;
	gobject_class->get_property = vips_object_get_property;

	object_class->nickname = "gifload";
	object_class->description = _( "load GIF with giflib" );

	foreign_class->suffs = vips_foreign_gif_suffs;

	load_class->is_a = vips_foreign_load_gif_is_a;

	gif_class->open = vips_foreign_load_gif_file_open;

	VIPS_ARG_STRING( class, "filename", 1, 
		_( "Filename" ),
		_( "Filename to load from" ),
		VIPS_ARGUMENT_REQUIRED_INPUT, 
		G_STRUCT_OFFSET( VipsForeignLoadGifFile, filename ),
		NULL );

}

Project: libvips
File: gifload.c

vips_foreign_load_gif_file_dispose( GObject *gobject )
{
	VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject;

	VIPS_FREEF( fclose, file->fp ); 

	G_OBJECT_CLASS( vips_foreign_load_gif_file_parent_class )->
		dispose( gobject );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif )
{
	VipsForeignLoad *load = (VipsForeignLoad *) gif;
	VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif;

	if( !file->fp ) {
		if( !(file->fp = 
			vips__file_open_read( file->filename, NULL, FALSE )) ) 
			return( -1 ); 

		VIPS_SETSTR( load->out->filename, file->filename );
	}
	else 
		rewind( file->fp );

	vips_foreign_load_gif_close( gif );
	gif->read_func = vips_giflib_file_read;

	return( VIPS_FOREIGN_LOAD_GIF_CLASS( 
		vips_foreign_load_gif_file_parent_class )->open( gif ) );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_generate( VipsRegion *or, 
	void *seq, void *a, void *b, gboolean *stop )
{
        VipsRect *r = &or->valid;
	VipsForeignLoadGif *gif = (VipsForeignLoadGif *) a;

	int y;

	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->file->SHeight + gif->page;
		int line = (r->top + y) % gif->file->SHeight;

		VipsPel *p, *q;
		int x;

		g_assert( line >= 0 && line < gif->frame->Ysize );
		g_assert( page >= 0 && page < gif->n_pages );

		/* current_page == 0 means we've not loaded any pages yet. So 
		 * we need to have loaded the page beyond the page we want.
		 */
		while( gif->current_page <= page ) {
			if( vips_foreign_load_gif_next_page( gif ) )
				return( -1 );

			gif->current_page += 1; 
		}

		/* @frame is always RGBA, but or may be G, GA, RGB or RGBA. 
		 * We have to pick out the values we want.
		 */
		p = VIPS_IMAGE_ADDR( gif->frame, 0, line );
		q = VIPS_REGION_ADDR( or, 0, r->top + y );
		switch( or->im->Bands ) {
		case 1:
			for( x = 0; x < gif->frame->Xsize; x++ ) {
				q[0] = p[1];

				q += 1;
				p += 4;
			}
			break;

		case 2:
			for( x = 0; x < gif->frame->Xsize; x++ ) {
				q[0] = p[1];
				q[1] = p[3];

				q += 2;
				p += 4;
			}
			break;

		case 3:
			for( x = 0; x < gif->frame->Xsize; x++ ) {
				q[0] = p[0];
				q[1] = p[1];
				q[2] = p[2];

				q += 3;
				p += 4;
			}
			break;

		case 4:
			memcpy( q, p, VIPS_IMAGE_SIZEOF_LINE( gif->frame ) );
			break;

		default:
			g_assert_not_reached();
			break;
		}
	}

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_get_flags( VipsForeignLoad *load )
{
	return( VIPS_FOREIGN_SEQUENTIAL );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_get_flags_filename( const char *filename )
{
	return( VIPS_FOREIGN_SEQUENTIAL );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_header( VipsForeignLoad *load )
{
	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
	VipsForeignLoadGifClass *gif_class = 
		(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
	VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;

	GifRecordType record;

	if( gif_class->open( gif ) )
		return( -1 );

	gif->n_pages = 0;

	do {
		if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) 
			continue;

		switch( record ) {
		case IMAGE_DESC_RECORD_TYPE:
			(void) vips_foreign_load_gif_scan_image( gif );

			gif->n_pages += 1;

			break;

		case EXTENSION_RECORD_TYPE:
			/* We will need to fetch the extensions to check for
			 * cmaps and transparency.
			 */
			(void) vips_foreign_load_gif_scan_extension( gif );
			break;

		case TERMINATE_RECORD_TYPE:
			gif->eof = TRUE;
			break;

		case SCREEN_DESC_RECORD_TYPE:
		case UNDEFINED_RECORD_TYPE:
			break;

		default:
			break;
		}
	} while( !gif->eof );

	if( gif->n == -1 )
		gif->n = gif->n_pages - gif->page;

	if( gif->page < 0 ||
		gif->n <= 0 ||
		gif->page + gif->n > gif->n_pages ) {
		vips_error( class->nickname, "%s", _( "bad page number" ) ); 
		return( -1 ); 
	}

	/* And set the output vips header from what we've learned.
	 */
	if( vips_foreign_load_gif_set_header( gif, load->out ) )
		return( -1 );

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_is_a( const char *filename )
{
	unsigned char buf[4];

	if( vips__get_bytes( filename, buf, 4 ) == 4 &&
		vips_foreign_load_gif_is_a_buffer( buf, 4 ) )
		return( 1 );

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_is_a_buffer( const void *buf, size_t len )
{
	const guchar *str = (const guchar *) buf;

	if( len >= 4 &&
		str[0] == 'G' && 
		str[1] == 'I' &&
		str[2] == 'F' &&
		str[3] == '8' )
		return( 1 );

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_load( VipsForeignLoad *load )
{
	VipsForeignLoadGifClass *class = 
		(VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load );
	VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load;
	VipsImage **t = (VipsImage **) 
		vips_object_local_array( VIPS_OBJECT( load ), 4 );

	/* Rewind.
	 */
	if( class->open( gif ) )
		return( -1 );

	VIPS_DEBUG_MSG( "vips_foreign_load_gif_load:\n" ); 

	/* Make the memory image we accumulate pixels in. We always accumulate
	 * to RGBA, then trim down to whatever the output image needs on
	 * _generate.
	 */
	gif->frame = vips_image_new_memory();
	vips_image_init_fields( gif->frame, 
		gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
	if( vips_image_write_prepare( gif->frame ) ) 
		return( -1 );

	/* A copy of the previous state of the frame, in case we have to
	 * process a DISPOSE_PREVIOUS.
	 */
	gif->previous = vips_image_new_memory();
	vips_image_init_fields( gif->previous, 
		gif->file->SWidth, gif->file->SHeight, 4, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
	if( vips_image_write_prepare( gif->previous ) ) 
		return( -1 );

	/* Make the output pipeline.
	 */
	t[0] = vips_image_new();
	if( vips_foreign_load_gif_set_header( gif, t[0] ) )
		return( -1 );

	/* Strips 8 pixels high to avoid too many tiny regions.
	 */
	if( vips_image_generate( t[0], 
		NULL, vips_foreign_load_gif_generate, NULL, gif, NULL ) ||
		vips_sequential( t[0], &t[1], 
			"tile_height", VIPS__FATSTRIP_HEIGHT, 
			NULL ) ||
		vips_image_write( t[1], load->real ) )
		return( -1 );

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif )
{
	GifRecordType record;
	gboolean have_read_frame;

	have_read_frame = FALSE;
	do { 
		if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) {
			vips_foreign_load_gif_error( gif ); 
			return( -1 ); 
		}

		switch( record ) {
		case IMAGE_DESC_RECORD_TYPE:
			VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
				"IMAGE_DESC_RECORD_TYPE\n" ); 

			if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) {
				vips_foreign_load_gif_error( gif ); 
				return( -1 ); 
			}

			if( vips_foreign_load_gif_render( gif ) )
				return( -1 ); 

			have_read_frame = TRUE;

			break;

		case EXTENSION_RECORD_TYPE:
			if( vips_foreign_load_gif_extension( gif ) )
				return( -1 );
			break;

		case TERMINATE_RECORD_TYPE:
			VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
				"TERMINATE_RECORD_TYPE\n" ); 
			gif->eof = TRUE;
			break;

		case SCREEN_DESC_RECORD_TYPE:
			VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
				"SCREEN_DESC_RECORD_TYPE\n" );
			break;

		case UNDEFINED_RECORD_TYPE:
			VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: "
				"UNDEFINED_RECORD_TYPE\n" );
			break;

		default:
			break;
		}
	} while( !have_read_frame &&
		!gif->eof );

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_open( VipsForeignLoadGif *gif )
{
#ifdef HAVE_GIFLIB_5
{
	int error;

	if( !(gif->file = DGifOpen( gif, gif->read_func, &error )) ) {
		vips_foreign_load_gif_error_vips( gif, error );
		return( -1 ); 
	}
}
#else 
	if( !(gif->file = DGifOpen( gif, gif->read_func )) ) { 
		vips_foreign_load_gif_error_vips( gif, GifLastError() ); 
		return( -1 ); 
	}
#endif

	gif->eof = FALSE;
	gif->current_page = 0;

	/* Allocate a line buffer now that we have the GIF width.
	 */
	VIPS_FREE( gif->line ) 
	if( !(gif->line = VIPS_ARRAY( NULL, gif->file->SWidth, GifPixelType )) )
		return( -1 ); 

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) 
{
	GifFileType *file = gif->file;

	/* Update the colour map for this frame.
	 */
	vips_foreign_load_gif_build_cmap( gif );

	/* BACKGROUND means we reset the frame to 0 (transparent) before we 
	 * render the next set of pixels.
	 */
	if( gif->dispose == DISPOSE_BACKGROUND ) 
		memset( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), 0, 
			VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );

	/* PREVIOUS means we init the frame with the frame before last, ie. we
	 * undo the last render.
	 *
	 * Anything other than PREVIOUS, we must update the previous buffer,
	 */
	if( gif->dispose == DISPOSE_PREVIOUS ) 
		memcpy( VIPS_IMAGE_ADDR( gif->frame, 0, 0 ),
			VIPS_IMAGE_ADDR( gif->previous, 0, 0 ),
			VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );
	else 
		memcpy( VIPS_IMAGE_ADDR( gif->previous, 0, 0 ),
			VIPS_IMAGE_ADDR( gif->frame, 0, 0 ),
			VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) );

	if( file->Image.Interlace ) {
		int i;

		VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: "
			"interlaced frame of %d x %d pixels at %d x %d\n",
			file->Image.Width, file->Image.Height,
			file->Image.Left, file->Image.Top ); 

		for( i = 0; i < 4; i++ ) {
			int y;

			for( y = InterlacedOffset[i]; 
				y < file->Image.Height;
			  	y += InterlacedJumps[i] ) {
				VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, 
					file->Image.Left, file->Image.Top + y );

				if( DGifGetLine( gif->file, gif->line, 
					file->Image.Width ) == GIF_ERROR ) {
					vips_foreign_load_gif_error( gif ); 
					return( -1 ); 
				}

				vips_foreign_load_gif_render_line( gif, 
					file->Image.Width, q, gif->line ); 
			}
		}
	}
	else {
		int y;

		VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: "
			"non-interlaced frame of %d x %d pixels at %d x %d\n",
			file->Image.Width, file->Image.Height,
			file->Image.Left, file->Image.Top ); 

		for( y = 0; y < file->Image.Height; y++ ) {
			VipsPel *q = VIPS_IMAGE_ADDR( gif->frame, 
				file->Image.Left, file->Image.Top + y );

			if( DGifGetLine( gif->file, gif->line, 
				file->Image.Width ) == GIF_ERROR ) {
				vips_foreign_load_gif_error( gif ); 
				return( -1 ); 
			}

			vips_foreign_load_gif_render_line( gif, 
				file->Image.Width, q, gif->line ); 
		}
	}

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_render_line( VipsForeignLoadGif *gif,
	int width, VipsPel * restrict q, VipsPel * restrict p )
{
	guint32 *iq;
	int x;

	iq = (guint32 *) q;
	for( x = 0; x < width; x++ ) {
		VipsPel v = p[x];
		
		if( v == gif->transparency ) {
			/* In DISPOSE_DO_NOT mode, the previous frame shows
			 * through (ie. we do nothing). In all other modes, 
			 * it's just transparent.
			 */
			if( gif->dispose != DISPOSE_DO_NOT ) 
				iq[x] = 0;
		}
		else
			/* Blast in the RGBA for this value.
			 */
			iq[x] = gif->cmap[v];
	}
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_scan_application_ext( VipsForeignLoadGif *gif,
	GifByteType *extension )
{
	gboolean have_netscape;

	/* The 11-byte NETSCAPE extension.
	 */
	have_netscape = FALSE;
	if( extension[0] == 11 &&
		vips_isprefix( "NETSCAPE2.0", (const char*) (extension + 1) ) ) 
		have_netscape = TRUE;

	while( extension != NULL ) {
		if( vips_foreign_load_gif_ext_next( gif, &extension ) )
			return( -1 ); 

		if( have_netscape &&
			extension &&
			extension[0] == 3 &&
			extension[1] == 1 ) 
			gif->loop = extension[2] | (extension[3] << 8);
	}

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_scan_comment_ext( VipsForeignLoadGif *gif,
	GifByteType *extension )
{
	VIPS_DEBUG_MSG( "gifload: type: comment\n" ); 

	if( !gif->comment ) { 
		/* Up to 257 with a NULL terminator.
		 */
		char comment[257];

		vips_strncpy( comment, (char *) (extension + 1), 256 );
		comment[extension[0]] = '\0';
		gif->comment = g_strdup( comment ); 
	}

	while( extension != NULL ) 
		if( vips_foreign_load_gif_ext_next( gif, &extension ) )
			return( -1 ); 

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif )
{
	GifByteType *extension;
	int ext_code;

	if( DGifGetExtension( gif->file, &ext_code, &extension ) == 
		GIF_ERROR ) {
		vips_foreign_load_gif_error( gif ); 
		return( -1 ); 
	}

	if( extension ) 
		switch( ext_code ) { 
		case GRAPHICS_EXT_FUNC_CODE: 
			if( extension[0] == 4 &&
				extension[1] & 0x1 ) {
				VIPS_DEBUG_MSG( "gifload: has transp.\n" ); 
				gif->has_transparency = TRUE;
			}

			if( !gif->has_delay ) { 
				VIPS_DEBUG_MSG( "gifload: has delay\n" ); 
				gif->has_delay = TRUE;
				gif->delay = extension[2] | (extension[3] << 8);
			}

			while( extension != NULL ) 
				if( vips_foreign_load_gif_ext_next( gif, 
					&extension ) )
					return( -1 ); 

			break;

		case APPLICATION_EXT_FUNC_CODE: 
			if( vips_foreign_load_gif_scan_application_ext( gif, 
				extension ) )
				return( -1 ); 
			break;

		case COMMENT_EXT_FUNC_CODE:
			if( vips_foreign_load_gif_scan_comment_ext( gif, 
				extension ) )
				return( -1 ); 
			break;

		default:
			/* Step over any NEXT blocks for unknown extensions.
			 */
			while( extension != NULL ) 
				if( vips_foreign_load_gif_ext_next( gif, 
					&extension ) )
					return( -1 ); 
			break;
		}

	return( 0 );
}

Project: libvips
File: gifload.c

vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image )
{
	vips_image_init_fields( image,
		gif->file->SWidth, gif->file->SHeight * gif->n, 
		(gif->has_colour ? 3 : 1) + (gif->has_transparency ? 1 : 0),
		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
		gif->has_colour ? 
		 	VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_B_W,
		1.0, 1.0 );
	vips_image_pipelinev( image, VIPS_DEMAND_STYLE_FATSTRIP, NULL );

	if( vips_object_argument_isset( VIPS_OBJECT( gif ), "n" ) )
		vips_image_set_int( image, 
			VIPS_META_PAGE_HEIGHT, gif->file->SHeight );
	vips_image_set_int( image, VIPS_META_N_PAGES, gif->n_pages );
	vips_image_set_int( image, "gif-delay", gif->delay );
	vips_image_set_int( image, "gif-loop", gif->loop );
	if( gif->comment ) 
		vips_image_set_string( image, "gif-comment", gif->comment );

	return( 0 );
}

Project: libvips
File: gifload.c

vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n )
{
	VipsForeignLoadGif *gif = (VipsForeignLoadGif *) file->UserData;
	VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) gif;
	size_t will_read = VIPS_MIN( n, buffer->bytes_to_go );

	memcpy( buf, buffer->p, will_read );
	buffer->p += will_read;
	buffer->bytes_to_go -= will_read;

	if( will_read == 0 )
		gif->eof = TRUE;

	return( will_read ); 
}

Project: libvips
File: gifload.c

vips_giflib_file_read( GifFileType *gfile, GifByteType *buffer, int n )
{
	VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gfile->UserData;
	VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif;

	if( feof( file->fp ) )
		gif->eof = TRUE;

	return( (int) fread( (void *) buffer, 1, n, file->fp ) );
}

Project: libvips
File: gifload.c

vips_gifload( const char *filename, VipsImage **out, ... )
{
	va_list ap;
	int result;

	va_start( ap, out );
	result = vips_call_split( "gifload", ap, filename, out );
	va_end( ap );

	return( result );
}

Project: libvips
File: gifload.c

vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... )
{
	va_list ap;
	VipsBlob *blob;
	int result;

	/* We don't take a copy of the data or free it.
	 */
	blob = vips_blob_new( NULL, buf, len );

	va_start( ap, out );
	result = vips_call_split( "gifload_buffer", ap, blob, out );
	va_end( ap );

	vips_area_unref( VIPS_AREA( blob ) );

	return( result );
}