tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dec_cache.cc (13571B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/dec_cache.h"
      7 
      8 #include <jxl/memory_manager.h>
      9 
     10 #include <algorithm>
     11 
     12 #include "lib/jxl/ac_strategy.h"
     13 #include "lib/jxl/base/status.h"
     14 #include "lib/jxl/blending.h"
     15 #include "lib/jxl/coeff_order.h"
     16 #include "lib/jxl/common.h"  // JXL_HIGH_PRECISION
     17 #include "lib/jxl/memory_manager_internal.h"
     18 #include "lib/jxl/render_pipeline/stage_blending.h"
     19 #include "lib/jxl/render_pipeline/stage_chroma_upsampling.h"
     20 #include "lib/jxl/render_pipeline/stage_cms.h"
     21 #include "lib/jxl/render_pipeline/stage_epf.h"
     22 #include "lib/jxl/render_pipeline/stage_from_linear.h"
     23 #include "lib/jxl/render_pipeline/stage_gaborish.h"
     24 #include "lib/jxl/render_pipeline/stage_noise.h"
     25 #include "lib/jxl/render_pipeline/stage_patches.h"
     26 #include "lib/jxl/render_pipeline/stage_splines.h"
     27 #include "lib/jxl/render_pipeline/stage_spot.h"
     28 #include "lib/jxl/render_pipeline/stage_to_linear.h"
     29 #include "lib/jxl/render_pipeline/stage_tone_mapping.h"
     30 #include "lib/jxl/render_pipeline/stage_upsampling.h"
     31 #include "lib/jxl/render_pipeline/stage_write.h"
     32 #include "lib/jxl/render_pipeline/stage_xyb.h"
     33 #include "lib/jxl/render_pipeline/stage_ycbcr.h"
     34 
     35 namespace jxl {
     36 
     37 Status GroupDecCache::InitOnce(JxlMemoryManager* memory_manager,
     38                               size_t num_passes, size_t used_acs) {
     39  for (size_t i = 0; i < num_passes; i++) {
     40    if (num_nzeroes[i].xsize() == 0) {
     41      // Allocate enough for a whole group - partial groups on the
     42      // right/bottom border just use a subset. The valid size is passed via
     43      // Rect.
     44 
     45      JXL_ASSIGN_OR_RETURN(num_nzeroes[i],
     46                           Image3I::Create(memory_manager, kGroupDimInBlocks,
     47                                           kGroupDimInBlocks));
     48    }
     49  }
     50  size_t max_block_area = 0;
     51 
     52  for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
     53    AcStrategy acs = AcStrategy::FromRawStrategy(o);
     54    if ((used_acs & (1 << o)) == 0) continue;
     55    size_t area =
     56        acs.covered_blocks_x() * acs.covered_blocks_y() * kDCTBlockSize;
     57    max_block_area = std::max(area, max_block_area);
     58  }
     59 
     60  if (max_block_area > max_block_area_) {
     61    max_block_area_ = max_block_area;
     62    // We need 3x float blocks for dequantized coefficients and 1x for scratch
     63    // space for transforms.
     64    JXL_ASSIGN_OR_RETURN(
     65        float_memory_,
     66        AlignedMemory::Create(memory_manager,
     67                              max_block_area_ * 7 * sizeof(float)));
     68    // We need 3x int32 or int16 blocks for quantized coefficients.
     69    JXL_ASSIGN_OR_RETURN(
     70        int32_memory_,
     71        AlignedMemory::Create(memory_manager,
     72                              max_block_area_ * 3 * sizeof(int32_t)));
     73    JXL_ASSIGN_OR_RETURN(
     74        int16_memory_,
     75        AlignedMemory::Create(memory_manager,
     76                              max_block_area_ * 3 * sizeof(int16_t)));
     77  }
     78 
     79  dec_group_block = float_memory_.address<float>();
     80  scratch_space = dec_group_block + max_block_area_ * 3;
     81  dec_group_qblock = int32_memory_.address<int32_t>();
     82  dec_group_qblock16 = int16_memory_.address<int16_t>();
     83  return true;
     84 }
     85 
     86 // Initialize the decoder state after all of DC is decoded.
     87 Status PassesDecoderState::InitForAC(size_t num_passes, ThreadPool* pool) {
     88  shared_storage.coeff_order_size = 0;
     89  for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
     90    if (((1 << o) & used_acs) == 0) continue;
     91    uint8_t ord = kStrategyOrder[o];
     92    shared_storage.coeff_order_size =
     93        std::max(kCoeffOrderOffset[3 * (ord + 1)] * kDCTBlockSize,
     94                 shared_storage.coeff_order_size);
     95  }
     96  size_t sz = num_passes * shared_storage.coeff_order_size;
     97  if (sz > shared_storage.coeff_orders.size()) {
     98    shared_storage.coeff_orders.resize(sz);
     99  }
    100  return true;
    101 }
    102 
    103 Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header,
    104                                           const ImageMetadata* metadata,
    105                                           ImageBundle* decoded,
    106                                           PipelineOptions options) {
    107  JxlMemoryManager* memory_manager = this->memory_manager();
    108  size_t num_c = 3 + frame_header.nonserialized_metadata->m.num_extra_channels;
    109  bool render_noise =
    110      (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0);
    111  size_t num_tmp_c = render_noise ? 3 : 0;
    112 
    113  if (frame_header.CanBeReferenced()) {
    114    // Necessary so that SetInputSizes() can allocate output buffers as needed.
    115    frame_storage_for_referencing = ImageBundle(memory_manager, metadata);
    116  }
    117 
    118  RenderPipeline::Builder builder(memory_manager, num_c + num_tmp_c);
    119 
    120  if (options.use_slow_render_pipeline) {
    121    builder.UseSimpleImplementation();
    122  }
    123 
    124  if (!frame_header.chroma_subsampling.Is444()) {
    125    for (size_t c = 0; c < 3; c++) {
    126      if (frame_header.chroma_subsampling.HShift(c) != 0) {
    127        JXL_RETURN_IF_ERROR(
    128            builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/true)));
    129      }
    130      if (frame_header.chroma_subsampling.VShift(c) != 0) {
    131        JXL_RETURN_IF_ERROR(builder.AddStage(
    132            GetChromaUpsamplingStage(c, /*horizontal=*/false)));
    133      }
    134    }
    135  }
    136 
    137  if (frame_header.loop_filter.gab) {
    138    JXL_RETURN_IF_ERROR(
    139        builder.AddStage(GetGaborishStage(frame_header.loop_filter)));
    140  }
    141 
    142  {
    143    const LoopFilter& lf = frame_header.loop_filter;
    144    if (lf.epf_iters >= 3) {
    145      JXL_RETURN_IF_ERROR(
    146          builder.AddStage(GetEPFStage(lf, sigma, EpfStage::Zero)));
    147    }
    148    if (lf.epf_iters >= 1) {
    149      JXL_RETURN_IF_ERROR(
    150          builder.AddStage(GetEPFStage(lf, sigma, EpfStage::One)));
    151    }
    152    if (lf.epf_iters >= 2) {
    153      JXL_RETURN_IF_ERROR(
    154          builder.AddStage(GetEPFStage(lf, sigma, EpfStage::Two)));
    155    }
    156  }
    157 
    158  bool late_ec_upsample = frame_header.upsampling != 1;
    159  for (auto ecups : frame_header.extra_channel_upsampling) {
    160    if (ecups != frame_header.upsampling) {
    161      // If patches are applied, either frame_header.upsampling == 1 or
    162      // late_ec_upsample is true.
    163      late_ec_upsample = false;
    164    }
    165  }
    166 
    167  if (!late_ec_upsample) {
    168    for (size_t ec = 0; ec < frame_header.extra_channel_upsampling.size();
    169         ec++) {
    170      if (frame_header.extra_channel_upsampling[ec] != 1) {
    171        JXL_RETURN_IF_ERROR(builder.AddStage(GetUpsamplingStage(
    172            frame_header.nonserialized_metadata->transform_data, 3 + ec,
    173            CeilLog2Nonzero(frame_header.extra_channel_upsampling[ec]))));
    174      }
    175    }
    176  }
    177 
    178  if ((frame_header.flags & FrameHeader::kPatches) != 0) {
    179    JXL_RETURN_IF_ERROR(builder.AddStage(GetPatchesStage(
    180        &shared->image_features.patches,
    181        &frame_header.nonserialized_metadata->m.extra_channel_info)));
    182  }
    183  if ((frame_header.flags & FrameHeader::kSplines) != 0) {
    184    JXL_RETURN_IF_ERROR(
    185        builder.AddStage(GetSplineStage(&shared->image_features.splines)));
    186  }
    187 
    188  if (frame_header.upsampling != 1) {
    189    size_t nb_channels =
    190        3 +
    191        (late_ec_upsample ? frame_header.extra_channel_upsampling.size() : 0);
    192    for (size_t c = 0; c < nb_channels; c++) {
    193      JXL_RETURN_IF_ERROR(builder.AddStage(GetUpsamplingStage(
    194          frame_header.nonserialized_metadata->transform_data, c,
    195          CeilLog2Nonzero(frame_header.upsampling))));
    196    }
    197  }
    198  if (render_noise) {
    199    JXL_RETURN_IF_ERROR(builder.AddStage(GetConvolveNoiseStage(num_c)));
    200    JXL_RETURN_IF_ERROR(builder.AddStage(GetAddNoiseStage(
    201        shared->image_features.noise_params, shared->cmap.base(), num_c)));
    202  }
    203  if (frame_header.dc_level != 0) {
    204    JXL_RETURN_IF_ERROR(builder.AddStage(GetWriteToImage3FStage(
    205        memory_manager, &shared_storage.dc_frames[frame_header.dc_level - 1])));
    206  }
    207 
    208  if (frame_header.CanBeReferenced() &&
    209      frame_header.save_before_color_transform) {
    210    JXL_RETURN_IF_ERROR(builder.AddStage(GetWriteToImageBundleStage(
    211        &frame_storage_for_referencing, output_encoding_info)));
    212  }
    213 
    214  bool has_alpha = false;
    215  size_t alpha_c = 0;
    216  for (size_t i = 0; i < metadata->extra_channel_info.size(); i++) {
    217    if (metadata->extra_channel_info[i].type == ExtraChannel::kAlpha) {
    218      has_alpha = true;
    219      alpha_c = 3 + i;
    220      break;
    221    }
    222  }
    223 
    224  if (fast_xyb_srgb8_conversion) {
    225 #if !JXL_HIGH_PRECISION
    226    JXL_ENSURE(!NeedsBlending(frame_header));
    227    JXL_ENSURE(!frame_header.CanBeReferenced() ||
    228               frame_header.save_before_color_transform);
    229    JXL_ENSURE(!options.render_spotcolors ||
    230               !metadata->Find(ExtraChannel::kSpotColor));
    231    bool is_rgba = (main_output.format.num_channels == 4);
    232    uint8_t* rgb_output = reinterpret_cast<uint8_t*>(main_output.buffer);
    233    JXL_RETURN_IF_ERROR(builder.AddStage(
    234        GetFastXYBTosRGB8Stage(rgb_output, main_output.stride, width, height,
    235                               is_rgba, has_alpha, alpha_c)));
    236 #endif
    237  } else {
    238    bool linear = false;
    239    if (frame_header.color_transform == ColorTransform::kYCbCr) {
    240      JXL_RETURN_IF_ERROR(builder.AddStage(GetYCbCrStage()));
    241    } else if (frame_header.color_transform == ColorTransform::kXYB) {
    242      JXL_RETURN_IF_ERROR(builder.AddStage(GetXYBStage(output_encoding_info)));
    243      if (output_encoding_info.color_encoding.GetColorSpace() !=
    244          ColorSpace::kXYB) {
    245        linear = true;
    246      }
    247    }  // Nothing to do for kNone.
    248 
    249    if (options.coalescing && NeedsBlending(frame_header)) {
    250      if (linear) {
    251        JXL_RETURN_IF_ERROR(
    252            builder.AddStage(GetFromLinearStage(output_encoding_info)));
    253        linear = false;
    254      }
    255      JXL_RETURN_IF_ERROR(builder.AddStage(GetBlendingStage(
    256          frame_header, this, output_encoding_info.color_encoding)));
    257    }
    258 
    259    if (options.coalescing && frame_header.CanBeReferenced() &&
    260        !frame_header.save_before_color_transform) {
    261      if (linear) {
    262        JXL_RETURN_IF_ERROR(
    263            builder.AddStage(GetFromLinearStage(output_encoding_info)));
    264        linear = false;
    265      }
    266      JXL_RETURN_IF_ERROR(builder.AddStage(GetWriteToImageBundleStage(
    267          &frame_storage_for_referencing, output_encoding_info)));
    268    }
    269 
    270    if (options.render_spotcolors &&
    271        frame_header.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) {
    272      for (size_t i = 0; i < metadata->extra_channel_info.size(); i++) {
    273        // Don't use Find() because there may be multiple spot color channels.
    274        const ExtraChannelInfo& eci = metadata->extra_channel_info[i];
    275        if (eci.type == ExtraChannel::kSpotColor) {
    276          JXL_RETURN_IF_ERROR(
    277              builder.AddStage(GetSpotColorStage(i, eci.spot_color)));
    278        }
    279      }
    280    }
    281 
    282    auto tone_mapping_stage = GetToneMappingStage(output_encoding_info);
    283    if (tone_mapping_stage) {
    284      if (!linear) {
    285        auto to_linear_stage = GetToLinearStage(output_encoding_info);
    286        if (!to_linear_stage) {
    287          if (!output_encoding_info.cms_set) {
    288            return JXL_FAILURE("Cannot tonemap this colorspace without a CMS");
    289          }
    290          auto cms_stage = GetCmsStage(output_encoding_info);
    291          if (cms_stage) {
    292            JXL_RETURN_IF_ERROR(builder.AddStage(std::move(cms_stage)));
    293          }
    294        } else {
    295          JXL_RETURN_IF_ERROR(builder.AddStage(std::move(to_linear_stage)));
    296        }
    297        linear = true;
    298      }
    299      JXL_RETURN_IF_ERROR(builder.AddStage(std::move(tone_mapping_stage)));
    300    }
    301 
    302    if (linear) {
    303      const size_t channels_src =
    304          (output_encoding_info.orig_color_encoding.IsCMYK()
    305               ? 4
    306               : output_encoding_info.orig_color_encoding.Channels());
    307      const size_t channels_dst =
    308          output_encoding_info.color_encoding.Channels();
    309      bool mixing_color_and_grey = (channels_dst != channels_src);
    310      if ((output_encoding_info.color_encoding_is_original) ||
    311          (!output_encoding_info.cms_set) || mixing_color_and_grey) {
    312        // in those cases we only need a linear stage in other cases we attempt
    313        // to obtain a cms stage: the cases are
    314        // - output_encoding_info.color_encoding_is_original: no cms stage
    315        // needed because it would be a no-op
    316        // - !output_encoding_info.cms_set: can't use the cms, so no point in
    317        // trying to add a cms stage
    318        // - mixing_color_and_grey: cms stage can't handle that
    319        // TODO(firsching): remove "mixing_color_and_grey" condition after
    320        // adding support for greyscale to cms stage.
    321        JXL_RETURN_IF_ERROR(
    322            builder.AddStage(GetFromLinearStage(output_encoding_info)));
    323      } else {
    324        if (!output_encoding_info.linear_color_encoding.CreateICC()) {
    325          return JXL_FAILURE("Failed to create ICC");
    326        }
    327        auto cms_stage = GetCmsStage(output_encoding_info);
    328        if (cms_stage) {
    329          JXL_RETURN_IF_ERROR(builder.AddStage(std::move(cms_stage)));
    330        }
    331      }
    332      linear = false;
    333    }
    334    (void)linear;
    335 
    336    if (main_output.callback.IsPresent() || main_output.buffer) {
    337      JXL_RETURN_IF_ERROR(builder.AddStage(GetWriteToOutputStage(
    338          main_output, width, height, has_alpha, unpremul_alpha, alpha_c,
    339          undo_orientation, extra_output, memory_manager)));
    340    } else {
    341      JXL_RETURN_IF_ERROR(builder.AddStage(
    342          GetWriteToImageBundleStage(decoded, output_encoding_info)));
    343    }
    344  }
    345  JXL_ASSIGN_OR_RETURN(render_pipeline,
    346                       std::move(builder).Finalize(shared->frame_dim));
    347  return render_pipeline->IsInitialized();
    348 }
    349 
    350 }  // namespace jxl