Maraiah/source/durandal/image.rs

259 lines
5.4 KiB
Rust

//! Image and color representations.
use crate::durandal::err::*;
use std::io;
/// Creates a RGB8 color from a R5G5B5 format color.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::image::{Color8, r5g5b5_to_rgb8};
///
/// assert_eq!(r5g5b5_to_rgb8(0x2345), Color8::new(64, 208, 40));
/// ```
pub fn r5g5b5_to_rgb8(rgb: u16) -> Color8
{
let r = (rgb >> 10) as u8 & 0x1f;
let g = (rgb >> 5) as u8 & 0x1f;
let b = rgb as u8 & 0x1f;
Color8::new(r << 3, g << 3, b << 3)
}
/// Creates a RGB16 color from a R5G5B5 format color.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::image::{Color16, r5g5b5_to_rgb16};
///
/// assert_eq!(r5g5b5_to_rgb16(0x2345), Color16::new(0x4000, 0xD000, 0x2800));
/// ```
pub fn r5g5b5_to_rgb16(rgb: u16) -> Color16
{
let r = rgb >> 10 & 0x1f;
let g = rgb >> 5 & 0x1f;
let b = rgb & 0x1f;
Color16::new(r << 11, g << 11, b << 11)
}
/// Creates a RGB16 color from a RGB8 format color.
///
/// # Examples
///
/// ```
/// use maraiah::durandal::image;
///
/// let inpl = image::r5g5b5_to_rgb8(0x2345);
/// let inph = image::r5g5b5_to_rgb16(0x2345);
///
/// assert_eq!(inph, image::rgb8_to_rgb16(inpl));
/// ```
pub fn rgb8_to_rgb16(cr: Color8) -> Color16
{
Color16::new(cr.r(), cr.g(), cr.b())
}
/// Writes a PPM file from an image.
///
/// # Errors
///
/// Errors if `out` cannot be written to.
pub fn write_ppm(out: &mut impl io::Write, im: &impl Image) -> ResultS<()>
{
write!(out, "P3\n{} {}\n{}\n", im.w(), im.h(), u16::max_value())?;
for y in 0..im.h() {
for x in 0..im.w() {
let cr = im.index(x, y);
write!(out, "{} {} {} ", cr.r(), cr.g(), cr.b())?;
}
out.write_all(b"\n")?;
}
Ok(())
}
/// Writes a TGA file from an image.
///
/// # Errors
///
/// Errors if `out` cannot be written to.
pub fn write_tga(out: &mut impl io::Write, im: &impl Image) -> ResultS<()>
{
// id len, color map type, image type
out.write_all(&[0, 0, 2])?;
// color map spec
out.write_all(&[0, 0, 0, 0, 0])?;
// x origin
out.write_all(&[0, 0])?;
// y origin
out.write_all(&[0, 0])?;
// width
out.write_all(&u16::to_le_bytes(im.w() as u16))?;
// height
out.write_all(&u16::to_le_bytes(im.h() as u16))?;
// depth, descriptor
out.write_all(&[32, 0])?;
for y in (0..im.h()).rev() {
for x in 0..im.w() {
let cr = im.index(x, y);
out.write_all(&[(cr.b() >> 8) as u8,
(cr.g() >> 8) as u8,
(cr.r() >> 8) as u8,
(cr.a() >> 8) as u8])?;
}
}
Ok(())
}
/// A generic color matrix image.
pub trait Image
{
/// The type of color this image uses for each pixel.
type Output: Color;
/// Returns the width of the image.
fn w(&self) -> usize;
/// Returns the height of the image.
fn h(&self) -> usize;
/// Returns the color of the pixel at column `x` at row `y`.
///
/// Panics if `x` is greater than the width of the image or `y` is greater
/// than the height of the image.
fn index(&self, x: usize, y: usize) -> &Self::Output;
/// The same as `index`, but will not panic if out of bounds.
fn get(&self, x: usize, y: usize) -> Option<&Self::Output>
{
if x < self.w() && y < self.h() {
Some(self.index(x, y))
} else {
None
}
}
}
/// Any color which may be represented as RGBA16.
pub trait Color: Sized + Copy + Clone
{
/// Returns the red component.
fn r(&self) -> u16;
/// Returns the green component.
fn g(&self) -> u16;
/// Returns the blue component.
fn b(&self) -> u16;
/// Returns the alpha component.
fn a(&self) -> u16;
}
impl Image16
{
/// Creates a new Image16.
pub fn new(w: usize, h: usize) -> Self
{
Self{w, h, cr: Vec::with_capacity(w * h)}
}
}
impl Image for Image16
{
type Output = Color16;
fn w(&self) -> usize {self.w}
fn h(&self) -> usize {self.h}
fn index(&self, x: usize, y: usize) -> &Self::Output
{
&self.cr[x + y * self.w]
}
}
impl Image8
{
/// Creates a new Image8.
pub fn new(w: usize, h: usize) -> Self
{
Self{w, h, cr: Vec::with_capacity(w * h)}
}
}
impl Image for Image8
{
type Output = Color8;
fn w(&self) -> usize {self.w}
fn h(&self) -> usize {self.h}
fn index(&self, x: usize, y: usize) -> &Self::Output
{
&self.cr[x + y * self.w]
}
}
impl Color16
{
pub const fn new(r: u16, g: u16, b: u16) -> Self {Self(r, g, b)}
}
impl Color for Color16
{
fn r(&self) -> u16 {self.0}
fn g(&self) -> u16 {self.1}
fn b(&self) -> u16 {self.2}
fn a(&self) -> u16 {u16::max_value()}
}
impl Color8
{
pub const fn new(r: u8, g: u8, b: u8) -> Self {Self(r, g, b)}
}
impl Color for Color8
{
fn r(&self) -> u16 {u16::from(self.0) << 8}
fn g(&self) -> u16 {u16::from(self.1) << 8}
fn b(&self) -> u16 {u16::from(self.2) << 8}
fn a(&self) -> u16 {u16::max_value()}
}
/// An RGB16 color.
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize)]
pub struct Color16(u16, u16, u16);
/// An RGB8 color.
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize)]
pub struct Color8(u8, u8, u8);
/// An RGB16 image.
#[derive(Debug, serde::Serialize)]
pub struct Image16
{
w: usize,
h: usize,
/// The raw color data for this image.
pub cr: Vec<Color16>,
}
/// An RGB8 image.
#[derive(Debug, serde::Serialize)]
pub struct Image8
{
w: usize,
h: usize,
/// The raw color data for this image.
pub cr: Vec<Color8>,
}
// EOF