Skip to main content

gfwx/color_transform/
mod.rs

1use std::{cmp, io, u8};
2
3use num_traits::cast::NumCast;
4
5use crate::bits::{BitsIOReader, BitsIOWriter, BitsReader, BitsWriter};
6use crate::encode::{signed_code, signed_decode};
7use crate::errors::DecompressError;
8use crate::header;
9
10#[cfg(test)]
11mod test;
12
13#[derive(Clone, Debug, PartialEq)]
14pub struct ChannelTransformFactor {
15    pub src_channel: usize,
16    pub factor: isize,
17}
18
19#[derive(Clone, Debug, PartialEq)]
20pub struct ChannelTransform {
21    pub dest_channel: usize,
22    pub channel_factors: Vec<ChannelTransformFactor>,
23    pub denominator: isize,
24    pub is_chroma: bool,
25}
26
27pub struct ChannelTransformBuilder {
28    dest_channel: usize,
29    channel_factors: Vec<ChannelTransformFactor>,
30    denominator: isize,
31    is_chroma: bool,
32}
33
34impl ChannelTransformBuilder {
35    pub fn with_dest_channel(dest_channel: usize) -> ChannelTransformBuilder {
36        ChannelTransformBuilder {
37            dest_channel,
38            channel_factors: vec![],
39            denominator: 1,
40            is_chroma: false,
41        }
42    }
43
44    pub fn set_chroma(&mut self) -> &mut Self {
45        self.is_chroma = true;
46        self
47    }
48
49    pub fn add_channel_factor(&mut self, src_channel: usize, factor: isize) -> &mut Self {
50        self.channel_factors.push(ChannelTransformFactor {
51            src_channel,
52            factor,
53        });
54        self
55    }
56
57    pub fn set_denominator(&mut self, denominator: isize) -> &mut Self {
58        assert!(
59            self.denominator > 0,
60            "Denominator should be positive integer"
61        );
62        self.denominator = denominator;
63        self
64    }
65
66    pub fn build(&self) -> ChannelTransform {
67        ChannelTransform {
68            dest_channel: self.dest_channel,
69            channel_factors: self.channel_factors.clone(),
70            denominator: self.denominator,
71            is_chroma: self.is_chroma,
72        }
73    }
74}
75
76#[derive(Clone, Debug, Default, PartialEq)]
77pub struct ColorTransformProgram {
78    channel_transforms: Vec<ChannelTransform>,
79}
80
81impl ColorTransformProgram {
82    pub fn new() -> Self {
83        ColorTransformProgram {
84            channel_transforms: vec![],
85        }
86    }
87
88    /// Stores input as yuv444. Does not perform any color transforms, just flags second and
89    /// third channels as chroma channels. Decompressed output also will be in yuv44.
90    pub fn yuv444_to_yuv444() -> Self {
91        let mut program = Self::new();
92
93        program
94            .add_channel_transform(
95                ChannelTransformBuilder::with_dest_channel(1)
96                    .set_chroma()
97                    .build(),
98            )
99            .add_channel_transform(
100                ChannelTransformBuilder::with_dest_channel(2)
101                    .set_chroma()
102                    .build(),
103            );
104        program
105    }
106
107    /// Stores rgb data as approximated YUV444 (real order is UYV).
108    /// performs the following: R -= G (chroma); B -= G (chroma); G += (R + B) / 4 (luma)
109    pub fn rgb_to_yuv() -> Self {
110        let mut program = Self::new();
111
112        program
113            .add_channel_transform(
114                ChannelTransformBuilder::with_dest_channel(0)
115                    .add_channel_factor(1, -1)
116                    .set_chroma()
117                    .build(),
118            )
119            .add_channel_transform(
120                ChannelTransformBuilder::with_dest_channel(2)
121                    .add_channel_factor(1, -1)
122                    .set_chroma()
123                    .build(),
124            )
125            .add_channel_transform(
126                ChannelTransformBuilder::with_dest_channel(1)
127                    .add_channel_factor(0, 1)
128                    .add_channel_factor(2, 1)
129                    .set_denominator(4)
130                    .build(),
131            );
132        program
133    }
134
135    /// Stores bgr data as approximated a710.
136    /// performs the following:
137    /// R -= G (chroma); B -= (G * 2 + R) / 2 (chroma); G += (B * 2 + R * 3) / 8 (luma)
138    pub fn bgr_to_a710() -> Self {
139        let mut program = Self::new();
140
141        program
142            .add_channel_transform(
143                ChannelTransformBuilder::with_dest_channel(2)
144                    .add_channel_factor(1, -1)
145                    .set_chroma()
146                    .build(),
147            )
148            .add_channel_transform(
149                ChannelTransformBuilder::with_dest_channel(0)
150                    .add_channel_factor(1, -2)
151                    .add_channel_factor(2, -1)
152                    .set_denominator(2)
153                    .set_chroma()
154                    .build(),
155            )
156            .add_channel_transform(
157                ChannelTransformBuilder::with_dest_channel(1)
158                    .add_channel_factor(0, 2)
159                    .add_channel_factor(2, 3)
160                    .set_denominator(8)
161                    .build(),
162            );
163        program
164    }
165
166    /// Stores rgb data as approximated a710.
167    /// performs the following:
168    /// R -= G (chroma); B -= (G * 2 + R) / 2 (chroma); G += (B * 2 + R * 3) / 8 (luma)
169    pub fn rgb_to_a710() -> Self {
170        let mut program = Self::new();
171
172        program
173            .add_channel_transform(
174                ChannelTransformBuilder::with_dest_channel(0)
175                    .add_channel_factor(1, -1)
176                    .set_chroma()
177                    .build(),
178            )
179            .add_channel_transform(
180                ChannelTransformBuilder::with_dest_channel(2)
181                    .add_channel_factor(1, -2)
182                    .add_channel_factor(0, -1)
183                    .set_denominator(2)
184                    .set_chroma()
185                    .build(),
186            )
187            .add_channel_transform(
188                ChannelTransformBuilder::with_dest_channel(1)
189                    .add_channel_factor(2, 2)
190                    .add_channel_factor(0, 3)
191                    .set_denominator(8)
192                    .build(),
193            );
194        program
195    }
196
197    pub fn decode(
198        mut buffer: &mut impl io::Read,
199        is_chroma: &mut [bool],
200    ) -> Result<Self, DecompressError> {
201        let mut stream = BitsIOReader::new(&mut buffer);
202        let mut color_transform_program = ColorTransformProgram::new();
203        loop {
204            let dest_channel = signed_decode(&mut stream, 2)?;
205
206            if dest_channel < 0 {
207                break;
208            }
209            let dest_channel = dest_channel as usize;
210
211            if dest_channel >= is_chroma.len() {
212                return Err(DecompressError::Malformed);
213            }
214
215            let mut channel_transform_builder =
216                ChannelTransformBuilder::with_dest_channel(dest_channel);
217            loop {
218                let src_channel = signed_decode(&mut stream, 2)?;
219
220                if src_channel < 0 {
221                    break;
222                }
223                if src_channel as usize >= is_chroma.len() {
224                    return Err(DecompressError::Malformed);
225                }
226
227                let factor = signed_decode(&mut stream, 2)?;
228                channel_transform_builder.add_channel_factor(src_channel as usize, factor as isize);
229            }
230            let denominator = signed_decode(&mut stream, 2)?;
231            if denominator == 0 {
232                return Err(DecompressError::Malformed);
233            }
234            channel_transform_builder.set_denominator(denominator as isize);
235
236            let channel_is_chroma = signed_decode(&mut stream, 2)?;
237
238            if channel_is_chroma != 0 {
239                channel_transform_builder.set_chroma();
240                is_chroma[dest_channel] = true;
241            }
242
243            color_transform_program.add_channel_transform(channel_transform_builder.build());
244        }
245        stream.flush_read_word();
246        Ok(color_transform_program)
247    }
248
249    pub fn add_channel_transform(&mut self, channel_transform: ChannelTransform) -> &mut Self {
250        self.channel_transforms.push(channel_transform);
251        self
252    }
253
254    pub fn is_channel_has_transform(&self, channel: usize) -> bool {
255        self.channel_transforms
256            .iter()
257            .filter(|t| t.dest_channel == channel)
258            .any(|c| c.denominator > 1 || !c.channel_factors.is_empty())
259    }
260
261    pub fn iter(&self) -> impl DoubleEndedIterator<Item = &ChannelTransform> {
262        self.channel_transforms.iter()
263    }
264
265    pub fn encode(
266        &self,
267        channels: usize,
268        mut buffer: &mut impl io::Write,
269    ) -> io::Result<Vec<bool>> {
270        let mut stream = BitsIOWriter::new(&mut buffer);
271        let mut is_chroma = vec![false; channels];
272
273        for channel_transform in &self.channel_transforms {
274            signed_code(channel_transform.dest_channel as i32, &mut stream, 2)?;
275
276            for channel_factor in &channel_transform.channel_factors {
277                signed_code(channel_factor.src_channel as i32, &mut stream, 2)?;
278                signed_code(channel_factor.factor as i32, &mut stream, 2)?;
279            }
280            signed_code(-1, &mut stream, 2)?;
281
282            signed_code(channel_transform.denominator as i32, &mut stream, 2)?;
283            signed_code(channel_transform.is_chroma as i32, &mut stream, 2)?;
284
285            is_chroma[channel_transform.dest_channel] = channel_transform.is_chroma;
286        }
287
288        // end of decode program
289        signed_code(-1, &mut stream, 2)?;
290        stream.flush_write_word()?;
291
292        Ok(is_chroma)
293    }
294
295    fn transform_base<T, F>(
296        &self,
297        image: &[T],
298        header: &header::Header,
299        aux: &mut [i16],
300        channels_in_index: usize,
301        channel_layer: F,
302    ) where
303        T: Into<i16> + Copy,
304        F: Fn(usize, usize, usize) -> usize,
305    {
306        assert!(aux.len() >= image.len());
307
308        let boost = header.get_boost() as i16;
309        let channels = header.channels as usize;
310        let channel_size = header.get_channel_size();
311        let mut is_channel_transformed = vec![false; channels * header.layers as usize];
312
313        for channel_transform in &self.channel_transforms {
314            let dest_base = channel_transform.dest_channel * channel_size;
315
316            for channel_factor in &channel_transform.channel_factors {
317                if is_channel_transformed[channel_factor.src_channel] {
318                    for i in 0..channel_size {
319                        aux[dest_base + i] += aux[channel_factor.src_channel * channel_size + i]
320                            * channel_factor.factor as i16;
321                    }
322                } else {
323                    let boosted_factor = channel_factor.factor as i16 * boost;
324                    let layer = channel_layer(channel_factor.src_channel, channels, channel_size);
325                    for i in 0..channel_size {
326                        aux[dest_base + i] +=
327                            image[layer + i * channels_in_index].into() * boosted_factor;
328                    }
329                }
330            }
331
332            let layer = channel_layer(channel_transform.dest_channel, channels, channel_size);
333            for i in 0..channel_size {
334                aux[dest_base + i] /= channel_transform.denominator as i16;
335                aux[dest_base + i] += image[layer + i * channels_in_index].into() * boost;
336            }
337
338            is_channel_transformed[channel_transform.dest_channel] = true;
339        }
340
341        for (channel, is_transformed) in is_channel_transformed.iter().enumerate() {
342            if !is_transformed {
343                let dest_base = channel * channel_size;
344                let layer = channel_layer(channel, channels, channel_size);
345                for i in 0..channel_size {
346                    aux[dest_base + i] = image[layer + i * channels_in_index].into() * boost;
347                }
348            }
349        }
350    }
351
352    pub fn transform_and_to_planar<T>(
353        &self,
354        image: &[T],
355        header: &header::Header,
356        mut aux: &mut [i16],
357    ) where
358        T: Into<i16> + Copy,
359    {
360        ColorTransformProgram::transform_base(
361            &self,
362            &image,
363            &header,
364            &mut aux,
365            header.channels as usize,
366            get_layer,
367        );
368    }
369
370    pub fn transform<T>(&self, image: &[T], header: &header::Header, mut aux: &mut [i16])
371    where
372        T: Into<i16> + Copy,
373    {
374        ColorTransformProgram::transform_base(
375            &self,
376            &image,
377            &header,
378            &mut aux,
379            1,
380            |channel, _, channel_size| channel * channel_size,
381        );
382    }
383
384    fn detransform_base<'a, T>(
385        &self,
386        aux: &mut [i16],
387        header: &header::Header,
388        channel_size: usize,
389        image: &'a mut [T],
390    ) -> (&'a mut [T], i16)
391    where
392        T: NumCast,
393    {
394        assert!(image.len() >= aux.len());
395
396        for channel_transform in self.channel_transforms.iter().rev() {
397            let mut transform_temp = vec![0_i16; channel_size];
398            let dest_base = channel_transform.dest_channel * channel_size;
399
400            for channel_factor in &channel_transform.channel_factors {
401                for i in 0..channel_size {
402                    transform_temp[i] += aux[channel_factor.src_channel * channel_size + i]
403                        * channel_factor.factor as i16;
404                }
405            }
406
407            for i in 0..channel_size {
408                transform_temp[i] /= channel_transform.denominator as i16;
409                aux[dest_base + i] -= transform_temp[i];
410            }
411        }
412
413        // split off leftover
414        let (image, _) = image.split_at_mut(aux.len());
415
416        let boost = header.get_boost();
417
418        (image, boost)
419    }
420
421    pub fn detransform_and_to_interleaved<T>(
422        &self,
423        mut aux: &mut [i16],
424        header: &header::Header,
425        channel_size: usize,
426        mut image: &mut [T],
427    ) where
428        T: NumCast,
429    {
430        let (image, boost) = ColorTransformProgram::detransform_base(
431            &self,
432            &mut aux,
433            &header,
434            channel_size,
435            &mut image,
436        );
437
438        let channels = header.channels as usize;
439        for c in 0..channels * header.layers as usize {
440            let layer = get_layer(c, channels, channel_size);
441            for i in 0..channel_size {
442                image[layer + i * channels] =
443                    T::from(cut_with_u8(aux[c * channel_size + i] / boost)).unwrap();
444            }
445        }
446    }
447
448    pub fn detransform<T>(
449        &self,
450        mut aux: &mut [i16],
451        header: &header::Header,
452        channel_size: usize,
453        mut image: &mut [T],
454    ) where
455        T: NumCast,
456    {
457        let (image, boost) = ColorTransformProgram::detransform_base(
458            &self,
459            &mut aux,
460            &header,
461            channel_size,
462            &mut image,
463        );
464
465        for (dest, src) in image.iter_mut().zip(aux.iter()) {
466            *dest = T::from(cut_with_u8(*src / boost)).unwrap();
467        }
468    }
469}
470
471fn convert_between_interleaved_and_planar<F, T>(
472    len: usize,
473    channels: usize,
474    skip_channels: &[usize],
475    mut output: &mut [T],
476    func: F,
477) where
478    F: Fn(&mut [T], usize, usize, usize),
479{
480    let channel_size = len / channels;
481
482    let mut skipped = 0;
483    for c in 0..channels {
484        if skip_channels.contains(&c) {
485            skipped += 1;
486            continue;
487        }
488        let dest_base = (c - skipped) * channel_size;
489        let layer = get_layer(c, channels, channel_size);
490        for i in 0..channel_size {
491            func(&mut output, layer, dest_base, i);
492        }
493    }
494}
495
496pub fn interleaved_to_planar<T>(
497    input: &[T],
498    channels: usize,
499    boost: i16,
500    mut output: &mut [i16],
501    skip_channels: &[usize],
502) where
503    T: Into<i16> + Copy,
504{
505    convert_between_interleaved_and_planar(
506        input.len(),
507        channels,
508        &skip_channels,
509        &mut output,
510        |output, layer, dest_base, i| {
511            output[dest_base + i] = input[layer + i * channels].into() * boost;
512        },
513    );
514}
515
516pub fn planar_to_interleaved<T>(
517    input: &[i16],
518    channels: usize,
519    boost: i16,
520    mut output: &mut [T],
521    skip_channels: &[usize],
522) where
523    T: NumCast,
524{
525    convert_between_interleaved_and_planar(
526        output.len(),
527        channels,
528        &skip_channels,
529        &mut output,
530        |output, layer, dest_base, i| {
531            output[layer + i * channels] =
532                T::from(cut_with_u8(input[dest_base + i] / boost)).unwrap();
533        },
534    );
535}
536
537fn get_layer(channel: usize, channels: usize, channel_size: usize) -> usize {
538    (channel / channels) * channel_size * channels + channel % channels
539}
540
541fn cut_with_u8<T>(value: T) -> T
542where
543    T: cmp::Ord + From<u8>,
544{
545    value.min(u8::MAX.into()).max(u8::MIN.into())
546}