Add MVP implementation of JPEGXL decoder.
Change-Id: I90140348eeb87c849a857a12008c201efc9e328d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/482596 Reviewed-by: Leon Scroggins <scroggo@google.com> Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
d161e2f6ec
commit
0dae2e870d
9
BUILD.gn
9
BUILD.gn
@ -1054,6 +1054,14 @@ optional("jpeg_encode") {
|
||||
]
|
||||
}
|
||||
|
||||
optional("jpegxl_decode") {
|
||||
enabled = skia_use_libjxl_decode
|
||||
public_defines = [ "SK_CODEC_DECODES_JPEGXL" ]
|
||||
|
||||
deps = [ "//third_party/libjxl" ]
|
||||
sources = [ "src/codec/SkJpegxlCodec.cpp" ]
|
||||
}
|
||||
|
||||
optional("ndk_images") {
|
||||
enabled = skia_use_ndk_images
|
||||
public_defines = [ "SK_ENABLE_NDK_IMAGES" ]
|
||||
@ -1285,6 +1293,7 @@ skia_component("skia") {
|
||||
":hsw",
|
||||
":jpeg_decode",
|
||||
":jpeg_encode",
|
||||
":jpegxl_decode",
|
||||
":ndk_images",
|
||||
":none",
|
||||
":png_decode",
|
||||
|
2
DEPS
2
DEPS
@ -26,10 +26,12 @@ deps = {
|
||||
"third_party/externals/expat" : "https://chromium.googlesource.com/external/github.com/libexpat/libexpat.git@a28238bdeebc087071777001245df1876a11f5ee",
|
||||
"third_party/externals/freetype" : "https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@5e9caaee7885cbc82f9f821bbec7f6c86f25b33a",
|
||||
"third_party/externals/harfbuzz" : "https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@a8b7f1880412c7f0c9ecdada0a4935011816c7dc",
|
||||
"third_party/externals/highway" : "https://chromium.googlesource.com/external/github.com/google/highway.git@424360251cdcfc314cfc528f53c872ecd63af0f0",
|
||||
"third_party/externals/icu" : "https://chromium.googlesource.com/chromium/deps/icu.git@a0718d4f121727e30b8d52c7a189ebf5ab52421f",
|
||||
"third_party/externals/imgui" : "https://skia.googlesource.com/external/github.com/ocornut/imgui.git@55d35d8387c15bf0cfd71861df67af8cfbda7456",
|
||||
"third_party/externals/libgifcodec" : "https://skia.googlesource.com/libgifcodec@fd59fa92a0c86788dcdd84d091e1ce81eda06a77",
|
||||
"third_party/externals/libjpeg-turbo" : "https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@24e310554f07c0fdb8ee52e3e708e4f3e9eb6e20",
|
||||
"third_party/externals/libjxl" : "https://chromium.googlesource.com/external/gitlab.com/wg1/jpeg-xl.git@a205468bc5d3a353fb15dae2398a101dff52f2d3",
|
||||
"third_party/externals/libpng" : "https://skia.googlesource.com/third_party/libpng.git@386707c6d19b974ca2e3db7f5c61873813c6fe44",
|
||||
"third_party/externals/libwebp" : "https://chromium.googlesource.com/webm/libwebp.git@69c7f16111ec582bf1e7cb4d0d4f8d127e28a715",
|
||||
"third_party/externals/microhttpd" : "https://android.googlesource.com/platform/external/libmicrohttpd@748945ec6f1c67b7efc934ab0808e1d32f2fb98d",
|
||||
|
@ -58,6 +58,7 @@ Milestone 98
|
||||
will return nullptr.
|
||||
* Removed SkCanvas::markCTM and SkCanvas::findMarkedCTM. These were created to be used with other
|
||||
features that have since been deleted, so they served no purpose.
|
||||
* Added limited JPEGXL support.
|
||||
|
||||
* * *
|
||||
|
||||
|
@ -54,6 +54,7 @@ declare_args() {
|
||||
skia_use_libheif = is_skia_dev_build
|
||||
skia_use_libjpeg_turbo_decode = true
|
||||
skia_use_libjpeg_turbo_encode = true
|
||||
skia_use_libjxl_decode = false
|
||||
skia_use_libpng_decode = true
|
||||
skia_use_libpng_encode = true
|
||||
skia_use_libwebp_decode = true
|
||||
|
@ -30,6 +30,7 @@ enum class SkEncodedImageFormat {
|
||||
kDNG,
|
||||
kHEIF,
|
||||
kAVIF,
|
||||
kJPEGXL,
|
||||
};
|
||||
|
||||
#endif // SkEncodedImageFormat_DEFINED
|
||||
|
@ -31,6 +31,10 @@
|
||||
#include "src/codec/SkJpegCodec.h"
|
||||
#endif
|
||||
|
||||
#ifdef SK_CODEC_DECODES_JPEGXL
|
||||
#include "src/codec/SkJpegxlCodec.h"
|
||||
#endif
|
||||
|
||||
#ifdef SK_CODEC_DECODES_PNG
|
||||
#include "src/codec/SkIcoCodec.h"
|
||||
#include "src/codec/SkPngCodec.h"
|
||||
@ -73,6 +77,9 @@ static std::vector<DecoderProc>* decoders() {
|
||||
#endif
|
||||
{ SkBmpCodec::IsBmp, SkBmpCodec::MakeFromStream },
|
||||
{ SkWbmpCodec::IsWbmp, SkWbmpCodec::MakeFromStream },
|
||||
#ifdef SK_CODEC_DECODES_JPEGXL
|
||||
{ SkJpegxlCodec::IsJpegxl, SkJpegxlCodec::MakeFromStream },
|
||||
#endif
|
||||
};
|
||||
return decoders;
|
||||
}
|
||||
|
453
src/codec/SkJpegxlCodec.cpp
Normal file
453
src/codec/SkJpegxlCodec.cpp
Normal file
@ -0,0 +1,453 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "src/codec/SkJpegxlCodec.h"
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "include/codec/SkEncodedOrigin.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkEncodedImageFormat.h"
|
||||
#include "include/core/SkImageInfo.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/private/SkEncodedInfo.h"
|
||||
#include "include/private/SkTFitsIn.h"
|
||||
#include "include/private/SkTo.h"
|
||||
#include "jxl/decode.h"
|
||||
#include "jxl/decode_cxx.h"
|
||||
#include "src/codec/SkFrameHolder.h"
|
||||
#include "src/codec/SkSampler.h"
|
||||
#include "src/core/SkOpts.h"
|
||||
#include "src/core/SkStreamPriv.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class Frame : public SkFrame {
|
||||
public:
|
||||
explicit Frame(int i, SkEncodedInfo::Alpha alpha) : INHERITED(i), fReportedAlpha(alpha) {}
|
||||
SkEncodedInfo::Alpha onReportedAlpha() const override { return fReportedAlpha; }
|
||||
|
||||
private:
|
||||
const SkEncodedInfo::Alpha fReportedAlpha;
|
||||
|
||||
using INHERITED = SkFrame;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool SkJpegxlCodec::IsJpegxl(const void* buffer, size_t bytesRead) {
|
||||
JxlSignature result = JxlSignatureCheck(reinterpret_cast<const uint8_t*>(buffer), bytesRead);
|
||||
return (result == JXL_SIG_CODESTREAM) || (result == JXL_SIG_CONTAINER);
|
||||
}
|
||||
|
||||
class SkJpegxlCodecPriv : public SkFrameHolder {
|
||||
public:
|
||||
SkJpegxlCodecPriv() : fDecoder(JxlDecoderMake(/* memory_manager= */ nullptr)) {}
|
||||
JxlDecoderPtr fDecoder; // unique_ptr with custom destructor
|
||||
JxlBasicInfo fInfo;
|
||||
bool fSeenAllFrames = false;
|
||||
std::vector<Frame> fFrames;
|
||||
int fLastProcessedFrame = SkCodec::kNoFrame;
|
||||
void* fDst;
|
||||
size_t fPixelShift;
|
||||
size_t fRowBytes;
|
||||
SkColorType fDstColorType;
|
||||
|
||||
protected:
|
||||
const SkFrame* onGetFrame(int i) const override {
|
||||
SkASSERT(i >= 0 && static_cast<size_t>(i) < fFrames.size());
|
||||
return static_cast<const SkFrame*>(&fFrames[i]);
|
||||
}
|
||||
};
|
||||
|
||||
SkJpegxlCodec::SkJpegxlCodec(std::unique_ptr<SkJpegxlCodecPriv> codec,
|
||||
SkEncodedInfo&& info,
|
||||
std::unique_ptr<SkStream> stream,
|
||||
sk_sp<SkData> data)
|
||||
: INHERITED(std::move(info), skcms_PixelFormat_RGBA_16161616LE, std::move(stream))
|
||||
, fCodec(std::move(codec))
|
||||
, fData(std::move(data)) {}
|
||||
|
||||
std::unique_ptr<SkCodec> SkJpegxlCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
||||
Result* result) {
|
||||
*result = kInternalError;
|
||||
// Either wrap or copy stream data.
|
||||
sk_sp<SkData> data = nullptr;
|
||||
if (stream->getMemoryBase()) {
|
||||
data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength());
|
||||
} else {
|
||||
data = SkCopyStreamToData(stream.get());
|
||||
// Data is copied; stream can be released now.
|
||||
stream.reset(nullptr);
|
||||
}
|
||||
|
||||
auto priv = std::make_unique<SkJpegxlCodecPriv>();
|
||||
JxlDecoder* dec = priv->fDecoder.get();
|
||||
|
||||
// Only query metadata this time.
|
||||
auto status = JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Fresh instance must accept request for subscription.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = JxlDecoderSetInput(dec, data->bytes(), data->size());
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Fresh instance must accept first chunk of input.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = JxlDecoderProcessInput(dec);
|
||||
if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
*result = kIncompleteInput;
|
||||
return nullptr;
|
||||
}
|
||||
if (status != JXL_DEC_BASIC_INFO) {
|
||||
*result = kInvalidInput;
|
||||
return nullptr;
|
||||
}
|
||||
JxlBasicInfo& info = priv->fInfo;
|
||||
status = JxlDecoderGetBasicInfo(dec, &info);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Current event is "JXL_DEC_BASIC_INFO" -> can't fail.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check that image dimensions are not too large.
|
||||
if (!SkTFitsIn<int32_t>(info.xsize) || !SkTFitsIn<int32_t>(info.ysize)) {
|
||||
*result = kInvalidInput;
|
||||
return nullptr;
|
||||
}
|
||||
int32_t width = SkTo<int32_t>(info.xsize);
|
||||
int32_t height = SkTo<int32_t>(info.ysize);
|
||||
|
||||
bool hasAlpha = (info.alpha_bits != 0);
|
||||
bool isGray = (info.num_color_channels == 1);
|
||||
SkEncodedInfo::Alpha alpha =
|
||||
hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha;
|
||||
SkEncodedInfo::Color color;
|
||||
if (hasAlpha) {
|
||||
color = isGray ? SkEncodedInfo::kGrayAlpha_Color : SkEncodedInfo::kRGBA_Color;
|
||||
} else {
|
||||
color = isGray ? SkEncodedInfo::kGray_Color : SkEncodedInfo::kRGB_Color;
|
||||
}
|
||||
|
||||
status = JxlDecoderProcessInput(dec);
|
||||
if (status != JXL_DEC_COLOR_ENCODING) {
|
||||
*result = kInvalidInput;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t iccSize = 0;
|
||||
// TODO(eustas): format field is currently ignored by decoder.
|
||||
status = JxlDecoderGetICCProfileSize(
|
||||
dec, /* format = */ nullptr, JXL_COLOR_PROFILE_TARGET_DATA, &iccSize);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Likely incompatible colorspace.
|
||||
iccSize = 0;
|
||||
}
|
||||
std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
|
||||
if (iccSize) {
|
||||
auto icc = SkData::MakeUninitialized(iccSize);
|
||||
// TODO(eustas): format field is currently ignored by decoder.
|
||||
status = JxlDecoderGetColorAsICCProfile(dec,
|
||||
/* format = */ nullptr,
|
||||
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||
reinterpret_cast<uint8_t*>(icc->writable_data()),
|
||||
iccSize);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Current event is JXL_DEC_COLOR_ENCODING -> can't fail.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return nullptr;
|
||||
}
|
||||
profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
|
||||
}
|
||||
|
||||
int bitsPerChannel = 16;
|
||||
|
||||
*result = kSuccess;
|
||||
SkEncodedInfo encodedInfo =
|
||||
SkEncodedInfo::Make(width, height, color, alpha, bitsPerChannel, std::move(profile));
|
||||
|
||||
return std::unique_ptr<SkCodec>(new SkJpegxlCodec(
|
||||
std::move(priv), std::move(encodedInfo), std::move(stream), std::move(data)));
|
||||
}
|
||||
|
||||
SkCodec::Result SkJpegxlCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
|
||||
const Options& options, int* rowsDecodedPtr) {
|
||||
// TODO(eustas): implement
|
||||
if (options.fSubset) {
|
||||
return kUnimplemented;
|
||||
}
|
||||
auto& codec = *fCodec.get();
|
||||
const int index = options.fFrameIndex;
|
||||
SkASSERT(0 == index || static_cast<size_t>(index) < codec.fFrames.size());
|
||||
auto* dec = codec.fDecoder.get();
|
||||
JxlDecoderStatus status;
|
||||
|
||||
if ((codec.fLastProcessedFrame >= index) || (codec.fLastProcessedFrame = SkCodec::kNoFrame)) {
|
||||
codec.fLastProcessedFrame = SkCodec::kNoFrame;
|
||||
JxlDecoderRewind(dec);
|
||||
status = JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Fresh decoder instance (after rewind) must accept subscription request.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return kInternalError;
|
||||
}
|
||||
status = JxlDecoderSetInput(dec, fData->bytes(), fData->size());
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Fresh decoder instance (after rewind) must accept first data chunk.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return kInternalError;
|
||||
}
|
||||
SkASSERT(codec.fLastProcessedFrame + 1 == 0);
|
||||
}
|
||||
|
||||
int nextFrame = codec.fLastProcessedFrame + 1;
|
||||
if (nextFrame < index) {
|
||||
JxlDecoderSkipFrames(dec, index - nextFrame);
|
||||
}
|
||||
|
||||
// Decode till the frame start.
|
||||
status = JxlDecoderProcessInput(dec);
|
||||
// TODO(eustas): actually, frame is not completely processed; for streaming / partial decoding
|
||||
// we should also add a flag that "last processed frame" is still incomplete, and
|
||||
// flip that flag when frame decoding is over.
|
||||
codec.fLastProcessedFrame = index;
|
||||
if (status != JXL_DEC_FRAME) {
|
||||
// TODO(eustas): check status: it might be either corrupted or incomplete input.
|
||||
return kInternalError;
|
||||
}
|
||||
|
||||
codec.fDst = dst;
|
||||
codec.fRowBytes = rowBytes;
|
||||
|
||||
// TODO(eustas): consider grayscale.
|
||||
uint32_t numColorChannels = 3;
|
||||
// TODO(eustas): consider no-alpha.
|
||||
uint32_t numAlphaChannels = 1;
|
||||
// NB: SKIA works with little-endian F16s.
|
||||
auto endianness = JXL_LITTLE_ENDIAN;
|
||||
|
||||
// Internally JXL does most processing in floats. By "default" we request
|
||||
// output data type to be U8; it takes less memory, but results in some precision loss.
|
||||
// We request F16 in two cases:
|
||||
// - destination type is F16
|
||||
// - color transformation is required; in this case values are remapped,
|
||||
// and with 8-bit precision it is likely that visual artefact will appear
|
||||
// (like banding, etc.)
|
||||
bool halfFloatOutput = false;
|
||||
if (fCodec->fDstColorType == kRGBA_F16_SkColorType) halfFloatOutput = true;
|
||||
if (colorXform()) halfFloatOutput = true;
|
||||
auto dataType = halfFloatOutput ? JXL_TYPE_FLOAT16 : JXL_TYPE_UINT8;
|
||||
|
||||
JxlPixelFormat format =
|
||||
{numColorChannels + numAlphaChannels, dataType, endianness, /* align = */ 0};
|
||||
status = JxlDecoderSetImageOutCallback(dec, &format, SkJpegxlCodec::imageOutCallback, this);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Current event is JXL_DEC_FRAME -> decoder must accept callback.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return kInternalError;
|
||||
}
|
||||
|
||||
// Decode till the frame start.
|
||||
status = JxlDecoderProcessInput(dec);
|
||||
if (status != JXL_DEC_FULL_IMAGE) {
|
||||
// TODO(eustas): check status: it might be either corrupted or incomplete input.
|
||||
return kInternalError;
|
||||
}
|
||||
// TODO(eustas): currently it is supposed that complete input is accessible;
|
||||
// when streaming support is added JXL_DEC_NEED_MORE_INPUT would also
|
||||
// become a legal outcome; amount of decoded scanlines should be calculated
|
||||
// based on callback invocations / render-pipeline API.
|
||||
*rowsDecodedPtr = dstInfo.height();
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
bool SkJpegxlCodec::onRewind() {
|
||||
JxlDecoderRewind(fCodec->fDecoder.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkJpegxlCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque,
|
||||
bool needsColorXform) {
|
||||
fCodec->fDstColorType = dstInfo.colorType();
|
||||
switch (dstInfo.colorType()) {
|
||||
case kRGBA_8888_SkColorType:
|
||||
return true; // memcpy
|
||||
case kBGRA_8888_SkColorType:
|
||||
return true; // rgba->bgra
|
||||
|
||||
case kRGBA_F16_SkColorType:
|
||||
SkASSERT(needsColorXform); // TODO(eustas): not necessary for JXL.
|
||||
return true; // memcpy
|
||||
|
||||
// TODO(eustas): implement
|
||||
case kRGB_565_SkColorType:
|
||||
return false;
|
||||
case kGray_8_SkColorType:
|
||||
return false;
|
||||
case kAlpha_8_SkColorType:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkJpegxlCodec::imageOutCallback(void* opaque, size_t x, size_t y,
|
||||
size_t num_pixels, const void* pixels) {
|
||||
SkJpegxlCodec* instance = reinterpret_cast<SkJpegxlCodec*>(opaque);
|
||||
auto& codec = *instance->fCodec.get();
|
||||
size_t offset = y * codec.fRowBytes + (x << codec.fPixelShift);
|
||||
void* dst = SkTAddOffset<void>(codec.fDst, offset);
|
||||
if (instance->colorXform()) {
|
||||
instance->applyColorXform(dst, pixels, num_pixels);
|
||||
return;
|
||||
}
|
||||
switch (codec.fDstColorType) {
|
||||
case kRGBA_8888_SkColorType:
|
||||
memcpy(dst, pixels, 4 * num_pixels);
|
||||
return;
|
||||
case kBGRA_8888_SkColorType:
|
||||
SkOpts::RGBA_to_bgrA((uint32_t*) dst, (const uint32_t*)(pixels), num_pixels);
|
||||
return;
|
||||
case kRGBA_F16_SkColorType:
|
||||
memcpy(dst, pixels, 8 * num_pixels);
|
||||
return;
|
||||
default:
|
||||
SK_ABORT("Selected output format is not supported yet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool SkJpegxlCodec::scanFrames() {
|
||||
auto decoder = JxlDecoderMake(/* memory_manager = */ nullptr);
|
||||
JxlDecoder* dec = decoder.get();
|
||||
auto* frameHolder = fCodec.get();
|
||||
auto& frames = frameHolder->fFrames;
|
||||
const auto& info = fCodec->fInfo;
|
||||
frames.clear();
|
||||
|
||||
auto alpha = (info.alpha_bits != 0) ? SkEncodedInfo::Alpha::kUnpremul_Alpha
|
||||
: SkEncodedInfo::Alpha::kOpaque_Alpha;
|
||||
|
||||
auto status = JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Fresh instance must accept request for subscription.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return true;
|
||||
}
|
||||
|
||||
status = JxlDecoderSetInput(dec, fData->bytes(), fData->size());
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
// Fresh instance must accept first input chunk.
|
||||
SkDEBUGFAIL("libjxl returned unexpected status");
|
||||
return true;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
status = JxlDecoderProcessInput(dec);
|
||||
switch (status) {
|
||||
case JXL_DEC_FRAME: {
|
||||
size_t frameId = frames.size();
|
||||
JxlFrameHeader frameHeader;
|
||||
if (JxlDecoderGetFrameHeader(dec, &frameHeader) != JXL_DEC_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
frames.emplace_back(static_cast<int>(frameId), alpha);
|
||||
auto& frame = frames.back();
|
||||
// TODO(eustas): for better consistency we need to track total duration and report
|
||||
// frame duration as delta to previous frame.
|
||||
int duration = (1000 * frameHeader.duration * info.animation.tps_denominator) /
|
||||
info.animation.tps_numerator;
|
||||
frame.setDuration(duration);
|
||||
frameHolder->setAlphaAndRequiredFrame(&frame);
|
||||
break;
|
||||
}
|
||||
case JXL_DEC_SUCCESS: {
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SkJpegxlCodec::onGetFrameCount() {
|
||||
if (!fCodec->fInfo.have_animation) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!fCodec->fSeenAllFrames) {
|
||||
fCodec->fSeenAllFrames = scanFrames();
|
||||
}
|
||||
|
||||
return fCodec->fFrames.size();
|
||||
}
|
||||
|
||||
bool SkJpegxlCodec::onGetFrameInfo(int index, FrameInfo* frameInfo) const {
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
if (static_cast<size_t>(index) >= fCodec->fFrames.size()) {
|
||||
return false;
|
||||
}
|
||||
fCodec->fFrames[index].fillIn(frameInfo, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
int SkJpegxlCodec::onGetRepetitionCount() {
|
||||
JxlBasicInfo& info = fCodec->fInfo;
|
||||
if (!info.have_animation) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (info.animation.num_loops == 0) {
|
||||
return kRepetitionCountInfinite;
|
||||
}
|
||||
|
||||
if (SkTFitsIn<int>(info.animation.num_loops)) {
|
||||
return info.animation.num_loops - 1;
|
||||
}
|
||||
|
||||
// Largest "non-infinite" value.
|
||||
return std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
const SkFrameHolder* SkJpegxlCodec::getFrameHolder() const {
|
||||
return fCodec.get();
|
||||
}
|
||||
|
||||
// TODO(eustas): implement
|
||||
// SkCodec::Result SkJpegxlCodec::onStartScanlineDecode(
|
||||
// const SkImageInfo& /*dstInfo*/, const Options& /*options*/) { return kUnimplemented; }
|
||||
|
||||
// TODO(eustas): implement
|
||||
// SkCodec::Result SkJpegxlCodec::onStartIncrementalDecode(
|
||||
// const SkImageInfo& /*dstInfo*/, void*, size_t, const Options&) { return kUnimplemented; }
|
||||
|
||||
// TODO(eustas): implement
|
||||
// SkCodec::Result SkJpegxlCodec::onIncrementalDecode(int*) { return kUnimplemented; }
|
||||
|
||||
// TODO(eustas): implement
|
||||
// bool SkJpegxlCodec::onSkipScanlines(int /*countLines*/) { return false; }
|
||||
|
||||
// TODO(eustas): implement
|
||||
// int SkJpegxlCodec::onGetScanlines(
|
||||
// void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) { return 0; }
|
||||
|
||||
// TODO(eustas): implement
|
||||
// SkSampler* SkJpegxlCodec::getSampler(bool /*createIfNecessary*/) { return nullptr; }
|
105
src/codec/SkJpegxlCodec.h
Normal file
105
src/codec/SkJpegxlCodec.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkJpegxlCodec_DEFINED
|
||||
#define SkJpegxlCodec_DEFINED
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/codec/SkScalingCodec.h"
|
||||
|
||||
enum class SkEncodedImageFormat;
|
||||
struct SkEncodedInfo;
|
||||
enum SkEncodedOrigin;
|
||||
class SkFrameHolder;
|
||||
struct SkImageInfo;
|
||||
class SkJpegxlCodecPriv;
|
||||
class SkSampler;
|
||||
class SkStream;
|
||||
|
||||
/*
|
||||
*
|
||||
* This class implements the decoding for jpegxl images
|
||||
*
|
||||
*/
|
||||
class SkJpegxlCodec : public SkScalingCodec {
|
||||
public:
|
||||
static bool IsJpegxl(const void*, size_t);
|
||||
|
||||
/*
|
||||
* Assumes IsJpegxl was called and returned true
|
||||
* Takes ownership of the stream
|
||||
*/
|
||||
static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*);
|
||||
|
||||
protected:
|
||||
/* TODO(eustas): implement when downscaling is supported. */
|
||||
/* SkISize onGetScaledDimensions(float desiredScale) const override; */
|
||||
|
||||
/* TODO(eustas): implement when up-/down-scaling is supported. */
|
||||
/* bool onDimensionsSupported(const SkISize&) override; */
|
||||
|
||||
SkEncodedImageFormat onGetEncodedFormat() const override {
|
||||
return SkEncodedImageFormat::kJPEGXL;
|
||||
}
|
||||
|
||||
Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
|
||||
const Options& options, int* rowsDecodedPtr) override;
|
||||
|
||||
/* TODO(eustas): add support for transcoded JPEG images? */
|
||||
/* bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&,
|
||||
SkYUVAPixmapInfo*) const override; */
|
||||
|
||||
/* TODO(eustas): add support for transcoded JPEG images? */
|
||||
/* Result onGetYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps) override; */
|
||||
|
||||
/* TODO(eustas): implement when cropped output is supported. */
|
||||
/* bool onGetValidSubset(SkIRect* desiredSubset) const override; */
|
||||
|
||||
bool onRewind() override;
|
||||
|
||||
/* TODO(eustas): top-down by default; do we need something else? */
|
||||
/* SkScanlineOrder onGetScanlineOrder() const override; */
|
||||
/* int onOutputScanline(int inputScanline) const override; */
|
||||
|
||||
bool conversionSupported(const SkImageInfo&, bool, bool) override;
|
||||
|
||||
int onGetFrameCount() override;
|
||||
|
||||
bool onGetFrameInfo(int, FrameInfo*) const override;
|
||||
|
||||
int onGetRepetitionCount() override;
|
||||
|
||||
private:
|
||||
const SkFrameHolder* getFrameHolder() const override;
|
||||
|
||||
// Result onStartScanlineDecode(
|
||||
// const SkImageInfo& /*dstInfo*/, const Options& /*options*/) override;
|
||||
// Result onStartIncrementalDecode(
|
||||
// const SkImageInfo& /*dstInfo*/, void*, size_t, const Options&) override;
|
||||
// Result onIncrementalDecode(int*) override;
|
||||
// bool onSkipScanlines(int /*countLines*/) override;
|
||||
// int onGetScanlines(void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) override;
|
||||
// SkSampler* getSampler(bool /*createIfNecessary*/) override;
|
||||
|
||||
// Opaque codec implementation for lightweight header file.
|
||||
std::unique_ptr<SkJpegxlCodecPriv> fCodec;
|
||||
sk_sp<SkData> fData;
|
||||
|
||||
bool scanFrames();
|
||||
static void imageOutCallback(
|
||||
void* opaque, size_t x, size_t y, size_t num_pixels, const void* pixels);
|
||||
|
||||
SkJpegxlCodec(std::unique_ptr<SkJpegxlCodecPriv> codec,
|
||||
SkEncodedInfo&& info,
|
||||
std::unique_ptr<SkStream> stream,
|
||||
sk_sp<SkData> data);
|
||||
|
||||
using INHERITED = SkScalingCodec;
|
||||
};
|
||||
|
||||
#endif
|
15
third_party/highway/BUILD.gn
vendored
Normal file
15
third_party/highway/BUILD.gn
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("../third_party.gni")
|
||||
|
||||
third_party("highway") {
|
||||
public_include_dirs = [ "../externals/highway" ]
|
||||
|
||||
sources = [
|
||||
"../externals/highway/hwy/aligned_allocator.cc",
|
||||
"../externals/highway/hwy/targets.cc",
|
||||
]
|
||||
}
|
100
third_party/libjxl/BUILD.gn
vendored
Normal file
100
third_party/libjxl/BUILD.gn
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
# Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("../third_party.gni")
|
||||
|
||||
third_party("libjxl") {
|
||||
deps = [
|
||||
"//third_party/brotli",
|
||||
"//third_party/highway",
|
||||
]
|
||||
|
||||
public_include_dirs = [
|
||||
".",
|
||||
"../externals/libjxl/lib/include",
|
||||
]
|
||||
|
||||
include_dirs = [ "../externals/libjxl" ]
|
||||
|
||||
defines = [
|
||||
# Does not really matter.
|
||||
"JPEGXL_MAJOR_VERSION=1",
|
||||
"JPEGXL_MINOR_VERSION=0",
|
||||
"JPEGXL_PATCH_VERSION=0",
|
||||
|
||||
"JPEGXL_ENABLE_SKCMS=1",
|
||||
"JPEGXL_ENABLE_TRANSCODE_JPEG=0",
|
||||
]
|
||||
|
||||
if (is_official_build) {
|
||||
defines += [ "JXL_DEBUG_ON_ABORT=0" ]
|
||||
}
|
||||
|
||||
sources = [
|
||||
"../externals/libjxl/lib/jxl/ac_strategy.cc",
|
||||
"../externals/libjxl/lib/jxl/alpha.cc",
|
||||
"../externals/libjxl/lib/jxl/ans_common.cc",
|
||||
"../externals/libjxl/lib/jxl/aux_out.cc",
|
||||
"../externals/libjxl/lib/jxl/base/cache_aligned.cc",
|
||||
"../externals/libjxl/lib/jxl/base/data_parallel.cc",
|
||||
"../externals/libjxl/lib/jxl/base/descriptive_statistics.cc",
|
||||
"../externals/libjxl/lib/jxl/base/padded_bytes.cc",
|
||||
"../externals/libjxl/lib/jxl/base/status.cc",
|
||||
"../externals/libjxl/lib/jxl/blending.cc",
|
||||
"../externals/libjxl/lib/jxl/chroma_from_luma.cc",
|
||||
"../externals/libjxl/lib/jxl/coeff_order.cc",
|
||||
"../externals/libjxl/lib/jxl/color_encoding_internal.cc",
|
||||
"../externals/libjxl/lib/jxl/color_management.cc",
|
||||
"../externals/libjxl/lib/jxl/compressed_dc.cc",
|
||||
"../externals/libjxl/lib/jxl/convolve.cc",
|
||||
"../externals/libjxl/lib/jxl/dct_scales.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_ans.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_cache.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_context_map.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_external_image.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_frame.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_group.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_group_border.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_huffman.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_modular.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_noise.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_patch_dictionary.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_reconstruct.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_upsample.cc",
|
||||
"../externals/libjxl/lib/jxl/dec_xyb.cc",
|
||||
"../externals/libjxl/lib/jxl/decode.cc",
|
||||
"../externals/libjxl/lib/jxl/decode_to_jpeg.cc",
|
||||
"../externals/libjxl/lib/jxl/enc_bit_writer.cc",
|
||||
"../externals/libjxl/lib/jxl/entropy_coder.cc",
|
||||
"../externals/libjxl/lib/jxl/epf.cc",
|
||||
"../externals/libjxl/lib/jxl/fields.cc",
|
||||
"../externals/libjxl/lib/jxl/filters.cc",
|
||||
"../externals/libjxl/lib/jxl/frame_header.cc",
|
||||
"../externals/libjxl/lib/jxl/gauss_blur.cc",
|
||||
"../externals/libjxl/lib/jxl/headers.cc",
|
||||
"../externals/libjxl/lib/jxl/huffman_table.cc",
|
||||
"../externals/libjxl/lib/jxl/icc_codec.cc",
|
||||
"../externals/libjxl/lib/jxl/icc_codec_common.cc",
|
||||
"../externals/libjxl/lib/jxl/image.cc",
|
||||
"../externals/libjxl/lib/jxl/image_bundle.cc",
|
||||
"../externals/libjxl/lib/jxl/image_metadata.cc",
|
||||
"../externals/libjxl/lib/jxl/jpeg/dec_jpeg_data.cc",
|
||||
"../externals/libjxl/lib/jxl/jpeg/dec_jpeg_data_writer.cc",
|
||||
"../externals/libjxl/lib/jxl/jpeg/jpeg_data.cc",
|
||||
"../externals/libjxl/lib/jxl/loop_filter.cc",
|
||||
"../externals/libjxl/lib/jxl/luminance.cc",
|
||||
"../externals/libjxl/lib/jxl/memory_manager_internal.cc",
|
||||
"../externals/libjxl/lib/jxl/modular/encoding/dec_ma.cc",
|
||||
"../externals/libjxl/lib/jxl/modular/encoding/encoding.cc",
|
||||
"../externals/libjxl/lib/jxl/modular/modular_image.cc",
|
||||
"../externals/libjxl/lib/jxl/modular/transform/squeeze.cc",
|
||||
"../externals/libjxl/lib/jxl/modular/transform/transform.cc",
|
||||
"../externals/libjxl/lib/jxl/opsin_params.cc",
|
||||
"../externals/libjxl/lib/jxl/passes_state.cc",
|
||||
"../externals/libjxl/lib/jxl/quant_weights.cc",
|
||||
"../externals/libjxl/lib/jxl/quantizer.cc",
|
||||
"../externals/libjxl/lib/jxl/splines.cc",
|
||||
"../externals/libjxl/lib/jxl/toc.cc",
|
||||
]
|
||||
}
|
11
third_party/libjxl/jxl/jxl_export.h
vendored
Normal file
11
third_party/libjxl/jxl/jxl_export.h
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef THIRD_PARTY_LIBJXL_JXL_JXL_EXPORT_H_
|
||||
#define THIRD_PARTY_LIBJXL_JXL_JXL_EXPORT_H_
|
||||
|
||||
#define JXL_EXPORT
|
||||
#define JXL_DEPRECATED
|
||||
|
||||
#endif // THIRD_PARTY_LIBJXL_JXL_JXL_EXPORT_H_
|
@ -166,6 +166,7 @@ static DEFINE_string2(match, m, nullptr,
|
||||
#endif
|
||||
|
||||
static DEFINE_string(jpgs , PATH_PREFIX "jpgs" , "Directory to read jpgs from.");
|
||||
static DEFINE_string(jxls , PATH_PREFIX "jxls" , "Directory to read jxls from.");
|
||||
static DEFINE_string(skps , PATH_PREFIX "skps" , "Directory to read skps from.");
|
||||
static DEFINE_string(mskps , PATH_PREFIX "mskps" , "Directory to read mskps from.");
|
||||
static DEFINE_string(lotties, PATH_PREFIX "lotties", "Directory to read (Bodymovin) jsons from.");
|
||||
@ -769,6 +770,10 @@ void Viewer::initSlides() {
|
||||
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
||||
return sk_make_sp<ImageSlide>(name, path);}
|
||||
},
|
||||
{ ".jxl", "jxl-dir", FLAGS_jxls,
|
||||
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
||||
return sk_make_sp<ImageSlide>(name, path);}
|
||||
},
|
||||
#if defined(SK_ENABLE_SKOTTIE)
|
||||
{ ".json", "skottie-dir", FLAGS_lotties,
|
||||
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
||||
|
Loading…
Reference in New Issue
Block a user