mod bytes_loader;
mod texture_loader;
use std::{
borrow::Cow,
fmt::{Debug, Display},
ops::Deref,
sync::Arc,
};
use ahash::HashMap;
use emath::{Float as _, OrderedFloat};
use epaint::{ColorImage, TextureHandle, TextureId, Vec2, mutex::Mutex, textures::TextureOptions};
use crate::Context;
pub use self::{bytes_loader::DefaultBytesLoader, texture_loader::DefaultTextureLoader};
#[derive(Clone, Debug)]
pub enum LoadError {
NoImageLoaders,
NotSupported,
FormatNotSupported { detected_format: Option<String> },
NoMatchingBytesLoader,
NoMatchingImageLoader { detected_format: Option<String> },
NoMatchingTextureLoader,
Loading(String),
}
impl LoadError {
pub fn byte_size(&self) -> usize {
match self {
Self::FormatNotSupported { detected_format }
| Self::NoMatchingImageLoader { detected_format } => {
detected_format.as_ref().map_or(0, |s| s.len())
}
Self::Loading(message) => message.len(),
_ => std::mem::size_of::<Self>(),
}
}
}
impl Display for LoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoImageLoaders => f.write_str(
"No image loaders are installed. If you're trying to load some images \
for the first time, follow the steps outlined in https://docs.rs/egui/latest/egui/load/index.html"),
Self::NoMatchingBytesLoader => f.write_str("No matching BytesLoader. Either you need to call Context::include_bytes, or install some more bytes loaders, e.g. using egui_extras."),
Self::NoMatchingImageLoader { detected_format: None } => f.write_str("No matching ImageLoader. Either no ImageLoader is installed or the image is corrupted / has an unsupported format."),
Self::NoMatchingImageLoader { detected_format: Some(detected_format) } => write!(f, "No matching ImageLoader for format: {detected_format:?}. Make sure you enabled the necessary features on the image crate."),
Self::NoMatchingTextureLoader => f.write_str("No matching TextureLoader. Did you remove the default one?"),
Self::NotSupported => f.write_str("Image scheme or URI not supported by this loader"),
Self::FormatNotSupported { detected_format } => write!(f, "Image format not supported by this loader: {detected_format:?}"),
Self::Loading(message) => f.write_str(message),
}
}
}
impl std::error::Error for LoadError {}
pub type Result<T, E = LoadError> = std::result::Result<T, E>;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SizeHint {
Scale(OrderedFloat<f32>),
Width(u32),
Height(u32),
Size {
width: u32,
height: u32,
maintain_aspect_ratio: bool,
},
}
impl SizeHint {
pub fn scale_by(self, factor: f32) -> Self {
match self {
Self::Scale(scale) => Self::Scale(OrderedFloat(factor * scale.0)),
Self::Width(width) => Self::Width((factor * width as f32).round() as _),
Self::Height(height) => Self::Height((factor * height as f32).round() as _),
Self::Size {
width,
height,
maintain_aspect_ratio,
} => Self::Size {
width: (factor * width as f32).round() as _,
height: (factor * height as f32).round() as _,
maintain_aspect_ratio,
},
}
}
}
impl Default for SizeHint {
#[inline]
fn default() -> Self {
Self::Scale(1.0.ord())
}
}
#[derive(Clone)]
pub enum Bytes {
Static(&'static [u8]),
Shared(Arc<[u8]>),
}
impl Debug for Bytes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Static(arg0) => f.debug_tuple("Static").field(&arg0.len()).finish(),
Self::Shared(arg0) => f.debug_tuple("Shared").field(&arg0.len()).finish(),
}
}
}
impl From<&'static [u8]> for Bytes {
#[inline]
fn from(value: &'static [u8]) -> Self {
Self::Static(value)
}
}
impl<const N: usize> From<&'static [u8; N]> for Bytes {
#[inline]
fn from(value: &'static [u8; N]) -> Self {
Self::Static(value)
}
}
impl From<Arc<[u8]>> for Bytes {
#[inline]
fn from(value: Arc<[u8]>) -> Self {
Self::Shared(value)
}
}
impl From<Vec<u8>> for Bytes {
#[inline]
fn from(value: Vec<u8>) -> Self {
Self::Shared(value.into())
}
}
impl AsRef<[u8]> for Bytes {
#[inline]
fn as_ref(&self) -> &[u8] {
match self {
Self::Static(bytes) => bytes,
Self::Shared(bytes) => bytes,
}
}
}
impl Deref for Bytes {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
#[derive(Clone)]
pub enum BytesPoll {
Pending {
size: Option<Vec2>,
},
Ready {
size: Option<Vec2>,
bytes: Bytes,
mime: Option<String>,
},
}
#[macro_export]
macro_rules! generate_loader_id {
($ty:ident) => {
concat!(module_path!(), "::", stringify!($ty))
};
}
pub use crate::generate_loader_id;
pub type BytesLoadResult = Result<BytesPoll>;
pub trait BytesLoader {
fn id(&self) -> &str;
fn load(&self, ctx: &Context, uri: &str) -> BytesLoadResult;
fn forget(&self, uri: &str);
fn forget_all(&self);
fn end_pass(&self, pass_index: u64) {
let _ = pass_index;
}
fn byte_size(&self) -> usize;
fn has_pending(&self) -> bool {
false
}
}
#[derive(Clone)]
pub enum ImagePoll {
Pending {
size: Option<Vec2>,
},
Ready { image: Arc<ColorImage> },
}
pub type ImageLoadResult = Result<ImagePoll>;
pub trait ImageLoader {
fn id(&self) -> &str;
fn load(&self, ctx: &Context, uri: &str, size_hint: SizeHint) -> ImageLoadResult;
fn forget(&self, uri: &str);
fn forget_all(&self);
fn end_pass(&self, pass_index: u64) {
let _ = pass_index;
}
fn byte_size(&self) -> usize;
fn has_pending(&self) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SizedTexture {
pub id: TextureId,
pub size: Vec2,
}
impl SizedTexture {
pub fn new(id: impl Into<TextureId>, size: impl Into<Vec2>) -> Self {
Self {
id: id.into(),
size: size.into(),
}
}
pub fn from_handle(handle: &TextureHandle) -> Self {
let size = handle.size();
Self {
id: handle.id(),
size: Vec2::new(size[0] as f32, size[1] as f32),
}
}
}
impl From<(TextureId, Vec2)> for SizedTexture {
#[inline]
fn from((id, size): (TextureId, Vec2)) -> Self {
Self { id, size }
}
}
impl<'a> From<&'a TextureHandle> for SizedTexture {
#[inline]
fn from(handle: &'a TextureHandle) -> Self {
Self::from_handle(handle)
}
}
#[derive(Clone, Copy)]
pub enum TexturePoll {
Pending {
size: Option<Vec2>,
},
Ready { texture: SizedTexture },
}
impl TexturePoll {
#[inline]
pub fn size(&self) -> Option<Vec2> {
match self {
Self::Pending { size } => *size,
Self::Ready { texture } => Some(texture.size),
}
}
#[inline]
pub fn texture_id(&self) -> Option<TextureId> {
match self {
Self::Pending { .. } => None,
Self::Ready { texture } => Some(texture.id),
}
}
}
pub type TextureLoadResult = Result<TexturePoll>;
pub trait TextureLoader {
fn id(&self) -> &str;
fn load(
&self,
ctx: &Context,
uri: &str,
texture_options: TextureOptions,
size_hint: SizeHint,
) -> TextureLoadResult;
fn forget(&self, uri: &str);
fn forget_all(&self);
fn end_pass(&self, pass_index: u64) {
let _ = pass_index;
}
fn byte_size(&self) -> usize;
}
type BytesLoaderImpl = Arc<dyn BytesLoader + Send + Sync + 'static>;
type ImageLoaderImpl = Arc<dyn ImageLoader + Send + Sync + 'static>;
type TextureLoaderImpl = Arc<dyn TextureLoader + Send + Sync + 'static>;
#[derive(Clone)]
pub struct Loaders {
pub include: Arc<DefaultBytesLoader>,
pub bytes: Mutex<Vec<BytesLoaderImpl>>,
pub image: Mutex<Vec<ImageLoaderImpl>>,
pub texture: Mutex<Vec<TextureLoaderImpl>>,
}
impl Default for Loaders {
fn default() -> Self {
let include = Arc::new(DefaultBytesLoader::default());
Self {
bytes: Mutex::new(vec![include.clone()]),
image: Mutex::new(Vec::new()),
texture: Mutex::new(vec![Arc::new(DefaultTextureLoader::default())]),
include,
}
}
}
impl Loaders {
pub fn end_pass(&self, pass_index: u64) {
let Self {
include,
bytes,
image,
texture,
} = self;
include.end_pass(pass_index);
for loader in bytes.lock().iter() {
loader.end_pass(pass_index);
}
for loader in image.lock().iter() {
loader.end_pass(pass_index);
}
for loader in texture.lock().iter() {
loader.end_pass(pass_index);
}
}
}