summaryrefslogtreecommitdiff
path: root/src/webp.c
diff options
context:
space:
mode:
authorRodrigo Arias Mallo <rodarima@gmail.com>2024-11-24 18:15:23 +0100
committerRodrigo Arias Mallo <rodarima@gmail.com>2024-11-24 19:08:41 +0100
commitb9e801cb940b45cfd9a4cf95bf5af68b084156b4 (patch)
tree655499d75bf733a773483caf22068e43c598defd /src/webp.c
parent53fb37ae030c294e29e820cc4c34bca4d0ac2e27 (diff)
Add WebP image support
See: https://www.toomanyatoms.com/software/mobilized_dillo.html Authored-By: dogma
Diffstat (limited to 'src/webp.c')
-rw-r--r--src/webp.c201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/webp.c b/src/webp.c
new file mode 100644
index 00000000..ebcc238a
--- /dev/null
+++ b/src/webp.c
@@ -0,0 +1,201 @@
+/*
+ * File: webp.c
+ *
+ * Copyright (C) 2024 dogma
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <config.h>
+#ifdef ENABLE_WEBP
+
+#include <stdlib.h> /* For abort() */
+
+#include <webp/decode.h>
+
+#include "msg.h"
+#include "image.hh"
+#include "cache.h"
+#include "dicache.h"
+
+enum prog_state {
+ IS_finished, IS_init, IS_nextdata
+};
+
+typedef struct {
+ DilloImage *Image; /* Image meta data */
+ DilloUrl *url; /* Primary Key for the dicache */
+ int version; /* Secondary Key for the dicache */
+ int bgcolor; /* Parent widget background color */
+ int state;
+ int y;
+ WebPIDecoder* idec;
+ WebPDecBuffer output_buffer; /* for RGBA */
+} DilloWebp;
+
+
+/*
+ * Free up the resources for this image.
+ */
+static void Webp_free(DilloWebp *webp)
+{
+ _MSG("Webp_free: webp=%p\n", webp);
+
+ WebPFreeDecBuffer(&webp->output_buffer);
+ if (webp->idec)
+ WebPIDelete(webp->idec);
+ dFree(webp);
+}
+
+/*
+ * Finish the decoding process (and free the memory)
+ */
+static void Webp_close(DilloWebp *webp, CacheClient_t *Client)
+{
+ _MSG("Webp_close\n");
+ /* Let dicache know decoding is over */
+ a_Dicache_close(webp->url, webp->version, Client);
+ Webp_free(webp);
+}
+
+/*
+ * Op: Operation to perform.
+ * If (Op == 0)
+ * start or continue processing an image if image data exists.
+ * else
+ * terminate processing, cleanup any allocated memory,
+ * close down the decoding process.
+ *
+ * Client->CbData : pointer to previously allocated work area.
+ * This holds the current state of the image processing and is kept
+ * across calls to this routine.
+ * Client->Buf : Pointer to data start.
+ * Client->BufSize : the size of the data buffer.
+ *
+ * You have to keep track of where you are in the image data and
+ * how much has been processed.
+ *
+ * It's entirely possible that you will not see the end of the data. The
+ * user may terminate transfer via a Stop button or there may be a network
+ * failure. This means that you can't just wait for all the data to be
+ * presented before starting conversion and display.
+ */
+void a_Webp_callback(int Op, void *data)
+{
+ CacheClient_t *Client = data;
+
+ if (Op == CA_Send) {
+ uint8_t* output;
+ VP8StatusCode ret;
+
+ DilloWebp *webp = (DilloWebp *)Client->CbData;
+
+ if (webp->state == IS_init) {
+ WebPBitstreamFeatures features;
+
+ ret = WebPGetFeatures(Client->Buf, Client->BufSize, &features);
+ if (ret != VP8_STATUS_OK) {
+ MSG("features ret is %d\n", ret);
+ return;
+ }
+ if (features.has_alpha) {
+ _MSG("WEBP: Alpha!\n");
+ }
+ webp->output_buffer.colorspace = features.has_alpha ? MODE_RGBA : MODE_RGB;
+
+ a_Dicache_set_parms(webp->url, webp->version, webp->Image,
+ features.width, features.height,
+ DILLO_IMG_TYPE_RGB, 1 / 2.2);
+
+ webp->idec = WebPINewDecoder(&webp->output_buffer);
+ webp->state = IS_nextdata;
+ }
+
+ ret = WebPIUpdate(webp->idec, Client->Buf, Client->BufSize);
+ /* SUSPENDED is a success state that means you don't have the entire file yet */
+ if (ret == VP8_STATUS_SUSPENDED || ret == VP8_STATUS_OK) {
+ /* last_y seems 1-based, which would be kind of crazy, but I would expect
+ * crazy idiocy from google.
+ */
+ int last_y, width, height, stride;
+
+ _MSG("webp completed. status: %s\n", ret == VP8_STATUS_SUSPENDED ? "suspended" : "ok (done)");
+ output = WebPIDecGetRGB(webp->idec, &last_y, &width, &height, &stride);
+ if (!output) {
+ _MSG("webp decoding no output\n");
+ } else {
+ unsigned char *line;
+ int row = webp->y;
+
+ if (webp->output_buffer.colorspace == MODE_RGBA)
+ line = dNew(unsigned char, width * 3);
+ else
+ line = output + row * stride;
+
+ for (; row < last_y; row++) {
+
+ if (webp->output_buffer.colorspace == MODE_RGBA) {
+ int j;
+
+ uint_t bg_blue = (webp->bgcolor) & 0xFF;
+ uint_t bg_green = (webp->bgcolor>>8) & 0xFF;
+ uint_t bg_red = (webp->bgcolor>>16) & 0xFF;
+ for (j = 0; j < width; j++) {
+ uchar_t alpha = output[row * stride + 4 * j + 3];
+ uchar_t r = output[row * stride + 4 * j];
+ uchar_t g = output[row * stride + 4 * j + 1];
+ uchar_t b = output[row * stride + 4 * j + 2];
+ line[3 * j] = (r * alpha + (bg_red * (0xFF - alpha))) / 0xFF;
+ line[3 * j + 1] = (g * alpha + (bg_green * (0xFF - alpha))) / 0xFF;
+ line[3 * j + 2] = (b * alpha + (bg_blue * (0xFF - alpha))) / 0xFF;
+ }
+ } else {
+ line = output + row * stride;
+ }
+ a_Dicache_write(webp->url, webp->version, line, row);
+ }
+ webp->y = last_y;
+
+ if (webp->output_buffer.colorspace == MODE_RGBA)
+ dFree(line);
+ }
+ } else {
+ MSG("webp WebPIUpdate failed with %d\n", ret);
+ }
+ } else if (Op == CA_Close) {
+ Webp_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Webp_free(data);
+ }
+}
+
+/*
+ * Create the image state data that must be kept between calls
+ */
+void *a_Webp_new(DilloImage *Image, DilloUrl *url, int version)
+{
+ DilloWebp *webp = dNew0(DilloWebp, 1);
+ _MSG("a_Webp_new: webp=%p\n", webp);
+
+ webp->Image = Image;
+ webp->url = url;
+ webp->version = version;
+
+ webp->bgcolor = Image->bg_color;
+ webp->state = IS_init;
+ webp->y = 0;
+ webp->idec = NULL;
+ WebPInitDecBuffer(&webp->output_buffer);
+
+ return webp;
+}
+
+#else /* ENABLE_WEBP */
+
+void *a_Webp_new() { return 0; }
+void a_Webp_callback() { return; }
+
+#endif /* ENABLE_WEBP */