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
-		{
-		}
 		
 	}
 }