You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by gr...@apache.org on 2020/01/28 02:12:54 UTC
[royale-asjs] branch develop updated: WIP on swf Graphics emulation.
This is an automated email from the ASF dual-hosted git repository.
gregdove pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
The following commit(s) were added to refs/heads/develop by this push:
new 8b354b8 WIP on swf Graphics emulation.
new b4bd64c Merge branch 'develop' of https://github.com/apache/royale-asjs into develop
8b354b8 is described below
commit 8b354b88570911c3444f41266c56b68f234e4504
Author: greg-dove <gr...@gmail.com>
AuthorDate: Tue Jan 28 14:32:07 2020 +1300
WIP on swf Graphics emulation.
---
.../Basic/src/main/resources/basic-manifest.xml | 2 +
.../org/apache/royale/display/UIGraphicsBase.as | 63 ++
.../main/royale/org/apache/royale/geom/Matrix.as | 33 +
.../Graphics/src/main/royale/GraphicsClasses.as | 21 +-
.../royale/org/apache/royale/display/BitmapData.as | 358 ++++++++
.../royale/org/apache/royale/display/CapsStyle.as | 46 +
.../royale/org/apache/royale/display/Graphics.as | 931 +++++++++++++++++++++
.../apache/royale/display/GraphicsBitmapFill.as | 97 +++
.../org/apache/royale/display/GraphicsEndFill.as | 40 +
.../apache/royale/display/GraphicsGradientFill.as | 256 ++++++
.../org/apache/royale/display/GraphicsSolidFill.as | 67 ++
.../org/apache/royale/display/GraphicsStroke.as | 169 ++++
.../org/apache/royale/display/IGraphicsData.as | 29 +
.../org/apache/royale/display/IGraphicsFill.as | 35 +
.../org/apache/royale/display/IGraphicsStroke.as | 31 +
.../org/apache/royale/display/IGraphicsTarget.as | 42 +
.../apache/royale/display/InterpolationMethod.as | 46 +
.../apache/royale/display/JPEGEncoderOptions.as | 40 +
.../royale/org/apache/royale/display/JointStyle.as | 46 +
.../org/apache/royale/display/LineScaleMode.as | 52 ++
.../org/apache/royale/display/PNGEncoderOptions.as | 41 +
.../royale/display/js/JSRuntimeGraphicsStore.as | 111 +++
.../apache/royale/display/js/createGraphicsSVG.as | 37 +
.../apache/royale/display/js/nonNullParamError.as | 30 +
.../MXRoyale/src/main/royale/mx/geom/Matrix.as | 3 -
25 files changed, 2622 insertions(+), 4 deletions(-)
diff --git a/frameworks/projects/Basic/src/main/resources/basic-manifest.xml b/frameworks/projects/Basic/src/main/resources/basic-manifest.xml
index 3c30bed..66974f4 100644
--- a/frameworks/projects/Basic/src/main/resources/basic-manifest.xml
+++ b/frameworks/projects/Basic/src/main/resources/basic-manifest.xml
@@ -275,4 +275,6 @@
<component id="Router" class="org.apache.royale.routing.Router"/>
+ <component id="UIGraphicsBase" class="org.apache.royale.display.UIGraphicsBase"/>
+
</componentPackage>
diff --git a/frameworks/projects/Basic/src/main/royale/org/apache/royale/display/UIGraphicsBase.as b/frameworks/projects/Basic/src/main/royale/org/apache/royale/display/UIGraphicsBase.as
new file mode 100644
index 0000000..e113f22
--- /dev/null
+++ b/frameworks/projects/Basic/src/main/royale/org/apache/royale/display/UIGraphicsBase.as
@@ -0,0 +1,63 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ import org.apache.royale.core.UIBase;
+
+ COMPILE::SWF{
+ import flash.display.Graphics;
+ }
+ COMPILE::JS{
+ import org.apache.royale.display.js.createGraphicsSVG;
+ }
+
+ public class UIGraphicsBase extends UIBase implements IGraphicsTarget{
+
+
+ COMPILE::SWF{
+ public function get graphicsRenderTarget():flash.display.Graphics{
+ return graphics;
+ }
+ }
+
+ COMPILE::JS{
+
+ private var _svg:SVGElement;
+
+ /**
+ * @royaleignorecoercion SVGElement
+ */
+ public function get graphicsRenderTarget():SVGElement{
+ if (!_svg) {
+ _svg = createGraphicsSVG('svg');
+ _svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
+ _svg.style.overflow = 'visible'; //it is hidden by default
+ if (element.childNodes.length) {
+ element.insertBefore(_svg, element.childNodes[0]);
+ } else element.appendChild(_svg);
+ }
+ return _svg;
+ }
+
+ }
+
+ }
+}
+
+
diff --git a/frameworks/projects/Core/src/main/royale/org/apache/royale/geom/Matrix.as b/frameworks/projects/Core/src/main/royale/org/apache/royale/geom/Matrix.as
index 609d759..94d3b9e 100644
--- a/frameworks/projects/Core/src/main/royale/org/apache/royale/geom/Matrix.as
+++ b/frameworks/projects/Core/src/main/royale/org/apache/royale/geom/Matrix.as
@@ -244,6 +244,39 @@ package org.apache.royale.geom
}
/**
+ * Creates the specific style of matrix expected by the certain methods of the swf Graphics emulation class in the Graphics library.
+ * @param width The width of the gradient box.
+ * @param height The height of the gradient box.
+ * @param rotation (default = 0) — The amount to rotate, in radians.
+ * @param tx (default = 0) — The distance, in pixels, to translate to the right along the x axis. This value is offset by half of the width parameter.
+ * @param ty (default = 0) — The distance, in pixels, to translate down along the y axis. This value is offset by half of the height parameter.
+ *
+ * This method is not reflectable in javascript, to allow for dead-code-elimination in applications that will not use it.
+ * If it is required for reflection, you can create a subclass, and overrride this method, simply calling the super method with the original arguments.
+ * That change will make it reflectable in the subclass.
+ * @royalesuppressexport
+ */
+ public function createGradientBox(width:Number, height:Number, rotation:Number = 0, tx:Number = 0, ty:Number = 0):void
+ {
+ this.tx = tx + width / 2;
+ this.ty = ty + height / 2;
+ a = width / 1638.4;
+ d = height / 1638.4;
+ if (rotation !== 0) {
+ var cos:Number = Math.cos(rotation);
+ var sin:Number = Math.sin(rotation);
+ c = -sin * a;
+ b = sin * d;
+ a = cos * a;
+ d = cos * d;
+ } else {
+ // the following seems unusual, but it correctly results in NaN values if a or d is NaN, which matches observed behavior from swf reference implementation
+ c = a * 0;
+ b = d * 0;
+ }
+ }
+
+ /**
* Returns a string representation of the Matrix.
* @langversion 3.0
* @playerversion Flash 10.2
diff --git a/frameworks/projects/Graphics/src/main/royale/GraphicsClasses.as b/frameworks/projects/Graphics/src/main/royale/GraphicsClasses.as
index bf91968..e97689f 100644
--- a/frameworks/projects/Graphics/src/main/royale/GraphicsClasses.as
+++ b/frameworks/projects/Graphics/src/main/royale/GraphicsClasses.as
@@ -53,7 +53,26 @@ internal class GraphicsClasses
import org.apache.royale.graphics.IPath; IPath;
import org.apache.royale.graphics.IRect; IRect;
import org.apache.royale.graphics.IText; IText;
-
+
+ //swf level graphics api support:
+ import org.apache.royale.display.Graphics; Graphics;
+ import org.apache.royale.display.IGraphicsTarget; IGraphicsTarget;
+ COMPILE::JS{
+ import org.apache.royale.display.CapsStyle; CapsStyle;
+ import org.apache.royale.display.GraphicsEndFill; GraphicsEndFill;
+ import org.apache.royale.display.GraphicsGradientFill; GraphicsGradientFill;
+ import org.apache.royale.display.GraphicsSolidFill; GraphicsSolidFill;
+ import org.apache.royale.display.GraphicsStroke; GraphicsStroke;
+ import org.apache.royale.display.IGraphicsData; IGraphicsData;
+ import org.apache.royale.display.IGraphicsFill; IGraphicsFill;
+ import org.apache.royale.display.IGraphicsStroke; IGraphicsStroke;
+ import org.apache.royale.display.InterpolationMethod; InterpolationMethod;
+ import org.apache.royale.display.JointStyle; JointStyle;
+ import org.apache.royale.display.LineScaleMode; LineScaleMode;
+
+ import org.apache.royale.display.BitmapData; BitmapData;
+ }
+
}
}
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/BitmapData.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/BitmapData.as
new file mode 100644
index 0000000..66229b7
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/BitmapData.as
@@ -0,0 +1,358 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ import org.apache.royale.display.js.JSRuntimeGraphicsStore;
+ import org.apache.royale.display.js.nonNullParamError;
+ import org.apache.royale.geom.Rectangle;
+ import org.apache.royale.geom.Point;
+ import org.apache.royale.geom.Matrix;
+ import org.apache.royale.utils.BinaryData;
+
+ COMPILE::JS
+ public class BitmapData
+ {
+
+ private var _canvas:HTMLCanvasElement;
+ private var _ctx:CanvasRenderingContext2D;
+ private var _lockedData:ImageData;
+ private static var _instIdx:uint = 0;
+
+
+
+ /**
+ *
+ * @param width
+ * @param height
+ * @param transparent
+ * @param fillColor
+ *
+ * @royaleignorecoercion HTMLCanvasElement
+ * @royaleignorecoercion CanvasRenderingContext2D
+ */
+ public function BitmapData(width:uint, height:uint, transparent:Boolean = true, fillColor:uint = 0xffffffff)
+ {
+ if ((width<0 || width > 8191)|| (height<0 || height>8191) || (width * height > 16777215)) throw new Error('width and/or height exceed the maximum dimensions');
+ this._transparent = transparent;
+ if (width && height) {
+ _canvas = document.createElement('canvas') as HTMLCanvasElement;
+ _canvas.width = _width = width;
+ _canvas.height = _height = height;
+ _id = 'royale-bitmapdata-' + _instIdx++;
+ _canvas.setAttributeNS(null,'id', _id);
+
+ _ctx = _canvas.getContext('2d') as CanvasRenderingContext2D;
+ if (fillColor || !transparent) { //do nothing for transparent black, it is the default
+ _ctx.fillStyle = convertColorValToStyle(fillColor);
+ _ctx.fillRect(0, 0, width, height);
+ }
+ _svgTarget = JSRuntimeGraphicsStore.getInstance().addBitmapDataImpl(_canvas);
+ requestAnimationFrame(onRenderUpdate);
+
+ } else throw new Error('ArgumentError: Error #2015: Invalid BitmapData.');
+
+ }
+
+ private var _id:String;
+ internal function getID():String{
+ return _id;
+ }
+
+
+ private var _dirty:Boolean = true; //at startup, it is always dirty, because the _svgTarget is not yet initialized with data
+ private var _svgTarget:SVGImageElement;
+
+
+ private static var _checked:Boolean;
+ private static var _safariDT:Boolean;
+
+ //this seems necessary for now, but hopefully a better way can be found.
+ private function get isSafariDesktop():Boolean {
+ if (_checked) return _safariDT;
+ var isSafariDT:Boolean;
+ if (window['safari']) {
+ var ua:String = window['navigator']['userAgent'];
+ if (ua.search(/version\/[\d\.]/i) > -1) {
+ isSafariDT = ua.search(/iPad|iPhone|iPod/) == -1
+ }
+ }
+ //var altName:String = goog.reflect.objectProperty('isSafariDesktop', this);
+ //BitmapData.prototype[altName] = isSafariDT
+ _safariDT = isSafariDT;
+ _checked = true;
+ console.log('safari Desktop?', isSafariDT);
+ return isSafariDT;
+ }
+
+ /**
+ *
+ * @royaleignorecoercion SVGElement
+ */
+ private function onRenderUpdate(timeStamp:Number):void{
+ if (_canvas && _svgTarget) {
+ var img_dataurl:String = _canvas.toDataURL("image/png");
+ _svgTarget.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", img_dataurl);
+ if (isSafariDesktop) {
+ //hack to trigger update of use nodes, seems necessary on Safari
+ var parentNode:SVGElement = _svgTarget.parentNode as SVGElement;
+ parentNode.appendChild(parentNode.removeChild(_svgTarget));
+ }
+ }
+ _dirty = false;
+ }
+
+ private function convertColorValToStyle(val:uint):String{
+ //use transparency rules
+ if (_transparent) {
+ if (val < 0x01000000) return 'rgba(0,0,0,0)';
+ else return 'rgba('+((val & 0xff0000)>>16)+','+((val & 0xff00)>>8)+','+(val & 0xff)+','+(((val & 0xff000000)>>>24)/255)+')';
+ } else {
+ val = val & 0xffffff;
+ return 'rgb('+((val & 0xff0000)>>16)+','+((val & 0xff00)>>8)+','+(val & 0xff)+')'
+ }
+ }
+
+
+ private var _width:uint;
+ public function get width():uint{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ return _width;
+ }
+
+ private var _height:uint;
+ public function get height():uint{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ return _height;
+ }
+
+ private var _transparent:Boolean;
+ public function get transparent():Boolean{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ return _transparent;
+ }
+
+ /**
+ * Compresses this BitmapData object using the selected compressor algorithm and returns a new ByteArray object.
+ * @param rect
+ * @param compressor
+ * @param byteArray
+ * @return
+ */
+ public function encode(rect:Rectangle, compressor:Object, byteArray:BinaryData = null):BinaryData{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ if (!rect) throw new TypeError(nonNullParamError('rect'));
+ if (!compressor) throw new TypeError(nonNullParamError('compressor'));
+ //observed in swf testing... this appears to unlock first
+ var wasLocked:Boolean = _lockedData;
+ if (wasLocked) unlock();
+ var dataString:String;
+ switch(compressor.constructor) {
+ case JPEGEncoderOptions :
+ var q:uint = JPEGEncoderOptions(compressor).quality;
+ if (q > 100) throw new RangeError('Error #2006: The supplied index is out of bounds.');
+ dataString = _canvas.toDataURL('image/jpeg',q/100);
+ break;
+ //we don't have JPEGXR in js (yet, maybe never)
+ case PNGEncoderOptions :
+ dataString = _canvas.toDataURL();
+ break;
+ default:
+ throw new Error('ArgumentError: Error #2004: One of the parameters is invalid.');
+ break;
+ }
+ const decodedData:String = window['atob'](dataString);
+ const l:uint = decodedData.length;
+ var bytes:Uint8Array = new Uint8Array(l);
+ for (var i:int = 0; i < l; i++) {
+ bytes[i] = decodedData.charCodeAt(i);
+ }
+ var output:BinaryData = new BinaryData(bytes.buffer);
+
+ if (byteArray) {
+ byteArray.writeBinaryData(output);
+ }
+
+ if (wasLocked) lock();
+ return output;
+ }
+
+
+ /**
+ * Sets a single pixel of a BitmapData object.
+ * @param x
+ * @param y
+ * @param color
+ */
+ public function setPixel(x:int, y:int, color:uint):void {
+ setPixel32(x,y,0xff000000 | color);
+ }
+
+ /**
+ * Sets the color and alpha transparency values of a single pixel of a BitmapData object.
+ * @param x
+ * @param y
+ * @param color
+ */
+
+ public function setPixel32(x:int, y:int, color:uint):void {
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ var pixel:ImageData;
+ var alpha:uint;
+ var idx:uint = 0;
+ if (_transparent) {
+ if (_lockedData) {
+ //get it from the lockedData
+ idx = (_width * 4) * y + x * 4;
+ pixel = _lockedData
+ } else {
+ //direct query
+ pixel = _ctx.getImageData(x,y,1,1);
+ }
+ alpha = pixel.data[idx+3];
+ if (alpha == 0) color = 0;
+ else alpha = (color>>24) & 0xff;
+
+ } else {
+ alpha = 0xff;//alpha is always 255. We don't have 24bit option
+ pixel = _ctx.createImageData(1, 1); //? @todo check this..
+ }
+
+ pixel.data[idx++] = (color >> 16) & 0xff;
+ pixel.data[idx++] = (color >>8) & 0xff;
+ pixel.data[idx++] = (color & 0xff);
+ pixel.data[idx] = alpha;
+ if (!_lockedData) {
+ //direct update
+ _ctx.putImageData(pixel, x,y);
+ if (!_dirty) {
+ _dirty = true;
+ requestAnimationFrame(onRenderUpdate);
+ }
+ }
+ }
+
+ public function lock():void{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ if (!_lockedData) {
+ _lockedData = _ctx.getImageData(0,0,_width, _height);
+ }
+ }
+
+ public function unlock():void{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ if (_lockedData) {
+ //update the view
+ _ctx.putImageData(_lockedData, 0,0);
+ _lockedData = null;
+ if (!_dirty) {
+ _dirty = true;
+ requestAnimationFrame(onRenderUpdate);
+ }
+ }
+ }
+
+ public function getPixel(x:int,y:int):uint{
+ return 0xffffff & getPixel32(x,y);
+ }
+
+ public function getPixel32(x:int,y:int):uint{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ var pixel:ImageData;
+ var alpha:uint;
+ var idx:uint = 0;
+ if (_transparent) {
+ if (_lockedData) {
+ //get it from the lockedData
+ idx = (_width * 4) * y + x * 4;
+ pixel = _lockedData
+ } else {
+ //direct query
+ pixel = _ctx.getImageData(x,y,1,1);
+ }
+ alpha = pixel.data[3];
+
+ } else {
+ alpha = 0xff;//alpha is always 255. We don't have 24bit option for native storage
+ pixel = _ctx.getImageData(x,y,1,1);
+ }
+ return (alpha<<24) | (pixel.data[idx++] <<16) | (pixel.data[idx++] <<8) | pixel.data[idx];
+ }
+
+ public function clone():BitmapData{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ //avoid the constructor fillRect code in all cases by using transparent black
+ var c:BitmapData = new BitmapData(_width, _height,true,0);
+ //now clone 'transparent' after constructor code has run.
+ c._transparent = _transparent;
+ //copy the underlying data from this instance to the clone
+ c._ctx.putImageData(_ctx.getImageData(0 , 0, _width, _height), 0, 0);
+ return c;
+ }
+
+ /**
+ * Fills a rectangular area of pixels with a specified ARGB color
+ * @param rect
+ * @param color
+ */
+ public function fillRect(rect:Rectangle, color:uint):void {
+ if (!rect) throw new TypeError('TypeError: Error #2007: Parameter rect must be non-null');
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ if (_lockedData) {
+ var wideData:Uint32Array = new Uint32Array(_lockedData.data.buffer);
+ if (!_transparent) color = 0xff000000 & color;
+ //ARGB -> RGBA
+ color = (((color<<8)>>>0) | color>>>24) >>>0;
+ //set values in the wideData
+ const yEnd:uint = rect.bottom;
+ const xStart:uint = rect.x;
+ const xEnd:uint = rect.right;
+ for (var y:uint = rect.y;y < yEnd;y++) {
+ var yOffset:uint = _width * y;
+ for (var x:uint = xStart; x < xEnd; x++) {
+ var idx:uint = yOffset + x ;
+ wideData[idx] = color;
+ }
+ }
+ } else {
+ _ctx.fillStyle = convertColorValToStyle(color);
+ _ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
+ if (!_dirty) {
+ _dirty = true;
+ requestAnimationFrame(onRenderUpdate);
+ }
+ }
+ }
+
+
+ public function get rect():Rectangle{
+ if (!_canvas) throw new Error('ArgumentError: Error #2015: Invalid BitmapData');
+ return new Rectangle(0, 0, _width, _height);
+ }
+
+ public function dispose():void{
+ if (_canvas) {
+ _ctx= null;
+ JSRuntimeGraphicsStore.getInstance().removeBitmapDataImpl(_canvas, _svgTarget);
+ _canvas = null;
+ _svgTarget = null;
+ _lockedData = null;
+ }
+ }
+
+ }
+}
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/CapsStyle.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/CapsStyle.as
new file mode 100644
index 0000000..f86cac2
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/CapsStyle.as
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ /**
+ * @royalesuppressexport
+ * These constants will be inlined in javascript
+ */
+ public class CapsStyle
+ {
+ /**
+ * Used to specify no caps in the caps parameter of the flash.display.Graphics.lineStyle() method.
+ */
+ public static const NONE:String = "none";
+
+ /**
+ * Used to specify round caps in the caps parameter of the flash.display.Graphics.lineStyle() method.
+ */
+ public static const ROUND:String = "round";
+
+ /**
+ * Used to specify square caps in the caps parameter of the flash.display.Graphics.lineStyle() method.
+ */
+ public static const SQUARE:String = "square";
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/Graphics.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/Graphics.as
new file mode 100644
index 0000000..7cd25e2
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/Graphics.as
@@ -0,0 +1,931 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ import org.apache.royale.display.GraphicsBitmapFill;
+ import org.apache.royale.display.js.JSRuntimeGraphicsStore;
+
+ COMPILE::JS{
+ import org.apache.royale.geom.Matrix;
+ import org.apache.royale.display.js.createGraphicsSVG;
+ }
+
+ COMPILE::SWF{
+ import flash.geom.Matrix;
+ import flash.utils.Dictionary;
+ import flash.display.BitmapData;
+ }
+
+ //todo: consider opt-in bounds updates, specific to graphics, dispatched as 'graphicsUpdate' on target,
+ // and available as 'current'/accurate from this graphics context at the time of the event. They would only
+ // represent the bounds of the Graphics content itself and exclude other potential children of the render target
+
+ /**
+ * @royalesuppressexport
+ */
+ public class Graphics
+ {
+
+ private static var unlocked:Boolean;
+
+ COMPILE::JS
+ private static var instanceMap:WeakMap;
+
+ COMPILE::SWF
+ private static var instanceMap:Dictionary;
+
+
+ public static function getInstanceFor(target:IGraphicsTarget):Graphics{
+ if (!target) return null;
+ var graphicsInst:Graphics;
+ COMPILE::JS{
+ if (!instanceMap) {
+ instanceMap = new WeakMap();
+ } else {
+ graphicsInst = instanceMap.get(target);
+ }
+ if (!graphicsInst) {
+ unlocked = true;
+ instanceMap.set(target, (graphicsInst = new Graphics(target)));
+ unlocked = false;
+ }
+ }
+ COMPILE::SWF{
+ if (!instanceMap) {
+ instanceMap = new Dictionary(true);
+ } else {
+ graphicsInst = instanceMap[target];
+ }
+ if (!graphicsInst) {
+ unlocked = true;
+ graphicsInst = instanceMap[target] = new Graphics(target);
+ unlocked = false;
+ }
+ }
+ return graphicsInst;
+ }
+
+ /**
+ * This provides explicit support for reflection if needed. Otherwise this class can be assumed to be unreflectable by normal means (across targets)
+ * @param instance - the instance of the Graphics class to be reflected against
+ * @return an object that is reflectable via string method names which map to runtime method references
+ *
+ */
+ public static function getReflectionMap(instance:Graphics):Object{
+ COMPILE::SWF{
+ return instance
+ }
+ COMPILE::JS{
+ if (!instance) return null;
+ return {
+ 'clear': instance.clear,
+ 'beginFill': instance.beginFill,
+ 'endFill': instance.endFill,
+ 'beginGradientFill': instance.beginGradientFill,
+ 'beginBitmapFill': instance.beginBitmapFill,
+ 'lineGradientStyle': instance.lineGradientStyle,
+ 'lineBitmapStyle': instance.lineBitmapStyle,
+ 'lineStyle': instance.lineStyle,
+ 'moveTo': instance.moveTo,
+ 'lineTo': instance.lineTo,
+ 'curveTo': instance.curveTo,
+ 'cubicCurveTo': instance.cubicCurveTo,
+ 'drawEllipse': instance.drawEllipse,
+ 'drawRoundRect': instance.drawRoundRect,
+ 'drawRoundRectComplex': instance.drawRoundRectComplex,
+ 'drawRect': instance.drawRect,
+ 'drawCircle': instance.drawCircle
+ }
+ }
+ }
+
+
+ public function Graphics(target:IGraphicsTarget)
+ {
+ super();
+ if (!unlocked) throw new Error('Constructor call not permitted, use static getInstanceFor method');
+ this.graphicsTarget = target;
+ COMPILE::JS
+ {
+ //increment the Graphics instance index
+ _instIdx ++;
+ }
+ }
+
+ private var graphicsTarget:IGraphicsTarget;
+
+ /**
+ * @royaleignorecoercion HTMLElement
+ */
+ public function clear():void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.clear();
+ }
+ COMPILE::JS
+ {
+ var svg:SVGElement = graphicsTarget.graphicsRenderTarget;
+ while (svg.firstChild) {
+ svg.removeChild(svg.firstChild);
+ }
+ _defs = null;
+ defsIdx = 0;
+ currentFill = null;
+ currentStroke = new GraphicsStroke();
+ _pathData = null;
+ _currentPath = null;
+ _currentStrokePath = null;
+ _strokePathData = null;
+ _nonZeroFill = false;
+ _lastPoint = _lastStartPoint = null;
+ _moveTo = null;
+ _strokeMove = false;
+
+ }
+ }
+
+
+ COMPILE::JS{
+
+ private static var _instIdx:uint = 0;
+ private var defsIdx:uint = 0;
+ private var _nonZeroFill:Boolean;
+ private var _defs:SVGDefsElement;
+
+ private function get svg():SVGElement{
+ return graphicsTarget.graphicsRenderTarget;
+ }
+
+ /**
+ * @royaleignorecoercion SVGDefsElement
+ */
+ private function get defs():SVGDefsElement{
+ if (!_defs) {
+ _defs = createGraphicsSVG('defs', false) as SVGDefsElement;
+ var svgNode:SVGElement = svg;
+ if (svgNode.childNodes.length) {
+ svgNode.insertBefore(_defs, svgNode.childNodes[0]);
+ } else svgNode.appendChild(_defs);
+ }
+ return _defs;
+ }
+
+ private var _pathData:Attr;
+ private function getPathData():Attr{
+ if (!_pathData) {
+ getCurrentPath();
+ }
+ return _pathData;
+ }
+
+ private var currentFill:IGraphicsFill;
+ private var currentStroke:GraphicsStroke = new GraphicsStroke();
+
+ private var _currentPath:SVGPathElement;
+ private var _currentStrokePath:SVGPathElement;
+
+ /**
+ * @royaleignorecoercion SVGPathElement
+ * @royaleignorecoercion SVGElement
+ */
+ private function getCurrentPath():SVGPathElement{
+ if (!_currentPath) {
+ _currentPath = createGraphicsSVG('path') as SVGPathElement;
+ _currentStrokePath = _currentPath;
+ _currentPath.setAttributeNS(null, 'd','');
+ _pathData = _currentPath.getAttributeNodeNS(null,'d');
+
+ if (currentFill) {
+ currentFill.apply(this, _currentPath );
+ if (!_nonZeroFill) {
+ //nonzero is default in svg, evenodd is default in swf
+ _currentPath.setAttributeNS(null, 'fill-rule', 'evenodd');
+ } else {
+ //set it to false for next time. This can only be set to true via GraphicsPath processing.
+ _nonZeroFill = false;
+ }
+ }
+ else setNoFill(_currentPath);
+ svg.appendChild(_currentPath);
+ //apply the current stroke now, spawning stroke paths if necessary as determined by stroke implementation
+ currentStroke.apply(this, _currentPath);
+ }
+ return _currentPath;
+ }
+
+ private var _strokePathData:Attr;
+ private var _lastStartPoint:String;
+ private var _moveTo:String;
+ private var _lastPoint:String; //'{x} {y}' for last point
+ private var _strokeMove:Boolean;
+ private function appendPathData(value:String, lastPoint:String):void{
+ _lastPoint = lastPoint;
+ if (_moveTo) {
+ value = _moveTo + value;
+ _moveTo = null;
+ } else if (!_pathData && value.charAt(0) !== 'M') {
+ value = 'M0 0' + value;
+ }
+ getPathData().value += value;
+ if (_currentStrokePath !== _currentPath) {
+ //we have a spawned stroke
+ if (_strokeMove && value.charAt(0) === 'M') {
+ _strokePathData.value = value;
+ } else {
+ _strokePathData.value += value;
+ }
+ _strokeMove = false;
+ }
+ }
+
+ /**
+ * support for linestyle changes that occur 'during' a path
+ * also supports special cases where filters are used to support certain effects in svg
+ *
+ * @royaleignorecoercion SVGPathElement
+ */
+ internal function spawnStroke(fromPaint:Boolean):SVGPathElement{
+
+ if (!_strokePathData) {
+ //retain the original for fill,
+ //if we had no stroke, then no need to create the original, just continue after
+ if (getCurrentPath().getAttributeNS(null, 'stroke') !== 'none') {
+ //otherwise set current path stroke to none, transfer previous stroke attributes to new sub path
+ _currentStrokePath = createGraphicsSVG('path') as SVGPathElement;
+ _currentStrokePath.setAttributeNS(null, 'd', getPathData().value);
+ _currentStrokePath.setAttributeNS(null, 'fill', 'none');
+ currentStroke.apply(this,_currentStrokePath);
+ getCurrentPath().setAttributeNS(null, 'stroke', 'none');
+ svg.appendChild(_currentStrokePath);
+ if (fromPaint) {
+ _strokePathData = _currentStrokePath.getAttributeNodeNS(null, 'd');
+ return _currentStrokePath;
+ }
+ }
+ }
+
+ _currentStrokePath = createGraphicsSVG('path') as SVGPathElement;
+ //then create the new stroke target
+ _strokeMove = true;
+ _currentStrokePath.setAttributeNS(null, 'd', 'M' + _lastPoint);
+ _currentStrokePath.setAttributeNS(null, 'fill', 'none');
+ _strokePathData = _currentStrokePath.getAttributeNodeNS(null, 'd');
+ svg.appendChild(_currentStrokePath);
+ _lastPoint = null;
+ return _currentStrokePath;
+ }
+
+ private const STROKE_SOLID_FILL:GraphicsSolidFill = new GraphicsSolidFill();
+ private const STROKE_GRADIENT_FILL:GraphicsGradientFill = new GraphicsGradientFill();
+ private const STROKE_BITMAP_FILL:GraphicsBitmapFill = new GraphicsBitmapFill();
+ private const SOLID_FILL:GraphicsSolidFill = new GraphicsSolidFill();
+ private const GRADIENT_FILL:GraphicsGradientFill = new GraphicsGradientFill();
+ private const BITMAP_FILL:GraphicsBitmapFill = new GraphicsBitmapFill();
+
+
+
+ /**
+ *
+ * @royaleignorecoercion SVGPatternElement
+ * @royaleignorecoercion SVGUseElement
+ */
+ internal function makeBitmapPaint(bitmapData:BitmapData, smooth:Boolean):SVGPatternElement{
+ var patternElement:SVGPatternElement = createGraphicsSVG('pattern', false) as SVGPatternElement;
+ patternElement.setAttributeNS(null, 'patternUnits', 'userSpaceOnUse');
+ var imageUse:SVGUseElement = createGraphicsSVG('use', false) as SVGUseElement;
+ var id:String = bitmapData.getID();
+ if (!defs.hasAttribute('xmlns:xlink')) {
+ defs.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+ }
+ imageUse.setAttributeNS( "http://www.w3.org/1999/xlink", "href", '#impl-'+ id);
+
+ if (!smooth) { //we will assume that 'auto' default is always smooth (until evidence suggests otherwise, the specs do say that quality should be emphasized by default)
+ //this is not supported on all browsers, but it does work for Chrome, Opera, Firefox, and also creates svg content that renders more accurately in Inkscape from testing
+ imageUse.setAttributeNS(null, 'style', 'image-rendering: optimizeSpeed; image-rendering: pixelated;')
+ }
+
+ patternElement.appendChild(imageUse);
+
+ defs.appendChild(patternElement);
+ patternElement.setAttribute('id', 'royale-bitmapfill-' + _instIdx + '-' + defsIdx);
+ defsIdx++;
+ return patternElement;
+ }
+
+
+ /**
+ *
+ * @royaleignorecoercion SVGGradientElement
+ */
+ internal function makeGradient(elementType:String):SVGGradientElement{
+ var gradientElement:SVGGradientElement = createGraphicsSVG(elementType, false) as SVGGradientElement;
+ gradientElement.setAttributeNS(null, 'gradientUnits', 'userSpaceOnUse');
+ defs.appendChild(gradientElement);
+ gradientElement.setAttribute('id', 'royale-gradient-' + _instIdx + '-' + defsIdx);
+ defsIdx++;
+ return gradientElement;
+ }
+
+ /**
+ * @royaleignorecoercion SVGStopElement
+ */
+ internal function makeGradientStop():SVGStopElement{
+ var stopElement:SVGStopElement = createGraphicsSVG('stop', false) as SVGStopElement;
+ return stopElement;
+ }
+
+ private static var _linearRGBfilter:SVGFilterElement;
+ /**
+ * This is used to allow emulation of 'linearRGB' interpolationMethod in gradients
+ * @royaleignorecoercion SVGFilterElement
+ * @royaleignorecoercion SVGElement
+ */
+ internal function getLinearRGBfilter():String{
+ var id:String = 'royale-linearRGB-filter';
+ if (_linearRGBfilter) {
+ return 'url(#' +id+ ')';
+ //return 'url(\'#' +_filter.getAttribute('id')+ '\')';
+ }
+ //var id:String = 'royale-linearRGB-filter' + _instIdx;
+ var filter:SVGFilterElement= createGraphicsSVG('filter', false) as SVGFilterElement;
+ filter.setAttribute('id', id);
+ filter.setAttributeNS(null, 'filterUnits', 'objectBoundingBox');
+ //The silent defaults for the filter region are: x="-10%" y="-10%" width="120%"
+ //generally that should accommodate most stroke sizes, but may clip some wide strokes.
+ //if clipping occurs and these need to be spawned to accommodate specifc cases, a stroke-width argument could be
+ //passed to this method and adjusted accordingly.
+ //likewise, for any paths that are horizontal or vertical likes only (one dimension is zero), objectBoundingBox units will not work
+ //nothing is attempted to deal with this at this point... it could however be addressed if there is demand.
+ //both of the above scenarios are assumed to be 'rare'
+ var arr:Array = ['R','G','B','A'];
+ arr = arr.map(function(col:String):String{return '<feFunc' + col +' type="gamma" amplitude="1" exponent="0.454545454545" offset="0"/>'});
+ filter.innerHTML = '<feComponentTransfer color-interpolation-filters="sRGB">' + arr.join('') + '</feComponentTransfer>';
+ _linearRGBfilter = filter;
+ JSRuntimeGraphicsStore.getInstance().addGraphicsImpl(filter as SVGElement);
+ // defs.appendChild(filter);
+ //return 'url(\'#' + id + '\')';
+ return 'url(#' + id + ')';
+ }
+ }
+
+ public function beginFill(color:uint, alpha:Number = 1.0):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.beginFill(color, alpha);
+ }
+ COMPILE::JS
+ {
+ if (_currentPath) endCurrentPath();
+ SOLID_FILL.color = color;
+ SOLID_FILL.alpha = alpha;
+ currentFill = SOLID_FILL;
+ }
+ }
+
+
+ public function endFill(): void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.endFill();
+ }
+ COMPILE::JS
+ {
+ endCurrentPath();
+ }
+ }
+
+
+
+ public function beginGradientFill(type:String, colors:Array, alphas:Array, ratios:Array, matrix:Matrix = null, spreadMethod:String = 'pad', interpolationMethod:String = 'rgb', focalPointRatio:Number = 0):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.beginGradientFill(type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio);
+ }
+ COMPILE::JS
+ {
+ if (_currentPath) endCurrentPath();
+ //validate the colors/alphas/ratios
+ if (colors == null || !colors.length) {
+ currentFill = null;
+ return
+ }
+ if (type !== 'linear' && type !== 'radial') throw new Error('Error #2008: Parameter type must be one of the accepted values.');
+ var gradientFill:GraphicsGradientFill = GRADIENT_FILL;
+ gradientFill.type = type;
+ gradientFill.colors = colors ? colors.slice(): null;
+ gradientFill.alphas = alphas ? alphas.slice(): null;
+ gradientFill.ratios = ratios ? ratios.slice(): null;
+ gradientFill.matrix = matrix ? matrix.clone() /*as Matrix*/: null;
+ gradientFill.spreadMethod = spreadMethod;
+ gradientFill.interpolationMethod = interpolationMethod;
+ gradientFill.focalPointRatio = focalPointRatio;
+ currentFill = gradientFill;
+ }
+ }
+
+ /*COMPILE::JS
+ private var _bitmapDatas:Array;
+ internal function usedBitmapData(bitmap:BitmapData):void{
+ var bdatas:Array = _bitmapDatas || (_bitmapDatas = []);
+ if (bdatas.indexOf(bitmap) == -1) {
+ bdatas.push(bitmap);
+ }
+ }
+ */
+ public function beginBitmapFill(bitmap:BitmapData, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false):void{
+ COMPILE::SWF{
+ graphicsTarget.graphicsRenderTarget.beginBitmapFill(bitmap, matrix, repeat, smooth);
+ }
+ COMPILE::JS
+ {
+ if (_currentPath) endCurrentPath();
+ //validate the btimapData
+ if (bitmap == null) {
+ throw new TypeError('Error #2007: Parameter bitmap must be non-null.')
+ }
+
+ var bitmapFill:GraphicsBitmapFill = BITMAP_FILL;
+ bitmapFill.bitmapData = bitmap;
+ bitmapFill.matrix = matrix ? matrix.clone()/* as Matrix*/: null;;
+ bitmapFill.repeat = repeat;
+ bitmapFill.smooth = smooth;
+ currentFill = bitmapFill;
+ }
+ }
+
+
+ public function lineBitmapStyle(bitmap:BitmapData, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false):void{
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.lineBitmapStyle(bitmap, matrix, repeat, smooth);
+ }
+ COMPILE::JS
+ {
+ //validate the btimapData
+ if (bitmap == null) {
+ throw new TypeError('Error #2007: Parameter bitmap must be non-null.')
+ }
+
+ var bitmapFill:GraphicsBitmapFill = STROKE_BITMAP_FILL;
+ bitmapFill.bitmapData = bitmap;
+ bitmapFill.matrix = matrix ? matrix.clone()/* as Matrix*/: null;
+ bitmapFill.repeat = repeat;
+ bitmapFill.smooth = smooth;
+ currentStroke.fill = bitmapFill;
+ if (_currentStrokePath) {
+ currentStroke.apply(this, _currentStrokePath);
+ }
+ }
+ }
+
+
+ public function lineGradientStyle(type:String, colors:Array, alphas:Array, ratios:Array, matrix:Matrix = null, spreadMethod:String = "pad", interpolationMethod:String = "rgb", focalPointRatio:Number = 0):void{
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.lineGradientStyle(type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio);
+ }
+ COMPILE::JS
+ {
+ //validate the colors/alphas/ratios
+ if (colors == null || !colors.length) {
+ currentStroke.thickness = NaN;
+ return
+ }
+ if (type !== 'linear' && type !== 'radial') throw new Error('Error #2008: Parameter type must be one of the accepted values.');
+ var gradientFill:GraphicsGradientFill = STROKE_GRADIENT_FILL;
+ gradientFill.type = type;
+ gradientFill.colors = colors ? colors.slice(): null;
+ gradientFill.alphas = alphas ? alphas.slice(): null;
+ gradientFill.ratios = ratios ? ratios.slice(): null;
+ gradientFill.matrix = matrix ? matrix.clone() /*as Matrix*/: null;
+ gradientFill.spreadMethod = spreadMethod;
+ gradientFill.interpolationMethod = interpolationMethod;
+ gradientFill.focalPointRatio = focalPointRatio;
+ currentStroke.fill = gradientFill;
+ if (_currentStrokePath) {
+ currentStroke.apply(this, _currentStrokePath);
+ }
+ }
+ }
+
+
+ public function lineStyle(thickness:Number = NaN, color:uint = 0, alpha:Number = 1.0, pixelHinting:Boolean = false, scaleMode:String = 'normal', caps:String = null, joints:String = null, miterLimit:Number = 3):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.lineStyle(thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit);
+ }
+ COMPILE::JS
+ {
+ var checkForSpawn:Boolean = _currentPath;
+ var strokeBase:GraphicsStroke = currentStroke;
+
+ caps = caps ? caps: CapsStyle.ROUND;
+ joints = joints ? joints: JointStyle.ROUND;
+ if (checkForSpawn && _lastPoint) {
+ if (strokeBase.thickness !== thickness
+ || strokeBase.fill !== STROKE_SOLID_FILL || color !== STROKE_SOLID_FILL.color || alpha !== STROKE_SOLID_FILL.alpha
+ // || strokeBase.pixelHinting !== pixelHinting
+ || strokeBase.scaleMode !== scaleMode
+ || strokeBase.caps !== caps
+ || strokeBase.joints !== joints
+ || strokeBase.miterLimit !== miterLimit)
+ {
+ spawnStroke(false);
+ }
+ }
+
+ STROKE_SOLID_FILL.color = color;
+ STROKE_SOLID_FILL.alpha = alpha;
+
+ strokeBase.thickness = thickness;
+ strokeBase.fill = STROKE_SOLID_FILL;
+ strokeBase.pixelHinting = pixelHinting;
+ strokeBase.scaleMode = scaleMode;
+ strokeBase.caps = caps ? caps: CapsStyle.ROUND;
+ strokeBase.joints = joints;
+ strokeBase.miterLimit = miterLimit;
+
+ if (_currentStrokePath) {
+ strokeBase.apply(this, _currentStrokePath);
+ }
+ }
+ }
+
+
+ COMPILE::JS
+ private function endCurrentPath():void{
+ if (!isNaN(currentStroke.thickness)) {
+
+ if (_pathData && _pathData.value) {
+ var lastChar:String = _pathData.value.substr(_pathData.value.length-1);
+ if (lastChar.toUpperCase() != 'Z') {
+ _pathData.value += 'Z';
+ }
+
+ if (_currentStrokePath !== _currentPath) {
+ _strokePathData.value += 'L' + _lastStartPoint;
+ }
+ }
+ }
+
+ _currentPath = null;
+ _currentStrokePath = null;
+ _pathData = null;
+ _strokePathData = null;
+ _lastStartPoint = null;
+ _lastPoint = null;
+ currentFill = null;
+ }
+
+ public function moveTo(x:Number, y:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.moveTo(x, y);
+ }
+ COMPILE::JS
+ {
+ var lp:String = x + ' ' + y;
+ /*if (!_lastStartPoint)*/ _lastStartPoint = lp;
+ _moveTo = 'M' + lp;
+ //appendPathData('M' + lp, lp);
+ }
+ }
+
+ public function lineTo(x:Number, y:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.lineTo(x, y);
+ }
+ COMPILE::JS
+ {
+ var lp:String = x + ' ' + y;
+ if (!_lastStartPoint) _lastStartPoint = '0 0';
+ appendPathData('L' + lp, lp);
+ }
+ }
+
+ public function curveTo(controlX:Number, controlY:Number, anchorX:Number, anchorY:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.curveTo(controlX, controlY, anchorX, anchorY);
+ }
+ COMPILE::JS
+ {
+ var lp:String = anchorX + ' ' + anchorY;
+ if (!_lastStartPoint) _lastStartPoint = '0 0';
+ appendPathData('Q' + controlX + ' ' + controlY + ' ' + lp, lp);
+ }
+ }
+
+ public function cubicCurveTo(controlX1:Number, controlY1:Number, controlX2:Number, controlY2:Number, anchorX:Number, anchorY:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY);
+ }
+ COMPILE::JS
+ {
+ var lp:String = anchorX + ' ' + anchorY;
+ if (!_lastStartPoint) _lastStartPoint = '0 0';
+ appendPathData('C' + controlX1 + ' ' + controlY1 + ' ' + controlX2 + ' ' + controlY2 + ' ' + lp, lp);
+ }
+ }
+
+
+ public function drawEllipse(x:Number, y:Number, width:Number, height:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.drawEllipse(x, y, width, height);
+ }
+ COMPILE::JS
+ {
+ //match flash api for NaNs:
+ if (isNaN(width) || isNaN(height)) {
+ throw new Error('Error #2004: One of the parameters is invalid.')
+ }
+ if (isNaN(y)) y = 0;
+ if (isNaN(x)) x = 0;
+
+ var d:String;
+ var rx:Number = width/2;
+ var ry:Number = height/2;
+ var centerY:String = ' ' + (y + ry);
+ var startPoint:String = (x + width) + centerY;
+ if (rx == 0 || ry == 0) {
+ //draw a back/forth line either vertical or horizontal
+ if (rx == 0) {
+ d = 'M' + x + ' ' + (y + height);
+ } else {
+ d = 'M' + startPoint;
+ }
+
+ if (rx || ry) {
+ if (rx)
+ d += 'L' + x + centerY + 'Z';
+ else {
+ d += 'L' + x + ' ' + y + 'Z';
+ }
+ }
+
+ } else {
+ var arcTo:String = 'A' + rx + ' ' + ry + ' 0 0 0 ';
+ d = 'M' + startPoint
+ + arcTo + x + centerY
+ + arcTo + startPoint;
+ }
+ _lastStartPoint = null;
+ appendPathData(d, startPoint);
+ }
+ }
+
+
+ public function drawRoundRect(x:Number, y:Number, width:Number, height:Number, ellipseWidth:Number, ellipseHeight:Number = NaN):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.drawRoundRect(x, y, width, height, ellipseWidth, ellipseHeight);
+ }
+ COMPILE::JS
+ {
+ //match flash api for NaNs:
+ if (isNaN(width) || isNaN(height) || isNaN(ellipseWidth)) {
+ throw new Error('Error #2004: One of the parameters is invalid.')
+ }
+ if (isNaN(y)) y = 0;
+ if (isNaN(x)) x = 0;
+
+ var rX:Number = Math.min(ellipseWidth/2, width/2);
+ if (isNaN(ellipseHeight)) {
+ ellipseHeight = ellipseWidth;
+ }
+ var rY:Number = Math.min(ellipseHeight /2, height/2);
+ if (rX == 0 || rY ==0)
+ {
+ //no rounded corners
+ _drawRect(x, y, width, height);
+ return;
+ }
+
+ //the following correctly emulates flash api behavior with -ve radius values, or -ve width and height
+ var xw:Number = x + width;
+ var yh:Number = y + height;
+ var sweep:String = rX * rY < 0 ? '0 ' : '1 ';
+ var arcBase:String = 'A' + rX + ' ' + rY +' 0 0 ' + sweep;
+ var startPoint:String = xw + ' ' + (yh - rY);
+ var d:String = 'M' + startPoint //xw +' ' + (yh - rY)
+ + arcBase + (xw - rX) +' ' + yh
+ + 'L' + (x + rX) +' ' + yh
+ + arcBase + x + ' ' + (yh - rY)
+ + 'L' + x +' ' + (y + rY)
+ + arcBase + (x + rX) + ' ' + y
+ + 'L' + (xw - rX) +' ' + y
+ + arcBase + xw + ' ' + (y + rY)
+ + 'Z'; //'L' + startPoint; //xw +' ' + (yh - rY); consider swapping this last one for 'Z'
+
+ _lastStartPoint = null;
+ appendPathData(d, startPoint);
+ }
+ }
+
+
+ public function drawRoundRectComplex(x:Number, y:Number, width:Number, height:Number, topLeftRadius:Number, topRightRadius:Number, bottomLeftRadius:Number, bottomRightRadius:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.drawRoundRectComplex(x, y, width, height, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ }
+ COMPILE::JS
+ {
+ if (isNaN(width) || isNaN(height) || isNaN(topLeftRadius) || isNaN(topRightRadius) || isNaN(bottomLeftRadius) || isNaN(bottomRightRadius)) {
+ throw new Error('Error #2004: One of the parameters is invalid.')
+ }
+ if (isNaN(y)) y = 0;
+ if (isNaN(x)) x = 0;
+
+ var d:String;
+ var rectOnly:Boolean = (height > 0 && width > 0) && (topLeftRadius==0 && topRightRadius==0 && bottomLeftRadius==0 && bottomRightRadius==0);
+ if (!rectOnly) {
+ rectOnly = (width == 0 || height ==0) && (topLeftRadius>=0 && topRightRadius>=0 && bottomLeftRadius>=0 && bottomRightRadius>=0 )
+ }
+ if(rectOnly){
+ _drawRect(x, y, width, height);
+ return;
+ }
+ //the following appears to emulate flash api behavior with -ve radius values, or -ve width and height
+ var xw:Number = x + width;
+ var yh:Number = y + height;
+
+ var maxSize:Number = width < height ? width / 2 : height / 2;
+ //observed behavior:
+
+ if (topRightRadius + bottomRightRadius > height) {
+ topRightRadius = bottomRightRadius = maxSize;
+ }
+
+ if (height < width) {
+ if (topLeftRadius + bottomLeftRadius > height) topLeftRadius = bottomLeftRadius = maxSize;
+ if (topRightRadius + bottomRightRadius > height) topRightRadius = bottomRightRadius = maxSize;
+ } else {
+ if (topLeftRadius + topRightRadius > width) topLeftRadius = topRightRadius = maxSize;
+ if (bottomLeftRadius + bottomRightRadius > width) bottomLeftRadius = bottomRightRadius = maxSize;
+ }
+ /*topLeftRadius = topLeftRadius > maxSize ? maxSize : topLeftRadius;
+ topRightRadius = topRightRadius > maxSize ? maxSize : topRightRadius;
+ bottomLeftRadius = bottomLeftRadius > maxSize ? maxSize : bottomLeftRadius;
+ bottomRightRadius = bottomRightRadius > maxSize ? maxSize : bottomRightRadius;*/
+ var startPoint:String = xw + ' ' + (yh - bottomRightRadius);
+ // bottom-right corner
+ if (bottomRightRadius){
+ d = 'M' + startPoint //xw + ' ' + (yh - bottomRightRadius)
+ + 'A' +bottomRightRadius + ' ' + bottomRightRadius + ' 0 0 1 ' + (xw-bottomRightRadius) + ' ' + yh;
+ } else {
+ d = 'M' + startPoint; //xw + ' ' + yh ;
+ }
+
+ // bottom-left corner
+ if (bottomLeftRadius) {
+ d += 'L' + (x + bottomLeftRadius) +' ' + yh
+ + 'A' +bottomLeftRadius + ' ' + bottomLeftRadius + ' 0 0 1 ' + x + ' ' + (yh - bottomLeftRadius);
+ } else {
+ d += 'L' + x + ' ' + yh
+ }
+
+ // top-left corner
+ if (topLeftRadius) {
+ d += 'L' + x + ' ' + (y + topLeftRadius)
+ + 'A' + topLeftRadius + ' ' + topLeftRadius + ' 0 0 1 ' + (x + topLeftRadius) + ' ' + y;
+ } else {
+ d += 'L' + x + ' ' + y;
+ }
+
+ // top-right corner
+ if (topRightRadius) {
+ d += 'L' + (xw - topRightRadius) +' ' + y
+ + 'A' + topRightRadius + ' ' + topRightRadius + ' 0 0 1 ' + xw + ' ' + (y + topRightRadius)
+ + 'Z'; //'L' + startPoint; //xw + ' ' + (yh - bottomRightRadius); //check swapping this last one for 'Z'
+ } else {
+ d += 'L' + xw + ' ' + y
+ + 'Z'; //'L' + startPoint; //xw + ' ' + (yh - bottomRightRadius);//check swapping this last one for 'Z'
+ }
+
+ //_lastStartPoint = null;
+ appendPathData(d, startPoint);
+ }
+ }
+
+ public function drawRect(x:Number, y:Number, width:Number, height:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.drawRect(x, y, width, height);
+ }
+ COMPILE::JS
+ {
+ if (isNaN(width) || isNaN(height)) {
+ throw new Error('Error #2004: One of the parameters is invalid.');
+ }
+ if (isNaN(x)) x = 0;
+ if (isNaN(y)) y = 0;
+
+ var w:Boolean = !!width;
+ var h:Boolean = !!height;
+ var startPoint:String = x + ' ' + y;
+ var d:String = 'M' + startPoint; //x + ' ' + y;
+ if (w) d += 'h' + width;
+ if (h) d += 'v' + height;
+ if (w) d += 'h' + -width;
+ if (w || h) d += 'Z';
+
+ _lastStartPoint = null;
+ appendPathData(d, startPoint);
+ }
+ }
+
+ COMPILE::JS
+ private function _drawRect(x:Number, y:Number, width:Number, height:Number):void{
+ //end point is bottom right (un-rounded form of rounded rectangle)
+ if (isNaN(width) || isNaN(height)) {
+ throw new Error('Error #2004: One of the parameters is invalid.');
+ }
+ if (isNaN(x)) x = 0;
+ if (isNaN(y)) y = 0;
+
+ var w:Boolean = !!width;
+ var h:Boolean = !!height;
+ var startPoint:String = (x + width) + ' ' + (y + height);
+ var d:String = 'M' + startPoint; //x+width + ' ' + y+ height;
+ if (w) d += 'h' + -width;
+ if (h) d += 'v' + -height;
+ if (w) d += 'h' + width;
+ if (w || h) d += 'Z';
+
+ appendPathData(d, startPoint);
+ }
+
+
+ public function drawCircle(x:Number, y:Number, radius:Number):void
+ {
+ COMPILE::SWF
+ {
+ graphicsTarget.graphicsRenderTarget.drawCircle(x, y, radius);
+ }
+ COMPILE::JS
+ {
+ if (isNaN(radius)) {
+ throw new Error('Error #2004: One of the parameters is invalid.');
+ }
+ //observed flash api behavior:
+ if (isNaN(x)) x = radius;
+ if (isNaN(y)) y = radius;
+ var startPoint:String;
+ var d:String;
+ if (radius) {
+ var arcBase:String = 'A'+ radius + ' ' + radius + ' 0 0 0 ';
+ var yS:String = ' ' + y;
+ startPoint = (x + radius) + yS;
+ d = 'M' + startPoint
+ + arcBase + (x - radius) + yS
+ + arcBase + startPoint;
+ } else {
+ startPoint = x + ' ' + y;
+ d = 'M' + startPoint;
+ }
+
+ _lastStartPoint = null;
+ appendPathData(d, startPoint);
+ }
+ }
+ }
+}
+
+
+
+COMPILE::JS
+function setNoFill(element:SVGPathElement):void {
+ element.setAttributeNS(null, 'fill', 'none');
+}
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsBitmapFill.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsBitmapFill.as
new file mode 100644
index 0000000..3e5080a
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsBitmapFill.as
@@ -0,0 +1,97 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ COMPILE::JS{
+ import org.apache.royale.geom.Matrix;
+ }
+ COMPILE::SWF{
+ import flash.geom.Matrix;
+ import flash.display.BitmapData;
+ }
+
+ /*COMPILE::JS{
+ import org.apache.royale.geom.Point;
+ }*/
+ /**
+ * @royalesuppresspublicvarwarning
+ * @royalesuppressexport
+ */
+ public class GraphicsBitmapFill implements IGraphicsFill, IGraphicsData
+ {
+ private static var _defaultMatrix:Matrix;
+ public var bitmapData:BitmapData;
+ public var matrix:Matrix;
+ public var repeat:Boolean;
+ public var smooth:Boolean;
+
+
+ public function GraphicsBitmapFill(bitmapData:BitmapData = null, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false){
+ this.bitmapData = bitmapData;
+ this.matrix = matrix;
+ this.repeat = repeat;
+ this.smooth = smooth;
+ }
+
+
+ COMPILE::JS
+ public function apply(graphics:Graphics, element:SVGPathElement):void {
+ var fillElement:SVGPatternElement = configurePatternElement(graphics, graphics.makeBitmapPaint(bitmapData, smooth));
+ element.setAttributeNS(null, 'fill', 'url(#'+ fillElement.getAttribute('id') + ')');
+ }
+
+ COMPILE::JS
+ public function applyStroke(graphics:Graphics, element:SVGPathElement):SVGPathElement {
+ //remove any stroke-opacity setting, which is not applicable
+ if (element.getAttributeNS(null, 'stroke-opacity') != null) {
+ element.setAttributeNS(null, 'stroke-opacity', null);
+ }
+
+ var strokeElement:SVGPatternElement = configurePatternElement(graphics, graphics.makeBitmapPaint(bitmapData, smooth));
+ element.setAttributeNS(null, 'stroke', 'url(#'+ strokeElement.getAttribute('id') + ')');
+ return element;
+ }
+
+ COMPILE::JS
+ private function configurePatternElement(graphics:Graphics, patternElement:SVGPatternElement):SVGPatternElement{
+
+ var w:String;
+ var h:String;
+ if (repeat) {
+ w = bitmapData.width.toString();
+ h = bitmapData.height.toString();
+ } else {
+ w = h = '100%'; //we cannot 'pad' yet. Need to investigate options, possibly using feConvolveMatrix somehow with edgeMode = 'duplicate'
+ }
+ patternElement.setAttributeNS(null, 'width',w);
+ patternElement.setAttributeNS(null, 'height', h);
+ if (matrix) {
+ var matrixString:String = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.tx + ',' + matrix.ty + ')';
+ patternElement.setAttributeNS(null, 'patternTransform', matrixString);
+ }
+
+ return patternElement;
+ }
+
+
+ }
+
+}
+
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsEndFill.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsEndFill.as
new file mode 100644
index 0000000..aaec203
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsEndFill.as
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ public class GraphicsEndFill implements IGraphicsFill, IGraphicsData
+ {
+
+ public function GraphicsEndFill(){
+ }
+
+ COMPILE::JS
+ public function apply(graphics:Graphics, element:SVGPathElement):void {
+
+ }
+ COMPILE::JS
+ public function applyStroke(graphics:Graphics, element:SVGPathElement):SVGPathElement {
+ return element;
+ }
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsGradientFill.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsGradientFill.as
new file mode 100644
index 0000000..2f45e17
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsGradientFill.as
@@ -0,0 +1,256 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ COMPILE::JS{
+ import org.apache.royale.geom.Matrix;
+ }
+ COMPILE::SWF{
+ import flash.geom.Matrix;
+ }
+
+ /*COMPILE::JS{
+ import org.apache.royale.geom.Point;
+ }*/
+ /**
+ * @royalesuppresspublicvarwarning
+ * @royalesuppressexport
+ */
+ public class GraphicsGradientFill implements IGraphicsFill, IGraphicsData
+ {
+ private static var _defaultMatrix:Matrix;
+ public var alphas:Array;
+ public var colors:Array;
+ public var ratios:Array;
+ public var matrix:Matrix;
+ public var spreadMethod:String;
+ public var type:String;
+
+ public var interpolationMethod:String;
+ public var focalPointRatio:Number;
+
+
+ public function GraphicsGradientFill(type:String = "linear", colors:Array = null, alphas:Array = null, ratios:Array = null, matrix:* = null, spreadMethod:* = 'pad', interpolationMethod:String = "rgb", focalPointRatio:Number = 0.0){
+ this.type = type;
+ this.colors = colors;
+ this.alphas = alphas;
+ this.ratios = ratios;
+ this.matrix = matrix;
+ this.spreadMethod = spreadMethod;
+ this.interpolationMethod = interpolationMethod;
+ this.focalPointRatio = focalPointRatio;
+ }
+
+
+ COMPILE::JS
+ public function apply(graphics:Graphics, element:SVGPathElement):void {
+ var fillElement:SVGGradientElement = configureGradientElement(graphics, graphics.makeGradient(this.type =='linear' ? 'linearGradient' : 'radialGradient'));
+
+ element.setAttributeNS(null, 'fill', 'url(#'+ fillElement.getAttribute('id') + ')');
+ if (interpolationMethod == InterpolationMethod.LINEAR_RGB) {
+ element.setAttributeNS(null, 'filter', graphics.getLinearRGBfilter());
+ }
+ }
+
+ COMPILE::JS
+ public function applyStroke(graphics:Graphics, element:SVGPathElement):SVGPathElement {
+ //remove any stroke-opacity setting, which is not applicable
+ if (element.getAttributeNS(null, 'stroke-opacity') != null) {
+ element.setAttributeNS(null, 'stroke-opacity', null);
+ }
+ var linearRGB:Boolean = interpolationMethod == InterpolationMethod.LINEAR_RGB;
+ if (element.getAttributeNS(null, 'fill') !== 'none') {
+ //deal with possible filter-based emulations
+ var filterCheck:String = element.getAttributeNS(null, 'filter');
+ if ((linearRGB && filterCheck != graphics.getLinearRGBfilter()) || (!linearRGB && filterCheck != null)) {
+ element = graphics.spawnStroke(true);
+ return element;
+ }
+ }
+ //var strokeElement:SVGGradientElement = graphics.makeGradient(this.type =='linear' ? 'linearGradient' : 'radialGradient');
+ var strokeElement:SVGGradientElement = configureGradientElement(graphics, graphics.makeGradient(this.type =='linear' ? 'linearGradient' : 'radialGradient'));
+ element.setAttributeNS(null, 'stroke', 'url(#'+ strokeElement.getAttribute('id') + ')');
+ if (linearRGB) {
+ element.setAttributeNS(null, 'filter', graphics.getLinearRGBfilter());
+ }
+ return element;
+ }
+
+ COMPILE::JS{
+
+ private static var _utilMatrix:Matrix;
+ private static function getUtilMatrix(copyFrom:Matrix):Matrix{
+ var ret:Matrix = _utilMatrix;
+ if (!ret) {
+ ret = _utilMatrix = new Matrix();
+ }
+ ret.copyFrom(copyFrom);
+ return ret;
+ }
+
+ private function configureGradientElement(graphics:Graphics, gradientElement:SVGGradientElement):SVGGradientElement{
+
+ //make stops
+ var l:uint = colors.length;
+ for (var i:uint = 0; i< l;i++) {
+ var hexColor:String = '#'+('00000'+uint(colors[i]).toString(16)).substr(-6);
+ var stopElement:SVGStopElement = graphics.makeGradientStop();
+ var alpha:Number = alphas[i];
+ var ratio:Number = ratios[i]/255;
+ stopElement.setAttributeNS(null,'stop-color', hexColor);
+ if (alpha != 1)
+ stopElement.setAttributeNS(null,'stop-opacity', alpha.toString());
+ stopElement.setAttributeNS(null,'offset', ratio.toString());
+ gradientElement.appendChild(stopElement);
+ }
+ var matrix:Matrix = this.matrix;
+ if (!matrix) {
+ if (_defaultMatrix) matrix = _defaultMatrix;
+ else {
+ //observed behavior in swf, reproduced via trial and error
+ matrix = _defaultMatrix = new Matrix(0.1220703125,0,0,0.1220703125);
+ }
+ }
+ var utilMatrix:Matrix = matrix;
+ var h_len:Number = 819.2;
+ var isRadial:Boolean = type == 'radial';
+ var isLinear:Boolean = type == 'linear';
+ var gTransform:Boolean = false;
+
+ if (Math.abs(utilMatrix.c) > 0.000001 || Math.abs(utilMatrix.b) > 0.000001) {
+
+ if (-1.0 * (Math.round(utilMatrix.a * utilMatrix.c * 1000000)/1000000) !== Math.round(utilMatrix.d * utilMatrix.b * 1000000)/1000000) {
+ utilMatrix = getUtilMatrix(matrix);
+
+ utilMatrix.translate(-matrix.tx, -matrix.ty);
+ var skewY:Number = Math.atan2( utilMatrix.b, utilMatrix.a);
+ var skewX:Number = -1 * Math.atan2(-utilMatrix.c, utilMatrix.d);
+
+ var diff:Number = (Math.PI/2) - (Math.abs(skewX) + Math.abs(skewY));
+
+ if (skewX > 0) diff = -diff;
+ var axis_rot:Number = skewY + diff;
+ utilMatrix.rotate(axis_rot);
+
+ skewX = Math.tan(skewX + skewY);
+ utilMatrix.a += utilMatrix.b * skewX;
+ utilMatrix.c += utilMatrix.d * skewX;
+ utilMatrix.tx += utilMatrix.ty * skewX;
+
+ if (isRadial) {
+ gTransform = uniformScaleAdjusted(utilMatrix);
+ }
+
+ utilMatrix.rotate(-axis_rot);
+ utilMatrix.translate(matrix.tx, matrix.ty);
+ } else {
+ if (isRadial) {
+ utilMatrix = getUtilMatrix(matrix);
+
+ utilMatrix.translate(-matrix.tx, -matrix.ty);
+ axis_rot = Math.atan2( utilMatrix.b, utilMatrix.a);
+ if (axis_rot) utilMatrix.rotate(-axis_rot);
+ gTransform = uniformScaleAdjusted(utilMatrix);
+ if (gTransform) {
+ if (axis_rot) utilMatrix.rotate(axis_rot);
+ utilMatrix.translate(matrix.tx, matrix.ty);
+ } else utilMatrix = matrix;
+ }
+ }
+
+ } else {
+ if (isRadial) {
+ utilMatrix = getUtilMatrix(matrix);
+
+ utilMatrix.b = utilMatrix.c = 0;
+ utilMatrix.translate(-matrix.tx, -matrix.ty);
+ gTransform = uniformScaleAdjusted(utilMatrix);
+ utilMatrix.translate(matrix.tx, matrix.ty);
+ }
+ }
+
+ if (isLinear) {
+ gradientElement.setAttributeNS(null,'x1', (utilMatrix.a * -h_len + utilMatrix.tx).toString());
+ gradientElement.setAttributeNS(null,'y1', (utilMatrix.b * -h_len + utilMatrix.ty).toString());
+ gradientElement.setAttributeNS(null,'x2', (utilMatrix.a * h_len + utilMatrix.tx).toString());
+ gradientElement.setAttributeNS(null,'y2', (utilMatrix.b * h_len + utilMatrix.ty).toString());
+ }
+ else if (isRadial) {
+ var fr:Number = focalPointRatio;
+ if (isNaN(fr)) fr = -1; //observed in flash
+ if (Math.abs(fr) > 1) {
+ fr = fr < 0 ? -1 : 1;
+ }
+ if (fr) {
+ fr *= h_len;
+ gradientElement.setAttributeNS(null, 'fx', (utilMatrix.a * fr + utilMatrix.tx).toString());
+ gradientElement.setAttributeNS(null, 'fy', (utilMatrix.b * fr + utilMatrix.ty).toString());
+ }
+
+ h_len *= h_len;
+ gradientElement.setAttributeNS(null, 'r', Math.sqrt(utilMatrix.a *utilMatrix.a * h_len + utilMatrix.b * utilMatrix.b * h_len).toString());
+
+ gradientElement.setAttributeNS(null, 'cx', matrix.tx.toString());
+ gradientElement.setAttributeNS(null, 'cy', matrix.ty.toString());
+
+ if (gTransform) {
+ //can mutate utilMatrix here, because original matrix content is no longer needed
+ utilMatrix.invert();
+ utilMatrix.concat(matrix);
+ gradientElement.setAttributeNS(null, 'gradientTransform', 'matrix( '+ utilMatrix.a + ',' + utilMatrix.b + ',' + utilMatrix.c + ',' + utilMatrix.d + ',' + utilMatrix.tx + ',' + utilMatrix.ty + ')');
+ }
+ }
+
+ //common
+ gradientElement.setAttributeNS(null, 'spreadMethod', spreadMethod);
+
+ //Note: it should be possible to use 'color-interpolation' here to support 'interpolationMethod' as linearRGB according to the spec,
+ //but in practice it is not implemented anywhere that I could see (in browsers, or Inkscape)
+ //so for now, interpolationMethod is supported elsewhere via a filter on the graphic element, which provides the
+ //required match for 'linearRGB' swf output.
+ //references: (an old bug about this) https://bugzilla.mozilla.org/show_bug.cgi?id=298281#c3
+ //live browser test comparison of svg (actual) vs. png (expected) :
+ // https://www.w3.org/Graphics/SVG/Test/20030813/htmlframe/full-painting-render-01-b.html
+
+ return gradientElement;
+ }
+
+
+ private static function uniformScaleAdjusted(matrix:Matrix):Boolean {
+ var x_s:Number =matrix.a * matrix.a + matrix.b * matrix.b;
+ var y_s:Number = matrix.c * matrix.c + matrix.d * matrix.d;
+ if (y_s != x_s) {
+ if (y_s < x_s) {
+ matrix.b = matrix.c = 0;
+ matrix.a = matrix.d = Math.sqrt(x_s);
+ } else {
+ matrix.b = matrix.c = 0;
+ matrix.a = matrix.d = Math.sqrt(y_s);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ }
+ }
+
+}
+
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsSolidFill.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsSolidFill.as
new file mode 100644
index 0000000..8313118
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsSolidFill.as
@@ -0,0 +1,67 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ /**
+ * @royalesuppresspublicvarwarning
+ * @royalesuppressexport
+ */
+ public class GraphicsSolidFill implements IGraphicsFill, IGraphicsData
+ {
+ public var alpha:Number = 1.0;
+ public var color:uint = 0;
+
+ public function GraphicsSolidFill(color:uint = 0, alpha:Number = 1.0)
+ {
+ this.color = color;
+ this.alpha = alpha;
+ }
+
+ COMPILE::JS
+ public function apply(graphics:Graphics, element:SVGPathElement):void
+ {
+ var hex:String = '#' + ('00000' + color.toString(16)).substr(-6);
+ element.setAttributeNS(null, 'fill', hex);
+ var alpha:Number = this.alpha;
+ if (alpha < 0) alpha = 0;
+ if (alpha < 1)
+ {
+ element.setAttributeNS(null, 'fill-opacity', alpha.toString());
+ }
+ }
+
+ COMPILE::JS
+ public function applyStroke(graphics:Graphics, element:SVGPathElement):SVGPathElement
+ {
+ var hex:String = '#' + ('00000' + color.toString(16)).substr(-6);
+ element.setAttributeNS(null, 'stroke', hex);
+ var alpha:Number = this.alpha;
+ if (alpha < 0) alpha = 0;
+ if (alpha < 1)
+ {
+ element.setAttributeNS(null, 'stroke-opacity', alpha.toString());
+ }
+ return element;
+ }
+
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsStroke.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsStroke.as
new file mode 100644
index 0000000..b7a1077
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/GraphicsStroke.as
@@ -0,0 +1,169 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ /**
+ * @royalesuppresspublicvarwarning
+ * @royalesuppressexport
+ */
+ public class GraphicsStroke implements IGraphicsStroke, IGraphicsData
+ {
+ private static const VALID_CAPS:Array = [CapsStyle.ROUND, CapsStyle.NONE, CapsStyle.SQUARE];
+ private static const VALID_JOINTS:Array = [JointStyle.ROUND, JointStyle.BEVEL, JointStyle.MITER];
+ private static const VALID_SCALE_MODES:Array = [LineScaleMode.NORMAL, LineScaleMode.HORIZONTAL, LineScaleMode.NONE, LineScaleMode.VERTICAL];
+
+ private var _caps:String;
+ /**
+ * Specifies the type of caps at the end of lines. Valid values are: CapsStyle.NONE, CapsStyle.ROUND, and CapsStyle.SQUARE. If a value is not indicated, Flash uses round caps.
+ */
+ public function get caps():String
+ {
+ return _caps ? _caps : (_caps = VALID_CAPS[0]);
+ }
+
+ public function set caps(value:String):void
+ {
+ if (VALID_CAPS.indexOf(value) != -1)
+ {
+ _caps = value;
+ } else throw new Error('Error #2008: Parameter caps must be one of the accepted values.')
+ }
+
+ /**
+ * Specifies the instance containing data for filling a stroke. An IGraphicsFill instance can represent a series of fill commands.
+ */
+ public var fill:IGraphicsFill;
+
+ private var _joints:String;
+ /**
+ * Specifies the type of joint appearance used at angles. Valid values are: JointStyle.BEVEL, JointStyle.MITER, and JointStyle.ROUND. If a value is not indicated, Flash uses round joints.
+ */
+ public function get joints():String
+ {
+ return _joints ? _joints : (_joints = VALID_JOINTS[0]);
+ }
+
+ public function set joints(value:String):void
+ {
+ if (VALID_JOINTS.indexOf(value) != -1)
+ {
+ _joints = value;
+ } else throw new Error('Error #2008: Parameter joints must be one of the accepted values.')
+ }
+
+ /**
+ * Indicates the limit at which a miter is cut off. Valid values range from 1 to 255 (and values outside that range are rounded to 1 or 255).
+ * This value is only used if the jointStyle is set to "miter". The miterLimit value represents the length that a miter can extend beyond the point at which the lines meet to form a joint. The value expresses a factor of the line thickness. For example, with a miterLimit factor of 2.5 and a thickness of 10 pixels, the miter is cut off at 25 pixels.
+ */
+ public var miterLimit:Number = 3;
+ /**
+ * Specifies whether to hint strokes to full pixels. This affects both the position of anchors of a curve and the line stroke size itself.
+ * With pixelHinting set to true, Flash Player hints line widths to full pixel widths. With pixelHinting set to false, disjoints can appear for curves and straight lines.
+ * For example, the following illustrations show how Flash Player renders two rounded rectangles that are identical,
+ * except that the pixelHinting parameter used in the lineStyle() method is set differently (the images are scaled by 200%, to emphasize the difference):
+ */
+ public var pixelHinting:Boolean = false;
+
+ private var _scaleMode:String;
+
+ public function get scaleMode():String
+ {
+ return _scaleMode ? _scaleMode : (_scaleMode = VALID_SCALE_MODES[0]);
+ }
+
+ public function set scaleMode(value:String):void
+ {
+ if (VALID_SCALE_MODES.indexOf(value) != -1)
+ {
+ _scaleMode = value;
+ } else throw new Error('Error #2008: Parameter scaleMode must be one of the accepted values.')
+ }
+
+
+ public var thickness:Number = NaN;
+
+
+ public function GraphicsStroke(thickness:Number = NaN, pixelHinting:Boolean = false, scaleMode:String = "normal", caps:String = "none", joints:String = "round", miterLimit:Number = 3.0, fill:IGraphicsFill = null)
+ {
+ this.thickness = thickness;
+ this.pixelHinting = pixelHinting;
+ this.scaleMode = scaleMode;
+ this.caps = caps;
+ this.joints = joints;
+ this.miterLimit = miterLimit;
+ this.fill = fill;
+ }
+
+
+ COMPILE::JS
+ public function apply(graphics:Graphics, element:SVGPathElement):SVGPathElement
+ {
+ //@todo remember that 0 ('hairline') width is not the same as zero in SVG
+ //NaN thickness is equivalent to 'no' stroke
+ var thickness:Number = this.thickness;
+ if (isNaN(thickness))
+ {
+ element.setAttributeNS(null, 'stroke', 'none');
+ return element;
+ }
+ //allow the fill the chance to spawn a new stroke target (e.g. interpolationMethod emulation in gradients)
+ element = fill.applyStroke(graphics, element);
+
+ if (thickness < 0) thickness = 0; //match swf (observed)
+ if (thickness > 255) thickness = 255;//match swf (observed)
+ if (thickness == 0)
+ {
+ //in swf it is 'hairline'
+ element.setAttributeNS(null, 'vector-effect', 'non-scaling-stroke');
+ element.setAttributeNS(null, 'stroke-width', '0.5');
+ } else {
+ element.setAttributeNS(null, 'stroke-width', '' + thickness);
+ }
+
+ //some things are only useful for actual 'path', so check first
+
+ var str_val:String = caps;
+ if (str_val == CapsStyle.NONE) str_val = 'butt'; //this is also the default in svg, so could avoid setting in this case.
+ element.setAttributeNS(null, 'stroke-linecap', str_val);
+ //stroke-linejoin default is miter
+ str_val = joints;
+ element.setAttributeNS(null, 'stroke-linejoin', str_val);
+ if (str_val == JointStyle.MITER)
+ {
+ //apply miter limit
+ var mVal:Number = miterLimit;
+ if (mVal < 1) mVal = 1;
+ if (mVal > 255) mVal = 255;
+ element.setAttributeNS(null, 'stroke-miterlimit', mVal.toString());
+ }
+
+ str_val = scaleMode;
+ if (str_val == LineScaleMode.NONE && thickness > 0)
+ {
+ //we can't do 'horizontal' and 'vertical' stroke scaling in svg
+ element.setAttributeNS(null, 'vector-effect', 'non-scaling-stroke');
+ }
+
+ return element;
+ }
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsData.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsData.as
new file mode 100644
index 0000000..eeb1130
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsData.as
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ public interface IGraphicsData
+ {
+
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsFill.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsFill.as
new file mode 100644
index 0000000..df59382
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsFill.as
@@ -0,0 +1,35 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+
+ public interface IGraphicsFill
+ {
+
+ COMPILE::JS
+ function apply(graphics:Graphics, element:SVGPathElement):void
+
+ COMPILE::JS
+ function applyStroke(graphics:Graphics, element:SVGPathElement):SVGPathElement;
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsStroke.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsStroke.as
new file mode 100644
index 0000000..70e67d6
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsStroke.as
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ public interface IGraphicsStroke
+ {
+
+ COMPILE::JS
+ function apply(graphics:Graphics, element:SVGPathElement):SVGPathElement;
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsTarget.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsTarget.as
new file mode 100644
index 0000000..38fa911
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/IGraphicsTarget.as
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+ import org.apache.royale.core.IRenderedObject;
+ import org.apache.royale.events.IEventDispatcher;
+ COMPILE::SWF{
+ import flash.display.Graphics;
+ }
+
+ //the main interface for integration with standard Royale display classes
+ public interface IGraphicsTarget extends IRenderedObject, IEventDispatcher
+ {
+
+ COMPILE::SWF{
+ function get graphicsRenderTarget():flash.display.Graphics;
+ }
+
+ COMPILE::JS{
+ function get graphicsRenderTarget():SVGElement;
+ }
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/InterpolationMethod.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/InterpolationMethod.as
new file mode 100644
index 0000000..7ff8278
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/InterpolationMethod.as
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ /**
+ * @royalesuppressexport
+ */
+ public class InterpolationMethod
+ {
+ /**
+ * Specifies that the linear RGB interpolation method should be used.
+ * This means that an RGB color space based on a linear RGB color model is used.
+ */
+ public static const LINEAR_RGB:String = "linearRGB";
+
+ /**
+ * Specifies that the RGB interpolation method should be used.
+ * This means that the gradient is rendered with exponential sRGB (standard RGB) space.
+ * The sRGB space is a W3C-endorsed standard that defines a non-linear conversion between
+ * red, green, and blue component values and the actual intensity of the visible component color.
+ */
+ public static const RGB:String = "rgb";
+
+
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/JPEGEncoderOptions.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/JPEGEncoderOptions.as
new file mode 100644
index 0000000..735e7a2
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/JPEGEncoderOptions.as
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ /**
+ * @royalesuppressexport
+ * This class is not available for reflection
+ */
+ public class JPEGEncoderOptions
+ {
+ /**
+ * @royalesuppresspublicvarwarning
+ */
+ public var quality:uint;
+
+ public function JPEGEncoderOptions(quality:uint = 80) {
+ this.quality = quality;
+ }
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/JointStyle.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/JointStyle.as
new file mode 100644
index 0000000..2015aa3
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/JointStyle.as
@@ -0,0 +1,46 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ /**
+ * @royalesuppressexport
+ * These constants will be inlined in javascript
+ */
+ public class JointStyle
+ {
+ /**
+ * Specifies beveled joints in the joints parameter of the Graphics.lineStyle() method.
+ */
+ public static const BEVEL:String = "bevel";
+
+ /**
+ * Specifies mitered joints in the joints parameter of the Graphics.lineStyle() method.
+ */
+ public static const MITER:String = "miter";
+
+ /**
+ * Specifies round joints in the joints parameter of the Graphics.lineStyle() method.
+ */
+ public static const ROUND:String = "round";
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/LineScaleMode.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/LineScaleMode.as
new file mode 100644
index 0000000..b02fe0a
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/LineScaleMode.as
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ /**
+ * @royalesuppressexport
+ * These constants will be inlined in javascript
+ */
+ public class LineScaleMode
+ {
+ /**
+ * With this setting used as the scaleMode parameter of the lineStyle() method, the thickness of the line scales only horizontally.
+ */
+ public static const HORIZONTAL:String = "horizontal";
+
+ /**
+ * With this setting used as the scaleMode parameter of the lineStyle() method, the thickness of the line never scales.
+ */
+ public static const NONE:String = "none";
+
+ /**
+ * With this setting used as the scaleMode parameter of the lineStyle() method,
+ * the thickness of the line always scales when the object is scaled (the default).
+ */
+ public static const NORMAL:String = "normal";
+
+ /**
+ * With this setting used as the scaleMode parameter of the lineStyle() method, the thickness of the line scales only vertically.
+ */
+ public static const VERTICAL:String = "vertical";
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/PNGEncoderOptions.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/PNGEncoderOptions.as
new file mode 100644
index 0000000..3907156
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/PNGEncoderOptions.as
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display
+{
+
+ /**
+ * @royalesuppressexport
+ * This class is not available for reflection
+ */
+ public class PNGEncoderOptions
+ {
+ /**
+ * Chooses compression speed over file size.
+ * @royalesuppresspublicvarwarning
+ */
+ public var fastCompression : Boolean;
+
+ public function PNGEncoderOptions(fastCompression:Boolean = false) {
+ this.fastCompression = fastCompression;
+ }
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/JSRuntimeGraphicsStore.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/JSRuntimeGraphicsStore.as
new file mode 100644
index 0000000..9ccf1d1
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/JSRuntimeGraphicsStore.as
@@ -0,0 +1,111 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display.js
+{
+
+
+ /**
+ * @royalesuppressexport
+ * Intended for internal emulation use only. non-reflectable
+ *
+ * This is an internal class for storing graphics content that is referenced elsewhere.
+ * It is not intended for direct display, but must be part of the DOM. Contents can be referenced elsewhere
+ * for actual display (such as svg 'use', for example).
+ */
+ COMPILE::JS
+ public class JSRuntimeGraphicsStore
+ {
+ private static var _instance:JSRuntimeGraphicsStore;
+ public static function getInstance():JSRuntimeGraphicsStore{
+ return _instance || (new JSRuntimeGraphicsStore());
+ }
+
+ public function JSRuntimeGraphicsStore(){
+ if (_instance) throw new Error('singleton only');
+ _instance = this;
+ setupStorage();
+ }
+
+ private var storageRoot:HTMLDivElement;
+ private var svgDefs:SVGElement;
+
+ /**
+ * @royaleignorecoercion HTMLDivElement
+ * @royaleignorecoercion CSSStyleDeclaration
+ */
+ private function setupStorage():void{
+ storageRoot = document.createElement('div') as HTMLDivElement;
+ var styles:CSSStyleDeclaration = storageRoot.style as CSSStyleDeclaration;
+ styles.position = 'fixed';
+ styles.visibility = 'hidden';
+ styles.top = '5000px';
+ styles.left = '5000px';
+ styles.width = '0';
+ styles.height = '0';
+ styles.margin = '0';
+ styles.padding = '0';
+ styles.border = 'none';
+ styles.userSelect = 'none';
+ var svg:SVGElement = createGraphicsSVG('svg');
+ svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
+ svg.setAttribute('xmlns:html', 'http://www.w3.org/1999/xhtml');
+ svg.setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
+ svgDefs = createGraphicsSVG('defs', false);
+ svg.appendChild(svgDefs);
+ storageRoot.appendChild(svg);
+ //document.body.appendChild(storageRoot);
+ //todo verify 'outside' body is OK here, otherwise revert to 'normal' line above:
+ document.body.parentNode.appendChild(storageRoot);
+ }
+
+ /**
+ * @royaleignorecoercion SVGImageElement
+ */
+ public function addBitmapDataImpl(element:HTMLCanvasElement):SVGImageElement{
+ storageRoot.appendChild(element);
+ var imgElement:SVGImageElement = createGraphicsSVG('image', false) as SVGImageElement;
+ imgElement.setAttributeNS(null, 'id', 'impl-'+element.getAttributeNS(null, 'id'));
+ //the following two are not needed on Chrome and Firefox, but are needed for IE11, Edge and Safari
+ imgElement.setAttributeNS(null, 'width', element.getAttributeNS(null, 'width'));
+ imgElement.setAttributeNS(null, 'height', element.getAttributeNS(null, 'height'));
+ //the following two are not needed on Chrome, Firefox, IE11, or Edge but are needed for Safari
+ imgElement.setAttributeNS(null, 'x', "0");
+ imgElement.setAttributeNS(null, 'y', "0");
+ svgDefs.appendChild(imgElement);
+ return imgElement;
+ }
+
+ public function removeBitmapDataImpl(element:HTMLCanvasElement, imgElement:SVGImageElement):void{
+ storageRoot.removeChild(element);
+ svgDefs.removeChild(imgElement);
+ }
+
+ public function addGraphicsImpl(element:SVGElement):void{
+ svgDefs.appendChild(element);
+ }
+
+ public function removeGraphicsImpl(element:SVGElement):void{
+ svgDefs.removeChild(element);
+ }
+
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/createGraphicsSVG.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/createGraphicsSVG.as
new file mode 100644
index 0000000..0f031d8
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/createGraphicsSVG.as
@@ -0,0 +1,37 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display.js
+{
+
+ COMPILE::JS
+ /**
+ * @royaleignorecoercion SVGElement
+ */
+ public function createGraphicsSVG(elementName:String, noPointerEvents:Boolean = true):SVGElement
+ {
+ var svgElement:SVGElement = document.createElementNS('http://www.w3.org/2000/svg', elementName) as SVGElement;
+ //Graphics (emulation) has no inherent pointer-events because it is supposed to be visual only,
+ //but elements inside 'defs' don't need this, so it is avoidable when not needed:
+ if (noPointerEvents) svgElement.setAttribute('pointer-events', 'none');
+ return svgElement;
+ }
+
+}
+
+
diff --git a/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/nonNullParamError.as b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/nonNullParamError.as
new file mode 100644
index 0000000..a3cd98d
--- /dev/null
+++ b/frameworks/projects/Graphics/src/main/royale/org/apache/royale/display/js/nonNullParamError.as
@@ -0,0 +1,30 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.royale.display.js
+{
+
+ COMPILE::JS
+ public function nonNullParamError(paramName:String):String
+ {
+ return 'Error #2007: Parameter ' + paramName + ' must be non-null.';
+ }
+
+}
+
+
diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/geom/Matrix.as b/frameworks/projects/MXRoyale/src/main/royale/mx/geom/Matrix.as
index 33e4546..67b3344 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/mx/geom/Matrix.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/geom/Matrix.as
@@ -41,9 +41,6 @@ package mx.geom
super(a,b,c,d,tx,ty);
}
- public function createGradientBox(width:Number, height:Number, rotation:Number = 0, tx:Number = 0, ty:Number = 0):void
- {
- }
}
}