1use std::io::{Read, Seek, SeekFrom};
2
3use bom::{BOMBlock, BOMEror, BOMResult};
4use deku::{ctx::Order, prelude::*, reader::Reader};
5
6use super::ColorSpace;
7use crate::PayloadBytes;
8
9#[derive(Debug, DekuRead)]
10#[deku(magic = b"tmfk", endian = "little")]
11pub struct KeyFmt {
12 pub version: u32,
13 pub max_count: u32,
14 #[deku(reader = "KeyFmt::read_attr_types(deku::reader, *max_count)")]
15 pub attribute_types: Vec<AttributeType>,
16}
17
18impl KeyFmt {
19 fn read_attr_types<R: Read + Seek>(
20 reader: &mut Reader<R>,
21 count: u32,
22 ) -> Result<Vec<AttributeType>, DekuError> {
23 let mut attr_types: Vec<AttributeType> = vec![];
24 for _ in 0..count {
25 attr_types.push(AttributeType::from_reader_with_ctx(
26 reader,
27 deku::ctx::Endian::Little,
28 )?);
29 reader.seek(SeekFrom::Current(2))?;
30 }
31 Ok(attr_types)
32 }
33}
34
35#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, DekuRead)]
36#[deku(endian = "little")]
37pub struct KeyToken {
38 pub x: u16,
39 pub y: u16,
40 count: u16,
41 #[deku(count = "count")]
42 pub attrs: Vec<Attribute>,
43}
44
45#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, DekuRead)]
46#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
47pub struct Attribute {
48 pub name: AttributeType,
49 pub val: u16,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, DekuRead)]
53#[deku(id_type = "u16", endian = "little", ctx = "endian: deku::ctx::Endian")]
54pub enum AttributeType {
55 #[deku(id = 0)]
56 ThemeLook,
57 #[deku(id = 1)]
58 Element,
59 #[deku(id = 2)]
60 Part,
61 #[deku(id = 3)]
62 Size,
63 #[deku(id = 4)]
64 Direction,
65 #[deku(id = 5)]
66 Placeholder,
67 #[deku(id = 6)]
68 Value,
69 #[deku(id = 7)]
70 ThemeAppearance,
71 #[deku(id = 8)]
72 Dimension1,
73 #[deku(id = 9)]
74 Dimension2,
75 #[deku(id = 10)]
76 State,
77 #[deku(id = 11)]
78 Layer,
79 #[deku(id = 12)]
80 Scale,
81 #[deku(id = 13)]
82 Localization,
83 #[deku(id = 14)]
84 PresentationState,
85 #[deku(id = 15)]
86 Idiom,
87 #[deku(id = 16)]
88 Subtype,
89 #[deku(id = 17)]
90 Identifier,
91 #[deku(id = 18)]
92 PreviousValue,
93 #[deku(id = 19)]
94 PreviousState,
95 #[deku(id = 20)]
96 HorizontalSizeClass,
97 #[deku(id = 21)]
98 VerticalSizeClass,
99 #[deku(id = 22)]
100 MemoryLevelClass,
101 #[deku(id = 23)]
102 GraphicsFeatureSetClass,
103 #[deku(id = 24)]
104 DisplayGamut,
105 #[deku(id = 25)]
106 DeploymentTarget,
107 #[deku(id = 26)]
108 GlyphWeight,
109 #[deku(id = 27)]
110 GlyphSize,
111
112 #[deku(id_pat = "_")]
113 Unknown { tag: u16 },
114}
115
116#[derive(Debug, Clone)]
117pub enum Rendition {
118 Color(RenditionColor),
119 RawData(RenditionRawData),
120 ThemeCBCK(RenditionThemeCBCK),
121 MultisizeImageSet(RenditionMultisizeImageSet),
122 Unknown {
123 tag: [u8; 4],
124 data: RenditionUnknown,
125 },
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum RenditionKind {
130 None,
131 Color,
132 RawData,
133 ThemeCBCK,
134 MultisizeImageSet,
135 Unknown,
136}
137
138#[derive(Debug, Clone, DekuRead)]
139#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
140pub struct RenditionColor {
141 pub version: u32,
142 pub color_space: ColorSpace,
143 _component_count: u32,
144 #[deku(count = "_component_count")]
145 pub components: Vec<f64>,
146}
147
148#[derive(Debug, Clone)]
149pub struct RenditionRawData {
150 pub version: u32,
151 _raw_data_length: u32,
152 pub raw_data: PayloadBytes,
153}
154
155#[derive(Debug, Clone)]
156pub struct RenditionThemeCBCK {
157 pub version: u32,
158 pub compression_type: CompressionType,
159 _raw_data_length: u32,
160 pub chunks: Vec<ThemeChunk>,
161}
162
163impl RenditionThemeCBCK {
164 pub fn raw_datas(&self) -> impl Iterator<Item = &[u8]> {
165 self.chunks.iter().map(|chunk| chunk.raw_data.as_slice())
166 }
167}
168
169#[derive(Debug, Clone, DekuRead)]
170#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
171pub struct RenditionMultisizeImageSet {
172 pub version: u32,
173 _count: u32,
174 #[deku(count = "_count")]
175 pub entries: Vec<MultisizeImageSetEntry>,
176}
177
178#[derive(Debug, Clone)]
179pub struct RenditionUnknown {
180 pub version: u32,
182 _raw_data_length: u32,
183 pub raw_data: PayloadBytes,
184}
185
186#[derive(Debug, Clone, DekuRead)]
187#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
188pub struct CBCK {
189 pub idk: u32,
190 pub tag: u32,
191 pub a: u32,
192 pub b: u32,
193}
194
195#[derive(Debug, Clone, DekuRead)]
196#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
197pub struct MultisizeImageSetEntry {
198 pub width: u32,
199 pub height: u32,
200 pub index: u16,
201 pub idiom: Idiom,
202}
203
204#[derive(Debug, Clone, DekuRead)]
205#[deku(endian = "little")]
206pub struct ThemePartHeader {
207 pub tag: [u8; 4],
208 pub arg2: u32,
209 pub arg3: u32,
210 pub height: u32,
211 pub raw_len: u32,
212}
213
214#[derive(Debug, Clone)]
215pub struct ThemeChunk {
216 pub part_header: Option<ThemePartHeader>,
217 pub raw_data: PayloadBytes,
218}
219
220pub(crate) fn parse_rendition_payload(payload: PayloadBytes) -> BOMResult<Rendition> {
221 let mut reader = Reader::new(BOMBlock::new(payload));
222 let mut tag = [0_u8; 4];
223 reader.read_bytes(4, &mut tag, Order::default())?;
224
225 match &tag {
226 b"RLOC" => {
227 let data =
228 RenditionColor::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little)?;
229 Ok(Rendition::Color(data))
230 }
231 b"DWAR" => {
232 let version = read_u32(&mut reader)?;
233 let raw_data_length = read_u32(&mut reader)?;
234 let raw_data = payload_slice(&mut reader, raw_data_length)?;
235 Ok(Rendition::RawData(RenditionRawData {
236 version,
237 _raw_data_length: raw_data_length,
238 raw_data,
239 }))
240 }
241 b"MLEC" => {
242 let version = read_u32(&mut reader)?;
243 let compression_type =
244 CompressionType::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little)?;
245 let raw_data_length = read_u32(&mut reader)?;
246 let chunks = read_theme_chunks(&mut reader, version, raw_data_length)?;
247 Ok(Rendition::ThemeCBCK(RenditionThemeCBCK {
248 version,
249 compression_type,
250 _raw_data_length: raw_data_length,
251 chunks,
252 }))
253 }
254 b"SISM" => {
255 let data = RenditionMultisizeImageSet::from_reader_with_ctx(
256 &mut reader,
257 deku::ctx::Endian::Little,
258 )?;
259 Ok(Rendition::MultisizeImageSet(data))
260 }
261 _ => {
262 let version = read_u32(&mut reader)?;
263 let raw_data_length = read_u32(&mut reader)?;
264 let raw_data = payload_slice(&mut reader, raw_data_length)?;
265 Ok(Rendition::Unknown {
266 tag,
267 data: RenditionUnknown {
268 version,
269 _raw_data_length: raw_data_length,
270 raw_data,
271 },
272 })
273 }
274 }
275}
276
277fn read_u32(reader: &mut Reader<BOMBlock>) -> Result<u32, DekuError> {
278 u32::from_reader_with_ctx(reader, deku::ctx::Endian::Little)
279}
280
281fn payload_slice(reader: &mut Reader<BOMBlock>, len: u32) -> BOMResult<PayloadBytes> {
282 let len = len as usize;
283 let slice = reader.as_mut().slice_at_current(len)?;
284 reader.bits_read = reader.bits_read.saturating_add(len.saturating_mul(8));
285 reader.leftover = None;
286 Ok(slice)
287}
288
289fn read_theme_chunks(
290 reader: &mut Reader<BOMBlock>,
291 version: u32,
292 raw_len: u32,
293) -> BOMResult<Vec<ThemeChunk>> {
294 match version {
295 0 | 2 => {
296 let raw_data = payload_slice(reader, raw_len)?;
297 Ok(vec![ThemeChunk {
298 part_header: None,
299 raw_data,
300 }])
301 }
302 1 | 3 => {
303 let mut result = vec![];
304 for _ in 0..raw_len {
305 let part_header = ThemePartHeader::from_reader_with_ctx(reader, ())?;
306 let raw_data = payload_slice(reader, part_header.raw_len)?;
307 result.push(ThemeChunk {
308 part_header: Some(part_header),
309 raw_data,
310 });
311 }
312 Ok(result)
313 }
314 _ => Err(BOMEror::ParseStruct(DekuError::Parse(
315 format!("Not support version {}", version).into(),
316 ))),
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
321#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
322pub enum CompressionType {
323 #[deku(id = 0)]
324 Uncompressed,
325 #[deku(id = 1)]
326 Rle,
327 #[deku(id = 2)]
328 Zip,
329 #[deku(id = 3)]
330 Lzvn,
331 #[deku(id = 4)]
332 Lzfse,
333 #[deku(id = 5)]
334 JpegLzfse,
335 #[deku(id = 6)]
336 Blurred,
337 #[deku(id = 7)]
338 Astc,
339 #[deku(id = 8)]
340 PaletteImg,
341 #[deku(id = 9)]
342 HEVC, #[deku(id = 10)]
344 DeepmapLzfse,
345 #[deku(id = 11)]
347 Deepmap2,
348
349 #[deku(id_pat = "_")]
350 Unknown { tag: u32 },
351}
352
353#[derive(Debug, Clone, DekuRead)]
354#[deku(id_type = "u16", endian = "little", ctx = "endian: deku::ctx::Endian")]
355pub enum Idiom {
356 #[deku(id = 0)]
357 Universal,
358 #[deku(id = 1)]
359 Phone,
360 #[deku(id = 2)]
361 Pad,
362 #[deku(id = 3)]
363 TV,
364 #[deku(id = 4)]
365 Car,
366 #[deku(id = 5)]
367 Watch,
368 #[deku(id = 6)]
369 Marketing,
370
371 #[deku(id_pat = "_")]
372 Unknown { tag: u16 },
373}
374
375#[derive(Debug, DekuRead)]
376#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
377pub struct BitmapList {
378 pub bitmap_count: u32, pub zero: u32, pub rendition_length: u32,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead)]
384#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
385pub enum LayoutType {
386 #[deku(id = 6)]
387 Gradient,
388 #[deku(id = 7)]
389 Effect,
390 #[deku(id = 9)]
391 Vector,
392 #[deku(id = 10)]
393 OnePartFixedSize,
394 #[deku(id = 11)]
395 OnePartTile,
396 #[deku(id = 12)]
397 OnePartScale,
398 #[deku(id = 20)]
399 ThreePartHorizontalTile,
400 #[deku(id = 21)]
401 ThreePartHorizontalScale,
402 #[deku(id = 22)]
403 ThreePartHorizontalUniform,
404 #[deku(id = 23)]
405 ThreePartVerticalTile,
406 #[deku(id = 24)]
407 ThreePartVerticalScale,
408 #[deku(id = 25)]
409 ThreePartVerticalUniform,
410 #[deku(id = 30)]
411 NinePartTile,
412 #[deku(id = 31)]
413 NinePartScale,
414 #[deku(id = 32)]
415 NinePartHorizontalUniformVerticalScale,
416 #[deku(id = 33)]
417 NinePartHorizontalScaleVerticalUniform,
418 #[deku(id = 34)]
419 NinePartEdgesOnly,
420 #[deku(id = 40)]
421 SixPart,
422 #[deku(id = 50)]
423 AnimationFilmstrip,
424
425 #[deku(id = 1000)]
426 Data,
427 #[deku(id = 1001)]
428 ExternalLink,
429 #[deku(id = 1002)]
430 LayerStack,
431 #[deku(id = 1003)]
432 InternalReference,
433 #[deku(id = 1004)]
434 PackedImage,
435 #[deku(id = 1005)]
436 NameList,
437 #[deku(id = 1006)]
438 UnknownAddObject,
439 #[deku(id = 1007)]
440 Texture,
441 #[deku(id = 1008)]
442 TextureImage,
443 #[deku(id = 1009)]
444 Color,
445 #[deku(id = 1010)]
446 MultisizeImage,
447 #[deku(id = 1012)]
448 LayerReference,
449 #[deku(id = 1013)]
450 ContentRendition,
451 #[deku(id = 1014)]
452 RecognitionObject,
453
454 #[deku(id_pat = "_")]
455 Unknown { tag: u32 },
456}
457
458#[derive(Debug, DekuRead)]
459#[deku(
460 endian = "little",
461 bit_order = "lsb",
462 ctx = "endian: deku::ctx::Endian"
463)]
464pub struct Flags {
465 #[deku(bits = 1)]
466 pub is_header_flagged_fpo: bool,
467 #[deku(bits = 1)]
468 pub is_excluded_from_contrast_filter: bool,
469 #[deku(bits = 1)]
470 pub is_vector_based: bool,
471 #[deku(bits = 1)]
472 pub is_opaque: bool,
473 #[deku(bits = 4)]
474 pub bitmap_encoding: u8,
475 #[deku(bits = 1)]
476 pub opt_out_of_thinning: bool,
477 #[deku(bits = 1)]
478 pub is_flippable: bool,
479 #[deku(bits = 1)]
480 pub is_tintable: bool,
481 #[deku(bits = 1)]
482 pub preserved_vector_representation: bool,
483 #[deku(bits = 20)]
484 _reserved: u32,
485}
486
487#[derive(Debug, DekuRead)]
488#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
489pub struct Slice {
490 pub x: u32,
491 pub y: u32,
492 pub width: u32,
493 pub height: u32,
494}
495
496#[derive(Debug, DekuRead)]
497#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
498pub struct Metric {
499 pub width: u32,
500 pub height: u32,
501}
502
503#[derive(Debug, DekuRead)]
504#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
505pub enum RenditionType {
506 #[deku(id = 1001)]
507 Slices(RenditionTypeSlice),
508 #[deku(id = 1003)]
509 Metrics(RenditionTypeMetric),
510 #[deku(id = 1004)]
511 BlendModeAndOpacity(RenditionTypeBlendModeAndOpacity),
512 #[deku(id = 1005)]
513 UTI(RenditionTypeUTI),
514 #[deku(id = 1006)]
515 EXIFOrientation(RenditionTypeEXIFOrientation),
516 #[deku(id = 1007)]
517 BytesPerRow { length: u32, bytes_per_row: u32 },
518 #[deku(id = 1010)]
519 Reference(RenditionTypeReference),
520 #[deku(id_pat = "_")]
521 Unknown {
522 tag: u32,
523 data: RenditionTypeUnknown,
524 },
525}
526
527#[derive(Debug, DekuRead)]
528#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
529pub struct RenditionTypeSlice {
530 _length: u32,
531 _count: u32,
532 #[deku(count = "_count")]
533 pub data: Vec<Slice>,
534}
535
536#[derive(Debug, DekuRead)]
537#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
538pub struct RenditionTypeMetric {
539 _length: u32,
540 _count: u32,
541 pub top_right_inset: Metric,
542 pub bottom_left_inset: Metric,
543 pub image_size: Metric,
544}
545
546#[derive(Debug, DekuRead)]
547#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
548pub struct RenditionTypeBlendModeAndOpacity {
549 _length: u32,
550 pub blend_mode: u32,
551 pub opacity: f32,
552}
553
554#[derive(Debug, DekuRead)]
555#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
556pub struct RenditionTypeUTI {
557 _length: u32,
558 pub string_length: u32,
559 pub padding: u32,
560 #[deku(count = "string_length")]
561 pub string: Vec<u8>,
562}
563
564#[derive(Debug, DekuRead)]
565#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
566pub struct RenditionTypeEXIFOrientation {
567 _length: u32,
568 pub orientation: EXIFOrientationValue,
569}
570
571#[derive(Debug, DekuRead)]
572#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
573pub struct RenditionTypeBytesPerRow {
574 _length: u32,
575 pub bytes_per_row: u32,
576}
577
578#[derive(Debug, DekuRead)]
579#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
580pub struct RenditionTypeReference {
581 _length: u32,
582 _magic: [u8; 4], _padding: u32, pub x: u32,
585 pub y: u32,
586 pub width: u32,
587 pub height: u32,
588 pub layout: u16, _key_length: u32,
590 #[deku(count = "_key_length")]
591 pub keys: Vec<u8>, }
593#[derive(Debug, DekuRead)]
594#[deku(endian = "little", ctx = "endian: deku::ctx::Endian")]
595pub struct RenditionTypeUnknown {
596 _length: u32,
597 #[deku(count = "_length")]
598 pub data: Vec<u8>,
599}
600
601#[derive(Debug, DekuRead)]
602#[deku(id_type = "u32", endian = "little", ctx = "endian: deku::ctx::Endian")]
603pub enum EXIFOrientationValue {
604 #[deku(id = 0)]
605 None,
606 #[deku(id = 1)]
607 Normal,
608 #[deku(id = 2)]
609 Mirrored,
610 #[deku(id = 3)]
611 Rotated180,
612 #[deku(id = 4)]
613 Rotated180Mirrored,
614 #[deku(id = 5)]
615 Rotated90,
616 #[deku(id = 6)]
617 Rotated90Mirrored,
618 #[deku(id = 7)]
619 Rotated270,
620 #[deku(id = 8)]
621 Rotated270Mirrored,
622
623 #[deku(id_pat = "_")]
624 Unknown { tag: u32 },
625}
626
627#[cfg(test)]
628mod tests {
629 use std::io::Cursor;
630
631 use super::*;
632
633 fn parse_flags(raw: u32) -> Flags {
634 let mut reader = Reader::new(Cursor::new(raw.to_le_bytes()));
635 Flags::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little)
636 .expect("parse CSI flags")
637 }
638
639 #[test]
640 fn flags_decode_each_bit_at_its_little_endian_position() {
641 let patterns = (0..32)
642 .map(|bit| 1u32 << bit)
643 .chain([0, u32::MAX, 0x1234_5678]);
644
645 for raw in patterns {
646 let flags = parse_flags(raw);
647 assert_eq!(
648 [
649 flags.is_header_flagged_fpo,
650 flags.is_excluded_from_contrast_filter,
651 flags.is_vector_based,
652 flags.is_opaque,
653 flags.opt_out_of_thinning,
654 flags.is_flippable,
655 flags.is_tintable,
656 flags.preserved_vector_representation,
657 ],
658 [0, 1, 2, 3, 8, 9, 10, 11].map(|bit| raw & (1 << bit) != 0),
659 "boolean flags for {raw:#010x}"
660 );
661 assert_eq!(flags.bitmap_encoding, ((raw >> 4) & 0xf) as u8);
662 assert_eq!(flags._reserved, raw >> 12);
663 }
664 }
665
666 #[test]
667 fn flags_preserve_every_bitmap_encoding_value() {
668 for encoding in 0u8..16 {
669 let flags = parse_flags(0x0f0f | (u32::from(encoding) << 4));
670 assert_eq!(flags.bitmap_encoding, encoding);
671 }
672 }
673
674 #[test]
675 fn flags_consume_exactly_four_bytes() {
676 let mut bytes = 0x1234_5678u32.to_le_bytes().to_vec();
677 bytes.extend_from_slice(&0xa1b2_c3d4u32.to_le_bytes());
678 let mut reader = Reader::new(Cursor::new(bytes));
679
680 Flags::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little).unwrap();
681 let next = u32::from_reader_with_ctx(&mut reader, deku::ctx::Endian::Little).unwrap();
682
683 assert_eq!(next, 0xa1b2_c3d4);
684 }
685}