use crate::{textures::TextureOptions, Color32};
use std::sync::Arc;
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ImageData {
Color(Arc<ColorImage>),
Font(FontImage),
}
impl ImageData {
pub fn size(&self) -> [usize; 2] {
match self {
Self::Color(image) => image.size,
Self::Font(image) => image.size,
}
}
pub fn width(&self) -> usize {
self.size()[0]
}
pub fn height(&self) -> usize {
self.size()[1]
}
pub fn bytes_per_pixel(&self) -> usize {
match self {
Self::Color(_) | Self::Font(_) => 4,
}
}
}
#[derive(Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ColorImage {
pub size: [usize; 2],
pub pixels: Vec<Color32>,
}
impl ColorImage {
pub fn new(size: [usize; 2], color: Color32) -> Self {
Self {
size,
pixels: vec![color; size[0] * size[1]],
}
}
pub fn from_rgba_unmultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
assert_eq!(size[0] * size[1] * 4, rgba.len());
let pixels = rgba
.chunks_exact(4)
.map(|p| Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))
.collect();
Self { size, pixels }
}
pub fn from_rgba_premultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
assert_eq!(size[0] * size[1] * 4, rgba.len());
let pixels = rgba
.chunks_exact(4)
.map(|p| Color32::from_rgba_premultiplied(p[0], p[1], p[2], p[3]))
.collect();
Self { size, pixels }
}
pub fn from_gray(size: [usize; 2], gray: &[u8]) -> Self {
assert_eq!(size[0] * size[1], gray.len());
let pixels = gray.iter().map(|p| Color32::from_gray(*p)).collect();
Self { size, pixels }
}
#[doc(alias = "from_grey_iter")]
pub fn from_gray_iter(size: [usize; 2], gray_iter: impl Iterator<Item = u8>) -> Self {
let pixels: Vec<_> = gray_iter.map(Color32::from_gray).collect();
assert_eq!(size[0] * size[1], pixels.len());
Self { size, pixels }
}
#[cfg(feature = "bytemuck")]
pub fn as_raw(&self) -> &[u8] {
bytemuck::cast_slice(&self.pixels)
}
#[cfg(feature = "bytemuck")]
pub fn as_raw_mut(&mut self) -> &mut [u8] {
bytemuck::cast_slice_mut(&mut self.pixels)
}
pub fn region(&self, region: &emath::Rect, pixels_per_point: Option<f32>) -> Self {
let pixels_per_point = pixels_per_point.unwrap_or(1.0);
let min_x = (region.min.x * pixels_per_point) as usize;
let max_x = (region.max.x * pixels_per_point) as usize;
let min_y = (region.min.y * pixels_per_point) as usize;
let max_y = (region.max.y * pixels_per_point) as usize;
assert!(
min_x <= max_x && min_y <= max_y,
"Screenshot region is invalid: {region:?}"
);
let width = max_x - min_x;
let height = max_y - min_y;
let mut output = Vec::with_capacity(width * height);
let row_stride = self.size[0];
for row in min_y..max_y {
output.extend_from_slice(
&self.pixels[row * row_stride + min_x..row * row_stride + max_x],
);
}
Self {
size: [width, height],
pixels: output,
}
}
pub fn from_rgb(size: [usize; 2], rgb: &[u8]) -> Self {
assert_eq!(size[0] * size[1] * 3, rgb.len());
let pixels = rgb
.chunks_exact(3)
.map(|p| Color32::from_rgb(p[0], p[1], p[2]))
.collect();
Self { size, pixels }
}
pub fn example() -> Self {
let width = 128;
let height = 64;
let mut img = Self::new([width, height], Color32::TRANSPARENT);
for y in 0..height {
for x in 0..width {
let h = x as f32 / width as f32;
let s = 1.0;
let v = 1.0;
let a = y as f32 / height as f32;
img[(x, y)] = crate::Hsva { h, s, v, a }.into();
}
}
img
}
#[inline]
pub fn width(&self) -> usize {
self.size[0]
}
#[inline]
pub fn height(&self) -> usize {
self.size[1]
}
}
impl std::ops::Index<(usize, usize)> for ColorImage {
type Output = Color32;
#[inline]
fn index(&self, (x, y): (usize, usize)) -> &Color32 {
let [w, h] = self.size;
assert!(x < w && y < h);
&self.pixels[y * w + x]
}
}
impl std::ops::IndexMut<(usize, usize)> for ColorImage {
#[inline]
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color32 {
let [w, h] = self.size;
assert!(x < w && y < h);
&mut self.pixels[y * w + x]
}
}
impl From<ColorImage> for ImageData {
#[inline(always)]
fn from(image: ColorImage) -> Self {
Self::Color(Arc::new(image))
}
}
impl From<Arc<ColorImage>> for ImageData {
#[inline]
fn from(image: Arc<ColorImage>) -> Self {
Self::Color(image)
}
}
impl std::fmt::Debug for ColorImage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ColorImage")
.field("size", &self.size)
.field("pixel-count", &self.pixels.len())
.finish_non_exhaustive()
}
}
#[derive(Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct FontImage {
pub size: [usize; 2],
pub pixels: Vec<f32>,
}
impl FontImage {
pub fn new(size: [usize; 2]) -> Self {
Self {
size,
pixels: vec![0.0; size[0] * size[1]],
}
}
#[inline]
pub fn width(&self) -> usize {
self.size[0]
}
#[inline]
pub fn height(&self) -> usize {
self.size[1]
}
#[inline]
pub fn srgba_pixels(&self, gamma: Option<f32>) -> impl ExactSizeIterator<Item = Color32> + '_ {
let gamma = gamma.unwrap_or(0.55);
self.pixels.iter().map(move |coverage| {
let alpha = coverage.powf(gamma);
let a = fast_round(alpha * 255.0);
Color32::from_rgba_premultiplied(a, a, a, a)
})
}
pub fn region(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self {
assert!(x + w <= self.width());
assert!(y + h <= self.height());
let mut pixels = Vec::with_capacity(w * h);
for y in y..y + h {
let offset = y * self.width() + x;
pixels.extend(&self.pixels[offset..(offset + w)]);
}
assert_eq!(pixels.len(), w * h);
Self {
size: [w, h],
pixels,
}
}
}
impl std::ops::Index<(usize, usize)> for FontImage {
type Output = f32;
#[inline]
fn index(&self, (x, y): (usize, usize)) -> &f32 {
let [w, h] = self.size;
assert!(x < w && y < h);
&self.pixels[y * w + x]
}
}
impl std::ops::IndexMut<(usize, usize)> for FontImage {
#[inline]
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut f32 {
let [w, h] = self.size;
assert!(x < w && y < h);
&mut self.pixels[y * w + x]
}
}
impl From<FontImage> for ImageData {
#[inline(always)]
fn from(image: FontImage) -> Self {
Self::Font(image)
}
}
#[inline]
fn fast_round(r: f32) -> u8 {
(r + 0.5) as _ }
#[derive(Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[must_use = "The painter must take care of this"]
pub struct ImageDelta {
pub image: ImageData,
pub options: TextureOptions,
pub pos: Option<[usize; 2]>,
}
impl ImageDelta {
pub fn full(image: impl Into<ImageData>, options: TextureOptions) -> Self {
Self {
image: image.into(),
options,
pos: None,
}
}
pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, options: TextureOptions) -> Self {
Self {
image: image.into(),
options,
pos: Some(pos),
}
}
pub fn is_whole(&self) -> bool {
self.pos.is_none()
}
}