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 DBG_PFX "LDR-webp"
#define CLAMP(X, MIN, MAX) ((X) < (MIN) ? (MIN) : ((X) > (MAX) ? (MAX) : (X)))
static const char *const _formats[] = { "webp" }; static const char *const _formats[] = { "webp" };
static int static int
@ -114,41 +116,76 @@ _load(ImlibImage * im, int load_data)
return rc; 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 static int
_save(ImlibImage * im) _save(ImlibImage * im)
{ {
int rc; int rc;
FILE *f = im->fi->fp; FILE *f = im->fi->fp;
ImlibImageTag *quality_tag; ImlibImageTag *quality_tag;
float quality; ImlibImageTag *compression_tag;
uint8_t *fdata; WebPConfig conf;
size_t encoded_size; WebPPicture pic;
int compression;
int lossless;
int free_pic = 0;
rc = LOAD_FAIL; rc = LOAD_FAIL;
fdata = NULL;
quality = 75; if (!WebPConfigInit(&conf) || !WebPPictureInit(&pic))
goto quit;
conf.quality = 75;
quality_tag = __imlib_GetTag(im, "quality"); quality_tag = __imlib_GetTag(im, "quality");
if (quality_tag) 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; compression = 9 - (conf.quality / 10); /* convert to compression */
if (quality < 0) if (compression_tag)
quality = 0; compression = compression_tag->val;
else if (quality > 100) WebPConfigLosslessPreset(&conf, CLAMP(compression, 0, 9));
quality = 100; }
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, if (!WebPValidateConfig(&conf))
im->w * 4, quality, &fdata); {
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; goto quit;
rc = LOAD_SUCCESS; rc = LOAD_SUCCESS;
quit: quit:
if (fdata) if (free_pic)
WebPFree(fdata); WebPPictureFree(&pic);
return rc; return rc;
} }