Files
another-boids-in-rust/vendor/x11rb/src/image.rs

1299 lines
42 KiB
Rust

//! Utility code for working with images.
//!
//! This module contains the [`Image`] struct which represents an arbitrary image in
//! `ImageFormat::ZPixmap`. If you do not know what `ZPixmap` means, then rest assured that you do
//! not want to know the details. It suffices to know that the values of the individual pixels are
//! saved one after another in memory.
//!
//! An [`Image`] can be converted to a different internal representation. [`Image::native`]
//! converts it to the native format of the X11 server. These conversions do not change the actual
//! content of the image, but only the way that it is laid out in memory (e.g. byte order and
//! padding). Specifically, there is no support for converting an image to another `depth`.
//!
//! The code in this module is only available when the `image` feature of the library is
//! enabled.
// For future readers:
//
// - Z-Pixmap means that the pixels are right next to each other. I.e. first you have the value of
// the pixel at (0, 0), then (1, 0), .... At the end of the row, there might be some padding
// before the next row begins.
// - XY-Bitmap consists of individual bits. The bits are assigned colors via a GC's foreground and
// background color when uploading to the X11 server.
// - XY-Pixmap consists of bit planes. This means you first get the first bit of each pixel,
// stuffed together into bytes. After all of the first pixels are represented, the second plane
// begins with the second bit of each pixel etc.
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,
};
/// The description of a single color component.
///
/// For example, in an RGB image, pixels are often saved as `0xRRGGBB`, where each letter
/// represents the respective color component. In the example, green has a `width` of 8 (it takes
/// up 8 bits) and a `shift` of `16` (there are 16 less significant bits beyond it). This info is
/// represented as a `ColorComponent`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ColorComponent {
/// Number of bits for the component
width: u8,
/// Offset in an u32 of the component
shift: u8,
}
impl ColorComponent {
/// Create a new color component with the given information.
///
/// The following conditions must be satisfied:
/// - `width <= 16`: color components have at most 16 bits.
/// - `shift < 32`: pixel values have at most 32 bits.
/// - `shift + width <= 32`: pixel values have at most 32 bits.
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 })
}
}
/// Get the bit width of the color component.
pub fn width(self) -> u8 {
self.width
}
/// Get the bit shift of the color component.
pub fn shift(self) -> u8 {
self.shift
}
/// Get the pixel mask representing this color component.
///
/// The mask can be used to mask off other colors in a pixel value. Only the bits that
/// correspond to this color component are set.
/// ```
/// # use x11rb::image::ColorComponent;
/// let red = ColorComponent::new(8, 16)?;
/// assert_eq!(red.mask(), 0xff0000);
/// # Ok::<(), x11rb::errors::ParseError>(())
/// ```
pub fn mask(self) -> u32 {
// Get a mask with 'width' set bits.
let mask = (1u32 << self.width) - 1;
// Shift the mask into the right position
mask << self.shift
}
/// Create a new color component from a color mask.
///
/// This turns a color mask into its individual components.
/// ```
/// # use x11rb::image::ColorComponent;
/// let red1 = ColorComponent::new(8, 16);
/// let red2 = ColorComponent::from_mask(0xff0000);
/// ```
///
/// # Errors
///
/// This function fails if the given value is not a well-formed mask. This means that at least
/// one bit must be set and the set bits must be consecutive.
pub fn from_mask(mask: u32) -> Result<Self, ParseError> {
let width = mask.count_ones();
let shift = mask.trailing_zeros();
// Both width and shift can be at most 32, which should fit into u8.
let result = Self::new(width.try_into().unwrap(), shift.try_into().unwrap())?;
if mask != result.mask() {
Err(ParseError::InvalidValue)
} else {
Ok(result)
}
}
/// Get this color component out of a pixel value.
///
/// This function gets a single pixel value and returns this color component's value in that
/// pixel value, expanded to width 16.
/// ```
/// # use x11rb::image::ColorComponent;
/// let red = ColorComponent::new(8, 16)?;
/// assert_eq!(0xABAB, red.decode(0x78AB_4321));
/// # Ok::<(), x11rb::errors::ParseError>(())
/// ```
pub fn decode(self, pixel: u32) -> u16 {
// Get the color component out
let value = (pixel & self.mask()) >> self.shift;
// Now expand from with `self.width` to width 16.
let mut width = self.width;
let mut value = value << (16 - width);
// Add some low bits by using the high bits
while width < 16 {
value |= value >> width;
width <<= 1;
}
value.try_into().unwrap()
}
/// Encode a color value according to this pixel format.
///
/// ```
/// # use x11rb::image::ColorComponent;
/// let red = ColorComponent::new(8, 16)?;
/// assert_eq!(0xAB0000, red.encode(0xABCD));
/// # Ok::<(), x11rb::errors::ParseError>(())
/// ```
pub fn encode(self, intensity: u16) -> u32 {
// First truncate to width `self.width`, then place at the correct offset.
(u32::from(intensity) >> (16 - self.width)) << self.shift
}
}
/// A collection of color components describing the red, green, and blue components of a pixel.
///
/// A [`ColorComponent`] describes a single color component in an image. This structure describes
/// the `red`, `green`, and `blue` color components by containing a [`ColorComponent`] for each of
/// them.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelLayout {
red: ColorComponent,
green: ColorComponent,
blue: ColorComponent,
}
impl PixelLayout {
/// Create a new pixel layout from the description of each component
pub fn new(red: ColorComponent, green: ColorComponent, blue: ColorComponent) -> Self {
Self { red, green, blue }
}
/// Create a new pixel layout
///
/// This function errors if the visual has a different class than `TrueColor` or `DirectColor`,
/// because color pallets and grayscales are not supported. This function also errors if the
/// mask components of the visual are malformed.
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)?,
))
}
}
/// Get the depth of this pixel layout.
///
/// The depth is the number of significant bits of each pixel value.
pub fn depth(self) -> u8 {
// TODO: I am not quite sure this implementation is correct. The protocol seems to allow
// unused bits in the middle..?
self.red.width + self.green.width + self.blue.width
}
/// Decode a pixel value into its red, green, and blue components.
///
/// This function returns each component expanded to width 16.
/// ```
/// # use x11rb::image::{ColorComponent, PixelLayout};
/// let layout = PixelLayout::new(
/// ColorComponent::new(8, 16)?,
/// ColorComponent::new(8, 8)?,
/// ColorComponent::new(8, 0)?,
/// );
/// assert_eq!((0xABAB, 0x4343, 0x2121), layout.decode(0x78AB_4321));
/// # Ok::<(), x11rb::errors::ParseError>(())
/// ```
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)
}
/// Encode a color value according to this layout.
///
/// ```
/// # use x11rb::image::{ColorComponent, PixelLayout};
/// let layout = PixelLayout::new(
/// ColorComponent::new(8, 16)?,
/// ColorComponent::new(8, 8)?,
/// ColorComponent::new(8, 0)?,
/// );
/// assert_eq!(0x00AB_4321, layout.encode((0xABAB, 0x4343, 0x2121)));
/// # Ok::<(), x11rb::errors::ParseError>(())
/// ```
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
}
}
// Compute the stride based on some information of the image
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 &[
// bpp=pad=8
(0, 8, 8, 0),
(1, 8, 8, 1),
(2, 8, 8, 2),
(3, 8, 8, 3),
(41, 8, 8, 41),
// bpp=8, pad=16
(0, 8, 16, 0),
(1, 8, 16, 2),
(2, 8, 16, 2),
(3, 8, 16, 4),
(41, 8, 16, 42),
// bpp=16, pad=16
(0, 16, 16, 0),
(1, 16, 16, 2),
(2, 16, 16, 4),
(3, 16, 16, 6),
(41, 16, 16, 82),
// bpp=16, pad=32
(0, 16, 32, 0),
(1, 16, 32, 4),
(2, 16, 32, 4),
(3, 16, 32, 8),
(41, 16, 32, 84),
// bpp=32, pad=32
(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}");
}
}
}
// Find the format with the given depth in `setup.pixmap_formats`.
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! {
/// The padding of scanlines.
///
/// Each line of an image is padded to a multiple of some value. This value is the scanline
/// padding, which this enum represents.
#[non_exhaustive]
pub enum ScanlinePad {
/// Padding to multiples of 8 bits, i.e. no padding.
Pad8 = 8,
/// Padding to multiples of 16 bits, i.e. the next even number of bytes.
Pad16 = 16,
/// Padding to multiples of 32 bits, i.e. the next multiple of 4.
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! {
/// The number of bits required to store one pixel.
///
/// This value is only about the size of one pixel in memory. Other names for it include
/// `bits_per_pixel` or `bpp`. It may be larger than the number of meaningful bits for a pixel
/// value, which is its `depth`.
#[non_exhaustive]
pub enum BitsPerPixel {
/// Each pixel takes one bit of memory.
B1 = 1,
/// Each pixel takes four bits of memory.
B4 = 4,
/// Each pixel takes one byte of memory.
B8 = 8,
/// Each pixel takes two bytes of memory.
B16 = 16,
/// Each pixel takes three bytes of memory.
///
/// This is most likely not what you want to use, even if you have RGB data with 8 bits per
/// channel. This corresponds to `depth=24`, but is almost always stored as four bytes
/// per pixel, which is `BitsPerPixel::B32`.
B24 = 24,
/// Each pixel takes four bytes of memory.
B32 = 32,
}
}
/// Order in which bytes are stored in memory.
///
/// If the numberof bits per pixel is less than 8, then this is the
/// order in which bits are packed into bytes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ImageOrder {
/// Least significant byte first
LsbFirst,
/// Most significant byte first
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),
}
}
}
/// The description of an image.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Image<'a> {
/// Width in pixels.
width: u16,
/// Height in pixels.
height: u16,
/// Right padding on each scanline.
scanline_pad: ScanlinePad,
/// Color depth in bits.
depth: u8,
/// Storage per pixel in bits. Must be >= depth.
bits_per_pixel: BitsPerPixel,
/// Byte order of components.
///
/// This is the nibble order when bits_per_pixel is 4.
byte_order: ImageOrder,
/// The image data.
data: Cow<'a, [u8]>,
}
impl<'a> Image<'a> {
/// The width in pixels.
pub fn width(&self) -> u16 {
self.width
}
/// The height in pixels.
pub fn height(&self) -> u16 {
self.height
}
/// The padding on the right side of each scanline.
///
/// Each scanline is rounded up to some multiple of the `scanline_pad`.
pub fn scanline_pad(&self) -> ScanlinePad {
self.scanline_pad
}
/// Color depth in bits.
pub fn depth(&self) -> u8 {
self.depth
}
/// Number of bits required to store one pixel.
///
/// This is always `>= depth`.
pub fn bits_per_pixel(&self) -> BitsPerPixel {
self.bits_per_pixel
}
/// Order in which bytes are stored in memory.
///
/// If `bits_per_pixel()` is smaller than 8, then this is the order in which bits are packed
/// into bytes.
pub fn byte_order(&self) -> ImageOrder {
self.byte_order
}
/// Raw pixel data.
pub fn data(&self) -> &[u8] {
&self.data
}
/// Mutable access to the raw pixel data.
///
/// If the `Image` was constructed with `Cow::Borrowed`-access to its pixel data, then a copy
/// is made when this method is called.
pub fn data_mut(&mut self) -> &mut [u8] {
self.data.to_mut()
}
/// Construct a new image from existing data.
///
/// This constructs a new `Image` from given `data` without copying this data around. The other
/// parameters describe the format that the image data is in.
///
/// See [`Image::allocate`] for a variant that allocates memory for you and
/// [`Image::allocate_native`] for allocating an image that is in an X11 server's native
/// format.
///
/// # Errors
///
/// The only possible error is that `data.len()` is too short for an image as described by the
/// other parameters.
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,
})
}
}
/// Construct a new, empty image.
///
/// This function allocates memory for a new image in the format that is described by the
/// parameters.
///
/// See [`Image::new`] for a variant that wraps an existing in-memory image in an `Image` and
/// [`Image::allocate_native`] for allocating an image that is in an X11 server's native
/// format.
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,
}
}
/// Construct a new, empty image.
///
/// This function allocates memory for a new image in the format that the X11 server expects.
/// The image will have size `width`x`height` and a depth of `depth`.
///
/// See [`Image::new`] for a variant that wraps an existing in-memory image in an `Image` and
/// [`Image::allocate`] for allocating an image that is in a more general format, not
/// necessarily what the X11 server wants.
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()?,
))
}
/// The stride is the number of bytes that each row of pixel data occupies in memory.
fn stride(&self) -> usize {
compute_stride(self.width, self.bits_per_pixel, self.scanline_pad)
}
/// Get an image from the X11 server.
///
/// This function sends a [`GetImage`](crate::protocol::xproto::GetImageRequest) request, waits
/// for its response and wraps it in a new `Image`. The image and the corresponding visual id
/// are returned.
///
/// The returned image contains the rectangle with top left corner `(x, y)` and size `(width,
/// height)` of the given `drawable`.
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))
}
/// Construct an `Image` from a `GetImageReply`.
///
/// This function takes a `GetImageReply` and wraps it in an `Image`. The given `width` and
/// `height` describe the corresponding values of the `GetImage` request that was used to get
/// the `GetImageReply`.
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),
)
}
/// Put an image to the X11 server.
///
/// This function sends a [`PutImage`](crate::protocol::xproto::PutImageRequest) request. This
/// will upload this image to the given `drawable` to position `(dst_x, dst_y)`.
///
/// The server's maximum request size is honored. This means that a too large `PutImage`
/// request is automatically split up into smaller pieces. Thus, if this function returns an
/// error, the image could already be partially sent.
///
/// Before uploading, the image is translated into the server's native format via
/// [`Image::native`]. This may convert the image to another format, which can be slow. If you
/// intend to upload the same image multiple times, it is likely more efficient to call
/// [`Image::native`] once initially so that the conversion is not repeated on each upload.
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> {
// Upload the image without exceeding the server's maximum request size
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, // left_pad must always be 0 for ZPixmap
self.depth,
data,
)?);
y_offset += next_lines;
byte_offset = next_byte_offset;
}
Ok(result)
}
/// Convert this image into the format specified by the other parameters.
///
/// This function may need to copy the image, hence returns a `Cow`.
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,
);
// This is the slowest possible way to do this. But also the easiest one to implement.
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)
}
}
/// Convert this image into the native format of the X11 server.
///
/// This function may need to copy the image, hence returns a `Cow`.
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()?,
))
}
/// Reencode this image to a different pixel layout / depth.
///
/// Each pixel of this image is interpreted according to `own` and written to the resulting
/// image in the format described by `output`.
///
/// The resulting image is always in the native format as described by `setup`.
pub fn reencode<'b>(
&'b self,
own: PixelLayout,
output: PixelLayout,
setup: &Setup,
) -> Result<Cow<'b, Self>, ParseError> {
if own == output {
self.native(setup)
} else {
// Yay, we get to convert the image :-(
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))
}
}
/// Set a single pixel in this image.
///
/// The pixel at position `(x, y)` will be set to the value `pixel`. `pixel` is truncated to
/// this image's [`Self::bits_per_pixel`].
///
/// If the image was constructed from a `Cow::Borrowed` access to its pixel data, this causes
/// the whole pixel data to be copied. See [`Image::new`] and [`Image::data_mut`].
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;
}
}
}
/// Get the value of a single pixel.
///
/// This function gets the value of the pixel at `(x, y)`.
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);
// TODO Can this code (and the one in put_pixel) be simplified? E.g. handle B4 as a special
// case and copy bits_per_pixel.into() / 8 bytes in other cases?
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),
}
}
}
}
/// Get a version of this image with `'static` lifetime.
///
/// If the image was constructed from a `Cow::Borrowed`, this clones the contained data.
/// Otherwise, this simply returns `self`.
pub fn into_owned(self) -> Image<'static> {
// It would be great if we could just implement ToOwned, but that requires implementing
// Borrow, which we cannot do. Thus, this function exists as a work-around.
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;
// Due to Pad16, this image needs two bytes
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()
}
// Cow::Borrowed is copied
let data = vec![0];
let orig_ptr = data.as_ptr();
assert_ne!(with_data(Cow::Borrowed(&data)), orig_ptr);
// Cow::Owned is kept
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 = [
// First row
0x18, 0xF8, 0, 0, 0, 0, 0, 0, 0x56, 0x78,
// Padding Pad32
0, 0,
// Second row
0, 0, 0, 0, 0, 0, 0, 0, 0xBA, 0x98,
// Padding Pad32
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 = [
// First row
0xAB, 0x36, 0x18, 0xF8, 0x12, 0x34, 0x56, 0x78,
// Second row
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,
];
}