1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
6 #include "lib/jxl/dec_cache.h"
8 #include "lib/jxl/blending.h"
9 #include "lib/jxl/common.h" // JXL_HIGH_PRECISION
10 #include "lib/jxl/render_pipeline/stage_blending.h"
11 #include "lib/jxl/render_pipeline/stage_chroma_upsampling.h"
12 #include "lib/jxl/render_pipeline/stage_cms.h"
13 #include "lib/jxl/render_pipeline/stage_epf.h"
14 #include "lib/jxl/render_pipeline/stage_from_linear.h"
15 #include "lib/jxl/render_pipeline/stage_gaborish.h"
16 #include "lib/jxl/render_pipeline/stage_noise.h"
17 #include "lib/jxl/render_pipeline/stage_patches.h"
18 #include "lib/jxl/render_pipeline/stage_splines.h"
19 #include "lib/jxl/render_pipeline/stage_spot.h"
20 #include "lib/jxl/render_pipeline/stage_to_linear.h"
21 #include "lib/jxl/render_pipeline/stage_tone_mapping.h"
22 #include "lib/jxl/render_pipeline/stage_upsampling.h"
23 #include "lib/jxl/render_pipeline/stage_write.h"
24 #include "lib/jxl/render_pipeline/stage_xyb.h"
25 #include "lib/jxl/render_pipeline/stage_ycbcr.h"
29 Status PassesDecoderState::PreparePipeline(ImageBundle* decoded,
30 PipelineOptions options) {
31 const FrameHeader& frame_header = shared->frame_header;
32 size_t num_c = 3 + frame_header.nonserialized_metadata->m.num_extra_channels;
33 if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) {
37 if (frame_header.CanBeReferenced()) {
38 // Necessary so that SetInputSizes() can allocate output buffers as needed.
39 frame_storage_for_referencing = ImageBundle(decoded->metadata());
42 RenderPipeline::Builder builder(num_c);
44 if (options.use_slow_render_pipeline) {
45 builder.UseSimpleImplementation();
48 if (!frame_header.chroma_subsampling.Is444()) {
49 for (size_t c = 0; c < 3; c++) {
50 if (frame_header.chroma_subsampling.HShift(c) != 0) {
51 builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/true));
53 if (frame_header.chroma_subsampling.VShift(c) != 0) {
54 builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/false));
59 if (frame_header.loop_filter.gab) {
60 builder.AddStage(GetGaborishStage(frame_header.loop_filter));
64 const LoopFilter& lf = frame_header.loop_filter;
65 if (lf.epf_iters >= 3) {
66 builder.AddStage(GetEPFStage(lf, sigma, 0));
68 if (lf.epf_iters >= 1) {
69 builder.AddStage(GetEPFStage(lf, sigma, 1));
71 if (lf.epf_iters >= 2) {
72 builder.AddStage(GetEPFStage(lf, sigma, 2));
76 bool late_ec_upsample = frame_header.upsampling != 1;
77 for (auto ecups : frame_header.extra_channel_upsampling) {
78 if (ecups != frame_header.upsampling) {
79 // If patches are applied, either frame_header.upsampling == 1 or
80 // late_ec_upsample is true.
81 late_ec_upsample = false;
85 if (!late_ec_upsample) {
86 for (size_t ec = 0; ec < frame_header.extra_channel_upsampling.size();
88 if (frame_header.extra_channel_upsampling[ec] != 1) {
89 builder.AddStage(GetUpsamplingStage(
90 frame_header.nonserialized_metadata->transform_data, 3 + ec,
91 CeilLog2Nonzero(frame_header.extra_channel_upsampling[ec])));
96 if ((frame_header.flags & FrameHeader::kPatches) != 0) {
98 GetPatchesStage(&shared->image_features.patches,
99 3 + shared->metadata->m.num_extra_channels));
101 if ((frame_header.flags & FrameHeader::kSplines) != 0) {
102 builder.AddStage(GetSplineStage(&shared->image_features.splines));
105 if (frame_header.upsampling != 1) {
108 (late_ec_upsample ? frame_header.extra_channel_upsampling.size() : 0);
109 for (size_t c = 0; c < nb_channels; c++) {
110 builder.AddStage(GetUpsamplingStage(
111 frame_header.nonserialized_metadata->transform_data, c,
112 CeilLog2Nonzero(frame_header.upsampling)));
115 if (options.render_noise && (frame_header.flags & FrameHeader::kNoise) != 0) {
116 builder.AddStage(GetConvolveNoiseStage(num_c - 3));
117 builder.AddStage(GetAddNoiseStage(shared->image_features.noise_params,
118 shared->cmap, num_c - 3));
120 if (frame_header.dc_level != 0) {
121 builder.AddStage(GetWriteToImage3FStage(
122 &shared_storage.dc_frames[frame_header.dc_level - 1]));
125 if (frame_header.CanBeReferenced() &&
126 frame_header.save_before_color_transform) {
127 builder.AddStage(GetWriteToImageBundleStage(
128 &frame_storage_for_referencing, output_encoding_info.color_encoding));
131 bool has_alpha = false;
133 for (size_t i = 0; i < decoded->metadata()->extra_channel_info.size(); i++) {
134 if (decoded->metadata()->extra_channel_info[i].type ==
135 ExtraChannel::kAlpha) {
142 if (fast_xyb_srgb8_conversion) {
143 #if !JXL_HIGH_PRECISION
144 JXL_ASSERT(!NeedsBlending(this));
145 JXL_ASSERT(!frame_header.CanBeReferenced() ||
146 frame_header.save_before_color_transform);
147 JXL_ASSERT(!options.render_spotcolors ||
148 !decoded->metadata()->Find(ExtraChannel::kSpotColor));
149 bool is_rgba = (main_output.format.num_channels == 4);
150 uint8_t* rgb_output = reinterpret_cast<uint8_t*>(main_output.buffer);
151 builder.AddStage(GetFastXYBTosRGB8Stage(rgb_output, main_output.stride,
152 width, height, is_rgba, has_alpha,
157 if (frame_header.color_transform == ColorTransform::kYCbCr) {
158 builder.AddStage(GetYCbCrStage());
159 } else if (frame_header.color_transform == ColorTransform::kXYB) {
160 builder.AddStage(GetXYBStage(output_encoding_info));
161 if (output_encoding_info.color_encoding.GetColorSpace() !=
165 } // Nothing to do for kNone.
167 if (options.coalescing && NeedsBlending(this)) {
169 builder.AddStage(GetFromLinearStage(output_encoding_info));
173 GetBlendingStage(this, output_encoding_info.color_encoding));
176 if (options.coalescing && frame_header.CanBeReferenced() &&
177 !frame_header.save_before_color_transform) {
179 builder.AddStage(GetFromLinearStage(output_encoding_info));
182 builder.AddStage(GetWriteToImageBundleStage(
183 &frame_storage_for_referencing, output_encoding_info.color_encoding));
186 if (options.render_spotcolors &&
187 frame_header.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) {
188 for (size_t i = 0; i < decoded->metadata()->extra_channel_info.size();
190 // Don't use Find() because there may be multiple spot color channels.
191 const ExtraChannelInfo& eci =
192 decoded->metadata()->extra_channel_info[i];
193 if (eci.type == ExtraChannel::kSpotColor) {
194 builder.AddStage(GetSpotColorStage(3 + i, eci.spot_color));
199 auto tone_mapping_stage = GetToneMappingStage(output_encoding_info);
200 if (tone_mapping_stage) {
202 auto to_linear_stage = GetToLinearStage(output_encoding_info);
203 if (!to_linear_stage) {
204 if (!output_encoding_info.cms_set) {
205 return JXL_FAILURE("Cannot tonemap this colorspace without a CMS");
207 auto cms_stage = GetCmsStage(output_encoding_info);
209 builder.AddStage(std::move(cms_stage));
212 builder.AddStage(std::move(to_linear_stage));
216 builder.AddStage(std::move(tone_mapping_stage));
220 const size_t channels_src =
221 (output_encoding_info.orig_color_encoding.IsCMYK()
223 : output_encoding_info.orig_color_encoding.Channels());
224 const size_t channels_dst =
225 output_encoding_info.color_encoding.Channels();
226 bool mixing_color_and_grey = (channels_dst != channels_src);
227 if ((output_encoding_info.color_encoding_is_original) ||
228 (!output_encoding_info.cms_set) || mixing_color_and_grey) {
229 // in those cases we only need a linear stage in other cases we attempt
230 // to obtain an cms stage: the cases are
231 // - output_encoding_info.color_encoding_is_original: no cms stage
232 // needed because it would be a no-op
233 // - !output_encoding_info.cms_set: can't use the cms, so no point in
234 // trying to add a cms stage
235 // - mixing_color_and_grey: cms stage can't handle that
236 // TODO(firsching): remove "mixing_color_and_grey" condition after
237 // adding support for greyscale to cms stage.
238 builder.AddStage(GetFromLinearStage(output_encoding_info));
240 if (!output_encoding_info.linear_color_encoding.CreateICC()) {
241 return JXL_FAILURE("Failed to create ICC");
243 auto cms_stage = GetCmsStage(output_encoding_info);
245 builder.AddStage(std::move(cms_stage));
251 if (main_output.callback.IsPresent() || main_output.buffer) {
252 builder.AddStage(GetWriteToOutputStage(main_output, width, height,
253 has_alpha, unpremul_alpha, alpha_c,
254 undo_orientation, extra_output));
256 builder.AddStage(GetWriteToImageBundleStage(
257 decoded, output_encoding_info.color_encoding));
260 render_pipeline = std::move(builder).Finalize(shared->frame_dim);
261 return render_pipeline->IsInitialized();