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, Downscale,
43 ImageCodec, ImageDecode, ImageDecodeRows, PixelFormat, RowSink, TileBatchDecode,
44};
45use std::sync::Mutex;
46use std::time::{Duration, Instant};
47
48pub(crate) const DEFAULT_MAX_DECODE_BYTES: usize = 512 * 1024 * 1024;
49pub(super) const MAX_DECODE_SCAN_WARNINGS: usize = 1;
50const CPU_ROI_CHECKPOINT_CADENCE_MCUS: u32 = 1024;
51const CPU_ROI_CHECKPOINT_MIN_TARGET_MCUS: u32 = 4096;
52
53std::thread_local! {
54 static DEFAULT_SCRATCH: RefCell<ScratchPool> = RefCell::new(ScratchPool::new());
55 static DEFAULT_CONTEXT: RefCell<DecoderContext> = RefCell::new(DecoderContext::new());
56}
57
58mod view;
59pub use self::view::JpegView;
60mod allocation;
61mod output_format;
62use self::output_format::{
63 allocate_output_buffer_with_live_budget, checked_output_geometry, downscale_profile_name,
64 jpeg_downscale, output_format_from_parts, output_format_profile_name, scaled_dimensions,
65 scaled_rect_covering,
66};
67mod extended12;
68use self::extended12::{lossless_color_sampling, upsample_h2v1_sample_at, upsample_h2v2_rows_at};
69mod lossless_helpers;
70pub(crate) use self::lossless_helpers::restart_index_allocation_bytes;
71use self::lossless_helpers::{
72 decode_lossless_color_sample, decode_lossless_sampled_color_mcu, emit_decode_scan_profile,
73 lossless_predictor_gray_rows, lossless_predictor_value, lossless_predictor_value_u16,
74 restart_index_for_stream, validate_lossless_color_plan, write_lossless_color16_sampled_output,
75 write_lossless_color8_sampled_output, LosslessColorIntoSample, LosslessColorPlanes,
76 LosslessColorRowSample, LosslessRestartTracker, LosslessSampledColorPlanesMut,
77 LosslessSampledMcu,
78};
79mod color_convert;
80use self::color_convert::{
81 convert_ycbcr16_to_rgb16_in_place, convert_ycbcr8_to_rgb8_in_place, copy_gray16_scaled_rect,
82 copy_gray8_scaled_rect, copy_rgb16_to_rgba16, copy_ycbcr16_row_to_rgb16,
83 copy_ycbcr8_row_to_rgb8,
84};
85mod warning_ownership;
86use self::warning_ownership::{merged_warnings, try_clone_warnings};
87mod core_traits;
88use self::core_traits::{CroppedWriter, ProgressiveDownscaleWriter};
89mod lossless_region;
90use self::lossless_region::{LosslessRegionRequest, LosslessRgbRegionFallback, LosslessRgbaAlpha};
91mod scratch;
92use self::scratch::{
93 additional_decode_scratch_bytes, checked_scratch_len, checked_usize_product,
94 compute_decode_scratch_bytes, compute_extended12_planes_scratch_bytes,
95 compute_lossless_scratch_bytes, lossless_sampled_plane_layout, LosslessSampledPlaneLayout,
96};
97mod sink_writer;
98pub(crate) use self::sink_writer::SinkWriter;
99mod plan;
100use self::plan::find_component_index;
101mod routing;
102mod rows;
103mod sequential;
104mod tile;
105pub(crate) use self::tile::{
106 decode_prepared_jpeg_tile_rgb8_in_context, planned_jpeg_tile_decode_live_bytes,
107 PlannedJpegTileDecode,
108};
109pub use self::tile::{
110 decode_prepared_jpeg_tiles_rgb8, decode_tile_into, decode_tile_into_in_context,
111 decode_tile_into_in_context_with_options, decode_tile_region_into_in_context,
112 decode_tile_region_into_in_context_with_options, decode_tile_region_scaled_into_in_context,
113 decode_tile_region_scaled_into_in_context_with_options, decode_tile_scaled_into_in_context,
114 decode_tile_scaled_into_in_context_with_options, decode_tiles_into,
115 decode_tiles_into_with_options, decode_tiles_region_scaled_into,
116 decode_tiles_region_scaled_into_with_options, decode_tiles_scaled_into,
117 decode_tiles_scaled_into_with_options,
118};
119mod lossless_render;
120
121#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct DecodeOutcome {
128 pub decoded: Rect,
132 pub warnings: Vec<Warning>,
135}
136
137impl From<DecodeOutcome> for CoreDecodeOutcome<Warning> {
138 fn from(outcome: DecodeOutcome) -> Self {
139 Self {
140 decoded: outcome.decoded.into(),
141 warnings: outcome.warnings,
142 }
143 }
144}
145
146#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151pub struct DecodeRequest {
152 pub fmt: PixelFormat,
154 pub region: Option<Rect>,
156 pub scale: Downscale,
158}
159
160impl DecodeRequest {
161 #[must_use]
163 pub const fn full(fmt: PixelFormat) -> Self {
164 Self {
165 fmt,
166 region: None,
167 scale: Downscale::None,
168 }
169 }
170
171 #[must_use]
173 pub const fn scaled(fmt: PixelFormat, scale: Downscale) -> Self {
174 Self {
175 fmt,
176 region: None,
177 scale,
178 }
179 }
180
181 #[must_use]
183 pub const fn region(fmt: PixelFormat, region: Rect) -> Self {
184 Self {
185 fmt,
186 region: Some(region),
187 scale: Downscale::None,
188 }
189 }
190
191 #[must_use]
193 pub const fn region_scaled(fmt: PixelFormat, region: Rect, scale: Downscale) -> Self {
194 Self {
195 fmt,
196 region: Some(region),
197 scale,
198 }
199 }
200}
201
202pub type TileDecodeJob<'i, 'o> = j2k_core::TileDecodeJob<'i, 'o>;
204
205pub struct TileDecodeOutput<'o> {
207 pub out: &'o mut [u8],
209 pub stride: usize,
211 pub fmt: PixelFormat,
213}
214
215pub struct PreparedJpegTileJob<'i, 'o> {
218 pub input: PreparedJpeg<'i>,
220 pub out: &'o mut [u8],
222 pub stride: usize,
224 pub options: DecodeOptions,
226}
227
228#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct DecodedTile {
231 pub dimensions: (u32, u32),
233 pub decoded: Rect,
235 pub warnings: Vec<Warning>,
237}
238
239pub type TileScaledDecodeJob<'i, 'o> = j2k_core::TileScaledDecodeJob<'i, 'o>;
241
242pub type TileRegionScaledDecodeJob<'i, 'o> = j2k_core::TileRegionScaledDecodeJob<'i, 'o>;
245
246pub type TileBatchError = j2k_core::BatchDecodeError<JpegError>;
250
251pub type PreparedTileBatchError = j2k_core::BatchInfrastructureError;
256
257pub trait ComponentRowWriter {
260 fn write_gray_row(&mut self, y: u32, gray_row: &[u8]) -> Result<(), JpegError>;
266
267 fn write_ycbcr_row(
273 &mut self,
274 y: u32,
275 y_row: &[u8],
276 cb_row: &[u8],
277 cr_row: &[u8],
278 ) -> Result<(), JpegError>;
279
280 fn write_rgb_row(
286 &mut self,
287 y: u32,
288 r_row: &[u8],
289 g_row: &[u8],
290 b_row: &[u8],
291 ) -> Result<(), JpegError>;
292}
293
294#[derive(Debug)]
297pub struct Decoder<'a> {
298 pub(crate) bytes: &'a [u8],
299 pub(crate) info: Info,
300 pub(crate) warnings: Vec<Warning>,
301 pub(crate) backend: Backend,
302 pub(crate) plan: PreparedDecodePlan,
303 pub(crate) progressive_plan: Option<PreparedProgressivePlan>,
304 lossless_plan: Option<PreparedLosslessPlan>,
305 pub(crate) cpu_entropy_checkpoints: Mutex<CpuCheckpointCache>,
306}
307
308struct PreparedDecoderMetadata {
309 info: Info,
310 warnings: Vec<Warning>,
311 plan: PreparedDecodePlan,
312 progressive_plan: Option<PreparedProgressivePlan>,
313 lossless_plan: Option<PreparedLosslessPlan>,
314}
315
316#[derive(Debug, Clone)]
317struct PreparedLosslessPlan {
318 predictor: u8,
319 bit_depth: u8,
320 dc_table: PreparedHuffmanTableId,
321 dimensions: (u32, u32),
322 scan_offset: usize,
323}
324
325#[derive(Clone, Copy, Debug, Eq, PartialEq)]
326enum LosslessColorSampling {
327 S444,
328 S422,
329 S420,
330}
331
332impl<'a> Decoder<'a> {
333 pub fn inspect(input: &'a [u8]) -> Result<Info, JpegError> {
341 let info = parse_info(input)?;
342 Ok(info)
343 }
344
345 fn from_bytes_with_options(input: &'a [u8], options: DecodeOptions) -> Result<Self, JpegError> {
346 let view = JpegView::parse_with_options(input, options)?;
347 DEFAULT_CONTEXT.with(|ctx| Self::from_view_in_context(view, &mut ctx.borrow_mut()))
348 }
349
350 pub fn new(input: &'a [u8]) -> Result<Self, JpegError> {
362 Self::from_bytes_with_options(input, DecodeOptions::default())
363 }
364
365 pub fn from_view(view: JpegView<'a>) -> Result<Self, JpegError> {
372 DEFAULT_CONTEXT.with(|ctx| Self::from_view_in_context(view, &mut ctx.borrow_mut()))
373 }
374
375 pub(crate) fn from_view_with_external_live(
382 view: JpegView<'a>,
383 external_live_bytes: usize,
384 ) -> Result<Self, JpegError> {
385 DEFAULT_CONTEXT.with(|ctx| {
386 Self::from_view_in_context_with_external_live(
387 view,
388 &mut ctx.borrow_mut(),
389 external_live_bytes,
390 )
391 })
392 }
393
394 pub fn from_view_in_context(
402 view: JpegView<'a>,
403 ctx: &mut DecoderContext,
404 ) -> Result<Self, JpegError> {
405 Self::from_view_in_context_with_external_live(view, ctx, 0)
406 }
407
408 fn from_view_in_context_with_external_live(
409 view: JpegView<'a>,
410 ctx: &mut DecoderContext,
411 external_live_bytes: usize,
412 ) -> Result<Self, JpegError> {
413 let JpegView {
414 bytes,
415 header,
416 info,
417 options,
418 } = view;
419 let backend = Backend::detect();
420 let PreparedDecoderMetadata {
421 info,
422 warnings,
423 plan,
424 progressive_plan,
425 lossless_plan,
426 } = Self::prepare_header_with_external_live(
427 header,
428 info,
429 options,
430 bytes,
431 ctx,
432 external_live_bytes,
433 )?;
434 Ok(Self {
435 bytes,
436 info,
437 warnings,
438 backend,
439 plan,
440 progressive_plan,
441 lossless_plan,
442 cpu_entropy_checkpoints: Mutex::new(CpuCheckpointCache::default()),
443 })
444 }
445
446 pub fn info(&self) -> &Info {
448 &self.info
449 }
450
451 pub(crate) fn restart_index(&self) -> Result<Option<RestartIndex>, JpegError> {
456 restart_index_for_stream(
457 self.bytes,
458 Some(self.plan.scan_offset),
459 &self.info,
460 self.plan.restart_interval,
461 )
462 }
463}
464
465#[cfg(test)]
466mod tests;