Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reuse image crate #503

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 35 additions & 63 deletions sugarloaf/src/components/core/image.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,29 @@
// use crate::{Hasher, Rectangle, Size};
use image::{DynamicImage, GenericImageView};

use crate::components::core::shapes::Hasher;

use std::hash::{Hash, Hasher as _};
use std::path::PathBuf;
use std::sync::Arc;

/// A handle of some image data.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq)]
pub struct Handle {
id: u64,
data: Data,
}

impl Handle {
/// Creates an image [`Handle`] pointing to the image of the given path.
///
/// Makes an educated guess about the image format by examining the data in the file.
pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle {
Self::from_data(Data::Path(path.into()))
}

/// Creates an image [`Handle`] containing the image pixels directly. This
/// function expects the input data to be provided as a `Vec<u8>` of RGBA
/// pixels.
///
/// This is useful if you have already decoded your image.
pub fn from_pixels(
width: u32,
height: u32,
pixels: impl AsRef<[u8]> + Send + Sync + 'static,
) -> Handle {
Self::from_data(Data::Rgba {
width,
height,
pixels: Bytes::new(pixels),
})
/// Creates a new handler for the provided data.
pub fn new(data: Data) -> Self {
Self::from(data)
}

/// Creates an image [`Handle`] containing the image data directly.
///
/// Makes an educated guess about the image format by examining the given data.
///
/// This is useful if you already have your image loaded in-memory, maybe
/// because you downloaded or generated it procedurally.
pub fn from_memory(bytes: impl AsRef<[u8]> + Send + Sync + 'static) -> Handle {
Self::from_data(Data::Bytes(Bytes::new(bytes)))
}

fn from_data(data: Data) -> Handle {
let mut hasher = Hasher::default();
data.hash(&mut hasher);

Handle {
id: hasher.finish(),
data,
/// Returns the data into memory and returns it (for example if the data is just a path).
pub fn load_image(&self) -> image::ImageResult<DynamicImage> {
match &self.data {
Data::Path(path) => image::open(path),
Data::Image(img) => Ok(img.clone()),
}
}

Expand All @@ -68,12 +38,16 @@ impl Handle {
}
}

impl<T> From<T> for Handle
where
T: Into<PathBuf>,
{
fn from(path: T) -> Handle {
Handle::from_path(path.into())
/// Creates a image [`Handle`] for the given data.
impl From<Data> for Handle {
fn from(data: Data) -> Self {
let mut hasher = Hasher::default();
data.hash(&mut hasher);

Self {
id: hasher.finish(),
data,
}
}
}

Expand Down Expand Up @@ -131,32 +105,30 @@ impl std::ops::Deref for Bytes {
}

/// The data of a raster image.
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq)]
pub enum Data {
/// File data
Path(PathBuf),

/// In-memory data
Bytes(Bytes),

/// Decoded image pixels in RGBA format.
Rgba {
/// The width of the image.
width: u32,
/// The height of the image.
height: u32,
/// The pixels.
pixels: Bytes,
},
Image(DynamicImage),
}

impl Hash for Data {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Path(path) => path.hash(state),
Self::Image(img) => img.as_bytes().hash(state),
}
}
}

impl std::fmt::Debug for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Data::Path(path) => write!(f, "Path({path:?})"),
Data::Bytes(_) => write!(f, "Bytes(...)"),
Data::Rgba { width, height, .. } => {
write!(f, "Pixels({width} * {height})")
Data::Image(img) => {
let (width, height) = img.dimensions();
write!(f, "Image({width} * {height})")
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions sugarloaf/src/components/layer/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ impl Atlas {
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
width: u32,
height: u32,
data: &[u8],
img: &image::RgbaImage,
) -> Option<Entry> {
let width = img.width();
let height = img.height();
let entry = {
let current_size = self.layers.len();
let entry = self.allocate(width, height)?;
Expand Down Expand Up @@ -103,7 +103,7 @@ impl Atlas {
let offset = row * padded_width;

padded_data[offset..offset + 4 * width as usize].copy_from_slice(
&data[row * 4 * width as usize..(row + 1) * 4 * width as usize],
&img.as_raw()[row * 4 * width as usize..(row + 1) * 4 * width as usize],
)
}

Expand Down
2 changes: 1 addition & 1 deletion sugarloaf/src/components/layer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl LayerBrush {

pub fn dimensions(&self, handle: &image::Handle) -> Size<u32> {
let mut cache = self.raster_cache.borrow_mut();
let memory = cache.load(handle);
let memory = cache.load_mut(handle);

memory.dimensions()
}
Expand Down
54 changes: 10 additions & 44 deletions sugarloaf/src/components/layer/raster.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::components::core::shapes::Size;
use crate::components::layer::atlas::{self, Atlas};
use crate::components::layer::image::{Data, Handle};
use image as image_rs;

use std::collections::{HashMap, HashSet};
Expand All @@ -9,7 +8,7 @@ use std::collections::{HashMap, HashSet};
#[derive(Debug)]
pub enum Memory {
/// Image data on host
Host(image_rs::ImageBuffer<image_rs::Rgba<u8>, Vec<u8>>),
Host(image_rs::RgbaImage),
/// Storage entry
Device(atlas::Entry),
/// Image not found
Expand Down Expand Up @@ -47,55 +46,24 @@ pub struct Cache {
hits: HashSet<u64>,
}

/// Tries to load an image by its [`Handle`].
pub fn load_image(handle: &Handle) -> image_rs::ImageResult<image_rs::DynamicImage> {
match handle.data() {
Data::Path(path) => {
let image = ::image::open(path)?;
Ok(image)
}
Data::Bytes(bytes) => {
let image = ::image::load_from_memory(bytes)?;
Ok(image)
}
Data::Rgba {
width,
height,
pixels,
} => {
if let Some(image) =
image_rs::ImageBuffer::from_vec(*width, *height, pixels.to_vec())
{
Ok(image_rs::DynamicImage::ImageRgba8(image))
} else {
Err(image_rs::error::ImageError::Limits(
image_rs::error::LimitError::from_kind(
image_rs::error::LimitErrorKind::DimensionError,
),
))
}
}
}
}

impl Cache {
/// Load image
pub fn load(
pub fn load_mut(
&mut self,
handle: &crate::components::layer::image::Handle,
) -> &mut Memory {
if self.contains(handle) {
return self.get(handle).unwrap();
return self.get_mut(handle).unwrap();
}

let memory = match load_image(handle) {
Ok(image) => Memory::Host(image.to_rgba8()),
let memory = match handle.load_image() {
Ok(img) => Memory::Host(img.to_rgba8()),
Err(image_rs::error::ImageError::IoError(_)) => Memory::NotFound,
Err(_) => Memory::Invalid,
};

self.insert(handle, memory);
self.get(handle).unwrap()
self.get_mut(handle).unwrap()
}

/// Load image and upload raster data
Expand All @@ -106,12 +74,10 @@ impl Cache {
handle: &crate::components::layer::image::Handle,
atlas: &mut Atlas,
) -> Option<&atlas::Entry> {
let memory = self.load(handle);

if let Memory::Host(image) = memory {
let (width, height) = image.dimensions();
let memory = self.load_mut(handle);

let entry = atlas.upload(device, encoder, width, height, image)?;
if let Memory::Host(img) = memory {
let entry = atlas.upload(device, encoder, img)?;

*memory = Memory::Device(entry);
}
Expand Down Expand Up @@ -142,7 +108,7 @@ impl Cache {
self.hits.clear();
}

fn get(
fn get_mut(
&mut self,
handle: &crate::components::layer::image::Handle,
) -> Option<&mut Memory> {
Expand Down
63 changes: 63 additions & 0 deletions sugarloaf/src/layout/span_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
use crate::layout::builder_data::FontSettingKey;
use crate::layout::builder_data::EMPTY_FONT_SETTINGS;
use crate::sugarloaf::primitives::SugarCursor;
use crate::Sugar;
use crate::SugarDecoration;
use crate::SugarStyle;
pub use swash::text::Language;
use swash::{Setting, Stretch, Style, Weight};

Expand Down Expand Up @@ -111,6 +114,66 @@ impl FragmentStyle {
}
}

impl From<&Sugar> for FragmentStyle {
fn from(sugar: &Sugar) -> Self {
let mut style = FragmentStyle::default();

match sugar.style {
SugarStyle::BoldItalic => {
style.font_attrs.1 = Weight::BOLD;
style.font_attrs.2 = Style::Italic;
}
SugarStyle::Bold => {
style.font_attrs.1 = Weight::BOLD;
}
SugarStyle::Italic => {
style.font_attrs.2 = Style::Italic;
}
_ => {}
}

let mut has_underline_cursor = false;
match sugar.cursor {
SugarCursor::Underline(cursor_color) => {
style.underline = true;
style.underline_offset = Some(-1.);
style.underline_color = Some(cursor_color);
style.underline_size = Some(-1.);

has_underline_cursor = true;
}
SugarCursor::Block(cursor_color) => {
style.cursor = SugarCursor::Block(cursor_color);
}
SugarCursor::Caret(cursor_color) => {
style.cursor = SugarCursor::Caret(cursor_color);
}
_ => {}
}

match &sugar.decoration {
SugarDecoration::Underline => {
if !has_underline_cursor {
style.underline = true;
style.underline_offset = Some(-2.);
style.underline_size = Some(1.);
}
}
SugarDecoration::Strikethrough => {
style.underline = true;
style.underline_offset = Some(style.font_size / 2.);
style.underline_size = Some(2.);
}
_ => {}
}

style.color = sugar.foreground_color;
style.background_color = Some(sugar.background_color);

style
}
}

/// Style that can be applied to a range of text.
#[derive(Debug, PartialEq, Clone)]
pub enum SpanStyle {
Expand Down
4 changes: 3 additions & 1 deletion sugarloaf/src/sugarloaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod primitives;
pub mod state;
mod tree;

use crate::components::core::image::Data;
use crate::components::core::{image::Handle, shapes::Rectangle};
use crate::components::layer::{self, LayerBrush};
use crate::components::rect::{Rect, RectBrush};
Expand All @@ -25,6 +26,7 @@ use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
};
use state::SugarState;
use std::path::PathBuf;

#[cfg(target_arch = "wasm32")]
pub struct Database;
Expand Down Expand Up @@ -225,7 +227,7 @@ impl Sugarloaf {

#[inline]
pub fn set_background_image(&mut self, image: &ImageProperties) -> &mut Self {
let handle = Handle::from_path(image.path.to_owned());
let handle = Handle::from(Data::Path(PathBuf::from(image.path.clone())));
self.background_image = Some(layer::types::Image::Raster {
handle,
bounds: Rectangle {
Expand Down
Loading
Loading