pixels: Ensure expected formats when accesing bytes of snapshot (#37767)

I introduced snapshot in #36119 to pack raw bytes and metadata together,
now we take the next step and require for user to always specify what
kind of byte data they want when calling `as_bytes` or `to_vec`
(basically joining transform and data). There are also valid usages when
one might require just one property of bytes (textures can generally
handle both RGBA and BGRA). There are also valid usages of using just
raw bytes (when cropping). This PR tries to make such usages more
obvious.

This will make it easier to fix stuff around 2d canvas (we do not want
to assume any bytes properties in abstraction).

Testing: Code is covered by WPT tests.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
sagudev 2025-07-03 17:02:41 +02:00 committed by GitHub
parent e3baec4807
commit a631b42e60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 142 additions and 94 deletions

View file

@ -18,6 +18,33 @@ pub enum SnapshotPixelFormat {
BGRA,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum Alpha {
Premultiplied,
NotPremultiplied,
/// This is used for opaque textures for which the presence of alpha in the
/// output data format does not matter.
DontCare,
}
impl Alpha {
pub const fn from_premultiplied(is_premultiplied: bool) -> Self {
if is_premultiplied {
Self::Premultiplied
} else {
Self::NotPremultiplied
}
}
pub const fn needs_alpha_multiplication(&self) -> bool {
match self {
Alpha::Premultiplied => false,
Alpha::NotPremultiplied => true,
Alpha::DontCare => false,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum SnapshotAlphaMode {
/// Internal data is opaque (alpha is cleared to 1)
@ -37,20 +64,17 @@ impl Default for SnapshotAlphaMode {
}
impl SnapshotAlphaMode {
pub const fn is_premultiplied(&self) -> bool {
pub const fn alpha(&self) -> Alpha {
match self {
SnapshotAlphaMode::Opaque => true,
SnapshotAlphaMode::AsOpaque { premultiplied } => *premultiplied,
SnapshotAlphaMode::Transparent { premultiplied } => *premultiplied,
SnapshotAlphaMode::Opaque => Alpha::DontCare,
SnapshotAlphaMode::AsOpaque { premultiplied } => {
Alpha::from_premultiplied(*premultiplied)
},
SnapshotAlphaMode::Transparent { premultiplied } => {
Alpha::from_premultiplied(*premultiplied)
},
}
}
pub const fn is_opaque(&self) -> bool {
matches!(
self,
SnapshotAlphaMode::Opaque | SnapshotAlphaMode::AsOpaque { .. }
)
}
}
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
@ -112,14 +136,6 @@ impl<T> Snapshot<T> {
pub const fn alpha_mode(&self) -> SnapshotAlphaMode {
self.alpha_mode
}
pub const fn is_premultiplied(&self) -> bool {
self.alpha_mode().is_premultiplied()
}
pub const fn is_opaque(&self) -> bool {
self.alpha_mode().is_opaque()
}
}
impl Snapshot<SnapshotData> {
@ -181,14 +197,6 @@ impl Snapshot<SnapshotData> {
}
*/
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.data
}
/// Convert inner data of snapshot to target format and alpha mode.
/// If data is already in target format and alpha mode no work will be done.
pub fn transform(
@ -200,7 +208,7 @@ impl Snapshot<SnapshotData> {
let multiply = match (self.alpha_mode, target_alpha_mode) {
(SnapshotAlphaMode::Opaque, _) => Multiply::None,
(alpha_mode, SnapshotAlphaMode::Opaque) => {
if alpha_mode.is_premultiplied() {
if alpha_mode.alpha() == Alpha::Premultiplied {
Multiply::UnMultiply
} else {
Multiply::None
@ -232,6 +240,37 @@ impl Snapshot<SnapshotData> {
self.format = target_format;
}
pub fn as_raw_bytes(&self) -> &[u8] {
&self.data
}
pub fn as_raw_bytes_mut(&mut self) -> &mut [u8] {
&mut self.data
}
pub fn as_bytes(
&mut self,
target_alpha_mode: Option<SnapshotAlphaMode>,
target_format: Option<SnapshotPixelFormat>,
) -> (&mut [u8], SnapshotAlphaMode, SnapshotPixelFormat) {
let target_alpha_mode = target_alpha_mode.unwrap_or(self.alpha_mode);
let target_format = target_format.unwrap_or(self.format);
self.transform(target_alpha_mode, target_format);
(&mut self.data, target_alpha_mode, target_format)
}
pub fn to_vec(
mut self,
target_alpha_mode: Option<SnapshotAlphaMode>,
target_format: Option<SnapshotPixelFormat>,
) -> (Vec<u8>, SnapshotAlphaMode, SnapshotPixelFormat) {
let target_alpha_mode = target_alpha_mode.unwrap_or(self.alpha_mode);
let target_format = target_format.unwrap_or(self.format);
self.transform(target_alpha_mode, target_format);
let SnapshotData::Owned(data) = self.data;
(data, target_alpha_mode, target_format)
}
pub fn as_ipc(self) -> Snapshot<IpcSharedMemory> {
let Snapshot {
size,
@ -250,12 +289,6 @@ impl Snapshot<SnapshotData> {
alpha_mode,
}
}
pub fn to_vec(self) -> Vec<u8> {
match self.data {
SnapshotData::Owned(data) => data,
}
}
}
impl Snapshot<IpcSharedMemory> {