mirror of
https://github.com/fergalmoran/ladybird.git
synced 2025-12-22 09:19:03 +00:00
LibGfx: Decode AVIF images
Use libavif to decode AVIF images in LibGfx.
This commit is contained in:
committed by
Andreas Kling
parent
4ed46adeee
commit
4ef76f3198
4
.github/actions/setup/action.yml
vendored
4
.github/actions/setup/action.yml
vendored
@@ -26,7 +26,7 @@ runs:
|
|||||||
|
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install autoconf autoconf-archive automake build-essential cmake libavcodec-dev fonts-liberation2 zip curl tar ccache clang-18 clang++-18 lld-18 gcc-13 g++-13 libstdc++-13-dev \
|
sudo apt-get install autoconf autoconf-archive automake build-essential cmake libavcodec-dev fonts-liberation2 zip curl tar ccache clang-18 clang++-18 lld-18 gcc-13 g++-13 libstdc++-13-dev \
|
||||||
ninja-build unzip qt6-base-dev qt6-tools-dev-tools libqt6svg6-dev qt6-multimedia-dev libgl1-mesa-dev libpulse-dev libssl-dev libegl1-mesa-dev
|
ninja-build unzip qt6-base-dev qt6-tools-dev-tools libqt6svg6-dev qt6-multimedia-dev libgl1-mesa-dev libpulse-dev libssl-dev libegl1-mesa-dev nasm
|
||||||
|
|
||||||
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100
|
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100
|
||||||
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100
|
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100
|
||||||
@@ -52,7 +52,7 @@ runs:
|
|||||||
set -e
|
set -e
|
||||||
sudo xcode-select --switch /Applications/Xcode_15.4.app
|
sudo xcode-select --switch /Applications/Xcode_15.4.app
|
||||||
brew update
|
brew update
|
||||||
brew install autoconf autoconf-archive automake coreutils bash ffmpeg ninja wabt ccache unzip qt llvm@18
|
brew install autoconf autoconf-archive automake coreutils bash ffmpeg ninja wabt ccache unzip qt llvm@18 nasm
|
||||||
|
|
||||||
- name: 'Install vcpkg'
|
- name: 'Install vcpkg'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <AK/ByteString.h>
|
#include <AK/ByteString.h>
|
||||||
#include <LibCore/MappedFile.h>
|
#include <LibCore/MappedFile.h>
|
||||||
|
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||||
#include <LibGfx/ImageFormats/ICOLoader.h>
|
#include <LibGfx/ImageFormats/ICOLoader.h>
|
||||||
@@ -1007,3 +1008,64 @@ TEST_CASE(test_jxl_modular_property_8)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_avif_simple_lossy)
|
||||||
|
{
|
||||||
|
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("avif/simple-lossy.avif"sv)));
|
||||||
|
EXPECT(Gfx::AVIFImageDecoderPlugin::sniff(file->bytes()));
|
||||||
|
auto plugin_decoder = TRY_OR_FAIL(Gfx::AVIFImageDecoderPlugin::create(file->bytes()));
|
||||||
|
|
||||||
|
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 240, 240 }));
|
||||||
|
|
||||||
|
// While AVIF YUV contents are defined bit-exact, the YUV->RGB conversion isn't.
|
||||||
|
// So pixels changing by 1 or so below is fine if you change code.
|
||||||
|
EXPECT_EQ(frame.image->get_pixel(120, 232), Gfx::Color(0xf1, 0xef, 0xf0, 255));
|
||||||
|
EXPECT_EQ(frame.image->get_pixel(198, 202), Gfx::Color(0x7b, 0xaa, 0xd6, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_avif_simple_lossless)
|
||||||
|
{
|
||||||
|
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("avif/simple-lossless.avif"sv)));
|
||||||
|
EXPECT(Gfx::AVIFImageDecoderPlugin::sniff(file->bytes()));
|
||||||
|
auto plugin_decoder = TRY_OR_FAIL(Gfx::AVIFImageDecoderPlugin::create(file->bytes()));
|
||||||
|
|
||||||
|
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 386, 395 }));
|
||||||
|
EXPECT_EQ(frame.image->get_pixel(0, 0), Gfx::Color(0, 0, 0, 0));
|
||||||
|
EXPECT_EQ(frame.image->get_pixel(289, 332), Gfx::Color(0xf2, 0xee, 0xd3, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_avif_simple_lossy_bitdepth10)
|
||||||
|
{
|
||||||
|
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("avif/simple-bitdepth10.avif"sv)));
|
||||||
|
EXPECT(!Gfx::AVIFImageDecoderPlugin::sniff(file->bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_avif_icc_profile)
|
||||||
|
{
|
||||||
|
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("avif/icc_profile.avif"sv)));
|
||||||
|
EXPECT(Gfx::AVIFImageDecoderPlugin::sniff(file->bytes()));
|
||||||
|
auto plugin_decoder = TRY_OR_FAIL(Gfx::AVIFImageDecoderPlugin::create(file->bytes()));
|
||||||
|
|
||||||
|
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 240, 240 }));
|
||||||
|
EXPECT(TRY_OR_FAIL(plugin_decoder->icc_data()).has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_avif_no_icc_profile)
|
||||||
|
{
|
||||||
|
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("avif/simple-lossy.avif"sv)));
|
||||||
|
EXPECT(Gfx::AVIFImageDecoderPlugin::sniff(file->bytes()));
|
||||||
|
auto plugin_decoder = TRY_OR_FAIL(Gfx::AVIFImageDecoderPlugin::create(file->bytes()));
|
||||||
|
|
||||||
|
auto frame = TRY_OR_FAIL(expect_single_frame_of_size(*plugin_decoder, { 240, 240 }));
|
||||||
|
EXPECT(!TRY_OR_FAIL(plugin_decoder->icc_data()).has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(test_avif_frame_out_of_bounds)
|
||||||
|
{
|
||||||
|
auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("avif/simple-lossy.avif"sv)));
|
||||||
|
EXPECT(Gfx::AVIFImageDecoderPlugin::sniff(file->bytes()));
|
||||||
|
auto plugin_decoder = TRY_OR_FAIL(Gfx::AVIFImageDecoderPlugin::create(file->bytes()));
|
||||||
|
|
||||||
|
auto frame1 = TRY_OR_FAIL(plugin_decoder->frame(0));
|
||||||
|
EXPECT(plugin_decoder->frame(1).is_error());
|
||||||
|
}
|
||||||
|
|||||||
BIN
Tests/LibGfx/test-inputs/avif/icc_profile.avif
Executable file
BIN
Tests/LibGfx/test-inputs/avif/icc_profile.avif
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
BIN
Tests/LibGfx/test-inputs/avif/simple-bitdepth10.avif
Normal file
BIN
Tests/LibGfx/test-inputs/avif/simple-bitdepth10.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Tests/LibGfx/test-inputs/avif/simple-lossless.avif
Normal file
BIN
Tests/LibGfx/test-inputs/avif/simple-lossless.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
BIN
Tests/LibGfx/test-inputs/avif/simple-lossy.avif
Normal file
BIN
Tests/LibGfx/test-inputs/avif/simple-lossy.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -52,6 +52,7 @@ set(SOURCES
|
|||||||
ImageFormats/WebPSharedLossless.cpp
|
ImageFormats/WebPSharedLossless.cpp
|
||||||
ImageFormats/WebPWriter.cpp
|
ImageFormats/WebPWriter.cpp
|
||||||
ImageFormats/WebPWriterLossless.cpp
|
ImageFormats/WebPWriterLossless.cpp
|
||||||
|
ImageFormats/AVIFLoader.cpp
|
||||||
ImmutableBitmap.cpp
|
ImmutableBitmap.cpp
|
||||||
MedianCut.cpp
|
MedianCut.cpp
|
||||||
Painter.cpp
|
Painter.cpp
|
||||||
@@ -96,5 +97,6 @@ find_package(PkgConfig)
|
|||||||
pkg_check_modules(WOFF2 REQUIRED IMPORTED_TARGET libwoff2dec)
|
pkg_check_modules(WOFF2 REQUIRED IMPORTED_TARGET libwoff2dec)
|
||||||
find_package(JPEG REQUIRED)
|
find_package(JPEG REQUIRED)
|
||||||
find_package(PNG REQUIRED)
|
find_package(PNG REQUIRED)
|
||||||
|
find_package(LIBAVIF REQUIRED)
|
||||||
|
|
||||||
target_link_libraries(LibGfx PRIVATE PkgConfig::WOFF2 JPEG::JPEG PNG::PNG)
|
target_link_libraries(LibGfx PRIVATE PkgConfig::WOFF2 JPEG::JPEG PNG::PNG avif)
|
||||||
|
|||||||
196
Userland/Libraries/LibGfx/ImageFormats/AVIFLoader.cpp
Normal file
196
Userland/Libraries/LibGfx/ImageFormats/AVIFLoader.cpp
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
|
||||||
|
* Copyright (c) 2024, doctortheemh <doctortheemh@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/ByteBuffer.h>
|
||||||
|
#include <AK/Error.h>
|
||||||
|
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||||
|
|
||||||
|
#include <avif/avif.h>
|
||||||
|
|
||||||
|
namespace Gfx {
|
||||||
|
|
||||||
|
class AVIFLoadingContext {
|
||||||
|
AK_MAKE_NONMOVABLE(AVIFLoadingContext);
|
||||||
|
AK_MAKE_NONCOPYABLE(AVIFLoadingContext);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum State {
|
||||||
|
NotDecoded = 0,
|
||||||
|
Error,
|
||||||
|
HeaderDecoded,
|
||||||
|
BitmapDecoded,
|
||||||
|
};
|
||||||
|
|
||||||
|
State state { State::NotDecoded };
|
||||||
|
ReadonlyBytes data;
|
||||||
|
|
||||||
|
avifDecoder* decoder { nullptr };
|
||||||
|
|
||||||
|
// image properties
|
||||||
|
Optional<IntSize> size;
|
||||||
|
bool has_alpha { false };
|
||||||
|
size_t image_count { 0 };
|
||||||
|
size_t repetition_count { 0 };
|
||||||
|
ByteBuffer icc_data;
|
||||||
|
|
||||||
|
Vector<ImageFrameDescriptor> frame_descriptors;
|
||||||
|
|
||||||
|
AVIFLoadingContext() = default;
|
||||||
|
~AVIFLoadingContext()
|
||||||
|
{
|
||||||
|
avifDecoderDestroy(decoder);
|
||||||
|
decoder = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AVIFImageDecoderPlugin::AVIFImageDecoderPlugin(ReadonlyBytes data, OwnPtr<AVIFLoadingContext> context)
|
||||||
|
: m_context(move(context))
|
||||||
|
{
|
||||||
|
m_context->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVIFImageDecoderPlugin::~AVIFImageDecoderPlugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErrorOr<void> decode_avif_header(AVIFLoadingContext& context)
|
||||||
|
{
|
||||||
|
if (context.state >= AVIFLoadingContext::HeaderDecoded)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (context.decoder == nullptr) {
|
||||||
|
context.decoder = avifDecoderCreate();
|
||||||
|
|
||||||
|
if (context.decoder == nullptr) {
|
||||||
|
return Error::from_string_literal("failed to allocate AVIF decoder");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
avifResult result = avifDecoderSetIOMemory(context.decoder, context.data.data(), context.data.size());
|
||||||
|
if (result != AVIF_RESULT_OK)
|
||||||
|
return Error::from_string_literal("Cannot set IO on avifDecoder");
|
||||||
|
|
||||||
|
result = avifDecoderParse(context.decoder);
|
||||||
|
if (result != AVIF_RESULT_OK)
|
||||||
|
return Error::from_string_literal("Failed to decode AVIF");
|
||||||
|
|
||||||
|
if (context.decoder->image->depth != 8)
|
||||||
|
return Error::from_string_literal("Unsupported bitdepth");
|
||||||
|
|
||||||
|
// Image header now decoded, save some results for fast access in other parts of the plugin.
|
||||||
|
context.size = IntSize { context.decoder->image->width, context.decoder->image->height };
|
||||||
|
context.has_alpha = context.decoder->alphaPresent == 1;
|
||||||
|
context.image_count = context.decoder->imageCount;
|
||||||
|
context.repetition_count = context.decoder->repetitionCount <= 0 ? 0 : context.decoder->repetitionCount;
|
||||||
|
context.state = AVIFLoadingContext::State::HeaderDecoded;
|
||||||
|
|
||||||
|
if (context.decoder->image->icc.size > 0) {
|
||||||
|
context.icc_data.resize(context.decoder->image->icc.size);
|
||||||
|
memcpy(context.icc_data.data(), context.decoder->image->icc.data, context.decoder->image->icc.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static ErrorOr<void> decode_avif_image(AVIFLoadingContext& context)
|
||||||
|
{
|
||||||
|
VERIFY(context.state >= AVIFLoadingContext::State::HeaderDecoded);
|
||||||
|
|
||||||
|
avifRGBImage rgb;
|
||||||
|
while (avifDecoderNextImage(context.decoder) == AVIF_RESULT_OK) {
|
||||||
|
auto bitmap_format = context.has_alpha ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888;
|
||||||
|
auto bitmap = TRY(Bitmap::create(bitmap_format, context.size.value()));
|
||||||
|
|
||||||
|
avifRGBImageSetDefaults(&rgb, context.decoder->image);
|
||||||
|
rgb.pixels = bitmap->scanline_u8(0);
|
||||||
|
rgb.rowBytes = bitmap->pitch();
|
||||||
|
rgb.format = avifRGBFormat::AVIF_RGB_FORMAT_BGRA;
|
||||||
|
|
||||||
|
avifResult result = avifImageYUVToRGB(context.decoder->image, &rgb);
|
||||||
|
if (result != AVIF_RESULT_OK)
|
||||||
|
return Error::from_string_literal("Conversion from YUV to RGB failed");
|
||||||
|
|
||||||
|
auto duration = context.decoder->imageCount == 1 ? 0 : static_cast<int>(context.decoder->imageTiming.duration * 1000);
|
||||||
|
context.frame_descriptors.append(ImageFrameDescriptor { bitmap, duration });
|
||||||
|
|
||||||
|
context.state = AVIFLoadingContext::BitmapDecoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
IntSize AVIFImageDecoderPlugin::size()
|
||||||
|
{
|
||||||
|
return m_context->size.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AVIFImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||||
|
{
|
||||||
|
AVIFLoadingContext context;
|
||||||
|
context.data = data;
|
||||||
|
return !decode_avif_header(context).is_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> AVIFImageDecoderPlugin::create(ReadonlyBytes data)
|
||||||
|
{
|
||||||
|
auto context = TRY(try_make<AVIFLoadingContext>());
|
||||||
|
auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AVIFImageDecoderPlugin(data, move(context))));
|
||||||
|
TRY(decode_avif_header(*plugin->m_context));
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AVIFImageDecoderPlugin::is_animated()
|
||||||
|
{
|
||||||
|
return m_context->image_count > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AVIFImageDecoderPlugin::loop_count()
|
||||||
|
{
|
||||||
|
return is_animated() ? m_context->repetition_count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AVIFImageDecoderPlugin::frame_count()
|
||||||
|
{
|
||||||
|
if (!is_animated())
|
||||||
|
return 1;
|
||||||
|
return m_context->image_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AVIFImageDecoderPlugin::first_animated_frame_index()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<ImageFrameDescriptor> AVIFImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
|
||||||
|
{
|
||||||
|
if (index >= frame_count())
|
||||||
|
return Error::from_string_literal("AVIFImageDecoderPlugin: Invalid frame index");
|
||||||
|
|
||||||
|
if (m_context->state == AVIFLoadingContext::State::Error)
|
||||||
|
return Error::from_string_literal("AVIFImageDecoderPlugin: Decoding failed");
|
||||||
|
|
||||||
|
if (m_context->state < AVIFLoadingContext::State::BitmapDecoded) {
|
||||||
|
TRY(decode_avif_image(*m_context));
|
||||||
|
m_context->state = AVIFLoadingContext::State::BitmapDecoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= m_context->frame_descriptors.size())
|
||||||
|
return Error::from_string_literal("AVIFImageDecoderPlugin: Invalid frame index");
|
||||||
|
return m_context->frame_descriptors[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Optional<ReadonlyBytes>> AVIFImageDecoderPlugin::icc_data()
|
||||||
|
{
|
||||||
|
if (m_context->state < AVIFLoadingContext::State::HeaderDecoded)
|
||||||
|
(void)frame(0);
|
||||||
|
|
||||||
|
if (!m_context->icc_data.is_empty())
|
||||||
|
return m_context->icc_data;
|
||||||
|
return OptionalNone {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
Userland/Libraries/LibGfx/ImageFormats/AVIFLoader.h
Normal file
38
Userland/Libraries/LibGfx/ImageFormats/AVIFLoader.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Nico Weber <thakis@chromium.org>
|
||||||
|
* Copyright (c) 2024, doctortheemh <doctortheemh@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||||
|
|
||||||
|
namespace Gfx {
|
||||||
|
|
||||||
|
class AVIFLoadingContext;
|
||||||
|
|
||||||
|
class AVIFImageDecoderPlugin final : public ImageDecoderPlugin {
|
||||||
|
public:
|
||||||
|
static bool sniff(ReadonlyBytes);
|
||||||
|
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||||
|
|
||||||
|
virtual ~AVIFImageDecoderPlugin() override;
|
||||||
|
|
||||||
|
virtual IntSize size() override;
|
||||||
|
|
||||||
|
virtual bool is_animated() override;
|
||||||
|
virtual size_t loop_count() override;
|
||||||
|
virtual size_t frame_count() override;
|
||||||
|
virtual size_t first_animated_frame_index() override;
|
||||||
|
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
|
||||||
|
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVIFImageDecoderPlugin(ReadonlyBytes, OwnPtr<AVIFLoadingContext>);
|
||||||
|
|
||||||
|
OwnPtr<AVIFLoadingContext> m_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
|
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||||
#include <LibGfx/ImageFormats/ICOLoader.h>
|
#include <LibGfx/ImageFormats/ICOLoader.h>
|
||||||
@@ -35,6 +36,7 @@ static ErrorOr<OwnPtr<ImageDecoderPlugin>> probe_and_sniff_for_appropriate_plugi
|
|||||||
{ TIFFImageDecoderPlugin::sniff, TIFFImageDecoderPlugin::create },
|
{ TIFFImageDecoderPlugin::sniff, TIFFImageDecoderPlugin::create },
|
||||||
{ TinyVGImageDecoderPlugin::sniff, TinyVGImageDecoderPlugin::create },
|
{ TinyVGImageDecoderPlugin::sniff, TinyVGImageDecoderPlugin::create },
|
||||||
{ WebPImageDecoderPlugin::sniff, WebPImageDecoderPlugin::create },
|
{ WebPImageDecoderPlugin::sniff, WebPImageDecoderPlugin::create },
|
||||||
|
{ AVIFImageDecoderPlugin::sniff, AVIFImageDecoderPlugin::create }
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& plugin : s_initializers) {
|
for (auto& plugin : s_initializers) {
|
||||||
|
|||||||
10
vcpkg.json
10
vcpkg.json
@@ -13,6 +13,12 @@
|
|||||||
"apng"
|
"apng"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "libavif",
|
||||||
|
"features": [
|
||||||
|
"dav1d"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "skia",
|
"name": "skia",
|
||||||
"platform": "osx",
|
"platform": "osx",
|
||||||
@@ -59,6 +65,10 @@
|
|||||||
"name": "libpng",
|
"name": "libpng",
|
||||||
"version": "1.6.43#1"
|
"version": "1.6.43#1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "libavif",
|
||||||
|
"version": "1.0.4#1"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "skia",
|
"name": "skia",
|
||||||
"version": "124#0"
|
"version": "124#0"
|
||||||
|
|||||||
Reference in New Issue
Block a user