std::unique_ptr<SkCodec> SkJpegxlCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
Result* result) {
SkASSERT(result); if (!stream) {
*result = SkCodec::kInvalidInput; return nullptr;
}
*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);
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));
}
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();
case kRGBA_F16_SkColorType:
SkASSERT(needsColorXform); // TODO(eustas): not necessary for JXL. returntrue; // memcpy
// TODO(eustas): implement case kRGB_565_SkColorType: returnfalse; case kGray_8_SkColorType: returnfalse; case kAlpha_8_SkColorType: returnfalse;
default: returnfalse;
} returntrue;
}
void SkJpegxlCodec::imageOutCallback(void* opaque, size_t x, size_t y,
size_t num_pixels, constvoid* 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; constauto& 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"); returntrue;
}
status = JxlDecoderSetInput(dec, fData->bytes(), fData->size()); if (status != JXL_DEC_SUCCESS) { // Fresh instance must accept first input chunk.
SkDEBUGFAIL("libjxl returned unexpected status"); returntrue;
}
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) { returntrue;
}
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: { returntrue;
} default: { returnfalse;
}
}
}
}
int SkJpegxlCodec::onGetFrameCount() { if (!fCodec->fInfo.have_animation) { return 1;
}
if (!fCodec->fSeenAllFrames) {
fCodec->fSeenAllFrames = scanFrames();
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.30Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.