// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.
// If the previous frame was not a kRegularFrame, `decoded` may have different // dimensions; must reset to avoid errors.
decoded->RemoveColor();
decoded->ClearExtraChannels();
if (JXL_DEBUG_V_LEVEL >= 3) { for (size_t i = 0; i < toc_entries; ++i) {
JXL_DEBUG_V(3, "TOC entry %" PRIuS " size %" PRIuS " id %" PRIuS "", i,
toc_[i].size, toc_[i].id);
}
}
// Overflow check. if (group_codes_begin + section_sizes_sum_ < group_codes_begin) { return JXL_FAILURE("Invalid group codes");
}
if (!frame_header_.chroma_subsampling.Is444() &&
!(frame_header_.flags & FrameHeader::kSkipAdaptiveDCSmoothing) &&
frame_header_.encoding == FrameEncoding::kVarDCT) { return JXL_FAILURE( "Non-444 chroma subsampling is not allowed when adaptive DC " "smoothing is enabled");
} returntrue;
}
Status FrameDecoder::InitFrameOutput() {
JXL_RETURN_IF_ERROR(
InitializePassesSharedState(frame_header_, &dec_state_->shared_storage));
JXL_RETURN_IF_ERROR(dec_state_->Init(frame_header_));
modular_frame_decoder_.Init(frame_dim_);
if (decoded_->IsJPEG()) { if (frame_header_.encoding == FrameEncoding::kModular) { return JXL_FAILURE("Cannot output JPEG from Modular");
}
jpeg::JPEGData* jpeg_data = decoded_->jpeg_data.get();
size_t num_components = jpeg_data->components.size(); if (num_components != 1 && num_components != 3) { return JXL_FAILURE("Invalid number of components");
} if (frame_header_.nonserialized_metadata->m.xyb_encoded) { return JXL_FAILURE("Cannot decode to JPEG an XYB image");
} auto jpeg_c_map = JpegOrder(ColorTransform::kYCbCr, num_components == 1);
decoded_->jpeg_data->width = frame_dim_.xsize;
decoded_->jpeg_data->height = frame_dim_.ysize; for (size_t c = 0; c < num_components; c++) { auto& component = jpeg_data->components[jpeg_c_map[c]];
component.width_in_blocks =
frame_dim_.xsize_blocks >> frame_header_.chroma_subsampling.HShift(c);
component.height_in_blocks =
frame_dim_.ysize_blocks >> frame_header_.chroma_subsampling.VShift(c);
component.h_samp_factor =
1 << frame_header_.chroma_subsampling.RawHShift(c);
component.v_samp_factor =
1 << frame_header_.chroma_subsampling.RawVShift(c);
component.coeffs.resize(component.width_in_blocks *
component.height_in_blocks * jxl::kDCTBlockSize);
}
}
Status FrameDecoder::FinalizeDC() { // Do Adaptive DC smoothing if enabled. This *must* happen between all the // ProcessDCGroup and ProcessACGroup.
JxlMemoryManager* memory_manager = dec_state_->memory_manager(); if (frame_header_.encoding == FrameEncoding::kVarDCT &&
!(frame_header_.flags & FrameHeader::kSkipAdaptiveDCSmoothing) &&
!(frame_header_.flags & FrameHeader::kUseDcFrame)) {
JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing(
memory_manager, dec_state_->shared->quantizer.MulDC(),
&dec_state_->shared_storage.dc_storage, pool_));
}
finalized_dc_ = true; returntrue;
}
Status FrameDecoder::AllocateOutput() { if (allocated_) returntrue;
modular_frame_decoder_.MaybeDropFullImage();
decoded_->origin = frame_header_.frame_origin;
JXL_RETURN_IF_ERROR(
dec_state_->InitForAC(frame_header_.passes.num_passes, nullptr));
allocated_ = true; returntrue;
}
Status FrameDecoder::ProcessACGlobal(BitReader* br) {
JXL_ENSURE(finalized_dc_);
JxlMemoryManager* memory_manager = dec_state_->memory_manager();
// Decode AC group. if (frame_header_.encoding == FrameEncoding::kVarDCT) {
JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.Decode(
memory_manager, br, &modular_frame_decoder_));
JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.EnsureComputed(
memory_manager, dec_state_->used_acs));
JXL_DEBUG_V(3, "Processing AC global with %d passes and %" PRIuS " sets of histograms",
frame_header_.passes.num_passes,
dec_state_->shared_storage.num_histograms);
dec_state_->code.resize(kMaxNumPasses);
dec_state_->context_map.resize(kMaxNumPasses); // Read coefficient orders and histograms.
size_t max_num_bits_ac = 0; for (size_t i = 0; i < frame_header_.passes.num_passes; i++) {
uint16_t used_orders = U32Coder::Read(kOrderEnc, br);
JXL_RETURN_IF_ERROR(DecodeCoeffOrders(
memory_manager, used_orders, dec_state_->used_acs,
&dec_state_->shared_storage
.coeff_orders[i * dec_state_->shared_storage.coeff_order_size],
br));
size_t num_contexts =
dec_state_->shared->num_histograms *
dec_state_->shared_storage.block_ctx_map.NumACContexts();
JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, num_contexts,
&dec_state_->code[i],
&dec_state_->context_map[i])); // Add extra values to enable the cheat in hot loop of DecodeACVarBlock.
dec_state_->context_map[i].resize(
num_contexts + kZeroDensityContextLimit - kZeroDensityContextCount);
max_num_bits_ac =
std::max(max_num_bits_ac, dec_state_->code[i].max_num_bits);
}
max_num_bits_ac += CeilLog2Nonzero(frame_header_.passes.num_passes); // 16-bit buffer for decoding to JPEG are not implemented. // TODO(veluca): figure out the exact limit - 16 should still work with // 16-bit buffers, but we are excluding it for safety. bool use_16_bit = max_num_bits_ac < 16 && !decoded_->IsJPEG(); bool store = frame_header_.passes.num_passes > 1;
size_t xs = store ? kGroupDim * kGroupDim : 0;
size_t ys = store ? frame_dim_.num_groups : 0; if (use_16_bit) {
JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
ACImageT<int16_t>::Make(memory_manager, xs, ys));
} else {
JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
ACImageT<int32_t>::Make(memory_manager, xs, ys));
} if (store) {
dec_state_->coefficients->ZeroFill();
}
}
// Set JPEG decoding data. if (decoded_->IsJPEG()) {
decoded_->color_transform = frame_header_.color_transform;
decoded_->chroma_subsampling = frame_header_.chroma_subsampling; const std::vector<QuantEncoding>& qe =
dec_state_->shared_storage.matrices.encodings(); if (qe.empty() || qe[0].mode != QuantEncoding::Mode::kQuantModeRAW ||
std::abs(qe[0].qraw.qtable_den - 1.f / (8 * 255)) > 1e-8f) { return JXL_FAILURE( "Quantization table is not a JPEG quantization table.");
}
jpeg::JPEGData* jpeg_data = decoded_->jpeg_data.get();
size_t num_components = jpeg_data->components.size(); bool is_gray = (num_components == 1);
JXL_ENSURE(frame_header_.color_transform != ColorTransform::kXYB); auto jpeg_c_map = JpegOrder(frame_header_.color_transform, is_gray);
size_t qt_set = 0;
JXL_ENSURE(num_components <= 3);
JXL_ENSURE(qe[0].qraw.qtable->size() == 3 * 8 * 8); int* qtable = qe[0].qraw.qtable->data(); for (size_t c = 0; c < num_components; c++) { // TODO(eustas): why 1-st quant table for gray?
size_t quant_c = is_gray ? 1 : c;
size_t qpos = jpeg_data->components[jpeg_c_map[c]].quant_idx;
JXL_ENSURE(qpos != jpeg_data->quant.size());
qt_set |= 1 << qpos; for (size_t x = 0; x < 8; x++) { for (size_t y = 0; y < 8; y++) {
jpeg_data->quant[qpos].values[x * 8 + y] =
qtable[quant_c * 64 + y * 8 + x];
}
}
} for (size_t i = 0; i < jpeg_data->quant.size(); i++) { if (qt_set & (1 << i)) continue; if (i == 0) return JXL_FAILURE("First quant table unused."); // Unused quant table is set to copy of previous quant table for (size_t j = 0; j < 64; j++) {
jpeg_data->quant[i].values[j] = jpeg_data->quant[i - 1].values[j];
}
}
}
decoded_ac_global_ = true; returntrue;
}
Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
BitReader* JXL_RESTRICT* br,
size_t num_passes, size_t thread, bool force_draw, bool dc_only) {
size_t group_dim = frame_dim_.group_dim; const size_t gx = ac_group_id % frame_dim_.xsize_groups; const size_t gy = ac_group_id / frame_dim_.xsize_groups; const size_t x = gx * group_dim; const size_t y = gy * group_dim;
JxlMemoryManager* memory_manager = dec_state_->memory_manager();
JXL_DEBUG_V(3, "Processing AC group %" PRIuS "(%" PRIuS ",%" PRIuS ") group_dim: %" PRIuS " decoded passes: %u new passes: %" PRIuS,
ac_group_id, gx, gy, group_dim,
decoded_passes_per_ac_group_[ac_group_id], num_passes);
if (finalized_dc_ && ac_global_sec != num && !decoded_ac_global_) {
JXL_RETURN_IF_ERROR(ProcessACGlobal(sections[ac_global_sec].br));
section_status[ac_global_sec] = SectionStatus::kDone;
}
if (progressive_detail_ >= JxlProgressiveDetail::kLastPasses) { // Mark that we only want the next progression pass.
size_t target_complete_passes = NextNumPassesToPause(); for (size_t i = 0; i < ac_group_sec.size(); i++) {
desired_num_ac_passes[i] =
std::min(desired_num_ac_passes[i],
target_complete_passes - decoded_passes_per_ac_group_[i]);
}
}
if (decoded_ac_global_) { // Mark all the AC groups that we received as not complete yet. for (size_t i = 0; i < ac_group_sec.size(); i++) { if (desired_num_ac_passes[i] != 0) {
dec_state_->render_pipeline->ClearDone(i);
}
}
constauto prepare_storage = [this](size_t num_threads) -> Status {
JXL_RETURN_IF_ERROR(
PrepareStorage(num_threads, decoded_passes_per_ac_group_.size())); returntrue;
}; constauto process_group = [this, &ac_group_sec, &desired_num_ac_passes,
&num, §ions, §ion_status](
size_t g, size_t thread) -> Status { if (desired_num_ac_passes[g] == 0) { // no new AC pass, nothing to do returntrue;
}
(void)num;
size_t first_pass = decoded_passes_per_ac_group_[g];
BitReader* JXL_RESTRICT readers[kMaxNumPasses]; for (size_t i = 0; i < desired_num_ac_passes[g]; i++) {
JXL_ENSURE(ac_group_sec[g][first_pass + i] != num);
readers[i] = sections[ac_group_sec[g][first_pass + i]].br;
}
JXL_RETURN_IF_ERROR(ProcessACGroup(
g, readers, desired_num_ac_passes[g], GetStorageLocation(thread, g), /*force_draw=*/false, /*dc_only=*/false)); for (size_t i = 0; i < desired_num_ac_passes[g]; i++) {
section_status[ac_group_sec[g][first_pass + i]] = SectionStatus::kDone;
} returntrue;
};
JXL_RETURN_IF_ERROR(RunOnPool(pool_, 0, ac_group_sec.size(),
prepare_storage, process_group, "DecodeGroup"));
}
Status FrameDecoder::Flush() { bool has_blending = frame_header_.blending_info.mode != BlendMode::kReplace ||
frame_header_.custom_size_or_origin; for (constauto& blending_info_ec :
frame_header_.extra_channel_blending_info) { if (blending_info_ec.mode != BlendMode::kReplace) has_blending = true;
} // No early Flush() if blending is enabled. if (has_blending && !is_finalized_) { returnfalse;
} // No early Flush() - nothing to do - if the frame is a kSkipProgressive // frame. if (frame_header_.frame_type == FrameType::kSkipProgressive &&
!is_finalized_) { returntrue;
} if (decoded_->IsJPEG()) { // Nothing to do. returntrue;
}
JXL_RETURN_IF_ERROR(AllocateOutput());
uint32_t completely_decoded_ac_pass = *std::min_element(
decoded_passes_per_ac_group_.begin(), decoded_passes_per_ac_group_.end()); if (completely_decoded_ac_pass < frame_header_.passes.num_passes) { // We don't have all AC yet: force a draw of all the missing areas. // Mark all sections as not complete. for (size_t i = 0; i < decoded_passes_per_ac_group_.size(); i++) { if (decoded_passes_per_ac_group_[i] < frame_header_.passes.num_passes) {
dec_state_->render_pipeline->ClearDone(i);
}
} constauto prepare_storage = [this](const size_t num_threads) -> Status {
JXL_RETURN_IF_ERROR(
PrepareStorage(num_threads, decoded_passes_per_ac_group_.size())); returntrue;
}; constauto process_group = [this](const uint32_t g,
size_t thread) -> Status { if (decoded_passes_per_ac_group_[g] == frame_header_.passes.num_passes) { // This group was drawn already, nothing to do. returntrue;
}
BitReader* JXL_RESTRICT readers[kMaxNumPasses] = {};
JXL_RETURN_IF_ERROR(ProcessACGroup(
g, readers, /*num_passes=*/0, GetStorageLocation(thread, g), /*force_draw=*/true, /*dc_only=*/!decoded_ac_global_)); returntrue;
};
JXL_RETURN_IF_ERROR(RunOnPool(pool_, 0, decoded_passes_per_ac_group_.size(),
prepare_storage, process_group, "ForceDrawGroup"));
}
// undo global modular transforms and copy int pixel buffers to float ones
JXL_RETURN_IF_ERROR(modular_frame_decoder_.FinalizeDecoding(
frame_header_, dec_state_, pool_, is_finalized_));
returntrue;
}
int FrameDecoder::SavedAs(const FrameHeader& header) { if (header.frame_type == FrameType::kDCFrame) { // bits 16, 32, 64, 128 for DC level return 16 << (header.dc_level - 1);
} elseif (header.CanBeReferenced()) { // bits 1, 2, 4 and 8 for the references return 1 << header.save_as_reference;
}
return 0;
}
bool FrameDecoder::HasEverything() const { if (!decoded_dc_global_) returnfalse; if (!decoded_ac_global_) returnfalse; if (HasDcGroupToDecode()) returnfalse; for (constauto& nb_passes : decoded_passes_per_ac_group_) { if (nb_passes < frame_header_.passes.num_passes) returnfalse;
} returntrue;
}
int FrameDecoder::References() const { if (is_finalized_) { return 0;
} if (!HasEverything()) return 0;
int result = 0;
// Blending if (frame_header_.frame_type == FrameType::kRegularFrame ||
frame_header_.frame_type == FrameType::kSkipProgressive) { bool cropped = frame_header_.custom_size_or_origin; if (cropped || frame_header_.blending_info.mode != BlendMode::kReplace) {
result |= (1 << frame_header_.blending_info.source);
} constauto& extra = frame_header_.extra_channel_blending_info; for (constauto& ecbi : extra) { if (cropped || ecbi.mode != BlendMode::kReplace) {
result |= (1 << ecbi.source);
}
}
}
// Patches if (frame_header_.flags & FrameHeader::kPatches) {
result |= dec_state_->shared->image_features.patches.GetReferences();
}
// DC Level if (frame_header_.flags & FrameHeader::kUseDcFrame) { // Reads from the next dc level int dc_level = frame_header_.dc_level + 1; // bits 16, 32, 64, 128 for DC level
result |= (16 << (dc_level - 1));
}
return result;
}
Status FrameDecoder::FinalizeFrame() { if (is_finalized_) { return JXL_FAILURE("FinalizeFrame called multiple times");
}
is_finalized_ = true; if (decoded_->IsJPEG()) { // Nothing to do. returntrue;
}
// undo global modular transforms and copy int pixel buffers to float ones
JXL_RETURN_IF_ERROR(
modular_frame_decoder_.FinalizeDecoding(frame_header_, dec_state_, pool_, /*inplace=*/true));
if (frame_header_.CanBeReferenced()) { auto& info = dec_state_->shared_storage
.reference_frames[frame_header_.save_as_reference];
*info.frame = std::move(dec_state_->frame_storage_for_referencing);
info.ib_is_in_xyb = frame_header_.save_before_color_transform;
} returntrue;
}
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.