mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 13:11:19 +01:00
refactor(tvos): fallback to libwebp if cg fails (#9534) d3b61cf628
Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
@@ -1 +1 @@
|
||||
6af68fed8b3491cf91d7d7b34aab79f9f4249a86
|
||||
d3b61cf628cacea0331b433761e5601884315479
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user