use ixy::HasSize as _;
use crate::{
core::{Pos, Rect},
ops::{GridRead, GridReadUnchecked, GridWrite, GridWriteUnchecked},
};
pub fn copy_rect<E: Clone>(
src: &impl GridRead<Element = E>,
src_rect: Rect,
dst: &mut impl GridWrite<Element = E>,
dst_rect: Rect,
) {
if src_rect.size() == dst_rect.size() {
dst.fill_rect_iter(dst_rect, src.iter_rect(src_rect).cloned());
} else {
for y in 0..src_rect.height() {
for x in 0..src_rect.width() {
let src_pos = src_rect.top_left() + Pos::new(x, y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
if let Some(value) = src.get(src_pos) {
let _ = dst.set(dst_pos, value.clone());
}
}
}
}
}
pub unsafe fn copy_rect_unchecked<E: Clone>(
src: &impl GridReadUnchecked<Element = E>,
src_rect: Rect,
dst: &mut impl GridWriteUnchecked<Element = E>,
dst_rect: Rect,
) {
if src_rect.size() == dst_rect.size() {
unsafe {
dst.fill_rect_iter_unchecked(dst_rect, src.iter_rect_unchecked(src_rect).cloned());
}
} else {
for y in 0..src_rect.height() {
for x in 0..src_rect.width() {
let src_pos = src_rect.top_left() + Pos::new(x, y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
unsafe {
let value = src.get_unchecked(src_pos);
dst.set_unchecked(dst_pos, value.clone());
}
}
}
}
}
pub fn blit_rect<S: Clone, D>(
src: &impl GridRead<Element = S>,
src_rect: Rect,
dst: &mut (impl GridWrite<Element = D> + GridRead<Element = D>),
dst_rect: Rect,
blend: &impl Fn(&S, &D) -> D,
) {
for y in 0..src_rect.height() {
for x in 0..src_rect.width() {
let src_pos = src_rect.top_left() + Pos::new(x, y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
if let Some(src_value) = src.get(src_pos) {
if let Some(dst_value) = dst.get(dst_pos) {
let blended_value = blend(src_value, dst_value);
let _ = dst.set(dst_pos, blended_value);
}
}
}
}
}
pub unsafe fn blit_rect_unchecked<E: Clone>(
src: &impl GridReadUnchecked<Element = E>,
src_rect: Rect,
dst: &mut (impl GridWriteUnchecked<Element = E> + GridReadUnchecked<Element = E>),
dst_rect: Rect,
blend: &impl Fn(&E, &E) -> E,
) {
for y in 0..src_rect.height() {
for x in 0..src_rect.width() {
let src_pos = src_rect.top_left() + Pos::new(x, y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
let src_value = unsafe { src.get_unchecked(src_pos) };
let dst_value = unsafe { dst.get_unchecked(dst_pos) };
let blended_value = blend(src_value, dst_value);
unsafe { dst.set_unchecked(dst_pos, blended_value) };
}
}
}
pub fn blit_rect_scaled<S: Clone, D>(
src: &impl GridRead<Element = S>,
src_rect: Rect,
dst: &mut (impl GridWrite<Element = D> + GridRead<Element = D>),
dst_rect: Rect,
blend: &impl Fn(&S, &D) -> D,
) {
for y in 0..dst_rect.height() {
for x in 0..dst_rect.width() {
let src_x = x * src_rect.width() / dst_rect.width();
let src_y = y * src_rect.height() / dst_rect.height();
let src_pos = src_rect.top_left() + Pos::new(src_x, src_y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
if let Some(src_value) = src.get(src_pos) {
if let Some(dst_value) = dst.get(dst_pos) {
let blended_value = blend(src_value, dst_value);
let _ = dst.set(dst_pos, blended_value);
}
}
}
}
}
pub fn blit_rect_scaled_unchecked<E: Clone>(
src: &impl GridReadUnchecked<Element = E>,
src_rect: Rect,
dst: &mut (impl GridWriteUnchecked<Element = E> + GridReadUnchecked<Element = E>),
dst_rect: Rect,
blend: &impl Fn(&E, &E) -> E,
) {
for y in 0..dst_rect.height() {
for x in 0..dst_rect.width() {
let src_x = x * src_rect.width() / dst_rect.width();
let src_y = y * src_rect.height() / dst_rect.height();
let src_pos = src_rect.top_left() + Pos::new(src_x, src_y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
let src_value = unsafe { src.get_unchecked(src_pos) };
let dst_value = unsafe { dst.get_unchecked(dst_pos) };
let blended_value = blend(src_value, dst_value);
unsafe { dst.set_unchecked(dst_pos, blended_value) };
}
}
}
pub fn copy_rect_scaled<E: Clone>(
src: &impl GridRead<Element = E>,
src_rect: Rect,
dst: &mut impl GridWrite<Element = E>,
dst_rect: Rect,
) {
if src_rect.size() == dst_rect.size() {
copy_rect(src, src_rect, dst, dst_rect);
return;
}
for y in 0..dst_rect.height() {
for x in 0..dst_rect.width() {
let src_x = x * src_rect.width() / dst_rect.width();
let src_y = y * src_rect.height() / dst_rect.height();
let src_pos = src_rect.top_left() + Pos::new(src_x, src_y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
if let Some(value) = src.get(src_pos) {
let _ = dst.set(dst_pos, value.clone());
}
}
}
}
pub unsafe fn copy_rect_scaled_unchecked<E: Clone>(
src: &impl GridReadUnchecked<Element = E>,
src_rect: Rect,
dst: &mut (impl GridWriteUnchecked<Element = E> + GridReadUnchecked<Element = E>),
dst_rect: Rect,
) {
for y in 0..dst_rect.height() {
for x in 0..dst_rect.width() {
let src_x = x * src_rect.width() / dst_rect.width();
let src_y = y * src_rect.height() / dst_rect.height();
let src_pos = src_rect.top_left() + Pos::new(src_x, src_y);
let dst_pos = dst_rect.top_left() + Pos::new(x, y);
let src_value = unsafe { src.get_unchecked(src_pos) };
unsafe { dst.set_unchecked(dst_pos, src_value.clone()) };
}
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use crate::{
buf::VecGrid,
core::{Rect, RowMajor},
};
use alloc::{vec, vec::Vec};
#[test]
fn copy_rect_same_size_3x3() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
copy_rect(
&src,
Rect::from_ltwh(0, 0, 3, 3),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 2, 3,
4, 5, 6,
7, 8, 9
]
);
}
#[test]
fn copy_rect_same_size_subset_2x2() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
copy_rect(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 2, 2),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 2, 0,
4, 5, 0,
0, 0, 0
]
);
}
#[test]
fn copy_rect_different_size() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
copy_rect(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 2, 0,
4, 5, 0,
0, 0, 0
]
);
}
#[test]
fn copy_rect_scaled_2x2_to_4x4() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(4, 4);
copy_rect_scaled(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 4, 4),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 4, 4))
.copied()
.collect::<Vec<_>>(),
vec![
1, 1, 2, 2,
1, 1, 2, 2,
4, 4, 5, 5,
4, 4, 5, 5
]
);
}
#[test]
fn copy_rect_scaled_2x2_to_3x3() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
copy_rect_scaled(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 1, 2,
1, 1, 2,
4, 4, 5
]
);
}
#[test]
fn copy_rect_scaled_same_size_opt() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
copy_rect_scaled(
&src,
Rect::from_ltwh(0, 0, 3, 3),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 2, 3,
4, 5, 6,
7, 8, 9
]
);
}
struct AddBlend;
impl AddBlend {
#[allow(clippy::unused_self)]
fn blend(&self, src: i32, dst: i32) -> i32 {
src + dst
}
}
#[test]
fn blit_rect_test() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let blend = AddBlend;
let mut dst = VecGrid::<i32, RowMajor>::new_filled(3, 3, 10);
blit_rect(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
&|src, dst| blend.blend(*src, *dst),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
11, 12, 10,
14, 15, 10,
10, 10, 10
]
);
}
#[test]
fn blit_rect_scaled_test() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let blend = AddBlend;
let mut dst = VecGrid::<i32, RowMajor>::new_filled(4, 4, 10);
blit_rect_scaled(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 4, 4),
&|src, dst| blend.blend(*src, *dst),
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 4, 4))
.copied()
.collect::<Vec<_>>(),
vec![
11, 11, 12, 12,
11, 11, 12, 12,
14, 14, 15, 15,
14, 14, 15, 15
]
);
}
#[test]
fn copy_rect_unchecked_same_size() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
unsafe {
copy_rect_unchecked(
&src,
Rect::from_ltwh(0, 0, 3, 3),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
);
}
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 2, 3,
4, 5, 6,
7, 8, 9
]
);
}
#[test]
fn copy_rect_unchecked_different_size() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(3, 3);
unsafe {
copy_rect_unchecked(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
);
}
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
1, 2, 0,
4, 5, 0,
0, 0, 0
]
);
}
#[test]
fn blit_rect_unchecked_test() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new_filled(3, 3, 10);
unsafe {
blit_rect_unchecked(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 3, 3),
&|src, dst| src + dst,
);
}
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 3, 3))
.copied()
.collect::<Vec<_>>(),
vec![
11, 12, 10,
14, 15, 10,
10, 10, 10
]
);
}
#[test]
fn blit_rect_scaled_unchecked_test() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new_filled(4, 4, 10);
blit_rect_scaled_unchecked(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 4, 4),
&|src, dst| src + dst,
);
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 4, 4))
.copied()
.collect::<Vec<_>>(),
vec![
11, 11, 12, 12,
11, 11, 12, 12,
14, 14, 15, 15,
14, 14, 15, 15
]
);
}
#[test]
fn copy_rect_scaled_unchecked_test() {
#[rustfmt::skip]
let src = VecGrid::with_buffer_row_major(vec![
1, 2, 3,
4, 5, 6,
7, 8, 9],
3, 3).unwrap();
let mut dst = VecGrid::<i32, RowMajor>::new(4, 4);
unsafe {
copy_rect_scaled_unchecked(
&src,
Rect::from_ltwh(0, 0, 2, 2),
&mut dst,
Rect::from_ltwh(0, 0, 4, 4),
);
}
#[rustfmt::skip]
assert_eq!(
dst.iter_rect(Rect::from_ltwh(0, 0, 4, 4))
.copied()
.collect::<Vec<_>>(),
vec![
1, 1, 2, 2,
1, 1, 2, 2,
4, 4, 5, 5,
4, 4, 5, 5
]
);
}
}