use crate::{
ha_renderer::RenderStageResources, math::Rect, render_target::RenderTargetId,
resources::resource_mapping::ResourceMapping, HasContextResources, ResourceReference,
};
use core::id::ID;
use glow::*;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, hash::Hash};
#[derive(Debug, Clone)]
pub enum ImageError {
NoResources,
InvalidSize(usize, usize),
Internal(String),
}
pub type ImageId = ID<Image>;
pub type VirtualImageId = ID<VirtualImage>;
pub type ImageReference = ResourceReference<ImageId, VirtualImageId>;
pub type ImageResourceMapping = ResourceMapping<Image, VirtualImage>;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImageMode {
Image2d,
Image2dArray,
Image3d,
}
impl Default for ImageMode {
fn default() -> Self {
Self::Image2d
}
}
impl ImageMode {
pub fn as_gl(&self) -> u32 {
match self {
Self::Image2d => TEXTURE_2D,
Self::Image2dArray => TEXTURE_2D_ARRAY,
Self::Image3d => TEXTURE_3D,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImageFormat {
RGBA,
RGB,
Luminance,
Data,
}
impl Default for ImageFormat {
fn default() -> Self {
Self::RGBA
}
}
impl ImageFormat {
pub fn as_internal_format_gl(&self) -> u32 {
match self {
Self::RGBA => RGBA,
Self::RGB => RGB,
Self::Luminance => LUMINANCE,
Self::Data => RGBA32F,
}
}
pub fn as_format_gl(&self) -> u32 {
match self {
Self::RGBA => RGBA,
Self::RGB => RGB,
Self::Luminance => LUMINANCE,
Self::Data => RGBA,
}
}
pub fn as_type_gl(&self) -> u32 {
match self {
Self::Data => FLOAT,
_ => UNSIGNED_BYTE,
}
}
pub fn alignment(&self) -> usize {
let size = match self {
Self::RGBA | Self::Data => 4,
_ => 1,
};
std::mem::size_of::<u8>() * size
}
pub fn bytesize(self) -> usize {
let size = match self {
Self::RGBA => 4,
Self::RGB => 3,
Self::Luminance => 1,
Self::Data => 16,
};
std::mem::size_of::<u8>() * size
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ImageFiltering {
Nearest,
Linear,
Bilinear,
}
impl Default for ImageFiltering {
fn default() -> Self {
Self::Linear
}
}
impl ImageFiltering {
pub fn as_gl(&self) -> (u32, u32) {
match self {
ImageFiltering::Nearest => (NEAREST, NEAREST),
ImageFiltering::Linear => (LINEAR, LINEAR),
ImageFiltering::Bilinear => (LINEAR_MIPMAP_LINEAR, LINEAR),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ImageMipmap {
None,
Generate(Option<usize>),
}
impl Default for ImageMipmap {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ImageDescriptor {
#[serde(default)]
pub mode: ImageMode,
#[serde(default)]
pub format: ImageFormat,
#[serde(default)]
pub mipmap: ImageMipmap,
}
#[derive(Debug)]
pub struct ImageResources {
pub handle: <Context as HasContext>::Texture,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ImageDetailedInfo {
pub mode: ImageMode,
pub format: ImageFormat,
pub mipmap: ImageMipmap,
pub width: usize,
pub height: usize,
pub depth: usize,
}
#[derive(Debug)]
pub struct Image {
mode: ImageMode,
format: ImageFormat,
mipmap: ImageMipmap,
width: usize,
height: usize,
depth: usize,
data: Vec<u8>,
resources: Option<ImageResources>,
dirty: bool,
}
impl Drop for Image {
fn drop(&mut self) {
if self.resources.is_some() {
panic!(
"Dropping {} without calling `context_release` to release resources first!",
std::any::type_name::<Self>()
);
}
}
}
impl HasContextResources<Context> for Image {
type Error = ImageError;
fn has_resources(&self) -> bool {
self.resources.is_some()
}
fn context_initialize(&mut self, context: &Context) -> Result<(), Self::Error> {
self.context_release(context)?;
let handle = match unsafe { context.create_texture() } {
Ok(handle) => handle,
Err(error) => return Err(ImageError::Internal(error)),
};
self.resources = Some(ImageResources { handle });
self.maintain(context)
}
fn context_release(&mut self, context: &Context) -> Result<(), Self::Error> {
if let Some(resources) = std::mem::take(&mut self.resources) {
unsafe {
context.delete_texture(resources.handle);
}
}
Ok(())
}
}
impl Image {
pub fn new(
descriptor: ImageDescriptor,
width: usize,
height: usize,
depth: usize,
data: Vec<u8>,
) -> Result<Self, ImageError> {
let ImageDescriptor {
mode,
format,
mipmap,
} = descriptor;
let size = format.bytesize() * width * height * depth;
if size == data.len() {
Ok(Self {
mode,
format,
mipmap,
width,
height,
depth,
data,
resources: None,
dirty: true,
})
} else {
Err(ImageError::InvalidSize(data.len(), size))
}
}
pub fn detailed_info(&self) -> ImageDetailedInfo {
ImageDetailedInfo {
mode: self.mode,
format: self.format,
mipmap: self.mipmap,
width: self.width,
height: self.height,
depth: self.depth,
}
}
pub fn mode(&self) -> ImageMode {
self.mode
}
pub fn format(&self) -> ImageFormat {
self.format
}
pub fn mipmap(&self) -> ImageMipmap {
self.mipmap
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn set_data(&mut self, data: Vec<u8>) -> Result<(), ImageError> {
let size = self.format.bytesize() * self.width * self.height * self.depth;
if size == data.len() {
self.data = data;
self.dirty = true;
Ok(())
} else {
Err(ImageError::InvalidSize(data.len(), size))
}
}
pub fn with_data<F>(&mut self, mut f: F)
where
F: FnMut(&mut [u8]),
{
f(&mut self.data);
self.dirty = true;
}
pub fn overwrite(
&mut self,
width: usize,
height: usize,
depth: usize,
data: Vec<u8>,
) -> Result<(), ImageError> {
let size = self.format.bytesize() * width * height * depth;
if size == data.len() {
self.width = width;
self.height = height;
self.depth = depth;
self.data = data;
self.dirty = true;
Ok(())
} else {
Err(ImageError::InvalidSize(data.len(), size))
}
}
pub fn resources(&self, _: &RenderStageResources<'_>) -> Option<&ImageResources> {
self.resources.as_ref()
}
pub(crate) fn maintain(&mut self, context: &Context) -> Result<(), ImageError> {
let resources = match &self.resources {
Some(resources) => resources,
None => return Err(ImageError::NoResources),
};
if self.dirty {
unsafe {
let gl_mode = self.mode.as_gl();
let gl_internal_format = self.format.as_internal_format_gl();
let gl_format = self.format.as_format_gl();
let gl_type = self.format.as_type_gl();
let alignment = self.format.alignment();
context.bind_texture(gl_mode, Some(resources.handle));
context.pixel_store_i32(PACK_ALIGNMENT, alignment as _);
context.pixel_store_i32(UNPACK_ALIGNMENT, alignment as _);
match self.mode {
ImageMode::Image2d => {
context.tex_image_2d(
gl_mode,
0,
gl_internal_format as _,
self.width as _,
self.height as _,
0,
gl_format,
gl_type,
Some(&self.data),
);
}
ImageMode::Image2dArray | ImageMode::Image3d => {
context.tex_image_3d(
gl_mode,
0,
gl_internal_format as _,
self.width as _,
self.height as _,
self.depth as _,
0,
gl_format,
gl_type,
Some(&self.data),
);
}
}
if let ImageMipmap::Generate(limit) = self.mipmap {
if let Some(limit) = limit {
context.tex_parameter_i32(gl_mode, TEXTURE_MAX_LEVEL, limit as i32);
}
context.generate_mipmap(gl_mode);
}
context.bind_texture(gl_mode, None);
}
self.dirty = false;
}
Ok(())
}
}
#[derive(Debug)]
pub struct VirtualImageDetailedInfo {
pub source: VirtualImageSource,
pub uvs: HashMap<ImageId, (Rect, usize)>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VirtualImageSource {
Image(ImageId),
RenderTargetDepthStencil(RenderTargetId),
RenderTargetColor(RenderTargetId, String),
}
impl VirtualImageSource {
pub fn image(&self) -> Option<ImageId> {
match self {
Self::Image(id) => Some(*id),
_ => None,
}
}
pub fn render_target_depth_stencil(&self) -> Option<RenderTargetId> {
match self {
Self::RenderTargetDepthStencil(id) => Some(*id),
_ => None,
}
}
pub fn render_target_color(&self) -> Option<(RenderTargetId, &str)> {
match self {
Self::RenderTargetColor(id, name) => Some((*id, name.as_str())),
_ => None,
}
}
}
#[derive(Debug)]
pub struct VirtualImage {
source: VirtualImageSource,
uvs: HashMap<ImageId, (Rect, usize)>,
map: HashMap<String, ImageId>,
table: HashMap<ImageId, String>,
}
impl VirtualImage {
pub fn new(source: VirtualImageSource) -> Self {
Self {
source,
uvs: Default::default(),
map: Default::default(),
table: Default::default(),
}
}
pub fn source(&self) -> &VirtualImageSource {
&self.source
}
pub fn register_image_uvs(&mut self, uvs: Rect, page: usize) -> ImageId {
let id = ImageId::new();
self.uvs.insert(id, (uvs, page));
id
}
pub fn register_named_image_uvs(
&mut self,
name: impl ToString,
uvs: Rect,
page: usize,
) -> ImageId {
let id = self.register_image_uvs(uvs, page);
self.map.insert(name.to_string(), id);
self.table.insert(id, name.to_string());
id
}
pub fn unregister_image_uvs(&mut self, id: ImageId) -> Option<(Rect, usize)> {
if let Some(name) = self.table.remove(&id) {
self.map.remove(&name);
}
self.uvs.remove(&id)
}
pub fn unregister_named_image_uvs(&mut self, name: &str) -> Option<(Rect, usize)> {
if let Some(id) = self.map.remove(name) {
self.table.remove(&id);
return self.uvs.remove(&id);
}
None
}
pub fn image_uvs(&self, id: ImageId) -> Option<(Rect, usize)> {
self.uvs.get(&id).copied()
}
pub fn named_image_uvs(&self, name: &str) -> Option<(Rect, usize)> {
if let Some(id) = self.map.get(name) {
return self.uvs.get(id).copied();
}
None
}
pub fn image_name(&self, id: ImageId) -> Option<&str> {
self.table.get(&id).map(|name| name.as_str())
}
pub fn detailed_info(&self) -> VirtualImageDetailedInfo {
VirtualImageDetailedInfo {
source: self.source.to_owned(),
uvs: self.uvs.to_owned(),
}
}
}