refactor(tvos): fallback to libwebp if cg fails (#9534) d3b61cf628

Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
dskuza
2025-05-12 18:31:55 +00:00
parent eef564bd17
commit e72b1b12bc
4 changed files with 165 additions and 160 deletions

View File

@@ -1 +1 @@
6af68fed8b3491cf91d7d7b34aab79f9f4249a86
d3b61cf628cacea0331b433761e5601884315479

View File

@@ -16,6 +16,11 @@ newoption({
description = 'don\'t build jpeg support into the rive_decoders library (built-in jpeg decoding will fail)',
})
newoption({
trigger = 'no_rive_webp',
description = 'don\'t build webp support into the rive_decoders library (built-in webp decoding will fail)',
})
if not _OPTIONS["no_rive_png"] then
dofile(rive .. '/dependencies/premake5_libpng_v2.lua')
end
@@ -24,6 +29,10 @@ if not _OPTIONS["no_rive_jpeg"] then
dofile(rive .. '/dependencies/premake5_libjpeg_v2.lua')
end
if not _OPTIONS["no_rive_webp"] then
dofile(rive .. '/dependencies/premake5_libwebp_v2.lua')
end
dofile(rive .. '/dependencies/premake5_libwebp_v2.lua')
project('rive_decoders')
@@ -35,11 +44,13 @@ do
includedirs({
'include',
'../include',
libwebp .. '/src',
'%{cfg.targetdir}/include/libpng',
})
files({ 'src/bitmap_decoder.cpp' })
files({
'src/bitmap_decoder.cpp',
'src/bitmap_decoder_thirdparty.cpp',
})
filter({ 'options:not no-libjpeg-renames' })
do
@@ -49,38 +60,14 @@ do
forceincludes({ 'rive_libjpeg_renames.h' })
end
filter({
'system:macosx or system:ios',
'not options:variant=appletvos',
'not options:variant=appletvsimulator',
})
do
files({ 'src/**.mm' })
end
filter({ 'system:ios', 'options:variant=appletvos or options:variant=appletvsimulator' })
do
files({
'src/bitmap_decoder_thirdparty.cpp',
'src/decode_webp.cpp',
'src/decode_jpeg.cpp',
'src/decode_png.cpp',
})
end
filter({ 'system:not macosx', 'system:not ios' })
do
files({
'src/bitmap_decoder_thirdparty.cpp',
'src/decode_webp.cpp',
})
end
filter({'options:not no_rive_png'})
do
includedirs({
libpng
})
dependson('zlib', 'libpng')
defines({ 'RIVE_PNG' })
files({ 'src/decode_png.cpp' })
end
filter({'options:not no_rive_jpeg'})
@@ -88,19 +75,18 @@ do
includedirs({
libjpeg
})
end
filter({ 'system:not macosx', 'system:not ios', 'options:not no_rive_png' })
do
dependson('zlib', 'libpng')
defines({ 'RIVE_PNG' })
files({ 'src/decode_png.cpp' })
end
filter({ 'system:not macosx', 'system:not ios', 'options:not no_rive_jpeg' })
do
dependson('libjpeg')
defines({ 'RIVE_JPEG' })
files({ 'src/decode_jpeg.cpp' })
end
filter({'options:not no_rive_webp'})
do
includedirs({
libwebp .. '/src'
})
dependson('libwebp')
defines({ 'RIVE_WEBP' })
files({ 'src/decode_webp.cpp' })
end
end

View File

@@ -1,119 +0,0 @@
/*
* Copyright 2023 Rive
*/
#include "rive/decoders/bitmap_decoder.hpp"
#include "rive/rive_types.hpp"
#include "rive/math/math_types.hpp"
#include "rive/core/type_conversions.hpp"
#include "utils/auto_cf.hpp"
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include <CoreGraphics/CoreGraphics.h>
#include <ImageIO/ImageIO.h>
#elif TARGET_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
#endif
#include <stdio.h>
#include <string.h>
#include <vector>
// 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<uint8_t[]> 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<uint8_t[]> 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<uint32_t>(width);
platformImage->height = rive::castTo<uint32_t>(height);
platformImage->opaque = isOpaque;
platformImage->pixels = std::move(pixels);
return true;
}
std::unique_ptr<Bitmap> Bitmap::decode(const uint8_t bytes[], size_t byteCount)
{
PlatformCGImage image;
if (!cg_image_decode(bytes, byteCount, &image))
{
return nullptr;
}
return std::make_unique<Bitmap>(image.width,
image.height,
// CG always premultiplies alpha.
PixelFormat::RGBAPremul,
std::move(image.pixels));
}

View File

@@ -3,6 +3,23 @@
*/
#include "rive/decoders/bitmap_decoder.hpp"
#ifdef __APPLE__
#include "rive/rive_types.hpp"
#include "rive/math/math_types.hpp"
#include "rive/core/type_conversions.hpp"
#include "utils/auto_cf.hpp"
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include <CoreGraphics/CoreGraphics.h>
#include <ImageIO/ImageIO.h>
#elif TARGET_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
#endif
#endif
#include <stdio.h>
#include <string.h>
#include <vector>
@@ -13,7 +30,9 @@ std::unique_ptr<Bitmap> DecodePng(const uint8_t bytes[], size_t byteCount);
#ifdef RIVE_JPEG
std::unique_ptr<Bitmap> DecodeJpeg(const uint8_t bytes[], size_t byteCount);
#endif
#ifdef RIVE_WEBP
std::unique_ptr<Bitmap> DecodeWebP(const uint8_t bytes[], size_t byteCount);
#endif
static Bitmap::ImageFormat _formats[] = {
{
@@ -40,7 +59,11 @@ static Bitmap::ImageFormat _formats[] = {
"webp",
Bitmap::ImageType::webp,
{0x52, 0x49, 0x46},
#ifdef RIVE_WEBP
DecodeWebP,
#else
nullptr,
#endif
},
};
@@ -70,6 +93,120 @@ const Bitmap::ImageFormat* Bitmap::RecognizeImageFormat(const uint8_t bytes[],
return nullptr;
}
#ifdef __APPLE__
// 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<uint8_t[]> 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<uint8_t[]> 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<uint32_t>(width);
platformImage->height = rive::castTo<uint32_t>(height);
platformImage->opaque = isOpaque;
platformImage->pixels = std::move(pixels);
return true;
}
std::unique_ptr<Bitmap> Bitmap::decode(const uint8_t bytes[], size_t byteCount)
{
PlatformCGImage image;
if (!cg_image_decode(bytes, byteCount, &image))
{
#if defined RIVE_APPLETVOS || defined RIVE_APPLETVOS_SIMULATOR
const Bitmap::ImageFormat* format =
Bitmap::RecognizeImageFormat(bytes, byteCount);
if (format != nullptr)
{
auto bitmap = format->decodeImage != nullptr
? format->decodeImage(bytes, byteCount)
: nullptr;
return bitmap;
}
#endif
return nullptr;
}
return std::make_unique<Bitmap>(image.width,
image.height,
// CG always premultiplies alpha.
PixelFormat::RGBAPremul,
std::move(image.pixels));
}
#else
std::unique_ptr<Bitmap> Bitmap::decode(const uint8_t bytes[], size_t byteCount)
{
const ImageFormat* format = RecognizeImageFormat(bytes, byteCount);
@@ -88,3 +225,4 @@ std::unique_ptr<Bitmap> Bitmap::decode(const uint8_t bytes[], size_t byteCount)
}
return nullptr;
}
#endif