ironrdp_pdu/codecs/rfx/
data_messages.rs

1use core::iter;
2
3use bit_field::BitField as _;
4use bitflags::bitflags;
5use ironrdp_core::{
6    cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult,
7    ReadCursor, WriteCursor,
8};
9use num_derive::FromPrimitive;
10use num_traits::FromPrimitive as _;
11
12use crate::codecs::rfx::Block;
13
14const CONTEXT_ID: u8 = 0;
15const TILE_SIZE: u16 = 0x0040;
16const COLOR_CONVERSION_ICT: u16 = 1;
17const CLW_XFORM_DWT_53_A: u16 = 1;
18const SCALAR_QUANTIZATION: u16 = 1;
19const LRF: bool = true;
20const CBT_REGION: u16 = 0xcac1;
21const NUMBER_OF_TILESETS: u16 = 1;
22const CBT_TILESET: u16 = 0xcac2;
23const IDX: u16 = 0;
24const IS_LAST_TILESET_FLAG: bool = true;
25const RECTANGLE_SIZE: usize = 8;
26
27/// [2.2.2.2.4] TS_RFX_CONTEXT
28///
29/// [2.2.2.2.4]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/bde1ce78-5d9e-44c1-8a15-5843fa12270a
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ContextPdu {
32    pub flags: OperatingMode,
33    pub entropy_algorithm: EntropyAlgorithm,
34}
35
36impl ContextPdu {
37    const NAME: &'static str = "RfxContext";
38
39    const FIXED_PART_SIZE: usize = 1 /* ctxId */ + 2 /* tileSize */ + 2 /* properties */;
40}
41
42impl Encode for ContextPdu {
43    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
44        ensure_fixed_part_size!(in: dst);
45
46        dst.write_u8(CONTEXT_ID);
47        dst.write_u16(TILE_SIZE);
48
49        let mut properties: u16 = 0;
50        properties.set_bits(0..3, self.flags.bits());
51        properties.set_bits(3..5, COLOR_CONVERSION_ICT);
52        properties.set_bits(5..9, CLW_XFORM_DWT_53_A);
53        properties.set_bits(9..13, self.entropy_algorithm.as_u16());
54        properties.set_bits(13..15, SCALAR_QUANTIZATION);
55        properties.set_bit(15, false); // reserved
56        dst.write_u16(properties);
57
58        Ok(())
59    }
60
61    fn name(&self) -> &'static str {
62        Self::NAME
63    }
64
65    fn size(&self) -> usize {
66        Self::FIXED_PART_SIZE
67    }
68}
69
70impl<'de> Decode<'de> for ContextPdu {
71    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
72        ensure_fixed_part_size!(in: src);
73
74        let id = src.read_u8();
75        if id != CONTEXT_ID {
76            return Err(invalid_field_err!("ctxId", "Invalid context ID"));
77        }
78
79        let tile_size = src.read_u16();
80        if tile_size != TILE_SIZE {
81            return Err(invalid_field_err!("tileSize", "Invalid tile size"));
82        }
83
84        let properties = src.read_u16();
85        let flags = OperatingMode::from_bits_truncate(properties.get_bits(0..3));
86        let color_conversion_transform = properties.get_bits(3..5);
87        if color_conversion_transform != COLOR_CONVERSION_ICT {
88            return Err(invalid_field_err!("cct", "Invalid color conversion transform"));
89        }
90
91        let dwt = properties.get_bits(5..9);
92        if dwt != CLW_XFORM_DWT_53_A {
93            return Err(invalid_field_err!("dwt", "Invalid DWT"));
94        }
95
96        let entropy_algorithm_bits = properties.get_bits(9..13);
97        let entropy_algorithm = EntropyAlgorithm::from_u16(entropy_algorithm_bits)
98            .ok_or_else(|| invalid_field_err!("entropy_algorithm", "Invalid entropy algorithm"))?;
99
100        let quantization_type = properties.get_bits(13..15);
101        if quantization_type != SCALAR_QUANTIZATION {
102            return Err(invalid_field_err!("qt", "Invalid quantization type"));
103        }
104
105        let _reserved = properties.get_bit(15);
106
107        Ok(Self {
108            flags,
109            entropy_algorithm,
110        })
111    }
112}
113
114/// [2.2.2.3.1] TS_RFX_FRAME_BEGIN
115///
116/// [2.2.2.3.1]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/7a938a26-3fc2-436b-bc84-09dfff59b5e7
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct FrameBeginPdu {
119    pub index: u32,
120    pub number_of_regions: i16,
121}
122
123impl FrameBeginPdu {
124    const NAME: &'static str = "RfxFrameBegin";
125
126    const FIXED_PART_SIZE: usize = 4 /* frameIdx */ + 2 /* numRegions */;
127}
128
129impl Encode for FrameBeginPdu {
130    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
131        ensure_fixed_part_size!(in: dst);
132
133        dst.write_u32(self.index);
134        dst.write_i16(self.number_of_regions);
135
136        Ok(())
137    }
138
139    fn name(&self) -> &'static str {
140        Self::NAME
141    }
142
143    fn size(&self) -> usize {
144        Self::FIXED_PART_SIZE
145    }
146}
147
148impl<'de> Decode<'de> for FrameBeginPdu {
149    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
150        ensure_fixed_part_size!(in: src);
151
152        let index = src.read_u32();
153        let number_of_regions = src.read_i16();
154
155        Ok(Self {
156            index,
157            number_of_regions,
158        })
159    }
160}
161
162/// [2.2.2.3.2] TS_RFX_FRAME_END
163///
164/// [2.2.2.3.1]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/b4cb2676-0268-450b-ad32-72f66d0598e8
165#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct FrameEndPdu;
167
168impl FrameEndPdu {
169    const NAME: &'static str = "RfxFrameEnd";
170
171    const FIXED_PART_SIZE: usize = 0;
172}
173
174impl Encode for FrameEndPdu {
175    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
176        ensure_fixed_part_size!(in: dst);
177
178        Ok(())
179    }
180
181    fn name(&self) -> &'static str {
182        Self::NAME
183    }
184
185    fn size(&self) -> usize {
186        Self::FIXED_PART_SIZE
187    }
188}
189
190impl<'de> Decode<'de> for FrameEndPdu {
191    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
192        ensure_fixed_part_size!(in: src);
193
194        Ok(Self)
195    }
196}
197
198/// [2.2.2.3.3] TS_RFX_REGION
199///
200/// [2.2.2.3.3]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/23d2a1d6-1be0-4357-83eb-998b66ddd4d9
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct RegionPdu {
203    pub rectangles: Vec<RfxRectangle>,
204}
205
206impl RegionPdu {
207    const NAME: &'static str = "RfxRegion";
208
209    const FIXED_PART_SIZE: usize = 1 /* regionFlags */ + 2 /* numRects */ + 2 /* regionType */ + 2 /* numTilesets */;
210}
211
212impl Encode for RegionPdu {
213    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
214        ensure_fixed_part_size!(in: dst);
215
216        let mut region_flags = 0;
217        region_flags.set_bit(0, LRF);
218        dst.write_u8(region_flags);
219
220        dst.write_u16(cast_length!("numRectangles", self.rectangles.len())?);
221        for rectangle in self.rectangles.iter() {
222            rectangle.encode(dst)?;
223        }
224
225        dst.write_u16(CBT_REGION);
226        dst.write_u16(NUMBER_OF_TILESETS);
227
228        Ok(())
229    }
230
231    fn name(&self) -> &'static str {
232        Self::NAME
233    }
234
235    fn size(&self) -> usize {
236        Self::FIXED_PART_SIZE + self.rectangles.len() * RECTANGLE_SIZE
237    }
238}
239
240impl<'de> Decode<'de> for RegionPdu {
241    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
242        ensure_fixed_part_size!(in: src);
243
244        let region_flags = src.read_u8();
245        let lrf = region_flags.get_bit(0);
246        if lrf != LRF {
247            return Err(invalid_field_err!("lrf", "Invalid lrf"));
248        }
249
250        let number_of_rectangles = usize::from(src.read_u16());
251
252        ensure_size!(in: src, size: number_of_rectangles * RECTANGLE_SIZE);
253
254        let rectangles = iter::repeat_with(|| RfxRectangle::decode(src))
255            .take(number_of_rectangles)
256            .collect::<Result<Vec<_>, _>>()?;
257
258        ensure_size!(in: src, size: 4);
259
260        let region_type = src.read_u16();
261        if region_type != CBT_REGION {
262            return Err(invalid_field_err!("regionType", "Invalid region type"));
263        }
264
265        let number_of_tilesets = src.read_u16();
266        if number_of_tilesets != NUMBER_OF_TILESETS {
267            return Err(invalid_field_err!("numTilesets", "Invalid number of tilesets"));
268        }
269
270        Ok(Self { rectangles })
271    }
272}
273
274/// [2.2.2.3.4] TS_RFX_TILESET
275///
276/// [2.2.2.3.4] https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/7c926114-4bea-4c69-a9a1-caa6e88847a6
277#[derive(Debug, Clone, PartialEq, Eq)]
278pub struct TileSetPdu<'a> {
279    pub entropy_algorithm: EntropyAlgorithm,
280    pub quants: Vec<Quant>,
281    pub tiles: Vec<Tile<'a>>,
282}
283
284impl TileSetPdu<'_> {
285    const NAME: &'static str = "RfxTileSet";
286
287    const FIXED_PART_SIZE: usize = 2 /* subtype */ + 2 /* idx */ + 2 /* properties */ + 1 /* numQuant */ + 1 /* tileSize */+ 2 /* numTiles */ + 4 /* tilesDataSize */;
288}
289
290impl Encode for TileSetPdu<'_> {
291    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
292        ensure_fixed_part_size!(in: dst);
293
294        dst.write_u16(CBT_TILESET);
295        dst.write_u16(IDX);
296
297        let mut properties: u16 = 0;
298        properties.set_bit(0, IS_LAST_TILESET_FLAG);
299        properties.set_bits(1..4, OperatingMode::empty().bits()); // The decoder MUST ignore this flag
300        properties.set_bits(4..6, COLOR_CONVERSION_ICT);
301        properties.set_bits(6..10, CLW_XFORM_DWT_53_A);
302        properties.set_bits(10..14, self.entropy_algorithm.as_u16());
303        properties.set_bits(14..16, SCALAR_QUANTIZATION);
304        dst.write_u16(properties);
305
306        dst.write_u8(cast_length!("numQuant", self.quants.len())?);
307        dst.write_u8(u8::try_from(TILE_SIZE).expect("TILE_SIZE value fits into u8"));
308        dst.write_u16(cast_length!("numTiles", self.tiles.len())?);
309
310        let tiles_data_size = self.tiles.iter().map(|t| Block::Tile(t.clone()).size()).sum::<usize>();
311        dst.write_u32(cast_length!("tilesDataSize", tiles_data_size)?);
312
313        for quant in &self.quants {
314            quant.encode(dst)?;
315        }
316
317        for tile in &self.tiles {
318            Block::Tile(tile.clone()).encode(dst)?;
319        }
320
321        Ok(())
322    }
323
324    fn name(&self) -> &'static str {
325        Self::NAME
326    }
327
328    fn size(&self) -> usize {
329        Self::FIXED_PART_SIZE
330            + self.quants.iter().map(Encode::size).sum::<usize>()
331            + self.tiles.iter().map(|t| Block::Tile(t.clone()).size()).sum::<usize>()
332    }
333}
334
335impl<'de> Decode<'de> for TileSetPdu<'de> {
336    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
337        ensure_fixed_part_size!(in: src);
338
339        let subtype = src.read_u16();
340        if subtype != CBT_TILESET {
341            return Err(invalid_field_err!("subtype", "Invalid message type"));
342        }
343
344        let id_of_context = src.read_u16();
345        if id_of_context != IDX {
346            return Err(invalid_field_err!("id_of_context", "Invalid RFX context"));
347        }
348
349        let properties = src.read_u16();
350        let is_last = properties.get_bit(0);
351        if is_last != IS_LAST_TILESET_FLAG {
352            return Err(invalid_field_err!("last", "Invalid last flag"));
353        }
354
355        // The encoder MUST set `flags` value to the value of flags
356        // that is set in the properties field of TS_RFX_CONTEXT.
357        // The decoder MUST ignore this flag and MUST use the flags specified
358        // in the flags field of the TS_RFX_CONTEXT.
359
360        let color_conversion_transform = properties.get_bits(4..6);
361        if color_conversion_transform != COLOR_CONVERSION_ICT {
362            return Err(invalid_field_err!("cct", "Invalid color conversion"));
363        }
364
365        let dwt = properties.get_bits(6..10);
366        if dwt != CLW_XFORM_DWT_53_A {
367            return Err(invalid_field_err!("xft", "Invalid DWT"));
368        }
369
370        let entropy_algorithm_bits = properties.get_bits(10..14);
371        let entropy_algorithm = EntropyAlgorithm::from_u16(entropy_algorithm_bits)
372            .ok_or_else(|| invalid_field_err!("entropy", "Invalid entropy algorithm"))?;
373
374        let quantization_type = properties.get_bits(14..16);
375        if quantization_type != SCALAR_QUANTIZATION {
376            return Err(invalid_field_err!("scalar", "Invalid quantization type"));
377        }
378
379        let number_of_quants = usize::from(src.read_u8());
380
381        let tile_size = u16::from(src.read_u8());
382        if tile_size != TILE_SIZE {
383            return Err(invalid_field_err!("tile_size", "Invalid tile size"));
384        }
385
386        let number_of_tiles = usize::from(src.read_u16());
387        let _tiles_data_size = src.read_u32();
388
389        let quants = iter::repeat_with(|| Quant::decode(src))
390            .take(number_of_quants)
391            .collect::<Result<Vec<_>, _>>()?;
392
393        let tiles = iter::repeat_with(|| Block::decode(src))
394            .take(number_of_tiles)
395            .collect::<Result<Vec<_>, _>>()?;
396
397        let tiles = tiles
398            .into_iter()
399            .map(|b| match b {
400                Block::Tile(tile) => Ok(tile),
401                _ => Err(invalid_field_err!("tile", "Invalid block type, expected Tile")),
402            })
403            .collect::<Result<Vec<_>, _>>()?;
404
405        Ok(Self {
406            entropy_algorithm,
407            quants,
408            tiles,
409        })
410    }
411}
412/// [2.2.2.1.6] TS_RFX_RECT
413///
414/// [2.2.2.1.6]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/26eb819a-955b-4b08-b3a0-997231170059
415#[derive(Debug, Clone, PartialEq, Eq)]
416pub struct RfxRectangle {
417    pub x: u16,
418    pub y: u16,
419    pub width: u16,
420    pub height: u16,
421}
422
423impl RfxRectangle {
424    const NAME: &'static str = "RfxRectangle";
425
426    const FIXED_PART_SIZE: usize = 4 * 2 /* x, y, width, height */;
427}
428
429impl Encode for RfxRectangle {
430    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
431        ensure_fixed_part_size!(in: dst);
432
433        dst.write_u16(self.x);
434        dst.write_u16(self.y);
435        dst.write_u16(self.width);
436        dst.write_u16(self.height);
437
438        Ok(())
439    }
440
441    fn name(&self) -> &'static str {
442        Self::NAME
443    }
444
445    fn size(&self) -> usize {
446        Self::FIXED_PART_SIZE
447    }
448}
449
450impl<'de> Decode<'de> for RfxRectangle {
451    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
452        ensure_fixed_part_size!(in: src);
453
454        let x = src.read_u16();
455        let y = src.read_u16();
456        let width = src.read_u16();
457        let height = src.read_u16();
458
459        Ok(Self { x, y, width, height })
460    }
461}
462
463/// 2.2.2.1.5 TS_RFX_CODEC_QUANT
464///
465/// [2.2.2.1.5]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/3e9c8af4-7539-4c9d-95de-14b1558b902c
466#[derive(Debug, Clone, PartialEq, Eq)]
467pub struct Quant {
468    pub ll3: u8,
469    pub lh3: u8,
470    pub hl3: u8,
471    pub hh3: u8,
472    pub lh2: u8,
473    pub hl2: u8,
474    pub hh2: u8,
475    pub lh1: u8,
476    pub hl1: u8,
477    pub hh1: u8,
478}
479
480// The quantization values control the compression rate and quality. The value
481// range is between 6 and 15. The higher value, the higher compression rate and
482// lower quality.
483//
484// This is the default values being use by the MS RDP server, and we will also
485// use it as our default values for the encoder.
486impl Default for Quant {
487    fn default() -> Self {
488        Self {
489            ll3: 6,
490            lh3: 6,
491            hl3: 6,
492            hh3: 6,
493            lh2: 7,
494            hl2: 7,
495            hh2: 8,
496            lh1: 8,
497            hl1: 8,
498            hh1: 9,
499        }
500    }
501}
502
503impl Quant {
504    const NAME: &'static str = "RfxFrameEnd";
505
506    const FIXED_PART_SIZE: usize = 5 /* 10 * 4 bits */;
507}
508
509impl Encode for Quant {
510    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
511        ensure_fixed_part_size!(in: dst);
512
513        let mut level3 = 0;
514        level3.set_bits(0..4, u16::from(self.ll3));
515        level3.set_bits(4..8, u16::from(self.lh3));
516        level3.set_bits(8..12, u16::from(self.hl3));
517        level3.set_bits(12..16, u16::from(self.hh3));
518
519        let mut level2_with_lh1 = 0;
520        level2_with_lh1.set_bits(0..4, u16::from(self.lh2));
521        level2_with_lh1.set_bits(4..8, u16::from(self.hl2));
522        level2_with_lh1.set_bits(8..12, u16::from(self.hh2));
523        level2_with_lh1.set_bits(12..16, u16::from(self.lh1));
524
525        let mut level1 = 0;
526        level1.set_bits(0..4, self.hl1);
527        level1.set_bits(4..8, self.hh1);
528
529        dst.write_u16(level3);
530        dst.write_u16(level2_with_lh1);
531        dst.write_u8(level1);
532
533        Ok(())
534    }
535
536    fn name(&self) -> &'static str {
537        Self::NAME
538    }
539
540    fn size(&self) -> usize {
541        Self::FIXED_PART_SIZE
542    }
543}
544
545impl<'de> Decode<'de> for Quant {
546    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
547        #![allow(
548            clippy::similar_names,
549            reason = "it’s hard to do better than ll3, lh3, etc without going overly verbose"
550        )]
551
552        ensure_fixed_part_size!(in: src);
553
554        let ll3lh3 = src.read_u8();
555        let ll3 = ll3lh3.get_bits(0..4);
556        let lh3 = ll3lh3.get_bits(4..8);
557
558        let hl3hh3 = src.read_u8();
559        let hl3 = hl3hh3.get_bits(0..4);
560        let hh3 = hl3hh3.get_bits(4..8);
561
562        let lh2hl2 = src.read_u8();
563        let lh2 = lh2hl2.get_bits(0..4);
564        let hl2 = lh2hl2.get_bits(4..8);
565
566        let hh2lh1 = src.read_u8();
567        let hh2 = hh2lh1.get_bits(0..4);
568        let lh1 = hh2lh1.get_bits(4..8);
569
570        let hl1hh1 = src.read_u8();
571        let hl1 = hl1hh1.get_bits(0..4);
572        let hh1 = hl1hh1.get_bits(4..8);
573
574        Ok(Self {
575            ll3,
576            lh3,
577            hl3,
578            hh3,
579            lh2,
580            hl2,
581            hh2,
582            lh1,
583            hl1,
584            hh1,
585        })
586    }
587}
588/// [2.2.2.3.4.1] TS_RFX_TILE
589///
590/// [2.2.2.3.4.1]: https://learn.microsoft.com/pt-br/openspecs/windows_protocols/ms-rdprfx/89e669ed-b6dd-4591-a267-73a72bc6d84e
591#[derive(Debug, Clone, PartialEq, Eq)]
592pub struct Tile<'a> {
593    pub y_quant_index: u8,
594    pub cb_quant_index: u8,
595    pub cr_quant_index: u8,
596
597    pub x: u16,
598    pub y: u16,
599
600    pub y_data: &'a [u8],
601    pub cb_data: &'a [u8],
602    pub cr_data: &'a [u8],
603}
604
605impl Tile<'_> {
606    const NAME: &'static str = "RfxTile";
607
608    const FIXED_PART_SIZE: usize = 1 /* quantIdxY */ + 1 /* quantIdxCb */ + 1 /* quantIdxCr */ + 2 /* xIdx */ + 2 /* yIdx */ + 2 /* YLen */ + 2 /* CbLen */ + 2 /* CrLen */;
609}
610
611impl Encode for Tile<'_> {
612    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
613        ensure_size!(in: dst, size: self.size());
614
615        dst.write_u8(self.y_quant_index);
616        dst.write_u8(self.cb_quant_index);
617        dst.write_u8(self.cr_quant_index);
618
619        dst.write_u16(self.x);
620        dst.write_u16(self.y);
621
622        dst.write_u16(cast_length!("YLen", self.y_data.len())?);
623        dst.write_u16(cast_length!("CbLen", self.cb_data.len())?);
624        dst.write_u16(cast_length!("CrLen", self.cr_data.len())?);
625
626        dst.write_slice(self.y_data);
627        dst.write_slice(self.cb_data);
628        dst.write_slice(self.cr_data);
629
630        Ok(())
631    }
632
633    fn name(&self) -> &'static str {
634        Self::NAME
635    }
636
637    fn size(&self) -> usize {
638        Self::FIXED_PART_SIZE + self.y_data.len() + self.cb_data.len() + self.cr_data.len()
639    }
640}
641
642impl<'de> Decode<'de> for Tile<'de> {
643    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
644        #![allow(clippy::similar_names)] // It’s hard to find better names for cr, cb, etc.
645        ensure_fixed_part_size!(in: src);
646
647        let y_quant_index = src.read_u8();
648        let cb_quant_index = src.read_u8();
649        let cr_quant_index = src.read_u8();
650
651        let x = src.read_u16();
652        let y = src.read_u16();
653
654        let y_component_length = usize::from(src.read_u16());
655        let cb_component_length = usize::from(src.read_u16());
656        let cr_component_length = usize::from(src.read_u16());
657
658        ensure_size!(in: src, size: y_component_length + cb_component_length + cr_component_length);
659
660        let y_data = src.read_slice(y_component_length);
661        let cb_data = src.read_slice(cb_component_length);
662        let cr_data = src.read_slice(cr_component_length);
663
664        Ok(Self {
665            y_quant_index,
666            cb_quant_index,
667            cr_quant_index,
668
669            x,
670            y,
671
672            y_data,
673            cb_data,
674            cr_data,
675        })
676    }
677}
678
679#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)]
680#[repr(u16)]
681pub enum EntropyAlgorithm {
682    Rlgr1 = 0x01,
683    Rlgr3 = 0x04,
684}
685
686impl EntropyAlgorithm {
687    #[expect(
688        clippy::as_conversions,
689        reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
690    )]
691    fn as_u16(self) -> u16 {
692        self as u16
693    }
694}
695
696bitflags! {
697    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
698    pub struct OperatingMode: u16 {
699        const IMAGE_MODE = 0x02; // if not set, the codec is operating in video mode
700    }
701}