cgifsave: avoid size issue (#2628)
* cgifsave: avoid size issue avoid size issue by restoring the original transparency setting after quantization/dithering * switch to per-frame alpha channel * add option for lossy transparency * switch lossy transparency off by default * switch to g_malloc instead of g_malloc0 no need to 0 initialize at this point * adapt to libvips coding conventions * cgifsave: avoid size issue avoid size issue by restoring the original transparency setting after quantization/dithering * switch to per-frame alpha channel * add option for lossy transparency * switch lossy transparency off by default * switch to g_malloc instead of g_malloc0 no need to 0 initialize at this point * adapt to libvips coding conventions * switch to official Homebrew sources for installing cgif * bump required cgif version to v0.2.0
This commit is contained in:
parent
c02b69eaba
commit
b0f993eec8
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -82,9 +82,7 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install meson ninja fftw fontconfig glib libexif libgsf little-cms2 orc pango
|
||||
brew install cfitsio libheif libimagequant libjpeg-turbo libmatio librsvg libspng libtiff openexr openjpeg openslide poppler webp
|
||||
brew tap lovell/cgif-packaging https://github.com/lovell/cgif-packaging.git
|
||||
brew install --build-bottle lovell/cgif-packaging/cgif
|
||||
brew install cfitsio libheif libimagequant libjpeg-turbo libmatio librsvg libspng libtiff openexr openjpeg openslide poppler webp cgif
|
||||
|
||||
- name: Install Clang 13
|
||||
if: runner.os == 'Linux' && matrix.build.cc == 'clang-13'
|
||||
|
@ -1493,8 +1493,8 @@ AC_ARG_WITH([cgif],
|
||||
AS_HELP_STRING([--without-cgif], [build without cgif (default: test)]))
|
||||
|
||||
if test x"$quantisation_package" != x"" && test x"$with_cgif" != x"no"; then
|
||||
PKG_CHECK_MODULES(CGIF, cgif,
|
||||
[AC_DEFINE(HAVE_CGIF,1,[define if you have cgif installed.])
|
||||
PKG_CHECK_MODULES(CGIF, cgif >= 0.2.0,
|
||||
[AC_DEFINE(HAVE_CGIF,1,[define if you have cgif >= 0.2.0 installed.])
|
||||
with_cgif=yes
|
||||
PACKAGES_USED="$PACKAGES_USED cgif"
|
||||
],
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
@ -61,6 +62,7 @@ typedef struct _VipsForeignSaveCgif {
|
||||
double dither;
|
||||
int effort;
|
||||
int bitdepth;
|
||||
double maxerror;
|
||||
VipsTarget *target;
|
||||
|
||||
/* Derived write params.
|
||||
@ -100,6 +102,10 @@ typedef struct _VipsForeignSaveCgif {
|
||||
*/
|
||||
VipsPel *index;
|
||||
|
||||
/* frame_bytes head (needed for transparency trick).
|
||||
*/
|
||||
VipsPel *frame_bytes_head;
|
||||
|
||||
/* The frame as written by libcgif.
|
||||
*/
|
||||
CGIF *cgif_context;
|
||||
@ -138,6 +144,7 @@ vips_foreign_save_cgif_dispose( GObject *gobject )
|
||||
|
||||
VIPS_FREE( cgif->palette_rgb );
|
||||
VIPS_FREE( cgif->index );
|
||||
VIPS_FREE( cgif->frame_bytes_head );
|
||||
|
||||
VIPS_FREEF( cgif_close, cgif->cgif_context );
|
||||
|
||||
@ -153,6 +160,25 @@ static int vips__cgif_write( void *target, const uint8_t *buffer,
|
||||
(const void *) buffer, (size_t) length );
|
||||
}
|
||||
|
||||
/* Compare pixels in a lossy way (allow a slight colour difference).
|
||||
In combination with the GIF transparency optimization this leads to
|
||||
less difference between frames and therefore improves the compression ratio.
|
||||
*/
|
||||
static gboolean
|
||||
cgif_pixels_are_equal(const VipsPel *cur, const VipsPel *bef, double maxerror)
|
||||
{
|
||||
if( bef[3] != 0xFF )
|
||||
/* Done. Cannot compare with alpha channel in frame before.
|
||||
*/
|
||||
return FALSE;
|
||||
/* Test Euclidean distance between the two points.
|
||||
*/
|
||||
const int dR = cur[0] - bef[0];
|
||||
const int dG = cur[1] - bef[1];
|
||||
const int dB = cur[2] - bef[2];
|
||||
return( sqrt( dR * dR + dG * dG + dB * dB ) <= maxerror );
|
||||
}
|
||||
|
||||
/* We have a complete frame --- write!
|
||||
*/
|
||||
static int
|
||||
@ -274,8 +300,6 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
|
||||
if( !cgif->cgif_context ) {
|
||||
cgif->cgif_config.pGlobalPalette = cgif->palette_rgb;
|
||||
cgif->cgif_config.attrFlags = CGIF_ATTR_IS_ANIMATED;
|
||||
cgif->cgif_config.attrFlags |=
|
||||
cgif->has_transparency ? CGIF_ATTR_HAS_TRANSPARENCY : 0;
|
||||
cgif->cgif_config.width = frame_rect->width;
|
||||
cgif->cgif_config.height = frame_rect->height;
|
||||
cgif->cgif_config.numGlobalPaletteEntries = cgif->lp->count;
|
||||
@ -286,12 +310,6 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
|
||||
cgif->cgif_context = cgif_newgif( &cgif->cgif_config );
|
||||
}
|
||||
|
||||
/* Reset global transparency flag.
|
||||
*/
|
||||
cgif->cgif_config.attrFlags =
|
||||
(cgif->cgif_config.attrFlags & ~CGIF_ATTR_HAS_TRANSPARENCY) |
|
||||
(cgif->has_transparency ? CGIF_ATTR_HAS_TRANSPARENCY : 0);
|
||||
|
||||
/* Write frame to cgif.
|
||||
*/
|
||||
memset( &frame_config, 0, sizeof( CGIF_FrameConfig ) );
|
||||
@ -303,6 +321,50 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
|
||||
frame_config.genFlags =
|
||||
CGIF_FRAME_GEN_USE_TRANSPARENCY |
|
||||
CGIF_FRAME_GEN_USE_DIFF_WINDOW;
|
||||
frame_config.attrFlags = 0;
|
||||
|
||||
/* Switch per-frame alpha channel on.
|
||||
* Index 0 is used for pixels with alpha channel.
|
||||
*/
|
||||
if( cgif->has_transparency ) {
|
||||
frame_config.attrFlags |= CGIF_FRAME_ATTR_HAS_ALPHA;
|
||||
frame_config.transIndex = 0;
|
||||
}
|
||||
|
||||
/* Do the transparency trick (only possible when no alpha channel present)
|
||||
*/
|
||||
if( cgif->frame_bytes_head ) {
|
||||
VipsPel *cur, *bef;
|
||||
|
||||
cur = frame_bytes;
|
||||
bef = cgif->frame_bytes_head;
|
||||
if( !cgif->has_transparency ) {
|
||||
const uint8_t trans_index = cgif->lp->count;
|
||||
const double maxerror = cgif->maxerror;
|
||||
|
||||
int i;
|
||||
|
||||
for( i = 0; i < n_pels; i++ ) {
|
||||
if( cgif_pixels_are_equal( cur, bef, maxerror ) )
|
||||
cgif->index[i] = trans_index;
|
||||
else {
|
||||
bef[0] = cur[0];
|
||||
bef[1] = cur[1];
|
||||
bef[2] = cur[2];
|
||||
bef[3] = cur[3];
|
||||
}
|
||||
cur += 4;
|
||||
bef += 4;
|
||||
}
|
||||
frame_config.attrFlags |= CGIF_FRAME_ATTR_HAS_SET_TRANS;
|
||||
frame_config.transIndex = trans_index;
|
||||
} else {
|
||||
/* Transparency trick not possible (alpha channel present).
|
||||
* Update head.
|
||||
*/
|
||||
memcpy( bef, cur, 4 * n_pels);
|
||||
}
|
||||
}
|
||||
|
||||
if( cgif->delay &&
|
||||
page_index < cgif->delay_length )
|
||||
@ -312,13 +374,21 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
|
||||
/* Attach a local palette, if we need one.
|
||||
*/
|
||||
if( cgif->cgif_config.attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE ) {
|
||||
frame_config.attrFlags = CGIF_FRAME_ATTR_USE_LOCAL_TABLE;
|
||||
frame_config.attrFlags |= CGIF_FRAME_ATTR_USE_LOCAL_TABLE;
|
||||
frame_config.pLocalPalette = cgif->palette_rgb;
|
||||
frame_config.numLocalPaletteEntries = cgif->lp->count;
|
||||
}
|
||||
|
||||
cgif_addframe( cgif->cgif_context, &frame_config );
|
||||
|
||||
if( !cgif->frame_bytes_head ) {
|
||||
/* Keep head frame_bytes in memory for transparency trick
|
||||
* which avoids the size explosion (#2576).
|
||||
*/
|
||||
cgif->frame_bytes_head = g_malloc( 4 * n_pels );
|
||||
memcpy( cgif->frame_bytes_head, frame_bytes, 4 * n_pels );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -529,6 +599,12 @@ vips_foreign_save_cgif_class_init( VipsForeignSaveCgifClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveCgif, bitdepth ),
|
||||
1, 8, 8 );
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "maxerror", 13,
|
||||
_( "Max. error for lossy transparency setting"),
|
||||
_( "Maximum pixel error to allow when identifying areas as identical (inter frame)" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveCgif, maxerror ),
|
||||
0, 32, 0.0 );
|
||||
}
|
||||
|
||||
static void
|
||||
@ -537,6 +613,7 @@ vips_foreign_save_cgif_init( VipsForeignSaveCgif *gif )
|
||||
gif->dither = 1.0;
|
||||
gif->effort = 7;
|
||||
gif->bitdepth = 8;
|
||||
gif->maxerror = 0.0;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveCgifTarget {
|
||||
|
@ -234,7 +234,7 @@ endif
|
||||
|
||||
cgif_dep = disabler()
|
||||
if quantisation_package.found()
|
||||
cgif_dep = dependency('cgif', required: get_option('cgif'))
|
||||
cgif_dep = dependency('cgif', version: '>=0.2.0', required: get_option('cgif'))
|
||||
if cgif_dep.found()
|
||||
libvips_deps += cgif_dep
|
||||
cfg_var.set('HAVE_CGIF', '1')
|
||||
|
Loading…
Reference in New Issue
Block a user