summaryrefslogtreecommitdiff
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
parent53fb37ae030c294e29e820cc4c34bca4d0ac2e27 (diff)
Add WebP image support
See: https://www.toomanyatoms.com/software/mobilized_dillo.html Authored-By: dogma
-rw-r--r--configure.ac31
-rw-r--r--src/IO/mime.c3
-rw-r--r--src/IO/mime.h2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/dicache.c16
-rw-r--r--src/dwebp.h19
-rw-r--r--src/webp.c201
7 files changed, 274 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 268e24ba..03715e1b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,11 @@ AC_ARG_ENABLE([png],
[enable_png=$enableval],
[enable_png=yes])
+AC_ARG_ENABLE([webp],
+ [AS_HELP_STRING([--disable-webp], [Disable support for WEBP images])],
+ [enable_webp=$enableval],
+ [enable_webp=yes])
+
AC_ARG_ENABLE([jpeg],
[AS_HELP_STRING([--disable-jpeg], [Disable support for JPEG images])],
[enable_jpeg=$enableval],
@@ -284,6 +289,30 @@ if test "x$jpeg_ok" = "xyes"; then
AC_DEFINE([ENABLE_JPEG], [1], [Enable JPEG images])
fi
+dnl ----------------
+dnl Test for libwebp
+dnl ----------------
+dnl
+if test "x$enable_webp" = "xyes"; then
+ AC_CHECK_HEADER(webp/decode.h, webp_ok=yes, webp_ok=no)
+
+ if test "x$webp_ok" = "xyes"; then
+ old_libs="$LIBS"
+ AC_CHECK_LIB(webp, WebPGetInfo, webp_ok=yes, webp_ok=no)
+ LIBS="$old_libs"
+ fi
+
+ if test "x$webp_ok" = "xyes"; then
+ LIBWEBP_LIBS="-lwebp"
+ else
+ AC_MSG_WARN([*** No libwebp found. Disabling webp images.***])
+ fi
+fi
+
+if test "x$webp_ok" = "xyes"; then
+ AC_DEFINE([ENABLE_WEBP], [1], [Enable webp images])
+fi
+
dnl -------------
dnl Test for zlib
dnl -------------
@@ -688,6 +717,7 @@ AC_SUBST(LIBJPEG_LDFLAGS)
AC_SUBST(LIBJPEG_CPPFLAGS)
AC_SUBST(LIBPNG_LIBS)
AC_SUBST(LIBPNG_CFLAGS)
+AC_SUBST(LIBWEBP_LIBS)
AC_SUBST(LIBZ_LIBS)
AC_SUBST(LIBSSL_LIBS)
AC_SUBST(LIBPTHREAD_LIBS)
@@ -739,6 +769,7 @@ _AS_ECHO([ JPEG enabled : ${jpeg_ok}])
_AS_ECHO([ PNG enabled : ${png_ok}])
_AS_ECHO([ GIF enabled : ${enable_gif}])
_AS_ECHO([ SVG enabled : ${enable_svg}])
+_AS_ECHO([ WEBP enabled : ${enable_webp}])
_AS_ECHO([])
_AS_ECHO([ HTML tests : ${html_tests_ok}])
_AS_ECHO([])
diff --git a/src/IO/mime.c b/src/IO/mime.c
index 1f6a3d47..3d88429e 100644
--- a/src/IO/mime.c
+++ b/src/IO/mime.c
@@ -108,6 +108,9 @@ void a_Mime_init(void)
Mime_add_minor_type("image/png", a_Dicache_png_image);
Mime_add_minor_type("image/x-png", a_Dicache_png_image); /* deprecated */
#endif
+#ifdef ENABLE_WEBP
+ Mime_add_minor_type("image/webp", a_Dicache_webp_image);
+#endif
#ifdef ENABLE_SVG
Mime_add_minor_type("image/svg+xml", a_Dicache_svg_image);
#endif
diff --git a/src/IO/mime.h b/src/IO/mime.h
index 47bbf0ba..341e2ace 100644
--- a/src/IO/mime.h
+++ b/src/IO/mime.h
@@ -32,6 +32,8 @@ void *a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
void **Data);
void *a_Dicache_png_image (const char *Type,void *web, CA_Callback_t *Call,
void **Data);
+void *a_Dicache_webp_image (const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
void **Data);
void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
diff --git a/src/Makefile.am b/src/Makefile.am
index bb1f6fc3..34c05e30 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,7 +20,7 @@ dillo_LDADD = \
$(top_builddir)/dw/libDw-fltk.a \
$(top_builddir)/dw/libDw-core.a \
$(top_builddir)/lout/liblout.a \
- @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ \
+ @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBWEBP_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ \
@LIBICONV_LIBS@ @LIBPTHREAD_LIBS@ @LIBX11_LIBS@ @LIBSSL_LIBS@
dillo_SOURCES = \
@@ -118,6 +118,8 @@ dillo_SOURCES = \
djpeg.h \
png.c \
dpng.h \
+ webp.c \
+ dwebp.h \
svg.c \
nanosvg.h \
nanosvgrast.h \
diff --git a/src/dicache.c b/src/dicache.c
index c9f4067b..e86f5655 100644
--- a/src/dicache.c
+++ b/src/dicache.c
@@ -19,14 +19,15 @@
#include "web.hh"
#include "dicache.h"
#include "dpng.h"
+#include "dwebp.h"
#include "dgif.h"
#include "djpeg.h"
#include "dsvg.h"
-
enum {
DIC_Gif,
DIC_Png,
+ DIC_Webp,
DIC_Jpeg,
DIC_Svg
};
@@ -390,6 +391,10 @@ static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
DicEntry->Decoder = (CA_Callback_t)a_Gif_callback;
DicEntry->DecoderData =
a_Gif_new(web->Image, DicEntry->url, DicEntry->version);
+ } else if (ImgType == DIC_Webp) {
+ DicEntry->Decoder = (CA_Callback_t)a_Webp_callback;
+ DicEntry->DecoderData =
+ a_Webp_new(web->Image, DicEntry->url, DicEntry->version);
} else if (ImgType == DIC_Png) {
DicEntry->Decoder = (CA_Callback_t)a_Png_callback;
DicEntry->DecoderData =
@@ -422,6 +427,15 @@ void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
}
/**
+ * WEBP wrapper for Dicache_image()
+ */
+void *a_Dicache_webp_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Webp, Type, Ptr, Call, Data);
+}
+
+/**
* GIF wrapper for Dicache_image()
*/
void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
diff --git a/src/dwebp.h b/src/dwebp.h
new file mode 100644
index 00000000..930380b0
--- /dev/null
+++ b/src/dwebp.h
@@ -0,0 +1,19 @@
+#ifndef __WEBP_H__
+#define __WEBP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Webp_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Webp_callback(int Op, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__WEBP_H__ */
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 */