From 999af030383ce99d70d51bc02fded62ba4e45380 Mon Sep 17 00:00:00 2001 From: kunitoki Date: Tue, 17 Dec 2024 11:39:36 +0100 Subject: [PATCH] Added rive_decoders --- cmake/yup_modules.cmake | 2 + .../include/rive/decoders/bitmap_decoder.hpp | 78 ++++++++ thirdparty/rive_decoders/rive_decoders.cpp | 39 ++++ thirdparty/rive_decoders/rive_decoders.h | 47 +++++ thirdparty/rive_decoders/rive_decoders.mm | 24 +++ .../rive_decoders/source/bitmap_decoder.cpp | 71 ++++++++ .../rive_decoders/source/bitmap_decoder_cg.mm | 168 ++++++++++++++++++ .../source/bitmap_decoder_thirdparty.cpp | 97 ++++++++++ .../rive_decoders/source/decode_jpeg.cpp | 131 ++++++++++++++ .../rive_decoders/source/decode_png.cpp | 160 +++++++++++++++++ .../rive_decoders/source/decode_webp.cpp | 72 ++++++++ thirdparty/rive_renderer/rive_renderer.h | 4 +- 12 files changed, 891 insertions(+), 2 deletions(-) create mode 100644 thirdparty/rive_decoders/include/rive/decoders/bitmap_decoder.hpp create mode 100644 thirdparty/rive_decoders/rive_decoders.cpp create mode 100644 thirdparty/rive_decoders/rive_decoders.h create mode 100644 thirdparty/rive_decoders/rive_decoders.mm create mode 100644 thirdparty/rive_decoders/source/bitmap_decoder.cpp create mode 100644 thirdparty/rive_decoders/source/bitmap_decoder_cg.mm create mode 100644 thirdparty/rive_decoders/source/bitmap_decoder_thirdparty.cpp create mode 100644 thirdparty/rive_decoders/source/decode_jpeg.cpp create mode 100644 thirdparty/rive_decoders/source/decode_png.cpp create mode 100644 thirdparty/rive_decoders/source/decode_webp.cpp diff --git a/cmake/yup_modules.cmake b/cmake/yup_modules.cmake index 85b7e4f0..5270fd00 100644 --- a/cmake/yup_modules.cmake +++ b/cmake/yup_modules.cmake @@ -471,9 +471,11 @@ function (_yup_add_default_modules modules_path) yup_add_module (${modules_path}/thirdparty/glad) yup_add_module (${modules_path}/thirdparty/harfbuzz) yup_add_module (${modules_path}/thirdparty/libpng) + yup_add_module (${modules_path}/thirdparty/libwebp) yup_add_module (${modules_path}/thirdparty/sheenbidi) yup_add_module (${modules_path}/thirdparty/yoga_library) yup_add_module (${modules_path}/thirdparty/rive) + yup_add_module (${modules_path}/thirdparty/rive_decoders) yup_add_module (${modules_path}/thirdparty/rive_renderer) yup_add_module (${modules_path}/thirdparty/oboe_library) diff --git a/thirdparty/rive_decoders/include/rive/decoders/bitmap_decoder.hpp b/thirdparty/rive_decoders/include/rive/decoders/bitmap_decoder.hpp new file mode 100644 index 00000000..e9b98567 --- /dev/null +++ b/thirdparty/rive_decoders/include/rive/decoders/bitmap_decoder.hpp @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Rive + */ + +#ifndef _RIVE_BITMAP_DECODER_HPP_ +#define _RIVE_BITMAP_DECODER_HPP_ + +#include +#include + +/// Bitmap will always take ownership of the bytes it is constructed with. +class Bitmap +{ +public: + enum class PixelFormat : uint8_t + { + RGB, + RGBA + }; + + Bitmap(uint32_t width, + uint32_t height, + PixelFormat pixelFormat, + std::unique_ptr bytes); + + Bitmap(uint32_t width, + uint32_t height, + PixelFormat pixelFormat, + const uint8_t* bytes); + +private: + uint32_t m_Width; + uint32_t m_Height; + PixelFormat m_PixelFormat; + std::unique_ptr m_Bytes; + +public: + uint32_t width() const { return m_Width; } + uint32_t height() const { return m_Height; } + PixelFormat pixelFormat() const { return m_PixelFormat; } + const uint8_t* bytes() const { return m_Bytes.get(); } + std::unique_ptr detachBytes() + { + return std::move(m_Bytes); + } + size_t byteSize() const; + size_t byteSize(PixelFormat format) const; + size_t bytesPerPixel(PixelFormat format) const; + + enum class ImageType + { + png, + jpeg, + webp, + }; + + using BitmapDecoder = std::unique_ptr (*)(const uint8_t bytes[], + size_t byteCount); + + struct ImageFormat + { + const char* name; + ImageType type; + std::vector fingerprint; + BitmapDecoder decodeImage; + }; + + static const ImageFormat* RecognizeImageFormat(const uint8_t bytes[], + size_t byteCount); + + static std::unique_ptr decode(const uint8_t bytes[], + size_t byteCount); + + // Change the pixel format (note this will resize bytes). + void pixelFormat(PixelFormat format); +}; + +#endif diff --git a/thirdparty/rive_decoders/rive_decoders.cpp b/thirdparty/rive_decoders/rive_decoders.cpp new file mode 100644 index 00000000..43c78931 --- /dev/null +++ b/thirdparty/rive_decoders/rive_decoders.cpp @@ -0,0 +1,39 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2024 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "rive_decoders.h" + +#include + +#include "source/bitmap_decoder.cpp" +#include "source/bitmap_decoder_thirdparty.cpp" + +#if RIVE_JPEG +#include "source/decode_jpeg.cpp" +#endif + +#if RIVE_PNG +#include "source/decode_png.cpp" +#endif + +#if RIVE_WEBP +#include "source/decode_webp.cpp" +#endif diff --git a/thirdparty/rive_decoders/rive_decoders.h b/thirdparty/rive_decoders/rive_decoders.h new file mode 100644 index 00000000..0206a806 --- /dev/null +++ b/thirdparty/rive_decoders/rive_decoders.h @@ -0,0 +1,47 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2024 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +/* + ============================================================================== + + BEGIN_JUCE_MODULE_DECLARATION + + ID: rive_decoders + vendor: rive + version: 1.0 + name: Rive Decoders. + description: The Rive Decoders is a companion library for ratser image decoding. + website: https://github.com/rive-app/rive-runtime + license: MIT + + dependencies: libpng + searchpaths: include + defines: RIVE_PNG=1 + enableARC: 1 + + END_JUCE_MODULE_DECLARATION + + ============================================================================== +*/ + +#pragma once + +#include diff --git a/thirdparty/rive_decoders/rive_decoders.mm b/thirdparty/rive_decoders/rive_decoders.mm new file mode 100644 index 00000000..5f0786ff --- /dev/null +++ b/thirdparty/rive_decoders/rive_decoders.mm @@ -0,0 +1,24 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2024 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "rive_decoders.cpp" + +// #include "source/bitmap_decoder_cg.mm" diff --git a/thirdparty/rive_decoders/source/bitmap_decoder.cpp b/thirdparty/rive_decoders/source/bitmap_decoder.cpp new file mode 100644 index 00000000..b31e0126 --- /dev/null +++ b/thirdparty/rive_decoders/source/bitmap_decoder.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/decoders/bitmap_decoder.hpp" +#include "rive/rive_types.hpp" +#include +#include +#include + +Bitmap::Bitmap(uint32_t width, + uint32_t height, + PixelFormat pixelFormat, + std::unique_ptr bytes) : + m_Width(width), + m_Height(height), + m_PixelFormat(pixelFormat), + m_Bytes(std::move(bytes)) +{} + +Bitmap::Bitmap(uint32_t width, + uint32_t height, + PixelFormat pixelFormat, + const uint8_t* bytes) : + Bitmap(width, height, pixelFormat, std::unique_ptr(bytes)) +{} + +size_t Bitmap::bytesPerPixel(PixelFormat format) const +{ + switch (format) + { + case PixelFormat::RGB: + return 3; + case PixelFormat::RGBA: + return 4; + } + RIVE_UNREACHABLE(); +} + +size_t Bitmap::byteSize(PixelFormat format) const +{ + return m_Width * m_Height * bytesPerPixel(format); +} + +size_t Bitmap::byteSize() const { return byteSize(m_PixelFormat); } + +void Bitmap::pixelFormat(PixelFormat format) +{ + if (format == m_PixelFormat) + { + return; + } + auto nextByteSize = byteSize(format); + auto nextBytes = std::unique_ptr(new uint8_t[nextByteSize]); + + size_t fromBytesPerPixel = bytesPerPixel(m_PixelFormat); + size_t toBytesPerPixel = bytesPerPixel(format); + int writeIndex = 0; + int readIndex = 0; + for (uint32_t i = 0; i < m_Width * m_Height; i++) + { + for (size_t j = 0; j < toBytesPerPixel; j++) + { + nextBytes[writeIndex++] = + j < fromBytesPerPixel ? m_Bytes[readIndex++] : 255; + } + } + + m_Bytes = std::move(nextBytes); + m_PixelFormat = format; +} diff --git a/thirdparty/rive_decoders/source/bitmap_decoder_cg.mm b/thirdparty/rive_decoders/source/bitmap_decoder_cg.mm new file mode 100644 index 00000000..fef10626 --- /dev/null +++ b/thirdparty/rive_decoders/source/bitmap_decoder_cg.mm @@ -0,0 +1,168 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/decoders/bitmap_decoder.hpp" +#include "rive/rive_types.hpp" +#include "rive/math/simd.hpp" +#include "rive/math/math_types.hpp" +#include "rive/core/type_conversions.hpp" +#include "utils/auto_cf.hpp" + +#include + +#if TARGET_OS_IPHONE +#include +#include +#elif TARGET_OS_MAC +#include +#endif + +#include +#include +#include + +// Represents raw, premultiplied, RGBA image data with tightly packed rows +// (width * 4 bytes). +struct PlatformCGImage +{ + uint32_t width = 0; + uint32_t height = 0; + bool opaque = false; + std::unique_ptr pixels; +}; + +bool cg_image_decode(const uint8_t* encodedBytes, + size_t encodedSizeInBytes, + PlatformCGImage* platformImage) +{ + AutoCF data = + CFDataCreate(kCFAllocatorDefault, encodedBytes, encodedSizeInBytes); + if (!data) + { + return false; + } + + AutoCF source = CGImageSourceCreateWithData(data, nullptr); + if (!source) + { + return false; + } + + AutoCF image = CGImageSourceCreateImageAtIndex(source, 0, nullptr); + if (!image) + { + return false; + } + + bool isOpaque = false; + switch (CGImageGetAlphaInfo(image.get())) + { + case kCGImageAlphaNone: + case kCGImageAlphaNoneSkipFirst: + case kCGImageAlphaNoneSkipLast: + isOpaque = true; + break; + default: + break; + } + + const size_t width = CGImageGetWidth(image); + const size_t height = CGImageGetHeight(image); + const size_t rowBytes = width * 4; // 4 bytes per pixel + const size_t size = rowBytes * height; + + const size_t bitsPerComponent = 8; + CGBitmapInfo cgInfo = kCGBitmapByteOrder32Big; // rgba + if (isOpaque) + { + cgInfo |= kCGImageAlphaNoneSkipLast; + } + else + { + cgInfo |= kCGImageAlphaPremultipliedLast; + } + + std::unique_ptr pixels(new uint8_t[size]); + + AutoCF cs = CGColorSpaceCreateDeviceRGB(); + AutoCF cg = CGBitmapContextCreate( + pixels.get(), width, height, bitsPerComponent, rowBytes, cs, cgInfo); + if (!cg) + { + return false; + } + + CGContextSetBlendMode(cg, kCGBlendModeCopy); + CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); + + platformImage->width = rive::castTo(width); + platformImage->height = rive::castTo(height); + platformImage->opaque = isOpaque; + platformImage->pixels = std::move(pixels); + + return true; +} + +std::unique_ptr Bitmap::decode(const uint8_t bytes[], size_t byteCount) +{ + PlatformCGImage image; + if (!cg_image_decode(bytes, byteCount, &image)) + { + return nullptr; + } + + // CG only supports premultiplied alpha. Unmultiply now. + size_t imageNumPixels = image.height * image.width; + size_t imageSizeInBytes = imageNumPixels * 4; + // Process 2 pixels at once, deal with odd number of pixels + if (imageNumPixels & 1) + { + imageSizeInBytes -= 4; + } + size_t i; + for (i = 0; i < imageSizeInBytes; i += 8) + { + // Load 2 pixels into 64 bits + auto twoPixels = rive::simd::load(&image.pixels[i]); + auto a0 = twoPixels[3]; + auto a1 = twoPixels[7]; + // Avoid computation if both pixels are either fully transparent or + // opaque pixels + if ((a0 > 0 && a0 < 255) || (a1 > 0 && a1 < 255)) + { + // Avoid potential division by zero + a0 = std::max(a0, 1); + a1 = std::max(a1, 1); + // Cast to 16 bits to avoid overflow + rive::uint16x8 rgbaWidex2 = rive::simd::cast(twoPixels); + // Unpremult: multiply by RGB by "255.0 / alpha" + rgbaWidex2 *= rive::uint16x8{255, 255, 255, 1, 255, 255, 255, 1}; + rgbaWidex2 /= rive::uint16x8{a0, a0, a0, 1, a1, a1, a1, 1}; + // Cast back to 8 bits and store + twoPixels = rive::simd::cast(rgbaWidex2); + rive::simd::store(&image.pixels[i], twoPixels); + } + } + // Process last odd pixel if needed + if (imageNumPixels & 1) + { + // Load 1 pixel into 32 bits + auto rgba = rive::simd::load(&image.pixels[i]); + // Avoid computation for fully transparent or opaque pixels + if (rgba.a > 0 && rgba.a < 255) + { + // Cast to 16 bits to avoid overflow + rive::uint16x4 rgbaWide = rive::simd::cast(rgba); + // Unpremult: multiply by RGB by "255.0 / alpha" + rgbaWide *= rive::uint16x4{255, 255, 255, 1}; + rgbaWide /= rive::uint16x4{rgba.a, rgba.a, rgba.a, 1}; + // Cast back to 8 bits and store + rgba = rive::simd::cast(rgbaWide); + rive::simd::store(&image.pixels[i], rgba); + } + } + + return std::make_unique( + image.width, image.height, PixelFormat::RGBA, std::move(image.pixels)); +} diff --git a/thirdparty/rive_decoders/source/bitmap_decoder_thirdparty.cpp b/thirdparty/rive_decoders/source/bitmap_decoder_thirdparty.cpp new file mode 100644 index 00000000..2d88efbe --- /dev/null +++ b/thirdparty/rive_decoders/source/bitmap_decoder_thirdparty.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/decoders/bitmap_decoder.hpp" + +#include +#include +#include + +#ifdef RIVE_PNG +std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount); +#endif +#ifdef RIVE_JPEG +std::unique_ptr DecodeJpeg(const uint8_t bytes[], size_t byteCount); +#endif +#ifdef RIVE_WEBP +std::unique_ptr DecodeWebP(const uint8_t bytes[], size_t byteCount); +#endif + +static Bitmap::ImageFormat _formats[] = { + { + "png", + Bitmap::ImageType::png, + {0x89, 0x50, 0x4E, 0x47}, +#ifdef RIVE_PNG + DecodePng, +#else + nullptr, +#endif + }, + { + "jpeg", + Bitmap::ImageType::jpeg, + {0xFF, 0xD8, 0xFF}, +#ifdef RIVE_JPEG + DecodeJpeg, +#else + nullptr, +#endif + }, + { + "webp", + Bitmap::ImageType::webp, + {0x52, 0x49, 0x46}, +#ifdef RIVE_WEBP + DecodeWebP, +#else + nullptr, +#endif + }, +}; + +const Bitmap::ImageFormat* Bitmap::RecognizeImageFormat(const uint8_t bytes[], + size_t byteCount) +{ + for (const ImageFormat& format : _formats) + { + auto& fingerprint = format.fingerprint; + + // Immediately discard decoders with fingerprints that are longer than + // the file buffer. + if (format.fingerprint.size() > byteCount) + { + continue; + } + + // If the fingerprint doesn't match, discrd this decoder. These are all + // bytes so .size() is fine here. + if (memcmp(fingerprint.data(), bytes, fingerprint.size()) != 0) + { + continue; + } + + return &format; + } + return nullptr; +} + +std::unique_ptr Bitmap::decode(const uint8_t bytes[], size_t byteCount) +{ + const ImageFormat* format = RecognizeImageFormat(bytes, byteCount); + if (format != nullptr) + { + auto bitmap = format->decodeImage != nullptr + ? format->decodeImage(bytes, byteCount) + : nullptr; + if (!bitmap) + { + fprintf(stderr, + "Bitmap::decode - failed to decode a %s.\n", + format->name); + } + return bitmap; + } + return nullptr; +} diff --git a/thirdparty/rive_decoders/source/decode_jpeg.cpp b/thirdparty/rive_decoders/source/decode_jpeg.cpp new file mode 100644 index 00000000..22f7640e --- /dev/null +++ b/thirdparty/rive_decoders/source/decode_jpeg.cpp @@ -0,0 +1,131 @@ +// Adapted from libjpeg-turbo's example: +// https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/example.c +#include "rive/decoders/bitmap_decoder.hpp" + +#include "jpeglib.h" +#include "jerror.h" + +#include +#include +#include +#include + +struct my_error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +typedef struct my_error_mgr* my_error_ptr; + +void my_error_exit(j_common_ptr cinfo) +{ + // cinfo.err really points to a my_error_mgr struct, so coerce pointer + my_error_ptr myerr = (my_error_ptr)cinfo->err; + + // Always display the message. + // We could postpone this until after returning, if we chose. + (*cinfo->err->output_message)(cinfo); + + // Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); +} + +std::unique_ptr DecodeJpeg(const uint8_t bytes[], size_t byteCount) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; + + JSAMPARRAY buffer = nullptr; + std::unique_ptr pixelBuffer; + int row_stride; + + // Step 1: allocate and initialize JPEG decompression object. + + // We set up the normal JPEG error routines, then override error_exit. + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + // Establish the setjmp return context for my_error_exit to use. + if (setjmp(jerr.setjmp_buffer)) + { + // If we get here, the JPEG code has signaled an error. + // We need to clean up the JPEG object, close the input file, and + // return. + jpeg_destroy_decompress(&cinfo); + return nullptr; + } + + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + // Step 2: specify data source + jpeg_mem_src(&cinfo, bytes, byteCount); + + // Step 3: read file parameters with jpeg_read_header() + + jpeg_read_header(&cinfo, TRUE); + + // Step 4: set parameters for decompression + + // always want 8 bit RGB + cinfo.data_precision = 8; + cinfo.out_color_space = JCS_RGB; + + // Step 5: Start decompressor + jpeg_start_decompress(&cinfo); + + /// Api worked as expected and gave us correct format even for jpeg 12 or 16 + assert(cinfo.data_precision == 8); + assert(cinfo.output_components == 3); + + size_t pixelBufferSize = static_cast(cinfo.output_width) * + static_cast(cinfo.output_height) * + static_cast(cinfo.output_components); + pixelBuffer = std::make_unique(pixelBufferSize); + + uint8_t* pixelWriteBuffer = (uint8_t*)pixelBuffer.get(); + const uint8_t* pixelWriteBufferEnd = pixelWriteBuffer + pixelBufferSize; + + // Samples per row in output buffer + row_stride = cinfo.output_width * cinfo.output_components; + // Make a one-row-high sample array that will go away when done with image + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, + JPOOL_IMAGE, + row_stride, + 1); + + // Step 6: while (scan lines remain to be read) + // jpeg_read_scanlines(...); + + // Here we use the library's state variable cinfo->output_scanline as the + // loop counter, so that we don't have to keep track ourselves. + // + while (cinfo.output_scanline < cinfo.output_height) + { + // jpeg_read_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could ask for + // more than one scanline at a time if that's more convenient. + jpeg_read_scanlines(&cinfo, buffer, 1); + + if (pixelWriteBuffer + row_stride > pixelWriteBufferEnd) + { + // memcpy would cause an overflow. + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return nullptr; + } + memcpy(pixelWriteBuffer, buffer[0], row_stride); + pixelWriteBuffer += row_stride; + } + + // Step 7: Finish decompression + jpeg_finish_decompress(&cinfo); + + // Step 8: Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); + + return std::make_unique(cinfo.output_width, + cinfo.output_height, + Bitmap::PixelFormat::RGB, + std::move(pixelBuffer)); +} \ No newline at end of file diff --git a/thirdparty/rive_decoders/source/decode_png.cpp b/thirdparty/rive_decoders/source/decode_png.cpp new file mode 100644 index 00000000..75333c24 --- /dev/null +++ b/thirdparty/rive_decoders/source/decode_png.cpp @@ -0,0 +1,160 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/decoders/bitmap_decoder.hpp" +#include "png.h" +#include +#include +#include +#include + +struct EncodedImageBuffer +{ + const uint8_t* bytes; + size_t position; + size_t size; +}; + +static void ReadDataFromMemory(png_structp png_ptr, + png_bytep outBytes, + png_size_t byteCountToRead) +{ + png_voidp a = png_get_io_ptr(png_ptr); + if (a == nullptr) + { + return; + } + EncodedImageBuffer& stream = *(EncodedImageBuffer*)a; + + size_t bytesRead = + std::min(byteCountToRead, (stream.size - stream.position)); + memcpy(outBytes, stream.bytes + stream.position, bytesRead); + stream.position += bytesRead; + + if ((png_size_t)bytesRead != byteCountToRead) + { + // Report image error? + } +} + +std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount) +{ + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + std::unique_ptr pixelBuffer; + std::unique_ptr rowsPointer; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + nullptr, + nullptr, + nullptr); + + if (png_ptr == nullptr) + { + printf("DecodePng - libpng failed (png_create_read_struct)."); + return nullptr; + } + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + printf("DecodePng - libpng failed (png_create_info_struct)."); + return nullptr; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return nullptr; + } + + EncodedImageBuffer stream = {bytes, 0, byteCount}; + + png_set_read_fn(png_ptr, &stream, ReadDataFromMemory); + + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, + info_ptr, + &width, + &height, + &bit_depth, + &color_type, + &interlace_type, + NULL, + NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE || + (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) + { + // expand to 3 or 4 channels + png_set_expand(png_ptr); + } + + png_bytep trns = 0; + int trnsCount = 0; + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + png_get_tRNS(png_ptr, info_ptr, &trns, &trnsCount, 0); + png_set_expand(png_ptr); + } + + if (bit_depth == 16) + { + png_set_strip_16(png_ptr); + } + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(png_ptr); + } + + png_read_update_info(png_ptr, info_ptr); + uint8_t channels = png_get_channels(png_ptr, info_ptr); + + size_t pixelBufferSize = static_cast(width) * + static_cast(height) * + static_cast(channels); + pixelBuffer = std::make_unique(pixelBufferSize); + const uint8_t* pixelBufferEnd = pixelBuffer.get() + pixelBufferSize; + + rowsPointer = std::make_unique(height); + png_bytep* rows = rowsPointer.get(); + size_t rowStride = (size_t)width * (size_t)channels; + uint8_t* rowWrite = pixelBuffer.get(); + for (png_uint_32 row = 0; row < height; row++) + { + rows[row] = rowWrite; + rowWrite += rowStride; + if (rowWrite > pixelBufferEnd) + { + // Read would overflow. + return nullptr; + } + } + png_read_image(png_ptr, rows); + png_read_end(png_ptr, info_ptr); + + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); + + Bitmap::PixelFormat pixelFormat; + assert(channels == 3 || channels == 4); + switch (channels) + { + case 4: + pixelFormat = Bitmap::PixelFormat::RGBA; + break; + case 3: + pixelFormat = Bitmap::PixelFormat::RGB; + break; + } + return std::make_unique(width, + height, + pixelFormat, + std::move(pixelBuffer)); +} diff --git a/thirdparty/rive_decoders/source/decode_webp.cpp b/thirdparty/rive_decoders/source/decode_webp.cpp new file mode 100644 index 00000000..5d6cca24 --- /dev/null +++ b/thirdparty/rive_decoders/source/decode_webp.cpp @@ -0,0 +1,72 @@ +#include "rive/decoders/bitmap_decoder.hpp" +#include "webp/decode.h" +#include "webp/demux.h" +#include +#include +#include + +std::unique_ptr DecodeWebP(const uint8_t bytes[], size_t byteCount) +{ + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) + { + fprintf(stderr, "DecodeWebP - Library version mismatch!\n"); + return nullptr; + } + config.options.dithering_strength = 50; + config.options.alpha_dithering_strength = 100; + + if (!WebPGetInfo(bytes, byteCount, nullptr, nullptr)) + { + fprintf(stderr, + "DecodeWebP - Input file doesn't appear to be WebP format.\n"); + } + + WebPData data = {bytes, byteCount}; + WebPDemuxer* demuxer = WebPDemux(&data); + if (demuxer == nullptr) + { + fprintf(stderr, "DecodeWebP - Could not create demuxer.\n"); + } + + WebPIterator currentFrame; + if (!WebPDemuxGetFrame(demuxer, 1, ¤tFrame)) + { + fprintf(stderr, "DecodeWebP - WebPDemuxGetFrame couldn't get frame.\n"); + WebPDemuxDelete(demuxer); + return nullptr; + } + config.output.colorspace = MODE_RGBA; + + uint32_t width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); + uint32_t height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); + + size_t pixelBufferSize = static_cast(width) * + static_cast(height) * + static_cast(4); + std::unique_ptr pixelBuffer = + std::make_unique(pixelBufferSize); + + config.output.u.RGBA.rgba = (uint8_t*)pixelBuffer.get(); + config.output.u.RGBA.stride = (int)(width * 4); + config.output.u.RGBA.size = pixelBufferSize; + config.output.is_external_memory = 1; + + if (WebPDecode(currentFrame.fragment.bytes, + currentFrame.fragment.size, + &config) != VP8_STATUS_OK) + { + fprintf(stderr, "DecodeWebP - WebPDemuxGetFrame couldn't decode.\n"); + WebPDemuxReleaseIterator(¤tFrame); + WebPDemuxDelete(demuxer); + return nullptr; + } + + WebPDemuxReleaseIterator(¤tFrame); + WebPDemuxDelete(demuxer); + + return std::make_unique(width, + height, + Bitmap::PixelFormat::RGBA, + std::move(pixelBuffer)); +} \ No newline at end of file diff --git a/thirdparty/rive_renderer/rive_renderer.h b/thirdparty/rive_renderer/rive_renderer.h index 3a6b752a..3dbf06c6 100644 --- a/thirdparty/rive_renderer/rive_renderer.h +++ b/thirdparty/rive_renderer/rive_renderer.h @@ -32,10 +32,10 @@ website: https://github.com/rive-app/rive-runtime license: MIT - dependencies: rive glad + dependencies: rive rive_decoders glad searchpaths: include source osxFrameworks: Metal QuartzCore - defines: WITH_RIVE_TEXT=1 + defines: WITH_RIVE_TEXT=1 RIVE_DECODERS=1 iosDefines: RIVE_IOS=1 iosSimDefines: RIVE_IOS_SIMULATOR=1 linuxDefines: RIVE_DESKTOP_GL=1