servo: Merge #2920 - Implement very basic canvas rendering context logic. No visual display present (from Ms2ger:canvas)

Source-Repo: https://github.com/servo/servo
Source-Revision: 440b2a995ffe76aaff8e8080188e5614b830ae38
This commit is contained in:
Aalhad 2014-07-25 11:53:47 -04:00
Родитель 13ca64ac28
Коммит 8981183ec1
8 изменённых файлов: 411 добавлений и 8 удалений

Просмотреть файл

@ -0,0 +1,147 @@
/* 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::CanvasRenderingContext2DBinding;
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::trace::Untraceable;
use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
use azure::azure_hl::{DrawTarget, Color, B8G8R8A8, SkiaBackend, StrokeOptions, DrawOptions};
use azure::azure_hl::ColorPattern;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use std::comm;
use std::task::TaskBuilder;
#[deriving(Encodable)]
pub struct CanvasRenderingContext2D {
reflector_: Reflector,
global: GlobalField,
renderer: Untraceable<Sender<CanvasMsg>>,
}
enum CanvasMsg {
FillRect(Rect<f32>),
ClearRect(Rect<f32>),
StrokeRect(Rect<f32>),
Recreate(Size2D<i32>),
Close,
}
struct CanvasRenderTask {
drawtarget: DrawTarget,
fill_color: ColorPattern,
stroke_color: ColorPattern,
stroke_opts: StrokeOptions,
}
impl CanvasRenderTask {
fn new(size: Size2D<i32>) -> CanvasRenderTask {
CanvasRenderTask {
drawtarget: CanvasRenderTask::create(size),
fill_color: ColorPattern::new(Color::new(0., 0., 0., 1.)),
stroke_color: ColorPattern::new(Color::new(0., 0., 0., 1.)),
stroke_opts: StrokeOptions::new(1.0, 1.0),
}
}
fn start(size: Size2D<i32>) -> Sender<CanvasMsg> {
let (chan, port) = comm::channel::<CanvasMsg>();
let builder = TaskBuilder::new().named("CanvasTask");
builder.spawn(proc() {
let mut renderer = CanvasRenderTask::new(size);
loop {
match port.recv() {
FillRect(ref rect) => renderer.fill_rect(rect),
StrokeRect(ref rect) => renderer.stroke_rect(rect),
ClearRect(ref rect) => renderer.clear_rect(rect),
Recreate(size) => renderer.recreate(size),
Close => break,
}
}
});
chan
}
fn fill_rect(&self, rect: &Rect<f32>) {
let drawopts = DrawOptions::new(1.0, 0);
self.drawtarget.fill_rect(rect, &self.fill_color, Some(&drawopts));
}
fn clear_rect(&self, rect: &Rect<f32>) {
self.drawtarget.clear_rect(rect);
}
fn stroke_rect(&self, rect: &Rect<f32>) {
let drawopts = DrawOptions::new(1.0, 0);
self.drawtarget.stroke_rect(rect, &self.stroke_color, &self.stroke_opts, &drawopts);
}
fn create(size: Size2D<i32>) -> DrawTarget {
DrawTarget::new(SkiaBackend, size, B8G8R8A8)
}
fn recreate(&mut self, size: Size2D<i32>) {
self.drawtarget = CanvasRenderTask::create(size);
}
}
impl CanvasRenderingContext2D {
pub fn new_inherited(global: &GlobalRef, size: Size2D<i32>) -> CanvasRenderingContext2D {
CanvasRenderingContext2D {
reflector_: Reflector::new(),
global: GlobalField::from_rooted(global),
renderer: Untraceable::new(CanvasRenderTask::start(size)),
}
}
pub fn new(global: &GlobalRef, size: Size2D<i32>) -> Temporary<CanvasRenderingContext2D> {
reflect_dom_object(box CanvasRenderingContext2D::new_inherited(global, size),
global, CanvasRenderingContext2DBinding::Wrap)
}
pub fn recreate(&self, size: Size2D<i32>) {
self.renderer.send(Recreate(size));
}
}
pub trait CanvasRenderingContext2DMethods {
fn FillRect(&self, x: f64, y: f64, width: f64, height: f64);
fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64);
fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64);
}
impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> {
fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
self.renderer.send(FillRect(rect));
}
fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
self.renderer.send(ClearRect(rect));
}
fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
self.renderer.send(StrokeRect(rect));
}
}
impl Reflectable for CanvasRenderingContext2D {
fn reflector<'a>(&'a self) -> &'a Reflector {
&self.reflector_
}
}
#[unsafe_destructor]
impl Drop for CanvasRenderingContext2D {
fn drop(&mut self) {
self.renderer.send(Close);
}
}

Просмотреть файл

@ -4,18 +4,35 @@
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector};
use dom::canvasrenderingcontext2d::CanvasRenderingContext2D;
use dom::document::Document;
use dom::element::HTMLCanvasElementTypeId;
use dom::element::{Element, HTMLCanvasElementTypeId, AttributeHandlers};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
use geom::size::Size2D;
use std::cell::Cell;
use std::num;
static DefaultWidth: u32 = 300;
static DefaultHeight: u32 = 150;
#[deriving(Encodable)]
pub struct HTMLCanvasElement {
pub htmlelement: HTMLElement,
context: Traceable<Cell<Option<JS<CanvasRenderingContext2D>>>>,
width: Traceable<Cell<u32>>,
height: Traceable<Cell<u32>>,
}
impl HTMLCanvasElementDerived for EventTarget {
@ -27,7 +44,10 @@ impl HTMLCanvasElementDerived for EventTarget {
impl HTMLCanvasElement {
pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLCanvasElement {
HTMLCanvasElement {
htmlelement: HTMLElement::new_inherited(HTMLCanvasElementTypeId, localName, document)
htmlelement: HTMLElement::new_inherited(HTMLCanvasElementTypeId, localName, document),
context: Traceable::new(Cell::new(None)),
width: Traceable::new(Cell::new(DefaultWidth)),
height: Traceable::new(Cell::new(DefaultHeight)),
}
}
@ -38,6 +58,106 @@ impl HTMLCanvasElement {
}
pub trait HTMLCanvasElementMethods {
fn Width(&self) -> u32;
fn SetWidth(&self, width: u32);
fn Height(&self) -> u32;
fn SetHeight(&self, height: u32);
fn GetContext(&self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>>;
}
impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
fn Width(&self) -> u32 {
self.width.get()
}
fn SetWidth(&self, width: u32) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_uint_attribute("width", width)
}
fn Height(&self) -> u32 {
self.height.get()
}
fn SetHeight(&self, height: u32) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_uint_attribute("height", height)
}
fn GetContext(&self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>> {
if id.as_slice() != "2d" {
return None;
}
if self.context.get().is_none() {
let window = window_from_node(self).root();
let (w, h) = (self.width.get() as i32, self.height.get() as i32);
let context = CanvasRenderingContext2D::new(&Window(*window), Size2D(w, h));
self.context.assign(Some(context));
}
self.context.get().map(|context| Temporary::new(context))
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLCanvasElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods+> {
let element: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(element as &VirtualMethods+)
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value.clone()),
_ => (),
}
let recreate = match name.as_slice() {
"width" => {
self.width.set(DefaultWidth);
true
}
"height" => {
self.height.set(DefaultHeight);
true
}
_ => false,
};
if recreate {
let (w, h) = (self.width.get() as i32, self.height.get() as i32);
match self.context.get() {
Some(ref context) => context.root().recreate(Size2D(w, h)),
None => ()
}
}
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let recreate = match name.as_slice() {
"width" => {
self.width.set(num::from_str_radix(value.as_slice(), 10).unwrap());
true
}
"height" => {
self.height.set(num::from_str_radix(value.as_slice(), 10).unwrap());
true
}
_ => false,
};
if recreate {
let (w, h) = (self.width.get() as i32, self.height.get() as i32);
match self.context.get() {
Some(ref context) => context.root().recreate(Size2D(w, h)),
None => ()
}
}
}
}
impl Reflectable for HTMLCanvasElement {

Просмотреть файл

@ -6,6 +6,7 @@ use dom::attr::{AttrValue, StringAttrValue};
use dom::bindings::codegen::InheritTypes::ElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementCast;
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast;
use dom::bindings::codegen::InheritTypes::HTMLImageElementCast;
@ -13,11 +14,14 @@ use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::js::JSRef;
use dom::element::Element;
use dom::element::{ElementTypeId, HTMLAnchorElementTypeId, HTMLBodyElementTypeId, HTMLImageElementTypeId};
use dom::element::{HTMLIFrameElementTypeId, HTMLObjectElementTypeId, HTMLStyleElementTypeId};
use dom::element::{ElementTypeId, HTMLAnchorElementTypeId};
use dom::element::{HTMLBodyElementTypeId, HTMLCanvasElementTypeId};
use dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
use dom::element::{HTMLObjectElementTypeId, HTMLStyleElementTypeId};
use dom::event::Event;
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlcanvaselement::HTMLCanvasElement;
use dom::htmlelement::HTMLElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement;
@ -111,6 +115,10 @@ pub fn vtable_for<'a>(node: &'a JSRef<Node>) -> &'a VirtualMethods+ {
let element: &JSRef<HTMLBodyElement> = HTMLBodyElementCast::to_ref(node).unwrap();
element as &VirtualMethods+
}
ElementNodeTypeId(HTMLCanvasElementTypeId) => {
let element: &JSRef<HTMLCanvasElement> = HTMLCanvasElementCast::to_ref(node).unwrap();
element as &VirtualMethods+
}
ElementNodeTypeId(HTMLImageElementTypeId) => {
let element: &JSRef<HTMLImageElement> = HTMLImageElementCast::to_ref(node).unwrap();
element as &VirtualMethods+

Просмотреть файл

@ -0,0 +1,104 @@
/* -*- 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/. */
// http://www.whatwg.org/html/#2dcontext
//[Constructor(optional unsigned long width, unsigned long height), Exposed=Window,Worker]
interface CanvasRenderingContext2D {
// back-reference to the canvas
//readonly attribute HTMLCanvasElement canvas;
// canvas dimensions
// attribute unsigned long width;
// attribute unsigned long height;
// for contexts that aren't directly fixed to a specific canvas
//void commit(); // push the image to the output bitmap
// state
//void save(); // push state on state stack
//void restore(); // pop state stack and restore state
// transformations (default transform is the identity matrix)
// attribute SVGMatrix currentTransform;
//void scale(unrestricted double x, unrestricted double y);
//void rotate(unrestricted double angle);
//void translate(unrestricted double x, unrestricted double y);
//void transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
//void setTransform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
//void resetTransform();
// compositing
// attribute unrestricted double globalAlpha; // (default 1.0)
// attribute DOMString globalCompositeOperation; // (default source-over)
// image smoothing
// attribute boolean imageSmoothingEnabled; // (default true)
// colours and styles (see also the CanvasDrawingStyles interface)
// attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
// attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black)
//CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
//CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
//CanvasPattern createPattern(CanvasImageSource image, [TreatNullAs=EmptyString] DOMString repetition);
// shadows
// attribute unrestricted double shadowOffsetX; // (default 0)
// attribute unrestricted double shadowOffsetY; // (default 0)
// attribute unrestricted double shadowBlur; // (default 0)
// attribute DOMString shadowColor; // (default transparent black)
// rects
//void clearRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
//[LenientFloat]
void clearRect(double x, double y, double w, double h);
//void fillRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
//[LenientFloat]
void fillRect(double x, double y, double w, double h);
//void strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
//[LenientFloat]
void strokeRect(double x, double y, double w, double h);
// path API (see also CanvasPathMethods)
//void beginPath();
//void fill(optional CanvasFillRule fillRule = "nonzero");
//void fill(Path2D path, optional CanvasFillRule fillRule = "nonzero");
//void stroke();
//void stroke(Path2D path);
//void drawSystemFocusRing(Element element);
//void drawSystemFocusRing(Path2D path, Element element);
//boolean drawCustomFocusRing(Element element);
//boolean drawCustomFocusRing(Path2D path, Element element);
//void scrollPathIntoView();
//void scrollPathIntoView(Path2D path);
//void clip(optional CanvasFillRule fillRule = "nonzero");
//void clip(Path2D path, optional CanvasFillRule fillRule = "nonzero");
//void resetClip();
//boolean isPointInPath(unrestricted double x, unrestricted double y, optional CanvasFillRule fillRule = "nonzero");
//boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional CanvasFillRule fillRule = "nonzero");
//boolean isPointInStroke(unrestricted double x, unrestricted double y);
//boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
// text (see also the CanvasDrawingStyles interface)
//void fillText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
//void strokeText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
//TextMetrics measureText(DOMString text);
// drawing images
//void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy);
//void drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
//void drawImage(CanvasImageSource image, unrestricted double sx, unrestricted double sy, unrestricted double sw, unrestricted double sh, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
// hit regions
//void addHitRegion(optional HitRegionOptions options);
//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);
};

Просмотреть файл

@ -7,10 +7,13 @@
//typedef (CanvasRenderingContext2D or WebGLRenderingContext) RenderingContext;
interface HTMLCanvasElement : HTMLElement {
// attribute unsigned long width;
// attribute unsigned long height;
[Pure]
attribute unsigned long width;
[Pure]
attribute unsigned long height;
//RenderingContext? getContext(DOMString contextId, any... arguments);
CanvasRenderingContext2D? getContext(DOMString contextId);
//boolean probablySupportsContext(DOMString contextId, any... arguments);
//void setContext(RenderingContext context);

Просмотреть файл

@ -19,6 +19,7 @@
#[phase(plugin, link)]
extern crate log;
extern crate azure;
extern crate debug;
extern crate cssparser;
extern crate collections;
@ -72,6 +73,7 @@ pub mod dom {
pub mod attrlist;
pub mod blob;
pub mod browsercontext;
pub mod canvasrenderingcontext2d;
pub mod characterdata;
pub mod clientrect;
pub mod clientrectlist;

Просмотреть файл

@ -53,6 +53,7 @@ var ecmaGlobals = [
var interfaceNamesInGlobalScope = [
"Attr",
"Blob",
"CanvasRenderingContext2D",
"CharacterData",
"ClientRect", // #2814
"ClientRectList", // #2814

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body>
<canvas id="tutorial" style="border:1px solid #d3d3d3"></canvas>
<script type="text/javascript">
canvas = document.getElementById('tutorial');
canvas.width = 200;
canvas.height = 200;
var ctx = canvas.getContext('2d');
ctx.fillRect(10.0,10.0,100.0,100.0);
ctx.strokeRect(20.0,40.0,80.0,80.0);
ctx.clearRect(10.0,10.0,100.0,100.0);
</script>
</body>
</html>