From 8653d2b286ebc15ed8bb90a234f4e7294c34132a Mon Sep 17 00:00:00 2001
From: Sebastião Guerra <sebastiao.luiz.guerra@gmail.com>
Date: Wed, 28 Oct 2026 06:22:48 -0300
Subject: Add WebP decoder support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add an optional libwebp decoder and integrate it with the existing codec build and package conventions.

Signed-off-by: Sebastião Guerra <sebastiao.luiz.guerra@gmail.com>
---
 CMakeLists.txt                     |   1 +
 ConfigureChecks.cmake              |  10 ++
 kernel/CMakeLists.txt              |   1 +
 kernel/include/fmt_codec_webp.h    |  33 +++++++
 kernel/kls_webp/CMakeLists.txt     |  15 +++
 kernel/kls_webp/fmt_codec_webp.cpp | 195 +++++++++++++++++++++++++++++++++++++
 kernel/xpm/codec_webp.xpm          |  25 +++++
 7 files changed, 280 insertions(+)
 create mode 100644 kernel/include/fmt_codec_webp.h
 create mode 100644 kernel/kls_webp/CMakeLists.txt
 create mode 100644 kernel/kls_webp/fmt_codec_webp.cpp
 create mode 100644 kernel/xpm/codec_webp.xpm

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d83fa53..503c93b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -115,6 +115,7 @@ option( BUILD_TTF      "Build ttf codec"      ${BUILD_ALL} )
 option( BUILD_UTAH     "Build utah codec"     ${BUILD_ALL} )
 option( BUILD_WAL      "Build wal codec"      ${BUILD_ALL} )
 option( BUILD_WBMP     "Build wbmp codec"     ${BUILD_ALL} )
+option( BUILD_WEBP     "Build webp codec"     ${BUILD_ALL} )
 option( BUILD_WMF      "Build wmf codec"      ${BUILD_ALL} )
 option( BUILD_XBM      "Build xbm codec"      ${BUILD_ALL} )
 option( BUILD_XCF      "Build xcf codec"      ${BUILD_ALL} )
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index f11d879..27f8503 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -271,6 +271,16 @@ if( BUILD_UTAH )
 endif( )
 
 
+##### checks for webp codec #####
+
+if( BUILD_WEBP )
+  pkg_search_module( WEBP libwebp )
+  if( NOT WEBP_FOUND )
+    tde_message_fatal( "libwebp was requested but not found on your system" )
+  endif( )
+endif( )
+
+
 ##### checks for wmf codec #####
 
 if( BUILD_WMF )
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index bd7d3b6..e874c45 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -98,6 +98,7 @@ tde_conditional_add_subdirectory( BUILD_SVG       kls_svg      )
 tde_conditional_add_subdirectory( BUILD_TIFF      kls_tiff     )
 tde_conditional_add_subdirectory( BUILD_TTF       kls_ttf      )
 tde_conditional_add_subdirectory( BUILD_UTAH      kls_utah     )
+tde_conditional_add_subdirectory( BUILD_WEBP      kls_webp     )
 tde_conditional_add_subdirectory( BUILD_WMF       kls_wmf      )
 tde_conditional_add_subdirectory( BUILD_XCF       kls_xcf      )
 tde_conditional_add_subdirectory( BUILD_XIM       kls_xim      )
diff --git a/kernel/include/fmt_codec_webp.h b/kernel/include/fmt_codec_webp.h
new file mode 100644
index 0000000..c848bc9
--- /dev/null
+++ b/kernel/include/fmt_codec_webp.h
@@ -0,0 +1,33 @@
+/*  This file is part of ksquirrel-libs.
+
+    Copyright (c) 2026 Trinity Desktop Project
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation;
+    either version 2 of the License, or (at your option) any later
+    version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+*/
+
+#ifndef KSQUIRREL_LIBS_CLASS_DEFINITION_WEBP_H
+#define KSQUIRREL_LIBS_CLASS_DEFINITION_WEBP_H
+
+#include <vector>
+
+#include "ksquirrel-libs/fmt_codec_base.h"
+
+class fmt_codec : public fmt_codec_base
+{
+    public:
+        BASE_CODEC_DECLARATIONS
+
+    private:
+        std::vector<RGBA> pixels;
+};
+
+#endif
diff --git a/kernel/kls_webp/CMakeLists.txt b/kernel/kls_webp/CMakeLists.txt
new file mode 100644
index 0000000..c75c6db
--- /dev/null
+++ b/kernel/kls_webp/CMakeLists.txt
@@ -0,0 +1,15 @@
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_SOURCE_DIR}/kernel/include
+  ${CMAKE_BINARY_DIR}
+  ${WEBP_INCLUDE_DIRS}
+)
+
+link_directories( ${WEBP_LIBRARY_DIRS} )
+
+tde_add_library( kls_webp SHARED AUTOMOC
+  SOURCES fmt_codec_webp.cpp
+  LINK ksquirrel-libs-shared ${WEBP_LIBRARIES}
+  VERSION 0.8.0
+  DESTINATION ${LIB_INSTALL_DIR}/ksquirrel-libs
+)
diff --git a/kernel/kls_webp/fmt_codec_webp.cpp b/kernel/kls_webp/fmt_codec_webp.cpp
new file mode 100644
index 0000000..fdb4442
--- /dev/null
+++ b/kernel/kls_webp/fmt_codec_webp.cpp
@@ -0,0 +1,195 @@
+/*  This file is part of ksquirrel-libs.
+
+    Copyright (c) 2026 Trinity Desktop Project
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation;
+    either version 2 of the License, or (at your option) any later
+    version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+*/
+
+#include <cstring>
+#include <limits>
+#include <new>
+#include <stdint.h>
+#include <stdexcept>
+#include <vector>
+
+#include <webp/decode.h>
+
+#include "ksquirrel-libs/error.h"
+#include "fmt_codec_webp.h"
+#include "../xpm/codec_webp.xpm"
+
+fmt_codec::fmt_codec() : fmt_codec_base()
+{}
+
+fmt_codec::~fmt_codec()
+{}
+
+void fmt_codec::options(codec_options *o)
+{
+    o->version = "1.0.0";
+    o->name = "WebP";
+    o->filter = "*.webp *.WEBP ";
+    o->config = "";
+    o->mime = "RIFF....WEBP";
+    o->mimetype = "image/webp";
+    o->pixmap = codec_webp;
+    o->readable = true;
+    o->canbemultiple = false;
+    o->writestatic = false;
+    o->writeanimated = false;
+    o->needtempfile = false;
+}
+
+s32 fmt_codec::read_init(const std::string &file)
+{
+    std::vector<RGBA>().swap(pixels);
+    finfo.image.clear();
+    finfo.meta.clear();
+    finfo.animated = false;
+    currentImage = -1;
+    line = -1;
+
+    frs.open(file.c_str(), ios::binary | ios::in);
+    if(!frs.good())
+        return SQE_R_NOFILE;
+
+    frs.seekg(0, ios::end);
+    const std::streamoff fileSize = frs.tellg();
+    if(fileSize <= 0 || static_cast<uintmax_t>(fileSize) >
+       static_cast<uintmax_t>(std::numeric_limits<size_t>::max()))
+    {
+        frs.close();
+        return SQE_R_BADFILE;
+    }
+
+    std::vector<uint8_t> data;
+    try
+    {
+        data.resize(static_cast<size_t>(fileSize));
+    }
+    catch(const std::bad_alloc &)
+    {
+        frs.close();
+        return SQE_R_NOMEMORY;
+    }
+    catch(const std::length_error &)
+    {
+        frs.close();
+        return SQE_R_NOMEMORY;
+    }
+
+    frs.seekg(0, ios::beg);
+    size_t offset = 0;
+    while(offset < data.size())
+    {
+        const size_t remaining = data.size() - offset;
+        const int chunkSize = remaining > static_cast<size_t>(std::numeric_limits<int>::max())
+                            ? std::numeric_limits<int>::max()
+                            : static_cast<int>(remaining);
+        if(!frs.readK(&data[offset], chunkSize))
+        {
+            frs.close();
+            return SQE_R_BADFILE;
+        }
+        offset += static_cast<size_t>(chunkSize);
+    }
+    frs.close();
+
+    WebPBitstreamFeatures features;
+    if(WebPGetFeatures(&data[0], data.size(), &features) != VP8_STATUS_OK ||
+       features.width <= 0 || features.height <= 0)
+        return SQE_R_BADFILE;
+
+    // KSquirrel has no frame-selection API, so animated WebP is unsupported.
+    if(features.has_animation)
+        return SQE_R_NOTSUPPORTED;
+
+    const uint64_t pixelCount = static_cast<uint64_t>(features.width) *
+                                static_cast<uint64_t>(features.height);
+    if(static_cast<size_t>(features.width) > std::numeric_limits<size_t>::max() / sizeof(RGBA) ||
+       features.width > std::numeric_limits<int>::max() / sizeof(RGBA) ||
+       pixelCount > std::numeric_limits<size_t>::max() / sizeof(RGBA))
+        return SQE_R_NOMEMORY;
+
+    try
+    {
+        pixels.resize(static_cast<size_t>(pixelCount));
+    }
+    catch(const std::bad_alloc &)
+    {
+        return SQE_R_NOMEMORY;
+    }
+    catch(const std::length_error &)
+    {
+        return SQE_R_NOMEMORY;
+    }
+
+    const size_t stride = static_cast<size_t>(features.width) * sizeof(RGBA);
+    if(!WebPDecodeRGBAInto(&data[0], data.size(),
+                           reinterpret_cast<uint8_t *>(&pixels[0]),
+                           pixels.size() * sizeof(RGBA), stride))
+    {
+        std::vector<RGBA>().swap(pixels);
+        return SQE_R_BADFILE;
+    }
+    std::vector<uint8_t>().swap(data);
+
+    fmt_image image;
+    image.w = features.width;
+    image.h = features.height;
+    image.bpp = features.has_alpha ? 32 : 24;
+    image.hasalpha = features.has_alpha != 0;
+    image.colorspace = image.hasalpha ? "RGBA" : "RGB";
+    image.compression = "WebP";
+    finfo.image.push_back(image);
+
+    return SQE_OK;
+}
+
+s32 fmt_codec::read_next()
+{
+    currentImage++;
+    line = -1;
+    return currentImage == 0 ? SQE_OK : SQE_NOTOK;
+}
+
+s32 fmt_codec::read_next_pass()
+{
+    line = -1;
+    return SQE_OK;
+}
+
+s32 fmt_codec::read_scanline(RGBA *scan)
+{
+    if(currentImage != 0 || !scan || finfo.image.empty())
+        return SQE_R_BADFILE;
+
+    ++line;
+    if(line < 0 || line >= finfo.image[0].h)
+        return SQE_R_BADFILE;
+
+    const size_t offset = static_cast<size_t>(line) * finfo.image[0].w;
+    memcpy(scan, &pixels[offset], static_cast<size_t>(finfo.image[0].w) * sizeof(RGBA));
+    return SQE_OK;
+}
+
+void fmt_codec::read_close()
+{
+    std::vector<RGBA>().swap(pixels);
+    finfo.image.clear();
+    finfo.meta.clear();
+    finfo.animated = false;
+    currentImage = -1;
+    line = -1;
+}
+
+#include "fmt_codec_cd_func.h"
diff --git a/kernel/xpm/codec_webp.xpm b/kernel/xpm/codec_webp.xpm
new file mode 100644
index 0000000..59cb3d4
--- /dev/null
+++ b/kernel/xpm/codec_webp.xpm
@@ -0,0 +1,25 @@
+/* XPM */
+/* Copyright (c) 2026 Trinity Desktop Project */
+static const char *codec_webp[] = {
+"16 16 4 1",
+"  c None",
+". c #245A91",
+"+ c #FFFFFF",
+"X c #58A6D6",
+"                ",
+"  XXXXXXXXXXXX  ",
+"  X..........X  ",
+"  X..........X  ",
+"  X..........X  ",
+"  +.+++.++..++  ",
+"  +.++..+.+.+X  ",
+"  +.+++.++..++  ",
+"  ++++..+.+.+X  ",
+"  +.+++.++..+X  ",
+"  X..........X  ",
+"  X..........X  ",
+"  X..........X  ",
+"  X..........X  ",
+"  XXXXXXXXXXXX  ",
+"                "
+};
-- 
cgit v1.2.3

