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