Skip to content

Commit

Permalink
Add sampling property for image (#1080)
Browse files Browse the repository at this point in the history
* add sampling property

* sampling => image_sampling, add NetworkImage sampling attribute, add docs for sampling attribute

* accident

* fmt
  • Loading branch information
Aiving authored Feb 1, 2025
1 parent f11f551 commit 163cb18
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 6 deletions.
6 changes: 5 additions & 1 deletion crates/components/src/network_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub struct NetworkImageProps {
pub aspect_ratio: Option<String>,
/// Cover of the image.
pub cover: Option<String>,
/// Image sampling algorithm.
pub sampling: Option<String>,
}

/// Image status.
Expand Down Expand Up @@ -92,6 +94,7 @@ pub fn NetworkImage(
alt,
aspect_ratio,
cover,
sampling,
}: NetworkImageProps,
) -> Element {
let mut asset_cacher = use_asset_cacher();
Expand Down Expand Up @@ -159,7 +162,8 @@ pub fn NetworkImage(
a11y_name: alt,
aspect_ratio,
cover,
cache_key: "{url}"
cache_key: "{url}",
sampling,
})
}
ImageState::Loading => {
Expand Down
26 changes: 22 additions & 4 deletions crates/core/src/elements/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use freya_node_state::{
AspectRatio,
ImageCover,
ReferencesState,
SamplingMode,
StyleState,
TransformState,
};
Expand Down Expand Up @@ -46,9 +47,6 @@ impl ElementUtils for ImageElement {
return;
};

let mut paint = Paint::default();
paint.set_anti_alias(true);

let width_ratio = area.width() / image.width() as f32;
let height_ratio = area.height() / image.height() as f32;

Expand Down Expand Up @@ -88,7 +86,27 @@ impl ElementUtils for ImageElement {
canvas.clip_rect(clip_rect, ClipOp::Intersect, true);
}

canvas.draw_image_rect(image, None, rect, &paint);
let sampling = match node_style.image_sampling {
SamplingMode::Nearest => {
SamplingOptions::new(FilterMode::Nearest, MipmapMode::None)
}
SamplingMode::Bilinear => {
SamplingOptions::new(FilterMode::Linear, MipmapMode::None)
}
SamplingMode::Trilinear => {
SamplingOptions::new(FilterMode::Linear, MipmapMode::Linear)
}
SamplingMode::Mitchell => SamplingOptions::from(CubicResampler::mitchell()),
SamplingMode::CatmullRom => SamplingOptions::from(CubicResampler::catmull_rom()),
};

canvas.draw_image_rect_with_sampling_options(
image,
None,
rect,
sampling,
&Paint::default(),
);

if node_transform.image_cover == ImageCover::Center {
canvas.restore();
Expand Down
30 changes: 29 additions & 1 deletion crates/elements/src/attributes/image_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ def_attribute!(
/// ```
aspect_ratio,


/// `cover` controls how an `image` element position is rendered inside the given dimensions.
///
/// Accepted values:
/// - `fill` (default): The image will be rendered from the start of the given dimensions.
/// - `center`: The image will be rendered in the center of the given dimensions.
///
///
/// ```rust, no_run
/// # use freya::prelude::*;
/// static RUST_LOGO: &[u8] = include_bytes!("../_docs/rust_logo.png");
Expand All @@ -61,6 +61,7 @@ def_attribute!(
/// `cache_key` is optinal but its recommended to be used, specialy for high quality images.
/// You can pass any value that can be transformed into a string. Like a URL.
///
///
/// ```rust, no_run
/// # use freya::prelude::*;
/// static RUST_LOGO: &[u8] = include_bytes!("../_docs/rust_logo.png");
Expand All @@ -79,4 +80,31 @@ def_attribute!(
/// ```
cache_key,

/// `sampling` controls how an `image` element is resized when scaling from its original size to smaller or larger sizes.
///
/// Accepted values:
/// - `nearest` or `none` (default): The image will be resized using nearest-neighbor interpolation.
/// - `bilinear`: The image will be resized using bilinear interpolation.
/// - `trilinear`: The image will be resized using trilinear interpolation.
/// - `mitchell`: The image will be resized using Mitchell-Netravali interpolation, also known as Bicubic.
/// - `catmull-rom`: The image will be resized using Catmull-Rom interpolation.
///
///
/// ```rust, no_run
/// # use freya::prelude::*;
/// static RUST_LOGO: &[u8] = include_bytes!("../_docs/rust_logo.png");
///
/// fn app() -> Element {
/// let image_data = static_bytes(RUST_LOGO);
/// rsx!(
/// image {
/// image_data: image_data,
/// width: "96",
/// height: "96",
/// sampling: "trilinear",
/// }
/// )
/// }
/// ```
sampling,
);
1 change: 1 addition & 0 deletions crates/elements/src/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ def_element!(
aspect_ratio,
cover,
cache_key,
sampling,

// Reference
reference,
Expand Down
47 changes: 47 additions & 0 deletions crates/engine/src/mocked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,17 @@ impl Canvas {
unimplemented!("This is mocked")
}

pub fn draw_image_rect_with_sampling_options(
&self,
image: impl AsRef<Image>,
src: Option<(&Rect, SrcRectConstraint)>,
dst: impl AsRef<Rect>,
sampling_options: impl Into<SamplingOptions>,
paint: &Paint,
) -> &Self {
unimplemented!("This is mocked")
}

pub fn draw_rect(&self, _rect: Rect, _paint: &Paint) -> &Self {
unimplemented!("This is mocked")
}
Expand Down Expand Up @@ -1131,6 +1142,30 @@ pub enum SrcRectConstraint {
#[derive(Default)]
pub struct SamplingOptions;

impl SamplingOptions {
pub fn new(filter_mode: FilterMode, mm: MipmapMode) -> Self {
unimplemented!("This is mocked")
}
}

pub struct CubicResampler;

impl CubicResampler {
pub fn mitchell() -> Self {
unimplemented!("This is mocked")
}

pub fn catmull_rom() -> Self {
unimplemented!("This is mocked")
}
}

impl From<CubicResampler> for SamplingOptions {
fn from(_: CubicResampler) -> Self {
unimplemented!("This is mocked")
}
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Default)]
pub enum RectHeightStyle {
Expand Down Expand Up @@ -1351,6 +1386,18 @@ impl FilterMode {
pub const Last: FilterMode = FilterMode::Linear;
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum MipmapMode {
None = 0,
Nearest = 1,
Linear = 2,
}

impl MipmapMode {
pub const Last: MipmapMode = MipmapMode::Linear;
}

pub struct Path;

impl Path {
Expand Down
4 changes: 4 additions & 0 deletions crates/engine/src/skia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub use skia_safe::{
set_resource_cache_single_allocation_byte_limit,
set_resource_cache_total_bytes_limit,
},
images::raster_from_data,
path::ArcSize,
resources::LocalResourceProvider,
rrect::Corner,
Expand Down Expand Up @@ -66,6 +67,7 @@ pub use skia_safe::{
Color,
ColorSpace,
ColorType,
CubicResampler,
Data,
EncodedImageFormat,
FilterMode,
Expand All @@ -78,11 +80,13 @@ pub use skia_safe::{
ImageInfo,
MaskFilter,
Matrix,
MipmapMode,
Paint,
PaintStyle,
Path,
PathDirection,
PathFillType,
Pixmap,
Point,
RRect,
Rect,
Expand Down
2 changes: 2 additions & 0 deletions crates/native-core/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub enum AttributeName {
AspectRatio,
ImageCover,
ImageCacheKey,
Sampling,

// Focus
A11yId,
Expand Down Expand Up @@ -301,6 +302,7 @@ impl FromStr for AttributeName {
"aspect_ratio" => Ok(AttributeName::AspectRatio),
"cover" => Ok(AttributeName::ImageCover),
"cache_key" => Ok(AttributeName::ImageCacheKey),
"sampling" => Ok(AttributeName::Sampling),
"a11y_id" => Ok(AttributeName::A11yId),
"a11y_focusable" => Ok(AttributeName::A11yFocusable),
"a11y_auto_focus" => Ok(AttributeName::A11yAutoFocus),
Expand Down
8 changes: 8 additions & 0 deletions crates/state/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::{
Parse,
ParseAttribute,
ParseError,
SamplingMode,
Shadow,
};

Expand All @@ -46,6 +47,7 @@ pub struct StyleState {
pub borders: Vec<Border>,
pub shadows: Vec<Shadow>,
pub corner_radius: CornerRadius,
pub image_sampling: SamplingMode,
pub image_data: Option<AttributesBytes>,
pub svg_data: Option<AttributesBytes>,
pub overflow: OverflowMode,
Expand Down Expand Up @@ -116,6 +118,11 @@ impl ParseAttribute for StyleState {
}
}
}
AttributeName::Sampling => {
if let Some(value) = attr.value.as_text() {
self.image_sampling = SamplingMode::parse(value)?;
}
}
AttributeName::ImageData => {
if let OwnedAttributeValue::Custom(CustomAttributeValues::Bytes(bytes)) = attr.value
{
Expand Down Expand Up @@ -168,6 +175,7 @@ impl State<CustomAttributeValues> for StyleState {
AttributeName::Shadow,
AttributeName::CornerRadius,
AttributeName::CornerSmoothing,
AttributeName::Sampling,
AttributeName::ImageData,
AttributeName::SvgData,
AttributeName::SvgContent,
Expand Down
2 changes: 2 additions & 0 deletions crates/state/src/values/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod highlight;
mod image_cover;
mod overflow;
mod position;
mod sampling;
mod shadow;
mod size;
mod text_height;
Expand All @@ -34,6 +35,7 @@ pub use gradient::*;
pub use highlight::*;
pub use image_cover::*;
pub use overflow::*;
pub use sampling::*;
pub use shadow::*;
pub use size::*;
pub use text_height::*;
40 changes: 40 additions & 0 deletions crates/state/src/values/sampling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::fmt;

use crate::{
Parse,
ParseError,
};

#[derive(Clone, Debug, PartialEq, Default)]
pub enum SamplingMode {
#[default]
Nearest,
Bilinear,
Trilinear,
Mitchell,
CatmullRom,
}

impl Parse for SamplingMode {
fn parse(value: &str) -> Result<Self, ParseError> {
Ok(match value {
"bilinear" => SamplingMode::Bilinear,
"trilinear" => SamplingMode::Trilinear,
"mitchell" => SamplingMode::Mitchell,
"catmull-rom" => SamplingMode::CatmullRom,
_ => SamplingMode::Nearest,
})
}
}

impl fmt::Display for SamplingMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
SamplingMode::Nearest => "nearest",
SamplingMode::Bilinear => "bilinear",
SamplingMode::Trilinear => "trilinear",
SamplingMode::Mitchell => "mitchell",
SamplingMode::CatmullRom => "catmull-rom",
})
}
}
51 changes: 51 additions & 0 deletions examples/image_sampling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]

use freya::prelude::*;

fn main() {
launch(app);
}

static RUST_LOGO: &[u8] = include_bytes!("./rust_logo.png");

fn app() -> Element {
let image_data = static_bytes(RUST_LOGO);

rsx!(
rect {
width: "100%",
height: "100%",
padding: "50",
main_align: "center",

for sampling in [
"nearest",
"bilinear",
"trilinear",
"mitchell",
"catmull-rom"
] {
rect {
direction: "horizontal",
spacing: "12",
main_align: "center",
cross_align: "center",

image {
image_data: image_data.clone(),
width: "96",
height: "96",
sampling,
}

label {
"Sampling mode: {sampling}"
}
}
}
}
)
}

0 comments on commit 163cb18

Please sign in to comment.