sentryshot_filter/
pad.rs

1// SPDX-License-Identifier: MPL-2.0+
2
3use crate::draw::{DrawContext, NewDrawContextError};
4use sentryshot_util::{Frame, ResetBufferError};
5use std::num::NonZeroU16;
6use thiserror::Error;
7
8#[derive(Debug, Error, PartialEq, Eq)]
9pub enum PadError {
10    #[error("reset buffer: {0}")]
11    ResetBuffer(#[from] ResetBufferError),
12
13    #[error("size out of bounds: {0}+{1} > {2}")]
14    OutOfBounds(u16, u16, u16),
15
16    #[error("create draw context: {0}")]
17    NewDrawContext(#[from] NewDrawContextError),
18}
19
20/// Pads `src_frame` and stores it in `dst_frame`
21///
22/// `dst_frame` will be resized if necessary.
23pub fn pad(
24    src_frame: &Frame,
25    dst_frame: &mut Frame,
26    dst_width: NonZeroU16,
27    dst_height: NonZeroU16,
28    x: u16,
29    y: u16,
30) -> Result<(), PadError> {
31    use PadError::*;
32    let src_width = src_frame.width().get();
33    let src_height = src_frame.height().get();
34
35    if x + src_width > dst_width.get() {
36        return Err(OutOfBounds(x, src_width, dst_width.get()));
37    }
38    if y + src_height > dst_height.get() {
39        return Err(OutOfBounds(y, src_height, dst_height.get()));
40    }
41
42    let pix_fmt = src_frame.pix_fmt();
43
44    dst_frame.set_width(dst_width);
45    dst_frame.set_height(dst_height);
46    dst_frame.set_pix_fmt(pix_fmt);
47
48    // The line size doesn't actually change.
49    *dst_frame.linesize_mut() = *src_frame.linesize();
50
51    let draw = DrawContext::new(src_frame.pix_fmt())?;
52
53    //needs_copy = frame_needs_copy(s, in);
54
55    //if (needs_copy) {
56    //av_log(inlink->dst, AV_LOG_DEBUG, "Direct padding impossible allocating new frame\n");
57    dst_frame.reset_buffer(dst_width, dst_height, src_frame.pix_fmt(), 1)?;
58    /*} else {
59        int i;
60
61        out = in;
62        for (i = 0; i < 4 && out->data[i] && out->linesize[i]; i++) {
63            int hsub = s->draw.hsub[i];
64            int vsub = s->draw.vsub[i];
65            out->data[i] -= (s->x >> hsub) * s->draw.pixelstep[i] +
66                            (s->y >> vsub) * out->linesize[i];
67        }
68    }*/
69
70    /* top bar */
71    /*if (s->y) {
72        ff_fill_rectangle(&s->draw, &s->color,
73                          out->data, out->linesize,
74                          0, 0, s->w, s->y);
75    }
76
77    /* bottom bar */
78    if (s->h > s->y + s->in_h) {
79        ff_fill_rectangle(&s->draw, &s->color,
80                          out->data, out->linesize,
81                          0, s->y + s->in_h, s->w, s->h - s->y - s->in_h);
82    }
83
84    /* left border */
85    ff_fill_rectangle(&s->draw, &s->color, out->data, out->linesize,
86                      0, s->y, s->x, in->height);*/
87
88    let (dst_linesize, dst_data) = dst_frame.line_and_data_mut();
89    draw.copy_rectangle(
90        dst_data,
91        dst_linesize,
92        src_frame.data(),
93        src_frame.linesize(),
94        x,
95        y,
96        0,
97        0,
98        src_width,
99        src_height,
100    );
101    /*if (needs_copy) {
102        ff_copy_rectangle2(&s->draw,
103                          out->data, out->linesize, in->data, in->linesize,
104                          s->x, s->y, 0, 0, in->width, in->height);
105    }
106
107    /* right border */
108    ff_fill_rectangle(&s->draw, &s->color, out->data, out->linesize,
109                      s->x + s->in_w, s->y, s->w - s->x - s->in_w,
110                      in->height);*/
111
112    Ok(())
113}
114
115#[allow(clippy::too_many_arguments, clippy::unwrap_used)]
116#[cfg(test)]
117mod tests {
118    use super::*;
119    use pretty_assertions::assert_eq;
120    use pretty_hex::pretty_hex;
121    use sentryshot_util::pixfmt::PixelFormat;
122    use std::num::NonZeroU16;
123    use test_case::test_case;
124
125    #[test_case(
126        2, 2, PixelFormat::RGB24,
127        &[0,0,0, 1,1,1,
128          2,2,2, 3,3,3],
129        &[0,0,0, 1,1,1,
130          2,2,2, 3,3,3],
131        2, 2, 0, 0;
132        "rgb24 minimal"
133    )]
134    #[test_case(
135        2, 2, PixelFormat::RGB24,
136        &[0,0,0, 1,1,1,
137          2,2,2, 3,3,3],
138        &[0,0,0, 1,1,1, 0,0,0,
139          2,2,2, 3,3,3, 0,0,0,],
140        3, 2, 0, 0;
141        "rgb24 right"
142    )]
143    #[test_case(
144        2, 2, PixelFormat::RGB24,
145        &[0,0,0, 1,1,1,
146          2,2,2, 3,3,3],
147        &[0,0,0, 1,1,1,
148          2,2,2, 3,3,3,
149          0,0,0, 0,0,0],
150        2, 3, 0, 0;
151        "rgb24 bottom"
152    )]
153    #[test_case(
154        2, 2, PixelFormat::RGB24,
155        &[1,1,1, 2,2,2,
156          3,3,3, 4,4,4],
157        &[0,0,0, 1,1,1, 2,2,2,
158          0,0,0, 3,3,3, 4,4,4],
159        3, 2, 1, 0;
160        "rgb24 left"
161    )]
162    #[test_case(
163        2, 2, PixelFormat::RGB24,
164        &[0,0,0, 1,1,1,
165          2,2,2, 3,3,3],
166        &[0,0,0, 0,0,0,
167          0,0,0, 1,1,1,
168          2,2,2, 3,3,3],
169        2, 3, 0, 1;
170        "rgb24 top"
171    )]
172    fn test_pad(
173        src_width: u16,
174        src_height: u16,
175        pix_fmt: PixelFormat,
176        input: &[u8],
177        want: &[u8],
178        width: u16,
179        height: u16,
180        x: u16,
181        y: u16,
182    ) {
183        let src_frame = Frame::from_raw(
184            input,
185            pix_fmt,
186            NonZeroU16::new(src_width).unwrap(),
187            NonZeroU16::new(src_height).unwrap(),
188            1,
189        )
190        .unwrap();
191
192        let mut dst_frame = Frame::new();
193        pad(
194            &src_frame,
195            &mut dst_frame,
196            NonZeroU16::new(width).unwrap(),
197            NonZeroU16::new(height).unwrap(),
198            x,
199            y,
200        )
201        .unwrap();
202
203        let mut raw = Vec::new();
204        dst_frame.copy_to_buffer(&mut raw, 1).unwrap();
205
206        assert_eq!(width, dst_frame.width().get());
207        assert_eq!(height, dst_frame.height().get());
208        assert_eq!(pix_fmt, dst_frame.pix_fmt());
209        assert_eq!(pretty_hex(&want), pretty_hex(&raw));
210    }
211
212    #[test_case(
213        2, 2, PixelFormat::RGB24,
214        &[0,0,0, 1,1,1,
215          2,2,2, 3,3,3],
216        2, 2, 1, 0,
217        PadError::OutOfBounds(1, 2, 2);
218        "width out of bounds"
219    )]
220    #[test_case(
221        2, 2, PixelFormat::RGB24,
222        &[0,0,0, 1,1,1,
223          2,2,2, 3,3,3],
224        2, 2, 0, 3,
225        PadError::OutOfBounds(3, 2, 2);
226        "height out of bounds"
227    )]
228    fn test_pad_errors(
229        src_width: u16,
230        src_height: u16,
231        pix_fmt: PixelFormat,
232        input: &[u8],
233        width: u16,
234        height: u16,
235        x: u16,
236        y: u16,
237        want: PadError,
238    ) {
239        let src_frame = Frame::from_raw(
240            input,
241            pix_fmt,
242            NonZeroU16::new(src_width).unwrap(),
243            NonZeroU16::new(src_height).unwrap(),
244            1,
245        )
246        .unwrap();
247
248        let mut dst_frame = Frame::new();
249        let result = pad(
250            &src_frame,
251            &mut dst_frame,
252            NonZeroU16::new(width).unwrap(),
253            NonZeroU16::new(height).unwrap(),
254            x,
255            y,
256        );
257        assert_eq!(result, Err(want));
258    }
259}