use std::borrow::Cow;
use crate::connection::Connection;
use crate::cookie::VoidCookie;
use crate::errors::{ConnectionError, ParseError, ReplyError};
use crate::protocol::xproto::{
get_image, put_image, Drawable, Format, Gcontext, GetImageReply, ImageFormat,
ImageOrder as XprotoImageOrder, Setup, VisualClass, Visualid, Visualtype,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ColorComponent {
width: u8,
shift: u8,
}
impl ColorComponent {
pub fn new(width: u8, shift: u8) -> Result<Self, ParseError> {
if width > 16 || shift >= 32 || shift + width > 32 {
Err(ParseError::InvalidValue)
} else {
Ok(Self { width, shift })
}
}
pub fn width(self) -> u8 {
self.width
}
pub fn shift(self) -> u8 {
self.shift
}
pub fn mask(self) -> u32 {
let mask = (1u32 << self.width) - 1;
mask << self.shift
}
pub fn from_mask(mask: u32) -> Result<Self, ParseError> {
let width = mask.count_ones();
let shift = mask.trailing_zeros();
let result = Self::new(width.try_into().unwrap(), shift.try_into().unwrap())?;
if mask != result.mask() {
Err(ParseError::InvalidValue)
} else {
Ok(result)
}
}
pub fn decode(self, pixel: u32) -> u16 {
let value = (pixel & self.mask()) >> self.shift;
let mut width = self.width;
let mut value = value << (16 - width);
while width < 16 {
value |= value >> width;
width <<= 1;
}
value.try_into().unwrap()
}
pub fn encode(self, intensity: u16) -> u32 {
(u32::from(intensity) >> (16 - self.width)) << self.shift
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelLayout {
red: ColorComponent,
green: ColorComponent,
blue: ColorComponent,
}
impl PixelLayout {
pub fn new(red: ColorComponent, green: ColorComponent, blue: ColorComponent) -> Self {
Self { red, green, blue }
}
pub fn from_visual_type(visual: Visualtype) -> Result<Self, ParseError> {
if visual.class != VisualClass::TRUE_COLOR && visual.class != VisualClass::DIRECT_COLOR {
Err(ParseError::InvalidValue)
} else {
Ok(Self::new(
ColorComponent::from_mask(visual.red_mask)?,
ColorComponent::from_mask(visual.green_mask)?,
ColorComponent::from_mask(visual.blue_mask)?,
))
}
}
pub fn depth(self) -> u8 {
self.red.width + self.green.width + self.blue.width
}
pub fn decode(self, pixel: u32) -> (u16, u16, u16) {
let red = self.red.decode(pixel);
let green = self.green.decode(pixel);
let blue = self.blue.decode(pixel);
(red, green, blue)
}
pub fn encode(self, (red, green, blue): (u16, u16, u16)) -> u32 {
let red = self.red.encode(red);
let green = self.green.encode(green);
let blue = self.blue.encode(blue);
red | green | blue
}
}
fn compute_stride(width: u16, bits_per_pixel: BitsPerPixel, scanline_pad: ScanlinePad) -> usize {
let value = usize::from(width) * usize::from(bits_per_pixel);
scanline_pad.round_to_multiple(value) / 8
}
#[cfg(test)]
mod test_stride {
use super::compute_stride;
#[test]
fn test_stride() {
for &(width, bpp, pad, stride) in &[
(0, 8, 8, 0),
(1, 8, 8, 1),
(2, 8, 8, 2),
(3, 8, 8, 3),
(41, 8, 8, 41),
(0, 8, 16, 0),
(1, 8, 16, 2),
(2, 8, 16, 2),
(3, 8, 16, 4),
(41, 8, 16, 42),
(0, 16, 16, 0),
(1, 16, 16, 2),
(2, 16, 16, 4),
(3, 16, 16, 6),
(41, 16, 16, 82),
(0, 16, 32, 0),
(1, 16, 32, 4),
(2, 16, 32, 4),
(3, 16, 32, 8),
(41, 16, 32, 84),
(0, 32, 32, 0),
(1, 32, 32, 4),
(2, 32, 32, 8),
(3, 32, 32, 12),
(41, 32, 32, 164),
] {
let actual = compute_stride(width, bpp.try_into().unwrap(), pad.try_into().unwrap());
assert_eq!(stride, actual, "width={width}, bpp={bpp}, pad={pad}");
}
}
}
fn find_format(setup: &Setup, depth: u8) -> Result<&Format, ParseError> {
setup
.pixmap_formats
.iter()
.find(|f| f.depth == depth)
.ok_or(ParseError::InvalidValue)
}
macro_rules! number_enum {
{
$(#[$meta:meta])*
$vis:vis enum $enum_name:ident {
$(
$(#[$variant_meta:meta])*
$variant_name:ident = $value:literal,
)*
}
} => {
$(#[$meta])*
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
$vis enum $enum_name {
$(
$(#[$variant_meta])*
$variant_name = $value,
)*
}
impl TryFrom<u8> for $enum_name {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
$($value => Ok(Self::$variant_name),)*
_ => Err(ParseError::InvalidValue),
}
}
}
impl From<$enum_name> for u8 {
fn from(value: $enum_name) -> u8 {
match value {
$($enum_name::$variant_name => $value,)*
}
}
}
impl From<$enum_name> for usize {
fn from(value: $enum_name) -> usize {
u8::from(value).into()
}
}
}
}
number_enum! {
#[non_exhaustive]
pub enum ScanlinePad {
Pad8 = 8,
Pad16 = 16,
Pad32 = 32,
}
}
impl ScanlinePad {
fn round_to_multiple(self, value: usize) -> usize {
let value = value + usize::from(self) - 1;
value - value % usize::from(self)
}
}
#[cfg(test)]
mod test_scanline_pad {
use super::ScanlinePad;
#[test]
fn number_conversions() {
assert_eq!(8_u8, ScanlinePad::Pad8.into());
assert_eq!(16_u8, ScanlinePad::Pad16.into());
assert_eq!(32_u8, ScanlinePad::Pad32.into());
assert_eq!(8.try_into(), Ok(ScanlinePad::Pad8));
assert_eq!(16.try_into(), Ok(ScanlinePad::Pad16));
assert_eq!(32.try_into(), Ok(ScanlinePad::Pad32));
}
#[test]
fn test_round_to_multiple() {
for &(value, pad8, pad16, pad32) in [
(0, 0, 0, 0),
(1, 8, 16, 32),
(2, 8, 16, 32),
(3, 8, 16, 32),
(4, 8, 16, 32),
(5, 8, 16, 32),
(6, 8, 16, 32),
(7, 8, 16, 32),
(8, 8, 16, 32),
(9, 16, 16, 32),
(10, 16, 16, 32),
(11, 16, 16, 32),
(12, 16, 16, 32),
(13, 16, 16, 32),
(14, 16, 16, 32),
(15, 16, 16, 32),
(16, 16, 16, 32),
(17, 24, 32, 32),
(33, 40, 48, 64),
(47, 48, 48, 64),
(48, 48, 48, 64),
(49, 56, 64, 64),
]
.iter()
{
assert_eq!(
pad8,
ScanlinePad::Pad8.round_to_multiple(value),
"value={value} for pad8"
);
assert_eq!(
pad16,
ScanlinePad::Pad16.round_to_multiple(value),
"value={value} for pad16"
);
assert_eq!(
pad32,
ScanlinePad::Pad32.round_to_multiple(value),
"value={value} for pad32"
);
}
}
}
number_enum! {
#[non_exhaustive]
pub enum BitsPerPixel {
B1 = 1,
B4 = 4,
B8 = 8,
B16 = 16,
B24 = 24,
B32 = 32,
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ImageOrder {
LsbFirst,
MsbFirst,
}
impl TryFrom<XprotoImageOrder> for ImageOrder {
type Error = ParseError;
fn try_from(value: XprotoImageOrder) -> Result<Self, ParseError> {
match value {
XprotoImageOrder::LSB_FIRST => Ok(Self::LsbFirst),
XprotoImageOrder::MSB_FIRST => Ok(Self::MsbFirst),
_ => Err(ParseError::InvalidValue),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Image<'a> {
width: u16,
height: u16,
scanline_pad: ScanlinePad,
depth: u8,
bits_per_pixel: BitsPerPixel,
byte_order: ImageOrder,
data: Cow<'a, [u8]>,
}
impl<'a> Image<'a> {
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn scanline_pad(&self) -> ScanlinePad {
self.scanline_pad
}
pub fn depth(&self) -> u8 {
self.depth
}
pub fn bits_per_pixel(&self) -> BitsPerPixel {
self.bits_per_pixel
}
pub fn byte_order(&self) -> ImageOrder {
self.byte_order
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn data_mut(&mut self) -> &mut [u8] {
self.data.to_mut()
}
pub fn new(
width: u16,
height: u16,
scanline_pad: ScanlinePad,
depth: u8,
bits_per_pixel: BitsPerPixel,
byte_order: ImageOrder,
data: Cow<'a, [u8]>,
) -> Result<Self, ParseError> {
let stride = compute_stride(width, bits_per_pixel, scanline_pad);
let expected_size = usize::from(height) * stride;
if data.len() < expected_size {
Err(ParseError::InsufficientData)
} else {
Ok(Self {
width,
height,
scanline_pad,
depth,
bits_per_pixel,
byte_order,
data,
})
}
}
pub fn allocate(
width: u16,
height: u16,
scanline_pad: ScanlinePad,
depth: u8,
bits_per_pixel: BitsPerPixel,
byte_order: ImageOrder,
) -> Self {
let stride = compute_stride(width, bits_per_pixel, scanline_pad);
let data = Cow::Owned(vec![0; usize::from(height) * stride]);
Self {
width,
height,
scanline_pad,
depth,
bits_per_pixel,
byte_order,
data,
}
}
pub fn allocate_native(
width: u16,
height: u16,
depth: u8,
setup: &Setup,
) -> Result<Self, ParseError> {
let format = find_format(setup, depth)?;
Ok(Self::allocate(
width,
height,
format.scanline_pad.try_into()?,
depth,
format.bits_per_pixel.try_into()?,
setup.image_byte_order.try_into()?,
))
}
fn stride(&self) -> usize {
compute_stride(self.width, self.bits_per_pixel, self.scanline_pad)
}
pub fn get(
conn: &impl Connection,
drawable: Drawable,
x: i16,
y: i16,
width: u16,
height: u16,
) -> Result<(Self, Visualid), ReplyError> {
let reply = get_image(
conn,
ImageFormat::Z_PIXMAP,
drawable,
x,
y,
width,
height,
!0,
)?
.reply()?;
let visual = reply.visual;
let image = Self::get_from_reply(conn.setup(), width, height, reply)?;
Ok((image, visual))
}
pub fn get_from_reply(
setup: &Setup,
width: u16,
height: u16,
reply: GetImageReply,
) -> Result<Self, ParseError> {
let format = find_format(setup, reply.depth)?;
Self::new(
width,
height,
format.scanline_pad.try_into()?,
reply.depth,
format.bits_per_pixel.try_into()?,
setup.image_byte_order.try_into()?,
Cow::Owned(reply.data),
)
}
pub fn put<'c, Conn: Connection>(
&self,
conn: &'c Conn,
drawable: Drawable,
gc: Gcontext,
dst_x: i16,
dst_y: i16,
) -> Result<Vec<VoidCookie<'c, Conn>>, ConnectionError> {
self.native(conn.setup())?
.put_impl(conn, drawable, gc, dst_x, dst_y)
}
fn put_impl<'c, Conn: Connection>(
&self,
conn: &'c Conn,
drawable: Drawable,
gc: Gcontext,
dst_x: i16,
dst_y: i16,
) -> Result<Vec<VoidCookie<'c, Conn>>, ConnectionError> {
let max_bytes = conn.maximum_request_bytes();
let put_image_header = 24;
let stride = self.stride();
let lines_per_request = (max_bytes - put_image_header) / stride;
let mut result = Vec::with_capacity(
(usize::from(self.height()) + lines_per_request - 1) / lines_per_request,
);
let lines_per_request = lines_per_request.try_into().unwrap_or(u16::MAX);
assert!(lines_per_request > 0);
let (mut y_offset, mut byte_offset) = (0, 0);
while y_offset < self.height {
let next_lines = lines_per_request.min(self.height - y_offset);
let next_byte_offset = byte_offset + usize::from(next_lines) * stride;
let data = &self.data[byte_offset..next_byte_offset];
result.push(put_image(
conn,
ImageFormat::Z_PIXMAP,
drawable,
gc,
self.width,
next_lines,
dst_x,
dst_y + i16::try_from(y_offset).unwrap(),
0, self.depth,
data,
)?);
y_offset += next_lines;
byte_offset = next_byte_offset;
}
Ok(result)
}
pub fn convert(
&self,
scanline_pad: ScanlinePad,
bits_per_pixel: BitsPerPixel,
byte_order: ImageOrder,
) -> Cow<'_, Self> {
let already_converted = scanline_pad == self.scanline_pad
&& bits_per_pixel == self.bits_per_pixel
&& byte_order == self.byte_order;
if already_converted {
Cow::Borrowed(self)
} else {
let mut copy = Image::allocate(
self.width,
self.height,
scanline_pad,
self.depth,
bits_per_pixel,
byte_order,
);
for y in 0..self.height {
for x in 0..self.width {
copy.put_pixel(x, y, self.get_pixel(x, y))
}
}
Cow::Owned(copy)
}
}
pub fn native(&self, setup: &Setup) -> Result<Cow<'_, Self>, ParseError> {
let format = find_format(setup, self.depth)?;
Ok(self.convert(
format.scanline_pad.try_into()?,
format.bits_per_pixel.try_into()?,
setup.image_byte_order.try_into()?,
))
}
pub fn reencode<'b>(
&'b self,
own: PixelLayout,
output: PixelLayout,
setup: &Setup,
) -> Result<Cow<'b, Self>, ParseError> {
if own == output {
self.native(setup)
} else {
let (width, height) = (self.width(), self.height());
let mut result = Image::allocate_native(width, height, output.depth(), setup)?;
for y in 0..height {
for x in 0..width {
let pixel = self.get_pixel(x, y);
let pixel = output.encode(own.decode(pixel));
result.put_pixel(x, y, pixel);
}
}
Ok(Cow::Owned(result))
}
}
pub fn put_pixel(&mut self, x: u16, y: u16, pixel: u32) {
assert!(x < self.width);
assert!(y < self.height);
let row_start = usize::from(y) * self.stride();
let x = usize::from(x);
let data = self.data.to_mut();
match self.bits_per_pixel {
BitsPerPixel::B1 => {
let (byte, bit) = compute_depth_1_address(x, self.byte_order);
let pixel = ((pixel & 0x01) << bit) as u8;
let old = data[row_start + byte];
let bit_cleared = old & !(1 << bit);
data[row_start + byte] = bit_cleared | pixel;
}
BitsPerPixel::B4 => {
let mut pixel = pixel & 0x0f;
let odd_x = x % 2 == 1;
let mask = if odd_x == (self.byte_order == ImageOrder::MsbFirst) {
pixel <<= 4;
0xf0
} else {
0x0f
};
data[row_start + x / 2] = (data[row_start + x / 2] & !mask) | (pixel as u8);
}
BitsPerPixel::B8 => data[row_start + x] = pixel as u8,
BitsPerPixel::B16 => {
let (p0, p1) = match self.byte_order {
ImageOrder::LsbFirst => (pixel, pixel >> 8),
ImageOrder::MsbFirst => (pixel >> 8, pixel),
};
data[row_start + 2 * x + 1] = p1 as u8;
data[row_start + 2 * x] = p0 as u8;
}
BitsPerPixel::B24 => {
let (p0, p1, p2) = match self.byte_order {
ImageOrder::LsbFirst => (pixel, pixel >> 8, pixel >> 16),
ImageOrder::MsbFirst => (pixel >> 16, pixel >> 8, pixel),
};
data[row_start + 3 * x + 2] = p2 as u8;
data[row_start + 3 * x + 1] = p1 as u8;
data[row_start + 3 * x] = p0 as u8;
}
BitsPerPixel::B32 => {
let (p0, p1, p2, p3) = match self.byte_order {
ImageOrder::LsbFirst => (pixel, pixel >> 8, pixel >> 16, pixel >> 24),
ImageOrder::MsbFirst => (pixel >> 24, pixel >> 16, pixel >> 8, pixel),
};
data[row_start + 4 * x + 3] = p3 as u8;
data[row_start + 4 * x + 2] = p2 as u8;
data[row_start + 4 * x + 1] = p1 as u8;
data[row_start + 4 * x] = p0 as u8;
}
}
}
pub fn get_pixel(&self, x: u16, y: u16) -> u32 {
assert!(x < self.width);
assert!(y < self.height);
let row_start = usize::from(y) * self.stride();
let x = usize::from(x);
match self.bits_per_pixel {
BitsPerPixel::B1 => {
let (byte, bit) = compute_depth_1_address(x, self.byte_order);
((self.data[row_start + byte] >> bit) & 1).into()
}
BitsPerPixel::B4 => {
let byte = u32::from(self.data[row_start + x / 2]);
let odd_x = x % 2 == 1;
if odd_x == (self.byte_order == ImageOrder::MsbFirst) {
byte >> 4
} else {
byte & 0x0f
}
}
BitsPerPixel::B8 => self.data[row_start + x].into(),
BitsPerPixel::B16 => {
let p1 = u32::from(self.data[row_start + 2 * x + 1]);
let p0 = u32::from(self.data[row_start + 2 * x]);
match self.byte_order {
ImageOrder::LsbFirst => p0 | (p1 << 8),
ImageOrder::MsbFirst => p1 | (p0 << 8),
}
}
BitsPerPixel::B24 => {
let p2 = u32::from(self.data[row_start + 3 * x + 2]);
let p1 = u32::from(self.data[row_start + 3 * x + 1]);
let p0 = u32::from(self.data[row_start + 3 * x]);
match self.byte_order {
ImageOrder::LsbFirst => p0 | (p1 << 8) | (p2 << 16),
ImageOrder::MsbFirst => p2 | (p1 << 8) | (p0 << 16),
}
}
BitsPerPixel::B32 => {
let p3 = u32::from(self.data[row_start + 4 * x + 3]);
let p2 = u32::from(self.data[row_start + 4 * x + 2]);
let p1 = u32::from(self.data[row_start + 4 * x + 1]);
let p0 = u32::from(self.data[row_start + 4 * x]);
match self.byte_order {
ImageOrder::LsbFirst => p0 | (p1 << 8) | (p2 << 16) | (p3 << 24),
ImageOrder::MsbFirst => p3 | (p2 << 8) | (p1 << 16) | (p0 << 24),
}
}
}
}
pub fn into_owned(self) -> Image<'static> {
Image {
data: self.data.into_owned().into(),
..self
}
}
}
fn compute_depth_1_address(x: usize, order: ImageOrder) -> (usize, usize) {
let bit = match order {
ImageOrder::MsbFirst => 7 - x % 8,
ImageOrder::LsbFirst => x % 8,
};
(x / 8, bit)
}
#[cfg(test)]
mod test_image {
use super::{BitsPerPixel, Image, ImageOrder, ParseError, ScanlinePad};
use std::borrow::Cow;
#[test]
fn test_new_too_short() {
let depth = 16;
let result = Image::new(
1,
1,
ScanlinePad::Pad16,
depth,
BitsPerPixel::B8,
ImageOrder::MsbFirst,
Cow::Owned(vec![0]),
);
assert_eq!(result.unwrap_err(), ParseError::InsufficientData);
}
#[test]
fn test_new() {
let depth = 16;
let image = Image::new(
2,
1,
ScanlinePad::Pad16,
depth,
BitsPerPixel::B8,
ImageOrder::MsbFirst,
Cow::Owned(vec![42, 125]),
)
.unwrap();
assert_eq!(image.width(), 2);
assert_eq!(image.height(), 1);
assert_eq!(image.scanline_pad(), ScanlinePad::Pad16);
assert_eq!(image.depth(), depth);
assert_eq!(image.bits_per_pixel(), BitsPerPixel::B8);
assert_eq!(image.byte_order(), ImageOrder::MsbFirst);
assert_eq!(image.data(), [42, 125]);
}
#[test]
fn test_into_owned_keeps_owned_data() {
fn with_data(data: Cow<'_, [u8]>) -> *const u8 {
let orig_ptr = data.as_ptr();
let image = Image::new(
1,
1,
ScanlinePad::Pad8,
1,
BitsPerPixel::B1,
ImageOrder::MsbFirst,
data,
)
.unwrap();
assert_eq!(image.data().as_ptr(), orig_ptr);
image.into_owned().data().as_ptr()
}
let data = vec![0];
let orig_ptr = data.as_ptr();
assert_ne!(with_data(Cow::Borrowed(&data)), orig_ptr);
assert_eq!(with_data(Cow::Owned(data)), orig_ptr);
}
#[test]
fn put_pixel_depth1() {
let mut image = Image::allocate(
16,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B1,
ImageOrder::MsbFirst,
);
for x in 0..8 {
image.put_pixel(x, 0, 1);
}
assert_eq!(0b_1111_1111, image.data()[0]);
image.put_pixel(0, 0, 0);
assert_eq!(0b_0111_1111, image.data()[0]);
image.put_pixel(2, 0, 0);
assert_eq!(0b_0101_1111, image.data()[0]);
image.put_pixel(4, 0, 0);
assert_eq!(0b_0101_0111, image.data()[0]);
image.put_pixel(6, 0, 0);
assert_eq!(0b_0101_0101, image.data()[0]);
image.data_mut()[1] = 0;
image.put_pixel(8, 0, 1);
assert_eq!(0b_1000_0000, image.data()[1]);
image.put_pixel(15, 0, 1);
assert_eq!(0b_1000_0001, image.data()[1]);
assert_eq!(0b_0000_0000, image.data()[5]);
image.put_pixel(15, 1, 1);
assert_eq!(0b_0000_0001, image.data()[5]);
}
#[test]
fn put_pixel_depth4() {
let mut image = Image::allocate(
8,
2,
ScanlinePad::Pad16,
1,
BitsPerPixel::B4,
ImageOrder::MsbFirst,
);
for pos in 0..=0xf {
image.put_pixel(pos % 8, pos / 8, pos.into());
}
assert_eq!(
image.data(),
[0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE]
);
}
#[test]
fn put_pixel_depth8() {
let mut image = Image::allocate(
256,
2,
ScanlinePad::Pad8,
1,
BitsPerPixel::B8,
ImageOrder::MsbFirst,
);
for x in 0..=0xff {
image.put_pixel(x, 0, x.into());
}
image.put_pixel(255, 1, 0x1245_89AB);
let expected = (0..=0xff)
.chain((0..0xff).map(|_| 0))
.chain(std::iter::once(0xAB))
.collect::<Vec<_>>();
assert_eq!(image.data(), &expected[..]);
}
#[test]
fn put_pixel_depth16() {
let mut image = Image::allocate(
5,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B16,
ImageOrder::MsbFirst,
);
image.put_pixel(0, 0, 0xAB_36_18_F8);
image.put_pixel(4, 0, 0x12_34_56_78);
image.put_pixel(4, 1, 0xFE_DC_BA_98);
#[rustfmt::skip]
let expected = [
0x18, 0xF8, 0, 0, 0, 0, 0, 0, 0x56, 0x78,
0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0xBA, 0x98,
0, 0,
];
assert_eq!(image.data(), expected);
}
#[test]
fn put_pixel_depth32() {
let mut image = Image::allocate(
2,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B32,
ImageOrder::MsbFirst,
);
image.put_pixel(0, 0, 0xAB_36_18_F8);
image.put_pixel(1, 0, 0x12_34_56_78);
image.put_pixel(1, 1, 0xFE_DC_BA_98);
#[rustfmt::skip]
let expected = [
0xAB, 0x36, 0x18, 0xF8, 0x12, 0x34, 0x56, 0x78,
0x00, 0x00, 0x00, 0x00, 0xFE, 0xDC, 0xBA, 0x98,
];
assert_eq!(image.data(), expected);
}
#[test]
fn get_pixel_depth1() {
let image = Image::new(
16,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B1,
ImageOrder::MsbFirst,
Cow::Borrowed(&DATA),
)
.unwrap();
assert_eq!(1, image.get_pixel(0, 0));
assert_eq!(1, image.get_pixel(10, 0));
assert_eq!(0, image.get_pixel(15, 0));
assert_eq!(0, image.get_pixel(0, 1));
assert_eq!(1, image.get_pixel(10, 1));
assert_eq!(0, image.get_pixel(15, 1));
}
#[test]
fn get_pixel_depth4() {
let image = Image::new(
16,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B4,
ImageOrder::MsbFirst,
Cow::Borrowed(&DATA),
)
.unwrap();
assert_eq!(0xB, image.get_pixel(0, 0));
assert_eq!(0x4, image.get_pixel(10, 0));
assert_eq!(0x7, image.get_pixel(15, 0));
assert_eq!(0x0, image.get_pixel(0, 1));
assert_eq!(0xC, image.get_pixel(10, 1));
assert_eq!(0x9, image.get_pixel(15, 1));
}
#[test]
fn get_pixel_depth8() {
let image = Image::new(
3,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B8,
ImageOrder::MsbFirst,
Cow::Borrowed(&DATA),
)
.unwrap();
assert_eq!(0xAB, image.get_pixel(0, 0));
assert_eq!(0x36, image.get_pixel(1, 0));
assert_eq!(0x18, image.get_pixel(2, 0));
assert_eq!(0x12, image.get_pixel(0, 1));
assert_eq!(0x34, image.get_pixel(1, 1));
assert_eq!(0x56, image.get_pixel(2, 1));
}
#[test]
fn get_pixel_depth16() {
let image = Image::new(
3,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B16,
ImageOrder::MsbFirst,
Cow::Borrowed(&DATA),
)
.unwrap();
assert_eq!(0xAB36, image.get_pixel(0, 0));
assert_eq!(0x18F8, image.get_pixel(1, 0));
assert_eq!(0x1234, image.get_pixel(2, 0));
assert_eq!(0x0000, image.get_pixel(0, 1));
assert_eq!(0x0000, image.get_pixel(1, 1));
assert_eq!(0xFEDC, image.get_pixel(2, 1));
}
#[test]
fn get_pixel_depth32() {
let image = Image::new(
2,
2,
ScanlinePad::Pad32,
1,
BitsPerPixel::B32,
ImageOrder::MsbFirst,
Cow::Borrowed(&DATA),
)
.unwrap();
assert_eq!(0xAB36_18F8, image.get_pixel(0, 0));
assert_eq!(0x1234_5678, image.get_pixel(1, 0));
assert_eq!(0x0000_0000, image.get_pixel(0, 1));
assert_eq!(0xFEDC_BA98, image.get_pixel(1, 1));
}
static DATA: [u8; 16] = [
0xAB, 0x36, 0x18, 0xF8, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xDC, 0xBA,
0x98,
];
}