Skip to content

Commit

Permalink
Let the Resource Allocator decode the images
Browse files Browse the repository at this point in the history
We pass the encoded image data into the resource allocator now instead
of trying to decode the image in the scene manager. This way the
renderer has the ability to support all sorts of image formats that we
might not support. For example, this would also allow the renderer to
decode GIFs itself and thus properly animate them.
  • Loading branch information
CryZe committed Sep 16, 2022
1 parent 79d9dab commit 6ad7b9e
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 66 deletions.
25 changes: 5 additions & 20 deletions src/rendering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,26 +444,11 @@ impl<A: ResourceAllocator> RenderContext<'_, A> {
}

fn create_icon(&mut self, image_data: &[u8]) -> Option<Icon<A::Image>> {
#[cfg(feature = "image")]
{
if image_data.is_empty() {
return None;
}

let image = image::load_from_memory(image_data).ok()?.to_rgba8();

Some(Icon {
aspect_ratio: image.width() as f32 / image.height() as f32,
image: self
.handles
.create_image(image.width(), image.height(), &image),
})
}
#[cfg(not(feature = "image"))]
{
let _ = image_data;
None
}
let (image, aspect_ratio) = self.handles.create_image(image_data)?;
Some(Icon {
aspect_ratio,
image,
})
}

fn scale(&mut self, factor: f32) {
Expand Down
13 changes: 7 additions & 6 deletions src/rendering/resource/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ pub trait ResourceAllocator {
builder.finish()
}

/// Creates an image out of the image data provided. The image's resolution
/// is provided as well. The data is an array of RGBA8 encoded pixels (red,
/// green, blue, alpha with each channel being an u8).
fn create_image(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Image;
/// Creates an image out of the image data provided. The data represents the
/// image in its original file format. It needs to be parsed in order to be
/// visualized. The parsed image as well as the aspect ratio (width /
/// height) are returned in case the image was parsed successfully.
fn create_image(&mut self, data: &[u8]) -> Option<(Self::Image, f32)>;

/// Creates a font from the font description provided. It is expected that
/// the the font description is used in a font matching algorithm to find
Expand Down Expand Up @@ -191,8 +192,8 @@ impl<A: ResourceAllocator> ResourceAllocator for &mut A {
MutPathBuilder((*self).path_builder())
}

fn create_image(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Image {
(*self).create_image(width, height, data)
fn create_image(&mut self, data: &[u8]) -> Option<(Self::Image, f32)> {
(*self).create_image(data)
}

fn create_font(&mut self, font: Option<&Font>, kind: FontKind) -> Self::Font {
Expand Down
6 changes: 3 additions & 3 deletions src/rendering/resource/handles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ impl<A: ResourceAllocator> ResourceAllocator for Handles<A> {
self.next(circle)
}

fn create_image(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Image {
let image = self.allocator.create_image(width, height, data);
self.next(image)
fn create_image(&mut self, data: &[u8]) -> Option<(Self::Image, f32)> {
let (image, aspect_ratio) = self.allocator.create_image(data)?;
Some((self.next(image), aspect_ratio))
}

fn create_font(&mut self, font: Option<&Font>, kind: super::FontKind) -> Self::Font {
Expand Down
81 changes: 44 additions & 37 deletions src/rendering/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use image::{self, RgbaImage};
struct SkiaBuilder(PathBuilder);

type SkiaPath = Option<UnsafeRc<Path>>;
type SkiaImage = Option<UnsafeRc<Pixmap>>;
type SkiaImage = UnsafeRc<Pixmap>;
type SkiaFont = Font<SkiaPath>;
type SkiaLabel = Label<SkiaPath>;

Expand Down Expand Up @@ -87,16 +87,27 @@ impl ResourceAllocator for SkiaAllocator {
path_builder()
}

fn create_image(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Image {
let mut image = Pixmap::new(width, height)?;
for (d, &[r, g, b, a]) in image
.pixels_mut()
.iter_mut()
.zip(bytemuck::cast_slice::<_, [u8; 4]>(data))
fn create_image(&mut self, data: &[u8]) -> Option<(Self::Image, f32)> {
#[cfg(feature = "image")]
{
*d = tiny_skia::ColorU8::from_rgba(r, g, b, a).premultiply();
let src = image::load_from_memory(data).ok()?.to_rgba8();
let (width, height) = (src.width(), src.height());
// FIXME: Turn the image into a Pixmap directly. We should not need
// to allocate a new Pixmap here.
let mut dst = Pixmap::new(width, height)?;
for (d, &[r, g, b, a]) in dst
.pixels_mut()
.iter_mut()
.zip(bytemuck::cast_slice::<u8, [u8; 4]>(&src))
{
*d = tiny_skia::ColorU8::from_rgba(r, g, b, a).premultiply();
}
Some((UnsafeRc::new(dst), width as f32 / height as f32))
}
#[cfg(not(feature = "image"))]
{
None
}
Some(UnsafeRc::new(image))
}

fn create_font(&mut self, font: Option<&settings::Font>, kind: FontKind) -> Self::Font {
Expand Down Expand Up @@ -397,28 +408,26 @@ fn render_layer(
}
}
Entity::Image(image, transform) => {
if let Some(image) = image.as_deref() {
canvas.fill_path(
rectangle,
&Paint {
shader: Pattern::new(
image.as_ref(),
SpreadMode::Pad,
FilterQuality::Bilinear,
1.0,
tiny_skia::Transform::from_scale(
1.0 / image.width() as f32,
1.0 / image.height() as f32,
),
canvas.fill_path(
rectangle,
&Paint {
shader: Pattern::new(
image.as_ref(),
SpreadMode::Pad,
FilterQuality::Bilinear,
1.0,
tiny_skia::Transform::from_scale(
1.0 / image.width() as f32,
1.0 / image.height() as f32,
),
anti_alias: true,
..Default::default()
},
FillRule::Winding,
convert_transform(transform),
None,
);
}
),
anti_alias: true,
..Default::default()
},
FillRule::Winding,
convert_transform(transform),
None,
);
}
Entity::Label(label, shader, transform) => {
let label = label.read().unwrap();
Expand Down Expand Up @@ -622,13 +631,11 @@ fn calculate_bounds(layer: &[Entity<SkiaPath, SkiaImage, SkiaLabel>]) -> (f32, f
}
}
}
Entity::Image(image, transform) => {
if image.is_some() {
for y in [0.0, 1.0] {
let transformed_y = transform.transform_y(y);
min_y = min_y.min(transformed_y);
max_y = max_y.max(transformed_y);
}
Entity::Image(_, transform) => {
for y in [0.0, 1.0] {
let transformed_y = transform.transform_y(y);
min_y = min_y.min(transformed_y);
max_y = max_y.max(transformed_y);
}
}
Entity::Label(label, _, transform) => {
Expand Down

0 comments on commit 6ad7b9e

Please sign in to comment.