From e1a891ea96bb67be81fb1b5aa6ecf9edd4621dc3 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Fri, 4 Jul 2025 22:22:20 +0200 Subject: [PATCH] canvas: Use snapshot in canvas backends (#37863) This removes assumption about pixel format from backend abstraction to actual backend implementation. This is important for vello. Testing: WPT tests --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- components/canvas/backend.rs | 10 +- components/canvas/canvas_data.rs | 85 +++------- components/canvas/canvas_paint_thread.rs | 54 ++----- components/canvas/raqote_backend.rs | 151 +++++++++++------- components/pixels/snapshot.rs | 9 +- components/script/canvas_state.rs | 15 +- components/script/dom/canvaspattern.rs | 10 +- components/script/dom/imagedata.rs | 13 ++ components/shared/canvas/canvas.rs | 11 +- .../canvas-display-p3-pattern-image.html.ini | 108 ------------- 10 files changed, 174 insertions(+), 292 deletions(-) diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs index 01bcc6d26f5..5da67feaf3f 100644 --- a/components/canvas/backend.rs +++ b/components/canvas/backend.rs @@ -2,15 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::borrow::Cow; - use canvas_traits::canvas::{ CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, PathSegment, }; +use compositing_traits::SerializableImageData; use euclid::Angle; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use lyon_geom::Arc; +use pixels::Snapshot; use style::color::AbsoluteColor; +use webrender_api::ImageDescriptor; use crate::canvas_data::{CanvasPaintState, Filter, PathBuilderRef, TextRun}; @@ -61,7 +62,7 @@ pub(crate) trait GenericDrawTarget { destination: Point2D, ); fn create_similar_draw_target(&self, size: &Size2D) -> Self; - fn create_source_surface_from_data(&self, data: &[u8]) -> Option; + fn create_source_surface_from_data(&self, data: Snapshot) -> Option; fn draw_surface( &mut self, surface: B::SourceSurface, @@ -113,7 +114,8 @@ pub(crate) trait GenericDrawTarget { draw_options: &B::DrawOptions, ); fn surface(&self) -> B::SourceSurface; - fn bytes(&self) -> Cow<[u8]>; + fn image_descriptor_and_serializable_data(&self) -> (ImageDescriptor, SerializableImageData); + fn snapshot(&self) -> Snapshot; } /// A generic Path that abstracts the interface for raqote's PathBuilder/Path. diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 923b719599a..87122aa4fc6 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -8,22 +8,22 @@ use std::sync::Arc; use app_units::Au; use canvas_traits::canvas::*; -use compositing_traits::{CrossProcessCompositorApi, SerializableImageData}; +use compositing_traits::CrossProcessCompositorApi; use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::point2; use fonts::{ ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore, LAST_RESORT_GLYPH_ADVANCE, ShapingFlags, ShapingOptions, }; -use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; +use ipc_channel::ipc::IpcSender; use log::warn; -use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat}; +use pixels::Snapshot; use range::Range; use servo_arc::Arc as ServoArc; use style::color::AbsoluteColor; use style::properties::style_structs::Font as FontStyleStruct; use unicode_script::Script; -use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; +use webrender_api::ImageKey; use crate::backend::{ Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPath, PatternHelpers, @@ -352,15 +352,7 @@ impl<'a, B: Backend> CanvasData<'a, B> { let size = size.max(MIN_WR_IMAGE_SIZE); let draw_target = backend.create_drawtarget(size); let image_key = compositor_api.generate_image_key_blocking().unwrap(); - let descriptor = ImageDescriptor { - size: size.cast().cast_unit(), - stride: None, - format: ImageFormat::BGRA8, - offset: 0, - flags: ImageDescriptorFlags::empty(), - }; - let data = - SerializableImageData::Raw(IpcSharedMemory::from_bytes(draw_target.bytes().as_ref())); + let (descriptor, data) = draw_target.image_descriptor_and_serializable_data(); compositor_api.add_image(image_key, descriptor, data); CanvasData { state: backend.new_paint_state(), @@ -380,31 +372,27 @@ impl<'a, B: Backend> CanvasData<'a, B> { pub(crate) fn draw_image( &mut self, - image_data: &[u8], - image_size: Size2D, + snapshot: Snapshot, dest_rect: Rect, source_rect: Rect, smoothing_enabled: bool, - premultiply: bool, ) { // We round up the floating pixel values to draw the pixels let source_rect = source_rect.ceil(); // It discards the extra pixels (if any) that won't be painted - let image_data = if Rect::from_size(image_size.to_f64()).contains_rect(&source_rect) { - pixels::rgba8_get_rect(image_data, image_size, source_rect.to_u32()).into() + let snapshot = if Rect::from_size(snapshot.size().to_f64()).contains_rect(&source_rect) { + snapshot.get_rect(source_rect.to_u32()) } else { - image_data.into() + snapshot }; let draw_options = self.state.draw_options.clone(); let writer = |draw_target: &mut B::DrawTarget| { write_image::( draw_target, - image_data, - source_rect.size, + snapshot, dest_rect, smoothing_enabled, - premultiply, &draw_options, ); }; @@ -1107,29 +1095,18 @@ impl<'a, B: Backend> CanvasData<'a, B> { /// Update image in WebRender pub(crate) fn update_image_rendering(&mut self) { - let descriptor = ImageDescriptor { - size: self.drawtarget.get_size().cast_unit(), - stride: None, - format: ImageFormat::BGRA8, - offset: 0, - flags: ImageDescriptorFlags::empty(), - }; - let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes( - self.drawtarget.bytes().as_ref(), - )); + let (descriptor, data) = self.drawtarget.image_descriptor_and_serializable_data(); self.compositor_api .update_image(self.image_key, descriptor, data); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - pub(crate) fn put_image_data(&mut self, mut imagedata: Vec, rect: Rect) { - assert_eq!(imagedata.len() % 4, 0); - assert_eq!(rect.size.area() as usize, imagedata.len() / 4); - pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); + pub(crate) fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect) { + assert_eq!(rect.size, snapshot.size()); let source_surface = self .drawtarget - .create_source_surface_from_data(&imagedata) + .create_source_surface_from_data(snapshot) .unwrap(); self.drawtarget.copy_surface( source_surface, @@ -1217,29 +1194,19 @@ impl<'a, B: Backend> CanvasData<'a, B> { ) -> Snapshot { let canvas_size = canvas_size.unwrap_or(self.drawtarget.get_size().cast()); - let data = if let Some(read_rect) = read_rect { + if let Some(read_rect) = read_rect { let canvas_rect = Rect::from_size(canvas_size); if canvas_rect .intersection(&read_rect) .is_none_or(|rect| rect.is_empty()) { - vec![] + Snapshot::empty() } else { - pixels::rgba8_get_rect(self.drawtarget.bytes().as_ref(), canvas_size, read_rect) - .to_vec() + self.drawtarget.snapshot().get_rect(read_rect) } } else { - self.drawtarget.bytes().into_owned() - }; - - Snapshot::from_vec( - canvas_size, - SnapshotPixelFormat::BGRA, - SnapshotAlphaMode::Transparent { - premultiplied: true, - }, - data, - ) + self.drawtarget.snapshot() + } } } @@ -1279,22 +1246,16 @@ pub(crate) struct CanvasPaintState<'a, B: Backend> { /// premultiply: Determines whenever the image data should be premultiplied or not fn write_image( draw_target: &mut B::DrawTarget, - mut image_data: Vec, - image_size: Size2D, + snapshot: Snapshot, dest_rect: Rect, smoothing_enabled: bool, - premultiply: bool, draw_options: &B::DrawOptions, ) { - if image_data.is_empty() { + if snapshot.size().is_empty() { return; } - if premultiply { - pixels::rgba8_premultiply_inplace(&mut image_data); - } - - let image_rect = Rect::new(Point2D::zero(), image_size); + let image_rect = Rect::new(Point2D::zero(), snapshot.size().cast()); // From spec https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage // When scaling up, if the imageSmoothingEnabled attribute is set to true, the user agent should attempt @@ -1307,7 +1268,7 @@ fn write_image( }; let source_surface = draw_target - .create_source_surface_from_data(&image_data) + .create_source_surface_from_data(snapshot) .unwrap(); draw_target.draw_surface(source_surface, dest_rect, image_rect, filter, draw_options); diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 4c8781887e0..eb28fe97037 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -17,7 +17,7 @@ use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; use net_traits::ResourceThreads; -use pixels::{Snapshot, SnapshotPixelFormat}; +use pixels::Snapshot; use style::color::AbsoluteColor; use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::ImageKey; @@ -175,29 +175,16 @@ impl<'a> CanvasPaintThread<'a> { .canvas(canvas_id) .is_point_in_path_(&path[..], x, y, fill_rule, chan), Canvas2dMsg::DrawImage(snapshot, dest_rect, source_rect, smoothing_enabled) => { - let mut snapshot = snapshot.to_owned(); - let size = snapshot.size(); - let (data, alpha_mode, _) = - snapshot.as_bytes(None, Some(SnapshotPixelFormat::BGRA)); self.canvas(canvas_id).draw_image( - data, - size, + snapshot.to_owned(), dest_rect, source_rect, smoothing_enabled, - alpha_mode.alpha().needs_alpha_multiplication(), - ) - }, - Canvas2dMsg::DrawEmptyImage(image_size, dest_rect, source_rect) => { - self.canvas(canvas_id).draw_image( - &vec![0; image_size.area() as usize * 4], - image_size, - dest_rect, - source_rect, - false, - false, ) }, + Canvas2dMsg::DrawEmptyImage(image_size, dest_rect, source_rect) => self + .canvas(canvas_id) + .draw_image(Snapshot::cleared(image_size), dest_rect, source_rect, false), Canvas2dMsg::DrawImageInOther( other_canvas_id, image_size, @@ -205,18 +192,14 @@ impl<'a> CanvasPaintThread<'a> { source_rect, smoothing, ) => { - let mut snapshot = self + let snapshot = self .canvas(canvas_id) .read_pixels(Some(source_rect.to_u32()), Some(image_size)); - let (data, alpha_mode, _) = - snapshot.as_bytes(None, Some(SnapshotPixelFormat::BGRA)); self.canvas(other_canvas_id).draw_image( - data, - source_rect.size.to_u32(), + snapshot, dest_rect, source_rect, smoothing, - alpha_mode.alpha().needs_alpha_multiplication(), ); }, Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point), @@ -266,9 +249,9 @@ impl<'a> CanvasPaintThread<'a> { .read_pixels(Some(dest_rect), Some(canvas_size)); sender.send(snapshot.as_ipc()).unwrap(); }, - Canvas2dMsg::PutImageData(rect, receiver) => { + Canvas2dMsg::PutImageData(rect, snapshot) => { self.canvas(canvas_id) - .put_image_data(receiver.recv().unwrap(), rect); + .put_image_data(snapshot.to_owned(), rect); }, Canvas2dMsg::SetShadowOffsetX(value) => { self.canvas(canvas_id).set_shadow_offset_x(value) @@ -403,22 +386,15 @@ impl Canvas<'_> { fn draw_image( &mut self, - data: &[u8], - size: Size2D, + snapshot: Snapshot, dest_rect: Rect, source_rect: Rect, smoothing_enabled: bool, - premultiply: bool, ) { match self { - Canvas::Raqote(canvas_data) => canvas_data.draw_image( - data, - size, - dest_rect, - source_rect, - smoothing_enabled, - premultiply, - ), + Canvas::Raqote(canvas_data) => { + canvas_data.draw_image(snapshot, dest_rect, source_rect, smoothing_enabled) + }, } } @@ -618,9 +594,9 @@ impl Canvas<'_> { } } - fn put_image_data(&mut self, unwrap: Vec, rect: Rect) { + fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect) { match self { - Canvas::Raqote(canvas_data) => canvas_data.put_image_data(unwrap, rect), + Canvas::Raqote(canvas_data) => canvas_data.put_image_data(snapshot, rect), } } diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 45cf461b753..20c57408fff 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -2,19 +2,22 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use canvas_traits::canvas::*; +use compositing_traits::SerializableImageData; use cssparser::color::clamp_unit_f32; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use font_kit::font::Font; use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods}; +use ipc_channel::ipc::IpcSharedMemory; use log::warn; +use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat}; use range::Range; use raqote::PathOp; use style::color::AbsoluteColor; +use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat}; use crate::backend::{ Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPath, PatternHelpers, @@ -34,7 +37,7 @@ thread_local! { pub(crate) struct RaqoteBackend; impl Backend for RaqoteBackend { - type Pattern<'a> = Pattern<'a>; + type Pattern<'a> = Pattern; type StrokeOptions = raqote::StrokeStyle; type Color = raqote::SolidSource; type DrawOptions = raqote::DrawOptions; @@ -112,12 +115,12 @@ impl Backend for RaqoteBackend { } #[derive(Clone)] -pub enum Pattern<'a> { +pub enum Pattern { // argb Color(u8, u8, u8, u8), LinearGradient(LinearGradientPattern), RadialGradient(RadialGradientPattern), - Surface(SurfacePattern<'a>), + Surface(SurfacePattern), } #[derive(Clone)] @@ -165,17 +168,17 @@ impl RadialGradientPattern { } #[derive(Clone)] -pub struct SurfacePattern<'a> { - image: raqote::Image<'a>, +pub struct SurfacePattern { + image: Snapshot, filter: raqote::FilterMode, extend: raqote::ExtendMode, repeat: Repetition, transform: Transform2D, } -impl<'a> SurfacePattern<'a> { +impl SurfacePattern { fn new( - image: raqote::Image<'a>, + image: Snapshot, filter: raqote::FilterMode, repeat: Repetition, transform: Transform2D, @@ -195,7 +198,7 @@ impl<'a> SurfacePattern<'a> { } } pub fn size(&self) -> Size2D { - Size2D::new(self.image.width as f32, self.image.height as f32) + self.image.size().cast() } pub fn repetition(&self) -> &Repetition { &self.repeat @@ -224,7 +227,7 @@ impl Repetition { } } -pub fn source<'a>(pattern: &Pattern<'a>) -> raqote::Source<'a> { +pub fn source(pattern: &Pattern) -> raqote::Source { match pattern { Pattern::Color(a, r, g, b) => raqote::Source::Solid( raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), @@ -243,16 +246,30 @@ pub fn source<'a>(pattern: &Pattern<'a>) -> raqote::Source<'a> { pattern.radius2, raqote::Spread::Pad, ), - Pattern::Surface(pattern) => raqote::Source::Image( - pattern.image, - pattern.extend, - pattern.filter, - pattern.transform, - ), + Pattern::Surface(pattern) => { + #[allow(unsafe_code)] + let data = unsafe { + let data = pattern.image.as_raw_bytes(); + std::slice::from_raw_parts( + data.as_ptr() as *const u32, + data.len() / std::mem::size_of::(), + ) + }; + raqote::Source::Image( + raqote::Image { + width: pattern.image.size().width as i32, + height: pattern.image.size().height as i32, + data, + }, + pattern.extend, + pattern.filter, + pattern.transform, + ) + }, } } -impl PatternHelpers for Pattern<'_> { +impl PatternHelpers for Pattern { fn is_zero_size_gradient(&self) -> bool { match self { Pattern::RadialGradient(pattern) => { @@ -371,9 +388,17 @@ impl GenericDrawTarget for raqote::DrawTarget { } fn create_source_surface_from_data( &self, - data: &[u8], + data: Snapshot, ) -> Option<::SourceSurface> { - Some(data.to_vec()) + Some( + data.to_vec( + Some(SnapshotAlphaMode::Transparent { + premultiplied: true, + }), + Some(SnapshotPixelFormat::BGRA), + ) + .0, + ) } #[allow(unsafe_code)] fn draw_surface( @@ -384,23 +409,20 @@ impl GenericDrawTarget for raqote::DrawTarget { filter: Filter, draw_options: &::DrawOptions, ) { - let surface_data = surface; - let image = raqote::Image { - width: source.size.width as i32, - height: source.size.height as i32, - data: unsafe { - std::slice::from_raw_parts( - surface_data.as_ptr() as *const u32, - surface_data.len() / std::mem::size_of::(), - ) + let image = Snapshot::from_vec( + source.size.cast(), + SnapshotPixelFormat::BGRA, + SnapshotAlphaMode::Transparent { + premultiplied: true, }, - }; + surface, + ); let transform = raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32) .then_scale( - image.width as f32 / dest.size.width as f32, - image.height as f32 / dest.size.height as f32, + source.size.width as f32 / dest.size.width as f32, + source.size.height as f32 / dest.size.height as f32, ); let pattern = Pattern::Surface(SurfacePattern::new( @@ -434,7 +456,7 @@ impl GenericDrawTarget for raqote::DrawTarget { fn fill( &mut self, path: &::Path, - pattern: &::Pattern<'_>, + pattern: &Pattern, draw_options: &::DrawOptions, ) { let path = path.into(); @@ -470,7 +492,7 @@ impl GenericDrawTarget for raqote::DrawTarget { &mut self, text_runs: Vec, start: Point2D, - pattern: &::Pattern<'_>, + pattern: &Pattern, draw_options: &::DrawOptions, ) { let mut advance = 0.; @@ -558,12 +580,12 @@ impl GenericDrawTarget for raqote::DrawTarget { self.set_transform(matrix); } fn surface(&self) -> ::SourceSurface { - self.bytes().to_vec() + self.get_data_u8().to_vec() } fn stroke( &mut self, path: &::Path, - pattern: &Pattern<'_>, + pattern: &Pattern, stroke_options: &::StrokeOptions, draw_options: &::DrawOptions, ) { @@ -586,12 +608,33 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke(&pb.finish(), &source(pattern), stroke_options, draw_options); } - #[allow(unsafe_code)] - fn bytes(&self) -> Cow<[u8]> { - let v = self.get_data(); - Cow::Borrowed(unsafe { - std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) - }) + + fn image_descriptor_and_serializable_data( + &self, + ) -> ( + webrender_api::ImageDescriptor, + compositing_traits::SerializableImageData, + ) { + let descriptor = ImageDescriptor { + size: self.get_size().cast_unit(), + stride: None, + format: ImageFormat::BGRA8, + offset: 0, + flags: ImageDescriptorFlags::empty(), + }; + let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes(self.get_data_u8())); + (descriptor, data) + } + + fn snapshot(&self) -> Snapshot { + Snapshot::from_vec( + self.get_size().cast(), + SnapshotPixelFormat::BGRA, + SnapshotAlphaMode::Transparent { + premultiplied: true, + }, + self.get_data_u8().to_vec(), + ) } } @@ -728,8 +771,8 @@ impl ToRaqoteStyle for LineCapStyle { } } -pub trait ToRaqotePattern<'a> { - fn to_raqote_pattern(self) -> Option>; +pub trait ToRaqotePattern { + fn to_raqote_pattern(self) -> Option; } pub trait ToRaqoteGradientStop { @@ -750,9 +793,9 @@ impl ToRaqoteGradientStop for CanvasGradientStop { } } -impl ToRaqotePattern<'_> for FillOrStrokeStyle { +impl ToRaqotePattern for FillOrStrokeStyle { #[allow(unsafe_code)] - fn to_raqote_pattern(self) -> Option> { + fn to_raqote_pattern(self) -> Option { use canvas_traits::canvas::FillOrStrokeStyle::*; match self { @@ -785,19 +828,17 @@ impl ToRaqotePattern<'_> for FillOrStrokeStyle { stops, ))) }, - Surface(ref style) => { + Surface(style) => { let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y); - let data = &style.surface_data[..]; - - let image = raqote::Image { - width: style.surface_size.width as i32, - height: style.surface_size.height as i32, - data: unsafe { - std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) + let mut snapshot = style.surface_data.to_owned(); + snapshot.transform( + SnapshotAlphaMode::Transparent { + premultiplied: true, }, - }; + SnapshotPixelFormat::BGRA, + ); Some(Pattern::Surface(SurfacePattern::new( - image, + snapshot, raqote::FilterMode::Nearest, repeat, style.transform, diff --git a/components/pixels/snapshot.rs b/components/pixels/snapshot.rs index 5cb8308db7a..7f371f7383a 100644 --- a/components/pixels/snapshot.rs +++ b/components/pixels/snapshot.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut}; -use euclid::default::Size2D; +use euclid::default::{Rect, Size2D}; use image::codecs::jpeg::JpegEncoder; use image::codecs::png::PngEncoder; use image::codecs::webp::WebPEncoder; @@ -13,7 +13,7 @@ use ipc_channel::ipc::IpcSharedMemory; use malloc_size_of_derive::MallocSizeOf; use serde::{Deserialize, Serialize}; -use crate::{EncodedImageType, Multiply, transform_inplace}; +use crate::{EncodedImageType, Multiply, rgba8_get_rect, transform_inplace}; #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] pub enum SnapshotPixelFormat { @@ -180,6 +180,11 @@ impl Snapshot { } } + pub fn get_rect(&self, rect: Rect) -> Self { + let data = rgba8_get_rect(self.as_raw_bytes(), self.size(), rect).to_vec(); + Self::from_vec(rect.size, self.format, self.alpha_mode, data) + } + // TODO: https://github.com/servo/servo/issues/36594 /* /// # Safety diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 33cf2d1dafe..269dba979da 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -1186,14 +1186,7 @@ impl CanvasState { let size = snapshot.size(); Ok(Some(CanvasPattern::new( global, - snapshot - .to_vec( - Some(SnapshotAlphaMode::Transparent { - premultiplied: true, - }), - Some(SnapshotPixelFormat::BGRA), - ) - .0, // TODO: send snapshot + snapshot, size.cast(), rep, self.is_origin_clean(image), @@ -1703,10 +1696,8 @@ impl CanvasState { }; // Step 7. - let (sender, receiver) = ipc::bytes_channel().unwrap(); - let pixels = unsafe { &imagedata.get_rect(Rect::new(src_rect.origin, dst_rect.size)) }; - self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(dst_rect, receiver)); - sender.send(pixels).unwrap(); + let snapshot = imagedata.get_snapshot_rect(Rect::new(src_rect.origin, dst_rect.size)); + self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(dst_rect, snapshot.as_ipc())); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage diff --git a/components/script/dom/canvaspattern.rs b/components/script/dom/canvaspattern.rs index 8c5d4340328..a262cccf5a2 100644 --- a/components/script/dom/canvaspattern.rs +++ b/components/script/dom/canvaspattern.rs @@ -5,6 +5,7 @@ use canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle}; use dom_struct::dom_struct; use euclid::default::{Size2D, Transform2D}; +use pixels::{IpcSnapshot, Snapshot}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasPatternMethods; @@ -21,7 +22,8 @@ use crate::script_runtime::CanGc; #[dom_struct] pub(crate) struct CanvasPattern { reflector_: Reflector, - surface_data: Vec, + #[no_trace] + surface_data: IpcSnapshot, #[no_trace] surface_size: Size2D, repeat_x: bool, @@ -33,7 +35,7 @@ pub(crate) struct CanvasPattern { impl CanvasPattern { fn new_inherited( - surface_data: Vec, + surface_data: Snapshot, surface_size: Size2D, repeat: RepetitionStyle, origin_clean: bool, @@ -47,7 +49,7 @@ impl CanvasPattern { CanvasPattern { reflector_: Reflector::new(), - surface_data, + surface_data: surface_data.as_ipc(), surface_size, repeat_x: x, repeat_y: y, @@ -57,7 +59,7 @@ impl CanvasPattern { } pub(crate) fn new( global: &GlobalScope, - surface_data: Vec, + surface_data: Snapshot, surface_size: Size2D, repeat: RepetitionStyle, origin_clean: bool, diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index 70c2febbc3e..0435c6141c8 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -12,6 +12,7 @@ use js::gc::CustomAutoRooterGuard; use js::jsapi::JSObject; use js::rust::HandleObject; use js::typedarray::{ClampedU8, Uint8ClampedArray}; +use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat}; use super::bindings::buffer_source::{HeapBufferSource, create_heap_buffer_source_with_length}; use crate::dom::bindings::buffer_source::create_buffer_source; @@ -183,6 +184,18 @@ impl ImageData { pixels::rgba8_get_rect(unsafe { self.as_slice() }, self.get_size().to_u32(), rect) } + #[allow(unsafe_code)] + pub(crate) fn get_snapshot_rect(&self, rect: Rect) -> Snapshot { + Snapshot::from_vec( + rect.size, + SnapshotPixelFormat::RGBA, + SnapshotAlphaMode::Transparent { + premultiplied: false, + }, + unsafe { self.get_rect(rect).into_owned() }, + ) + } + #[allow(unsafe_code)] pub(crate) fn to_shared_memory(&self) -> IpcSharedMemory { // This is safe because we copy the slice content diff --git a/components/shared/canvas/canvas.rs b/components/shared/canvas/canvas.rs index 181ea3993b5..cc46be5cde2 100644 --- a/components/shared/canvas/canvas.rs +++ b/components/shared/canvas/canvas.rs @@ -6,11 +6,10 @@ use std::default::Default; use std::str::FromStr; use euclid::default::{Point2D, Rect, Size2D, Transform2D}; -use ipc_channel::ipc::{IpcBytesReceiver, IpcSender}; +use ipc_channel::ipc::IpcSender; use malloc_size_of_derive::MallocSizeOf; use pixels::IpcSnapshot; use serde::{Deserialize, Serialize}; -use serde_bytes::ByteBuf; use strum::{Display, EnumString}; use style::color::AbsoluteColor; use style::properties::style_structs::Font as FontStyleStruct; @@ -110,7 +109,7 @@ pub enum Canvas2dMsg { LineTo(Point2D), MoveTo(Point2D), MeasureText(String, IpcSender), - PutImageData(Rect, IpcBytesReceiver), + PutImageData(Rect, IpcSnapshot), QuadraticCurveTo(Point2D, Point2D), Rect(Rect), RestoreContext, @@ -210,7 +209,7 @@ impl RadialGradientStyle { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SurfaceStyle { - pub surface_data: ByteBuf, + pub surface_data: IpcSnapshot, pub surface_size: Size2D, pub repeat_x: bool, pub repeat_y: bool, @@ -219,14 +218,14 @@ pub struct SurfaceStyle { impl SurfaceStyle { pub fn new( - surface_data: Vec, + surface_data: IpcSnapshot, surface_size: Size2D, repeat_x: bool, repeat_y: bool, transform: Transform2D, ) -> Self { Self { - surface_data: ByteBuf::from(surface_data), + surface_data, surface_size, repeat_x, repeat_y, diff --git a/tests/wpt/meta/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html.ini b/tests/wpt/meta/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html.ini index eb34457e264..3b0bcfdb710 100644 --- a/tests/wpt/meta/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/wide-gamut-canvas/canvas-display-p3-pattern-image.html.ini @@ -1,76 +1,34 @@ [canvas-display-p3-pattern-image.html] - [sRGB-FF0000FF.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-FF0000FF.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-FF0000FF.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-FF0000FF.png, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-FF0000CC.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-FF0000CC.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-FF0000CC.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-FF0000CC.png, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-BB0000FF.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-BB0000FF.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-BB0000FF.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-BB0000FF.png, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-BB0000CC.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-BB0000CC.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-BB0000CC.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-BB0000CC.png, Context display-p3, ImageData display-p3] expected: FAIL - [Display-P3-FF0000FF.png, Context srgb, ImageData srgb] - expected: FAIL - [Display-P3-FF0000FF.png, Context srgb, ImageData display-p3] expected: FAIL - [Display-P3-FF0000FF.png, Context display-p3, ImageData srgb] - expected: FAIL - - [Display-P3-FF0000FF.png, Context display-p3, ImageData display-p3] - expected: FAIL - - [Display-P3-FF0000CC.png, Context srgb, ImageData srgb] - expected: FAIL - [Display-P3-FF0000CC.png, Context srgb, ImageData display-p3] expected: FAIL - [Display-P3-FF0000CC.png, Context display-p3, ImageData srgb] - expected: FAIL - - [Display-P3-FF0000CC.png, Context display-p3, ImageData display-p3] - expected: FAIL - [Display-P3-BB0000FF.png, Context srgb, ImageData srgb] expected: FAIL @@ -80,9 +38,6 @@ [Display-P3-BB0000FF.png, Context display-p3, ImageData srgb] expected: FAIL - [Display-P3-BB0000FF.png, Context display-p3, ImageData display-p3] - expected: FAIL - [Display-P3-BB0000CC.png, Context srgb, ImageData srgb] expected: FAIL @@ -92,12 +47,6 @@ [Display-P3-BB0000CC.png, Context display-p3, ImageData srgb] expected: FAIL - [Display-P3-BB0000CC.png, Context display-p3, ImageData display-p3] - expected: FAIL - - [Adobe-RGB-FF0000FF.png, Context srgb, ImageData srgb] - expected: FAIL - [Adobe-RGB-FF0000FF.png, Context srgb, ImageData display-p3] expected: FAIL @@ -107,9 +56,6 @@ [Adobe-RGB-FF0000FF.png, Context display-p3, ImageData display-p3] expected: FAIL - [Adobe-RGB-FF0000CC.png, Context srgb, ImageData srgb] - expected: FAIL - [Adobe-RGB-FF0000CC.png, Context srgb, ImageData display-p3] expected: FAIL @@ -167,78 +113,36 @@ [Generic-CMYK-BE000000.jpg, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-FFFF00000000FFFF.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-FFFF00000000FFFF.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-FFFF00000000FFFF.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-FFFF00000000CCCC.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-FFFF00000000CCCC.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-FFFF00000000CCCC.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-FFFF00000000CCCC.png, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-BBBC00000000FFFF.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-BBBC00000000FFFF.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-BBBC00000000FFFF.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-BBBC00000000FFFF.png, Context display-p3, ImageData display-p3] expected: FAIL - [sRGB-BBBC00000000CCCC.png, Context srgb, ImageData srgb] - expected: FAIL - [sRGB-BBBC00000000CCCC.png, Context srgb, ImageData display-p3] expected: FAIL - [sRGB-BBBC00000000CCCC.png, Context display-p3, ImageData srgb] - expected: FAIL - [sRGB-BBBC00000000CCCC.png, Context display-p3, ImageData display-p3] expected: FAIL - [Display-P3-FFFF00000000FFFF.png, Context srgb, ImageData srgb] - expected: FAIL - [Display-P3-FFFF00000000FFFF.png, Context srgb, ImageData display-p3] expected: FAIL - [Display-P3-FFFF00000000FFFF.png, Context display-p3, ImageData srgb] - expected: FAIL - - [Display-P3-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3] - expected: FAIL - - [Display-P3-FFFF00000000CCCC.png, Context srgb, ImageData srgb] - expected: FAIL - [Display-P3-FFFF00000000CCCC.png, Context srgb, ImageData display-p3] expected: FAIL - [Display-P3-FFFF00000000CCCC.png, Context display-p3, ImageData srgb] - expected: FAIL - - [Display-P3-FFFF00000000CCCC.png, Context display-p3, ImageData display-p3] - expected: FAIL - [Display-P3-BBBC00000000FFFF.png, Context srgb, ImageData srgb] expected: FAIL @@ -248,9 +152,6 @@ [Display-P3-BBBC00000000FFFF.png, Context display-p3, ImageData srgb] expected: FAIL - [Display-P3-BBBC00000000FFFF.png, Context display-p3, ImageData display-p3] - expected: FAIL - [Display-P3-BBBC00000000CCCC.png, Context srgb, ImageData srgb] expected: FAIL @@ -260,12 +161,6 @@ [Display-P3-BBBC00000000CCCC.png, Context display-p3, ImageData srgb] expected: FAIL - [Display-P3-BBBC00000000CCCC.png, Context display-p3, ImageData display-p3] - expected: FAIL - - [Adobe-RGB-FFFF00000000FFFF.png, Context srgb, ImageData srgb] - expected: FAIL - [Adobe-RGB-FFFF00000000FFFF.png, Context srgb, ImageData display-p3] expected: FAIL @@ -275,9 +170,6 @@ [Adobe-RGB-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3] expected: FAIL - [Adobe-RGB-FFFF00000000CCCC.png, Context srgb, ImageData srgb] - expected: FAIL - [Adobe-RGB-FFFF00000000CCCC.png, Context srgb, ImageData display-p3] expected: FAIL