diff options
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/enc_frame.cc')
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/enc_frame.cc | 308 |
1 files changed, 196 insertions, 112 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.cc b/third_party/jpeg-xl/lib/jxl/enc_frame.cc index aae59c49a6..8587e1aed2 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_frame.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_frame.cc @@ -12,13 +12,13 @@ #include <array> #include <atomic> #include <cmath> -#include <limits> +#include <memory> #include <numeric> +#include <utility> #include <vector> #include "lib/jxl/ac_context.h" #include "lib/jxl/ac_strategy.h" -#include "lib/jxl/ans_params.h" #include "lib/jxl/base/bits.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" @@ -31,7 +31,6 @@ #include "lib/jxl/coeff_order_fwd.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/common.h" // kMaxNumPasses -#include "lib/jxl/compressed_dc.h" #include "lib/jxl/dct_util.h" #include "lib/jxl/dec_external_image.h" #include "lib/jxl/enc_ac_strategy.h" @@ -47,7 +46,6 @@ #include "lib/jxl/enc_entropy_coder.h" #include "lib/jxl/enc_external_image.h" #include "lib/jxl/enc_fields.h" -#include "lib/jxl/enc_gaborish.h" #include "lib/jxl/enc_group.h" #include "lib/jxl/enc_heuristics.h" #include "lib/jxl/enc_modular.h" @@ -285,7 +283,8 @@ Status LoopFilterFromParams(const CompressParams& cparams, bool streaming_mode, if (frame_header->encoding == FrameEncoding::kModular && !cparams.IsLossless()) { // TODO(veluca): this formula is nonsense. - loop_filter->epf_sigma_for_modular = cparams.butteraugli_distance; + loop_filter->epf_sigma_for_modular = + std::max(cparams.butteraugli_distance, 1.0f); } if (frame_header->encoding == FrameEncoding::kModular && cparams.lossy_palette) { @@ -539,7 +538,7 @@ struct PixelStatsForChromacityAdjustment { float dx = 0; float db = 0; float exposed_blue = 0; - float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) const { + static float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) { float xmax = 0; float ymax = 0; for (size_t ty = 1; ty < rect.ysize(); ++ty) { @@ -583,7 +582,7 @@ struct PixelStatsForChromacityAdjustment { dx = CalcPlane(&opsin->Plane(0), rect); CalcExposedBlue(&opsin->Plane(1), &opsin->Plane(2), rect); } - int HowMuchIsXChannelPixelized() { + int HowMuchIsXChannelPixelized() const { if (dx >= 0.03) { return 2; } @@ -592,7 +591,7 @@ struct PixelStatsForChromacityAdjustment { } return 0; } - int HowMuchIsBChannelPixelized() { + int HowMuchIsBChannelPixelized() const { int add = exposed_blue >= 0.13 ? 1 : 0; if (db > 0.38) { return 2 + add; @@ -682,12 +681,12 @@ void ComputeNoiseParams(const CompressParams& cparams, bool streaming_mode, } } -void DownsampleColorChannels(const CompressParams& cparams, - const FrameHeader& frame_header, - bool color_is_jpeg, Image3F* opsin) { +Status DownsampleColorChannels(const CompressParams& cparams, + const FrameHeader& frame_header, + bool color_is_jpeg, Image3F* opsin) { if (color_is_jpeg || frame_header.upsampling == 1 || cparams.already_downsampled) { - return; + return true; } if (frame_header.encoding == FrameEncoding::kVarDCT && frame_header.upsampling == 2) { @@ -698,16 +697,18 @@ void DownsampleColorChannels(const CompressParams& cparams, // TODO(lode): DownsampleImage2_Iterative is currently too slow to // be used for squirrel, make it faster, and / or enable it only for // kitten. - DownsampleImage2_Iterative(opsin); + JXL_RETURN_IF_ERROR(DownsampleImage2_Iterative(opsin)); } else { - DownsampleImage2_Sharper(opsin); + JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(opsin)); } } else { - DownsampleImage(opsin, frame_header.upsampling); + JXL_ASSIGN_OR_RETURN(*opsin, + DownsampleImage(*opsin, frame_header.upsampling)); } if (frame_header.encoding == FrameEncoding::kVarDCT) { PadImageToBlockMultipleInPlace(opsin); } + return true; } template <typename V, typename R> @@ -741,14 +742,17 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, const size_t ysize_blocks = frame_dim.ysize_blocks; // no-op chroma from luma - shared.cmap = ColorCorrelationMap(xsize, ysize, false); + JXL_ASSIGN_OR_RETURN(shared.cmap, + ColorCorrelationMap::Create(xsize, ysize, false)); shared.ac_strategy.FillDCT8(); - FillImage(uint8_t(0), &shared.epf_sharpness); + FillImage(static_cast<uint8_t>(0), &shared.epf_sharpness); enc_state->coeffs.clear(); while (enc_state->coeffs.size() < enc_state->passes.size()) { - enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>( - kGroupDim * kGroupDim, frame_dim.num_groups)); + JXL_ASSIGN_OR_RETURN( + std::unique_ptr<ACImageT<int32_t>> coeffs, + ACImageT<int32_t>::Make(kGroupDim * kGroupDim, frame_dim.num_groups)); + enc_state->coeffs.emplace_back(std::move(coeffs)); } // convert JPEG quantization table to a Quantizer object @@ -779,7 +783,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, 1.0f / dcquantization[2]}; qe[AcStrategy::Type::DCT] = QuantEncoding::RAW(qt); - DequantMatricesSetCustom(&shared.matrices, qe, enc_modular); + JXL_RETURN_IF_ERROR( + DequantMatricesSetCustom(&shared.matrices, qe, enc_modular)); // Ensure that InvGlobalScale() is 1. shared.quantizer = Quantizer(&shared.matrices, 1, kGlobalScaleDenom); @@ -844,7 +849,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, kScale * row_s[x * kDCTBlockSize + coeffpos] + (kOffset - kBase * kScale) * scaled_m; if (std::abs(scaled_m) > 1e-8f) { - float from, to; + float from; + float to; if (scaled_m > 0) { from = (scaled_s - kZeroThresh) / scaled_m; to = (scaled_s + kZeroThresh) / scaled_m; @@ -889,7 +895,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, } } - Image3F dc = Image3F(xsize_blocks, ysize_blocks); + JXL_ASSIGN_OR_RETURN(Image3F dc, Image3F::Create(xsize_blocks, ysize_blocks)); if (!frame_header.chroma_subsampling.Is444()) { ZeroFillImage(&dc); for (auto& coeff : enc_state->coeffs) { @@ -944,7 +950,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, idc = inputjpeg[base] + 1024 / qt[c * 64]; } dc_counts[c][std::min(static_cast<uint32_t>(idc + 1024), - uint32_t(2047))]++; + static_cast<uint32_t>(2047))]++; total_dc[c]++; fdc[bx >> hshift] = idc * dcquantization_r[c]; if (c == 1 || !enc_state->cparams.force_cfl_jpeg_recompression || @@ -1024,18 +1030,27 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, *std::max_element(ctx_map.begin(), ctx_map.end()) + 1; // disable DC frame for now + std::atomic<bool> has_error{false}; auto compute_dc_coeffs = [&](const uint32_t group_index, size_t /* thread */) { + if (has_error) return; const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index); - enc_modular->AddVarDCTDC(frame_header, dc, r, group_index, - /*nl_dc=*/false, enc_state, - /*jpeg_transcode=*/true); - enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true, - enc_state); + if (!enc_modular->AddVarDCTDC(frame_header, dc, r, group_index, + /*nl_dc=*/false, enc_state, + /*jpeg_transcode=*/true)) { + has_error = true; + return; + } + if (!enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true, + enc_state)) { + has_error = true; + return; + } }; JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups, ThreadPool::NoInit, compute_dc_coeffs, "Compute DC coeffs")); + if (has_error) return JXL_FAILURE("Compute DC coeffs failed"); return true; } @@ -1077,10 +1092,12 @@ void ComputeAllCoeffOrders(PassesEncoderState& enc_state, // Working area for TokenizeCoefficients (per-group!) struct EncCache { // Allocates memory when first called. - void InitOnce() { + Status InitOnce() { if (num_nzeroes.xsize() == 0) { - num_nzeroes = Image3I(kGroupDimInBlocks, kGroupDimInBlocks); + JXL_ASSIGN_OR_RETURN( + num_nzeroes, Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks)); } + return true; } // TokenizeCoefficients Image3I num_nzeroes; @@ -1095,8 +1112,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, group_caches.resize(num_threads); return true; }; + std::atomic<bool> has_error{false}; const auto tokenize_group = [&](const uint32_t group_index, const size_t thread) { + if (has_error) return; // Tokenize coefficients. const Rect rect = shared.frame_dim.BlockGroupRect(group_index); for (size_t idx_pass = 0; idx_pass < enc_state->passes.size(); idx_pass++) { @@ -1107,7 +1126,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, enc_state->coeffs[idx_pass]->PlaneRow(2, group_index, 0).ptr32, }; // Ensure group cache is initialized. - group_caches[thread].InitOnce(); + if (!group_caches[thread].InitOnce()) { + has_error = true; + return; + } TokenizeCoefficients( &shared.coeff_orders[idx_pass * shared.coeff_order_size], rect, ac_rows, shared.ac_strategy, frame_header.chroma_subsampling, @@ -1116,8 +1138,11 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, shared.raw_quant_field, shared.block_ctx_map); } }; - return RunOnPool(pool, 0, shared.frame_dim.num_groups, tokenize_group_init, - tokenize_group, "TokenizeGroup"); + JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_groups, + tokenize_group_init, tokenize_group, + "TokenizeGroup")); + if (has_error) return JXL_FAILURE("TokenizeGroup failed"); + return true; } Status EncodeGlobalDCInfo(const PassesSharedState& shared, BitWriter* writer, @@ -1208,10 +1233,13 @@ Status EncodeGroups(const FrameHeader& frame_header, const size_t num_groups = frame_dim.num_groups; const size_t num_passes = enc_state->progressive_splitter.GetNumPasses(); const size_t global_ac_index = frame_dim.num_dc_groups + 1; - const bool is_small_image = frame_dim.num_groups == 1 && num_passes == 1; - - group_codes->resize( - NumTocEntries(num_groups, frame_dim.num_dc_groups, num_passes)); + const bool is_small_image = + !enc_state->streaming_mode && num_groups == 1 && num_passes == 1; + const size_t num_toc_entries = + is_small_image ? 1 + : AcGroupIndex(0, 0, num_groups, frame_dim.num_dc_groups) + + num_groups * num_passes; + group_codes->resize(num_toc_entries); const auto get_output = [&](const size_t index) { return &(*group_codes)[is_small_image ? 0 : index]; @@ -1308,35 +1336,48 @@ Status EncodeGroups(const FrameHeader& frame_header, enc_state, get_output(global_ac_index), enc_modular, aux_out)); } - std::atomic<int> num_errors{0}; + std::atomic<bool> has_error{false}; const auto process_group = [&](const uint32_t group_index, const size_t thread) { + if (has_error) return; AuxOut* my_aux_out = aux_outs[thread].get(); + size_t ac_group_id = + enc_state->streaming_mode + ? enc_modular->ComputeStreamingAbsoluteAcGroupId( + enc_state->dc_group_index, group_index, shared.frame_dim) + : group_index; + for (size_t i = 0; i < num_passes; i++) { + JXL_DEBUG_V(2, "Encoding AC group %u [abs %" PRIuS "] pass %" PRIuS, + group_index, ac_group_id, i); if (frame_header.encoding == FrameEncoding::kVarDCT) { if (!EncodeGroupTokenizedCoefficients( group_index, i, enc_state->histogram_idx[group_index], *enc_state, ac_group_code(i, group_index), my_aux_out)) { - num_errors.fetch_add(1, std::memory_order_relaxed); + has_error = true; return; } } // Write all modular encoded data (color?, alpha, depth, extra channels) if (!enc_modular->EncodeStream( ac_group_code(i, group_index), my_aux_out, kLayerModularAcGroup, - ModularStreamId::ModularAC(group_index, i))) { - num_errors.fetch_add(1, std::memory_order_relaxed); + ModularStreamId::ModularAC(ac_group_id, i))) { + has_error = true; return; } + JXL_DEBUG_V(2, + "AC group %u [abs %" PRIuS "] pass %" PRIuS + " encoded size is %" PRIuS " bits", + group_index, ac_group_id, i, + ac_group_code(i, group_index)->BitsWritten()); } }; JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, num_groups, resize_aux_outs, process_group, "EncodeGroupCoefficients")); - + if (has_error) return JXL_FAILURE("EncodeGroupCoefficients failed"); // Resizing aux_outs to 0 also Assimilates the array. static_cast<void>(resize_aux_outs(0)); - JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0); for (BitWriter& bw : *group_codes) { BitWriter::Allotment allotment(&bw, 8); @@ -1360,29 +1401,39 @@ Status ComputeEncodingData( PassesSharedState& shared = enc_state.shared; shared.metadata = metadata; if (enc_state.streaming_mode) { - shared.frame_dim.Set(xsize, ysize, /*group_size_shift=*/1, - /*maxhshift=*/0, /*maxvshift=*/0, - /*modular_mode=*/false, /*upsampling=*/1); + shared.frame_dim.Set( + xsize, ysize, frame_header.group_size_shift, + /*max_hshift=*/0, /*max_vshift=*/0, + mutable_frame_header.encoding == FrameEncoding::kModular, + /*upsampling=*/1); } else { shared.frame_dim = frame_header.ToFrameDimensions(); } shared.image_features.patches.SetPassesSharedState(&shared); const FrameDimensions& frame_dim = shared.frame_dim; - shared.ac_strategy = - AcStrategyImage(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.raw_quant_field = - ImageI(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.epf_sharpness = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.cmap = ColorCorrelationMap(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN( + shared.ac_strategy, + AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared.raw_quant_field, + ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared.epf_sharpness, + ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared.cmap, ColorCorrelationMap::Create( + frame_dim.xsize, frame_dim.ysize)); shared.coeff_order_size = kCoeffOrderMaxSize; if (frame_header.encoding == FrameEncoding::kVarDCT) { shared.coeff_orders.resize(frame_header.passes.num_passes * kCoeffOrderMaxSize); } - shared.quant_dc = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.dc_storage = Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks); + JXL_ASSIGN_OR_RETURN(shared.quant_dc, ImageB::Create(frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared.dc_storage, + Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); shared.dc = &shared.dc_storage; const size_t num_extra_channels = metadata->m.num_extra_channels; @@ -1397,16 +1448,19 @@ Status ComputeEncodingData( // computing inverse Gaborish and adaptive quantization map. int max_border = enc_state.streaming_mode ? kBlockDim : 0; Rect frame_rect(0, 0, frame_data.xsize, frame_data.ysize); - Rect patch_rect = Rect(x0, y0, xsize, ysize).Extend(max_border, frame_rect); + Rect frame_area_rect = Rect(x0, y0, xsize, ysize); + Rect patch_rect = frame_area_rect.Extend(max_border, frame_rect); JXL_ASSERT(patch_rect.IsInside(frame_rect)); // Allocating a large enough image avoids a copy when padding. - Image3F color(RoundUpToBlockDim(patch_rect.xsize()), - RoundUpToBlockDim(patch_rect.ysize())); + JXL_ASSIGN_OR_RETURN(Image3F color, + Image3F::Create(RoundUpToBlockDim(patch_rect.xsize()), + RoundUpToBlockDim(patch_rect.ysize()))); color.ShrinkTo(patch_rect.xsize(), patch_rect.ysize()); std::vector<ImageF> extra_channels(num_extra_channels); for (auto& extra_channel : extra_channels) { - extra_channel = jxl::ImageF(patch_rect.xsize(), patch_rect.ysize()); + JXL_ASSIGN_OR_RETURN( + extra_channel, ImageF::Create(patch_rect.xsize(), patch_rect.ysize())); } ImageF* alpha = alpha_eci ? &extra_channels[alpha_idx] : nullptr; ImageF* black = black_eci ? &extra_channels[black_idx] : nullptr; @@ -1432,7 +1486,9 @@ Status ComputeEncodingData( frame_info.ib_needs_color_transform) { if (frame_header.encoding == FrameEncoding::kVarDCT && cparams.speed_tier <= SpeedTier::kKitten) { - linear_storage = Image3F(patch_rect.xsize(), patch_rect.ysize()); + JXL_ASSIGN_OR_RETURN( + linear_storage, + Image3F::Create(patch_rect.xsize(), patch_rect.ysize())); linear = &linear_storage; } ToXYB(c_enc, metadata->m.IntensityTarget(), black, pool, &color, cms, @@ -1446,7 +1502,7 @@ Status ComputeEncodingData( bool lossless = cparams.IsLossless(); if (alpha && !alpha_eci->alpha_associated && frame_header.frame_type == FrameType::kRegularFrame && - !ApplyOverride(cparams.keep_invisible, lossless) && + !ApplyOverride(cparams.keep_invisible, true) && cparams.ec_resampling == cparams.resampling) { // simplify invisible pixels SimplifyInvisible(&color, *alpha, lossless); @@ -1467,15 +1523,17 @@ Status ComputeEncodingData( &mutable_frame_header); } - ComputeNoiseParams(cparams, enc_state.streaming_mode, !!jpeg_data, color, + bool has_jpeg_data = (jpeg_data != nullptr); + ComputeNoiseParams(cparams, enc_state.streaming_mode, has_jpeg_data, color, frame_dim, &mutable_frame_header, &shared.image_features.noise_params); - DownsampleColorChannels(cparams, frame_header, !!jpeg_data, &color); + JXL_RETURN_IF_ERROR( + DownsampleColorChannels(cparams, frame_header, has_jpeg_data, &color)); if (cparams.ec_resampling != 1 && !cparams.already_downsampled) { for (ImageF& ec : extra_channels) { - DownsampleImage(&ec, cparams.ec_resampling); + JXL_ASSIGN_OR_RETURN(ec, DownsampleImage(ec, cparams.ec_resampling)); } } @@ -1505,15 +1563,21 @@ Status ComputeEncodingData( TokenizeAllCoefficients(frame_header, pool, &enc_state)); } + if (cparams.modular_mode || !extra_channels.empty()) { + JXL_RETURN_IF_ERROR(enc_modular.ComputeEncodingData( + frame_header, metadata->m, &color, extra_channels, group_rect, + frame_dim, frame_area_rect, &enc_state, cms, pool, aux_out, + /*do_color=*/cparams.modular_mode)); + } + if (!enc_state.streaming_mode) { - if (cparams.modular_mode || !extra_channels.empty()) { - JXL_RETURN_IF_ERROR(enc_modular.ComputeEncodingData( - frame_header, metadata->m, &color, extra_channels, &enc_state, cms, - pool, aux_out, /*do_color=*/cparams.modular_mode)); + if (cparams.speed_tier < SpeedTier::kTortoise || + !cparams.ModularPartIsLossless() || cparams.responsive || + !cparams.custom_fixed_tree.empty()) { + // Use local trees if doing lossless modular, unless at very slow speeds. + JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool)); + JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool)); } - JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool)); - JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool)); - mutable_frame_header.UpdateFlag(shared.image_features.patches.HasAny(), FrameHeader::kPatches); mutable_frame_header.UpdateFlag(shared.image_features.splines.HasAny(), @@ -1526,6 +1590,7 @@ Status ComputeEncodingData( const size_t group_index = enc_state.dc_group_index; enc_modular.ClearStreamData(ModularStreamId::VarDCTDC(group_index)); enc_modular.ClearStreamData(ModularStreamId::ACMetadata(group_index)); + enc_modular.ClearModularStreamData(); } return true; } @@ -1614,49 +1679,58 @@ bool CanDoStreamingEncoding(const CompressParams& cparams, const FrameInfo& frame_info, const CodecMetadata& metadata, const JxlEncoderChunkedFrameAdapter& frame_data) { - if (frame_data.IsJPEG()) { - return false; - } - if (cparams.noise == Override::kOn || cparams.patches == Override::kOn) { + if (cparams.buffering == 0) { return false; } - if (cparams.progressive_dc != 0 || frame_info.dc_level != 0) { - return false; + if (cparams.buffering == -1) { + if (cparams.speed_tier < SpeedTier::kTortoise) return false; + if (cparams.speed_tier < SpeedTier::kSquirrel && + cparams.butteraugli_distance > 0.5f) { + return false; + } + if (cparams.speed_tier == SpeedTier::kSquirrel && + cparams.butteraugli_distance >= 3.f) { + return false; + } } - if (cparams.resampling != 1 || cparams.ec_resampling != 1) { + + // TODO(veluca): handle different values of `buffering`. + if (frame_data.xsize <= 2048 && frame_data.ysize <= 2048) { return false; } - if (cparams.max_error_mode) { + if (frame_data.IsJPEG()) { return false; } - if (cparams.color_transform != ColorTransform::kXYB) { + if (cparams.noise == Override::kOn || cparams.patches == Override::kOn) { return false; } - if (cparams.modular_mode) { + if (cparams.progressive_dc != 0 || frame_info.dc_level != 0) { return false; } - if (metadata.m.num_extra_channels > 0) { + if (cparams.resampling != 1 || cparams.ec_resampling != 1) { return false; } - if (cparams.buffering == 0) { + if (cparams.max_error_mode) { return false; } - if (cparams.buffering == 1 && frame_data.xsize <= 2048 && - frame_data.ysize <= 2048) { - return false; + if (!cparams.ModularPartIsLossless() || cparams.responsive > 0) { + if (metadata.m.num_extra_channels > 0 || cparams.modular_mode) { + return false; + } } - if (frame_data.xsize <= 256 && frame_data.ysize <= 256) { + ColorTransform ok_color_transform = + cparams.modular_mode ? ColorTransform::kNone : ColorTransform::kXYB; + if (cparams.color_transform != ok_color_transform) { return false; } return true; } void ComputePermutationForStreaming(size_t xsize, size_t ysize, - size_t num_passes, + size_t group_size, size_t num_passes, std::vector<coeff_order_t>& permutation, std::vector<size_t>& dc_group_order) { // This is only valid in VarDCT mode, otherwise there can be group shift. - const size_t group_size = 256; const size_t dc_group_size = group_size * kBlockDim; const size_t group_xsize = DivCeil(xsize, group_size); const size_t group_ysize = DivCeil(ysize, group_size); @@ -1794,7 +1868,7 @@ void RemoveUnusedHistograms(std::vector<uint8_t>& context_map, for (uint8_t histo_idx : inv_remap) { new_codes.encoding_info.emplace_back( std::move(codes.encoding_info[histo_idx])); - new_codes.uint_config.emplace_back(std::move(codes.uint_config[histo_idx])); + new_codes.uint_config.emplace_back(codes.uint_config[histo_idx]); new_codes.encoded_histograms.emplace_back( std::move(codes.encoded_histograms[histo_idx])); } @@ -1864,14 +1938,13 @@ Status EncodeFrameStreaming(const CompressParams& cparams, frame_info, jpeg_data.get(), true, &frame_header)); const size_t num_passes = enc_state.progressive_splitter.GetNumPasses(); - ModularFrameEncoder enc_modular(frame_header, cparams); + ModularFrameEncoder enc_modular(frame_header, cparams, true); std::vector<coeff_order_t> permutation; std::vector<size_t> dc_group_order; - ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, num_passes, - permutation, dc_group_order); + size_t group_size = frame_header.ToFrameDimensions().group_dim; + ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, group_size, + num_passes, permutation, dc_group_order); enc_state.shared.num_histograms = dc_group_order.size(); - // This is only valid in VarDCT mode, otherwise there can be group shift. - size_t group_size = 256; size_t dc_group_size = group_size * kBlockDim; size_t dc_group_xsize = DivCeil(frame_data.xsize, dc_group_size); size_t min_dc_global_size = 0; @@ -1898,8 +1971,7 @@ Status EncodeFrameStreaming(const CompressParams& cparams, enc_state.streaming_mode = true; enc_state.initialize_global_state = (i == 0); enc_state.dc_group_index = dc_ix; - enc_state.histogram_idx = - std::vector<uint8_t>(group_xsize * group_ysize, i); + enc_state.histogram_idx = std::vector<size_t>(group_xsize * group_ysize, i); std::vector<BitWriter> group_codes; JXL_RETURN_IF_ERROR(ComputeEncodingData( cparams, frame_info, metadata, frame_data, jpeg_data.get(), x0, y0, @@ -1931,9 +2003,13 @@ Status EncodeFrameStreaming(const CompressParams& cparams, JXL_RETURN_IF_ERROR( OutputGroups(std::move(group_codes), &group_sizes, output_processor)); } - JXL_RETURN_IF_ERROR(OutputAcGlobal(enc_state, - frame_header.ToFrameDimensions(), - &group_sizes, output_processor, aux_out)); + if (frame_header.encoding == FrameEncoding::kVarDCT) { + JXL_RETURN_IF_ERROR( + OutputAcGlobal(enc_state, frame_header.ToFrameDimensions(), + &group_sizes, output_processor, aux_out)); + } else { + group_sizes.push_back(0); + } JXL_ASSERT(group_sizes.size() == permutation.size()); size_t end_pos = output_processor->CurrentPosition(); output_processor->Seek(start_pos); @@ -1975,7 +2051,7 @@ Status EncodeFrameOneShot(const CompressParams& cparams, frame_info, jpeg_data.get(), false, &frame_header)); const size_t num_passes = enc_state.progressive_splitter.GetNumPasses(); - ModularFrameEncoder enc_modular(frame_header, cparams); + ModularFrameEncoder enc_modular(frame_header, cparams, false); JXL_RETURN_IF_ERROR(ComputeEncodingData( cparams, frame_info, metadata, frame_data, jpeg_data.get(), 0, 0, frame_data.xsize, frame_data.ysize, cms, pool, frame_header, enc_modular, @@ -2008,15 +2084,21 @@ Status EncodeFrame(const CompressParams& cparams_orig, JxlEncoderOutputProcessorWrapper* output_processor, AuxOut* aux_out) { CompressParams cparams = cparams_orig; - if (cparams.speed_tier == SpeedTier::kGlacier && !cparams.IsLossless()) { - cparams.speed_tier = SpeedTier::kTortoise; + if (cparams.speed_tier == SpeedTier::kTectonicPlate && + !cparams.IsLossless()) { + cparams.speed_tier = SpeedTier::kGlacier; + } + // Lightning mode is handled externally, so switch to Thunder mode to handle + // potentially weird cases. + if (cparams.speed_tier == SpeedTier::kLightning) { + cparams.speed_tier = SpeedTier::kThunder; } - if (cparams.speed_tier == SpeedTier::kGlacier) { + if (cparams.speed_tier == SpeedTier::kTectonicPlate) { std::vector<CompressParams> all_params; std::vector<size_t> size; CompressParams cparams_attempt = cparams_orig; - cparams_attempt.speed_tier = SpeedTier::kTortoise; + cparams_attempt.speed_tier = SpeedTier::kGlacier; cparams_attempt.options.max_properties = 4; for (float x : {0.0f, 80.f}) { @@ -2027,8 +2109,9 @@ Status EncodeFrame(const CompressParams& cparams_orig, // modular headers. for (int K : {0, 1 << 10, 70000}) { cparams_attempt.palette_colors = K; - for (int tree_mode : {-1, (int)ModularOptions::TreeMode::kNoWP, - (int)ModularOptions::TreeMode::kDefault}) { + for (int tree_mode : + {-1, static_cast<int>(ModularOptions::TreeMode::kNoWP), + static_cast<int>(ModularOptions::TreeMode::kDefault)}) { if (tree_mode == -1) { // LZ77 only cparams_attempt.options.nb_repeats = 0; @@ -2054,11 +2137,12 @@ Status EncodeFrame(const CompressParams& cparams_orig, size.resize(all_params.size()); - std::atomic<int> num_errors{0}; + std::atomic<bool> has_error{false}; JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, all_params.size(), ThreadPool::NoInit, [&](size_t task, size_t) { + if (has_error) return; std::vector<uint8_t> output(64); uint8_t* next_out = output.data(); size_t avail_out = output.size(); @@ -2066,13 +2150,13 @@ Status EncodeFrame(const CompressParams& cparams_orig, local_output.SetAvailOut(&next_out, &avail_out); if (!EncodeFrame(all_params[task], frame_info, metadata, frame_data, cms, nullptr, &local_output, aux_out)) { - num_errors.fetch_add(1, std::memory_order_relaxed); + has_error = true; return; } size[task] = local_output.CurrentPosition(); }, - "Compress kGlacier")); - JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0); + "Compress kTectonicPlate")); + if (has_error) return JXL_FAILURE("Compress kTectonicPlate failed"); size_t best_idx = 0; for (size_t i = 1; i < all_params.size(); i++) { @@ -2156,7 +2240,7 @@ Status EncodeFrame(const CompressParams& cparams_orig, size_t stride = ib.xsize() * num_channels * 4; color.resize(ib.ysize() * stride); JXL_RETURN_IF_ERROR(ConvertToExternal( - ib, /*bites_per_sample=*/32, /*float_out=*/true, num_channels, + ib, /*bits_per_sample=*/32, /*float_out=*/true, num_channels, JXL_NATIVE_ENDIAN, stride, pool, color.data(), color.size(), /*out_callback=*/{}, Orientation::kIdentity)); JxlPixelFormat format{num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; @@ -2169,7 +2253,7 @@ Status EncodeFrame(const CompressParams& cparams_orig, const ImageF* channel = &ib.extra_channels()[ec]; JXL_RETURN_IF_ERROR(ConvertChannelsToExternal( &channel, 1, - /*bites_per_sample=*/32, + /*bits_per_sample=*/32, /*float_out=*/true, JXL_NATIVE_ENDIAN, ec_stride, pool, ec_data.data(), ec_data.size(), /*out_callback=*/{}, Orientation::kIdentity)); frame_data.SetFromBuffer(1 + ec, ec_data.data(), ec_data.size(), ec_format); |