Imported Upstream version 0.9.0
[platform/upstream/libjxl.git] / lib / jxl / dec_cache.cc
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/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"
26
27 namespace jxl {
28
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) {
34     num_c += 3;
35   }
36
37   if (frame_header.CanBeReferenced()) {
38     // Necessary so that SetInputSizes() can allocate output buffers as needed.
39     frame_storage_for_referencing = ImageBundle(decoded->metadata());
40   }
41
42   RenderPipeline::Builder builder(num_c);
43
44   if (options.use_slow_render_pipeline) {
45     builder.UseSimpleImplementation();
46   }
47
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));
52       }
53       if (frame_header.chroma_subsampling.VShift(c) != 0) {
54         builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/false));
55       }
56     }
57   }
58
59   if (frame_header.loop_filter.gab) {
60     builder.AddStage(GetGaborishStage(frame_header.loop_filter));
61   }
62
63   {
64     const LoopFilter& lf = frame_header.loop_filter;
65     if (lf.epf_iters >= 3) {
66       builder.AddStage(GetEPFStage(lf, sigma, 0));
67     }
68     if (lf.epf_iters >= 1) {
69       builder.AddStage(GetEPFStage(lf, sigma, 1));
70     }
71     if (lf.epf_iters >= 2) {
72       builder.AddStage(GetEPFStage(lf, sigma, 2));
73     }
74   }
75
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;
82     }
83   }
84
85   if (!late_ec_upsample) {
86     for (size_t ec = 0; ec < frame_header.extra_channel_upsampling.size();
87          ec++) {
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])));
92       }
93     }
94   }
95
96   if ((frame_header.flags & FrameHeader::kPatches) != 0) {
97     builder.AddStage(
98         GetPatchesStage(&shared->image_features.patches,
99                         3 + shared->metadata->m.num_extra_channels));
100   }
101   if ((frame_header.flags & FrameHeader::kSplines) != 0) {
102     builder.AddStage(GetSplineStage(&shared->image_features.splines));
103   }
104
105   if (frame_header.upsampling != 1) {
106     size_t nb_channels =
107         3 +
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)));
113     }
114   }
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));
119   }
120   if (frame_header.dc_level != 0) {
121     builder.AddStage(GetWriteToImage3FStage(
122         &shared_storage.dc_frames[frame_header.dc_level - 1]));
123   }
124
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));
129   }
130
131   bool has_alpha = false;
132   size_t alpha_c = 0;
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) {
136       has_alpha = true;
137       alpha_c = 3 + i;
138       break;
139     }
140   }
141
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,
153                                             alpha_c));
154 #endif
155   } else {
156     bool linear = false;
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() !=
162           ColorSpace::kXYB) {
163         linear = true;
164       }
165     }  // Nothing to do for kNone.
166
167     if (options.coalescing && NeedsBlending(this)) {
168       if (linear) {
169         builder.AddStage(GetFromLinearStage(output_encoding_info));
170         linear = false;
171       }
172       builder.AddStage(
173           GetBlendingStage(this, output_encoding_info.color_encoding));
174     }
175
176     if (options.coalescing && frame_header.CanBeReferenced() &&
177         !frame_header.save_before_color_transform) {
178       if (linear) {
179         builder.AddStage(GetFromLinearStage(output_encoding_info));
180         linear = false;
181       }
182       builder.AddStage(GetWriteToImageBundleStage(
183           &frame_storage_for_referencing, output_encoding_info.color_encoding));
184     }
185
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();
189            i++) {
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));
195         }
196       }
197     }
198
199     auto tone_mapping_stage = GetToneMappingStage(output_encoding_info);
200     if (tone_mapping_stage) {
201       if (!linear) {
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");
206           }
207           auto cms_stage = GetCmsStage(output_encoding_info);
208           if (cms_stage) {
209             builder.AddStage(std::move(cms_stage));
210           }
211         } else {
212           builder.AddStage(std::move(to_linear_stage));
213         }
214         linear = true;
215       }
216       builder.AddStage(std::move(tone_mapping_stage));
217     }
218
219     if (linear) {
220       const size_t channels_src =
221           (output_encoding_info.orig_color_encoding.IsCMYK()
222                ? 4
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));
239       } else {
240         if (!output_encoding_info.linear_color_encoding.CreateICC()) {
241           return JXL_FAILURE("Failed to create ICC");
242         }
243         auto cms_stage = GetCmsStage(output_encoding_info);
244         if (cms_stage) {
245           builder.AddStage(std::move(cms_stage));
246         }
247       }
248       linear = false;
249     }
250
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));
255     } else {
256       builder.AddStage(GetWriteToImageBundleStage(
257           decoded, output_encoding_info.color_encoding));
258     }
259   }
260   render_pipeline = std::move(builder).Finalize(shared->frame_dim);
261   return render_pipeline->IsInitialized();
262 }
263
264 }  // namespace jxl