From 15828c2d1fdf00facd49aac1c28a7ef6adea9ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Tue, 18 Jun 2024 10:56:07 +0100 Subject: [PATCH] chore: remove unsafe tag from copy fast --- src/color.rs | 699 +++++++++++++++++++++++++-------------------------- src/util.rs | 10 +- 2 files changed, 354 insertions(+), 355 deletions(-) diff --git a/src/color.rs b/src/color.rs index 1f035352..40ee1e89 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,351 +1,348 @@ -//! Color manipulation functions and constants. - -use crate::util::copy_fast; - -pub const RGB_SIZE: usize = 3; -pub const RGBA_SIZE: usize = 4; -pub const RGB888_SIZE: usize = 3; -pub const XRGB8888_SIZE: usize = 4; -pub const RGB1555_SIZE: usize = 2; -pub const RGB565_SIZE: usize = 2; - -/// Defines the Game Boy pixel type as a buffer -/// with the size of RGB (3 bytes). -pub type Pixel = [u8; RGB_SIZE]; - -/// Defines a transparent Game Boy pixel type as a buffer -/// with the size of RGBA (4 bytes). -pub type PixelAlpha = [u8; RGBA_SIZE]; - -/// Defines a pixel with 5 bits per channel plus a padding -/// bit at the beginning. -pub type PixelRgb1555 = [u8; RGB1555_SIZE]; - -/// Defines a pixel with 5 bits per channel except for the -/// green channel which uses 6 bits. -pub type PixelRgb565 = [u8; RGB565_SIZE]; - -pub fn rgb555_to_rgb888(first: u8, second: u8) -> Pixel { - let r = (first & 0x1f) << 3; - let g = (((first & 0xe0) >> 5) | ((second & 0x03) << 3)) << 3; - let b = ((second & 0x7c) >> 2) << 3; - [r, g, b] -} - -pub fn rgb888_to_rgb1555(first: u8, second: u8, third: u8) -> PixelRgb1555 { - let pixel = rgb888_to_rgb1555_u16(first, second, third); - [pixel as u8, (pixel >> 8) as u8] -} - -pub fn rgb888_to_rgb1555_u16(first: u8, second: u8, third: u8) -> u16 { - let r = (first as u16 >> 3) & 0x1f; - let g = (second as u16 >> 3) & 0x1f; - let b = (third as u16 >> 3) & 0x1f; - let a = 1; - (a << 15) | (r << 10) | (g << 5) | b -} - -pub fn rgb888_to_rgb565(first: u8, second: u8, third: u8) -> PixelRgb565 { - let pixel = rgb888_to_rgb565_u16(first, second, third); - [pixel as u8, (pixel >> 8) as u8] -} - -pub fn rgb888_to_rgb565_u16(first: u8, second: u8, third: u8) -> u16 { - let r = (first as u16 >> 3) & 0x1f; - let g = (second as u16 >> 2) & 0x3f; - let b = (third as u16 >> 3) & 0x1f; - (r << 11) | (g << 5) | b -} - -pub fn rgb888_to_rgb1555_array(rgb888_pixels: &[u8], rgb1555_pixels: &mut [u8]) { - #[cfg(feature = "simd")] - { - rgb888_to_rgb1555_simd(rgb888_pixels, rgb1555_pixels); - } - #[cfg(not(feature = "simd"))] - { - rgb888_to_rgb1555_scalar(rgb888_pixels, rgb1555_pixels); - } -} - -/// Converts an array of RGB888 pixels to RGB565 format using a scalar implementation. -/// -/// This method should provide the same results as the SIMD implementation. -pub fn rgb888_to_rgb1555_scalar(rgb888_pixels: &[u8], rgb1555_pixels: &mut [u8]) { - assert!( - rgb888_pixels.len() % 3 == 0, - "Length of rgb888_pixels must be a multiple of 3" - ); - assert!( - rgb1555_pixels.len() % 2 == 0, - "Length of rgb1555_pixels must be a multiple of 2" - ); - assert!( - rgb888_pixels.len() / 3 == rgb1555_pixels.len() / 2, - "Length of rgb1555_pixels must be two thirds the length of rgb888_pixels" - ); - for index in 0..rgb888_pixels.len() / RGB_SIZE { - let (r, g, b) = ( - rgb888_pixels[index * RGB_SIZE], - rgb888_pixels[index * RGB_SIZE + 1], - rgb888_pixels[index * RGB_SIZE + 2], - ); - let rgb1555 = rgb888_to_rgb1555(r, g, b); - unsafe { - copy_fast( - &rgb1555, - &mut rgb1555_pixels[index * RGB1555_SIZE..index * RGB1555_SIZE + RGB1555_SIZE], - RGB1555_SIZE, - ) - } - } -} - -/// Converts an array of RGB888 pixels to RGB1555 format using SIMD. -/// -/// This method is only available when the `simd` feature is enabled. -/// -/// Note: The length of `rgb888_pixels` must be a multiple of 3, and -/// `rgb1555_pixels` must be a multiple of 2. -#[cfg(feature = "simd")] -pub fn rgb888_to_rgb1555_simd(rgb888_pixels: &[u8], rgb1555_pixels: &mut [u8]) { - use std::simd::u8x16; - - use crate::util::interleave_arrays; - - const SIMD_WIDTH: usize = 16; - - assert!( - rgb888_pixels.len() % 3 == 0, - "Length of rgb888_pixels must be a multiple of 3" - ); - assert!( - rgb1555_pixels.len() % 2 == 0, - "Length of rgb1555_pixels must be a multiple of 2" - ); - assert!( - rgb888_pixels.len() / 3 == rgb1555_pixels.len() / 2, - "Length of rgb1555_pixels must be two thirds the length of rgb888_pixels" - ); - - let num_pixels = rgb888_pixels.len() / 3; - let simd_chunks = num_pixels / SIMD_WIDTH; - - for index in 0..simd_chunks { - let offset = index * SIMD_WIDTH * 3; - let r = u8x16::from_slice(&[ - rgb888_pixels[offset], - rgb888_pixels[offset + 3], - rgb888_pixels[offset + 6], - rgb888_pixels[offset + 9], - rgb888_pixels[offset + 12], - rgb888_pixels[offset + 15], - rgb888_pixels[offset + 18], - rgb888_pixels[offset + 21], - rgb888_pixels[offset + 24], - rgb888_pixels[offset + 27], - rgb888_pixels[offset + 30], - rgb888_pixels[offset + 33], - rgb888_pixels[offset + 36], - rgb888_pixels[offset + 39], - rgb888_pixels[offset + 42], - rgb888_pixels[offset + 45], - ]); - let g = u8x16::from_slice(&[ - rgb888_pixels[offset + 1], - rgb888_pixels[offset + 4], - rgb888_pixels[offset + 7], - rgb888_pixels[offset + 10], - rgb888_pixels[offset + 13], - rgb888_pixels[offset + 16], - rgb888_pixels[offset + 19], - rgb888_pixels[offset + 22], - rgb888_pixels[offset + 25], - rgb888_pixels[offset + 28], - rgb888_pixels[offset + 31], - rgb888_pixels[offset + 34], - rgb888_pixels[offset + 37], - rgb888_pixels[offset + 40], - rgb888_pixels[offset + 43], - rgb888_pixels[offset + 46], - ]); - let b = u8x16::from_slice(&[ - rgb888_pixels[offset + 2], - rgb888_pixels[offset + 5], - rgb888_pixels[offset + 8], - rgb888_pixels[offset + 11], - rgb888_pixels[offset + 14], - rgb888_pixels[offset + 17], - rgb888_pixels[offset + 20], - rgb888_pixels[offset + 23], - rgb888_pixels[offset + 26], - rgb888_pixels[offset + 29], - rgb888_pixels[offset + 32], - rgb888_pixels[offset + 35], - rgb888_pixels[offset + 38], - rgb888_pixels[offset + 41], - rgb888_pixels[offset + 44], - rgb888_pixels[offset + 47], - ]); - - let r_shifted_high = (r >> 1) & u8x16::splat(0x7c); - let g_shifted_high = (g >> 6) & u8x16::splat(0x03); - let g_shifted_low = (g << 2) & u8x16::splat(0xe0); - let b_shifted = (b >> 3) & u8x16::splat(0x3f); - - let high_byte = u8x16::splat(0x80) | r_shifted_high | g_shifted_high; - let low_byte = g_shifted_low | b_shifted; - - let output_offset = index * SIMD_WIDTH * RGB1555_SIZE; - interleave_arrays( - low_byte.as_array(), - high_byte.as_array(), - &mut rgb1555_pixels[output_offset..output_offset + SIMD_WIDTH * RGB1555_SIZE], - ); - } - - let remainder = num_pixels % SIMD_WIDTH; - let offset = simd_chunks * SIMD_WIDTH * RGB_SIZE; - let offset_rgb1555 = simd_chunks * SIMD_WIDTH * RGB1555_SIZE; - - for index in 0..remainder { - let current_offset = offset + index * RGB_SIZE; - let (r, g, b) = ( - rgb888_pixels[current_offset], - rgb888_pixels[current_offset + 1], - rgb888_pixels[current_offset + 2], - ); - let rgb1555 = rgb888_to_rgb1555(r, g, b); - let output_offset = offset_rgb1555 + index * RGB1555_SIZE; - unsafe { - copy_fast( - &rgb1555, - &mut rgb1555_pixels[output_offset..output_offset + RGB1555_SIZE], - RGB1555_SIZE, - ); - } - } -} - -#[cfg(test)] -mod tests { - use super::{rgb888_to_rgb1555, rgb888_to_rgb1555_scalar}; - - #[test] - fn test_rgb888_to_rgb1555() { - let result = rgb888_to_rgb1555(255, 0, 0); - assert_eq!(result, [0b00000000, 0b11111100]); - - let result = rgb888_to_rgb1555(0, 255, 0); - assert_eq!(result, [0b11100000, 0b10000011]); - - let result = rgb888_to_rgb1555(0, 0, 255); - assert_eq!(result, [0b00011111, 0b10000000]); - - let result = rgb888_to_rgb1555(255, 255, 0); - assert_eq!(result, [0b11100000, 0b11111111]); - } - - #[test] - fn test_rgb888_to_rgb1555_scalar() { - let rgb888_pixels: Vec<u8> = vec![ - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 020, 020, 200, // Blueish - ]; - let mut rgb1555_pixels: Vec<u8> = vec![0; 36]; - - rgb888_to_rgb1555_scalar(&rgb888_pixels, &mut rgb1555_pixels); - - let expected_rgb1555: Vec<u8> = vec![ - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b01011001, 0b10001000, // Blueish - ]; - - assert_eq!(rgb1555_pixels, expected_rgb1555); - } - - #[test] - #[cfg(feature = "simd")] - fn test_rgb888_to_rgb1555_simd() { - use super::rgb888_to_rgb1555_simd; - - let rgb888_pixels: Vec<u8> = vec![ - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 000, 255, 000, // Green - 000, 000, 255, // Blue - 255, 255, 000, // Yellow - 255, 000, 000, // Red - 020, 020, 200, // Blueish - ]; - let mut rgb1555_pixels: Vec<u8> = vec![0; 36]; - - rgb888_to_rgb1555_simd(&rgb888_pixels, &mut rgb1555_pixels); - - let expected_rgb1555: Vec<u8> = vec![ - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b11100000, 0b10000011, // Green - 0b00011111, 0b10000000, // Blue - 0b11100000, 0b11111111, // Yellow - 0b00000000, 0b11111100, // Red - 0b01011001, 0b10001000, // Blueish - ]; - - assert_eq!(rgb1555_pixels, expected_rgb1555); - } -} +//! Color manipulation functions and constants. + +use crate::util::copy_fast; + +pub const RGB_SIZE: usize = 3; +pub const RGBA_SIZE: usize = 4; +pub const RGB888_SIZE: usize = 3; +pub const XRGB8888_SIZE: usize = 4; +pub const RGB1555_SIZE: usize = 2; +pub const RGB565_SIZE: usize = 2; + +/// Defines the Game Boy pixel type as a buffer +/// with the size of RGB (3 bytes). +pub type Pixel = [u8; RGB_SIZE]; + +/// Defines a transparent Game Boy pixel type as a buffer +/// with the size of RGBA (4 bytes). +pub type PixelAlpha = [u8; RGBA_SIZE]; + +/// Defines a pixel with 5 bits per channel plus a padding +/// bit at the beginning. +pub type PixelRgb1555 = [u8; RGB1555_SIZE]; + +/// Defines a pixel with 5 bits per channel except for the +/// green channel which uses 6 bits. +pub type PixelRgb565 = [u8; RGB565_SIZE]; + +pub fn rgb555_to_rgb888(first: u8, second: u8) -> Pixel { + let r = (first & 0x1f) << 3; + let g = (((first & 0xe0) >> 5) | ((second & 0x03) << 3)) << 3; + let b = ((second & 0x7c) >> 2) << 3; + [r, g, b] +} + +pub fn rgb888_to_rgb1555(first: u8, second: u8, third: u8) -> PixelRgb1555 { + let pixel = rgb888_to_rgb1555_u16(first, second, third); + [pixel as u8, (pixel >> 8) as u8] +} + +pub fn rgb888_to_rgb1555_u16(first: u8, second: u8, third: u8) -> u16 { + let r = (first as u16 >> 3) & 0x1f; + let g = (second as u16 >> 3) & 0x1f; + let b = (third as u16 >> 3) & 0x1f; + let a = 1; + (a << 15) | (r << 10) | (g << 5) | b +} + +pub fn rgb888_to_rgb565(first: u8, second: u8, third: u8) -> PixelRgb565 { + let pixel = rgb888_to_rgb565_u16(first, second, third); + [pixel as u8, (pixel >> 8) as u8] +} + +pub fn rgb888_to_rgb565_u16(first: u8, second: u8, third: u8) -> u16 { + let r = (first as u16 >> 3) & 0x1f; + let g = (second as u16 >> 2) & 0x3f; + let b = (third as u16 >> 3) & 0x1f; + (r << 11) | (g << 5) | b +} + +pub fn rgb888_to_rgb1555_array(rgb888_pixels: &[u8], rgb1555_pixels: &mut [u8]) { + #[cfg(feature = "simd")] + { + rgb888_to_rgb1555_simd(rgb888_pixels, rgb1555_pixels); + } + #[cfg(not(feature = "simd"))] + { + rgb888_to_rgb1555_scalar(rgb888_pixels, rgb1555_pixels); + } +} + +/// Converts an array of RGB888 pixels to RGB565 format using a scalar implementation. +/// +/// This method should provide the same results as the SIMD implementation. +pub fn rgb888_to_rgb1555_scalar(rgb888_pixels: &[u8], rgb1555_pixels: &mut [u8]) { + assert!( + rgb888_pixels.len() % 3 == 0, + "Length of rgb888_pixels must be a multiple of 3" + ); + assert!( + rgb1555_pixels.len() % 2 == 0, + "Length of rgb1555_pixels must be a multiple of 2" + ); + assert!( + rgb888_pixels.len() / 3 == rgb1555_pixels.len() / 2, + "Length of rgb1555_pixels must be two thirds the length of rgb888_pixels" + ); + for index in 0..rgb888_pixels.len() / RGB_SIZE { + let (r, g, b) = ( + rgb888_pixels[index * RGB_SIZE], + rgb888_pixels[index * RGB_SIZE + 1], + rgb888_pixels[index * RGB_SIZE + 2], + ); + let rgb1555 = rgb888_to_rgb1555(r, g, b); + let output_offset = index * RGB1555_SIZE; + copy_fast( + &rgb1555, + &mut rgb1555_pixels[output_offset..output_offset + RGB1555_SIZE], + RGB1555_SIZE, + ) + } +} + +/// Converts an array of RGB888 pixels to RGB1555 format using SIMD. +/// +/// This method is only available when the `simd` feature is enabled. +/// +/// Note: The length of `rgb888_pixels` must be a multiple of 3, and +/// `rgb1555_pixels` must be a multiple of 2. +#[cfg(feature = "simd")] +pub fn rgb888_to_rgb1555_simd(rgb888_pixels: &[u8], rgb1555_pixels: &mut [u8]) { + use std::simd::u8x16; + + use crate::util::interleave_arrays; + + const SIMD_WIDTH: usize = 16; + + assert!( + rgb888_pixels.len() % 3 == 0, + "Length of rgb888_pixels must be a multiple of 3" + ); + assert!( + rgb1555_pixels.len() % 2 == 0, + "Length of rgb1555_pixels must be a multiple of 2" + ); + assert!( + rgb888_pixels.len() / 3 == rgb1555_pixels.len() / 2, + "Length of rgb1555_pixels must be two thirds the length of rgb888_pixels" + ); + + let num_pixels = rgb888_pixels.len() / 3; + let simd_chunks = num_pixels / SIMD_WIDTH; + + for index in 0..simd_chunks { + let offset = index * SIMD_WIDTH * 3; + let r = u8x16::from_slice(&[ + rgb888_pixels[offset], + rgb888_pixels[offset + 3], + rgb888_pixels[offset + 6], + rgb888_pixels[offset + 9], + rgb888_pixels[offset + 12], + rgb888_pixels[offset + 15], + rgb888_pixels[offset + 18], + rgb888_pixels[offset + 21], + rgb888_pixels[offset + 24], + rgb888_pixels[offset + 27], + rgb888_pixels[offset + 30], + rgb888_pixels[offset + 33], + rgb888_pixels[offset + 36], + rgb888_pixels[offset + 39], + rgb888_pixels[offset + 42], + rgb888_pixels[offset + 45], + ]); + let g = u8x16::from_slice(&[ + rgb888_pixels[offset + 1], + rgb888_pixels[offset + 4], + rgb888_pixels[offset + 7], + rgb888_pixels[offset + 10], + rgb888_pixels[offset + 13], + rgb888_pixels[offset + 16], + rgb888_pixels[offset + 19], + rgb888_pixels[offset + 22], + rgb888_pixels[offset + 25], + rgb888_pixels[offset + 28], + rgb888_pixels[offset + 31], + rgb888_pixels[offset + 34], + rgb888_pixels[offset + 37], + rgb888_pixels[offset + 40], + rgb888_pixels[offset + 43], + rgb888_pixels[offset + 46], + ]); + let b = u8x16::from_slice(&[ + rgb888_pixels[offset + 2], + rgb888_pixels[offset + 5], + rgb888_pixels[offset + 8], + rgb888_pixels[offset + 11], + rgb888_pixels[offset + 14], + rgb888_pixels[offset + 17], + rgb888_pixels[offset + 20], + rgb888_pixels[offset + 23], + rgb888_pixels[offset + 26], + rgb888_pixels[offset + 29], + rgb888_pixels[offset + 32], + rgb888_pixels[offset + 35], + rgb888_pixels[offset + 38], + rgb888_pixels[offset + 41], + rgb888_pixels[offset + 44], + rgb888_pixels[offset + 47], + ]); + + let r_shifted_high = (r >> 1) & u8x16::splat(0x7c); + let g_shifted_high = (g >> 6) & u8x16::splat(0x03); + let g_shifted_low = (g << 2) & u8x16::splat(0xe0); + let b_shifted = (b >> 3) & u8x16::splat(0x3f); + + let high_byte = u8x16::splat(0x80) | r_shifted_high | g_shifted_high; + let low_byte = g_shifted_low | b_shifted; + + let output_offset = index * SIMD_WIDTH * RGB1555_SIZE; + interleave_arrays( + low_byte.as_array(), + high_byte.as_array(), + &mut rgb1555_pixels[output_offset..output_offset + SIMD_WIDTH * RGB1555_SIZE], + ); + } + + let remainder = num_pixels % SIMD_WIDTH; + let offset = simd_chunks * SIMD_WIDTH * RGB_SIZE; + let offset_rgb1555 = simd_chunks * SIMD_WIDTH * RGB1555_SIZE; + + for index in 0..remainder { + let current_offset = offset + index * RGB_SIZE; + let (r, g, b) = ( + rgb888_pixels[current_offset], + rgb888_pixels[current_offset + 1], + rgb888_pixels[current_offset + 2], + ); + let rgb1555 = rgb888_to_rgb1555(r, g, b); + let output_offset = offset_rgb1555 + index * RGB1555_SIZE; + copy_fast( + &rgb1555, + &mut rgb1555_pixels[output_offset..output_offset + RGB1555_SIZE], + RGB1555_SIZE, + ); + } +} + +#[cfg(test)] +mod tests { + use super::{rgb888_to_rgb1555, rgb888_to_rgb1555_scalar}; + + #[test] + fn test_rgb888_to_rgb1555() { + let result = rgb888_to_rgb1555(255, 0, 0); + assert_eq!(result, [0b00000000, 0b11111100]); + + let result = rgb888_to_rgb1555(0, 255, 0); + assert_eq!(result, [0b11100000, 0b10000011]); + + let result = rgb888_to_rgb1555(0, 0, 255); + assert_eq!(result, [0b00011111, 0b10000000]); + + let result = rgb888_to_rgb1555(255, 255, 0); + assert_eq!(result, [0b11100000, 0b11111111]); + } + + #[test] + fn test_rgb888_to_rgb1555_scalar() { + let rgb888_pixels: Vec<u8> = vec![ + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 020, 020, 200, // Blueish + ]; + let mut rgb1555_pixels: Vec<u8> = vec![0; 36]; + + rgb888_to_rgb1555_scalar(&rgb888_pixels, &mut rgb1555_pixels); + + let expected_rgb1555: Vec<u8> = vec![ + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b01011001, 0b10001000, // Blueish + ]; + + assert_eq!(rgb1555_pixels, expected_rgb1555); + } + + #[test] + #[cfg(feature = "simd")] + fn test_rgb888_to_rgb1555_simd() { + use super::rgb888_to_rgb1555_simd; + + let rgb888_pixels: Vec<u8> = vec![ + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 000, 255, 000, // Green + 000, 000, 255, // Blue + 255, 255, 000, // Yellow + 255, 000, 000, // Red + 020, 020, 200, // Blueish + ]; + let mut rgb1555_pixels: Vec<u8> = vec![0; 36]; + + rgb888_to_rgb1555_simd(&rgb888_pixels, &mut rgb1555_pixels); + + let expected_rgb1555: Vec<u8> = vec![ + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b11100000, 0b10000011, // Green + 0b00011111, 0b10000000, // Blue + 0b11100000, 0b11111111, // Yellow + 0b00000000, 0b11111100, // Red + 0b01011001, 0b10001000, // Blueish + ]; + + assert_eq!(rgb1555_pixels, expected_rgb1555); + } +} diff --git a/src/util.rs b/src/util.rs index 7d29261a..8d1814b0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -118,13 +118,15 @@ pub fn save_bmp(path: &str, pixels: &[u8], width: u32, height: u32) -> Result<() /// /// This function is optimized for performance and uses pointer-based /// operations to copy the data as fast as possible. -pub unsafe fn copy_fast(src: &[u8], dst: &mut [u8], count: usize) { +pub fn copy_fast(src: &[u8], dst: &mut [u8], count: usize) { debug_assert!(src.len() >= count); debug_assert!(dst.len() >= count); - let src_ptr = src.as_ptr(); - let dst_ptr = dst.as_mut_ptr(); - std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, count); + unsafe { + let src_ptr = src.as_ptr(); + let dst_ptr = dst.as_mut_ptr(); + std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, count); + } } // Interleaves two arrays of bytes into a single array using -- GitLab