dec_frame.h (14103B)
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 #ifndef LIB_JXL_DEC_FRAME_H_ 7 #define LIB_JXL_DEC_FRAME_H_ 8 9 #include <jxl/decode.h> 10 #include <jxl/types.h> 11 12 #include <algorithm> 13 #include <cstddef> 14 #include <cstdint> 15 #include <limits> 16 #include <utility> 17 #include <vector> 18 19 #include "lib/jxl/base/common.h" 20 #include "lib/jxl/base/compiler_specific.h" 21 #include "lib/jxl/base/data_parallel.h" 22 #include "lib/jxl/base/status.h" 23 #include "lib/jxl/common.h" // JXL_HIGH_PRECISION 24 #include "lib/jxl/dec_bit_reader.h" 25 #include "lib/jxl/dec_cache.h" 26 #include "lib/jxl/dec_modular.h" 27 #include "lib/jxl/frame_header.h" 28 #include "lib/jxl/image_bundle.h" 29 #include "lib/jxl/image_metadata.h" 30 31 namespace jxl { 32 33 // Decodes a frame. Groups may be processed in parallel by `pool`. 34 // `metadata` is the metadata that applies to all frames of the codestream 35 // `decoded->metadata` must already be set and must match metadata.m. 36 // Used in the encoder to model decoder behaviour, and in tests. 37 Status DecodeFrame(PassesDecoderState* dec_state, ThreadPool* JXL_RESTRICT pool, 38 const uint8_t* next_in, size_t avail_in, 39 FrameHeader* frame_header, ImageBundle* decoded, 40 const CodecMetadata& metadata, 41 bool use_slow_rendering_pipeline = false); 42 43 // TODO(veluca): implement "forced drawing". 44 class FrameDecoder { 45 public: 46 // All parameters must outlive the FrameDecoder. 47 FrameDecoder(PassesDecoderState* dec_state, const CodecMetadata& metadata, 48 ThreadPool* pool, bool use_slow_rendering_pipeline) 49 : dec_state_(dec_state), 50 pool_(pool), 51 frame_header_(&metadata), 52 modular_frame_decoder_(dec_state_->memory_manager()), 53 use_slow_rendering_pipeline_(use_slow_rendering_pipeline) {} 54 55 void SetRenderSpotcolors(bool rsc) { render_spotcolors_ = rsc; } 56 void SetCoalescing(bool c) { coalescing_ = c; } 57 58 // Read FrameHeader and table of contents from the given BitReader. 59 Status InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded, 60 bool is_preview); 61 62 // Checks frame dimensions for their limits, and sets the output 63 // image buffer. 64 Status InitFrameOutput(); 65 66 struct SectionInfo { 67 BitReader* JXL_RESTRICT br; 68 // Logical index of the section, regardless of any permutation that may be 69 // applied in the table of contents or of the physical position in the file. 70 size_t id; 71 // Index of the section in the order of the bytes inside the frame. 72 size_t index; 73 }; 74 75 struct TocEntry { 76 size_t size; 77 size_t id; 78 }; 79 80 enum SectionStatus { 81 // Processed correctly. 82 kDone = 0, 83 // Skipped because other required sections were not yet processed. 84 kSkipped = 1, 85 // Skipped because the section was already processed. 86 kDuplicate = 2, 87 // Only partially decoded: the section will need to be processed again. 88 kPartial = 3, 89 }; 90 91 // Processes `num` sections; each SectionInfo contains the index 92 // of the section and a BitReader that only contains the data of the section. 93 // `section_status` should point to `num` elements, and will be filled with 94 // information about whether each section was processed or not. 95 // A section is a part of the encoded file that is indexed by the TOC. 96 Status ProcessSections(const SectionInfo* sections, size_t num, 97 SectionStatus* section_status); 98 99 // Flushes all the data decoded so far to pixels. 100 Status Flush(); 101 102 // Runs final operations once a frame data is decoded. 103 // Must be called exactly once per frame, after all calls to ProcessSections. 104 Status FinalizeFrame(); 105 106 // Returns dependencies of this frame on reference ids as a bit mask: bits 0-3 107 // indicate reference frame 0-3 for patches and blending, bits 4-7 indicate DC 108 // frames this frame depends on. Only returns a valid result after all calls 109 // to ProcessSections are finished and before FinalizeFrame. 110 int References() const; 111 112 // Returns reference id of storage location where this frame is stored as a 113 // bit flag, or 0 if not stored. 114 // Matches the bit mask used for GetReferences: bits 0-3 indicate it is stored 115 // for patching or blending, bits 4-7 indicate DC frame. 116 // Unlike References, can be ran at any time as 117 // soon as the frame header is known. 118 static int SavedAs(const FrameHeader& header); 119 120 uint64_t SumSectionSizes() const { return section_sizes_sum_; } 121 const std::vector<TocEntry>& Toc() const { return toc_; } 122 123 const FrameHeader& GetFrameHeader() const { return frame_header_; } 124 125 // Returns whether a DC image has been decoded, accessible at low resolution 126 // at passes.shared_storage.dc_storage 127 bool HasDecodedDC() const { return finalized_dc_; } 128 bool HasDecodedAll() const { return toc_.size() == num_sections_done_; } 129 130 size_t NumCompletePasses() const { 131 return *std::min_element(decoded_passes_per_ac_group_.begin(), 132 decoded_passes_per_ac_group_.end()); 133 } 134 135 // If enabled, ProcessSections will stop and return true when the DC 136 // sections have been processed, instead of starting the AC sections. This 137 // will only occur if supported (that is, flushing will produce a valid 138 // 1/8th*1/8th resolution image). The return value of true then does not mean 139 // all sections have been processed, use HasDecodedDC and HasDecodedAll 140 // to check the true finished state. 141 // Returns the progressive detail that will be effective for the frame. 142 JxlProgressiveDetail SetPauseAtProgressive(JxlProgressiveDetail prog_detail) { 143 bool single_section = 144 frame_dim_.num_groups == 1 && frame_header_.passes.num_passes == 1; 145 if (frame_header_.frame_type != kSkipProgressive && 146 // If there's only one group and one pass, there is no separate section 147 // for DC and the entire full resolution image is available at once. 148 !single_section && 149 // If extra channels are encoded with modular without squeeze, they 150 // don't support DC. If the are encoded with squeeze, DC works in theory 151 // but the implementation may not yet correctly support this for Flush. 152 // Therefore, can't correctly pause for a progressive step if there is 153 // an extra channel (including alpha channel) 154 // TODO(firsching): Check if this is still the case. 155 decoded_->metadata()->extra_channel_info.empty() && 156 // DC is not guaranteed to be available in modular mode and may be a 157 // black image. If squeeze is used, it may be available depending on the 158 // current implementation. 159 // TODO(lode): do return DC if it's known that flushing at this point 160 // will produce a valid 1/8th downscaled image with modular encoding. 161 frame_header_.encoding == FrameEncoding::kVarDCT) { 162 progressive_detail_ = prog_detail; 163 } else { 164 progressive_detail_ = JxlProgressiveDetail::kFrames; 165 } 166 if (progressive_detail_ >= JxlProgressiveDetail::kPasses) { 167 for (size_t i = 1; i < frame_header_.passes.num_passes; ++i) { 168 passes_to_pause_.push_back(i); 169 } 170 } else if (progressive_detail_ >= JxlProgressiveDetail::kLastPasses) { 171 for (size_t i = 0; i < frame_header_.passes.num_downsample; ++i) { 172 passes_to_pause_.push_back(frame_header_.passes.last_pass[i] + 1); 173 } 174 // The format does not guarantee that these values are sorted. 175 std::sort(passes_to_pause_.begin(), passes_to_pause_.end()); 176 } 177 return progressive_detail_; 178 } 179 180 size_t NextNumPassesToPause() const { 181 auto it = std::upper_bound(passes_to_pause_.begin(), passes_to_pause_.end(), 182 NumCompletePasses()); 183 return (it != passes_to_pause_.end() ? *it 184 : std::numeric_limits<size_t>::max()); 185 } 186 187 // Sets the pixel callback or image buffer where the pixels will be decoded. 188 // 189 // @param undo_orientation: if true, indicates the frame decoder should apply 190 // the exif orientation to bring the image to the intended display 191 // orientation. 192 void SetImageOutput(const PixelCallback& pixel_callback, void* image_buffer, 193 size_t image_buffer_size, size_t xsize, size_t ysize, 194 JxlPixelFormat format, size_t bits_per_sample, 195 bool unpremul_alpha, bool undo_orientation) const { 196 dec_state_->width = xsize; 197 dec_state_->height = ysize; 198 dec_state_->main_output.format = format; 199 dec_state_->main_output.bits_per_sample = bits_per_sample; 200 dec_state_->main_output.callback = pixel_callback; 201 dec_state_->main_output.buffer = image_buffer; 202 dec_state_->main_output.buffer_size = image_buffer_size; 203 dec_state_->main_output.stride = GetStride(xsize, format); 204 const jxl::ExtraChannelInfo* alpha = 205 decoded_->metadata()->Find(jxl::ExtraChannel::kAlpha); 206 if (alpha && alpha->alpha_associated && unpremul_alpha) { 207 dec_state_->unpremul_alpha = true; 208 } 209 if (undo_orientation) { 210 dec_state_->undo_orientation = decoded_->metadata()->GetOrientation(); 211 if (static_cast<int>(dec_state_->undo_orientation) > 4) { 212 std::swap(dec_state_->width, dec_state_->height); 213 } 214 } 215 dec_state_->extra_output.clear(); 216 #if !JXL_HIGH_PRECISION 217 if (dec_state_->main_output.buffer && 218 (format.data_type == JXL_TYPE_UINT8) && (format.num_channels >= 3) && 219 !dec_state_->unpremul_alpha && 220 (dec_state_->undo_orientation == Orientation::kIdentity) && 221 decoded_->metadata()->xyb_encoded && 222 dec_state_->output_encoding_info.color_encoding.IsSRGB() && 223 dec_state_->output_encoding_info.all_default_opsin && 224 (dec_state_->output_encoding_info.desired_intensity_target == 225 dec_state_->output_encoding_info.orig_intensity_target) && 226 HasFastXYBTosRGB8() && frame_header_.needs_color_transform()) { 227 dec_state_->fast_xyb_srgb8_conversion = true; 228 } 229 #endif 230 } 231 232 void AddExtraChannelOutput(void* buffer, size_t buffer_size, size_t xsize, 233 JxlPixelFormat format, size_t bits_per_sample) { 234 ImageOutput out; 235 out.format = format; 236 out.bits_per_sample = bits_per_sample; 237 out.buffer = buffer; 238 out.buffer_size = buffer_size; 239 out.stride = GetStride(xsize, format); 240 dec_state_->extra_output.push_back(out); 241 } 242 243 private: 244 Status ProcessDCGlobal(BitReader* br); 245 Status ProcessDCGroup(size_t dc_group_id, BitReader* br); 246 Status FinalizeDC(); 247 Status AllocateOutput(); 248 Status ProcessACGlobal(BitReader* br); 249 Status ProcessACGroup(size_t ac_group_id, BitReader* JXL_RESTRICT* br, 250 size_t num_passes, size_t thread, bool force_draw, 251 bool dc_only); 252 void MarkSections(const SectionInfo* sections, size_t num, 253 const SectionStatus* section_status); 254 255 // Allocates storage for parallel decoding using up to `num_threads` threads 256 // of up to `num_tasks` tasks. The value of `thread` passed to 257 // `GetStorageLocation` must be smaller than the `num_threads` value passed 258 // here. The value of `task` passed to `GetStorageLocation` must be smaller 259 // than the value of `num_tasks` passed here. 260 Status PrepareStorage(size_t num_threads, size_t num_tasks) { 261 size_t storage_size = std::min(num_threads, num_tasks); 262 if (storage_size > group_dec_caches_.size()) { 263 group_dec_caches_.resize(storage_size); 264 } 265 use_task_id_ = num_threads > num_tasks; 266 bool use_noise = (frame_header_.flags & FrameHeader::kNoise) != 0; 267 bool use_group_ids = 268 (modular_frame_decoder_.UsesFullImage() && 269 (frame_header_.encoding == FrameEncoding::kVarDCT || use_noise)); 270 if (dec_state_->render_pipeline) { 271 JXL_RETURN_IF_ERROR(dec_state_->render_pipeline->PrepareForThreads( 272 storage_size, use_group_ids)); 273 } 274 return true; 275 } 276 277 size_t GetStorageLocation(size_t thread, size_t task) const { 278 if (use_task_id_) return task; 279 return thread; 280 } 281 282 static size_t BytesPerChannel(JxlDataType data_type) { 283 return (data_type == JXL_TYPE_UINT8 ? 1u 284 : data_type == JXL_TYPE_FLOAT ? 4u 285 : 2u); 286 } 287 288 static size_t GetStride(const size_t xsize, JxlPixelFormat format) { 289 size_t stride = 290 (xsize * BytesPerChannel(format.data_type) * format.num_channels); 291 if (format.align > 1) { 292 stride = (jxl::DivCeil(stride, format.align) * format.align); 293 } 294 return stride; 295 } 296 297 bool HasDcGroupToDecode() const { 298 return std::any_of(decoded_dc_groups_.cbegin(), decoded_dc_groups_.cend(), 299 [](uint8_t ready) { return ready == 0; }); 300 } 301 302 PassesDecoderState* dec_state_; 303 ThreadPool* pool_; 304 std::vector<TocEntry> toc_; 305 uint64_t section_sizes_sum_; 306 // TODO(veluca): figure out the duplication between these and dec_state_. 307 FrameHeader frame_header_; 308 FrameDimensions frame_dim_; 309 ImageBundle* decoded_; 310 ModularFrameDecoder modular_frame_decoder_; 311 bool render_spotcolors_ = true; 312 bool coalescing_ = true; 313 314 std::vector<uint8_t> processed_section_; 315 std::vector<uint8_t> decoded_passes_per_ac_group_; 316 std::vector<uint8_t> decoded_dc_groups_; 317 bool decoded_dc_global_; 318 bool decoded_ac_global_; 319 bool HasEverything() const; 320 bool finalized_dc_ = true; 321 size_t num_sections_done_ = 0; 322 bool is_finalized_ = true; 323 bool allocated_ = false; 324 325 std::vector<GroupDecCache> group_dec_caches_; 326 327 // Whether or not the task id should be used for storage indexing, instead of 328 // the thread id. 329 bool use_task_id_ = false; 330 331 // Testing setting: whether or not to use the slow rendering pipeline. 332 bool use_slow_rendering_pipeline_; 333 334 JxlProgressiveDetail progressive_detail_ = kFrames; 335 // Number of completed passes where section decoding should pause. 336 // Used for progressive details at least kLastPasses. 337 std::vector<int> passes_to_pause_; 338 }; 339 340 } // namespace jxl 341 342 #endif // LIB_JXL_DEC_FRAME_H_