// 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.
// These templates are not found via ADL. using hwy::HWY_NAMESPACE::AllFalse; using hwy::HWY_NAMESPACE::Gt; using hwy::HWY_NAMESPACE::Le; using hwy::HWY_NAMESPACE::MaskFromVec; using hwy::HWY_NAMESPACE::Or; using hwy::HWY_NAMESPACE::Rebind; using hwy::HWY_NAMESPACE::ShiftRight;
using D = HWY_FULL(float); using DU = HWY_FULL(uint32_t); using DI = HWY_FULL(int32_t); using DI16 = Rebind<int16_t, DI>; using DI16_FULL = HWY_CAPPED(int16_t, kDCTBlockSize);
constexpr D d;
constexpr DI di;
constexpr DI16 di16;
constexpr DI16_FULL di16_full;
// TODO(veluca): consider SIMDfying. void Transpose8x8InPlace(int32_t* JXL_RESTRICT block) { for (size_t x = 0; x < 8; x++) { for (size_t y = x + 1; y < 8; y++) {
std::swap(block[y * 8 + x], block[x * 8 + y]);
}
}
}
size_t idct_stride[3]; for (size_t c = 0; c < 3; c++) {
idct_stride[c] = render_pipeline_input.GetBuffer(c).first->PixelsPerRow();
}
HWY_ALIGN int32_t scaled_qtable[64 * 3];
ACType ac_type = dec_state->coefficients->Type(); auto dequant_block = ac_type == ACType::k16 ? DequantBlock<ACType::k16>
: DequantBlock<ACType::k32>; // Whether or not coefficients should be stored for future usage, and/or read // from past usage. bool accumulate = !dec_state->coefficients->IsEmpty(); // Offset of the current block in the group.
size_t offset = 0;
// TODO(veluca): all of this should be done only once per image. const ColorCorrelation& color_correlation = dec_state->shared->cmap.base(); if (jpeg_data) { if (!color_correlation.IsJPEGCompatible()) { return JXL_FAILURE("The CfL map is not JPEG-compatible");
}
jpeg_is_gray = (jpeg_data->components.size() == 1);
JXL_ENSURE(frame_header.color_transform != ColorTransform::kXYB);
jpeg_c_map = JpegOrder(frame_header.color_transform, jpeg_is_gray); const std::vector<QuantEncoding>& qe =
dec_state->shared->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.");
}
JXL_ENSURE(qe[0].qraw.qtable->size() == 3 * 8 * 8); int* qtable = qe[0].qraw.qtable->data(); for (size_t c = 0; c < 3; c++) { if (frame_header.color_transform == ColorTransform::kNone) {
dcoff[c] = 1024 / qtable[64 * c];
} for (size_t i = 0; i < 64; i++) { // Transpose the matrix, as it will be used on the transposed block. int n = qtable[64 + i]; int d = qtable[64 * c + i]; if (n <= 0 || d <= 0 || n >= 65536 || d >= 65536) { return JXL_FAILURE("Invalid JPEG quantization table");
}
scaled_qtable[64 * c + (i % 8) * 8 + (i / 8)] =
(1 << kCFLFixedPointPrecision) * n / d;
}
}
}
size_t hshift[3] = {cs.HShift(0), cs.HShift(1), cs.HShift(2)};
size_t vshift[3] = {cs.VShift(0), cs.VShift(1), cs.VShift(2)};
Rect r[3]; for (size_t i = 0; i < 3; i++) {
r[i] =
Rect(block_rect.x0() >> hshift[i], block_rect.y0() >> vshift[i],
block_rect.xsize() >> hshift[i], block_rect.ysize() >> vshift[i]); if (!r[i].IsInside({0, 0, dec_state->shared->dc->Plane(i).xsize(),
dec_state->shared->dc->Plane(i).ysize()})) { return JXL_FAILURE("Frame dimensions are too big for the image.");
}
}
for (size_t by = 0; by < ysize_blocks; ++by) {
get_block->StartRow(by);
size_t sby[3] = {by >> vshift[0], by >> vshift[1], by >> vshift[2]};
// Can only happen in the second or lower rows of a varblock. if (JXL_UNLIKELY(!acs.IsFirstBlock())) {
bx += llf_x; continue;
} const size_t log2_covered_blocks = acs.log2_covered_blocks();
size_t nzeros =
decoder->ReadHybridUintInlined<uses_lz77>(nzero_ctx, br, context_map); if (nzeros > size - covered_blocks) { return JXL_FAILURE("Invalid AC: nzeros %" PRIuS " too large for %" PRIuS " 8x8 blocks",
nzeros, covered_blocks);
} for (size_t y = 0; y < acs.covered_blocks_y(); y++) { for (size_t x = 0; x < acs.covered_blocks_x(); x++) {
row_nzeros[bx + x + y * nzeros_stride] =
(nzeros + covered_blocks - 1) >> log2_covered_blocks;
}
}
size_t prev = (nzeros > size / 16 ? 0 : 1); for (size_t k = covered_blocks; k < size && nzeros != 0; ++k) { const size_t ctx =
histo_offset + ZeroDensityContext(nzeros, k, covered_blocks,
log2_covered_blocks, prev); const size_t u_coeff =
decoder->ReadHybridUintInlined<uses_lz77>(ctx, br, context_map); // Hand-rolled version of UnpackSigned, shifting before the conversion to // signed integer to avoid undefined behavior of shifting negative numbers. const size_t magnitude = u_coeff >> 1; const size_t neg_sign = (~u_coeff) & 1; const intptr_t coeff = static_cast<intptr_t>((magnitude ^ (neg_sign - 1)) << shift); if (ac_type == ACType::k16) {
block.ptr16[order[k]] += coeff;
} else {
block.ptr32[order[k]] += coeff;
}
prev = static_cast<size_t>(u_coeff != 0);
nzeros -= prev;
} if (JXL_UNLIKELY(nzeros != 0)) { return JXL_FAILURE("Invalid AC: nzeros at end of block is %" PRIuS ", should be 0. Block (%" PRIuS ", %" PRIuS "), channel %" PRIuS,
nzeros, bx, by, c);
}
returntrue;
}
// Structs used by DecodeGroupImpl to get a quantized block. // GetBlockFromBitstream uses ANS decoding (and thus keeps track of row // pointers in row_nzeros), GetBlockFromEncoder simply reads the coefficient // image provided by the encoder.
struct GetBlockFromBitstream : public GetBlock { void StartRow(size_t by) override {
qf_row = rect.ConstRow(*qf, by); for (size_t c = 0; c < 3; c++) {
size_t sby = by >> vshift[c];
quant_dc_row = quant_dc->ConstRow(rect.y0() + by) + rect.x0(); for (size_t i = 0; i < num_passes; i++) {
row_nzeros[i][c] = group_dec_cache->num_nzeroes[i].PlaneRow(c, sby);
row_nzeros_top[i][c] =
sby == 0
? nullptr
: group_dec_cache->num_nzeroes[i].ConstPlaneRow(c, sby - 1);
}
}
}
for (size_t pass = 0; pass < num_passes; pass++) { // Select which histogram set to use among those of the current pass.
size_t cur_histogram = 0; if (histo_selector_bits != 0) {
cur_histogram = readers[pass]->ReadBits(histo_selector_bits);
} if (cur_histogram >= dec_state->shared->num_histograms) { return JXL_FAILURE("Invalid histogram selector");
}
ctx_offset[pass] = cur_histogram * block_ctx_map->NumACContexts();
JXL_ASSIGN_OR_RETURN(
decoders[pass],
ANSSymbolReader::Create(&dec_state->code[pass + first_pass],
readers[pass]));
}
nzeros_stride = group_dec_cache->num_nzeroes[0].PixelsPerRow(); for (size_t i = 0; i < num_passes; i++) {
JXL_ENSURE(
nzeros_stride == static_cast<size_t>(group_dec_cache->num_nzeroes[i].PixelsPerRow()));
} returntrue;
}
if (should_run_pipeline) {
*should_run_pipeline = draw != kDontDraw;
}
if (draw == kDraw && num_passes == 0 && first_pass == 0) {
JXL_RETURN_IF_ERROR(group_dec_cache->InitDCBufferOnce(memory_manager)); const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling; for (size_t c : {0, 1, 2}) {
size_t hs = cs.HShift(c);
size_t vs = cs.VShift(c); // We reuse filter_input_storage here as it is not currently in use. const Rect src_rect_precs =
dec_state->shared->frame_dim.BlockGroupRect(group_idx); const Rect src_rect =
Rect(src_rect_precs.x0() >> hs, src_rect_precs.y0() >> vs,
src_rect_precs.xsize() >> hs, src_rect_precs.ysize() >> vs); const Rect copy_rect(kRenderPipelineXOffset, 2, src_rect.xsize(),
src_rect.ysize());
JXL_RETURN_IF_ERROR(
CopyImageToWithPadding(src_rect, dec_state->shared->dc->Plane(c), 2,
copy_rect, &group_dec_cache->dc_buffer)); // Mirrorpad. Interleaving left and right padding ensures that padding // works out correctly even for images with DC size of 1. for (size_t y = 0; y < src_rect.ysize() + 4; y++) {
size_t xend = kRenderPipelineXOffset +
(dec_state->shared->dc->Plane(c).xsize() >> hs) -
src_rect.x0(); for (size_t ix = 0; ix < 2; ix++) { if (src_rect.x0() == 0) {
group_dec_cache->dc_buffer.Row(y)[kRenderPipelineXOffset - ix - 1] =
group_dec_cache->dc_buffer.Row(y)[kRenderPipelineXOffset + ix];
} if (src_rect.x0() + src_rect.xsize() + 2 >=
(dec_state->shared->dc->xsize() >> hs)) {
group_dec_cache->dc_buffer.Row(y)[xend + ix] =
group_dec_cache->dc_buffer.Row(y)[xend - ix - 1];
}
}
} constauto& buffer = render_pipeline_input.GetBuffer(c);
Rect dst_rect = buffer.second;
ImageF* upsampling_dst = buffer.first;
JXL_ENSURE(dst_rect.IsInside(*upsampling_dst));
RenderPipelineStage::RowInfo input_rows(1, std::vector<float*>(5));
RenderPipelineStage::RowInfo output_rows(1, std::vector<float*>(8)); for (size_t y = src_rect.y0(); y < src_rect.y0() + src_rect.ysize();
y++) { for (ssize_t iy = 0; iy < 5; iy++) {
input_rows[0][iy] = group_dec_cache->dc_buffer.Row(
Mirror(static_cast<ssize_t>(y) + iy - 2,
dec_state->shared->dc->Plane(c).ysize() >> vs) +
2 - src_rect.y0());
} for (size_t iy = 0; iy < 8; iy++) {
output_rows[0][iy] =
dst_rect.Row(upsampling_dst, ((y - src_rect.y0()) << 3) + iy) -
kRenderPipelineXOffset;
} // Arguments set to 0/nullptr are not used.
JXL_RETURN_IF_ERROR(dec_state->upsampler8x->ProcessRow(
input_rows, output_rows, /*xextra=*/0, src_rect.xsize(), 0, 0, thread));
}
} 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.