Use IpcSharedMemory for Canvas2dMsg::DrawImage (#30544)

* Use `IpcSharedMemory` for `Canvas2DMsg::DrawImage`

* Fix `Canvas2dMsg::DrawEmptyImage` crashes

* Do not premultiply canvas image data

* Move `image_data` back to its original position
This commit is contained in:
Ennui Langeweile 2023-10-18 10:39:58 -03:00 committed by GitHub
parent 66258bfbbd
commit 2d7dfb06c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 25 deletions

View file

@ -446,11 +446,12 @@ impl<'a> CanvasData<'a> {
pub fn draw_image( pub fn draw_image(
&mut self, &mut self,
image_data: Vec<u8>, image_data: &[u8],
image_size: Size2D<f64>, image_size: Size2D<f64>,
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();
@ -469,6 +470,7 @@ impl<'a> CanvasData<'a> {
source_rect.size, source_rect.size,
dest_rect, dest_rect,
smoothing_enabled, smoothing_enabled,
premultiply,
&draw_options, &draw_options,
); );
}; };
@ -1306,17 +1308,24 @@ pub struct CanvasPaintState<'a> {
/// image_size: The size of the image to be written /// image_size: The size of the image to be written
/// dest_rect: Area of the destination target where the pixels will be copied /// dest_rect: Area of the destination target where the pixels will be copied
/// smoothing_enabled: It determines if smoothing is applied to the image result /// smoothing_enabled: It determines if smoothing is applied to the image result
/// premultiply: Determines whenever the image data should be premultiplied or not
fn write_image( fn write_image(
draw_target: &mut dyn GenericDrawTarget, draw_target: &mut dyn GenericDrawTarget,
image_data: Vec<u8>, mut image_data: Vec<u8>,
image_size: Size2D<f64>, image_size: Size2D<f64>,
dest_rect: Rect<f64>, dest_rect: Rect<f64>,
smoothing_enabled: bool, smoothing_enabled: bool,
premultiply: bool,
draw_options: &DrawOptions, draw_options: &DrawOptions,
) { ) {
if image_data.is_empty() { if image_data.is_empty() {
return; 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(), 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

View file

@ -175,22 +175,27 @@ impl<'a> CanvasPaintThread<'a> {
.canvas(canvas_id) .canvas(canvas_id)
.is_point_in_path(x, y, fill_rule, chan), .is_point_in_path(x, y, fill_rule, chan),
Canvas2dMsg::DrawImage( Canvas2dMsg::DrawImage(
imagedata, ref image_data,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, smoothing_enabled,
) => { ) => self.canvas(canvas_id).draw_image(
let data = imagedata.map_or_else( &*image_data,
|| vec![0; image_size.width as usize * image_size.height as usize * 4], image_size,
|bytes| bytes.into_vec(), dest_rect,
); source_rect,
smoothing_enabled,
true,
),
Canvas2dMsg::DrawEmptyImage(image_size, dest_rect, source_rect) => {
self.canvas(canvas_id).draw_image( self.canvas(canvas_id).draw_image(
data, &vec![0; image_size.area() as usize * 4],
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, false,
false,
) )
}, },
Canvas2dMsg::DrawImageInOther( Canvas2dMsg::DrawImageInOther(
@ -204,11 +209,12 @@ impl<'a> CanvasPaintThread<'a> {
.canvas(canvas_id) .canvas(canvas_id)
.read_pixels(source_rect.to_u64(), image_size.to_u64()); .read_pixels(source_rect.to_u64(), image_size.to_u64());
self.canvas(other_canvas_id).draw_image( self.canvas(other_canvas_id).draw_image(
image_data.into(), &image_data,
source_rect.size, source_rect.size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing, smoothing,
false,
); );
}, },
Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point), Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point),

View file

@ -41,7 +41,8 @@ pub struct CanvasImageData {
pub enum Canvas2dMsg { pub enum Canvas2dMsg {
Arc(Point2D<f32>, f32, f32, f32, bool), Arc(Point2D<f32>, f32, f32, f32, bool),
ArcTo(Point2D<f32>, Point2D<f32>, f32), ArcTo(Point2D<f32>, Point2D<f32>, f32),
DrawImage(Option<ByteBuf>, Size2D<f64>, Rect<f64>, Rect<f64>, bool), DrawImage(IpcSharedMemory, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
DrawEmptyImage(Size2D<f64>, Rect<f64>, Rect<f64>),
DrawImageInOther(CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool), DrawImageInOther(CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
BeginPath, BeginPath,
BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>), BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>),

View file

@ -15,13 +15,12 @@ use canvas_traits::canvas::{
use cssparser::{Color as CSSColor, Parser, ParserInput, RGBA}; use cssparser::{Color as CSSColor, Parser, ParserInput, RGBA};
use euclid::default::{Point2D, Rect, Size2D, Transform2D}; use euclid::default::{Point2D, Rect, Size2D, Transform2D};
use euclid::vec2; use euclid::vec2;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
use net_traits::image_cache::{ImageCache, ImageResponse}; use net_traits::image_cache::{ImageCache, ImageResponse};
use net_traits::request::CorsSettings; use net_traits::request::CorsSettings;
use pixels::PixelFormat; use pixels::PixelFormat;
use profile_traits::ipc as profiled_ipc; use profile_traits::ipc as profiled_ipc;
use script_traits::ScriptMsg; use script_traits::ScriptMsg;
use serde_bytes::ByteBuf;
use servo_url::{ImmutableOrigin, ServoUrl}; use servo_url::{ImmutableOrigin, ServoUrl};
use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
use style::properties::style_structs::Font; use style::properties::style_structs::Font;
@ -260,7 +259,7 @@ impl CanvasState {
&self, &self,
url: ServoUrl, url: ServoUrl,
cors_setting: Option<CorsSettings>, cors_setting: Option<CorsSettings>,
) -> Option<(Vec<u8>, Size2D<u32>)> { ) -> Option<(IpcSharedMemory, Size2D<u32>)> {
let img = match self.request_image_from_cache(url, cors_setting) { let img = match self.request_image_from_cache(url, cors_setting) {
ImageResponse::Loaded(img, _) => img, ImageResponse::Loaded(img, _) => img,
ImageResponse::PlaceholderLoaded(_, _) | ImageResponse::PlaceholderLoaded(_, _) |
@ -272,7 +271,7 @@ impl CanvasState {
let image_size = Size2D::new(img.width, img.height); let image_size = Size2D::new(img.width, img.height);
let image_data = match img.format { let image_data = match img.format {
PixelFormat::BGRA8 => img.bytes.to_vec(), PixelFormat::BGRA8 => img.bytes.clone(),
pixel_format => unimplemented!("unsupported pixel format ({:?})", pixel_format), pixel_format => unimplemented!("unsupported pixel format ({:?})", pixel_format),
}; };
@ -492,12 +491,10 @@ impl CanvasState {
}, },
} }
} else { } else {
self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( self.send_canvas_2d_msg(Canvas2dMsg::DrawEmptyImage(
None,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled,
)); ));
} }
@ -554,12 +551,10 @@ impl CanvasState {
_ => return Err(Error::InvalidState), _ => return Err(Error::InvalidState),
} }
} else { } else {
self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( self.send_canvas_2d_msg(Canvas2dMsg::DrawEmptyImage(
None,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled,
)); ));
} }
@ -582,10 +577,9 @@ impl CanvasState {
dh: Option<f64>, dh: Option<f64>,
) -> ErrorResult { ) -> ErrorResult {
debug!("Fetching image {}.", url); debug!("Fetching image {}.", url);
let (mut image_data, image_size) = self let (image_data, image_size) = self
.fetch_image_data(url, cors_setting) .fetch_image_data(url, cors_setting)
.ok_or(Error::InvalidState)?; .ok_or(Error::InvalidState)?;
pixels::rgba8_premultiply_inplace(&mut image_data);
let image_size = image_size.to_f64(); let image_size = image_size.to_f64();
let dw = dw.unwrap_or(image_size.width); let dw = dw.unwrap_or(image_size.width);
@ -603,7 +597,7 @@ impl CanvasState {
let smoothing_enabled = self.state.borrow().image_smoothing_enabled; let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
self.send_canvas_2d_msg(Canvas2dMsg::DrawImage( self.send_canvas_2d_msg(Canvas2dMsg::DrawImage(
Some(ByteBuf::from(image_data)), image_data,
image_size, image_size,
dest_rect, dest_rect,
source_rect, source_rect,
@ -916,6 +910,7 @@ impl CanvasState {
.and_then(|url| { .and_then(|url| {
self.fetch_image_data(url, cors_setting_for_element(image.upcast())) self.fetch_image_data(url, cors_setting_for_element(image.upcast()))
}) })
.map(|data| (data.0.to_vec(), data.1))
.ok_or(Error::InvalidState)? .ok_or(Error::InvalidState)?
}, },
CanvasImageSource::HTMLCanvasElement(ref canvas) => { CanvasImageSource::HTMLCanvasElement(ref canvas) => {
@ -935,6 +930,7 @@ impl CanvasState {
CanvasImageSource::CSSStyleValue(ref value) => value CanvasImageSource::CSSStyleValue(ref value) => value
.get_url(self.base_url.clone()) .get_url(self.base_url.clone())
.and_then(|url| self.fetch_image_data(url, None)) .and_then(|url| self.fetch_image_data(url, None))
.map(|data| (data.0.to_vec(), data.1))
.ok_or(Error::InvalidState)?, .ok_or(Error::InvalidState)?,
}; };