1use crate::backend::Backend;
6use crate::context::DecoderContext;
7use crate::entropy::block::{decode_block_with_activity, BlockActivity, CoefficientBlock};
8use crate::entropy::huffman::{HuffmanTable, PreparedHuffmanTableId, PreparedHuffmanTables};
9use crate::entropy::progressive::{
10 decode_progressive, decode_progressive_dct_blocks, PreparedProgressiveComponentPlan,
11 PreparedProgressivePlan, PreparedProgressiveScan, PreparedProgressiveScanComponent,
12 COMPONENT_IMAGE_METADATA_BYTES,
13};
14use crate::entropy::sequential::{
15 decode_scan_baseline, decode_scan_baseline_rgb, decode_scan_fast_rgb_444,
16 decode_scan_fast_tile_rgb, decode_scan_fast_tile_rgb_region,
17 decode_scan_fast_tile_rgb_region_scaled, fast_tile_region_first_decode_mcu, finish_scan,
18 stripe_region_layout, FastTileRegionScaledRequest, PreparedComponentPlan, PreparedDecodePlan,
19 ResolvedPreparedComponentPlan,
20};
21use crate::entropy::ZIGZAG;
22use crate::error::{JpegError, MarkerKind, Warning};
23use crate::info::{
24 ColorSpace, DecodeOptions, DownscaleFactor, Info, OutputFormat, Rect, RestartIndex,
25 RestartSegment, SofKind,
26};
27use crate::internal::bit_reader::BitReader;
28use crate::internal::checkpoint::{checkpoint_before_mcu, CpuCheckpointCache, DeviceCheckpoint};
29use crate::internal::scratch::ScratchPool;
30use crate::lossless::{lossless_predict, LosslessSample};
31use crate::output::{
32 validate_buffer, Gray8Writer, InterleavedRgbWriter, OutputWriter, Rgb8Writer, Rgba8Writer,
33};
34use crate::parse::header::{parse_info, ParsedHeader};
35use crate::profile::{emit_jpeg_profile_fields, jpeg_profile_stages_enabled, ProfileField};
36use crate::segment::PreparedJpeg;
37use crate::JpegCodec;
38use alloc::vec::Vec;
39use core::cell::RefCell;
40pub use j2k_core::TileBatchOptions;
41use j2k_core::{
42 CompressedTransferSyntax, DecodeOutcome as CoreDecodeOutcome, DecodeRowsError,
43 DecoderContext as CoreDecoderContext, Downscale, ImageCodec, ImageDecode, ImageDecodeRows,
44 PixelFormat, RowSink, TileBatchDecode,
45};
46use std::sync::Mutex;
47use std::time::{Duration, Instant};
48
49pub(crate) const DEFAULT_MAX_DECODE_BYTES: usize = 512 * 1024 * 1024;
50pub(super) const MAX_DECODE_SCAN_WARNINGS: usize = 1;
51const CPU_ROI_CHECKPOINT_CADENCE_MCUS: u32 = 1024;
52const CPU_ROI_CHECKPOINT_MIN_TARGET_MCUS: u32 = 4096;
53
54std::thread_local! {
55 static DEFAULT_SCRATCH: RefCell<ScratchPool> = RefCell::new(ScratchPool::new());
56 static DEFAULT_CONTEXT: RefCell<DecoderContext> = RefCell::new(DecoderContext::new());
57}
58
59mod view;
60pub use self::view::JpegView;
61mod allocation;
62mod output_format;
63use self::output_format::{
64 allocate_output_buffer_with_live_budget, checked_output_geometry, downscale_profile_name,
65 jpeg_downscale, output_format_from_parts, output_format_profile_name, scaled_dimensions,
66 scaled_rect_covering,
67};
68mod extended12;
69use self::extended12::{lossless_color_sampling, upsample_h2v1_sample_at, upsample_h2v2_rows_at};
70mod lossless_helpers;
71pub(crate) use self::lossless_helpers::restart_index_allocation_bytes;
72use self::lossless_helpers::{
73 decode_lossless_color_sample, decode_lossless_sampled_color_mcu, emit_decode_scan_profile,
74 lossless_predictor_gray_rows, lossless_predictor_value, lossless_predictor_value_u16,
75 restart_index_for_stream, validate_lossless_color_plan, write_lossless_color16_sampled_output,
76 write_lossless_color8_sampled_output, LosslessColorIntoSample, LosslessColorPlanes,
77 LosslessColorRowSample, LosslessRestartTracker, LosslessSampledColorPlanesMut,
78 LosslessSampledMcu,
79};
80mod color_convert;
81use self::color_convert::{
82 convert_ycbcr16_to_rgb16_in_place, convert_ycbcr8_to_rgb8_in_place, copy_gray16_scaled_rect,
83 copy_gray8_scaled_rect, copy_rgb16_to_rgba16, copy_ycbcr16_row_to_rgb16,
84 copy_ycbcr8_row_to_rgb8,
85};
86mod warning_ownership;
87use self::warning_ownership::{merged_warnings, try_clone_warnings};
88mod core_traits;
89use self::core_traits::{CroppedWriter, ProgressiveDownscaleWriter};
90mod lossless_region;
91use self::lossless_region::{LosslessRegionRequest, LosslessRgbRegionFallback, LosslessRgbaAlpha};
92mod scratch;
93use self::scratch::{
94 additional_decode_scratch_bytes, checked_scratch_len, checked_usize_product,
95 compute_decode_scratch_bytes, compute_extended12_planes_scratch_bytes,
96 compute_lossless_scratch_bytes, lossless_sampled_plane_layout, LosslessSampledPlaneLayout,
97};
98mod sink_writer;
99pub(crate) use self::sink_writer::SinkWriter;
100mod plan;
101use self::plan::find_component_index;
102mod routing;
103mod rows;
104mod sequential;
105mod tile;
106pub(crate) use self::tile::{
107 decode_prepared_jpeg_tile_rgb8_in_context, planned_jpeg_tile_decode_live_bytes,
108 PlannedJpegTileDecode,
109};
110pub use self::tile::{
111 decode_prepared_jpeg_tiles_rgb8, decode_tile_into, decode_tile_into_in_context,
112 decode_tile_into_in_context_with_options, decode_tile_region_into_in_context,
113 decode_tile_region_into_in_context_with_options, decode_tile_region_scaled_into_in_context,
114 decode_tile_region_scaled_into_in_context_with_options, decode_tile_scaled_into_in_context,
115 decode_tile_scaled_into_in_context_with_options, decode_tiles_into,
116 decode_tiles_into_with_options, decode_tiles_region_scaled_into,
117 decode_tiles_region_scaled_into_with_options, decode_tiles_scaled_into,
118 decode_tiles_scaled_into_with_options,
119};
120mod lossless_render;
121
122#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct DecodeOutcome {
129 pub decoded: Rect,
133 pub warnings: Vec<Warning>,
136}
137
138impl From<DecodeOutcome> for CoreDecodeOutcome<Warning> {
139 fn from(outcome: DecodeOutcome) -> Self {
140 Self {
141 decoded: outcome.decoded.into(),
142 warnings: outcome.warnings,
143 }
144 }
145}
146
147#[derive(Clone, Copy, Debug, Eq, PartialEq)]
152pub struct DecodeRequest {
153 pub fmt: PixelFormat,
155 pub region: Option<Rect>,
157 pub scale: Downscale,
159}
160
161impl DecodeRequest {
162 #[must_use]
164 pub const fn full(fmt: PixelFormat) -> Self {
165 Self {
166 fmt,
167 region: None,
168 scale: Downscale::None,
169 }
170 }
171
172 #[must_use]
174 pub const fn scaled(fmt: PixelFormat, scale: Downscale) -> Self {
175 Self {
176 fmt,
177 region: None,
178 scale,
179 }
180 }
181
182 #[must_use]
184 pub const fn region(fmt: PixelFormat, region: Rect) -> Self {
185 Self {
186 fmt,
187 region: Some(region),
188 scale: Downscale::None,
189 }
190 }
191
192 #[must_use]
194 pub const fn region_scaled(fmt: PixelFormat, region: Rect, scale: Downscale) -> Self {
195 Self {
196 fmt,
197 region: Some(region),
198 scale,
199 }
200 }
201}
202
203pub type TileDecodeJob<'i, 'o> = j2k_core::TileDecodeJob<'i, 'o>;
205
206pub struct TileDecodeOutput<'o> {
208 pub out: &'o mut [u8],
210 pub stride: usize,
212 pub fmt: PixelFormat,
214}
215
216pub struct PreparedJpegTileJob<'i, 'o> {
219 pub input: PreparedJpeg<'i>,
221 pub out: &'o mut [u8],
223 pub stride: usize,
225 pub options: DecodeOptions,
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
231pub struct DecodedTile {
232 pub dimensions: (u32, u32),
234 pub decoded: Rect,
236 pub warnings: Vec<Warning>,
238}
239
240pub type TileScaledDecodeJob<'i, 'o> = j2k_core::TileScaledDecodeJob<'i, 'o>;
242
243pub type TileRegionScaledDecodeJob<'i, 'o> = j2k_core::TileRegionScaledDecodeJob<'i, 'o>;
246
247pub type TileBatchError = j2k_core::BatchDecodeError<JpegError>;
251
252pub type PreparedTileBatchError = j2k_core::BatchInfrastructureError;
257
258pub trait ComponentRowWriter {
261 fn write_gray_row(&mut self, y: u32, gray_row: &[u8]) -> Result<(), JpegError>;
267
268 fn write_ycbcr_row(
274 &mut self,
275 y: u32,
276 y_row: &[u8],
277 cb_row: &[u8],
278 cr_row: &[u8],
279 ) -> Result<(), JpegError>;
280
281 fn write_rgb_row(
287 &mut self,
288 y: u32,
289 r_row: &[u8],
290 g_row: &[u8],
291 b_row: &[u8],
292 ) -> Result<(), JpegError>;
293}
294
295#[derive(Debug)]
298pub struct Decoder<'a> {
299 pub(crate) bytes: &'a [u8],
300 pub(crate) info: Info,
301 pub(crate) warnings: Vec<Warning>,
302 pub(crate) backend: Backend,
303 pub(crate) plan: PreparedDecodePlan,
304 pub(crate) progressive_plan: Option<PreparedProgressivePlan>,
305 lossless_plan: Option<PreparedLosslessPlan>,
306 pub(crate) cpu_entropy_checkpoints: Mutex<CpuCheckpointCache>,
307}
308
309struct PreparedDecoderMetadata {
310 info: Info,
311 warnings: Vec<Warning>,
312 plan: PreparedDecodePlan,
313 progressive_plan: Option<PreparedProgressivePlan>,
314 lossless_plan: Option<PreparedLosslessPlan>,
315}
316
317#[derive(Debug, Clone)]
318struct PreparedLosslessPlan {
319 predictor: u8,
320 bit_depth: u8,
321 dc_table: PreparedHuffmanTableId,
322 dimensions: (u32, u32),
323 scan_offset: usize,
324}
325
326#[derive(Clone, Copy, Debug, Eq, PartialEq)]
327enum LosslessColorSampling {
328 S444,
329 S422,
330 S420,
331}
332
333impl<'a> Decoder<'a> {
334 pub fn inspect(input: &'a [u8]) -> Result<Info, JpegError> {
342 let info = parse_info(input)?;
343 Ok(info)
344 }
345
346 fn from_bytes_with_options(input: &'a [u8], options: DecodeOptions) -> Result<Self, JpegError> {
347 let view = JpegView::parse_with_options(input, options)?;
348 DEFAULT_CONTEXT.with(|ctx| Self::from_view_in_context(view, &mut ctx.borrow_mut()))
349 }
350
351 pub fn new(input: &'a [u8]) -> Result<Self, JpegError> {
363 Self::from_bytes_with_options(input, DecodeOptions::default())
364 }
365
366 pub fn from_view(view: JpegView<'a>) -> Result<Self, JpegError> {
373 DEFAULT_CONTEXT.with(|ctx| Self::from_view_in_context(view, &mut ctx.borrow_mut()))
374 }
375
376 pub(crate) fn from_view_with_external_live(
383 view: JpegView<'a>,
384 external_live_bytes: usize,
385 ) -> Result<Self, JpegError> {
386 DEFAULT_CONTEXT.with(|ctx| {
387 Self::from_view_in_context_with_external_live(
388 view,
389 &mut ctx.borrow_mut(),
390 external_live_bytes,
391 )
392 })
393 }
394
395 pub fn from_view_in_context(
403 view: JpegView<'a>,
404 ctx: &mut DecoderContext,
405 ) -> Result<Self, JpegError> {
406 Self::from_view_in_context_with_external_live(view, ctx, 0)
407 }
408
409 fn from_view_in_context_with_external_live(
410 view: JpegView<'a>,
411 ctx: &mut DecoderContext,
412 external_live_bytes: usize,
413 ) -> Result<Self, JpegError> {
414 let JpegView {
415 bytes,
416 header,
417 info,
418 options,
419 } = view;
420 let backend = Backend::detect();
421 let PreparedDecoderMetadata {
422 info,
423 warnings,
424 plan,
425 progressive_plan,
426 lossless_plan,
427 } = Self::prepare_header_with_external_live(
428 header,
429 info,
430 options,
431 bytes,
432 ctx,
433 external_live_bytes,
434 )?;
435 Ok(Self {
436 bytes,
437 info,
438 warnings,
439 backend,
440 plan,
441 progressive_plan,
442 lossless_plan,
443 cpu_entropy_checkpoints: Mutex::new(CpuCheckpointCache::default()),
444 })
445 }
446
447 pub fn info(&self) -> &Info {
449 &self.info
450 }
451
452 pub(crate) fn restart_index(&self) -> Result<Option<RestartIndex>, JpegError> {
457 restart_index_for_stream(
458 self.bytes,
459 Some(self.plan.scan_offset),
460 &self.info,
461 self.plan.restart_interval,
462 )
463 }
464}
465
466#[cfg(test)]
467mod tests;