Skip to content
Snippets Groups Projects

Support for SIMD operations for RGG1555

Merged João Magalhães requested to merge joamag/simd into master
Files
3
src/color.rs 0 → 100644
+ 347
0
//! 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);
copy_fast(
&rgb1555,
&mut rgb1555_pixels[index * RGB1555_SIZE..index * RGB1555_SIZE + 1],
RGB565_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 * RGB565_SIZE;
interleave_arrays(
low_byte.as_array(),
high_byte.as_array(),
&mut rgb1555_pixels[output_offset..output_offset + SIMD_WIDTH * RGB565_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 * RGB565_SIZE;
copy_fast(
&rgb1555,
&mut rgb1555_pixels[output_offset..output_offset + 1],
RGB565_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);
}
}
Loading