use crate::draw::{DrawContext, NewDrawContextError};
use sentryshot_util::{Frame, ResetBufferError};
use std::num::NonZeroU16;
use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum PadError {
#[error("reset buffer: {0}")]
ResetBuffer(#[from] ResetBufferError),
#[error("size out of bounds: {0}+{1} > {2}")]
OutOfBounds(u16, u16, u16),
#[error("create draw context: {0}")]
NewDrawContext(#[from] NewDrawContextError),
}
pub fn pad(
src_frame: &Frame,
dst_frame: &mut Frame,
dst_width: NonZeroU16,
dst_height: NonZeroU16,
x: u16,
y: u16,
) -> Result<(), PadError> {
use PadError::*;
let src_width = src_frame.width().get();
let src_height = src_frame.height().get();
if x + src_width > dst_width.get() {
return Err(OutOfBounds(x, src_width, dst_width.get()));
}
if y + src_height > dst_height.get() {
return Err(OutOfBounds(y, src_height, dst_height.get()));
}
let pix_fmt = src_frame.pix_fmt();
dst_frame.set_width(dst_width);
dst_frame.set_height(dst_height);
dst_frame.set_pix_fmt(pix_fmt);
*dst_frame.linesize_mut() = *src_frame.linesize();
let draw = DrawContext::new(src_frame.pix_fmt())?;
dst_frame.reset_buffer(dst_width, dst_height, src_frame.pix_fmt(), 1)?;
let (dst_linesize, dst_data) = dst_frame.line_and_data_mut();
draw.copy_rectangle(
dst_data,
dst_linesize,
src_frame.data(),
src_frame.linesize(),
x,
y,
0,
0,
src_width,
src_height,
);
Ok(())
}
#[allow(clippy::too_many_arguments, clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use pretty_hex::pretty_hex;
use sentryshot_util::pixfmt::PixelFormat;
use std::num::NonZeroU16;
use test_case::test_case;
#[test_case(
2, 2, PixelFormat::RGB24,
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
2, 2, 0, 0;
"rgb24 minimal"
)]
#[test_case(
2, 2, PixelFormat::RGB24,
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
&[0,0,0, 1,1,1, 0,0,0,
2,2,2, 3,3,3, 0,0,0,],
3, 2, 0, 0;
"rgb24 right"
)]
#[test_case(
2, 2, PixelFormat::RGB24,
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
&[0,0,0, 1,1,1,
2,2,2, 3,3,3,
0,0,0, 0,0,0],
2, 3, 0, 0;
"rgb24 bottom"
)]
#[test_case(
2, 2, PixelFormat::RGB24,
&[1,1,1, 2,2,2,
3,3,3, 4,4,4],
&[0,0,0, 1,1,1, 2,2,2,
0,0,0, 3,3,3, 4,4,4],
3, 2, 1, 0;
"rgb24 left"
)]
#[test_case(
2, 2, PixelFormat::RGB24,
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
&[0,0,0, 0,0,0,
0,0,0, 1,1,1,
2,2,2, 3,3,3],
2, 3, 0, 1;
"rgb24 top"
)]
fn test_pad(
src_width: u16,
src_height: u16,
pix_fmt: PixelFormat,
input: &[u8],
want: &[u8],
width: u16,
height: u16,
x: u16,
y: u16,
) {
let src_frame = Frame::from_raw(
input,
pix_fmt,
NonZeroU16::new(src_width).unwrap(),
NonZeroU16::new(src_height).unwrap(),
1,
)
.unwrap();
let mut dst_frame = Frame::new();
pad(
&src_frame,
&mut dst_frame,
NonZeroU16::new(width).unwrap(),
NonZeroU16::new(height).unwrap(),
x,
y,
)
.unwrap();
let mut raw = Vec::new();
dst_frame.copy_to_buffer(&mut raw, 1).unwrap();
assert_eq!(width, dst_frame.width().get());
assert_eq!(height, dst_frame.height().get());
assert_eq!(pix_fmt, dst_frame.pix_fmt());
assert_eq!(pretty_hex(&want), pretty_hex(&raw));
}
#[test_case(
2, 2, PixelFormat::RGB24,
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
2, 2, 1, 0,
PadError::OutOfBounds(1, 2, 2);
"width out of bounds"
)]
#[test_case(
2, 2, PixelFormat::RGB24,
&[0,0,0, 1,1,1,
2,2,2, 3,3,3],
2, 2, 0, 3,
PadError::OutOfBounds(3, 2, 2);
"height out of bounds"
)]
fn test_pad_errors(
src_width: u16,
src_height: u16,
pix_fmt: PixelFormat,
input: &[u8],
width: u16,
height: u16,
x: u16,
y: u16,
want: PadError,
) {
let src_frame = Frame::from_raw(
input,
pix_fmt,
NonZeroU16::new(src_width).unwrap(),
NonZeroU16::new(src_height).unwrap(),
1,
)
.unwrap();
let mut dst_frame = Frame::new();
let result = pad(
&src_frame,
&mut dst_frame,
NonZeroU16::new(width).unwrap(),
NonZeroU16::new(height).unwrap(),
x,
y,
);
assert_eq!(result, Err(want));
}
}