revise cgifsave to save mem

a bit simpler too

copying dloebl's idea from https://github.com/libvips/libvips/pull/3018
This commit is contained in:
John Cupitt 2022-09-08 10:16:50 +01:00
parent 5d1e26255d
commit c0e91d139d

View File

@ -33,8 +33,8 @@
*/
/*
#define DEBUG_VERBOSE
*/
#define DEBUG_VERBOSE
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -100,16 +100,13 @@ typedef struct _VipsForeignSaveCgif {
int *palette;
int n_colours;
/* The current frame coming from libvips, and the y position
* in the input image.
*/
VipsRegion *frame;
int write_y;
/* VipsRegion is not always contiguous, but we need contiguous RGBA
* forthe quantizer. We need to copy each frame to a local buffer.
/* The frame we are building, the y position in the frame.
*/
int frame_width;
int frame_height;
VipsPel *frame_bytes;
int write_y;
int page_number;
/* The current frame as seen by libimagequant.
*/
@ -152,12 +149,8 @@ vips_foreign_save_cgif_dispose( GObject *gobject )
{
VipsForeignSaveCgif *cgif = (VipsForeignSaveCgif *) gobject;
if( cgif->frame ) {
g_info( "cgifsave: %d frames",
cgif->frame->im->Ysize / cgif->frame->valid.height );
g_info( "cgifsave: %d unique palettes",
cgif->n_palettes_generated );
}
g_info( "cgifsave: %d frames", cgif->page_number );
g_info( "cgifsave: %d unique palettes", cgif->n_palettes_generated );
VIPS_FREEF( cgif_close, cgif->cgif_context );
@ -166,8 +159,6 @@ vips_foreign_save_cgif_dispose( GObject *gobject )
free_quantisation_result );
VIPS_FREEF( vips__quantise_attr_destroy, cgif->attr );
VIPS_UNREF( cgif->frame );
VIPS_UNREF( cgif->target );
VIPS_FREE( cgif->index );
@ -427,15 +418,12 @@ static int
vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( cgif );
VipsRect *frame_rect = &cgif->frame->valid;
int page_index = frame_rect->top / frame_rect->height;
int n_pels = frame_rect->height * frame_rect->width;
int n_pels = cgif->frame_height * cgif->frame_width;
gboolean has_transparency;
gboolean has_alpha_constraint;
VipsPel * restrict p;
int i;
int y;
VipsQuantiseImage *image;
gboolean use_local;
VipsQuantiseResult *quantisation_result;
@ -445,16 +433,9 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
VipsPel palette_rgb[256 * 3];
#ifdef DEBUG_VERBOSE
printf( "vips_foreign_save_cgif_write_frame: %d\n", page_index );
printf( "vips_foreign_save_cgif_write_frame: %d\n", cgif->page_number );
#endif/*DEBUG_VERBOSE*/
/* We need the frame as a contiguous RGBA buffer for the quantiser.
*/
for( y = 0; y < frame_rect->height; y++ )
memcpy( cgif->frame_bytes + y * 4 * frame_rect->width,
VIPS_REGION_ADDR( cgif->frame, 0, frame_rect->top + y ),
4 * frame_rect->width );
/* Threshold the alpha channel.
*
* Also, check if the alpha channel of the current frame matches the
@ -478,7 +459,7 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
p[2] = 0;
p[3] = 0;
if( page_index > 0 &&
if( cgif->page_number > 0 &&
cgif->previous_frame[i * 4 + 3] )
has_alpha_constraint = TRUE;
}
@ -489,7 +470,7 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
/* Set up new frame for libimagequant.
*/
image = vips__quantise_image_create_rgba( cgif->attr,
cgif->frame_bytes, frame_rect->width, frame_rect->height, 0 );
cgif->frame_bytes, cgif->frame_width, cgif->frame_height, 0 );
/* Quantise.
*/
@ -541,8 +522,8 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
cgif->cgif_config.numLoops = cgif->loop;
#endif/*HAVE_CGIF_ATTR_NO_LOOP*/
cgif->cgif_config.width = frame_rect->width;
cgif->cgif_config.height = frame_rect->height;
cgif->cgif_config.width = cgif->frame_width;
cgif->cgif_config.height = cgif->frame_height;
cgif->cgif_config.pGlobalPalette = palette_rgb;
cgif->cgif_config.numGlobalPaletteEntries = n_colours;
cgif->cgif_config.pWriteFn = vips__cgif_write;
@ -570,7 +551,7 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
/* Pixels which are equal to pixels in the previous frame can be made
* transparent, provided no alpha channel constraint is present.
*/
if( page_index > 0 &&
if( cgif->page_number > 0 &&
!has_alpha_constraint ) {
int trans = has_transparency ? 0 : n_colours;
@ -590,9 +571,9 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
}
if( cgif->delay &&
page_index < cgif->delay_length )
cgif->page_number < cgif->delay_length )
frame_config.delay =
VIPS_RINT( cgif->delay[page_index] / 10.0 );
VIPS_RINT( cgif->delay[cgif->page_number] / 10.0 );
/* Attach a local palette, if we need one.
*/
@ -627,59 +608,29 @@ static int
vips_foreign_save_cgif_sink_disc( VipsRegion *region, VipsRect *area, void *a )
{
VipsForeignSaveCgif *cgif = (VipsForeignSaveCgif *) a;
int line_size = cgif->frame_width * 4;
int y;
#ifdef DEBUG_VERBOSE
printf( "vips_foreign_save_cgif_sink_disc: strip at %d, height %d\n",
area->top, area->height );
#endif/*DEBUG_VERBOSE*/
/* Write the new pixels into frame.
*/
while( cgif->write_y < VIPS_RECT_BOTTOM( area ) ) {
VipsRect *to = &cgif->frame->valid;
for( y = 0; y < area->height; y++ ) {
memcpy( cgif->frame_bytes + cgif->write_y * line_size,
VIPS_REGION_ADDR( region, 0, area->top + y ),
line_size );
cgif->write_y += 1;
VipsRect hit;
/* The bit of the frame that we can fill.
*/
vips_rect_intersectrect( area, to, &hit );
/* Write the new pixels into the frame.
*/
vips_region_copy( region, cgif->frame,
&hit, hit.left, hit.top );
cgif->write_y += hit.height;
/* If we've filled the frame, write and move it down.
*/
if( VIPS_RECT_BOTTOM( &hit ) == VIPS_RECT_BOTTOM( to ) ) {
VipsRect new_frame;
VipsRect image;
if( vips_foreign_save_cgif_write_frame( cgif ) )
if( cgif->write_y >= cgif->frame_height ) {
if( vips_foreign_save_cgif_write_frame( cgif ) )
return( -1 );
new_frame.left = 0;
new_frame.top = cgif->write_y;
new_frame.width = to->width;
new_frame.height = to->height;
image.left = 0;
image.top = 0;
image.width = cgif->in->Xsize;
image.height = cgif->in->Ysize;
vips_rect_intersectrect( &new_frame, &image,
&new_frame );
/* End of image?
*/
if( vips_rect_isempty( &new_frame ) )
break;
if( vips_region_buffer( cgif->frame, &new_frame ) )
return( -1 );
}
}
cgif->write_y = 0;
cgif->page_number += 1;
}
}
return( 0 );
}
@ -693,9 +644,6 @@ vips_foreign_save_cgif_build( VipsObject *object )
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( cgif ), 2 );
int page_height;
VipsRect frame_rect;
if( VIPS_OBJECT_CLASS( vips_foreign_save_cgif_parent_class )->
build( object ) )
return( -1 );
@ -712,16 +660,14 @@ vips_foreign_save_cgif_build( VipsObject *object )
/* Animation properties.
*/
page_height = vips_image_get_page_height( cgif->in );
if( vips_image_get_typeof( cgif->in, "delay" ) )
vips_image_get_array_int( cgif->in, "delay",
&cgif->delay, &cgif->delay_length );
if( vips_image_get_typeof( cgif->in, "loop" ) )
vips_image_get_int( cgif->in, "loop", &cgif->loop );
frame_rect.left = 0;
frame_rect.top = 0;
frame_rect.width = cgif->in->Xsize;
frame_rect.height = page_height;
cgif->frame_height = vips_image_get_page_height( cgif->in );
cgif->frame_width = cgif->in->Xsize;
/* Reject images that exceed the pixel limit of libimagequant,
* or that exceed the GIF limit of 64k per axis.
@ -729,38 +675,27 @@ vips_foreign_save_cgif_build( VipsObject *object )
* Frame width * height will fit in an int, though frame size will
* need at least a uint.
*/
if( (guint64) frame_rect.width * frame_rect.height > INT_MAX / 4 ||
frame_rect.width > 65535 ||
frame_rect.height > 65535 ) {
if( (guint64) cgif->frame_width * cgif->frame_height > INT_MAX / 4 ||
cgif->frame_width > 65535 ||
cgif->frame_height > 65535 ) {
vips_error( class->nickname, "%s", _( "frame too large" ) );
return( -1 );
}
/* Assemble frames here.
*/
cgif->frame = vips_region_new( cgif->in );
if( vips_region_buffer( cgif->frame, &frame_rect ) )
return( -1 );
/* The regions will get used in the bg thread callback,
* so make sure we don't own them.
*/
vips__region_no_ownership( cgif->frame );
/* This RGBA frame as a contiguous buffer.
*/
cgif->frame_bytes = g_malloc0( (size_t) 4 *
frame_rect.width * frame_rect.height );
cgif->frame_width * cgif->frame_height );
/* The previous RGBA frame (for spotting pixels which haven't changed).
*/
cgif->previous_frame = g_malloc0( (size_t) 4 *
frame_rect.width * frame_rect.height );
cgif->frame_width * cgif->frame_height );
/* The frame index buffer.
*/
cgif->index = g_malloc0( (size_t) frame_rect.width *
frame_rect.height );
cgif->index = g_malloc0( (size_t) cgif->frame_width *
cgif->frame_height );
/* Set up libimagequant.
*/