WEBP saver: allow lossless and respect compression tag

this treats quality == 100 as lossless similar to some other savers.

in case of lossless encoding, compression is derived from "quality" if
compression_tag was not set. in case of lossy encoding, the default is
used when compression_tag isn't set.

additionally, this fixes a bug in the older saver where fwrite might
have been called with a null fdata (which is UB) in case of an error.
This commit is contained in:
NRK 2023-05-12 12:05:06 +06:00 committed by Kim Woelders
parent 8a36ae6d8d
commit 4f928f53a7
1 changed files with 52 additions and 15 deletions

View File

@ -7,6 +7,8 @@
#define DBG_PFX "LDR-webp"
#define CLAMP(X, MIN, MAX) ((X) < (MIN) ? (MIN) : ((X) > (MAX) ? (MAX) : (X)))
static const char *const _formats[] = { "webp" };
static int
@ -114,41 +116,76 @@ _load(ImlibImage * im, int load_data)
return rc;
}
static int
webp_write(const uint8_t * data, size_t size, const WebPPicture * pic)
{
FILE *f = pic->custom_ptr;
return fwrite(data, 1, size, f) == size;
}
static int
_save(ImlibImage * im)
{
int rc;
FILE *f = im->fi->fp;
ImlibImageTag *quality_tag;
float quality;
uint8_t *fdata;
size_t encoded_size;
ImlibImageTag *compression_tag;
WebPConfig conf;
WebPPicture pic;
int compression;
int lossless;
int free_pic = 0;
rc = LOAD_FAIL;
fdata = NULL;
quality = 75;
if (!WebPConfigInit(&conf) || !WebPPictureInit(&pic))
goto quit;
conf.quality = 75;
quality_tag = __imlib_GetTag(im, "quality");
if (quality_tag)
conf.quality = CLAMP(quality_tag->val, 0, 100);
compression_tag = __imlib_GetTag(im, "compression");
/* other savers seem to treat quality 100 as lossless, do the same here. */
lossless = conf.quality == 100;
if (lossless)
{
quality = quality_tag->val;
if (quality < 0)
quality = 0;
else if (quality > 100)
quality = 100;
compression = 9 - (conf.quality / 10); /* convert to compression */
if (compression_tag)
compression = compression_tag->val;
WebPConfigLosslessPreset(&conf, CLAMP(compression, 0, 9));
}
else if (compression_tag) /* for lossly encoding, only change conf.method if compression_tag was set */
{
conf.method = CLAMP(compression_tag->val, 0, 9);
conf.method *= 0.67; /* convert from [0, 9] to [0, 6]. (6/9 == 0.67) */
}
encoded_size = WebPEncodeBGRA((uint8_t *) im->data, im->w, im->h,
im->w * 4, quality, &fdata);
if (!WebPValidateConfig(&conf))
{
D("WebPValidateConfig failed");
goto quit;
}
if (fwrite(fdata, encoded_size, 1, f) != 1)
pic.use_argb = lossless; /* use_argb is recommended for lossless */
pic.width = im->w;
pic.height = im->h;
pic.writer = webp_write;
pic.custom_ptr = f;
if (!WebPPictureImportBGRA(&pic, (uint8_t *) im->data, im->w * 4))
QUIT_WITH_RC(LOAD_OOM);
free_pic = 1;
if (!WebPEncode(&conf, &pic))
goto quit;
rc = LOAD_SUCCESS;
quit:
if (fdata)
WebPFree(fdata);
if (free_pic)
WebPPictureFree(&pic);
return rc;
}