From 4f928f53a74e5c10d0a7374b73b2bb1ed16e2aca Mon Sep 17 00:00:00 2001 From: NRK Date: Fri, 12 May 2023 12:05:06 +0600 Subject: [PATCH] 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. --- src/modules/loaders/loader_webp.c | 67 ++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/modules/loaders/loader_webp.c b/src/modules/loaders/loader_webp.c index b35e801..88cfe62 100644 --- a/src/modules/loaders/loader_webp.c +++ b/src/modules/loaders/loader_webp.c @@ -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; }