legacy-imlib2/src/modules/loaders/loader_webp.c

194 lines
4.9 KiB
C

#include "config.h"
#include "Imlib2_Loader.h"
#include <webp/decode.h>
#include <webp/demux.h>
#include <webp/encode.h>
#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
_load(ImlibImage *im, int load_data)
{
int rc;
WebPData webp_data;
WebPDemuxer *demux;
WebPIterator iter;
int frame, fcount;
ImlibImageFrame *pf;
rc = LOAD_FAIL;
if (im->fi->fsize < 12)
return rc;
webp_data.bytes = im->fi->fdata;
webp_data.size = im->fi->fsize;
/* Init (includes signature check) */
demux = WebPDemux(&webp_data);
if (!demux)
goto quit;
rc = LOAD_BADIMAGE; /* Format accepted */
pf = NULL;
frame = im->frame;
if (frame > 0)
{
fcount = WebPDemuxGetI(demux, WEBP_FF_FRAME_COUNT);
if (frame > 1 && frame > fcount)
QUIT_WITH_RC(LOAD_BADFRAME);
pf = __imlib_GetFrame(im);
if (!pf)
QUIT_WITH_RC(LOAD_OOM);
pf->frame_count = fcount;
pf->loop_count = WebPDemuxGetI(demux, WEBP_FF_LOOP_COUNT);
if (pf->frame_count > 1)
pf->frame_flags |= FF_IMAGE_ANIMATED;
pf->canvas_w = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
pf->canvas_h = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
D("Canvas WxH=%dx%d frames=%d repeat=%d\n",
pf->canvas_w, pf->canvas_h, pf->frame_count, pf->loop_count);
}
else
{
frame = 1;
}
if (!WebPDemuxGetFrame(demux, frame, &iter))
goto quit;
WebPDemuxReleaseIterator(&iter);
im->w = iter.width;
im->h = iter.height;
if (pf)
{
pf->frame_x = iter.x_offset;
pf->frame_y = iter.y_offset;
pf->frame_delay = iter.duration;
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND)
pf->frame_flags |= FF_FRAME_DISPOSE_CLEAR;
if (iter.blend_method == WEBP_MUX_BLEND)
pf->frame_flags |= FF_FRAME_BLEND;
D("Canvas WxH=%dx%d frame=%d/%d X,Y=%d,%d WxH=%dx%d alpha=%d T=%d dm=%d co=%d bl=%d\n", //
pf->canvas_w, pf->canvas_h, iter.frame_num, pf->frame_count,
pf->frame_x, pf->frame_y, im->w, im->h, iter.has_alpha,
pf->frame_delay, iter.dispose_method, iter.complete,
iter.blend_method);
}
if (!IMAGE_DIMENSIONS_OK(im->w, im->h))
goto quit;
im->has_alpha = iter.has_alpha;
if (!load_data)
QUIT_WITH_RC(LOAD_SUCCESS);
/* Load data */
if (!__imlib_AllocateData(im))
QUIT_WITH_RC(LOAD_OOM);
if (WebPDecodeBGRAInto
(iter.fragment.bytes, iter.fragment.size, (uint8_t *) im->data,
sizeof(uint32_t) * im->w * im->h, im->w * 4) == NULL)
goto quit;
if (im->lc)
__imlib_LoadProgress(im, 0, 0, im->w, im->h);
rc = LOAD_SUCCESS;
quit:
if (demux)
WebPDemuxDelete(demux);
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;
ImlibImageTag *compression_tag;
WebPConfig conf;
WebPPicture pic;
int compression;
int lossless;
int free_pic = 0;
rc = LOAD_BADFILE;
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)
{
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) */
}
if (!WebPValidateConfig(&conf))
{
D("WebPValidateConfig failed");
goto quit;
}
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 (free_pic)
WebPPictureFree(&pic);
return rc;
}
IMLIB_LOADER(_formats, _load, _save);