1mod parser;
4
5#[cfg(test)]
6mod tests;
7
8use std::{cmp, mem};
9
10use bytemuck::{AnyBitPattern, Pod, Zeroable};
11
12use crate::{
13 buffer::{Buffer, BufferType},
14 config::Config,
15 context::Context,
16 display::Display,
17 error::Error,
18 raw::{Rectangle, VA_PADDING_LOW, VA_PADDING_MEDIUM},
19 surface::{RTFormat, Surface},
20 Entrypoint, Profile, Result, Rotation, SliceParameterBufferBase,
21};
22
23use self::parser::{JpegParser, SegmentKind, SofMarker};
24
25ffi_enum! {
26 pub enum ColorSpace: u8 {
27 YUV = 0,
28 RGB = 1,
29 BGR = 2,
30 }
31}
32
33#[derive(Clone, Copy)]
35#[repr(C)]
36pub struct IQMatrixBuffer {
37 load_quantiser_table: [u8; 4],
38 quantiser_table: [[u8; 64]; 4],
40 va_reserved: [u32; VA_PADDING_LOW],
41}
42
43impl IQMatrixBuffer {
44 pub fn new() -> Self {
45 unsafe { mem::zeroed() }
46 }
47
48 pub fn set_quantization_table(&mut self, index: u8, table_data: &[u8; 64]) {
49 assert!(index <= 3, "index {index} out of bounds");
50 let index = usize::from(index);
51 self.load_quantiser_table[index] = 1;
52 self.quantiser_table[index] = *table_data;
53 }
54}
55
56#[derive(Clone, Copy)]
57#[repr(C)]
58pub struct PictureParameterBuffer {
59 picture_width: u16,
60 picture_height: u16,
61 components: [Component; 255],
62 num_components: u8,
63 color_space: ColorSpace,
64 rotation: Rotation,
65 crop_rectangle: Rectangle,
66 va_reserved: [u32; VA_PADDING_MEDIUM - 3],
67}
68
69#[derive(Clone, Copy)]
70#[repr(C)]
71pub struct Component {
72 component_id: u8,
73 h_sampling_factor: u8,
74 v_sampling_factor: u8,
75 quantiser_table_selector: u8,
76}
77
78impl PictureParameterBuffer {
79 pub fn new(picture_width: u16, picture_height: u16, color_space: ColorSpace) -> Self {
80 unsafe {
81 let mut this: Self = mem::zeroed();
82 this.picture_width = picture_width;
83 this.picture_height = picture_height;
84 this.color_space = color_space;
85 this
86 }
87 }
88
89 #[inline]
90 pub fn picture_width(&self) -> u16 {
91 self.picture_width
92 }
93
94 #[inline]
95 pub fn picture_height(&self) -> u16 {
96 self.picture_height
97 }
98
99 #[inline]
100 pub fn set_rotation(&mut self, rotation: Rotation) {
101 self.rotation = rotation;
102 }
103
104 #[allow(non_snake_case)]
113 pub fn push_component(&mut self, Ci: u8, Hi: u8, Vi: u8, Tqi: u8) {
114 let index = usize::from(self.num_components);
115 self.num_components = self
116 .num_components
117 .checked_add(1)
118 .expect("maximum number of frame components reached");
119
120 self.components[index].component_id = Ci;
121 self.components[index].h_sampling_factor = Hi;
122 self.components[index].v_sampling_factor = Vi;
123 self.components[index].quantiser_table_selector = Tqi;
124 }
125}
126
127#[derive(Clone, Copy)]
128#[repr(C)]
129pub struct SliceParameterBuffer {
130 base: SliceParameterBufferBase,
131
132 slice_horizontal_position: u32,
133 slice_vertical_position: u32,
134
135 components: [ScanComponent; 4],
136 num_components: u8,
137
138 restart_interval: u16,
139 num_mcus: u32,
140
141 va_reserved: [u32; VA_PADDING_LOW],
142}
143
144#[derive(Clone, Copy)]
145#[repr(C)]
146struct ScanComponent {
147 component_selector: u8,
148 dc_table_selector: u8,
149 ac_table_selector: u8,
150}
151
152impl SliceParameterBuffer {
153 #[allow(non_snake_case)]
161 pub fn new(base: SliceParameterBufferBase, Ri: u16, num_mcus: u32) -> Self {
162 unsafe {
163 let mut this: Self = mem::zeroed();
164 this.base = base;
165 this.restart_interval = Ri;
166 this.num_mcus = num_mcus;
167 this
168 }
169 }
170
171 #[allow(non_snake_case)]
172 pub fn push_component(&mut self, Csj: u8, Tdj: u8, Taj: u8) {
173 let index = usize::from(self.num_components);
174 self.num_components = self
175 .num_components
176 .checked_add(1)
177 .expect("maximum number of scan components reached");
178
179 self.components[index].component_selector = Csj;
180 self.components[index].dc_table_selector = Tdj;
181 self.components[index].ac_table_selector = Taj;
182 }
183}
184
185#[derive(Clone, Copy, AnyBitPattern)]
187#[repr(C)]
188pub struct HuffmanTableBuffer {
189 load_huffman_table: [u8; 2],
190 huffman_table: [HuffmanTable; 2],
191 va_reserved: [u32; VA_PADDING_LOW],
192}
193
194impl HuffmanTableBuffer {
195 pub fn default_tables() -> Self {
196 let mut this = Self::zeroed();
197 this.set_huffman_table(0, &HuffmanTable::default_luminance());
198 this.set_huffman_table(1, &HuffmanTable::default_chrominance());
199 this
200 }
201
202 pub fn zeroed() -> Self {
203 unsafe { mem::zeroed() }
204 }
205
206 pub fn set_huffman_table(&mut self, index: u8, tbl: &HuffmanTable) {
207 assert!(index <= 1, "huffman table index {index} out of bounds");
208 let index = usize::from(index);
209 self.huffman_table[index] = *tbl;
210 self.load_huffman_table[index] = 1; }
212
213 pub fn huffman_table_mut(&mut self, index: u8) -> &mut HuffmanTable {
214 assert!(index <= 1, "huffman table index {index} out of bounds");
215 let index = usize::from(index);
216 self.load_huffman_table[index] = 1; &mut self.huffman_table[index]
218 }
219
220 pub fn clear_modified(&mut self) {
221 self.load_huffman_table = [0; 2];
222 }
223}
224
225#[derive(Clone, Copy, Pod, Zeroable)]
226#[repr(C)]
227pub struct HuffmanTable {
228 num_dc_codes: [u8; 16],
229 dc_values: [u8; 12],
230 num_ac_codes: [u8; 16],
231 ac_values: [u8; 162],
232 pad: [u8; 2],
233}
234
235impl HuffmanTable {
236 #[rustfmt::skip]
238 pub fn default_luminance() -> Self {
239 let mut this = Self::zeroed();
240 this.set_dc_table(
241 &[0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0],
242 &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b],
243 );
244 this.set_ac_table(
245 &[0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125],
246 &[
247 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
248 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
249 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
250 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
251 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
252 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
253 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
254 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
255 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
256 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
257 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
258 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
259 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
260 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
261 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
262 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
263 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
264 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
265 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
266 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
267 0xf9, 0xfa,
268 ]
269 );
270 this
271 }
272
273 #[rustfmt::skip]
275 pub fn default_chrominance() -> Self {
276 let mut this = Self::zeroed();
277 this.set_dc_table(
278 &[0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
279 &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b]
280 );
281 this.set_ac_table(
282 &[0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119],
283 &[
284 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
285 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
286 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
287 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
288 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
289 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
290 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
291 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
292 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
293 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
294 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
295 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
296 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
297 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
298 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
299 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
300 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
301 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
302 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
303 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
304 0xf9, 0xfa,
305 ]
306 );
307 this
308 }
309
310 pub fn zeroed() -> Self {
311 unsafe { mem::zeroed() }
312 }
313
314 #[allow(non_snake_case)]
315 pub fn set_dc_table(&mut self, Li: &[u8], Vij: &[u8]) {
316 assert!(
317 Li.len() <= 16,
318 "DC huffman table code count {} exceeds maximum",
319 Li.len(),
320 );
321 assert!(
322 Vij.len() <= 12,
323 "DC huffman table value count {} exceeds maximum",
324 Vij.len(),
325 );
326
327 self.num_dc_codes[..Li.len()].copy_from_slice(Li);
328 self.dc_values[..Vij.len()].copy_from_slice(Vij);
329 }
330
331 #[allow(non_snake_case)]
332 pub fn set_ac_table(&mut self, Li: &[u8], Vij: &[u8]) {
333 assert!(
334 Li.len() <= 16,
335 "AC huffman table code count {} exceeds maximum",
336 Li.len(),
337 );
338 assert!(
339 Vij.len() <= 162,
340 "AC huffman table value count {} exceeds maximum",
341 Vij.len(),
342 );
343
344 self.num_ac_codes[..Li.len()].copy_from_slice(Li);
345 self.ac_values[..Vij.len()].copy_from_slice(Vij);
346 }
347}
348
349#[derive(Debug, Clone, Copy)]
351pub struct JpegInfo {
352 width: u16,
353 height: u16,
354}
355
356impl JpegInfo {
357 pub fn new(jpeg: &[u8]) -> Result<Self> {
365 let mut parser = JpegParser::new(&jpeg);
366 let segment = parser
367 .next_segment()?
368 .ok_or_else(|| Error::from("missing SOI segment"))?;
369 if !matches!(segment.kind, parser::SegmentKind::Soi) {
370 return Err(Error::from("missing SOI segment"));
371 }
372
373 let sof = loop {
374 let segment = parser
375 .next_segment()?
376 .ok_or_else(|| Error::from("missing SOF segment"))?;
377 match segment.kind {
378 SegmentKind::Sof(sof) => break sof,
379 _ => {}
380 }
381 };
382
383 if sof.sof() != SofMarker::SOF0 {
384 return Err(Error::from(format!(
385 "not a baseline JPEG ({:?})",
386 sof.sof()
387 )));
388 }
389 if sof.P() != 8 {
390 return Err(Error::from(format!(
391 "unsupported sample precision of {} bits (only 8-bit samples are supported)",
392 sof.P()
393 )));
394 }
395
396 Ok(Self {
397 width: sof.X(),
398 height: sof.Y(),
399 })
400 }
401
402 #[inline]
403 pub fn width(&self) -> u16 {
404 self.width
405 }
406
407 #[inline]
408 pub fn height(&self) -> u16 {
409 self.height
410 }
411}
412
413pub struct JpegDecodeSession {
420 width: u32,
421 height: u32,
422 jpeg_surface: Surface,
423 jpeg_context: Context,
424}
425
426impl JpegDecodeSession {
427 pub fn new(display: &Display, width: u16, height: u16) -> Result<Self> {
437 let width = u32::from(width);
438 let height = u32::from(height);
439
440 let config = Config::new(&display, Profile::JPEGBaseline, Entrypoint::VLD)?;
441 let jpeg_context = Context::new(&config, width, height)?;
442 let jpeg_surface = Surface::new(&display, width, height, RTFormat::YUV420)?;
443
444 Ok(Self {
445 width,
446 height,
447 jpeg_surface,
448 jpeg_context,
449 })
450 }
451
452 #[inline]
453 pub fn surface(&mut self) -> &mut Surface {
454 &mut self.jpeg_surface
455 }
456
457 pub fn decode(&mut self, jpeg: &[u8]) -> Result<&mut Surface> {
466 macro_rules! bail {
468 ($($args:tt)*) => {
469 return Err(Error::from(format!(
470 $($args)*
471 )))
472 };
473 }
474
475 let mut dhtbuf = HuffmanTableBuffer::zeroed();
476 let mut max_h_factor = 0;
477 let mut max_v_factor = 0;
478 let mut restart_interval = 0;
479 let mut ppbuf = None;
480 let mut slice = None;
481 let mut iqbuf = IQMatrixBuffer::new();
482
483 let mut parser = JpegParser::new(&jpeg);
484 while let Some(segment) = parser.next_segment()? {
485 match segment.kind {
486 SegmentKind::Dqt(dqt) => {
487 for dqt in dqt.tables() {
488 if dqt.Pq() != 0 {
489 bail!("unexpected value `{}` for DQT Pq", dqt.Pq());
490 }
491 iqbuf.set_quantization_table(dqt.Tq(), &dqt.Qk());
492 }
493 }
494 SegmentKind::Dht(dht) => {
495 for table in dht.tables() {
496 if table.Th() > 1 {
497 bail!(
498 "invalid DHT destination slot {} (expected 0 or 1)",
499 table.Th()
500 );
501 }
502 let tbl = dhtbuf.huffman_table_mut(table.Th());
503 match table.Tc() {
504 0 => tbl.set_dc_table(table.Li(), table.Vij()),
505 1 => tbl.set_ac_table(table.Li(), table.Vij()),
506 _ => bail!("invalid DHT class {}", table.Tc()),
507 }
508 }
509 }
510 SegmentKind::Dri(dri) => restart_interval = dri.Ri(),
511 SegmentKind::Sof(sof) => {
512 if sof.sof() != SofMarker::SOF0 {
513 bail!("not a baseline JPEG (SOF={:?})", sof.sof());
514 }
515
516 if sof.P() != 8 {
517 bail!("sample precision of {} bits is not supported", sof.P());
518 }
519
520 if u32::from(sof.Y()) != self.height || u32::from(sof.X()) != self.width {
521 bail!(
522 "image dimension {}x{} does not match context dimention {}x{}",
523 sof.X(),
524 sof.Y(),
525 self.width,
526 self.height
527 );
528 }
529
530 let mut buf = PictureParameterBuffer::new(sof.X(), sof.Y(), ColorSpace::YUV);
531 for component in sof.components() {
532 buf.push_component(
533 component.Ci(),
534 component.Hi(),
535 component.Vi(),
536 component.Tqi(),
537 );
538 max_h_factor = cmp::max(u32::from(component.Hi()), max_h_factor);
539 max_v_factor = cmp::max(u32::from(component.Vi()), max_v_factor);
540 }
541 ppbuf = Some(buf);
542 }
543 SegmentKind::Sos(sos) => {
544 if sos.Ss() != 0 || sos.Se() != 63 {
545 bail!(
547 "invalid SOS header: Ss={}, Se={} (expected 0...63)",
548 sos.Ss(),
549 sos.Se(),
550 );
551 }
552
553 if sos.Ah() != 0 || sos.Al() != 0 {
554 bail!("invalid SOS header: Ah={}, Al={}", sos.Ah(), sos.Al());
556 }
557
558 let slice_data = sos.data();
559 let num_mcus = ((self.width + max_h_factor * 8 - 1) / (max_h_factor * 8))
560 * ((self.height + max_v_factor * 8 - 1) / (max_v_factor * 8));
561 let mut slice_params = SliceParameterBuffer::new(
562 SliceParameterBufferBase::new(slice_data.len().try_into().unwrap()),
563 restart_interval,
564 num_mcus,
565 );
566 for component in sos.components() {
567 slice_params.push_component(
568 component.Csj(),
569 component.Tdj(),
570 component.Taj(),
571 );
572 }
573 slice = Some((slice_params, slice_data));
574 }
575 SegmentKind::Eoi => break,
576 _ => {}
577 }
578 }
579
580 let Some(ppbuf) = ppbuf else {
581 bail!("file is missing SOI segment")
582 };
583 let Some((slice_params, slice_data)) = slice else {
584 bail!("file is missing SOS header")
585 };
586
587 let mut buf_dht = Buffer::new_param(&self.jpeg_context, BufferType::HuffmanTable, dhtbuf)?;
588 let mut buf_iq = Buffer::new_param(&self.jpeg_context, BufferType::IQMatrix, iqbuf)?;
589 let mut buf_pp =
590 Buffer::new_param(&self.jpeg_context, BufferType::PictureParameter, ppbuf)?;
591 let mut buf_slice_param =
592 Buffer::new_param(&self.jpeg_context, BufferType::SliceParameter, slice_params)?;
593 let mut buf_slice_data =
594 Buffer::new_data(&self.jpeg_context, BufferType::SliceData, &slice_data)?;
595
596 let mut picture = self.jpeg_context.begin_picture(&mut self.jpeg_surface)?;
597 unsafe {
598 picture.render_picture(&mut buf_dht)?;
599 picture.render_picture(&mut buf_iq)?;
600 picture.render_picture(&mut buf_pp)?;
601 picture.render_picture(&mut buf_slice_param)?;
602 picture.render_picture(&mut buf_slice_data)?;
603 picture.end_picture()?;
604 }
605
606 Ok(&mut self.jpeg_surface)
607 }
608}