First-pass for adding HTML5's Canvas.
This is not the final version yet (among other issues, the location is up for debate) but I would like to give people a chance to comment on it at this stage.
Review at http://gwt-code-reviews.appspot.com/1082801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9304 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/canvas/Canvas.gwt.xml b/user/src/com/google/gwt/canvas/Canvas.gwt.xml
new file mode 100644
index 0000000..3b1da45
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/Canvas.gwt.xml
@@ -0,0 +1,20 @@
+<!--
+ Copyright 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ use this file except in compliance with the License. You may obtain a copy of
+ the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.canvas.dom.DOM"/>
+ <source path="client"/>
+</module>
diff --git a/user/src/com/google/gwt/canvas/client/Canvas.java b/user/src/com/google/gwt/canvas/client/Canvas.java
new file mode 100644
index 0000000..22903f0
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/client/Canvas.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.client;
+
+import com.google.gwt.canvas.dom.client.Context;
+import com.google.gwt.canvas.dom.client.Context2d;
+import com.google.gwt.dom.client.CanvasElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.user.client.IsSupported;
+import com.google.gwt.user.client.ui.FocusWidget;
+
+/**
+ * A widget representing a <canvas> element.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ *
+ * This widget may not be supported on all browsers, see {@link IsSupported}
+ */
+public class Canvas extends FocusWidget implements IsSupported {
+ /**
+ * Runtime check for whether the canvas element is supported in this browser.
+ * See {@link IsSupported}
+ *
+ * @return whether the canvas element is supported
+ */
+ public static final native boolean isSupported() /*-{
+ return !!$doc.createElement('canvas').getContext;
+ }-*/;
+
+ /**
+ * Creates a Canvas.
+ */
+ public Canvas() {
+ setElement(Document.get().createCanvasElement());
+ }
+
+ /**
+ * Returns the attached Canvas Element.
+ *
+ * @return the Canvas Element
+ */
+ public CanvasElement getCanvasElement() {
+ return this.getElement().cast();
+ }
+
+ /**
+ * Gets the rendering context that may be used to draw on this canvas.
+ *
+ * @return the canvas rendering context
+ */
+ public Context getContext(String contextId) {
+ return getCanvasElement().getContext(contextId);
+ }
+
+ /**
+ * Returns a 2D rendering context.
+ *
+ * This is a convenience method, @see {@link #getContext(String)}
+ *
+ * @return a 2D canvas rendering context
+ */
+ public Context2d getContext2d() {
+ return getCanvasElement().getContext2d();
+ }
+
+ /**
+ * Gets the height of the internal canvas coordinate space.
+ *
+ * @return the height, in pixels
+ */
+ public int getCoordinateSpaceHeight() {
+ return getCanvasElement().getHeight();
+ }
+
+ /**
+ * Gets the width of the internal canvas coordinate space.
+ *
+ * @return the width, in pixels
+ */
+ public int getCoordinateSpaceWidth() {
+ return getCanvasElement().getWidth();
+ }
+
+ /**
+ * Sets the height of the internal canvas coordinate space.
+ *
+ * @param height the height, in pixels
+ */
+ public void setCoordinateSpaceHeight(int height) {
+ getCanvasElement().setHeight(height);
+ }
+
+ /**
+ * Sets the width of the internal canvas coordinate space.
+ *
+ * @param width the width, in pixels
+ */
+ public void setCoordinateSpaceWidth(int width) {
+ getCanvasElement().setWidth(width);
+ }
+
+ /**
+ * Returns a data URL for the current content of the canvas element.
+ *
+ * @return a data URL for the current content of this element.
+ */
+ public String toDataUrl() {
+ return getCanvasElement().toDataUrl();
+ }
+
+ /**
+ * Returns a data URL for the current content of the canvas element, with a specified type.
+ *
+ * @param type the type of the data url, e.g., image/jpeg or image/png.
+ * @return a data URL for the current content of this element with the specified type.
+ */
+ public String toDataUrl(String type) {
+ return getCanvasElement().toDataUrl(type);
+ }
+}
diff --git a/user/src/com/google/gwt/canvas/dom/DOM.gwt.xml b/user/src/com/google/gwt/canvas/dom/DOM.gwt.xml
new file mode 100644
index 0000000..eee5d89
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/DOM.gwt.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ use this file except in compliance with the License. You may obtain a copy of
+ the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+-->
+<module>
+ <source path="client"/>
+ <inherits name="com.google.gwt.dom.DOM"/>
+</module>
diff --git a/user/src/com/google/gwt/canvas/dom/client/CanvasGradient.java b/user/src/com/google/gwt/canvas/dom/client/CanvasGradient.java
new file mode 100644
index 0000000..c8d2341
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CanvasGradient.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * Gradient object used with {@link Context2d}.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ *
+ * This class serves as a reference to a gradient created using {@link Context2d#createPattern}
+ *
+ * @see <a href="http://www.w3.org/TR/2dcontext/#canvasgradient">CanvasGradient</a>
+ */
+public class CanvasGradient extends FillStrokeStyle {
+
+ protected CanvasGradient() {
+ }
+
+ /**
+ * Adds a new color stop to the gradient.
+ *
+ * @param offset value between 0 and 1 for where the color stop is located.
+ * @param color color at the stop.
+ */
+ public final native void addColorStop(float offset, String color) /*-{
+ this.addColorStop(offset, color);
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/CanvasPattern.java b/user/src/com/google/gwt/canvas/dom/client/CanvasPattern.java
new file mode 100644
index 0000000..c0e3f17
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CanvasPattern.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * Pattern object used with {@link Context2d}.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ *
+ * Note that this class has no methods. It simply serves as a reference to a
+ * pattern created using {@link Context2d#createPattern}.
+ *
+ * @see <a href="http://www.w3.org/TR/2dcontext/#canvaspattern">CanvasPattern</a>
+ */
+public class CanvasPattern extends FillStrokeStyle {
+
+ protected CanvasPattern() {
+ }
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/CanvasPixelArray.java b/user/src/com/google/gwt/canvas/dom/client/CanvasPixelArray.java
new file mode 100644
index 0000000..d792752
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CanvasPixelArray.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Array-like object holding the actual image data for an ImageData object. For each pixel,
+ * this object contains a red, green, blue and alpha value between 0 and 255 (in this order).
+ * Note that we use ints here to represent the data to avoid complexities stemming from
+ * bytes being signed in Java.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class CanvasPixelArray extends JavaScriptObject {
+
+ protected CanvasPixelArray() {
+ }
+
+ /**
+ * Returns the data value at index i.
+ */
+ public final native int get(int i) /*-{
+ return this[i] || 0;
+ }-*/;
+
+ /**
+ * Returns the length of the array.
+ */
+ public final native int getLength() /*-{
+ return this.length;
+ }-*/;
+
+ /**
+ * Sets the data value at position i to the given value.
+ *
+ * Most browsers will clamp this value to the range 0...255 that is not enforced in this
+ * implementation.
+ *
+ * @param i index to set.
+ * @param value value to set (use values from 0 to 255)
+ */
+ public final native void set(int i, int value) /*-{
+ // FF3.0 doesn't clamp the range. We don't manually clamp it to maximize performance.
+ this[i] = value;
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/Context.java b/user/src/com/google/gwt/canvas/dom/client/Context.java
new file mode 100644
index 0000000..0259462
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/Context.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * Rendering Context interface used to draw on a {@link CanvasElement}.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public interface Context {
+
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/Context2d.java b/user/src/com/google/gwt/canvas/dom/client/Context2d.java
new file mode 100644
index 0000000..f373e17
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/Context2d.java
@@ -0,0 +1,962 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.CanvasElement;
+import com.google.gwt.dom.client.ImageElement;
+
+/**
+ * Rendering interface used to draw on a {@link CanvasElement}.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ *
+ * @see <a href="http://www.w3.org/TR/2dcontext/#canvasrenderingcontext2d">W3C
+ * HTML 5 Specification</a>
+ */
+public class Context2d extends JavaScriptObject implements Context {
+ /**
+ * Specifies the context id property used in creating a Context.
+ */
+ public static final String CONTEXT_ID = "2d";
+
+ /**
+ * Enum for the repetition values.
+ *
+ * @see Context2d#createPattern(ImageElement, Repetition)
+ * @see Context2d#createPattern(CanvasElement, Repetition)
+ */
+ public enum Repetition {
+ REPEAT("repeat"), REPEAT_X("repeat-x"), REPEAT_Y("repeat-y"), NO_REPEAT("no-repeat");
+
+ private final String value;
+
+ private Repetition(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Enum for line-cap style.
+ *
+ * @see Context2d#setLineCap(LineCap)
+ */
+ public enum LineCap {
+ BUTT("butt"), ROUND("round"), SQUARE("square");
+
+ private final String value;
+
+ private LineCap(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Enum for line-join style.
+ *
+ * @see Context2d#setLineJoin(LineJoin)
+ */
+ public enum LineJoin {
+ ROUND("round"), BEVEL("bevel"), MITER("miter");
+
+ private final String value;
+
+ private LineJoin(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Enum for composite style.
+ *
+ * @see Context2d#setGlobalCompositeOperation(Composite)
+ */
+ public enum Composite {
+ /**
+ * A atop B. Display the source image wherever both images are opaque. Display
+ * the destination image wherever the destination image is opaque but the
+ * source image is transparent. Display transparency elsewhere.
+ */
+ SOURCE_ATOP("source-atop"),
+
+ /**
+ * A in B. Display the source image wherever both the source image and
+ * destination image are opaque. Display transparency elsewhere.
+ */
+ SOURCE_IN("source-in"),
+
+ /**
+ * A out B. Display the source image wherever the source image is opaque and
+ * the destination image is transparent. Display transparency elsewhere.
+ */
+ SOURCE_OUT("source-out"),
+
+ /**
+ * A over B. Display the source image wherever the source image is opaque.
+ * Display the destination image elsewhere.
+ */
+ SOURCE_OVER("source-over"),
+
+ /**
+ * B atop A. Same as source-atop but using the destination image instead of
+ * the source image and vice versa.
+ */
+ DESTINATION_ATOP("destination-atop"),
+
+ /**
+ * B in A. Same as source-in but using the destination image instead of the
+ * source image and vice versa.
+ */
+ DESTINATION_IN("destination-in"),
+
+ /**
+ * B out A. Same as source-out but using the destination image instead of the
+ * source image and vice versa.
+ */
+ DESTINATION_OUT("destination-out"),
+
+ /**
+ * B over A. Same as source-over but using the destination image instead of
+ * the source image and vice versa.
+ */
+ DESTINATION_OVER("destination-over"),
+
+ /**
+ * A plus B. Display the sum of the source image and destination image, with
+ * color values approaching 1 as a limit.
+ */
+ LIGHTER("lighter"),
+
+ /**
+ * A (B is ignored). Display the source image instead of the destination
+ * image.
+ */
+ COPY("copy"),
+
+ /**
+ * A xor B. Exclusive OR of the source image and destination image.
+ */
+ XOR("xor");
+
+ private final String value;
+
+ private Composite(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Enum for text align style.
+ *
+ * @see Context2d#setTextAlign(TextAlign)
+ */
+ public enum TextAlign {
+ START("start"), END("end"), LEFT("left"), RIGHT("right"), CENTER("center");
+
+ private final String value;
+
+ private TextAlign(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Enum for text baseline style.
+ *
+ * @see Context2d#setTextBaseline(TextBaseline)
+ */
+ public enum TextBaseline {
+ TOP("top"), HANGING("hanging"), MIDDLE("middle"), ALPHABETIC("alphabetic"),
+ IDEOGRAPHIC("ideographic"), BOTTOM("bottom");
+
+ private final String value;
+
+ private TextBaseline(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+ protected Context2d() {
+ }
+
+ /**
+ * Draws an arc.
+ */
+ public final native void arc(float x, float y, float radius, float startAngle,
+ float endAngle) /*-{
+ this.arc(x, y, radius, startAngle, endAngle);
+ }-*/;
+
+ /**
+ * Draws an arc.
+ */
+ public final native void arc(float x, float y, float radius, float startAngle, float endAngle,
+ boolean anticlockwise) /*-{
+ this.arc(x, y, radius, startAngle, endAngle, anticlockwise);
+ }-*/;
+
+ /**
+ * Draws an arc.
+ */
+ public final native void arcTo(float x1, float y1, float x2, float y2, float radius) /*-{
+ this.arcTo(x1, y1, x2, y2, radius);
+ }-*/;
+
+ /**
+ * Begins a new path.
+ */
+ public final native void beginPath() /*-{
+ this.beginPath();
+ }-*/;
+
+ /**
+ * Draws a Bezier curve.
+ */
+ public final native void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x,
+ float y) /*-{
+ this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+ }-*/;
+
+ /**
+ * Clears a rectangle.
+ */
+ public final native void clearRect(float x, float y, float w, float h) /*-{
+ this.clearRect(x, y, w, h);
+ }-*/;
+
+ /**
+ * Creates a new clipping region from the current path.
+ */
+ public final native void clip() /*-{
+ this.clip();
+ }-*/;
+
+ /**
+ * Closes the current path.
+ */
+ public final native void closePath() /*-{
+ this.closePath();
+ }-*/;
+
+ /**
+ * Creates and image data object of the given size.
+ */
+ public final native ImageData createImageData(int w, int h) /*-{
+ return this.createImageData(w, h);
+ }-*/;
+
+ /**
+ * Creates an image data object of the same size as the given object.
+ */
+ public final native ImageData createImageData(ImageData imagedata) /*-{
+ return this.createImageData(imagedata);
+ }-*/;
+
+ /**
+ * Creates a linear gradient.
+ */
+ public final native CanvasGradient createLinearGradient(float x0, float y0, float x1,
+ float y1) /*-{
+ return this.createLinearGradient(x0, y0, x1, y1);
+ }-*/;
+
+ /**
+ * Creates a pattern from another canvas.
+ */
+ public final CanvasPattern createPattern(CanvasElement image, Repetition repetition) {
+ return createPattern(image, repetition.getValue());
+ }
+
+ /**
+ * Creates a pattern from another canvas.
+ */
+ public final native CanvasPattern createPattern(CanvasElement image, String repetition) /*-{
+ return this.createPattern(image, repetition);
+ }-*/;
+
+ /**
+ * Creates a pattern from an image.
+ */
+ public final CanvasPattern createPattern(ImageElement image, Repetition repetition) {
+ return createPattern(image, repetition.getValue());
+ }
+
+ /**
+ * Creates a pattern from an image.
+ */
+ public final native CanvasPattern createPattern(ImageElement image, String repetition) /*-{
+ return this.createPattern(image, repetition);
+ }-*/;
+
+ /**
+ * Creates a radial gradient.
+ *
+ * Due to needing to wrap the contained gradient when in devmode, we create an array containing
+ * the CanvasGradient in devmode (when isScript == false.) @see FillStrokeStyle
+ */
+ public final native CanvasGradient createRadialGradient(float x0, float y0, float r0, float x1,
+ float y1, float r1) /*-{
+ return this.createRadialGradient(x0, y0, r0, x1, y1, r1);
+ }-*/;
+
+ /**
+ * Draws an image.
+ */
+ public final native void drawImage(CanvasElement image, float dx, float dy) /*-{
+ this.drawImage(image, dx, dy);
+ }-*/;
+
+ /**
+ * Draws a scaled image.
+ */
+ public final native void drawImage(CanvasElement image, float dx, float dy, float dw,
+ float dh) /*-{
+ this.drawImage(image, dx, dy, dw, dh);
+ }-*/;
+
+ /**
+ * Draws a scaled subset of an image.
+ */
+ public final native void drawImage(CanvasElement image, float sx, float sy, float sw, float sh,
+ float dx, float dy, float dw, float dh) /*-{
+ this.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
+ }-*/;
+
+ /**
+ * Draws an image.
+ */
+ public final native void drawImage(ImageElement image, float dx, float dy) /*-{
+ this.drawImage(image, dx, dy);
+ }-*/;
+
+ /**
+ * Draws a scaled image.
+ */
+ public final native void drawImage(ImageElement image, float dx, float dy, float dw,
+ float dh) /*-{
+ this.drawImage(image, dx, dy, dw, dh);
+ }-*/;
+
+ /**
+ * Draws a scaled subset of an image.
+ */
+ public final native void drawImage(ImageElement image, float sx, float sy, float sw, float sh,
+ float dx, float dy, float dw, float dh) /*-{
+ this.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
+ }-*/;
+
+ /**
+ * Fills the current path.
+ */
+ public final native void fill() /*-{
+ this.fill();
+ }-*/;
+
+ /**
+ * Fills a rectangle.
+ */
+ public final native void fillRect(float x, float y, float w, float h) /*-{
+ this.fillRect(x, y, w, h);
+ }-*/;
+
+ /**
+ * Draw text.
+ */
+ public final native void fillText(String text, float x, float y) /*-{
+ // FF3.0 does not implement this method.
+ if (this.fillText) {
+ this.fillText(text, x, y);
+ }
+ }-*/;
+
+ /**
+ * Draw text squeezed into the given max width.
+ */
+ public final native void fillText(String text, float x, float y, float maxWidth) /*-{
+ this.fillText(text, x, y, maxWidth);
+ }-*/;
+
+ /**
+ * Gets this context's canvas.
+ */
+ public final native CanvasElement getCanvas() /*-{
+ return this.canvas;
+ }-*/;
+
+ /**
+ * Returns the context's fillStyle.
+ *
+ * Due to needing to wrap CssColor when in devmode, we use different methods
+ * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+ */
+ public final FillStrokeStyle getFillStyle() {
+ if (GWT.isScript()) {
+ return getFillStyleWeb();
+ } else {
+ return getFillStyleDev();
+ }
+ }
+
+ /**
+ * Gets this context's font.
+ */
+ public final native String getFont() /*-{
+ return this.font;
+ }-*/;
+
+ /**
+ * Gets the global alpha value.
+ */
+ public final native float getGlobalAlpha() /*-{
+ return this.globalAlpha;
+ }-*/;
+
+ /**
+ * Gets the global composite operation.
+ */
+ public final native String getGlobalCompositeOperation() /*-{
+ return this.globalCompositeOperation;
+ }-*/;
+
+ /**
+ * Returns an image data object for the screen area denoted by
+ * sx, sy, sw and sh.
+ */
+ public final native ImageData getImageData(float sx, float sy, float sw, float sh) /*-{
+ return this.getImageData(sx, sy, sw, sh);
+ }-*/;
+
+ /**
+ * Gets the current line-cap style.
+ */
+ public final native String getLineCap() /*-{
+ return this.lineCap;
+ }-*/;
+
+ /**
+ * Gets the current line-join style.
+ */
+ public final native String getLineJoin() /*-{
+ return this.lineJoin;
+ }-*/;
+
+ /**
+ * Gets the current line-width.
+ */
+ public final native float getLineWidth() /*-{
+ return this.lineWidth;
+ }-*/;
+
+ /**
+ * Gets the current miter-limit.
+ */
+ public final native float getMiterLimit() /*-{
+ return this.miterLimit;
+ }-*/;
+
+ /**
+ * Gets the current shadow-blur.
+ */
+ public final native float getShadowBlur() /*-{
+ return this.shadowBlur;
+ }-*/;
+
+ /**
+ * Gets the current shadow color.
+ */
+ public final native String getShadowColor() /*-{
+ return this.shadowColor;
+ }-*/;
+
+ /**
+ * Gets the current x-shadow-offset.
+ */
+ public final native float getShadowOffsetX() /*-{
+ return this.shadowOffsetX;
+ }-*/;
+
+ /**
+ * Gets the current y-shadow-offset.
+ */
+ public final native float getShadowOffsetY() /*-{
+ return this.shadowOffsetY;
+ }-*/;
+
+ /**
+ * Returns the context's strokeStyle.
+ *
+ * Due to needing to wrap CssColor when in devmode, we use different methods
+ * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+ */
+ public final FillStrokeStyle getStrokeStyle() {
+ if (GWT.isScript()) {
+ return getStrokeStyleWeb();
+ } else {
+ return getStrokeStyleDev();
+ }
+ }
+
+ /**
+ * Gets the current text align.
+ */
+ public final native String getTextAlign() /*-{
+ return this.textAlign;
+ }-*/;
+
+ /**
+ * Gets the current text baseline.
+ */
+ public final native String getTextBaseline() /*-{
+ return this.textBaseline;
+ }-*/;
+
+ /**
+ * Returns true if the given point is in the current path.
+ *
+ * @param x x coordinate of the point to test.
+ * @param y y coordinate of the point to test.
+ * @return true if the given point is in the current path.
+ */
+ public final native boolean isPointInPath(float x, float y) /*-{
+ return this.isPointInPath(x, y);
+ }-*/;
+
+ /**
+ * Draws a line.
+ */
+ public final native void lineTo(float x, float y) /*-{
+ this.lineTo(x, y);
+ }-*/;
+
+ /**
+ * Returns the metrics for the given text.
+ */
+ public final native TextMetrics measureText(String text) /*-{
+ return this.measureText(text);
+ }-*/;
+
+ /**
+ * Sets the current path position.
+ */
+ public final native void moveTo(float x, float y) /*-{
+ this.moveTo(x, y);
+ }-*/;
+
+ /**
+ * Write the given image data to the given screen position.
+ *
+ * @param imagedata The image data to be written to the screen.
+ * @param x the x-position of the image data.
+ * @param y the y-position of the image data.
+ */
+ public final native void putImageData(ImageData imagedata, float x, float y) /*-{
+ return this.putImageData(imagedata, x, y);
+ }-*/;
+
+ /**
+ * Draws a quadratic curve.
+ */
+ public final native void quadraticCurveTo(float cpx, float cpy, float x, float y) /*-{
+ this.quadraticCurveTo(cpx, cpy, x, y);
+ }-*/;
+
+ /**
+ * Creates a new rectangular path.
+ */
+ public final native void rect(float x, float y, float w, float h) /*-{
+ this.rect(x, y, w, h);
+ }-*/;
+
+ /**
+ * Restores the context's state.
+ */
+ public final native void restore() /*-{
+ this.restore();
+ }-*/;
+
+ /**
+ * Applies rotation to the current transform.
+ */
+ public final native void rotate(float angle) /*-{
+ this.rotate(angle);
+ }-*/;
+
+ /**
+ * Saves the context's state.
+ */
+ public final native void save() /*-{
+ this.save();
+ }-*/;
+
+ /**
+ * Applies scale to the current transform.
+ */
+ public final native void scale(float x, float y) /*-{
+ this.scale(x, y);
+ }-*/;
+
+ /**
+ * Sets the context's fillStyle.
+ *
+ * Due to needing to wrap CssColor when in devmode, we use different methods
+ * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+ *
+ * @param fillStyle the fill style to set.
+ */
+ public final void setFillStyle(FillStrokeStyle fillStyle) {
+ if (GWT.isScript()) {
+ setFillStyleWeb(fillStyle);
+ } else {
+ setFillStyleDev(fillStyle);
+ }
+ }
+
+ /**
+ * Convenience method to set the context's fillStyle to a CssColor.
+ */
+ public final void setFillStyle(String fillStyleColor) {
+ setFillStyle(CssColor.make(fillStyleColor));
+ }
+
+ /**
+ * Sets the font.
+ */
+ public final native void setFont(String f) /*-{
+ this.font = f;
+ }-*/;
+
+ /**
+ * Sets the global alpha value.
+ */
+ public final native void setGlobalAlpha(float alpha) /*-{
+ this.globalAlpha = alpha;
+ }-*/;
+
+ /**
+ * Sets the global composite operation.
+ */
+ public final void setGlobalCompositeOperation(Composite composite) {
+ setGlobalCompositeOperation(composite.getValue());
+ }
+
+ /**
+ * Sets the global composite operation.
+ */
+ public final native void setGlobalCompositeOperation(String globalCompositeOperation) /*-{
+ this.globalCompositeOperation = globalCompositeOperation;
+ }-*/;
+
+ /**
+ * Sets the line-cap style.
+ */
+ public final void setLineCap(LineCap lineCap) {
+ setLineCap(lineCap.getValue());
+ }
+
+ /**
+ * Sets the line-cap style.
+ */
+ public final native void setLineCap(String lineCap) /*-{
+ this.lineCap = lineCap;
+ }-*/;
+
+ /**
+ * Sets the line-join style.
+ */
+ public final void setLineJoin(LineJoin lineJoin) {
+ setLineJoin(lineJoin.getValue());
+ }
+
+ /**
+ * Sets the line-join style.
+ */
+ public final native void setLineJoin(String lineJoin) /*-{
+ this.lineJoin = lineJoin;
+ }-*/;
+
+ /**
+ * Sets the line-width.
+ */
+ public final native void setLineWidth(float lineWidth) /*-{
+ this.lineWidth = lineWidth;
+ }-*/;
+
+ /**
+ * Sets the miter-limit.
+ */
+ public final native void setMiterLimit(float miterLimit) /*-{
+ this.miterLimit = miterLimit;
+ }-*/;
+
+ /**
+ * Sets the shadow-blur.
+ */
+ public final native void setShadowBlur(float shadowBlur) /*-{
+ this.shadowBlur = shadowBlur;
+ }-*/;
+
+ /**
+ * Sets the shadow-color.
+ */
+ public final native void setShadowColor(String shadowColor) /*-{
+ this.shadowColor = shadowColor;
+ }-*/;
+
+ /**
+ * Sets the x-shadow-offset.
+ */
+ public final native void setShadowOffsetX(float shadowOffsetX) /*-{
+ this.shadowOffsetX = shadowOffsetX;
+ }-*/;
+
+ /**
+ * Sets the y-shadow-offset.
+ */
+ public final native void setShadowOffsetY(float shadowOffsetY) /*-{
+ this.shadowOffsetY = shadowOffsetY;
+ }-*/;
+
+ /**
+ * Convenience method to set the context's strokeStyle to a CssColor.
+ */
+ public final void setStrokeStyle(String strokeStyleColor) {
+ setStrokeStyle(CssColor.make(strokeStyleColor));
+ }
+
+ /**
+ * Sets the context's strokeStyle.
+ *
+ * Due to needing to wrap CssColor when in devmode, we use different methods
+ * depending on whether we are in devmode (when isScript == false) or not. @see CssColor
+ *
+ * @param strokeStyle the stroke style to set.
+ */
+ public final void setStrokeStyle(FillStrokeStyle strokeStyle) {
+ if (GWT.isScript()) {
+ setStrokeStyleWeb(strokeStyle);
+ } else {
+ setStrokeStyleDev(strokeStyle);
+ }
+ }
+
+ /**
+ * Sets the text alignment.
+ */
+ public final void setTextAlign(TextAlign align) {
+ setTextAlign(align.getValue());
+ }
+
+ /**
+ * Sets the text alignment.
+ */
+ public final native void setTextAlign(String align) /*-{
+ this.textAlign = align
+ }-*/;
+
+ /**
+ * Sets the text baseline.
+ */
+ public final native void setTextBaseline(String baseline) /*-{
+ this.textBaseline = baseline
+ }-*/;
+
+ /**
+ * Sets the text baseline.
+ */
+ public final void setTextBaseline(TextBaseline baseline) {
+ setTextBaseline(baseline.getValue());
+ }
+
+ /**
+ * Sets the 2D transformation matrix.
+ */
+ public final native void setTransform(float m11, float m12, float m21, float m22, float dx,
+ float dy) /*-{
+ this.setTransform(m11, m12, m21, m22, dx, dy);
+ }-*/;
+
+ /**
+ * Draws the current path with the current stroke style.
+ */
+ public final native void stroke() /*-{
+ this.stroke();
+ }-*/;
+
+ /**
+ * Draws a rectangle with the current stroke style.
+ */
+ public final native void strokeRect(float x, float y, float w, float h) /*-{
+ this.strokeRect(x, y, w, h);
+ }-*/;
+
+ /**
+ * Draws the text outline.
+ */
+ public final native void strokeText(String text, float x, float y) /*-{
+ this.strokeText(text, x, y);
+ }-*/;
+
+ /**
+ * Draws the text outline, squeezing the text into the given max width by compressing the font.
+ */
+ public final native void strokeText(String text, float x, float y, float maxWith) /*-{
+ this.strokeText(text, x, y, maxWidth);
+ }-*/;
+
+ /**
+ * Multiplies the current transform by the given matrix.
+ */
+ public final native void transform(float m11, float m12, float m21, float m22, float dx,
+ float dy) /*-{
+ this.transform(m11, m12, m21, m22, dx, dy);
+ }-*/;
+
+ /**
+ * Applies translation to the current transform.
+ */
+ public final native void translate(float x, float y) /*-{
+ this.translate(x, y);
+ }-*/;
+
+ /**
+ * Returns the fill style when in devmode.
+ *
+ * Due to needing to wrap CssColor when in devmode, we wrap the CssColor in an array when in
+ * devmode (when isScript == false) or not. @see CssColor
+ *
+ * @return the fill style.
+ */
+ final native FillStrokeStyle getFillStyleDev() /*-{
+ if (typeof(this.fillStyle) == 'string') { // if it's a color
+ return [this.fillStyle];
+ } else {
+ return this.fillStyle;
+ }
+ }-*/;
+
+ /**
+ * Returns the fill style when in webmode.
+ *
+ * @return the fill style.
+ */
+ final native FillStrokeStyle getFillStyleWeb() /*-{
+ return this.fillStyle;
+ }-*/;
+
+ /**
+ * Returns the stroke style when in devmode.
+ *
+ * Due to needing to wrap CssColor when in devmode, we wrap the CssColor in an array when in
+ * devmode (when isScript == false) or not. @see CssColor
+ *
+ * @return the stroke style.
+ */
+ final native FillStrokeStyle getStrokeStyleDev() /*-{
+ if (typeof(this.strokeStyle) == 'string') { // if it's a color
+ return [this.strokeStyle];
+ } else {
+ return this.strokeStyle;
+ }
+ }-*/;
+
+ /**
+ * Returns the stroke style when in webmode.
+ *
+ * @return the stroke style.
+ */
+ final native FillStrokeStyle getStrokeStyleWeb() /*-{
+ return this.strokeStyle;
+ }-*/;
+
+ /**
+ * Sets the fill style when in devmode.
+ *
+ * Due to needing to wrap CssColor when in devmode, we check whether the fillStyle is a wrapped
+ * array and unwrap if necessary. @see CssColor
+ *
+ * @param fillStyle the fill style to set.
+ */
+ final native void setFillStyleDev(FillStrokeStyle fillStyle) /*-{
+ if (fillStyle[0] && typeof(fillStyle[0]) == 'string') {
+ this.fillStyle = fillStyle[0];
+ } else {
+ this.fillStyle = fillStyle;
+ }
+ }-*/;
+
+ /**
+ * Sets the fill style when in webmode.
+ *
+ * @param fillStyle the fill style to set.
+ */
+ final native void setFillStyleWeb(FillStrokeStyle fillStyle) /*-{
+ this.fillStyle = fillStyle;
+ }-*/;
+
+ /**
+ * Sets the stroke style when in devmode.
+ *
+ * Due to needing to wrap CssColor when in devmode, we check whether the strokeStyle is a wrapped
+ * array and unwrap if necessary. @see CssColor
+ *
+ * @param strokeStyle the stroke style to set.
+ */
+ final native void setStrokeStyleDev(FillStrokeStyle strokeStyle) /*-{
+ if (strokeStyle[0] && typeof(strokeStyle[0]) == 'string') {
+ this.strokeStyle = strokeStyle[0];
+ } else {
+ this.strokeStyle = strokeStyle;
+ }
+ }-*/;
+
+ /**
+ * Sets the stroke style when in webmode.
+ *
+ * @param strokeStyle the strokeStyle to set.
+ */
+ final native void setStrokeStyleWeb(FillStrokeStyle strokeStyle) /*-{
+ this.strokeStyle = strokeStyle;
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/CssColor.java b/user/src/com/google/gwt/canvas/dom/client/CssColor.java
new file mode 100644
index 0000000..d59fab8
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/CssColor.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+/**
+ * CSS Color object.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ *
+ * <p>
+ * To handle devmode we must wrap JSO strings in an array. Therefore, when in devmode, CssColor is
+ * actually an array with one element that is the JSO. In webmode, this is not needed.
+ * </p>
+ *
+ * @see <a href="http://www.w3.org/TR/CSS1/#color">Cascading Style Sheets, level 1</a>
+ */
+public class CssColor extends FillStrokeStyle {
+
+ /**
+ * Sets the RGB color value.
+ *
+ * @param r red, int between 0 and 255
+ * @param g green, int between 0 and 255
+ * @param b blue, int between 0 and 255
+ */
+ public static final CssColor make(int r, int g, int b) {
+ return make("rgb(" + r + "," + g + "," + b + ")");
+ }
+
+ /**
+ * Creates a CssColor object.
+ *
+ * Examples: blue, #ff0000, #f00, rgb(255,0,0)
+ *
+ * @param cssColor the CSS color
+ */
+ public static final native CssColor make(String cssColor) /*-{
+ // Due to needing to wrap CssColor when in devmode, we check whether the we are in devmode and
+ // wrap the String in an array if necessary.
+ return @com.google.gwt.core.client.GWT::isScript()() ? cssColor : [cssColor];
+ }-*/;
+
+ protected CssColor() {
+ }
+
+ /**
+ * Returns the value of the CssColor, as a String.
+ *
+ * @return the value of the color, as a String.
+ */
+ public final native String value() /*-{
+ // Due to needing to wrap CssColor when in devmode, we check whether the we are in devmode and
+ // unwrap the String if necessary.
+ return @com.google.gwt.core.client.GWT::isScript()() ? this : this[0];
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/FillStrokeStyle.java b/user/src/com/google/gwt/canvas/dom/client/FillStrokeStyle.java
new file mode 100644
index 0000000..b1cc31a
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/FillStrokeStyle.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Represents a CssColor, a CanvasGradient, or a CanvasPattern that is used for stroke and fill.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class FillStrokeStyle extends JavaScriptObject {
+ public static final int TYPE_CSSCOLOR = 0, TYPE_GRADIENT = 1, TYPE_PATTERN = 2;
+
+ protected FillStrokeStyle() { }
+
+ /**
+ * Returns the type of this FillStrokeStyle as an integer.
+ *
+ * @return The type of the object.
+ */
+ public final native int getType() /*-{
+ // Due to needing to wrap the contained CssColor when in devmode, we must unwrap the color to
+ // check its type when in devmode (when isScript == false.) @see CssColor
+ var unwrappedColor = @com.google.gwt.core.client.GWT::isScript()() ? this : this[0];
+ if (unwrappedColor && typeof(unwrappedColor) == 'string') {
+ return @com.google.gwt.canvas.dom.client.FillStrokeStyle::TYPE_CSSCOLOR;
+ } else if (typeof(this.addColorStop) == 'function') {
+ return @com.google.gwt.canvas.dom.client.FillStrokeStyle::TYPE_GRADIENT;
+ } else {
+ return @com.google.gwt.canvas.dom.client.FillStrokeStyle::TYPE_PATTERN;
+ }
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/ImageData.java b/user/src/com/google/gwt/canvas/dom/client/ImageData.java
new file mode 100644
index 0000000..d3bec67
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/ImageData.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Object that holds image data and a size.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class ImageData extends JavaScriptObject {
+ /**
+ * Number of colors at each location in the array.
+ *
+ * Because image data is stored as RGBA, this is 4.
+ */
+ @SuppressWarnings("unused")
+ private static final int NUM_COLORS = 4;
+
+ /**
+ * Offsets for each color use RGBA ordering.
+ */
+ private static final int OFFSET_RED = 0;
+ private static final int OFFSET_GREEN = 1;
+ private static final int OFFSET_BLUE = 2;
+ private static final int OFFSET_ALPHA = 3;
+
+ protected ImageData() {
+ }
+
+ /**
+ * Returns the alpha value at position (x,y).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the alpha value at position (x,y), or 0 if not in the image.
+ */
+ public final int getAlphaAt(int x, int y) {
+ return getColorAt(x, y, OFFSET_ALPHA);
+ }
+
+ /**
+ * Returns the blue value at position (x,y).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the blue value at position (x,y), or 0 if not in the image.
+ */
+ public final int getBlueAt(int x, int y) {
+ return getColorAt(x, y, OFFSET_BLUE);
+ }
+
+ /**
+ * Returns a canvas pixel array of the size width * height * 4.
+ */
+ public final native CanvasPixelArray getData() /*-{
+ return this.data;
+ }-*/;
+
+ /**
+ * Returns the green value at position (x,y).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the green value at position (x,y), or 0 if not in the image.
+ */
+ public final int getGreenAt(int x, int y) {
+ return getColorAt(x, y, OFFSET_GREEN);
+ }
+
+ /**
+ * Returns the height of this image data object.
+ */
+ public final native int getHeight() /*-{
+ return this.height;
+ }-*/;
+
+ /**
+ * Returns the red value at position (x,y).
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @return the red value at position (x,y), or 0 if not in the image.
+ */
+ public final int getRedAt(int x, int y) {
+ return getColorAt(x, y, OFFSET_RED);
+ }
+
+ /**
+ * Returns the width of this image data object.
+ */
+ public final native int getWidth() /*-{
+ return this.width;
+ }-*/;
+
+ /**
+ * Sets the alpha value at position (x,y).
+ *
+ * @param alpha the alpha value
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public final void setAlphaAt(int alpha, int x, int y) {
+ setColorAt(alpha, x, y, OFFSET_ALPHA);
+ }
+
+ /**
+ * Sets the blue value at position (x,y).
+ *
+ * @param blue the blue value
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public final void setBlueAt(int blue, int x, int y) {
+ setColorAt(blue, x, y, OFFSET_BLUE);
+ }
+
+ /**
+ * Sets the green value at position (x,y).
+ *
+ * @param green the green value
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public final void setGreenAt(int green, int x, int y) {
+ setColorAt(green, x, y, OFFSET_GREEN);
+ }
+
+ /**
+ * Sets the red value at position (x,y).
+ *
+ * @param red the red value
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public final void setRedAt(int red, int x, int y) {
+ setColorAt(red, x, y, OFFSET_RED);
+ }
+
+ /**
+ * Returns the color value at position (x,y) with the specified offset.
+ *
+ * Colors are stored in RGBA format, where the offset determines the color (R, G, B, or A.) The
+ * values are stored in row-major order. If the specified location is not in the image, 0 is
+ * returned.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param offset the color offset
+ * @return the color value at position (x,y), or 0 if not in the image.
+ */
+ private native int getColorAt(int x, int y, int offset) /*-{
+ return this.data[@com.google.gwt.canvas.dom.client.ImageData::NUM_COLORS *
+ (x + y * this.width) + offset] || 0;
+ }-*/;
+
+ /**
+ * Sets the color value at position (x,y) with the specified offset.
+ *
+ * Colors are stored in RGBA format, where the offset determines the color (R, G, B, or A.) The
+ * values are stored in row-major order.
+ *
+ * @param color the color (in the range 0...255)
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param offset the color offset
+ */
+ private native void setColorAt(int color, int x, int y, int offset) /*-{
+ this.data[@com.google.gwt.canvas.dom.client.ImageData::NUM_COLORS *
+ (x + y * this.width) + offset] = color;
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/canvas/dom/client/TextMetrics.java b/user/src/com/google/gwt/canvas/dom/client/TextMetrics.java
new file mode 100644
index 0000000..0e21efc
--- /dev/null
+++ b/user/src/com/google/gwt/canvas/dom/client/TextMetrics.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * HTML 5 Canvas text metrics.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ */
+public class TextMetrics extends JavaScriptObject {
+
+ protected TextMetrics() {
+ }
+
+ /**
+ * Return the width of the rendered text.
+ *
+ * @return the width of the text
+ */
+ public final native float getWidth() /*-{
+ return this.width;
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/dom/client/CanvasElement.java b/user/src/com/google/gwt/dom/client/CanvasElement.java
new file mode 100644
index 0000000..ece13ba
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/CanvasElement.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dom.client;
+
+import com.google.gwt.canvas.dom.client.Context;
+import com.google.gwt.canvas.dom.client.Context2d;
+
+/**
+ * Canvas element.
+ *
+ * <p>
+ * <span style="color:red">Experimental API: This class is still under rapid
+ * development, and is very likely to be deleted. Use it at your own risk.
+ * </span>
+ * </p>
+ *
+ * @see <a href="http://www.w3.org/TR/html5/#canvas">W3C HTML 5 Specification</a>
+ */
+@TagName(CanvasElement.TAG)
+public class CanvasElement extends Element {
+
+ public static final String TAG = "canvas";
+
+ protected CanvasElement() {
+ }
+
+ /**
+ * Gets the rendering context that may be used to draw on this canvas.
+ *
+ * @return the canvas rendering context
+ */
+ public final native Context getContext(String contextId) /*-{
+ return this.getContext(contextId);
+ }-*/;
+
+ /**
+ * Returns a 2D rendering context.
+ *
+ * This is a convenience method, @see {@link #getContext(String)}
+ *
+ * @return a 2D canvas rendering context
+ */
+ public final native Context2d getContext2d() /*-{
+ return this.getContext(@com.google.gwt.canvas.dom.client.Context2d::CONTEXT_ID);
+ }-*/;
+
+ /**
+ * Gets the height of the canvas.
+ *
+ * @return the height, in pixels
+ */
+ public final native int getHeight() /*-{
+ return this.height;
+ }-*/;
+
+ /**
+ * Gets the width of the canvas.
+ *
+ * @return the width, in pixels
+ */
+ public final native int getWidth() /*-{
+ return this.width;
+ }-*/;
+
+ /**
+ * Sets the height of the canvas.
+ *
+ * @param height the height, in pixels
+ */
+ public final native void setHeight(int height) /*-{
+ this.height = height;
+ }-*/;
+
+ /**
+ * Sets the width of the canvas.
+ *
+ * @param width the width, in pixels
+ */
+ public final native void setWidth(int width) /*-{
+ this.width = width;
+ }-*/;
+
+ /**
+ * Returns a data URL for the current content of the canvas element.
+ *
+ * @return a data URL for the current content of this element.
+ */
+ public final native String toDataUrl() /*-{
+ return this.toDataURL();
+ }-*/;
+
+ /**
+ * Returns a data URL for the current content of the canvas element, with a specified type.
+ *
+ * @param type the type of the data url, e.g., image/jpeg or image/png.
+ * @return a data URL for the current content of this element with the specified type.
+ */
+ public final native String toDataUrl(String type) /*-{
+ return this.toDataURL(type);
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
index 584e9e3..e46f3ca 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
@@ -19,6 +19,39 @@
* Mozilla implementation of StandardBrowser.
*/
class DOMImplMozilla extends DOMImplStandard {
+ private static boolean isGecko190OrBefore;
+ private static boolean isGecko190OrBeforeDetected;
+
+ /**
+ * Return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+ *
+ * @return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+ */
+ @SuppressWarnings("unused")
+ static boolean isGecko190OrBefore() {
+ if (!isGecko190OrBeforeDetected) {
+ isGecko190OrBefore = isGecko190OrBeforeImpl();
+ isGecko190OrBeforeDetected = true;
+ }
+ return isGecko190OrBefore;
+ }
+
+ /**
+ * Return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+ *
+ * @return true if using Gecko 1.9.0 (Firefox 3) or earlier.
+ */
+ private static native boolean isGecko190OrBeforeImpl() /*-{
+ var result = /rv:([0-9]+)\.([0-9]+)\.([0-9]+)?/.exec(navigator.userAgent.toLowerCase());
+ if (result && result.length >= 3) {
+ var version = (parseInt(result[1]) * 1000000) + (parseInt(result[2]) * 1000) +
+ parseInt(result.length == 4 ? result[3] : 0);
+ if (version <= 1009000) {
+ return true;
+ }
+ }
+ return false;
+ }-*/;
@Override
public native void buttonClick(ButtonElement button) /*-{
@@ -204,6 +237,11 @@
}
}-*/;
+ /**
+ * Return true if using Gecko 1.9 (Firefox 3) or later.
+ *
+ * @return true if using Gecko 1.9 (Firefox 3) or later
+ */
private native boolean isGecko19() /*-{
var result = /rv:([0-9]+)\.([0-9]+)/.exec(navigator.userAgent.toLowerCase());
if (result && result.length == 3) {
diff --git a/user/src/com/google/gwt/dom/client/Document.java b/user/src/com/google/gwt/dom/client/Document.java
index c193a26..3aaf978 100644
--- a/user/src/com/google/gwt/dom/client/Document.java
+++ b/user/src/com/google/gwt/dom/client/Document.java
@@ -114,6 +114,15 @@
}
/**
+ * Creates an <canvas> element.
+ *
+ * @return the newly created element
+ */
+ public final CanvasElement createCanvasElement() {
+ return (CanvasElement) DOMImpl.impl.createElement(this, CanvasElement.TAG);
+ }
+
+ /**
* Creates a <caption> element.
*
* @return the newly created element
diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml
index 28d9d20..905c37e 100644
--- a/user/src/com/google/gwt/user/User.gwt.xml
+++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -18,6 +18,7 @@
<!-- Most new code should inherit this module. -->
<!-- -->
<module>
+ <inherits name="com.google.gwt.canvas.Canvas"/>
<inherits name="com.google.gwt.core.Core"/>
<inherits name="com.google.gwt.text.Text"/>
<inherits name="com.google.gwt.event.Event"/>
diff --git a/user/src/com/google/gwt/user/client/IsSupported.java b/user/src/com/google/gwt/user/client/IsSupported.java
new file mode 100644
index 0000000..4a883ff
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/IsSupported.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.user.client;
+
+/**
+ * Interface for classes that are only supported on a limited set of browsers.
+ *
+ * By convention, classes that implement IsSupported will provide a static method
+ * <code>boolean isSupported()</code> that checks whether the feature is supported at runtime.
+ */
+public interface IsSupported {
+
+}
diff --git a/user/test/com/google/gwt/canvas/CanvasSuite.java b/user/test/com/google/gwt/canvas/CanvasSuite.java
new file mode 100644
index 0000000..0518df2
--- /dev/null
+++ b/user/test/com/google/gwt/canvas/CanvasSuite.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.canvas;
+
+import com.google.gwt.canvas.client.CanvasTest;
+import com.google.gwt.canvas.dom.client.Context2dTest;
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * Tests of Canvas.
+ */
+public class CanvasSuite {
+ public static Test suite() {
+ GWTTestSuite suite = new GWTTestSuite("Test suite for Canvas GWTTestCases");
+
+ suite.addTestSuite(CanvasTest.class);
+ suite.addTestSuite(Context2dTest.class);
+
+ return suite;
+ }
+
+ private CanvasSuite() {
+ }
+}
diff --git a/user/test/com/google/gwt/canvas/client/CanvasTest.java b/user/test/com/google/gwt/canvas/client/CanvasTest.java
new file mode 100644
index 0000000..bd3cb77
--- /dev/null
+++ b/user/test/com/google/gwt/canvas/client/CanvasTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.client;
+
+import com.google.gwt.canvas.dom.client.Context2d;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Tests {@link Canvas}.
+ *
+ * Because HtmlUnit does not support HTML5, you will need to run these tests manually in order to
+ * have them run. To do that, go to "run configurations" or "debug configurations", select the test
+ * you would like to run, and put this line in the VM args under the arguments tab:
+ * -Dgwt.args="-runStyle Manual:1"
+ */
+@DoNotRunWith(Platform.HtmlUnitUnknown)
+public class CanvasTest extends GWTTestCase {
+ protected Canvas canvas1;
+ protected Canvas canvas2;
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.canvas.Canvas";
+ }
+
+ @Override
+ protected void gwtSetUp() throws Exception {
+ canvas1 = new Canvas();
+ canvas2 = new Canvas();
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ RootPanel.get().add(canvas1);
+ RootPanel.get().add(canvas2);
+ }
+
+ @Override
+ protected void gwtTearDown() throws Exception {
+ RootPanel.get().remove(canvas1);
+ RootPanel.get().remove(canvas2);
+ }
+
+ /*
+ * If the canvas has no pixels (i.e. either its horizontal dimension or its vertical dimension
+ * is zero) then the method must return the string "data:,". (This is the shortest data: URL;
+ * it represents the empty string in a text/plain resource.)
+ *
+ * Due to browser inconsistencies, we just check for data:something.
+ */
+ public void testBlankDataUrl() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("0px");
+ canvas1.setWidth("0px");
+ assertEquals(0, canvas1.getOffsetHeight());
+ assertEquals(0, canvas1.getOffsetWidth());
+ canvas1.setCoordinateSpaceHeight(0);
+ canvas1.setCoordinateSpaceWidth(0);
+
+ String dataUrl = canvas1.toDataUrl();
+ assertTrue("toDataURL() should return data:something", dataUrl.startsWith("data:"));
+ }
+
+ public void testHeightAndWidth() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ assertEquals(40, canvas1.getOffsetHeight());
+ assertEquals(60, canvas1.getOffsetWidth());
+
+ // resize
+ canvas1.setHeight("41px");
+ canvas1.setWidth("61px");
+ assertEquals(41, canvas1.getOffsetHeight());
+ assertEquals(61, canvas1.getOffsetWidth());
+
+ // add 2d context, resize internal size, should have no effect
+ Context2d context = canvas1.getContext2d();
+ canvas1.setCoordinateSpaceHeight(140);
+ canvas1.setCoordinateSpaceWidth(160);
+ context.fillRect(2, 2, 300, 300);
+
+ assertEquals(41, canvas1.getOffsetHeight());
+ assertEquals(61, canvas1.getOffsetWidth());
+ }
+
+ public void testInternalHeightAndWidth() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ assertEquals(40, canvas1.getOffsetHeight());
+ assertEquals(60, canvas1.getOffsetWidth());
+
+ // resize internal
+ canvas1.setCoordinateSpaceHeight(140);
+ canvas1.setCoordinateSpaceWidth(160);
+ assertEquals(140, canvas1.getCoordinateSpaceHeight());
+ assertEquals(160, canvas1.getCoordinateSpaceWidth());
+
+ // resize element, should have no effect on internal size
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ assertEquals(40, canvas1.getOffsetHeight());
+ assertEquals(60, canvas1.getOffsetWidth());
+ assertEquals(140, canvas1.getCoordinateSpaceHeight());
+ assertEquals(160, canvas1.getCoordinateSpaceWidth());
+
+ // resize internal
+ canvas1.setCoordinateSpaceHeight(141);
+ canvas1.setCoordinateSpaceWidth(161);
+ assertEquals(141, canvas1.getCoordinateSpaceHeight());
+ assertEquals(161, canvas1.getCoordinateSpaceWidth());
+ }
+
+ public void testDataUrlWithType() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("10px");
+ canvas1.setWidth("10px");
+ canvas1.setCoordinateSpaceHeight(10);
+ canvas1.setCoordinateSpaceWidth(10);
+
+ String dataUrl = canvas1.toDataUrl("image/png");
+ assertTrue("toDataURL(image/png) should return data:image/png[data]",
+ dataUrl.startsWith("data:image/png"));
+ }
+}
diff --git a/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
new file mode 100644
index 0000000..f7834ce
--- /dev/null
+++ b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.canvas.dom.client;
+
+import com.google.gwt.canvas.client.Canvas;
+import com.google.gwt.canvas.dom.client.Context2d.Composite;
+import com.google.gwt.canvas.dom.client.Context2d.LineCap;
+import com.google.gwt.canvas.dom.client.Context2d.LineJoin;
+import com.google.gwt.canvas.dom.client.Context2d.TextAlign;
+import com.google.gwt.canvas.dom.client.Context2d.TextBaseline;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Tests {@link Context2d}.
+ *
+ * Because HtmlUnit does not support HTML5, you will need to run these tests manually in order to
+ * have them run. To do that, go to "run configurations" or "debug configurations", select the test
+ * you would like to run, and put this line in the VM args under the arguments tab:
+ * -Dgwt.args="-runStyle Manual:1"
+ */
+@DoNotRunWith(Platform.HtmlUnitUnknown)
+public class Context2dTest extends GWTTestCase {
+ protected Canvas canvas1;
+ protected Canvas canvas2;
+
+ native boolean isGecko190OrBefore() /*-{
+ return @com.google.gwt.dom.client.DOMImplMozilla::isGecko190OrBeforeImpl()();
+ }-*/;
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.canvas.Canvas";
+ }
+
+ @Override
+ protected void gwtSetUp() throws Exception {
+ canvas1 = new Canvas();
+ canvas2 = new Canvas();
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ RootPanel.get().add(canvas1);
+ RootPanel.get().add(canvas2);
+ }
+
+ @Override
+ protected void gwtTearDown() throws Exception {
+ RootPanel.get().remove(canvas1);
+ RootPanel.get().remove(canvas2);
+ }
+
+ public void testFillRect() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(80);
+ canvas1.setCoordinateSpaceWidth(120);
+ assertEquals(40, canvas1.getOffsetHeight());
+ assertEquals(60, canvas1.getOffsetWidth());
+ assertEquals(80, canvas1.getCoordinateSpaceHeight());
+ assertEquals(120, canvas1.getCoordinateSpaceWidth());
+
+ // get a 2d context
+ Context2d context = canvas1.getContext2d();
+
+ // draw green rectangle filling 1st half of canvas
+ context.setFillStyle(CssColor.make("#00fF00"));
+ context.fillRect(0f, 0f, 60f, 80f);
+ // draw red rectangle filling 2nd half of canvas
+ context.setFillStyle("#fF0000");
+ context.fillRect(60f, 0f, 60f, 80f);
+
+ // test that the first pixel is green and the last pixel is red
+ ImageData imageData = context.getImageData(0, 0, 120, 80);
+ CanvasPixelArray pixelArray = imageData.getData();
+ assertEquals(000, pixelArray.get(0));
+ assertEquals(255, pixelArray.get(1));
+ assertEquals(000, pixelArray.get(2));
+ assertEquals(255, pixelArray.get(3));
+ assertEquals(255, pixelArray.get((40 * 20 - 1) * 4 + 0));
+ assertEquals(000, pixelArray.get((40 * 20 - 1) * 4 + 1));
+ assertEquals(000, pixelArray.get((40 * 20 - 1) * 4 + 2));
+ assertEquals(255, pixelArray.get((40 * 20 - 1) * 4 + 3));
+
+ // draw a blue square in the top left
+ context.setFillStyle("#0000fF");
+ context.fillRect(0f, 0f, 20f, 20f);
+ imageData = context.getImageData(0, 0, 20, 20);
+ pixelArray = imageData.getData();
+
+ // test that the first pixel is blue
+ assertEquals(000, pixelArray.get(0));
+ assertEquals(000, pixelArray.get(1));
+ assertEquals(255, pixelArray.get(2));
+ assertEquals(255, pixelArray.get(3));
+ }
+
+ public void testFillStyle() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ // get the 2d contexts
+ Context2d context1 = canvas1.getContext2d();
+ Context2d context2 = canvas2.getContext2d();
+
+ // test that a color can be set and is correct
+ context1.setFillStyle(CssColor.make("#ffff00"));
+ FillStrokeStyle fillStyleCol = context1.getFillStyle();
+ assertTrue("fillStyleCol is a color", fillStyleCol.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertFalse("fillStyleCol is a color", fillStyleCol.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertFalse("fillStyleCol is a color", fillStyleCol.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+ // test that a gradient can be set and is correct
+ CanvasGradient gradient = context1.createLinearGradient(0, 0, 10, 20);
+ gradient.addColorStop(0.5f, "#ffaaff");
+ context1.setFillStyle(gradient);
+ FillStrokeStyle fillStyleGrad = context1.getFillStyle();
+ assertFalse("fillStyleGrad is a gradient", fillStyleGrad.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertTrue("fillStyleGrad is a gradient", fillStyleGrad.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertFalse("fillStyleGrad is a gradient", fillStyleGrad.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+ // test that a pattern can be set and is correct
+ CanvasPattern pattern = context1.createPattern(canvas1.getCanvasElement(),
+ Context2d.Repetition.REPEAT);
+ context1.setFillStyle(pattern);
+ FillStrokeStyle fillStylePat = context1.getFillStyle();
+ assertFalse("fillStylePat is a pattern", fillStylePat.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertFalse("fillStylePat is a pattern", fillStylePat.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertTrue("fillStylePat is a pattern", fillStylePat.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+ // test that a StrokeStyle can be passed around w/o knowing what it is
+ FillStrokeStyle fillStyle = context1.getFillStyle();
+ context2.setFillStyle(fillStyle);
+ FillStrokeStyle fillStyle2 = context2.getFillStyle();
+ assertFalse("fillStyle2 is a pattern", fillStyle2.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertFalse("fillStyle2 is a pattern", fillStyle2.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertTrue("fillStyle2 is a pattern", fillStyle2.getType() == FillStrokeStyle.TYPE_PATTERN);
+ }
+
+ public void testFont() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ Context2d context = canvas1.getContext2d();
+ context.setFont("40px Times New Roman");
+ assertEquals("40px Times New Roman", context.getFont());
+ }
+
+ public void testGlobalAlpha() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ Context2d context = canvas1.getContext2d();
+ context.setGlobalAlpha(0.5f);
+ assertEquals(0.5f, context.getGlobalAlpha());
+ }
+
+ // This test currently fails on IE9 and is disabled.
+ public void disabled_testGlobalComposite() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ Context2d context = canvas1.getContext2d();
+ context.setGlobalCompositeOperation(Composite.SOURCE_OVER);
+ assertEquals(Composite.SOURCE_OVER.getValue(), context.getGlobalCompositeOperation());
+ context.setGlobalCompositeOperation(Composite.DESTINATION_OVER);
+ assertEquals(Composite.DESTINATION_OVER.getValue(), context.getGlobalCompositeOperation());
+ }
+
+ public void testGradient() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(40);
+ canvas1.setCoordinateSpaceWidth(60);
+ Context2d context = canvas1.getContext2d();
+
+ // fill the canvas with black
+ context.setFillStyle("#000000");
+ context.fillRect(0, 0, 60, 40);
+
+ // create a linear gradient from the top-left to the bottom-right.
+ CanvasGradient linearGradient = context.createLinearGradient(0, 0, 60, 40);
+ linearGradient.addColorStop(0, "#ff0000");
+ linearGradient.addColorStop(1, "#00ffff");
+ context.setFillStyle(linearGradient);
+ context.fillRect(0, 0, 60, 40);
+
+ // test that the first pixel is ff0000, the last is 00ffff, and the middle is something else
+ // isn't exact due to rounding, give 5px approx wiggle-room
+ int approx = 5;
+ ImageData imageData = context.getImageData(0, 0, 60, 40);
+ CanvasPixelArray pixelArray = imageData.getData();
+
+ assertTrue(255 - pixelArray.get(0) < approx);
+ assertTrue(pixelArray.get(1) - 000 < approx);
+ assertTrue(pixelArray.get(2) - 000 < approx);
+ assertTrue(255 - pixelArray.get(3) < approx);
+ assertFalse(000 == pixelArray.get((60 * 40 / 2) * 4 + 0));
+ assertFalse(255 == pixelArray.get((60 * 40 / 2) * 4 + 0));
+ assertTrue(pixelArray.get((60 * 40 - 1) * 4 + 0) - 000 < approx);
+ assertTrue(255 - pixelArray.get((60 * 40 - 1) * 4 + 1) < approx);
+ assertTrue(255 - pixelArray.get((60 * 40 - 1) * 4 + 2) < approx);
+ assertTrue(255 - pixelArray.get((60 * 40 - 1) * 4 + 3) < approx);
+ }
+
+ public void testImageData() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ // Firefox 3.0 does not support createImageData(), so the following tests are disabled
+ // for FF 3.0 and before.
+ if (isGecko190OrBefore()) {
+ return;
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(40);
+ canvas1.setCoordinateSpaceWidth(60);
+ Context2d context = canvas1.getContext2d();
+
+ // fill the canvas with ffff00
+ context.setFillStyle("#ffff00");
+ context.fillRect(0, 0, 60, 40);
+
+ // create a 1 x 1 image
+ ImageData onePx = context.createImageData(1, 1); // will fail on FF3.0 (not such method)
+ assertEquals(1, onePx.getHeight());
+ assertEquals(1, onePx.getWidth());
+
+ // set the image to a single blue pixel
+ CanvasPixelArray onePxArray = onePx.getData();
+ onePxArray.set(0, 000);
+ onePxArray.set(1, 000);
+ onePxArray.set(2, 255);
+ onePxArray.set(3, 255);
+
+ // put the pixel at location 10, 10 on the canvas
+ context.putImageData(onePx, 10, 10);
+ ImageData verifyPx = context.getImageData(10, 10, 1, 1);
+ assertEquals(1, verifyPx.getWidth());
+ assertEquals(1, verifyPx.getHeight());
+ CanvasPixelArray verifyPxArray = verifyPx.getData();
+ assertEquals(onePxArray.get(0), verifyPxArray.get(0));
+ assertEquals(onePxArray.get(1), verifyPxArray.get(1));
+ assertEquals(onePxArray.get(2), verifyPxArray.get(2));
+ assertEquals(onePxArray.get(3), verifyPxArray.get(3));
+
+ // test that edge cases don't blow up: values outside the range 0...255
+ ImageData clampPixels = context.createImageData(3, 3); // will fail on FF3.0 (not such method)
+ CanvasPixelArray clampArraySet = clampPixels.getData();
+ try {
+ clampArraySet.set(2, -2);
+ clampArraySet.set(3, 270);
+ context.putImageData(clampPixels, 4f, 4f);
+ } catch (Exception e) {
+ fail("Should not throw exception when setting values outside the range of 0...255");
+ }
+ clampPixels = context.getImageData(4f, 4f, 3f, 3f);
+ CanvasPixelArray clampArrayGet = clampPixels.getData();
+
+ // test that edge cases don't blow up: fall off the CanvasPixelArray end
+ int aPixel = clampArrayGet.get(9999);
+ assertEquals("CanvasPixelArray should return 0 for values off its end", aPixel, 0);
+ int bPixel = clampArrayGet.get(-9999);
+ assertEquals("CanvasPixelArray should return 0 for values off its end", bPixel, 0);
+ }
+
+ public void testIsPointInPath() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(40);
+ canvas1.setCoordinateSpaceWidth(60);
+
+ Context2d context = canvas1.getContext2d();
+ context.beginPath();
+ context.moveTo(10f, 10f);
+ context.lineTo(20f, 20f);
+ context.lineTo(20f, 10f);
+ context.closePath();
+
+ assertTrue("Point should be in path", context.isPointInPath(18f, 12f));
+ assertFalse("Point should not be in path", context.isPointInPath(1f, 1f));
+ }
+
+ public void testLines() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(40);
+ canvas1.setCoordinateSpaceWidth(60);
+
+ Context2d context = canvas1.getContext2d();
+ context.setFillStyle("#ff00ff");
+ context.setLineCap(LineCap.BUTT);
+ context.setLineJoin(LineJoin.BEVEL);
+ context.setLineWidth(2f);
+
+ context.beginPath();
+ context.moveTo(10f, 10f);
+ context.lineTo(20f, 20f);
+ context.lineTo(20f, 10f);
+ context.closePath();
+
+ assertEquals(LineCap.BUTT.getValue(), context.getLineCap());
+ assertEquals(LineJoin.BEVEL.getValue(), context.getLineJoin());
+ assertEquals(2f, context.getLineWidth());
+ }
+
+ public void testMiter() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ Context2d context = canvas1.getContext2d();
+ context.setMiterLimit(3f);
+ assertEquals(3f, context.getMiterLimit());
+ }
+
+ public void testPixelManipulation() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(40);
+ canvas1.setCoordinateSpaceWidth(60);
+ Context2d context = canvas1.getContext2d();
+
+ // fill the canvas with ff0000
+ context.setFillStyle("#ff0000");
+ context.fillRect(0, 0, 60, 40);
+ // fill the 1st and 2nd quadrants with green
+ context.setFillStyle("#00ff00");
+ context.fillRect(0, 0, 60, 20);
+
+ ImageData imageData = context.getImageData(0, 0, 60, 40);
+ assertEquals("Pixels in first quadrant should be green", 0, imageData.getRedAt(45, 10));
+ assertEquals("Pixels in first quadrant should be green", 255, imageData.getGreenAt(45, 10));
+ assertEquals("Pixels in third quadrant should be red", 255, imageData.getRedAt(15, 30));
+ assertEquals("Pixels in third quadrant should be red", 0, imageData.getGreenAt(15, 30));
+ }
+
+ public void testShadows() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ // Firefox 3.0 returns the incorrect shadowBlur value so the following tests are disabled
+ // for FF 3.0 and before.
+ if (isGecko190OrBefore()) {
+ return;
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ canvas1.setCoordinateSpaceHeight(40);
+ canvas1.setCoordinateSpaceWidth(60);
+
+ Context2d context = canvas1.getContext2d();
+ context.setShadowBlur(3f);
+ context.setShadowColor("#ff00ff");
+ context.setShadowOffsetX(3f);
+ context.setShadowOffsetY(4f);
+ context.lineTo(60, 40);
+ assertEquals(3f, context.getShadowBlur());
+ assertEquals("#ff00ff", context.getShadowColor().toLowerCase());
+ assertEquals(3f, context.getShadowOffsetX());
+ assertEquals(4f, context.getShadowOffsetY());
+ }
+
+ public void testStrokeStyle() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ // get the 2d contexts
+ Context2d context1 = canvas1.getContext2d();
+ Context2d context2 = canvas2.getContext2d();
+
+ // test that a color can be set and is correct
+ context1.setStrokeStyle(CssColor.make("#ffff00"));
+ FillStrokeStyle strokeStyleCol = context1.getStrokeStyle();
+ assertTrue("strokeStyleCol is a color", strokeStyleCol.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertFalse("strokeStyleCol is a color", strokeStyleCol.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertFalse("strokeStyleCol is a color", strokeStyleCol.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+ // test that a gradient can be set and is correct
+ CanvasGradient gradient = context1.createLinearGradient(0, 0, 10, 20);
+ gradient.addColorStop(0.5f, "#ff00ff");
+ context1.setStrokeStyle(gradient);
+ FillStrokeStyle strokeStyleGrad = context1.getStrokeStyle();
+ assertFalse("strokeStyleGrad is a gradient", strokeStyleGrad.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertTrue("strokeStyleGrad is a gradient", strokeStyleGrad.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertFalse("strokeStyleGrad is a gradient", strokeStyleGrad.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+ // test that a pattern can be set and is correct
+ CanvasPattern pattern = context1.createPattern(canvas1.getCanvasElement(),
+ Context2d.Repetition.REPEAT);
+ context1.setStrokeStyle(pattern);
+ FillStrokeStyle strokeStylePat = context1.getStrokeStyle();
+ assertFalse("strokeStylePat is a pattern", strokeStylePat.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertFalse("strokeStylePat is a pattern", strokeStylePat.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertTrue("strokeStylePat is a pattern", strokeStylePat.getType() == FillStrokeStyle.TYPE_PATTERN);
+
+ // test that a StrokeStyle can be passed around w/o knowing what it is
+ FillStrokeStyle strokeStyle = context1.getStrokeStyle();
+ context2.setStrokeStyle(strokeStyle);
+ FillStrokeStyle strokeStyle2 = context2.getStrokeStyle();
+ assertFalse("strokeStyle2 is a pattern", strokeStyle2.getType() == FillStrokeStyle.TYPE_CSSCOLOR);
+ assertFalse("strokeStyle2 is a pattern", strokeStyle2.getType() == FillStrokeStyle.TYPE_GRADIENT);
+ assertTrue("strokeStyle2 is a pattern", strokeStyle2.getType() == FillStrokeStyle.TYPE_PATTERN);
+ }
+
+ public void testText() {
+ if (!canvas1.isSupported()) {
+ return; // disable tests if not supported
+ }
+
+ canvas1.setHeight("40px");
+ canvas1.setWidth("60px");
+ Context2d context = canvas1.getContext2d();
+ context.setFont("bold 40px sans-serif");
+ context.setTextAlign(TextAlign.CENTER);
+ context.setTextBaseline(TextBaseline.HANGING);
+ context.fillText("GWT", 50, 60);
+ assertEquals(TextAlign.CENTER.getValue(), context.getTextAlign());
+ assertEquals(TextBaseline.HANGING.getValue(), context.getTextBaseline());
+ }
+}