canvas: Make GetImageData simple and only IPC message to obtain pixels (#38274)

Currently we had `GetImageData` and `SendPixels` to obtain pixels from
script thread. This PR unifies those into single `GetImageData` that
does not need canvas size and has optional rect (for obtaining sub
image).

Testing: Existing WPT tests

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-07-26 04:28:52 +02:00 committed by GitHub
parent 77f85f390e
commit 8b2a5fca54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 26 additions and 63 deletions

View file

@ -723,12 +723,8 @@ impl<DrawTarget: GenericDrawTarget> CanvasData<DrawTarget> {
/// canvas_size: The size of the canvas we're reading from /// canvas_size: The size of the canvas we're reading from
/// read_rect: The area of the canvas we want to read from /// read_rect: The area of the canvas we want to read from
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn read_pixels( pub(crate) fn read_pixels(&mut self, read_rect: Option<Rect<u32>>) -> Snapshot {
&mut self, let canvas_size = self.drawtarget.get_size().cast();
read_rect: Option<Rect<u32>>,
canvas_size: Option<Size2D<u32>>,
) -> Snapshot {
let canvas_size = canvas_size.unwrap_or(self.drawtarget.get_size().cast());
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);

View file

@ -77,15 +77,6 @@ impl CanvasPaintThread {
Ok(CanvasMsg::Recreate(size, canvas_id)) => { Ok(CanvasMsg::Recreate(size, canvas_id)) => {
canvas_paint_thread.canvas(canvas_id).recreate(size); canvas_paint_thread.canvas(canvas_id).recreate(size);
}, },
Ok(CanvasMsg::FromScript(message, canvas_id)) => match message {
FromScriptMsg::SendPixels(chan) => {
chan.send(canvas_paint_thread
.canvas(canvas_id)
.read_pixels(None, None)
.as_ipc()
).unwrap();
},
},
Err(e) => { Err(e) => {
warn!("Error on CanvasPaintThread receive ({})", e); warn!("Error on CanvasPaintThread receive ({})", e);
}, },
@ -247,7 +238,6 @@ impl CanvasPaintThread {
), ),
Canvas2dMsg::DrawImageInOther( Canvas2dMsg::DrawImageInOther(
other_canvas_id, other_canvas_id,
image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing, smoothing,
@ -257,7 +247,7 @@ impl CanvasPaintThread {
) => { ) => {
let 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()));
self.canvas(other_canvas_id).draw_image( self.canvas(other_canvas_id).draw_image(
snapshot, snapshot,
dest_rect, dest_rect,
@ -272,10 +262,8 @@ impl CanvasPaintThread {
let metrics = self.canvas(canvas_id).measure_text(text, text_options); let metrics = self.canvas(canvas_id).measure_text(text, text_options);
sender.send(metrics).unwrap(); sender.send(metrics).unwrap();
}, },
Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender) => { Canvas2dMsg::GetImageData(dest_rect, sender) => {
let snapshot = self let snapshot = self.canvas(canvas_id).read_pixels(dest_rect);
.canvas(canvas_id)
.read_pixels(Some(dest_rect), Some(canvas_size));
sender.send(snapshot.as_ipc()).unwrap(); sender.send(snapshot.as_ipc()).unwrap();
}, },
Canvas2dMsg::PutImageData(rect, snapshot) => { Canvas2dMsg::PutImageData(rect, snapshot) => {
@ -436,13 +424,9 @@ impl Canvas {
} }
} }
fn read_pixels( fn read_pixels(&mut self, read_rect: Option<Rect<u32>>) -> Snapshot {
&mut self,
read_rect: Option<Rect<u32>>,
canvas_size: Option<Size2D<u32>>,
) -> Snapshot {
match self { match self {
Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect, canvas_size), Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect),
} }
} }

View file

@ -443,23 +443,6 @@ impl CanvasState {
} }
} }
pub(crate) fn get_rect(&self, canvas_size: Size2D<u32>, rect: Rect<u32>) -> Vec<u8> {
assert!(self.origin_is_clean());
assert!(Rect::from_size(canvas_size).contains_rect(&rect));
let (sender, receiver) = ipc::channel().unwrap();
self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(rect, canvas_size, sender));
let snapshot = receiver.recv().unwrap().to_owned();
snapshot
.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: false,
}),
Some(SnapshotPixelFormat::RGBA),
)
.0
}
/// ///
/// drawImage coordinates explained /// drawImage coordinates explained
/// ///
@ -701,7 +684,6 @@ impl CanvasState {
OffscreenRenderingContext::Context2d(ref context) => { OffscreenRenderingContext::Context2d(ref context) => {
context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(), self.get_canvas_id(),
image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, smoothing_enabled,
@ -781,7 +763,6 @@ impl CanvasState {
RenderingContext::Context2d(ref context) => { RenderingContext::Context2d(ref context) => {
context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(), self.get_canvas_id(),
image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, smoothing_enabled,
@ -813,7 +794,6 @@ impl CanvasState {
OffscreenRenderingContext::Context2d(ref context) => context OffscreenRenderingContext::Context2d(ref context) => context
.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( .send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther(
self.get_canvas_id(), self.get_canvas_id(),
image_size,
dest_rect, dest_rect,
source_rect, source_rect,
smoothing_enabled, smoothing_enabled,
@ -1755,7 +1735,19 @@ impl CanvasState {
}; };
let data = if self.is_paintable() { let data = if self.is_paintable() {
Some(self.get_rect(canvas_size, read_rect)) let (sender, receiver) = ipc::channel().unwrap();
self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(Some(read_rect), sender));
let snapshot = receiver.recv().unwrap().to_owned();
Some(
snapshot
.to_vec(
Some(SnapshotAlphaMode::Transparent {
premultiplied: false,
}),
Some(SnapshotPixelFormat::RGBA),
)
.0,
)
} else { } else {
None None
}; };

View file

@ -2,12 +2,11 @@
* 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 canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg, FromScriptMsg}; use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Size2D; use euclid::default::Size2D;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::{self, IpcSender};
use pixels::Snapshot; use pixels::Snapshot;
use profile_traits::ipc;
use script_bindings::inheritance::Castable; use script_bindings::inheritance::Castable;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use webrender_api::ImageKey; use webrender_api::ImageKey;
@ -162,10 +161,9 @@ impl CanvasContext for CanvasRenderingContext2D {
return None; return None;
} }
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender), self.get_canvas_id()); self.canvas_state
self.canvas_state.get_ipc_renderer().send(msg).unwrap(); .send_canvas_2d_msg(Canvas2dMsg::GetImageData(None, sender));
Some(receiver.recv().unwrap().to_owned()) Some(receiver.recv().unwrap().to_owned())
} }

View file

@ -449,7 +449,6 @@ pub struct TextOptions {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum CanvasMsg { pub enum CanvasMsg {
Canvas2d(Canvas2dMsg, CanvasId), Canvas2d(Canvas2dMsg, CanvasId),
FromScript(FromScriptMsg, CanvasId),
Recreate(Option<Size2D<u64>>, CanvasId), Recreate(Option<Size2D<u64>>, CanvasId),
Close(CanvasId), Close(CanvasId),
} }
@ -475,7 +474,6 @@ pub enum Canvas2dMsg {
), ),
DrawImageInOther( DrawImageInOther(
CanvasId, CanvasId,
Size2D<u32>,
Rect<f64>, Rect<f64>,
Rect<f64>, Rect<f64>,
bool, bool,
@ -512,7 +510,7 @@ pub enum Canvas2dMsg {
CompositionOptions, CompositionOptions,
Transform2D<f32>, Transform2D<f32>,
), ),
GetImageData(Rect<u32>, Size2D<u32>, IpcSender<IpcSnapshot>), GetImageData(Option<Rect<u32>>, IpcSender<IpcSnapshot>),
MeasureText(String, IpcSender<TextMetrics>, TextOptions), MeasureText(String, IpcSender<TextMetrics>, TextOptions),
PutImageData(Rect<u32>, IpcSnapshot), PutImageData(Rect<u32>, IpcSnapshot),
StrokeRect( StrokeRect(
@ -534,11 +532,6 @@ pub enum Canvas2dMsg {
UpdateImage(IpcSender<()>), UpdateImage(IpcSender<()>),
} }
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum FromScriptMsg {
SendPixels(IpcSender<IpcSnapshot>),
}
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct CanvasGradientStop { pub struct CanvasGradientStop {
pub offset: f64, pub offset: f64,