aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/canvas/canvas_paint_task.rs108
-rw-r--r--components/canvas/lib.rs1
-rw-r--r--components/net/image/base.rs11
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs57
-rw-r--r--components/script/dom/htmlcanvaselement.rs10
-rw-r--r--components/script/dom/imagedata.rs87
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/webidls/CanvasRenderingContext2D.webidl13
-rw-r--r--components/script/dom/webidls/ImageData.webidl23
-rw-r--r--components/util/vec.rs12
10 files changed, 304 insertions, 19 deletions
diff --git a/components/canvas/canvas_paint_task.rs b/components/canvas/canvas_paint_task.rs
index 095a8d76aca..6d7b0c0fb18 100644
--- a/components/canvas/canvas_paint_task.rs
+++ b/components/canvas/canvas_paint_task.rs
@@ -3,13 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use azure::azure_hl::{DrawTarget, SurfaceFormat, BackendType, StrokeOptions, DrawOptions};
-use azure::azure_hl::{ColorPattern, PatternRef, JoinStyle, CapStyle};
+use azure::azure_hl::{ColorPattern, PatternRef, JoinStyle, CapStyle, DrawSurfaceOptions, Filter};
+use azure::AzFloat;
+use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::color;
use util::task::spawn_named;
+use util::vec::byte_swap;
use std::borrow::ToOwned;
+use std::ops::Add;
use std::sync::mpsc::{channel, Sender};
#[derive(Clone)]
@@ -19,6 +23,8 @@ pub enum CanvasMsg {
StrokeRect(Rect<f32>),
Recreate(Size2D<i32>),
SendPixelContents(Sender<Vec<u8>>),
+ GetImageData(Rect<i32>, Size2D<i32>, Sender<Vec<u8>>),
+ PutImageData(Vec<u8>, Rect<i32>, Option<Rect<i32>>, Size2D<i32>),
Close,
}
@@ -51,6 +57,9 @@ impl<'a> CanvasPaintTask<'a> {
CanvasMsg::ClearRect(ref rect) => painter.clear_rect(rect),
CanvasMsg::Recreate(size) => painter.recreate(size),
CanvasMsg::SendPixelContents(chan) => painter.send_pixel_contents(chan),
+ CanvasMsg::GetImageData(dest_rect, canvas_size, chan) => painter.get_image_data(dest_rect, canvas_size, chan),
+ CanvasMsg::PutImageData(imagedata, image_data_rect, dirty_rect, canvas_size)
+ => painter.put_image_data(imagedata, image_data_rect, dirty_rect, canvas_size),
CanvasMsg::Close => break,
}
}
@@ -85,4 +94,101 @@ impl<'a> CanvasPaintTask<'a> {
chan.send(element.to_vec()).unwrap();
})
}
+
+ fn get_image_data(&self, mut dest_rect: Rect<i32>, canvas_size: Size2D<i32>, chan: Sender<Vec<u8>>) {
+ if dest_rect.size.width < 0 {
+ dest_rect.size.width = -dest_rect.size.width;
+ dest_rect.origin.x -= dest_rect.size.width;
+ }
+ if dest_rect.size.height < 0 {
+ dest_rect.size.height = -dest_rect.size.height;
+ dest_rect.origin.y -= dest_rect.size.height;
+ }
+ if dest_rect.size.width == 0 {
+ dest_rect.size.width = 1;
+ }
+ if dest_rect.size.height == 0 {
+ dest_rect.size.height = 1;
+ }
+
+ let canvas_rect = Rect(Point2D(0i32, 0i32), canvas_size);
+ let src_read_rect = canvas_rect.intersection(&dest_rect).unwrap_or(Rect::zero());
+
+ let mut dest_data = Vec::new();
+ //load the canvas data to the source vector
+ if !src_read_rect.is_empty() && canvas_size.width != 0 && canvas_size.height != 0 {
+ let data_surface = self.drawtarget.snapshot().get_data_surface();
+ let mut src_data = Vec::new();
+ data_surface.with_data(|element| {
+ src_data = element.to_vec();
+ });
+
+ let stride = data_surface.stride();
+
+ //start offset of the copyable rectangle
+ let mut src = (src_read_rect.origin.y * stride + src_read_rect.origin.x * 4) as usize;
+ //copy the data to the destination vector
+ for _ in range(0, src_read_rect.size.height) {
+ let row = &src_data[src .. src + (4 * src_read_rect.size.width) as usize];
+ dest_data.push_all(row);
+ src += stride as usize;
+ }
+ }
+ // bgra -> rgba
+ byte_swap(dest_data.as_mut_slice());
+ chan.send(dest_data).unwrap();
+ }
+
+ fn put_image_data(&mut self, mut imagedata: Vec<u8>, image_data_rect: Rect<i32>,
+ dirty_rect: Option<Rect<i32>>, canvas_size: Size2D<i32>) {
+
+ if image_data_rect.size.width <= 0 || image_data_rect.size.height <= 0 {
+ return
+ }
+
+ assert!(image_data_rect.size.width * image_data_rect.size.height * 4 == imagedata.len() as i32);
+ // rgba -> bgra
+ byte_swap(imagedata.as_mut_slice());
+
+ let new_image_data_rect = Rect(Point2D(0i32, 0i32),
+ Size2D(image_data_rect.size.width, image_data_rect.size.height));
+
+ let new_dirty_rect = match dirty_rect {
+ Some(mut dirty_rect) => {
+ if dirty_rect.size.width < 0 {
+ dirty_rect.origin.x = dirty_rect.origin.x + dirty_rect.size.width;
+ dirty_rect.size.width = -dirty_rect.size.width;
+ }
+ if dirty_rect.size.height < 0 {
+ dirty_rect.origin.y = dirty_rect.origin.y + dirty_rect.size.height;
+ dirty_rect.size.height = -dirty_rect.size.height;
+ }
+ new_image_data_rect.intersection(&dirty_rect)
+ },
+ None => Some(new_image_data_rect)
+ };
+
+ if let Some(new_dirty_rect) = new_dirty_rect {
+ let moved_dirty_rect = Rect(new_dirty_rect.origin.add(image_data_rect.origin),
+ new_dirty_rect.size).intersection(&Rect(Point2D(0i32, 0i32),
+ canvas_size)).unwrap_or(Rect::zero());
+ if moved_dirty_rect.is_empty() {
+ return
+ }
+
+ let source_surface = self.drawtarget.create_source_surface_from_data(imagedata.as_slice(),
+ image_data_rect.size, image_data_rect.size.width * 4, SurfaceFormat::B8G8R8A8);
+
+ let draw_surface_options = DrawSurfaceOptions::new(Filter::Linear, true);
+ let draw_options = DrawOptions::new(1.0f64 as AzFloat, 0);
+
+ self.drawtarget.draw_surface(source_surface,
+ Rect(Point2D(moved_dirty_rect.origin.x as AzFloat, moved_dirty_rect.origin.y as AzFloat),
+ Size2D(moved_dirty_rect.size.width as AzFloat, moved_dirty_rect.size.height as AzFloat)),
+ Rect(Point2D((moved_dirty_rect.origin.x - image_data_rect.origin.x) as AzFloat,
+ (moved_dirty_rect.origin.y - image_data_rect.origin.y) as AzFloat),
+ Size2D(moved_dirty_rect.size.width as AzFloat, moved_dirty_rect.size.height as AzFloat)),
+ draw_surface_options, draw_options);
+ }
+ }
}
diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs
index af26c94d3ba..32442c4c30c 100644
--- a/components/canvas/lib.rs
+++ b/components/canvas/lib.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![feature(core)]
+#![feature(collections)]
#![allow(missing_copy_implementations)]
diff --git a/components/net/image/base.rs b/components/net/image/base.rs
index 78e3a0f1159..4acbd35cb75 100644
--- a/components/net/image/base.rs
+++ b/components/net/image/base.rs
@@ -5,6 +5,7 @@
use std::iter::range_step;
use stb_image::image as stb_image;
use png;
+use util::vec::byte_swap;
// FIXME: Images must not be copied every frame. Instead we should atomically
// reference count them.
@@ -18,16 +19,6 @@ pub fn test_image_bin() -> Vec<u8> {
}
// TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this.
-fn byte_swap(data: &mut [u8]) {
- let length = data.len();
- for i in range_step(0, length, 4) {
- let r = data[i + 2];
- data[i + 2] = data[i + 0];
- data[i + 0] = r;
- }
-}
-
-// TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this.
fn byte_swap_and_premultiply(data: &mut [u8]) {
let length = data.len();
for i in range_step(0, length, 4) {
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index 2984a60c270..4b2232ee2f5 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -4,19 +4,24 @@
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
+use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
+use dom::bindings::error::Error::IndexSize;
+use dom::bindings::error::Fallible;
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, JSRef, LayoutJS, Temporary};
use dom::bindings::utils::{Reflector, reflect_dom_object};
-use dom::htmlcanvaselement::HTMLCanvasElement;
+use dom::htmlcanvaselement::{HTMLCanvasElement, HTMLCanvasElementHelpers};
+use dom::imagedata::{ImageData, ImageDataHelpers};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use canvas::canvas_paint_task::{CanvasMsg, CanvasPaintTask};
-use canvas::canvas_paint_task::CanvasMsg::{ClearRect, Close, FillRect, Recreate, StrokeRect};
+use canvas::canvas_paint_task::CanvasMsg::{ClearRect, Close, FillRect, Recreate, StrokeRect, GetImageData, PutImageData};
-use std::sync::mpsc::Sender;
+use std::num::{Float, ToPrimitive};
+use std::sync::mpsc::{channel, Sender};
#[dom_struct]
pub struct CanvasRenderingContext2D {
@@ -75,6 +80,52 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
self.renderer.send(StrokeRect(rect)).unwrap();
}
+
+ fn CreateImageData(self, sw: f64, sh: f64) -> Fallible<Temporary<ImageData>> {
+ if sw == 0.0 || sh == 0.0 {
+ return Err(IndexSize)
+ }
+
+ Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), None))
+ }
+
+ fn CreateImageData_(self, imagedata: JSRef<ImageData>) -> Fallible<Temporary<ImageData>> {
+ Ok(ImageData::new(self.global.root().r(), imagedata.Width(), imagedata.Height(), None))
+ }
+
+ fn GetImageData(self, sx: f64, sy: f64, sw: f64, sh: f64) -> Fallible<Temporary<ImageData>> {
+ if sw == 0.0 || sh == 0.0 {
+ return Err(IndexSize)
+ }
+
+ let (sender, receiver) = channel::<Vec<u8>>();
+ let dest_rect = Rect(Point2D(sx.to_i32().unwrap(), sy.to_i32().unwrap()), Size2D(sw.to_i32().unwrap(), sh.to_i32().unwrap()));
+ let canvas_size = self.canvas.root().r().get_size();
+ self.renderer.send(GetImageData(dest_rect, canvas_size, sender)).unwrap();
+ let data = receiver.recv().unwrap();
+ Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), Some(data)))
+ }
+
+ fn PutImageData(self, imagedata: JSRef<ImageData>, dx: f64, dy: f64) {
+ let data = imagedata.get_data_array(&self.global.root().r());
+ let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()), imagedata.get_size());
+ let dirty_rect = None;
+ let canvas_size = self.canvas.root().r().get_size();
+ self.renderer.send(PutImageData(data, image_data_rect, dirty_rect, canvas_size)).unwrap()
+ }
+
+ fn PutImageData_(self, imagedata: JSRef<ImageData>, dx: f64, dy: f64,
+ dirtyX: f64, dirtyY: f64, dirtyWidth: f64, dirtyHeight: f64) {
+ let data = imagedata.get_data_array(&self.global.root().r());
+ let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()),
+ Size2D(imagedata.Width().to_i32().unwrap(),
+ imagedata.Height().to_i32().unwrap()));
+ let dirty_rect = Some(Rect(Point2D(dirtyX.to_i32().unwrap(), dirtyY.to_i32().unwrap()),
+ Size2D(dirtyWidth.to_i32().unwrap(),
+ dirtyHeight.to_i32().unwrap())));
+ let canvas_size = self.canvas.root().r().get_size();
+ self.renderer.send(PutImageData(data, image_data_rect, dirty_rect, canvas_size)).unwrap()
+ }
}
#[unsafe_destructor]
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index 60ff0f88f82..c3ea7fe6cb1 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -83,6 +83,16 @@ impl LayoutHTMLCanvasElementHelpers for LayoutJS<HTMLCanvasElement> {
}
}
+pub trait HTMLCanvasElementHelpers {
+ fn get_size(&self) -> Size2D<i32>;
+}
+
+impl<'a> HTMLCanvasElementHelpers for JSRef<'a, HTMLCanvasElement> {
+ fn get_size(&self) -> Size2D<i32> {
+ Size2D(self.Width() as i32, self.Height() as i32)
+ }
+}
+
impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
fn Width(self) -> u32 {
self.width.get()
diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs
new file mode 100644
index 00000000000..4c88da4786d
--- /dev/null
+++ b/components/script/dom/imagedata.rs
@@ -0,0 +1,87 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::ImageDataBinding;
+use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::utils::{Reflector, reflect_dom_object};
+use geom::size::Size2D;
+use js::jsapi::{JSContext, JSObject};
+use js::jsfriendapi::bindgen::{JS_NewUint8ClampedArray, JS_GetUint8ClampedArrayData};
+use libc::uint8_t;
+use std::vec::Vec;
+use collections::slice;
+use std::ptr;
+
+#[dom_struct]
+#[allow(raw_pointer_derive)]
+pub struct ImageData {
+ reflector_: Reflector,
+ width: u32,
+ height: u32,
+ data: *mut JSObject,
+}
+
+impl ImageData {
+ #[allow(unsafe_blocks)]
+ fn new_inherited(width: u32, height: u32, data: Option<Vec<u8>>, global: GlobalRef) -> ImageData {
+ unsafe {
+ let cx = global.get_cx();
+ let js_object: *mut JSObject = JS_NewUint8ClampedArray(cx, width * height * 4);
+
+ if let Some(vec) = data {
+ let js_object_data: *mut uint8_t = JS_GetUint8ClampedArrayData(js_object, cx);
+ ptr::copy_nonoverlapping_memory(js_object_data, vec.as_ptr(), vec.len())
+ }
+
+ ImageData {
+ reflector_: Reflector::new(),
+ width: width,
+ height: height,
+ data: js_object,
+ }
+ }
+ }
+
+ pub fn new(global: GlobalRef, width: u32, height: u32, data: Option<Vec<u8>>) -> Temporary<ImageData> {
+ reflect_dom_object(box ImageData::new_inherited(width, height, data, global),
+ global, ImageDataBinding::Wrap)
+ }
+}
+
+pub trait ImageDataHelpers {
+ fn get_data_array(self, global: &GlobalRef) -> Vec<u8>;
+ fn get_size(&self) -> Size2D<i32>;
+}
+
+impl<'a> ImageDataHelpers for JSRef<'a, ImageData> {
+ #[allow(unsafe_blocks)]
+ fn get_data_array(self, global: &GlobalRef) -> Vec<u8> {
+ unsafe {
+ let cx = global.get_cx();
+ let data: *const uint8_t = JS_GetUint8ClampedArrayData(self.Data(cx), cx) as *const uint8_t;
+ let len = self.Width() * self.Height() * 4;
+ slice::from_raw_parts(data, len as uint).to_vec()
+ }
+ }
+
+ fn get_size(&self) -> Size2D<i32> {
+ Size2D(self.Width() as i32, self.Height() as i32)
+ }
+}
+
+impl<'a> ImageDataMethods for JSRef<'a, ImageData> {
+ fn Width(self) -> u32 {
+ self.width
+ }
+
+ fn Height(self) -> u32 {
+ self.height
+ }
+
+ fn Data(self, _: *mut JSContext) -> *mut JSObject {
+ self.data
+ }
+}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index d5446e525b4..1d95cf1d2d5 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -290,6 +290,7 @@ pub mod htmltrackelement;
pub mod htmlulistelement;
pub mod htmlvideoelement;
pub mod htmlunknownelement;
+pub mod imagedata;
pub mod keyboardevent;
pub mod location;
pub mod messageevent;
diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
index 2043347bfd2..6cef3012c25 100644
--- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl
+++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
@@ -96,9 +96,12 @@ interface CanvasRenderingContext2D {
//void removeHitRegion(DOMString id);
// pixel manipulation
- //ImageData createImageData(double sw, double sh);
- //ImageData createImageData(ImageData imagedata);
- //ImageData getImageData(double sx, double sy, double sw, double sh);
- //void putImageData(ImageData imagedata, double dx, double dy);
- //void putImageData(ImageData imagedata, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight);
+ [Throws]
+ ImageData createImageData(double sw, double sh);
+ [Throws]
+ ImageData createImageData(ImageData imagedata);
+ [Throws]
+ ImageData getImageData(double sx, double sy, double sw, double sh);
+ void putImageData(ImageData imagedata, double dx, double dy);
+ void putImageData(ImageData imagedata, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight);
};
diff --git a/components/script/dom/webidls/ImageData.webidl b/components/script/dom/webidls/ImageData.webidl
new file mode 100644
index 00000000000..23f54a61e7d
--- /dev/null
+++ b/components/script/dom/webidls/ImageData.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#imagedata
+ *
+ * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and Opera Software ASA.
+ * You are granted a license to use, reproduce and create derivative works of this document.
+ */
+
+//[Constructor(unsigned long sw, unsigned long sh),
+//Constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh),
+//Exposed=(Window,Worker)]
+interface ImageData {
+ //[Constant]
+ readonly attribute unsigned long width;
+ //[Constant]
+ readonly attribute unsigned long height;
+ //[Constant, StoreInSlot]
+ readonly attribute Uint8ClampedArray data;
+}; \ No newline at end of file
diff --git a/components/util/vec.rs b/components/util/vec.rs
index f612726d129..960e7e8db1a 100644
--- a/components/util/vec.rs
+++ b/components/util/vec.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp::{PartialOrd, PartialEq, Ordering};
+use std::iter::range_step;
#[cfg(test)]
use std::fmt::Debug;
@@ -64,6 +65,17 @@ impl<T:PartialEq + PartialOrd + Ord> Comparator<T,T> for DefaultComparator {
}
}
+
+// TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this.
+pub fn byte_swap(data: &mut [u8]) {
+ let length = data.len();
+ for i in range_step(0, length, 4) {
+ let r = data[i + 2];
+ data[i + 2] = data[i + 0];
+ data[i + 0] = r;
+ }
+}
+
#[cfg(test)]
fn test_find_all_elems<T: PartialEq + PartialOrd + Eq + Ord>(arr: &[T]) {
let mut i = 0;