1use 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
20pub 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 *dst_frame.linesize_mut() = *src_frame.linesize();
50
51 let draw = DrawContext::new(src_frame.pix_fmt())?;
52
53 dst_frame.reset_buffer(dst_width, dst_height, src_frame.pix_fmt(), 1)?;
58 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 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}