class Graphics {
 static final Map _BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63};
 final List<_GraphicsCommand> _commands = new List<_GraphicsCommand>();
 Rectangle _identityRectangle = new Rectangle.zero();
 bool _identityRectangleRefresh = true;
 clear() {
   _commands.clear();
   _identityRectangleRefresh = true;
 }
 _addCommand(_GraphicsCommand command) {
   _commands.add(command);
   _identityRectangleRefresh = true;
 }
 //-------------------------------------------------------------------------------------------------
 beginPath() =>
   _addCommand(new _GraphicsCommandBeginPath());
 closePath() =>
   _addCommand(new _GraphicsCommandClosePath());
 moveTo(num x, num y) =>
   _addCommand(new _GraphicsCommandMoveTo(x, y));
 lineTo(num x, num y) =>
   _addCommand(new _GraphicsCommandLineTo(x, y));
 arcTo(num controlX, num controlY, num endX, num endY, num radius) =>
   _addCommand(new _GraphicsCommandArcTo(controlX, controlY, endX, endY, radius));
 quadraticCurveTo(num controlX, num controlY, num endX, num endY) =>
   _addCommand(new _GraphicsCommandQuadraticCurveTo(controlX, controlY, endX, endY));
 bezierCurveTo(num controlX1, num controlY1, num controlX2, num controlY2, num endX, num endY) =>
   _addCommand(new _GraphicsCommandBezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY));
 arc(num x, num y, num radius, num startAngle, num endAngle, bool antiClockwise) =>
   _addCommand(new _GraphicsCommandArc(x, y, radius, startAngle, endAngle, antiClockwise));
 rect(num x, num y, num width, num height) =>
   _addCommand(new _GraphicsCommandRect(x,y, width, height));
 strokeColor(int color, [num width = 1.0, String joints = JointStyle.ROUND, String caps = CapsStyle.ROUND]) =>
   _addCommand(new _GraphicsCommandStrokeColor(_color2rgba(color), width, joints, caps));
 strokeGradient(GraphicsGradient gradient, [num width = 1.0, String joints = JointStyle.ROUND, String caps = CapsStyle.ROUND]) =>
   _addCommand(new _GraphicsCommandStrokeGradient(gradient, width, joints, caps));
 strokePattern(GraphicsPattern pattern, [num width = 1.0, String joints = JointStyle.ROUND, String caps = CapsStyle.ROUND]) =>
   _addCommand(new _GraphicsCommandStrokePattern(pattern, width, joints, caps));
 fillColor(int color) =>
   _addCommand(new _GraphicsCommandFillColor(_color2rgba(color)));
 fillGradient(GraphicsGradient gradient) =>
   _addCommand(new _GraphicsCommandFillGradient(gradient));
 fillPattern(GraphicsPattern pattern) =>
   _addCommand(new _GraphicsCommandFillPattern(pattern));
 //-------------------------------------------------------------------------------------------------
 rectRound(num x, num y, num width, num height, num ellipseWidth, num ellipseHeight) {
   _addCommand(new _GraphicsCommandMoveTo(x + ellipseWidth, y));
   _addCommand(new _GraphicsCommandLineTo(x + width - ellipseWidth, y));
   _addCommand(new _GraphicsCommandQuadraticCurveTo(x + width, y, x + width, y + ellipseHeight));
   _addCommand(new _GraphicsCommandLineTo(x + width, y + height - ellipseHeight));
   _addCommand(new _GraphicsCommandQuadraticCurveTo(x + width, y + height, x + width - ellipseWidth, y + height));
   _addCommand(new _GraphicsCommandLineTo(x + ellipseWidth, y + height));
   _addCommand(new _GraphicsCommandQuadraticCurveTo(x, y + height, x, y + height - ellipseHeight));
   _addCommand(new _GraphicsCommandLineTo(x, y + ellipseHeight));
   _addCommand(new _GraphicsCommandQuadraticCurveTo(x, y, x + ellipseWidth, y));
 }
 //-------------------------------------------------------------------------------------------------
 circle(num x, num y, num radius) {
   _addCommand(new _GraphicsCommandMoveTo(x + radius, y));
   _addCommand(new _GraphicsCommandArc(x, y, radius, 0, PI * 2, false));
 }
 //-------------------------------------------------------------------------------------------------
 ellipse(num x, num y, num width, num height) {
   num kappa = 0.5522848;
   num ox = (width / 2) * kappa;
   num oy = (height / 2) * kappa;
   num x1 = x - width / 2;
   num y1 = y - height / 2;
   num x2 = x + width / 2;
   num y2 = y + height / 2;
   num xm = x;
   num ym = y;
   _addCommand(new _GraphicsCommandMoveTo(x1, ym));
   _addCommand(new _GraphicsCommandBezierCurveTo(x1, ym - oy, xm - ox, y1, xm, y1));
   _addCommand(new _GraphicsCommandBezierCurveTo(xm + ox, y1, x2, ym - oy, x2, ym));
   _addCommand(new _GraphicsCommandBezierCurveTo(x2, ym + oy, xm + ox, y2, xm, y2));
   _addCommand(new _GraphicsCommandBezierCurveTo(xm - ox, y2, x1, ym + oy, x1, ym));
 }
 //-------------------------------------------------------------------------------------------------
 decode(String str)  {
   var base64 = _BASE_64;
   var instructions = [moveTo, lineTo, quadraticCurveTo, bezierCurveTo, closePath];
   List<int> paramCount = [2, 2, 4, 6, 0];
   List<num> params = new List<num>();
   var x=0, y=0;
   var i=0, l=str.length;
   while (i<l) {
     var c = str[i];
     var n = base64[c];
     var fi = n>>3; // highest order bits 1-3 code for operation.
     var f = instructions[fi];
     // check that we have a valid instruction & that the unused bits are empty:
     if (f == null || (n&3) > 0)
       throw("bad path data (@$i): $c");
     var pl = paramCount[fi];
     if (fi == 0) x=y=0; // move operations reset the position.
     params.length = 0;
     i++;
     var charCount = (n>>2&1)+2;  // 4th header bit indicates number size for this operation.
     for (var p=0; p<pl; p++) {
       var v = base64[str[i]];
       var sign = (v>>5) > 0 ? -1 : 1;
       v = ((v&31)<<6)|(base64[str[i+1]]);
       if (charCount == 3) v = (v<<6)|(base64[str[i+2]]);
       v = sign*v/10;
       if (p%2 > 0) x = (v += x);
       else y = (v += y);
       params.add(v);
       i += charCount;
     }
     Function.apply(f, params);
   }
 }
 //-------------------------------------------------------------------------------------------------
 //-------------------------------------------------------------------------------------------------
 Rectangle _getBoundsTransformed(Matrix matrix) {
   var bounds = new _GraphicsBounds(matrix);
   for(int i = 0; i < _commands.length; i++) {
     _commands[i].updateBounds(bounds);
   }
   return bounds.getRectangle();
 }
 //-------------------------------------------------------------------------------------------------
 bool _hitTestInput(num localX, num localY) {
   if (_identityRectangleRefresh) {
     _identityRectangleRefresh = false;
     _identityRectangle = _getBoundsTransformed(new Matrix.fromIdentity());
   }
   if (_identityRectangle.contains(localX, localY)) {
     var context = _dummyCanvasContext;
     context.setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
     context.beginPath();
     for(int i = 0; i < _commands.length; i++) {
       if (_commands[i].hitTestInput(context, localX, localY)) {
         return true;
       }
     }
   }
   return false;
 }
 //-------------------------------------------------------------------------------------------------
 void _drawPath(CanvasRenderingContext2D context) {
   for(int i = 0; i < _commands.length; i++) {
     _commands[i].drawPath(context);
   }
 }
 //-------------------------------------------------------------------------------------------------
 void render(RenderState renderState) {
   var renderContext = renderState.renderContext;
   var matrix = renderState.globalMatrix;
   var alpha = renderState.globalAlpha;
   if (renderContext is RenderContextCanvas) {
     var context = renderContext.rawContext;
     context.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
     context.globalAlpha = alpha;
     context.beginPath();
     for(int i = 0; i < _commands.length; i++) {
       _commands[i].render(context);
     }
   } else {
     // TODO: Native support for Graphics in WebGL will be added later.
     // For now please use the applyCache feature of DisplayObject.
   }
 }
}