Fix getImageData with sizes < 1 pixel

This commit is contained in:
David Zbarsky 2015-08-04 17:14:15 -04:00
parent 0a8ea98183
commit 2f47bdff4b
4 changed files with 27 additions and 34 deletions

View file

@ -29,8 +29,7 @@ impl<'a> CanvasPaintTask<'a> {
/// It reads image data from the canvas /// It reads image data from the canvas
/// 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
fn read_pixels(&self, read_rect: Rect<f64>, canvas_size: Size2D<f64>) -> Vec<u8>{ fn read_pixels(&self, read_rect: Rect<i32>, canvas_size: Size2D<f64>) -> Vec<u8>{
let read_rect = read_rect.to_i32();
let canvas_size = canvas_size.to_i32(); let canvas_size = canvas_size.to_i32();
let canvas_rect = Rect::new(Point2D::new(0i32, 0i32), canvas_size); let canvas_rect = Rect::new(Point2D::new(0i32, 0i32), canvas_size);
let src_read_rect = canvas_rect.intersection(&read_rect).unwrap_or(Rect::zero()); let src_read_rect = canvas_rect.intersection(&read_rect).unwrap_or(Rect::zero());
@ -396,7 +395,7 @@ impl<'a> CanvasPaintTask<'a> {
smoothing_enabled: bool) { smoothing_enabled: bool) {
// Reads pixels from source image // Reads pixels from source image
// In this case source and target are the same canvas // In this case source and target are the same canvas
let image_data = self.read_pixels(source_rect, image_size); let image_data = self.read_pixels(source_rect.to_i32(), image_size);
if self.need_to_draw_shadow() { if self.need_to_draw_shadow() {
let rect = Rect::new(Point2D::new(dest_rect.origin.x as f32, dest_rect.origin.y as f32), let rect = Rect::new(Point2D::new(dest_rect.origin.x as f32, dest_rect.origin.y as f32),
@ -567,24 +566,9 @@ impl<'a> CanvasPaintTask<'a> {
} }
fn get_image_data(&self, fn get_image_data(&self,
mut dest_rect: Rect<f64>, dest_rect: Rect<i32>,
canvas_size: Size2D<f64>, canvas_size: Size2D<f64>,
chan: IpcSender<Vec<u8>>) { chan: IpcSender<Vec<u8>>) {
if dest_rect.size.width < 0.0 {
dest_rect.size.width = -dest_rect.size.width;
dest_rect.origin.x -= dest_rect.size.width;
}
if dest_rect.size.height < 0.0 {
dest_rect.size.height = -dest_rect.size.height;
dest_rect.origin.y -= dest_rect.size.height;
}
if dest_rect.size.width == 0.0 {
dest_rect.size.width = 1.0;
}
if dest_rect.size.height == 0.0 {
dest_rect.size.height = 1.0;
}
let mut dest_data = self.read_pixels(dest_rect, canvas_size); let mut dest_data = self.read_pixels(dest_rect, canvas_size);
// bgra -> rgba // bgra -> rgba

View file

@ -88,7 +88,7 @@ pub enum Canvas2dMsg {
ClosePath, ClosePath,
Fill, Fill,
FillRect(Rect<f32>), FillRect(Rect<f32>),
GetImageData(Rect<f64>, Size2D<f64>, IpcSender<Vec<u8>>), GetImageData(Rect<i32>, Size2D<f64>, IpcSender<Vec<u8>>),
LineTo(Point2D<f32>), LineTo(Point2D<f32>),
MoveTo(Point2D<f32>), MoveTo(Point2D<f32>),
PutImageData(Vec<u8>, Rect<f64>, Option<Rect<f64>>), PutImageData(Vec<u8>, Rect<f64>, Option<Rect<f64>>),

View file

@ -32,6 +32,7 @@ use euclid::size::Size2D;
use canvas_traits::{CanvasMsg, Canvas2dMsg, CanvasCommonMsg}; use canvas_traits::{CanvasMsg, Canvas2dMsg, CanvasCommonMsg};
use canvas_traits::{FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle, RepetitionStyle}; use canvas_traits::{FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle, RepetitionStyle};
use canvas_traits::{LineCapStyle, LineJoinStyle, CompositionOrBlending}; use canvas_traits::{LineCapStyle, LineJoinStyle, CompositionOrBlending};
use canvas::canvas_paint_task::RectToi32;
use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::Msg as ConstellationMsg;
use net_traits::image_cache_task::{ImageCacheChan, ImageResponse}; use net_traits::image_cache_task::{ImageCacheChan, ImageResponse};
@ -298,7 +299,7 @@ impl CanvasRenderingContext2D {
let renderer = context.r().get_ipc_renderer(); let renderer = context.r().get_ipc_renderer();
let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap(); let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap();
// Reads pixels from source image // Reads pixels from source image
renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(source_rect, renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(source_rect.to_i32(),
image_size, image_size,
sender))).unwrap(); sender))).unwrap();
let imagedata = receiver.recv().unwrap(); let imagedata = receiver.recv().unwrap();
@ -377,7 +378,8 @@ impl CanvasRenderingContext2D {
let renderer = context.r().get_ipc_renderer(); let renderer = context.r().get_ipc_renderer();
let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap(); let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap();
// Reads pixels from source canvas // Reads pixels from source canvas
renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(source_rect, image_size, sender))).unwrap(); renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(source_rect.to_i32(),
image_size, sender))).unwrap();
return Some((receiver.recv().unwrap(), image_size)); return Some((receiver.recv().unwrap(), image_size));
} }
@ -884,18 +886,30 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D {
sy: Finite<f64>, sy: Finite<f64>,
sw: Finite<f64>, sw: Finite<f64>,
sh: Finite<f64>) -> Fallible<Root<ImageData>> { sh: Finite<f64>) -> Fallible<Root<ImageData>> {
let sx = *sx; let mut sx = *sx;
let sy = *sy; let mut sy = *sy;
let sw = *sw; let mut sw = *sw;
let sh = *sh; let mut sh = *sh;
if sw == 0.0 || sh == 0.0 { if sw == 0.0 || sh == 0.0 {
return Err(IndexSize) return Err(IndexSize)
} }
if sw < 0.0 {
sw = -sw;
sx -= sw;
}
if sh < 0.0 {
sh = -sh;
sy -= sh;
}
let sh = cmp::max(1, sh.to_u32().unwrap());
let sw = cmp::max(1, sw.to_u32().unwrap());
let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap(); let (sender, receiver) = ipc::channel::<Vec<u8>>().unwrap();
let dest_rect = Rect::new(Point2D::new(sx as f64, sy as f64), let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
Size2D::new(sw as f64, sh as f64)); Size2D::new(sw as i32, sh as i32));
let canvas_size = self.canvas.root().r().get_size(); let canvas_size = self.canvas.root().r().get_size();
let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64); let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
self.ipc_renderer self.ipc_renderer
@ -913,7 +927,7 @@ impl<'a> CanvasRenderingContext2DMethods for &'a CanvasRenderingContext2D {
chunk[2] = (chunk[2] as f32 / alpha) as u8; chunk[2] = (chunk[2] as f32 / alpha) as u8;
} }
Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), Some(data))) Ok(ImageData::new(self.global.root().r(), sw, sh, Some(data)))
} }
// https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata

View file

@ -1,5 +0,0 @@
[2d.imageData.get.tiny.html]
type: testharness
[getImageData() works for sizes smaller than one pixel]
expected: FAIL