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>
This commit is contained in:
sagudev 2025-07-04 22:22:20 +02:00 committed by GitHub
parent 8df5e1e74d
commit e1a891ea96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 174 additions and 292 deletions

View file

@ -2,15 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use canvas_traits::canvas::{ use canvas_traits::canvas::{
CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, PathSegment, CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, PathSegment,
}; };
use compositing_traits::SerializableImageData;
use euclid::Angle; use euclid::Angle;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use lyon_geom::Arc; use lyon_geom::Arc;
use pixels::Snapshot;
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
use webrender_api::ImageDescriptor;
use crate::canvas_data::{CanvasPaintState, Filter, PathBuilderRef, TextRun}; use crate::canvas_data::{CanvasPaintState, Filter, PathBuilderRef, TextRun};
@ -61,7 +62,7 @@ pub(crate) trait GenericDrawTarget<B: Backend> {
destination: Point2D<i32>, destination: Point2D<i32>,
); );
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self; fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
fn create_source_surface_from_data(&self, data: &[u8]) -> Option<B::SourceSurface>; fn create_source_surface_from_data(&self, data: Snapshot) -> Option<B::SourceSurface>;
fn draw_surface( fn draw_surface(
&mut self, &mut self,
surface: B::SourceSurface, surface: B::SourceSurface,
@ -113,7 +114,8 @@ pub(crate) trait GenericDrawTarget<B: Backend> {
draw_options: &B::DrawOptions, draw_options: &B::DrawOptions,
); );
fn surface(&self) -> B::SourceSurface; 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. /// A generic Path that abstracts the interface for raqote's PathBuilder/Path.

View file

@ -8,22 +8,22 @@ use std::sync::Arc;
use app_units::Au; use app_units::Au;
use canvas_traits::canvas::*; use canvas_traits::canvas::*;
use compositing_traits::{CrossProcessCompositorApi, SerializableImageData}; use compositing_traits::CrossProcessCompositorApi;
use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D};
use euclid::point2; use euclid::point2;
use fonts::{ use fonts::{
ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore, ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore,
LAST_RESORT_GLYPH_ADVANCE, ShapingFlags, ShapingOptions, LAST_RESORT_GLYPH_ADVANCE, ShapingFlags, ShapingOptions,
}; };
use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use ipc_channel::ipc::IpcSender;
use log::warn; use log::warn;
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat}; use pixels::Snapshot;
use range::Range; use range::Range;
use servo_arc::Arc as ServoArc; use servo_arc::Arc as ServoArc;
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
use style::properties::style_structs::Font as FontStyleStruct; use style::properties::style_structs::Font as FontStyleStruct;
use unicode_script::Script; use unicode_script::Script;
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; use webrender_api::ImageKey;
use crate::backend::{ use crate::backend::{
Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPath, PatternHelpers, 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 size = size.max(MIN_WR_IMAGE_SIZE);
let draw_target = backend.create_drawtarget(size); let draw_target = backend.create_drawtarget(size);
let image_key = compositor_api.generate_image_key_blocking().unwrap(); let image_key = compositor_api.generate_image_key_blocking().unwrap();
let descriptor = ImageDescriptor { let (descriptor, data) = draw_target.image_descriptor_and_serializable_data();
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()));
compositor_api.add_image(image_key, descriptor, data); compositor_api.add_image(image_key, descriptor, data);
CanvasData { CanvasData {
state: backend.new_paint_state(), state: backend.new_paint_state(),
@ -380,31 +372,27 @@ impl<'a, B: Backend> CanvasData<'a, B> {
pub(crate) fn draw_image( pub(crate) fn draw_image(
&mut self, &mut self,
image_data: &[u8], snapshot: Snapshot,
image_size: Size2D<u32>,
dest_rect: Rect<f64>, dest_rect: Rect<f64>,
source_rect: Rect<f64>, source_rect: Rect<f64>,
smoothing_enabled: bool, smoothing_enabled: bool,
premultiply: bool,
) { ) {
// We round up the floating pixel values to draw the pixels // We round up the floating pixel values to draw the pixels
let source_rect = source_rect.ceil(); let source_rect = source_rect.ceil();
// It discards the extra pixels (if any) that won't be painted // 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) { let snapshot = if Rect::from_size(snapshot.size().to_f64()).contains_rect(&source_rect) {
pixels::rgba8_get_rect(image_data, image_size, source_rect.to_u32()).into() snapshot.get_rect(source_rect.to_u32())
} else { } else {
image_data.into() snapshot
}; };
let draw_options = self.state.draw_options.clone(); let draw_options = self.state.draw_options.clone();
let writer = |draw_target: &mut B::DrawTarget| { let writer = |draw_target: &mut B::DrawTarget| {
write_image::<B>( write_image::<B>(
draw_target, draw_target,
image_data, snapshot,
source_rect.size,
dest_rect, dest_rect,
smoothing_enabled, smoothing_enabled,
premultiply,
&draw_options, &draw_options,
); );
}; };
@ -1107,29 +1095,18 @@ impl<'a, B: Backend> CanvasData<'a, B> {
/// Update image in WebRender /// Update image in WebRender
pub(crate) fn update_image_rendering(&mut self) { pub(crate) fn update_image_rendering(&mut self) {
let descriptor = ImageDescriptor { let (descriptor, data) = self.drawtarget.image_descriptor_and_serializable_data();
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(),
));
self.compositor_api self.compositor_api
.update_image(self.image_key, descriptor, data); .update_image(self.image_key, descriptor, data);
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata
pub(crate) fn put_image_data(&mut self, mut imagedata: Vec<u8>, rect: Rect<u32>) { pub(crate) fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect<u32>) {
assert_eq!(imagedata.len() % 4, 0); assert_eq!(rect.size, snapshot.size());
assert_eq!(rect.size.area() as usize, imagedata.len() / 4);
pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata);
let source_surface = self let source_surface = self
.drawtarget .drawtarget
.create_source_surface_from_data(&imagedata) .create_source_surface_from_data(snapshot)
.unwrap(); .unwrap();
self.drawtarget.copy_surface( self.drawtarget.copy_surface(
source_surface, source_surface,
@ -1217,29 +1194,19 @@ impl<'a, B: Backend> CanvasData<'a, B> {
) -> Snapshot { ) -> Snapshot {
let canvas_size = canvas_size.unwrap_or(self.drawtarget.get_size().cast()); 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); let canvas_rect = Rect::from_size(canvas_size);
if canvas_rect if canvas_rect
.intersection(&read_rect) .intersection(&read_rect)
.is_none_or(|rect| rect.is_empty()) .is_none_or(|rect| rect.is_empty())
{ {
vec![] Snapshot::empty()
} else { } else {
pixels::rgba8_get_rect(self.drawtarget.bytes().as_ref(), canvas_size, read_rect) self.drawtarget.snapshot().get_rect(read_rect)
.to_vec()
} }
} else { } else {
self.drawtarget.bytes().into_owned() self.drawtarget.snapshot()
}; }
Snapshot::from_vec(
canvas_size,
SnapshotPixelFormat::BGRA,
SnapshotAlphaMode::Transparent {
premultiplied: true,
},
data,
)
} }
} }
@ -1279,22 +1246,16 @@ pub(crate) struct CanvasPaintState<'a, B: Backend> {
/// premultiply: Determines whenever the image data should be premultiplied or not /// premultiply: Determines whenever the image data should be premultiplied or not
fn write_image<B: Backend>( fn write_image<B: Backend>(
draw_target: &mut B::DrawTarget, draw_target: &mut B::DrawTarget,
mut image_data: Vec<u8>, snapshot: Snapshot,
image_size: Size2D<f64>,
dest_rect: Rect<f64>, dest_rect: Rect<f64>,
smoothing_enabled: bool, smoothing_enabled: bool,
premultiply: bool,
draw_options: &B::DrawOptions, draw_options: &B::DrawOptions,
) { ) {
if image_data.is_empty() { if snapshot.size().is_empty() {
return; return;
} }
if premultiply { let image_rect = Rect::new(Point2D::zero(), snapshot.size().cast());
pixels::rgba8_premultiply_inplace(&mut image_data);
}
let image_rect = Rect::new(Point2D::zero(), image_size);
// From spec https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage // 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 // When scaling up, if the imageSmoothingEnabled attribute is set to true, the user agent should attempt
@ -1307,7 +1268,7 @@ fn write_image<B: Backend>(
}; };
let source_surface = draw_target let source_surface = draw_target
.create_source_surface_from_data(&image_data) .create_source_surface_from_data(snapshot)
.unwrap(); .unwrap();
draw_target.draw_surface(source_surface, dest_rect, image_rect, filter, draw_options); draw_target.draw_surface(source_surface, dest_rect, image_rect, filter, draw_options);

View file

@ -17,7 +17,7 @@ use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use log::warn; use log::warn;
use net_traits::ResourceThreads; use net_traits::ResourceThreads;
use pixels::{Snapshot, SnapshotPixelFormat}; use pixels::Snapshot;
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
use style::properties::style_structs::Font as FontStyleStruct; use style::properties::style_structs::Font as FontStyleStruct;
use webrender_api::ImageKey; use webrender_api::ImageKey;
@ -175,29 +175,16 @@ impl<'a> CanvasPaintThread<'a> {
.canvas(canvas_id) .canvas(canvas_id)
.is_point_in_path_(&path[..], x, y, fill_rule, chan), .is_point_in_path_(&path[..], x, y, fill_rule, chan),
Canvas2dMsg::DrawImage(snapshot, dest_rect, source_rect, smoothing_enabled) => { 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( self.canvas(canvas_id).draw_image(
data, snapshot.to_owned(),
size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, 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( Canvas2dMsg::DrawImageInOther(
other_canvas_id, other_canvas_id,
image_size, image_size,
@ -205,18 +192,14 @@ impl<'a> CanvasPaintThread<'a> {
source_rect, source_rect,
smoothing, smoothing,
) => { ) => {
let mut snapshot = self let snapshot = self
.canvas(canvas_id) .canvas(canvas_id)
.read_pixels(Some(source_rect.to_u32()), Some(image_size)); .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( self.canvas(other_canvas_id).draw_image(
data, snapshot,
source_rect.size.to_u32(),
dest_rect, dest_rect,
source_rect, source_rect,
smoothing, smoothing,
alpha_mode.alpha().needs_alpha_multiplication(),
); );
}, },
Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point), 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)); .read_pixels(Some(dest_rect), Some(canvas_size));
sender.send(snapshot.as_ipc()).unwrap(); sender.send(snapshot.as_ipc()).unwrap();
}, },
Canvas2dMsg::PutImageData(rect, receiver) => { Canvas2dMsg::PutImageData(rect, snapshot) => {
self.canvas(canvas_id) self.canvas(canvas_id)
.put_image_data(receiver.recv().unwrap(), rect); .put_image_data(snapshot.to_owned(), rect);
}, },
Canvas2dMsg::SetShadowOffsetX(value) => { Canvas2dMsg::SetShadowOffsetX(value) => {
self.canvas(canvas_id).set_shadow_offset_x(value) self.canvas(canvas_id).set_shadow_offset_x(value)
@ -403,22 +386,15 @@ impl Canvas<'_> {
fn draw_image( fn draw_image(
&mut self, &mut self,
data: &[u8], snapshot: Snapshot,
size: Size2D<u32>,
dest_rect: Rect<f64>, dest_rect: Rect<f64>,
source_rect: Rect<f64>, source_rect: Rect<f64>,
smoothing_enabled: bool, smoothing_enabled: bool,
premultiply: bool,
) { ) {
match self { match self {
Canvas::Raqote(canvas_data) => canvas_data.draw_image( Canvas::Raqote(canvas_data) => {
data, canvas_data.draw_image(snapshot, dest_rect, source_rect, smoothing_enabled)
size, },
dest_rect,
source_rect,
smoothing_enabled,
premultiply,
),
} }
} }
@ -618,9 +594,9 @@ impl Canvas<'_> {
} }
} }
fn put_image_data(&mut self, unwrap: Vec<u8>, rect: Rect<u32>) { fn put_image_data(&mut self, snapshot: Snapshot, rect: Rect<u32>) {
match self { match self {
Canvas::Raqote(canvas_data) => canvas_data.put_image_data(unwrap, rect), Canvas::Raqote(canvas_data) => canvas_data.put_image_data(snapshot, rect),
} }
} }

View file

@ -2,19 +2,22 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use canvas_traits::canvas::*; use canvas_traits::canvas::*;
use compositing_traits::SerializableImageData;
use cssparser::color::clamp_unit_f32; use cssparser::color::clamp_unit_f32;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use font_kit::font::Font; use font_kit::font::Font;
use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods}; use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods};
use ipc_channel::ipc::IpcSharedMemory;
use log::warn; use log::warn;
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
use range::Range; use range::Range;
use raqote::PathOp; use raqote::PathOp;
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
use crate::backend::{ use crate::backend::{
Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPath, PatternHelpers, Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPath, PatternHelpers,
@ -34,7 +37,7 @@ thread_local! {
pub(crate) struct RaqoteBackend; pub(crate) struct RaqoteBackend;
impl Backend for RaqoteBackend { impl Backend for RaqoteBackend {
type Pattern<'a> = Pattern<'a>; type Pattern<'a> = Pattern;
type StrokeOptions = raqote::StrokeStyle; type StrokeOptions = raqote::StrokeStyle;
type Color = raqote::SolidSource; type Color = raqote::SolidSource;
type DrawOptions = raqote::DrawOptions; type DrawOptions = raqote::DrawOptions;
@ -112,12 +115,12 @@ impl Backend for RaqoteBackend {
} }
#[derive(Clone)] #[derive(Clone)]
pub enum Pattern<'a> { pub enum Pattern {
// argb // argb
Color(u8, u8, u8, u8), Color(u8, u8, u8, u8),
LinearGradient(LinearGradientPattern), LinearGradient(LinearGradientPattern),
RadialGradient(RadialGradientPattern), RadialGradient(RadialGradientPattern),
Surface(SurfacePattern<'a>), Surface(SurfacePattern),
} }
#[derive(Clone)] #[derive(Clone)]
@ -165,17 +168,17 @@ impl RadialGradientPattern {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SurfacePattern<'a> { pub struct SurfacePattern {
image: raqote::Image<'a>, image: Snapshot,
filter: raqote::FilterMode, filter: raqote::FilterMode,
extend: raqote::ExtendMode, extend: raqote::ExtendMode,
repeat: Repetition, repeat: Repetition,
transform: Transform2D<f32>, transform: Transform2D<f32>,
} }
impl<'a> SurfacePattern<'a> { impl SurfacePattern {
fn new( fn new(
image: raqote::Image<'a>, image: Snapshot,
filter: raqote::FilterMode, filter: raqote::FilterMode,
repeat: Repetition, repeat: Repetition,
transform: Transform2D<f32>, transform: Transform2D<f32>,
@ -195,7 +198,7 @@ impl<'a> SurfacePattern<'a> {
} }
} }
pub fn size(&self) -> Size2D<f32> { pub fn size(&self) -> Size2D<f32> {
Size2D::new(self.image.width as f32, self.image.height as f32) self.image.size().cast()
} }
pub fn repetition(&self) -> &Repetition { pub fn repetition(&self) -> &Repetition {
&self.repeat &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 { match pattern {
Pattern::Color(a, r, g, b) => raqote::Source::Solid( Pattern::Color(a, r, g, b) => raqote::Source::Solid(
raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), 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, pattern.radius2,
raqote::Spread::Pad, raqote::Spread::Pad,
), ),
Pattern::Surface(pattern) => raqote::Source::Image( Pattern::Surface(pattern) => {
pattern.image, #[allow(unsafe_code)]
pattern.extend, let data = unsafe {
pattern.filter, let data = pattern.image.as_raw_bytes();
pattern.transform, std::slice::from_raw_parts(
), data.as_ptr() as *const u32,
data.len() / std::mem::size_of::<u32>(),
)
};
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 { fn is_zero_size_gradient(&self) -> bool {
match self { match self {
Pattern::RadialGradient(pattern) => { Pattern::RadialGradient(pattern) => {
@ -371,9 +388,17 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
} }
fn create_source_surface_from_data( fn create_source_surface_from_data(
&self, &self,
data: &[u8], data: Snapshot,
) -> Option<<RaqoteBackend as Backend>::SourceSurface> { ) -> Option<<RaqoteBackend as Backend>::SourceSurface> {
Some(data.to_vec()) Some(
data.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: true,
}),
Some(SnapshotPixelFormat::BGRA),
)
.0,
)
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn draw_surface( fn draw_surface(
@ -384,23 +409,20 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
filter: Filter, filter: Filter,
draw_options: &<RaqoteBackend as Backend>::DrawOptions, draw_options: &<RaqoteBackend as Backend>::DrawOptions,
) { ) {
let surface_data = surface; let image = Snapshot::from_vec(
let image = raqote::Image { source.size.cast(),
width: source.size.width as i32, SnapshotPixelFormat::BGRA,
height: source.size.height as i32, SnapshotAlphaMode::Transparent {
data: unsafe { premultiplied: true,
std::slice::from_raw_parts(
surface_data.as_ptr() as *const u32,
surface_data.len() / std::mem::size_of::<u32>(),
)
}, },
}; surface,
);
let transform = let transform =
raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32) raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32)
.then_scale( .then_scale(
image.width as f32 / dest.size.width as f32, source.size.width as f32 / dest.size.width as f32,
image.height as f32 / dest.size.height as f32, source.size.height as f32 / dest.size.height as f32,
); );
let pattern = Pattern::Surface(SurfacePattern::new( let pattern = Pattern::Surface(SurfacePattern::new(
@ -434,7 +456,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
fn fill( fn fill(
&mut self, &mut self,
path: &<RaqoteBackend as Backend>::Path, path: &<RaqoteBackend as Backend>::Path,
pattern: &<RaqoteBackend as Backend>::Pattern<'_>, pattern: &Pattern,
draw_options: &<RaqoteBackend as Backend>::DrawOptions, draw_options: &<RaqoteBackend as Backend>::DrawOptions,
) { ) {
let path = path.into(); let path = path.into();
@ -470,7 +492,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
&mut self, &mut self,
text_runs: Vec<TextRun>, text_runs: Vec<TextRun>,
start: Point2D<f32>, start: Point2D<f32>,
pattern: &<RaqoteBackend as Backend>::Pattern<'_>, pattern: &Pattern,
draw_options: &<RaqoteBackend as Backend>::DrawOptions, draw_options: &<RaqoteBackend as Backend>::DrawOptions,
) { ) {
let mut advance = 0.; let mut advance = 0.;
@ -558,12 +580,12 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
self.set_transform(matrix); self.set_transform(matrix);
} }
fn surface(&self) -> <RaqoteBackend as Backend>::SourceSurface { fn surface(&self) -> <RaqoteBackend as Backend>::SourceSurface {
self.bytes().to_vec() self.get_data_u8().to_vec()
} }
fn stroke( fn stroke(
&mut self, &mut self,
path: &<RaqoteBackend as Backend>::Path, path: &<RaqoteBackend as Backend>::Path,
pattern: &Pattern<'_>, pattern: &Pattern,
stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, stroke_options: &<RaqoteBackend as Backend>::StrokeOptions,
draw_options: &<RaqoteBackend as Backend>::DrawOptions, draw_options: &<RaqoteBackend as Backend>::DrawOptions,
) { ) {
@ -586,12 +608,33 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
self.stroke(&pb.finish(), &source(pattern), stroke_options, draw_options); self.stroke(&pb.finish(), &source(pattern), stroke_options, draw_options);
} }
#[allow(unsafe_code)]
fn bytes(&self) -> Cow<[u8]> { fn image_descriptor_and_serializable_data(
let v = self.get_data(); &self,
Cow::Borrowed(unsafe { ) -> (
std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) 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> { pub trait ToRaqotePattern {
fn to_raqote_pattern(self) -> Option<Pattern<'a>>; fn to_raqote_pattern(self) -> Option<Pattern>;
} }
pub trait ToRaqoteGradientStop { pub trait ToRaqoteGradientStop {
@ -750,9 +793,9 @@ impl ToRaqoteGradientStop for CanvasGradientStop {
} }
} }
impl ToRaqotePattern<'_> for FillOrStrokeStyle { impl ToRaqotePattern for FillOrStrokeStyle {
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn to_raqote_pattern(self) -> Option<Pattern<'static>> { fn to_raqote_pattern(self) -> Option<Pattern> {
use canvas_traits::canvas::FillOrStrokeStyle::*; use canvas_traits::canvas::FillOrStrokeStyle::*;
match self { match self {
@ -785,19 +828,17 @@ impl ToRaqotePattern<'_> for FillOrStrokeStyle {
stops, stops,
))) )))
}, },
Surface(ref style) => { Surface(style) => {
let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y); let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y);
let data = &style.surface_data[..]; let mut snapshot = style.surface_data.to_owned();
snapshot.transform(
let image = raqote::Image { SnapshotAlphaMode::Transparent {
width: style.surface_size.width as i32, premultiplied: true,
height: style.surface_size.height as i32,
data: unsafe {
std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4)
}, },
}; SnapshotPixelFormat::BGRA,
);
Some(Pattern::Surface(SurfacePattern::new( Some(Pattern::Surface(SurfacePattern::new(
image, snapshot,
raqote::FilterMode::Nearest, raqote::FilterMode::Nearest,
repeat, repeat,
style.transform, style.transform,

View file

@ -4,7 +4,7 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use euclid::default::Size2D; use euclid::default::{Rect, Size2D};
use image::codecs::jpeg::JpegEncoder; use image::codecs::jpeg::JpegEncoder;
use image::codecs::png::PngEncoder; use image::codecs::png::PngEncoder;
use image::codecs::webp::WebPEncoder; use image::codecs::webp::WebPEncoder;
@ -13,7 +13,7 @@ use ipc_channel::ipc::IpcSharedMemory;
use malloc_size_of_derive::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize}; 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)] #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum SnapshotPixelFormat { pub enum SnapshotPixelFormat {
@ -180,6 +180,11 @@ impl Snapshot<SnapshotData> {
} }
} }
pub fn get_rect(&self, rect: Rect<u32>) -> 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 // TODO: https://github.com/servo/servo/issues/36594
/* /*
/// # Safety /// # Safety

View file

@ -1186,14 +1186,7 @@ impl CanvasState {
let size = snapshot.size(); let size = snapshot.size();
Ok(Some(CanvasPattern::new( Ok(Some(CanvasPattern::new(
global, global,
snapshot snapshot,
.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: true,
}),
Some(SnapshotPixelFormat::BGRA),
)
.0, // TODO: send snapshot
size.cast(), size.cast(),
rep, rep,
self.is_origin_clean(image), self.is_origin_clean(image),
@ -1703,10 +1696,8 @@ impl CanvasState {
}; };
// Step 7. // Step 7.
let (sender, receiver) = ipc::bytes_channel().unwrap(); let snapshot = imagedata.get_snapshot_rect(Rect::new(src_rect.origin, dst_rect.size));
let pixels = unsafe { &imagedata.get_rect(Rect::new(src_rect.origin, dst_rect.size)) }; self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(dst_rect, snapshot.as_ipc()));
self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(dst_rect, receiver));
sender.send(pixels).unwrap();
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage

View file

@ -5,6 +5,7 @@
use canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle}; use canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::{Size2D, Transform2D}; use euclid::default::{Size2D, Transform2D};
use pixels::{IpcSnapshot, Snapshot};
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasPatternMethods; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasPatternMethods;
@ -21,7 +22,8 @@ use crate::script_runtime::CanGc;
#[dom_struct] #[dom_struct]
pub(crate) struct CanvasPattern { pub(crate) struct CanvasPattern {
reflector_: Reflector, reflector_: Reflector,
surface_data: Vec<u8>, #[no_trace]
surface_data: IpcSnapshot,
#[no_trace] #[no_trace]
surface_size: Size2D<u32>, surface_size: Size2D<u32>,
repeat_x: bool, repeat_x: bool,
@ -33,7 +35,7 @@ pub(crate) struct CanvasPattern {
impl CanvasPattern { impl CanvasPattern {
fn new_inherited( fn new_inherited(
surface_data: Vec<u8>, surface_data: Snapshot,
surface_size: Size2D<u32>, surface_size: Size2D<u32>,
repeat: RepetitionStyle, repeat: RepetitionStyle,
origin_clean: bool, origin_clean: bool,
@ -47,7 +49,7 @@ impl CanvasPattern {
CanvasPattern { CanvasPattern {
reflector_: Reflector::new(), reflector_: Reflector::new(),
surface_data, surface_data: surface_data.as_ipc(),
surface_size, surface_size,
repeat_x: x, repeat_x: x,
repeat_y: y, repeat_y: y,
@ -57,7 +59,7 @@ impl CanvasPattern {
} }
pub(crate) fn new( pub(crate) fn new(
global: &GlobalScope, global: &GlobalScope,
surface_data: Vec<u8>, surface_data: Snapshot,
surface_size: Size2D<u32>, surface_size: Size2D<u32>,
repeat: RepetitionStyle, repeat: RepetitionStyle,
origin_clean: bool, origin_clean: bool,

View file

@ -12,6 +12,7 @@ use js::gc::CustomAutoRooterGuard;
use js::jsapi::JSObject; use js::jsapi::JSObject;
use js::rust::HandleObject; use js::rust::HandleObject;
use js::typedarray::{ClampedU8, Uint8ClampedArray}; use js::typedarray::{ClampedU8, Uint8ClampedArray};
use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
use super::bindings::buffer_source::{HeapBufferSource, create_heap_buffer_source_with_length}; use super::bindings::buffer_source::{HeapBufferSource, create_heap_buffer_source_with_length};
use crate::dom::bindings::buffer_source::create_buffer_source; 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) 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<u32>) -> Snapshot {
Snapshot::from_vec(
rect.size,
SnapshotPixelFormat::RGBA,
SnapshotAlphaMode::Transparent {
premultiplied: false,
},
unsafe { self.get_rect(rect).into_owned() },
)
}
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn to_shared_memory(&self) -> IpcSharedMemory { pub(crate) fn to_shared_memory(&self) -> IpcSharedMemory {
// This is safe because we copy the slice content // This is safe because we copy the slice content

View file

@ -6,11 +6,10 @@ use std::default::Default;
use std::str::FromStr; use std::str::FromStr;
use euclid::default::{Point2D, Rect, Size2D, Transform2D}; 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 malloc_size_of_derive::MallocSizeOf;
use pixels::IpcSnapshot; use pixels::IpcSnapshot;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use style::color::AbsoluteColor; use style::color::AbsoluteColor;
use style::properties::style_structs::Font as FontStyleStruct; use style::properties::style_structs::Font as FontStyleStruct;
@ -110,7 +109,7 @@ pub enum Canvas2dMsg {
LineTo(Point2D<f32>), LineTo(Point2D<f32>),
MoveTo(Point2D<f32>), MoveTo(Point2D<f32>),
MeasureText(String, IpcSender<TextMetrics>), MeasureText(String, IpcSender<TextMetrics>),
PutImageData(Rect<u32>, IpcBytesReceiver), PutImageData(Rect<u32>, IpcSnapshot),
QuadraticCurveTo(Point2D<f32>, Point2D<f32>), QuadraticCurveTo(Point2D<f32>, Point2D<f32>),
Rect(Rect<f32>), Rect(Rect<f32>),
RestoreContext, RestoreContext,
@ -210,7 +209,7 @@ impl RadialGradientStyle {
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SurfaceStyle { pub struct SurfaceStyle {
pub surface_data: ByteBuf, pub surface_data: IpcSnapshot,
pub surface_size: Size2D<u32>, pub surface_size: Size2D<u32>,
pub repeat_x: bool, pub repeat_x: bool,
pub repeat_y: bool, pub repeat_y: bool,
@ -219,14 +218,14 @@ pub struct SurfaceStyle {
impl SurfaceStyle { impl SurfaceStyle {
pub fn new( pub fn new(
surface_data: Vec<u8>, surface_data: IpcSnapshot,
surface_size: Size2D<u32>, surface_size: Size2D<u32>,
repeat_x: bool, repeat_x: bool,
repeat_y: bool, repeat_y: bool,
transform: Transform2D<f32>, transform: Transform2D<f32>,
) -> Self { ) -> Self {
Self { Self {
surface_data: ByteBuf::from(surface_data), surface_data,
surface_size, surface_size,
repeat_x, repeat_x,
repeat_y, repeat_y,

View file

@ -1,76 +1,34 @@
[canvas-display-p3-pattern-image.html] [canvas-display-p3-pattern-image.html]
[sRGB-FF0000FF.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-FF0000FF.png, Context srgb, ImageData display-p3] [sRGB-FF0000FF.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FF0000FF.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-FF0000FF.png, Context display-p3, ImageData display-p3] [sRGB-FF0000FF.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FF0000CC.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-FF0000CC.png, Context srgb, ImageData display-p3] [sRGB-FF0000CC.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FF0000CC.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-FF0000CC.png, Context display-p3, ImageData display-p3] [sRGB-FF0000CC.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BB0000FF.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-BB0000FF.png, Context srgb, ImageData display-p3] [sRGB-BB0000FF.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BB0000FF.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-BB0000FF.png, Context display-p3, ImageData display-p3] [sRGB-BB0000FF.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BB0000CC.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-BB0000CC.png, Context srgb, ImageData display-p3] [sRGB-BB0000CC.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BB0000CC.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-BB0000CC.png, Context display-p3, ImageData display-p3] [sRGB-BB0000CC.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[Display-P3-FF0000FF.png, Context srgb, ImageData srgb]
expected: FAIL
[Display-P3-FF0000FF.png, Context srgb, ImageData display-p3] [Display-P3-FF0000FF.png, Context srgb, ImageData display-p3]
expected: FAIL 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] [Display-P3-FF0000CC.png, Context srgb, ImageData display-p3]
expected: FAIL 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] [Display-P3-BB0000FF.png, Context srgb, ImageData srgb]
expected: FAIL expected: FAIL
@ -80,9 +38,6 @@
[Display-P3-BB0000FF.png, Context display-p3, ImageData srgb] [Display-P3-BB0000FF.png, Context display-p3, ImageData srgb]
expected: FAIL expected: FAIL
[Display-P3-BB0000FF.png, Context display-p3, ImageData display-p3]
expected: FAIL
[Display-P3-BB0000CC.png, Context srgb, ImageData srgb] [Display-P3-BB0000CC.png, Context srgb, ImageData srgb]
expected: FAIL expected: FAIL
@ -92,12 +47,6 @@
[Display-P3-BB0000CC.png, Context display-p3, ImageData srgb] [Display-P3-BB0000CC.png, Context display-p3, ImageData srgb]
expected: FAIL 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] [Adobe-RGB-FF0000FF.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
@ -107,9 +56,6 @@
[Adobe-RGB-FF0000FF.png, Context display-p3, ImageData display-p3] [Adobe-RGB-FF0000FF.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[Adobe-RGB-FF0000CC.png, Context srgb, ImageData srgb]
expected: FAIL
[Adobe-RGB-FF0000CC.png, Context srgb, ImageData display-p3] [Adobe-RGB-FF0000CC.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
@ -167,78 +113,36 @@
[Generic-CMYK-BE000000.jpg, Context display-p3, ImageData display-p3] [Generic-CMYK-BE000000.jpg, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FFFF00000000FFFF.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-FFFF00000000FFFF.png, Context srgb, ImageData display-p3] [sRGB-FFFF00000000FFFF.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FFFF00000000FFFF.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3] [sRGB-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FFFF00000000CCCC.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-FFFF00000000CCCC.png, Context srgb, ImageData display-p3] [sRGB-FFFF00000000CCCC.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-FFFF00000000CCCC.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-FFFF00000000CCCC.png, Context display-p3, ImageData display-p3] [sRGB-FFFF00000000CCCC.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BBBC00000000FFFF.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-BBBC00000000FFFF.png, Context srgb, ImageData display-p3] [sRGB-BBBC00000000FFFF.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BBBC00000000FFFF.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-BBBC00000000FFFF.png, Context display-p3, ImageData display-p3] [sRGB-BBBC00000000FFFF.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BBBC00000000CCCC.png, Context srgb, ImageData srgb]
expected: FAIL
[sRGB-BBBC00000000CCCC.png, Context srgb, ImageData display-p3] [sRGB-BBBC00000000CCCC.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
[sRGB-BBBC00000000CCCC.png, Context display-p3, ImageData srgb]
expected: FAIL
[sRGB-BBBC00000000CCCC.png, Context display-p3, ImageData display-p3] [sRGB-BBBC00000000CCCC.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[Display-P3-FFFF00000000FFFF.png, Context srgb, ImageData srgb]
expected: FAIL
[Display-P3-FFFF00000000FFFF.png, Context srgb, ImageData display-p3] [Display-P3-FFFF00000000FFFF.png, Context srgb, ImageData display-p3]
expected: FAIL 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] [Display-P3-FFFF00000000CCCC.png, Context srgb, ImageData display-p3]
expected: FAIL 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] [Display-P3-BBBC00000000FFFF.png, Context srgb, ImageData srgb]
expected: FAIL expected: FAIL
@ -248,9 +152,6 @@
[Display-P3-BBBC00000000FFFF.png, Context display-p3, ImageData srgb] [Display-P3-BBBC00000000FFFF.png, Context display-p3, ImageData srgb]
expected: FAIL expected: FAIL
[Display-P3-BBBC00000000FFFF.png, Context display-p3, ImageData display-p3]
expected: FAIL
[Display-P3-BBBC00000000CCCC.png, Context srgb, ImageData srgb] [Display-P3-BBBC00000000CCCC.png, Context srgb, ImageData srgb]
expected: FAIL expected: FAIL
@ -260,12 +161,6 @@
[Display-P3-BBBC00000000CCCC.png, Context display-p3, ImageData srgb] [Display-P3-BBBC00000000CCCC.png, Context display-p3, ImageData srgb]
expected: FAIL 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] [Adobe-RGB-FFFF00000000FFFF.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL
@ -275,9 +170,6 @@
[Adobe-RGB-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3] [Adobe-RGB-FFFF00000000FFFF.png, Context display-p3, ImageData display-p3]
expected: FAIL expected: FAIL
[Adobe-RGB-FFFF00000000CCCC.png, Context srgb, ImageData srgb]
expected: FAIL
[Adobe-RGB-FFFF00000000CCCC.png, Context srgb, ImageData display-p3] [Adobe-RGB-FFFF00000000CCCC.png, Context srgb, ImageData display-p3]
expected: FAIL expected: FAIL