From f5dad336deb26ec12829dbb256c8fdeaf5e13ecc Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 13 May 2025 12:52:02 +1000 Subject: [PATCH 1/7] Initial lints --- canvas/src/bits.rs | 4 +++- canvas/src/color/transfer.rs | 1 + canvas/src/color/yuv.rs | 2 ++ canvas/src/frame.rs | 1 + canvas/src/layout.rs | 1 + canvas/src/shader.rs | 1 + texel/src/image/raw.rs | 1 + texel/src/layout/upsampling.rs | 1 + 8 files changed, 11 insertions(+), 1 deletion(-) diff --git a/canvas/src/bits.rs b/canvas/src/bits.rs index 3038cdc..fe4feca 100644 --- a/canvas/src/bits.rs +++ b/canvas/src/bits.rs @@ -1,5 +1,7 @@ use crate::layout::{SampleBits, SampleParts}; -use image_texel::{AsTexel, Texel}; +#[expect(unused_imports)] +use image_texel::AsTexel; +use image_texel::Texel; /// Specifies which bits a channel comes from, within a `TexelKind` aggregate. #[derive(Clone, Copy, Debug)] diff --git a/canvas/src/color/transfer.rs b/canvas/src/color/transfer.rs index ec7b06e..994795a 100644 --- a/canvas/src/color/transfer.rs +++ b/canvas/src/color/transfer.rs @@ -143,6 +143,7 @@ pub fn transfer_display_scene_smpte2084(val: f32) -> f32 { pub fn transfer_oe_smpte2084(val: f32) -> f32 { transfer_eo_inv_smpte2084(transfer_scene_display_smpte2084(val)) } +#[expect(dead_code)] pub fn transfer_oe_inv_smpte2084(val: f32) -> f32 { transfer_display_scene_smpte2084(transfer_eo_smpte2084(val)) } diff --git a/canvas/src/color/yuv.rs b/canvas/src/color/yuv.rs index 2a99a9c..895861e 100644 --- a/canvas/src/color/yuv.rs +++ b/canvas/src/color/yuv.rs @@ -107,6 +107,7 @@ impl Differencing { struct Bt407MPal; struct Bt407MPalPrecise; struct Pal525; +#[expect(dead_code)] struct Pal625; struct Bt601; struct Bt601Quantized; @@ -118,6 +119,7 @@ struct Bt2020; struct Bt2100; struct YDbDr; struct YCoCg; +#[expect(dead_code)] struct YCoCgR; // We derive the coefficients from scratch, from their definition. diff --git a/canvas/src/frame.rs b/canvas/src/frame.rs index 963b143..7e90248 100644 --- a/canvas/src/frame.rs +++ b/canvas/src/frame.rs @@ -18,6 +18,7 @@ pub struct Plane { inner: Image, } +#[expect(dead_code)] #[doc(hidden)] #[deprecated = "Use BytePlaneRef"] pub type BytePlane<'data> = BytePlaneRef<'data>; diff --git a/canvas/src/layout.rs b/canvas/src/layout.rs index bae5308..c87ca2c 100644 --- a/canvas/src/layout.rs +++ b/canvas/src/layout.rs @@ -376,6 +376,7 @@ pub enum SampleBits { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) enum BitEncoding { + #[expect(dead_code)] Opaque, UInt, Int, diff --git a/canvas/src/shader.rs b/canvas/src/shader.rs index a1102c4..7936e87 100644 --- a/canvas/src/shader.rs +++ b/canvas/src/shader.rs @@ -1231,6 +1231,7 @@ impl CommonPixel { ) } + #[expect(dead_code)] fn expand_yuy2(info: &Info, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) { struct ExpandYuy2; diff --git a/texel/src/image/raw.rs b/texel/src/image/raw.rs index 7c6ac61..05601b2 100644 --- a/texel/src/image/raw.rs +++ b/texel/src/image/raw.rs @@ -124,6 +124,7 @@ impl RawImage { /// Methods specifically with a dynamic layout. impl RawImage { + #[expect(dead_code)] pub(crate) fn try_from_dynamic(self, layout: Other) -> Result, Self> where Other: Into + Clone, diff --git a/texel/src/layout/upsampling.rs b/texel/src/layout/upsampling.rs index 70eaa72..bd10da5 100644 --- a/texel/src/layout/upsampling.rs +++ b/texel/src/layout/upsampling.rs @@ -11,6 +11,7 @@ pub(crate) struct Yuv420p { } impl Yuv420p { + #[expect(dead_code)] pub fn from_width_height(channel: TexelLayout, width: u32, height: u32) -> Option { use core::convert::TryFrom; if width % 2 != 0 || height % 2 != 0 { From 0ff701ae82cfb6fcd6a904eee8600eeb8949e34a Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 13 May 2025 14:19:55 +1000 Subject: [PATCH 2/7] `no_std` for `image-canvas` --- canvas/Cargo.toml | 5 ++ canvas/src/arch.rs | 92 ++++++++++++++++++++++++++++++------ canvas/src/color/oklab.rs | 3 +- canvas/src/color/srlab2.rs | 5 +- canvas/src/color/transfer.rs | 2 +- canvas/src/frame.rs | 4 ++ canvas/src/layout.rs | 4 +- canvas/src/lib.rs | 10 ++++ canvas/src/math.rs | 5 ++ canvas/src/shader.rs | 5 +- 10 files changed, 114 insertions(+), 21 deletions(-) create mode 100644 canvas/src/math.rs diff --git a/canvas/Cargo.toml b/canvas/Cargo.toml index a0b2f58..cb86618 100644 --- a/canvas/Cargo.toml +++ b/canvas/Cargo.toml @@ -14,6 +14,11 @@ categories = ["multimedia::images"] [dependencies] image-texel = { path = "../texel", version = "0.5.0" } bytemuck = "1.1" +libm = { version = "0.2", default-features = false, features = ["arch"] } + +[features] +# Use runtime feature detection on x86 and x86_64 targets. +runtime-features = [] [dev-dependencies] brunch = "0.6.1" diff --git a/canvas/src/arch.rs b/canvas/src/arch.rs index a311f2b..bdcffcd 100644 --- a/canvas/src/arch.rs +++ b/canvas/src/arch.rs @@ -1,4 +1,6 @@ #![allow(unsafe_code)] +// May be unused if no architecture features are detected at compile time or runtime. +#[allow(unused_imports)] use core::mem::transmute; // For when we want to make sure we have a texel at compile time based on bytemuck. @@ -12,8 +14,12 @@ macro_rules! expect_texel { }; } +// May be unused if no architecture features are detected at compile time or runtime. +#[allow(dead_code)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86_avx2; +// May be unused if no architecture features are detected at compile time or runtime. +#[allow(dead_code)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86_ssse3; @@ -30,29 +36,85 @@ pub(crate) struct ShuffleOps { impl ShuffleOps { /// FIXME(perf): implement and choose arch-specific shuffles. + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(unused_mut)] pub fn with_arch(mut self) -> Self { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + self = self.with_x86(); + } + + self + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(unused_mut)] + fn with_x86(mut self) -> Self { + #[cfg(target_feature = "ssse3")] + // SAFETY: `ssse3` detected at compile time + unsafe { + self = self.with_x86_ssse3(); + } + + #[cfg(not(target_feature = "ssse3"))] + #[cfg(feature = "runtime-features")] if std::is_x86_feature_detected!("ssse3") { - self.shuffle_u8x4 = unsafe { - transmute::(x86_ssse3::shuffle_u8x4) - }; - self.shuffle_u16x4 = unsafe { - transmute::(x86_ssse3::shuffle_u16x4) - }; + // SAFETY: `ssse3` detected at runtime + unsafe { + self = self.with_x86_ssse3(); + } + } + + #[cfg(target_feature = "avx2")] + // SAFETY: `avx2` detected at compile time + unsafe { + self = self.with_x86_avx2(); + } + + #[cfg(not(target_feature = "avx2"))] + #[cfg(feature = "runtime-features")] + if std::is_x86_feature_detected!("avx2") { + // SAFETY: `avx2` detected at runtime + unsafe { + self = self.with_x86_avx2(); + } } + self + } + + /// # Safety + /// + /// Must only be used when the `ssse3` feature is available. + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(dead_code)] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe fn with_x86_ssse3(mut self) -> Self { + self.shuffle_u8x4 = + unsafe { transmute::(x86_ssse3::shuffle_u8x4) }; + self.shuffle_u16x4 = unsafe { + transmute::(x86_ssse3::shuffle_u16x4) + }; + + self + } + + /// # Safety + /// + /// Must only be used when the `avx2` feature is available. + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(dead_code)] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe fn with_x86_avx2(mut self) -> Self { // Note: On Ivy Bridge these have the same *throughput* of 256bit-per-cycle as their SSSE3 // equivalents until Icelake. With Icelake they are twice as fast at 512bit-per-cycle. // Therefore, we don't select them until we find a way to predict/select this. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - if std::is_x86_feature_detected!("avx2") { - self.shuffle_u8x4 = unsafe { - transmute::(x86_avx2::shuffle_u8x4) - }; - self.shuffle_u16x4 = unsafe { - transmute::(x86_avx2::shuffle_u16x4) - }; - } + + self.shuffle_u8x4 = + unsafe { transmute::(x86_avx2::shuffle_u8x4) }; + self.shuffle_u16x4 = + unsafe { transmute::(x86_avx2::shuffle_u16x4) }; self } diff --git a/canvas/src/color/oklab.rs b/canvas/src/color/oklab.rs index bdd9bd2..08e1eea 100644 --- a/canvas/src/color/oklab.rs +++ b/canvas/src/color/oklab.rs @@ -1,4 +1,5 @@ use crate::color_matrix::ColMatrix; +use crate::math::powf; const M1: ColMatrix = ColMatrix([ [0.8189330101, 0.0329845436, 0.0482003018], @@ -94,7 +95,7 @@ pub(crate) fn f_lms_inv(lms: [f32; 3]) -> [f32; 3] { } fn pow([a, b, c]: [f32; 3], exp: f32) -> [f32; 3] { - [a.powf(exp), b.powf(exp), c.powf(exp)] + [powf(a, exp), powf(b, exp), powf(c, exp)] } fn copysign([a, b, c]: [f32; 3], [sa, sb, sc]: [f32; 3]) -> [f32; 3] { diff --git a/canvas/src/color/srlab2.rs b/canvas/src/color/srlab2.rs index f53e9ad..f1f7ca0 100644 --- a/canvas/src/color/srlab2.rs +++ b/canvas/src/color/srlab2.rs @@ -1,5 +1,6 @@ use crate::color::Whitepoint; use crate::color_matrix::ColMatrix; +use crate::math::powf; #[rustfmt::skip] const M_CAT02: ColMatrix = ColMatrix([ @@ -119,7 +120,7 @@ fn non_linearity(lms: [f32; 3]) -> [f32; 3] { // Limited to 0.08 precisely v * 24389.0 / 2700.0 } else { - 1.16 * v.powf(1.0 / 3.0) - 0.16 + 1.16 * powf(v, 1.0 / 3.0) - 0.16 } } @@ -131,7 +132,7 @@ fn non_linearity_inv(lms: [f32; 3]) -> [f32; 3] { if v.abs() < 0.08 { v * 2700.0 / 24389.0 } else { - ((v + 0.16) / 1.16).powf(3.0) + powf((v + 0.16) / 1.16, 3.0) } } diff --git a/canvas/src/color/transfer.rs b/canvas/src/color/transfer.rs index 994795a..bae75c5 100644 --- a/canvas/src/color/transfer.rs +++ b/canvas/src/color/transfer.rs @@ -1,7 +1,7 @@ /// To emulate the syntax used in GLSL more closely. #[inline] fn pow(base: f32, exp: f32) -> f32 { - base.powf(exp) + crate::math::powf(base, exp) } pub fn transfer_oe_bt709(val: f32) -> f32 { diff --git a/canvas/src/frame.rs b/canvas/src/frame.rs index 7e90248..8d10461 100644 --- a/canvas/src/frame.rs +++ b/canvas/src/frame.rs @@ -1,4 +1,8 @@ //! A byte-buffer based image descriptor. + +use alloc::borrow::ToOwned; +use alloc::vec::Vec; + use image_texel::image::{ImageMut, ImageRef}; use image_texel::Image; diff --git a/canvas/src/layout.rs b/canvas/src/layout.rs index c87ca2c..8598d6f 100644 --- a/canvas/src/layout.rs +++ b/canvas/src/layout.rs @@ -1,5 +1,6 @@ //! Defines layout and buffer of our images. -use crate::color::{Color, ColorChannel, ColorChannelModel}; + +use alloc::boxed::Box; use image_texel::image::{Coord, ImageRef}; use image_texel::layout::{ @@ -7,6 +8,7 @@ use image_texel::layout::{ Strides, TexelLayout, }; +use crate::color::{Color, ColorChannel, ColorChannelModel}; use crate::shader::ChunkSpec; /// The byte layout of a buffer. diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index a0c0908..2924e1c 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -55,6 +55,14 @@ // Deny, not forbid, unsafe code. In `arch` module we have inherently unsafe code, for the moment. // Maybe at a future point we gain some possibility to write such code safely. #![deny(unsafe_code)] +// Be std for doctests, avoids a weird warning about missing allocator. +#![cfg_attr(not(doctest), no_std)] + +#[cfg(feature = "runtime-features")] +extern crate std; + +#[macro_use] +extern crate alloc; mod arch; mod bits; @@ -65,6 +73,8 @@ mod color_matrix; mod frame; /// The layout implementation, builders, descriptors. pub mod layout; +/// Core maths operations. +mod math; /// Conversion operation. mod shader; diff --git a/canvas/src/math.rs b/canvas/src/math.rs new file mode 100644 index 0000000..7daac4e --- /dev/null +++ b/canvas/src/math.rs @@ -0,0 +1,5 @@ +/// Equivalent to `f32::powf` but suitable on `no_std`. +#[inline] +pub(crate) fn powf(base: f32, exp: f32) -> f32 { + libm::powf(base, exp) +} diff --git a/canvas/src/shader.rs b/canvas/src/shader.rs index 7936e87..74aab9c 100644 --- a/canvas/src/shader.rs +++ b/canvas/src/shader.rs @@ -2,6 +2,7 @@ //! //! Takes quite a lot of inspiration from how GPUs work. We have a primitive sampler unit, a //! fragment unit, and pipeline multiple texels in parallel. +use alloc::vec::Vec; use core::ops::Range; use image_texel::image::{ImageMut, ImageRef}; use image_texel::{AsTexel, Texel, TexelBuffer}; @@ -1483,7 +1484,9 @@ impl CommonPixel { // FIXME: do the transform u32::from_ne_bytes(x.as_ne_bytes()) when appropriate. join_fn: |num, bits, idx| { let max_val = bits.mask(); - let raw = (num[(idx & 0x3) as usize] * max_val as f32).round() as u32; + // Equivalent to `x.round() as u32` for positive-normal f32 + let round = |x| (x + 0.5) as u32; + let raw = round(num[(idx & 0x3) as usize] * max_val as f32); raw.min(max_val) }, bits, From c4ba5ee8195cefb1d767960006de706c78d36670 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 13 May 2025 14:20:04 +1000 Subject: [PATCH 3/7] `no_std` for `image-drm` --- drm/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drm/src/lib.rs b/drm/src/lib.rs index 8ab5702..6123ca6 100644 --- a/drm/src/lib.rs +++ b/drm/src/lib.rs @@ -13,6 +13,9 @@ //! pixel matrix. Then some of those formats map cleanly to planes of color information that can be //! viewed as a matrix with strides, which finally enables useful operations such as //! initialization. +// Be std for doctests, avoids a weird warning about missing allocator. +#![cfg_attr(not(doctest), no_std)] + use canvas::{layout, texels}; use core::convert::TryFrom; use core::fmt; From d627782a4adea8a97c8b9ee8b2843a6b12688e1d Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 13 May 2025 14:19:55 +1000 Subject: [PATCH 4/7] `no_std` for `image-canvas` --- canvas/Cargo.toml | 5 ++ canvas/src/arch.rs | 92 ++++++++++++++++++++++++++++++------ canvas/src/color/oklab.rs | 3 +- canvas/src/color/srlab2.rs | 5 +- canvas/src/color/transfer.rs | 2 +- canvas/src/frame.rs | 4 ++ canvas/src/layout.rs | 4 +- canvas/src/lib.rs | 10 ++++ canvas/src/math.rs | 5 ++ canvas/src/shader.rs | 5 +- 10 files changed, 114 insertions(+), 21 deletions(-) create mode 100644 canvas/src/math.rs diff --git a/canvas/Cargo.toml b/canvas/Cargo.toml index a0b2f58..cb86618 100644 --- a/canvas/Cargo.toml +++ b/canvas/Cargo.toml @@ -14,6 +14,11 @@ categories = ["multimedia::images"] [dependencies] image-texel = { path = "../texel", version = "0.5.0" } bytemuck = "1.1" +libm = { version = "0.2", default-features = false, features = ["arch"] } + +[features] +# Use runtime feature detection on x86 and x86_64 targets. +runtime-features = [] [dev-dependencies] brunch = "0.6.1" diff --git a/canvas/src/arch.rs b/canvas/src/arch.rs index a311f2b..bdcffcd 100644 --- a/canvas/src/arch.rs +++ b/canvas/src/arch.rs @@ -1,4 +1,6 @@ #![allow(unsafe_code)] +// May be unused if no architecture features are detected at compile time or runtime. +#[allow(unused_imports)] use core::mem::transmute; // For when we want to make sure we have a texel at compile time based on bytemuck. @@ -12,8 +14,12 @@ macro_rules! expect_texel { }; } +// May be unused if no architecture features are detected at compile time or runtime. +#[allow(dead_code)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86_avx2; +// May be unused if no architecture features are detected at compile time or runtime. +#[allow(dead_code)] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86_ssse3; @@ -30,29 +36,85 @@ pub(crate) struct ShuffleOps { impl ShuffleOps { /// FIXME(perf): implement and choose arch-specific shuffles. + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(unused_mut)] pub fn with_arch(mut self) -> Self { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + self = self.with_x86(); + } + + self + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(unused_mut)] + fn with_x86(mut self) -> Self { + #[cfg(target_feature = "ssse3")] + // SAFETY: `ssse3` detected at compile time + unsafe { + self = self.with_x86_ssse3(); + } + + #[cfg(not(target_feature = "ssse3"))] + #[cfg(feature = "runtime-features")] if std::is_x86_feature_detected!("ssse3") { - self.shuffle_u8x4 = unsafe { - transmute::(x86_ssse3::shuffle_u8x4) - }; - self.shuffle_u16x4 = unsafe { - transmute::(x86_ssse3::shuffle_u16x4) - }; + // SAFETY: `ssse3` detected at runtime + unsafe { + self = self.with_x86_ssse3(); + } + } + + #[cfg(target_feature = "avx2")] + // SAFETY: `avx2` detected at compile time + unsafe { + self = self.with_x86_avx2(); + } + + #[cfg(not(target_feature = "avx2"))] + #[cfg(feature = "runtime-features")] + if std::is_x86_feature_detected!("avx2") { + // SAFETY: `avx2` detected at runtime + unsafe { + self = self.with_x86_avx2(); + } } + self + } + + /// # Safety + /// + /// Must only be used when the `ssse3` feature is available. + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(dead_code)] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe fn with_x86_ssse3(mut self) -> Self { + self.shuffle_u8x4 = + unsafe { transmute::(x86_ssse3::shuffle_u8x4) }; + self.shuffle_u16x4 = unsafe { + transmute::(x86_ssse3::shuffle_u16x4) + }; + + self + } + + /// # Safety + /// + /// Must only be used when the `avx2` feature is available. + // May be unused if no architecture features are detected at compile time or runtime. + #[allow(dead_code)] + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + unsafe fn with_x86_avx2(mut self) -> Self { // Note: On Ivy Bridge these have the same *throughput* of 256bit-per-cycle as their SSSE3 // equivalents until Icelake. With Icelake they are twice as fast at 512bit-per-cycle. // Therefore, we don't select them until we find a way to predict/select this. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - if std::is_x86_feature_detected!("avx2") { - self.shuffle_u8x4 = unsafe { - transmute::(x86_avx2::shuffle_u8x4) - }; - self.shuffle_u16x4 = unsafe { - transmute::(x86_avx2::shuffle_u16x4) - }; - } + + self.shuffle_u8x4 = + unsafe { transmute::(x86_avx2::shuffle_u8x4) }; + self.shuffle_u16x4 = + unsafe { transmute::(x86_avx2::shuffle_u16x4) }; self } diff --git a/canvas/src/color/oklab.rs b/canvas/src/color/oklab.rs index bdd9bd2..08e1eea 100644 --- a/canvas/src/color/oklab.rs +++ b/canvas/src/color/oklab.rs @@ -1,4 +1,5 @@ use crate::color_matrix::ColMatrix; +use crate::math::powf; const M1: ColMatrix = ColMatrix([ [0.8189330101, 0.0329845436, 0.0482003018], @@ -94,7 +95,7 @@ pub(crate) fn f_lms_inv(lms: [f32; 3]) -> [f32; 3] { } fn pow([a, b, c]: [f32; 3], exp: f32) -> [f32; 3] { - [a.powf(exp), b.powf(exp), c.powf(exp)] + [powf(a, exp), powf(b, exp), powf(c, exp)] } fn copysign([a, b, c]: [f32; 3], [sa, sb, sc]: [f32; 3]) -> [f32; 3] { diff --git a/canvas/src/color/srlab2.rs b/canvas/src/color/srlab2.rs index f53e9ad..f1f7ca0 100644 --- a/canvas/src/color/srlab2.rs +++ b/canvas/src/color/srlab2.rs @@ -1,5 +1,6 @@ use crate::color::Whitepoint; use crate::color_matrix::ColMatrix; +use crate::math::powf; #[rustfmt::skip] const M_CAT02: ColMatrix = ColMatrix([ @@ -119,7 +120,7 @@ fn non_linearity(lms: [f32; 3]) -> [f32; 3] { // Limited to 0.08 precisely v * 24389.0 / 2700.0 } else { - 1.16 * v.powf(1.0 / 3.0) - 0.16 + 1.16 * powf(v, 1.0 / 3.0) - 0.16 } } @@ -131,7 +132,7 @@ fn non_linearity_inv(lms: [f32; 3]) -> [f32; 3] { if v.abs() < 0.08 { v * 2700.0 / 24389.0 } else { - ((v + 0.16) / 1.16).powf(3.0) + powf((v + 0.16) / 1.16, 3.0) } } diff --git a/canvas/src/color/transfer.rs b/canvas/src/color/transfer.rs index ec7b06e..4202809 100644 --- a/canvas/src/color/transfer.rs +++ b/canvas/src/color/transfer.rs @@ -1,7 +1,7 @@ /// To emulate the syntax used in GLSL more closely. #[inline] fn pow(base: f32, exp: f32) -> f32 { - base.powf(exp) + crate::math::powf(base, exp) } pub fn transfer_oe_bt709(val: f32) -> f32 { diff --git a/canvas/src/frame.rs b/canvas/src/frame.rs index 963b143..09ae416 100644 --- a/canvas/src/frame.rs +++ b/canvas/src/frame.rs @@ -1,4 +1,8 @@ //! A byte-buffer based image descriptor. + +use alloc::borrow::ToOwned; +use alloc::vec::Vec; + use image_texel::image::{ImageMut, ImageRef}; use image_texel::Image; diff --git a/canvas/src/layout.rs b/canvas/src/layout.rs index bae5308..3f471b4 100644 --- a/canvas/src/layout.rs +++ b/canvas/src/layout.rs @@ -1,5 +1,6 @@ //! Defines layout and buffer of our images. -use crate::color::{Color, ColorChannel, ColorChannelModel}; + +use alloc::boxed::Box; use image_texel::image::{Coord, ImageRef}; use image_texel::layout::{ @@ -7,6 +8,7 @@ use image_texel::layout::{ Strides, TexelLayout, }; +use crate::color::{Color, ColorChannel, ColorChannelModel}; use crate::shader::ChunkSpec; /// The byte layout of a buffer. diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index a0c0908..2924e1c 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -55,6 +55,14 @@ // Deny, not forbid, unsafe code. In `arch` module we have inherently unsafe code, for the moment. // Maybe at a future point we gain some possibility to write such code safely. #![deny(unsafe_code)] +// Be std for doctests, avoids a weird warning about missing allocator. +#![cfg_attr(not(doctest), no_std)] + +#[cfg(feature = "runtime-features")] +extern crate std; + +#[macro_use] +extern crate alloc; mod arch; mod bits; @@ -65,6 +73,8 @@ mod color_matrix; mod frame; /// The layout implementation, builders, descriptors. pub mod layout; +/// Core maths operations. +mod math; /// Conversion operation. mod shader; diff --git a/canvas/src/math.rs b/canvas/src/math.rs new file mode 100644 index 0000000..7daac4e --- /dev/null +++ b/canvas/src/math.rs @@ -0,0 +1,5 @@ +/// Equivalent to `f32::powf` but suitable on `no_std`. +#[inline] +pub(crate) fn powf(base: f32, exp: f32) -> f32 { + libm::powf(base, exp) +} diff --git a/canvas/src/shader.rs b/canvas/src/shader.rs index a1102c4..97ef2f9 100644 --- a/canvas/src/shader.rs +++ b/canvas/src/shader.rs @@ -2,6 +2,7 @@ //! //! Takes quite a lot of inspiration from how GPUs work. We have a primitive sampler unit, a //! fragment unit, and pipeline multiple texels in parallel. +use alloc::vec::Vec; use core::ops::Range; use image_texel::image::{ImageMut, ImageRef}; use image_texel::{AsTexel, Texel, TexelBuffer}; @@ -1482,7 +1483,9 @@ impl CommonPixel { // FIXME: do the transform u32::from_ne_bytes(x.as_ne_bytes()) when appropriate. join_fn: |num, bits, idx| { let max_val = bits.mask(); - let raw = (num[(idx & 0x3) as usize] * max_val as f32).round() as u32; + // Equivalent to `x.round() as u32` for positive-normal f32 + let round = |x| (x + 0.5) as u32; + let raw = round(num[(idx & 0x3) as usize] * max_val as f32); raw.min(max_val) }, bits, From e6ade8f22eed7f856eb99e115c87b36002fc0d39 Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 13 May 2025 14:20:04 +1000 Subject: [PATCH 5/7] `no_std` for `image-drm` --- drm/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drm/src/lib.rs b/drm/src/lib.rs index 8ab5702..6123ca6 100644 --- a/drm/src/lib.rs +++ b/drm/src/lib.rs @@ -13,6 +13,9 @@ //! pixel matrix. Then some of those formats map cleanly to planes of color information that can be //! viewed as a matrix with strides, which finally enables useful operations such as //! initialization. +// Be std for doctests, avoids a weird warning about missing allocator. +#![cfg_attr(not(doctest), no_std)] + use canvas::{layout, texels}; use core::convert::TryFrom; use core::fmt; From 6cb24d9deaa1a64f28dca7c091b5221bd9fb715f Mon Sep 17 00:00:00 2001 From: Zac Harrold Date: Tue, 13 May 2025 20:28:03 +1000 Subject: [PATCH 6/7] Use `libm::powf` explicitly --- canvas/src/color/oklab.rs | 2 +- canvas/src/color/srlab2.rs | 2 +- canvas/src/color/transfer.rs | 2 +- canvas/src/lib.rs | 2 -- canvas/src/math.rs | 5 ----- 5 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 canvas/src/math.rs diff --git a/canvas/src/color/oklab.rs b/canvas/src/color/oklab.rs index 08e1eea..e2291fe 100644 --- a/canvas/src/color/oklab.rs +++ b/canvas/src/color/oklab.rs @@ -1,5 +1,5 @@ use crate::color_matrix::ColMatrix; -use crate::math::powf; +use libm::powf; const M1: ColMatrix = ColMatrix([ [0.8189330101, 0.0329845436, 0.0482003018], diff --git a/canvas/src/color/srlab2.rs b/canvas/src/color/srlab2.rs index f1f7ca0..849a0df 100644 --- a/canvas/src/color/srlab2.rs +++ b/canvas/src/color/srlab2.rs @@ -1,6 +1,6 @@ use crate::color::Whitepoint; use crate::color_matrix::ColMatrix; -use crate::math::powf; +use libm::powf; #[rustfmt::skip] const M_CAT02: ColMatrix = ColMatrix([ diff --git a/canvas/src/color/transfer.rs b/canvas/src/color/transfer.rs index bae75c5..e1b9ecb 100644 --- a/canvas/src/color/transfer.rs +++ b/canvas/src/color/transfer.rs @@ -1,7 +1,7 @@ /// To emulate the syntax used in GLSL more closely. #[inline] fn pow(base: f32, exp: f32) -> f32 { - crate::math::powf(base, exp) + libm::powf(base, exp) } pub fn transfer_oe_bt709(val: f32) -> f32 { diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 2924e1c..3930042 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -73,8 +73,6 @@ mod color_matrix; mod frame; /// The layout implementation, builders, descriptors. pub mod layout; -/// Core maths operations. -mod math; /// Conversion operation. mod shader; diff --git a/canvas/src/math.rs b/canvas/src/math.rs deleted file mode 100644 index 7daac4e..0000000 --- a/canvas/src/math.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// Equivalent to `f32::powf` but suitable on `no_std`. -#[inline] -pub(crate) fn powf(base: f32, exp: f32) -> f32 { - libm::powf(base, exp) -} From 43b05c5835db54cce8a5630b6578e4da3461fa5a Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+HeroicKatora@users.noreply.github.com> Date: Tue, 13 May 2025 15:50:49 +0200 Subject: [PATCH 7/7] Remove #[expect()] for unfinished features --- canvas/src/bits.rs | 1 - canvas/src/color/yuv.rs | 2 -- canvas/src/frame.rs | 1 - canvas/src/layout.rs | 1 - canvas/src/shader.rs | 1 - texel/src/image/raw.rs | 1 - texel/src/layout/upsampling.rs | 1 - 7 files changed, 8 deletions(-) diff --git a/canvas/src/bits.rs b/canvas/src/bits.rs index fe4feca..6e55551 100644 --- a/canvas/src/bits.rs +++ b/canvas/src/bits.rs @@ -1,5 +1,4 @@ use crate::layout::{SampleBits, SampleParts}; -#[expect(unused_imports)] use image_texel::AsTexel; use image_texel::Texel; diff --git a/canvas/src/color/yuv.rs b/canvas/src/color/yuv.rs index 895861e..2a99a9c 100644 --- a/canvas/src/color/yuv.rs +++ b/canvas/src/color/yuv.rs @@ -107,7 +107,6 @@ impl Differencing { struct Bt407MPal; struct Bt407MPalPrecise; struct Pal525; -#[expect(dead_code)] struct Pal625; struct Bt601; struct Bt601Quantized; @@ -119,7 +118,6 @@ struct Bt2020; struct Bt2100; struct YDbDr; struct YCoCg; -#[expect(dead_code)] struct YCoCgR; // We derive the coefficients from scratch, from their definition. diff --git a/canvas/src/frame.rs b/canvas/src/frame.rs index 8d10461..09ae416 100644 --- a/canvas/src/frame.rs +++ b/canvas/src/frame.rs @@ -22,7 +22,6 @@ pub struct Plane { inner: Image, } -#[expect(dead_code)] #[doc(hidden)] #[deprecated = "Use BytePlaneRef"] pub type BytePlane<'data> = BytePlaneRef<'data>; diff --git a/canvas/src/layout.rs b/canvas/src/layout.rs index 8598d6f..3f471b4 100644 --- a/canvas/src/layout.rs +++ b/canvas/src/layout.rs @@ -378,7 +378,6 @@ pub enum SampleBits { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) enum BitEncoding { - #[expect(dead_code)] Opaque, UInt, Int, diff --git a/canvas/src/shader.rs b/canvas/src/shader.rs index 74aab9c..97ef2f9 100644 --- a/canvas/src/shader.rs +++ b/canvas/src/shader.rs @@ -1232,7 +1232,6 @@ impl CommonPixel { ) } - #[expect(dead_code)] fn expand_yuy2(info: &Info, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) { struct ExpandYuy2; diff --git a/texel/src/image/raw.rs b/texel/src/image/raw.rs index 05601b2..7c6ac61 100644 --- a/texel/src/image/raw.rs +++ b/texel/src/image/raw.rs @@ -124,7 +124,6 @@ impl RawImage { /// Methods specifically with a dynamic layout. impl RawImage { - #[expect(dead_code)] pub(crate) fn try_from_dynamic(self, layout: Other) -> Result, Self> where Other: Into + Clone, diff --git a/texel/src/layout/upsampling.rs b/texel/src/layout/upsampling.rs index bd10da5..70eaa72 100644 --- a/texel/src/layout/upsampling.rs +++ b/texel/src/layout/upsampling.rs @@ -11,7 +11,6 @@ pub(crate) struct Yuv420p { } impl Yuv420p { - #[expect(dead_code)] pub fn from_width_height(channel: TexelLayout, width: u32, height: u32) -> Option { use core::convert::TryFrom; if width % 2 != 0 || height % 2 != 0 {