cgifsave: theshold alpha and check alpha constraint in one pass (#2827)

* cgifsave: theshold alpha and check alpha constraint in one pass

* cgifsave: get rid of sqrt()
This commit is contained in:
Sergey Alexandrovich 2022-06-03 23:49:03 +06:00 committed by GitHub
parent cb1927e1b7
commit 4fab8beae9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 34 additions and 46 deletions

View File

@ -45,7 +45,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vips/vips.h>
@ -163,7 +162,7 @@ static int vips__cgif_write( void *target, const uint8_t *buffer,
*/
static gboolean
vips_foreign_save_cgif_pixels_are_equal( const VipsPel *cur, const VipsPel *bef,
double maxerror )
double sq_maxerror )
{
if( bef[3] != cur[3] )
/* Alpha channel must be identical.
@ -180,27 +179,7 @@ vips_foreign_save_cgif_pixels_are_equal( const VipsPel *cur, const VipsPel *bef,
const int dG = cur[1] - bef[1];
const int dB = cur[2] - bef[2];
return( sqrt( dR * dR + dG * dG + dB * dB ) <= maxerror );
}
/* Check if the alpha channel of the current frame matches the frame before.
* If the current frame has an alpha component which is not identical to the
* frame from before we are forced to use the transparency index for the
* alpha channel instead of for the transparency size optimization (maxerror).
*/
static gboolean
vips_foreign_save_cgif_check_alpha_constraint( const VipsPel *cur,
const VipsPel *bef, int n_pels )
{
while( n_pels-- ) {
if( cur[3] == 0 && bef[3] != 0 )
return TRUE;
cur += 4;
bef += 4;
}
return FALSE;
return( dR * dR + dG * dG + dB * dB <= sq_maxerror );
}
/* We have a complete frame --- write!
@ -217,8 +196,10 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
VipsPel *frame_bytes =
VIPS_REGION_ADDR( cgif->frame, 0, frame_rect->top );
VipsPel * restrict p;
int i;
VipsPel * restrict cur;
VipsPel * restrict bef;
gboolean has_alpha_constraint = FALSE;
VipsPel *rgb;
CGIF_FrameConfig frame_config;
@ -234,21 +215,32 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
/* Threshold the alpha channel. It's safe to modify the region since
* it's a buffer we made.
*
* Also, check if the alpha channel of the current frame matches the
* frame before.
* If the current frame has an alpha component which is not identical
* to the frame from before we are forced to use the transparency index
* for the alpha channel instead of for the transparency size
* optimization (maxerror).
*/
p = frame_bytes;
cur = frame_bytes;
bef = cgif->frame_bytes_head;
for( i = 0; i < n_pels; i++ ) {
if( p[3] >= 128 )
p[3] = 255;
if( cur[3] >= 128 )
cur[3] = 255;
else {
/* Helps the quanizer generate a better palette.
*/
p[0] = 0;
p[1] = 0;
p[2] = 0;
p[3] = 0;
cur[0] = 0;
cur[1] = 0;
cur[2] = 0;
cur[3] = 0;
if( bef && bef[i * 4 + 3] != 0 )
has_alpha_constraint = TRUE;
}
p += 4;
cur += 4;
}
/* Do we need to compute a new palette? Do it if the frame sum
@ -266,14 +258,14 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
* to [255, 0, 0] are detected.
*/
checksum = 0;
p = frame_bytes;
cur = frame_bytes;
for( i = 0; i < n_pels; i++ ) {
checksum += p[0] * 1000;
checksum += p[1] * 100;
checksum += p[2] * 10;
checksum += p[3];
checksum += cur[0] * 1000;
checksum += cur[1] * 100;
checksum += cur[2] * 10;
checksum += cur[3];
p += 4;
cur += 4;
}
if( cgif->frame_checksum == 0 ||
@ -390,21 +382,15 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
* transparent.
*/
if( cgif->frame_bytes_head ) {
VipsPel *cur, *bef;
gboolean has_alpha_constraint;
cur = frame_bytes;
bef = cgif->frame_bytes_head;
has_alpha_constraint =
vips_foreign_save_cgif_check_alpha_constraint( cur,
bef, n_pels );
/* Transparency trick is only possible when no alpha channel
* constraint is present.
*/
if( !has_alpha_constraint ) {
int i;
uint8_t trans_index;
double sq_maxerror;
trans_index = cgif->lp->count;
if( cgif->has_transparency ) {
@ -413,9 +399,11 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
~CGIF_FRAME_ATTR_HAS_ALPHA;
}
sq_maxerror = cgif->maxerror * cgif->maxerror;
for( i = 0; i < n_pels; i++ ) {
if( vips_foreign_save_cgif_pixels_are_equal(
cur, bef, cgif->maxerror ) )
cur, bef, sq_maxerror ) )
cgif->index[i] = trans_index;
else {
bef[0] = cur[0];