zune_jpeg/
misc.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software;
5 *
6 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
7 */
8
9//!Miscellaneous stuff
10#![allow(dead_code)]
11
12use alloc::format;
13use core::cmp::max;
14use core::fmt;
15
16use zune_core::bytestream::{ZByteReader, ZReaderTrait};
17use zune_core::colorspace::ColorSpace;
18use zune_core::log::trace;
19
20use crate::components::{ComponentID, SampleRatios};
21use crate::errors::DecodeErrors;
22use crate::huffman::HuffmanTable;
23use crate::JpegDecoder;
24
25/// Start of baseline DCT Huffman coding
26
27pub const START_OF_FRAME_BASE: u16 = 0xffc0;
28
29/// Start of another frame
30
31pub const START_OF_FRAME_EXT_SEQ: u16 = 0xffc1;
32
33/// Start of progressive DCT encoding
34
35pub const START_OF_FRAME_PROG_DCT: u16 = 0xffc2;
36
37/// Start of Lossless sequential Huffman coding
38
39pub const START_OF_FRAME_LOS_SEQ: u16 = 0xffc3;
40
41/// Start of extended sequential DCT arithmetic coding
42
43pub const START_OF_FRAME_EXT_AR: u16 = 0xffc9;
44
45/// Start of Progressive DCT arithmetic coding
46
47pub const START_OF_FRAME_PROG_DCT_AR: u16 = 0xffca;
48
49/// Start of Lossless sequential Arithmetic coding
50
51pub const START_OF_FRAME_LOS_SEQ_AR: u16 = 0xffcb;
52
53/// Undo run length encoding of coefficients by placing them in natural order
54#[rustfmt::skip]
55pub const UN_ZIGZAG: [usize; 64 + 16] = [
56     0,  1,  8, 16,  9,  2,  3, 10,
57    17, 24, 32, 25, 18, 11,  4,  5,
58    12, 19, 26, 33, 40, 48, 41, 34,
59    27, 20, 13,  6,  7, 14, 21, 28,
60    35, 42, 49, 56, 57, 50, 43, 36,
61    29, 22, 15, 23, 30, 37, 44, 51,
62    58, 59, 52, 45, 38, 31, 39, 46,
63    53, 60, 61, 54, 47, 55, 62, 63,
64    // Prevent overflowing
65    63, 63, 63, 63, 63, 63, 63, 63,
66    63, 63, 63, 63, 63, 63, 63, 63
67];
68
69/// Align data to a 16 byte boundary
70#[repr(align(16))]
71#[derive(Clone)]
72
73pub struct Aligned16<T: ?Sized>(pub T);
74
75impl<T> Default for Aligned16<T>
76where
77    T: Default
78{
79    fn default() -> Self {
80        Aligned16(T::default())
81    }
82}
83
84/// Align data to a 32 byte boundary
85#[repr(align(32))]
86#[derive(Clone)]
87pub struct Aligned32<T: ?Sized>(pub T);
88
89impl<T> Default for Aligned32<T>
90where
91    T: Default
92{
93    fn default() -> Self {
94        Aligned32(T::default())
95    }
96}
97
98/// Markers that identify different Start of Image markers
99/// They identify the type of encoding and whether the file use lossy(DCT) or
100/// lossless compression and whether we use Huffman or arithmetic coding schemes
101#[derive(Eq, PartialEq, Copy, Clone)]
102#[allow(clippy::upper_case_acronyms)]
103pub enum SOFMarkers {
104    /// Baseline DCT markers
105    BaselineDct,
106    /// SOF_1 Extended sequential DCT,Huffman coding
107    ExtendedSequentialHuffman,
108    /// Progressive DCT, Huffman coding
109    ProgressiveDctHuffman,
110    /// Lossless (sequential), huffman coding,
111    LosslessHuffman,
112    /// Extended sequential DEC, arithmetic coding
113    ExtendedSequentialDctArithmetic,
114    /// Progressive DCT, arithmetic coding,
115    ProgressiveDctArithmetic,
116    /// Lossless ( sequential), arithmetic coding
117    LosslessArithmetic
118}
119
120impl Default for SOFMarkers {
121    fn default() -> Self {
122        Self::BaselineDct
123    }
124}
125
126impl SOFMarkers {
127    /// Check if a certain marker is sequential DCT or not
128
129    pub fn is_sequential_dct(self) -> bool {
130        matches!(
131            self,
132            Self::BaselineDct
133                | Self::ExtendedSequentialHuffman
134                | Self::ExtendedSequentialDctArithmetic
135        )
136    }
137
138    /// Check if a marker is a Lossles type or not
139
140    pub fn is_lossless(self) -> bool {
141        matches!(self, Self::LosslessHuffman | Self::LosslessArithmetic)
142    }
143
144    /// Check whether a marker is a progressive marker or not
145
146    pub fn is_progressive(self) -> bool {
147        matches!(
148            self,
149            Self::ProgressiveDctHuffman | Self::ProgressiveDctArithmetic
150        )
151    }
152
153    /// Create a marker from an integer
154
155    pub fn from_int(int: u16) -> Option<SOFMarkers> {
156        match int {
157            START_OF_FRAME_BASE => Some(Self::BaselineDct),
158            START_OF_FRAME_PROG_DCT => Some(Self::ProgressiveDctHuffman),
159            START_OF_FRAME_PROG_DCT_AR => Some(Self::ProgressiveDctArithmetic),
160            START_OF_FRAME_LOS_SEQ => Some(Self::LosslessHuffman),
161            START_OF_FRAME_LOS_SEQ_AR => Some(Self::LosslessArithmetic),
162            START_OF_FRAME_EXT_SEQ => Some(Self::ExtendedSequentialHuffman),
163            START_OF_FRAME_EXT_AR => Some(Self::ExtendedSequentialDctArithmetic),
164            _ => None
165        }
166    }
167}
168
169impl fmt::Debug for SOFMarkers {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        match &self {
172            Self::BaselineDct => write!(f, "Baseline DCT"),
173            Self::ExtendedSequentialHuffman => {
174                write!(f, "Extended sequential DCT, Huffman Coding")
175            }
176            Self::ProgressiveDctHuffman => write!(f, "Progressive DCT,Huffman Encoding"),
177            Self::LosslessHuffman => write!(f, "Lossless (sequential) Huffman encoding"),
178            Self::ExtendedSequentialDctArithmetic => {
179                write!(f, "Extended sequential DCT, arithmetic coding")
180            }
181            Self::ProgressiveDctArithmetic => write!(f, "Progressive DCT, arithmetic coding"),
182            Self::LosslessArithmetic => write!(f, "Lossless (sequential) arithmetic coding")
183        }
184    }
185}
186
187/// Read `buf.len()*2` data from the underlying `u8` buffer and convert it into
188/// u16, and store it into `buf`
189///
190/// # Arguments
191/// - reader: A mutable reference to the underlying reader.
192/// - buf: A mutable reference to a slice containing u16's
193#[inline]
194pub fn read_u16_into<T>(reader: &mut ZByteReader<T>, buf: &mut [u16]) -> Result<(), DecodeErrors>
195where
196    T: ZReaderTrait
197{
198    for i in buf {
199        *i = reader.get_u16_be_err()?;
200    }
201
202    Ok(())
203}
204
205/// Set up component parameters.
206///
207/// This modifies the components in place setting up details needed by other
208/// parts fo the decoder.
209pub(crate) fn setup_component_params<T: ZReaderTrait>(
210    img: &mut JpegDecoder<T>
211) -> Result<(), DecodeErrors> {
212    let img_width = img.width();
213    let img_height = img.height();
214
215    // in case of adobe app14 being present, zero may indicate
216    // either CMYK if components are 4 or RGB if components are 3,
217    // see https://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
218    // so since we may not know how many number of components
219    // we have when decoding app14, we have to defer that check
220    // until now.
221    //
222    // We know adobe app14 was present since it's the only one that can modify
223    // input colorspace to be CMYK
224    if img.components.len() == 3 && img.input_colorspace == ColorSpace::CMYK {
225        img.input_colorspace = ColorSpace::RGB;
226    }
227
228    for component in &mut img.components {
229        // compute interleaved image info
230        // h_max contains the maximum horizontal component
231        img.h_max = max(img.h_max, component.horizontal_sample);
232        // v_max contains the maximum vertical component
233        img.v_max = max(img.v_max, component.vertical_sample);
234        img.mcu_width = img.h_max * 8;
235        img.mcu_height = img.v_max * 8;
236        // Number of MCU's per width
237        img.mcu_x = (usize::from(img.info.width) + img.mcu_width - 1) / img.mcu_width;
238        // Number of MCU's per height
239        img.mcu_y = (usize::from(img.info.height) + img.mcu_height - 1) / img.mcu_height;
240
241        if img.h_max != 1 || img.v_max != 1 {
242            // interleaved images have horizontal and vertical sampling factors
243            // not equal to 1.
244            img.is_interleaved = true;
245        }
246        // Extract quantization tables from the arrays into components
247        let qt_table = *img.qt_tables[component.quantization_table_number as usize]
248            .as_ref()
249            .ok_or_else(|| {
250                DecodeErrors::DqtError(format!(
251                    "No quantization table for component {:?}",
252                    component.component_id
253                ))
254            })?;
255
256        let x = (usize::from(img_width) * component.horizontal_sample + img.h_max - 1) / img.h_max;
257        let y = (usize::from(img_height) * component.horizontal_sample + img.h_max - 1) / img.v_max;
258        component.x = x;
259        component.w2 = img.mcu_x * component.horizontal_sample * 8;
260        // probably not needed. :)
261        component.y = y;
262        component.quantization_table = qt_table;
263        // initially stride contains its horizontal sub-sampling
264        component.width_stride *= img.mcu_x * 8;
265    }
266    {
267        // Sampling factors are one thing that suck
268        // this fixes a specific problem with images like
269        //
270        // (2 2) None
271        // (2 1) H
272        // (2 1) H
273        //
274        // The images exist in the wild, the images are not meant to exist
275        // but they do, it's just an annoying horizontal sub-sampling that
276        // I don't know why it exists.
277        // But it does
278        // So we try to cope with that.
279        // I am not sure of how to explain how to fix it, but it involved a debugger
280        // and to much coke(the legal one)
281        //
282        // If this wasn't present, self.upsample_dest would have the wrong length
283        let mut handle_that_annoying_bug = false;
284
285        if let Some(y_component) = img
286            .components
287            .iter()
288            .find(|c| c.component_id == ComponentID::Y)
289        {
290            if y_component.horizontal_sample == 2 || y_component.vertical_sample == 2 {
291                handle_that_annoying_bug = true;
292            }
293        }
294        if handle_that_annoying_bug {
295            for comp in &mut img.components {
296                if (comp.component_id != ComponentID::Y)
297                    && (comp.horizontal_sample != 1 || comp.vertical_sample != 1)
298                {
299                    comp.fix_an_annoying_bug = 2;
300                }
301            }
302        }
303    }
304
305    if img.is_mjpeg {
306        fill_default_mjpeg_tables(
307            img.is_progressive,
308            &mut img.dc_huffman_tables,
309            &mut img.ac_huffman_tables
310        );
311    }
312
313    Ok(())
314}
315
316///Calculate number of fill bytes added to the end of a JPEG image
317/// to fill the image
318///
319/// JPEG usually inserts padding bytes if the image width cannot be evenly divided into
320/// 8 , 16 or 32 chunks depending on the sub sampling ratio. So given a sub-sampling ratio,
321/// and the actual width, this calculates the padded bytes that were added to the image
322///
323///  # Params
324/// -actual_width: Actual width of the image
325/// -sub_sample: Sub sampling factor of the image
326///
327/// # Returns
328/// The padded width, this is how long the width is for a particular image
329pub fn calculate_padded_width(actual_width: usize, sub_sample: SampleRatios) -> usize {
330    match sub_sample {
331        SampleRatios::None | SampleRatios::V => {
332            // None+V sends one MCU row, so that's a simple calculation
333            ((actual_width + 7) / 8) * 8
334        }
335        SampleRatios::H | SampleRatios::HV => {
336            // sends two rows, width can be expanded by up to 15 more bytes
337            ((actual_width + 15) / 16) * 16
338        }
339    }
340}
341
342// https://www.loc.gov/preservation/digital/formats/fdd/fdd000063.shtml
343// "Avery Lee, writing in the rec.video.desktop newsgroup in 2001, commented that "MJPEG, or at
344//  least the MJPEG in AVIs having the MJPG fourcc, is restricted JPEG with a fixed -- and
345//  *omitted* -- Huffman table. The JPEG must be YCbCr colorspace, it must be 4:2:2, and it must
346//  use basic Huffman encoding, not arithmetic or progressive.... You can indeed extract the
347//  MJPEG frames and decode them with a regular JPEG decoder, but you have to prepend the DHT
348//  segment to them, or else the decoder won't have any idea how to decompress the data.
349//  The exact table necessary is given in the OpenDML spec.""
350pub fn fill_default_mjpeg_tables(
351    is_progressive: bool, dc_huffman_tables: &mut [Option<HuffmanTable>],
352    ac_huffman_tables: &mut [Option<HuffmanTable>]
353) {
354    // Section K.3.3
355    trace!("Filling with default mjpeg tables");
356
357    if dc_huffman_tables[0].is_none() {
358        // Table K.3
359        dc_huffman_tables[0] = Some(
360            HuffmanTable::new_unfilled(
361                &[
362                    0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
363                    0x00, 0x00, 0x00, 0x00
364                ],
365                &[
366                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
367                ],
368                true,
369                is_progressive
370            )
371            .unwrap()
372        );
373    }
374    if dc_huffman_tables[1].is_none() {
375        // Table K.4
376        dc_huffman_tables[1] = Some(
377            HuffmanTable::new_unfilled(
378                &[
379                    0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
380                    0x00, 0x00, 0x00, 0x00
381                ],
382                &[
383                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
384                ],
385                true,
386                is_progressive
387            )
388            .unwrap()
389        );
390    }
391    if ac_huffman_tables[0].is_none() {
392        // Table K.5
393        ac_huffman_tables[0] = Some(
394            HuffmanTable::new_unfilled(
395                &[
396                    0x00, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04,
397                    0x00, 0x00, 0x01, 0x7D
398                ],
399                &[
400                    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
401                    0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42,
402                    0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A,
403                    0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35,
404                    0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
405                    0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
406                    0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84,
407                    0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
408                    0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3,
409                    0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
410                    0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1,
411                    0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
412                    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
413                ],
414                false,
415                is_progressive
416            )
417            .unwrap()
418        );
419    }
420    if ac_huffman_tables[1].is_none() {
421        // Table K.6
422        ac_huffman_tables[1] = Some(
423            HuffmanTable::new_unfilled(
424                &[
425                    0x00, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
426                    0x00, 0x01, 0x02, 0x77
427                ],
428                &[
429                    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
430                    0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1,
431                    0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24,
432                    0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
433                    0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
434                    0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
435                    0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82,
436                    0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
437                    0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA,
438                    0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
439                    0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
440                    0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4,
441                    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
442                ],
443                false,
444                is_progressive
445            )
446            .unwrap()
447        );
448    }
449}