dec_cache.cc (10183B)
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 "lib/jxl/base/status.h" 9 #include "lib/jxl/blending.h" 10 #include "lib/jxl/common.h" // JXL_HIGH_PRECISION 11 #include "lib/jxl/render_pipeline/stage_blending.h" 12 #include "lib/jxl/render_pipeline/stage_chroma_upsampling.h" 13 #include "lib/jxl/render_pipeline/stage_cms.h" 14 #include "lib/jxl/render_pipeline/stage_epf.h" 15 #include "lib/jxl/render_pipeline/stage_from_linear.h" 16 #include "lib/jxl/render_pipeline/stage_gaborish.h" 17 #include "lib/jxl/render_pipeline/stage_noise.h" 18 #include "lib/jxl/render_pipeline/stage_patches.h" 19 #include "lib/jxl/render_pipeline/stage_splines.h" 20 #include "lib/jxl/render_pipeline/stage_spot.h" 21 #include "lib/jxl/render_pipeline/stage_to_linear.h" 22 #include "lib/jxl/render_pipeline/stage_tone_mapping.h" 23 #include "lib/jxl/render_pipeline/stage_upsampling.h" 24 #include "lib/jxl/render_pipeline/stage_write.h" 25 #include "lib/jxl/render_pipeline/stage_xyb.h" 26 #include "lib/jxl/render_pipeline/stage_ycbcr.h" 27 28 namespace jxl { 29 30 Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, 31 ImageBundle* decoded, 32 PipelineOptions options) { 33 size_t num_c = 3 + frame_header.nonserialized_metadata->m.num_extra_channels; 34 if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) { 35 num_c += 3; 36 } 37 38 if (frame_header.CanBeReferenced()) { 39 // Necessary so that SetInputSizes() can allocate output buffers as needed. 40 frame_storage_for_referencing = ImageBundle(decoded->metadata()); 41 } 42 43 RenderPipeline::Builder builder(num_c); 44 45 if (options.use_slow_render_pipeline) { 46 builder.UseSimpleImplementation(); 47 } 48 49 if (!frame_header.chroma_subsampling.Is444()) { 50 for (size_t c = 0; c < 3; c++) { 51 if (frame_header.chroma_subsampling.HShift(c) != 0) { 52 builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/true)); 53 } 54 if (frame_header.chroma_subsampling.VShift(c) != 0) { 55 builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/false)); 56 } 57 } 58 } 59 60 if (frame_header.loop_filter.gab) { 61 builder.AddStage(GetGaborishStage(frame_header.loop_filter)); 62 } 63 64 { 65 const LoopFilter& lf = frame_header.loop_filter; 66 if (lf.epf_iters >= 3) { 67 builder.AddStage(GetEPFStage(lf, sigma, 0)); 68 } 69 if (lf.epf_iters >= 1) { 70 builder.AddStage(GetEPFStage(lf, sigma, 1)); 71 } 72 if (lf.epf_iters >= 2) { 73 builder.AddStage(GetEPFStage(lf, sigma, 2)); 74 } 75 } 76 77 bool late_ec_upsample = frame_header.upsampling != 1; 78 for (auto ecups : frame_header.extra_channel_upsampling) { 79 if (ecups != frame_header.upsampling) { 80 // If patches are applied, either frame_header.upsampling == 1 or 81 // late_ec_upsample is true. 82 late_ec_upsample = false; 83 } 84 } 85 86 if (!late_ec_upsample) { 87 for (size_t ec = 0; ec < frame_header.extra_channel_upsampling.size(); 88 ec++) { 89 if (frame_header.extra_channel_upsampling[ec] != 1) { 90 builder.AddStage(GetUpsamplingStage( 91 frame_header.nonserialized_metadata->transform_data, 3 + ec, 92 CeilLog2Nonzero(frame_header.extra_channel_upsampling[ec]))); 93 } 94 } 95 } 96 97 if ((frame_header.flags & FrameHeader::kPatches) != 0) { 98 builder.AddStage( 99 GetPatchesStage(&shared->image_features.patches, 100 3 + shared->metadata->m.num_extra_channels)); 101 } 102 if ((frame_header.flags & FrameHeader::kSplines) != 0) { 103 builder.AddStage(GetSplineStage(&shared->image_features.splines)); 104 } 105 106 if (frame_header.upsampling != 1) { 107 size_t nb_channels = 108 3 + 109 (late_ec_upsample ? frame_header.extra_channel_upsampling.size() : 0); 110 for (size_t c = 0; c < nb_channels; c++) { 111 builder.AddStage(GetUpsamplingStage( 112 frame_header.nonserialized_metadata->transform_data, c, 113 CeilLog2Nonzero(frame_header.upsampling))); 114 } 115 } 116 if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) { 117 builder.AddStage(GetConvolveNoiseStage(num_c - 3)); 118 builder.AddStage(GetAddNoiseStage(shared->image_features.noise_params, 119 shared->cmap, num_c - 3)); 120 } 121 if (frame_header.dc_level != 0) { 122 builder.AddStage(GetWriteToImage3FStage( 123 &shared_storage.dc_frames[frame_header.dc_level - 1])); 124 } 125 126 if (frame_header.CanBeReferenced() && 127 frame_header.save_before_color_transform) { 128 builder.AddStage(GetWriteToImageBundleStage( 129 &frame_storage_for_referencing, output_encoding_info.color_encoding)); 130 } 131 132 bool has_alpha = false; 133 size_t alpha_c = 0; 134 for (size_t i = 0; i < decoded->metadata()->extra_channel_info.size(); i++) { 135 if (decoded->metadata()->extra_channel_info[i].type == 136 ExtraChannel::kAlpha) { 137 has_alpha = true; 138 alpha_c = 3 + i; 139 break; 140 } 141 } 142 143 if (fast_xyb_srgb8_conversion) { 144 #if !JXL_HIGH_PRECISION 145 JXL_ASSERT(!NeedsBlending(frame_header)); 146 JXL_ASSERT(!frame_header.CanBeReferenced() || 147 frame_header.save_before_color_transform); 148 JXL_ASSERT(!options.render_spotcolors || 149 !decoded->metadata()->Find(ExtraChannel::kSpotColor)); 150 bool is_rgba = (main_output.format.num_channels == 4); 151 uint8_t* rgb_output = reinterpret_cast<uint8_t*>(main_output.buffer); 152 builder.AddStage(GetFastXYBTosRGB8Stage(rgb_output, main_output.stride, 153 width, height, is_rgba, has_alpha, 154 alpha_c)); 155 #endif 156 } else { 157 bool linear = false; 158 if (frame_header.color_transform == ColorTransform::kYCbCr) { 159 builder.AddStage(GetYCbCrStage()); 160 } else if (frame_header.color_transform == ColorTransform::kXYB) { 161 builder.AddStage(GetXYBStage(output_encoding_info)); 162 if (output_encoding_info.color_encoding.GetColorSpace() != 163 ColorSpace::kXYB) { 164 linear = true; 165 } 166 } // Nothing to do for kNone. 167 168 if (options.coalescing && NeedsBlending(frame_header)) { 169 if (linear) { 170 builder.AddStage(GetFromLinearStage(output_encoding_info)); 171 linear = false; 172 } 173 builder.AddStage(GetBlendingStage(frame_header, this, 174 output_encoding_info.color_encoding)); 175 } 176 177 if (options.coalescing && frame_header.CanBeReferenced() && 178 !frame_header.save_before_color_transform) { 179 if (linear) { 180 builder.AddStage(GetFromLinearStage(output_encoding_info)); 181 linear = false; 182 } 183 builder.AddStage(GetWriteToImageBundleStage( 184 &frame_storage_for_referencing, output_encoding_info.color_encoding)); 185 } 186 187 if (options.render_spotcolors && 188 frame_header.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) { 189 for (size_t i = 0; i < decoded->metadata()->extra_channel_info.size(); 190 i++) { 191 // Don't use Find() because there may be multiple spot color channels. 192 const ExtraChannelInfo& eci = 193 decoded->metadata()->extra_channel_info[i]; 194 if (eci.type == ExtraChannel::kSpotColor) { 195 builder.AddStage(GetSpotColorStage(3 + i, eci.spot_color)); 196 } 197 } 198 } 199 200 auto tone_mapping_stage = GetToneMappingStage(output_encoding_info); 201 if (tone_mapping_stage) { 202 if (!linear) { 203 auto to_linear_stage = GetToLinearStage(output_encoding_info); 204 if (!to_linear_stage) { 205 if (!output_encoding_info.cms_set) { 206 return JXL_FAILURE("Cannot tonemap this colorspace without a CMS"); 207 } 208 auto cms_stage = GetCmsStage(output_encoding_info); 209 if (cms_stage) { 210 builder.AddStage(std::move(cms_stage)); 211 } 212 } else { 213 builder.AddStage(std::move(to_linear_stage)); 214 } 215 linear = true; 216 } 217 builder.AddStage(std::move(tone_mapping_stage)); 218 } 219 220 if (linear) { 221 const size_t channels_src = 222 (output_encoding_info.orig_color_encoding.IsCMYK() 223 ? 4 224 : output_encoding_info.orig_color_encoding.Channels()); 225 const size_t channels_dst = 226 output_encoding_info.color_encoding.Channels(); 227 bool mixing_color_and_grey = (channels_dst != channels_src); 228 if ((output_encoding_info.color_encoding_is_original) || 229 (!output_encoding_info.cms_set) || mixing_color_and_grey) { 230 // in those cases we only need a linear stage in other cases we attempt 231 // to obtain an cms stage: the cases are 232 // - output_encoding_info.color_encoding_is_original: no cms stage 233 // needed because it would be a no-op 234 // - !output_encoding_info.cms_set: can't use the cms, so no point in 235 // trying to add a cms stage 236 // - mixing_color_and_grey: cms stage can't handle that 237 // TODO(firsching): remove "mixing_color_and_grey" condition after 238 // adding support for greyscale to cms stage. 239 builder.AddStage(GetFromLinearStage(output_encoding_info)); 240 } else { 241 if (!output_encoding_info.linear_color_encoding.CreateICC()) { 242 return JXL_FAILURE("Failed to create ICC"); 243 } 244 auto cms_stage = GetCmsStage(output_encoding_info); 245 if (cms_stage) { 246 builder.AddStage(std::move(cms_stage)); 247 } 248 } 249 linear = false; 250 } 251 (void)linear; 252 253 if (main_output.callback.IsPresent() || main_output.buffer) { 254 builder.AddStage(GetWriteToOutputStage(main_output, width, height, 255 has_alpha, unpremul_alpha, alpha_c, 256 undo_orientation, extra_output)); 257 } else { 258 builder.AddStage(GetWriteToImageBundleStage( 259 decoded, output_encoding_info.color_encoding)); 260 } 261 } 262 JXL_ASSIGN_OR_RETURN(render_pipeline, 263 std::move(builder).Finalize(shared->frame_dim)); 264 return render_pipeline->IsInitialized(); 265 } 266 267 } // namespace jxl