context: /*
    Copyright (C) 2002, 2004, 2005, 2006, 2007 Jeroen Frijters
    Copyright (C) 2006 Active Endpoints, Inc.
    Copyright (C) 2006 - 2011 Volker Berlin (i-net software)
    Copyright (C) 2011 Karsten Heinrich (i-net software)
  
    This software is provided 'as-is', without any express or implied
    warranty.  In no event will the authors be held liable for any damages
    arising from the use of this software.
  
    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:
  
    1. The origin of this software must not be misrepresented; you must not
       claim that you wrote the original software. If you use this software
       in a product, an acknowledgment in the product documentation would be
       appreciated but is not required.
    2. Altered source versions must be plainly marked as such, and must not be
       misrepresented as being the original software.
    3. This notice may not be removed or altered from any source distribution.
  
    Jeroen Frijters
    jeroen@frijters.net 
  
  */
  
  using System;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Drawing.Text;
  using System.Globalization;
  using System.Windows.Forms;
  using java.awt.image;
  using java.util;
  
  namespace ikvm.awt
  {
  
      internal class BitmapGraphics : NetGraphics
      {
          private readonly Bitmap bitmap;
  
          internal BitmapGraphics(Bitmap bitmap, java.awt.Font font, Color fgcolor, Color bgcolor)
              : base(createGraphics(bitmap), font, fgcolor, bgcolor)
          {
              this.bitmap = bitmap;
          }
  
          internal BitmapGraphics(Bitmap bitmap)
              : base(createGraphics(bitmap), null, Color.White, Color.Black)
          {
              this.bitmap = bitmap;
          }
  
          protected override SizeF GetSize() {
              return bitmap.Size;
          }
  
          private static Graphics createGraphics(Bitmap bitmap)
          {
              // lock to prevent the exception
              // System.InvalidOperationException: Object is currently in use elsewhere
              lock (bitmap)
              {
                  return Graphics.FromImage(bitmap);
              }
          }
  
          public override java.awt.Graphics create()
          {
              BitmapGraphics newGraphics = (BitmapGraphics)MemberwiseClone();
              newGraphics.init(createGraphics(bitmap));
              return newGraphics;
          }
  
          public override void copyArea(int x, int y, int width, int height, int dx, int dy)
  		{
              Bitmap copy = new Bitmap(width, height);
              using (Graphics gCopy = Graphics.FromImage(copy))
              {
                  gCopy.DrawImage(bitmap, new Rectangle(0, 0, width, height), x, y, width, height, GraphicsUnit.Pixel);
              }
              g.DrawImageUnscaled(copy, x + dx, y + dy);
  		}
      }
  
      internal class ComponentGraphics : NetGraphics
      {
          private readonly Control control;
  
          internal ComponentGraphics(Control control, java.awt.Color fgColor, java.awt.Color bgColor, java.awt.Font font)
              : base(control.CreateGraphics(), font, J2C.ConvertColor(fgColor), J2C.ConvertColor(bgColor))
          {
              this.control = control;
          }
  
          protected override SizeF GetSize() {
              return control.Size;
          }
  
          public override java.awt.Graphics create()
          {
              ComponentGraphics newGraphics = (ComponentGraphics)MemberwiseClone();
              newGraphics.init(control.CreateGraphics());
              return newGraphics;
          }
  
          private Point getPointToScreenImpl(Point point)
          {
              return this.control.PointToScreen(point);
          }
  
          private Point getPointToScreen(Point point)
          {
              return (Point)this.control.Invoke(new Converter<Point,Point>(getPointToScreenImpl),point);
          }
  
  		public override void copyArea(int x, int y, int width, int height, int dx, int dy)
  		{
              Point src = getPointToScreen(new Point(x + (int)this.g.Transform.OffsetX, y + (int)this.g.Transform.OffsetY));
              Point dest = new Point(x + (int)this.g.Transform.OffsetX + dx, y + (int)this.g.Transform.OffsetY + dy);
              this.g.CopyFromScreen(src, dest, new Size(width, height));
  		}
  
  		public override void clip(java.awt.Shape shape)
  		{
  			if (shape == null)
  			{
  				// the API specification says that this will clear
  				// the clip, but in fact the reference implementation throws a 
  				// NullPointerException - see the following entry in the bug parade:
  				// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6206189
  				throw new java.lang.NullPointerException();
  			}
  			base.clip(shape);
  		}
      }
  
      internal class PrintGraphicsContext
      {
          internal PrintGraphics Current;
      }
  
      internal class PrintGraphics : NetGraphics
      {
          private NetGraphicsState myState;
          private PrintGraphicsContext baseContext;
          private bool disposed = false;
          private bool isBase = true;
  
          internal PrintGraphics(Graphics g)
              : base(g, null, Color.White, Color.Black)
          {
              baseContext = new PrintGraphicsContext();
              baseContext.Current = this;
          }
  
          public override java.awt.Graphics create()
          {
              checkState();
              myState = new NetGraphicsState();
              myState.saveGraphics(this);
              PrintGraphics newGraphics = (PrintGraphics)MemberwiseClone();
              newGraphics.myState = null;
              newGraphics.isBase = false;
              newGraphics.baseContext = baseContext;
              baseContext.Current = newGraphics; // since it is very likely that the next op will be on that graphics
              // this is similar to init
              myState.restoreGraphics(newGraphics);
              return newGraphics;
          }
  
          /// <summary>
          /// Checks whether the properties of this instance are set to the bse Graphics. If not, the context
          /// of the currently PrintGraphics is saved and the context if this instance is restored.
          /// </summary>
          private void checkState()
          {
              // this is required to simulate Graphics.create(), which is not possible in .NET
              // we simply call Save on create() an restore this state, if any method is called
              // on the current graphics. This will work for almost any use case of create()
              if (baseContext != null && baseContext.Current != this)
              {
                  if (!baseContext.Current.disposed)
                  {
                      if (baseContext.Current.myState == null)
                      {
                          baseContext.Current.myState = new NetGraphicsState(baseContext.Current);
                      }
                      else
                      {
                          baseContext.Current.myState.saveGraphics(baseContext.Current);
                      }
                  }
                  baseContext.Current = this;
                  if (myState != null) // is only null, if this instance was already disposed
                  {
                      myState.restoreGraphics(this);
                  }
              }
          }
  
          public override void copyArea(int x, int y, int width, int height, int dx, int dy)
          {
              throw new NotImplementedException();
          }
  
          public override void clearRect(int x, int y, int width, int height)
          {
              checkState();
              base.clearRect(x, y, width, height);
          }
  
          public override void clipRect(int x, int y, int w, int h)
          {
              checkState();
              base.clearRect(x, y, w, h);
          }
  
          public override void clip(java.awt.Shape shape)
          {
              checkState();
              base.clip(shape);
          }
  
          public override void dispose()
          {            
              myState = null;
              if (pen != null) pen.Dispose();
              if (brush != null) brush.Dispose();
              disposed = true;
              if (!isBase)
              {
                  // only dispose the underlying Graphics if this is the base PrintGraphics!
                  return;
              }
              base.dispose();
          }
  
          public override void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
          {
              checkState();
              base.drawArc(x, y, width, height, startAngle, arcAngle);
          }
  
          public override void drawBytes(byte[] data, int offset, int length, int x, int y)
          {
              checkState();
              base.drawBytes(data, offset, length, x, y);
          }
  
          public override void drawChars(char[] data, int offset, int length, int x, int y)
          {
              checkState();
              base.drawChars(data, offset, length, x, y);
          }
  
          public override bool drawImage(java.awt.Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, java.awt.Color color, java.awt.image.ImageObserver observer)
          {
              checkState();
              return base.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, color, observer);
          }
  
          public override bool drawImage(java.awt.Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, java.awt.image.ImageObserver observer)
          {
              checkState();
              return base.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
          }
  
          public override bool drawImage(java.awt.Image img, int x, int y, int width, int height, java.awt.Color bgcolor, java.awt.image.ImageObserver observer)
          {
              checkState();
              return base.drawImage( img, x, y, width, height, bgcolor, observer);
          }
  
          public override bool drawImage(java.awt.Image img, int x, int y, java.awt.Color bgcolor, java.awt.image.ImageObserver observer)
          {
              checkState();
              return base.drawImage(img, x, y, bgcolor, observer);
          }
  
          public override bool drawImage(java.awt.Image img, int x, int y, int width, int height, java.awt.image.ImageObserver observer)
          {
              checkState();
              return base.drawImage( img, x, y, width, height, observer);
          }
  
          public override bool drawImage(java.awt.Image img, int x, int y, java.awt.image.ImageObserver observer)
          {
              checkState();
              return base.drawImage(img, x, y, observer);
          }
  
          public override void drawLine(int x1, int y1, int x2, int y2)
          {
              checkState();
              base.drawLine(x1, y1, x2, y2);
          }
  
          public override void drawOval(int x, int y, int w, int h)
          {
              checkState();
              base.drawOval(x, y, w, h);
          }
  
          public override void drawPolygon(java.awt.Polygon polygon)
          {
              checkState();
              base.drawPolygon(polygon);
          }
  
          public override void drawPolygon(int[] aX, int[] aY, int aLength)
          {
              checkState();
              base.drawPolygon(aX, aY, aLength);
          }
  
          public override void drawPolyline(int[] aX, int[] aY, int aLength)
          {
              checkState();
              base.drawPolyline(aX, aY, aLength);
          }
  
          public override void drawRect(int x, int y, int width, int height)
          {
              checkState();
              base.drawRect(x, y, width, height);
          }
  
          public override void drawRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)
          {
              checkState();
              base.drawRoundRect(x, y, w, h, arcWidth, arcHeight);
          }
  
          public override void fill3DRect(int x, int y, int width, int height, bool raised)
          {
              checkState();
              base.fill3DRect(x, y, width, height, raised);
          }
  
          public override void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
          {
              checkState();
              base.fillArc(x, y, width, height, startAngle, arcAngle);
          }
  
          public override void fillOval(int x, int y, int w, int h)
          {
              checkState();
              base.fillOval(x, y, w, h);
          }
  
          public override void fillPolygon(java.awt.Polygon polygon)
          {
              checkState();
              base.fillPolygon(polygon);
          }
  
          public override void fillPolygon(int[] aX, int[] aY, int aLength)
          {
              checkState();
              base.fillPolygon(aX, aY, aLength);
          }
  
          public override void fillRect(int x, int y, int width, int height)
          {
              checkState();
              base.fillRect(x, y, width, height);
          }
  
          public override void fillRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)
          {
              checkState();
              base.fillRoundRect(x, y, w, h, arcWidth, arcHeight);
          }
  
          public override java.awt.Shape getClip()
          {
              checkState();
              return base.getClip();
          }
  
          public override java.awt.Rectangle getClipBounds(java.awt.Rectangle r)
          {
              checkState();
              return base.getClipBounds( r );
          }
  
          public override java.awt.Rectangle getClipBounds()
          {
              checkState();
              return base.getClipBounds();
          }
  
          [Obsolete]
          public override java.awt.Rectangle getClipRect()
          {
              checkState();
              return base.getClipRect();
          }
  
          public override java.awt.Color getColor()
          {
              checkState();
              return base.getColor();
          }
  
          public override java.awt.Font getFont()
          {
              checkState();
              return base.getFont();
          }
  
          public override java.awt.FontMetrics getFontMetrics(java.awt.Font f)
          {
              checkState();
              return base.getFontMetrics(f);
          }
  
          public override java.awt.FontMetrics getFontMetrics()
          {
              checkState();
              return base.getFontMetrics();
          }
  
          public override void setClip(int x, int y, int width, int height)
          {
              checkState();
              base.setClip(x,y,width,height);
          }
  
          public override void setClip(java.awt.Shape shape)
          {
              checkState();
              base.setClip(shape);
          }
  
          public override void setColor(java.awt.Color color)
          {
              checkState();
              base.setColor(color);
          }
  
          public override void setFont(java.awt.Font f)
          {
              checkState();
              base.setFont(f);
          }
  
          public override void setPaintMode()
          {
              checkState();
              base.setPaintMode();
          }
  
          public override void setXORMode(java.awt.Color param)
          {
              checkState();
              base.setXORMode(param);
          }
  
          public override void translate(int x, int y)
          {
              checkState();
              base.translate(x, y);
          }
  
          public override void draw(java.awt.Shape shape)
          {
              checkState();
              base.draw(shape);
          }
  
          public override bool drawImage(java.awt.Image img, java.awt.geom.AffineTransform xform, ImageObserver observer)
          {
              checkState();
              return base.drawImage(img, xform, observer);
          }
  
          public override void drawImage(java.awt.image.BufferedImage image, BufferedImageOp op, int x, int y)
          {
              checkState();
              base.drawImage(image, op, x, y);
          }
         
          public override void drawRenderedImage(java.awt.image.RenderedImage img, java.awt.geom.AffineTransform xform)
          {
              checkState();
              base.drawRenderedImage(img, xform);
          }
  
          public override void drawRenderableImage(java.awt.image.renderable.RenderableImage image, java.awt.geom.AffineTransform xform)
          {
              checkState();
              base.drawRenderableImage(image, xform);
          }
  
          public override void drawString(string str, int x, int y)
          {
              checkState();
              base.drawString(str, x, y);
          }
  
          public override void drawString(string text, float x, float y)
          {
              checkState();
              base.drawString(text, x, y);
          }
  
          public override void drawString(java.text.AttributedCharacterIterator iterator, int x, int y)
          {
              checkState();
              base.drawString(iterator, x, y);
          }
  
          public override void drawString(java.text.AttributedCharacterIterator iterator, float x, float y)
          {
              checkState();
              base.drawString(iterator, x, y);
          }
  
          public override void fill(java.awt.Shape shape)
          {
              checkState();
              base.fill(shape);
          }
  
          public override bool hit(java.awt.Rectangle rect, java.awt.Shape s, bool onStroke)
          {
              checkState();
              return base.hit(rect, s, onStroke);
          }
  
          public override java.awt.GraphicsConfiguration getDeviceConfiguration()
          {
              // no check here, since invariant
              return base.getDeviceConfiguration();
          }
  
          public override void setComposite(java.awt.Composite comp)
          {
              checkState();
              base.setComposite(comp);
          }
  
          public override void setPaint(java.awt.Paint paint)
          {
              checkState();
              base.setPaint(paint);
          }
  
          public override void setStroke(java.awt.Stroke stroke)
          {
              checkState();
              base.setStroke(stroke);
          }
  
          public override void setRenderingHint(java.awt.RenderingHints.Key hintKey, Object hintValue)
          {
              checkState();
              base.setRenderingHint(hintKey, hintValue);
          }
  
          public override object getRenderingHint(java.awt.RenderingHints.Key hintKey)
          {
              checkState();
              return base.getRenderingHint(hintKey);
          }
  
          public override void setRenderingHints(java.util.Map hints)
          {
              checkState();
              base.setRenderingHints(hints);            
          }
  
          public override void addRenderingHints(java.util.Map hints)
          {
              checkState();
              base.addRenderingHints(hints);
          }
  
          public override java.awt.RenderingHints getRenderingHints()
          {
              checkState();
              return base.getRenderingHints();
          }
  
          public override void translate(double x, double y)
          {
              checkState();
              base.translate(x, y);
          }
  
          public override void rotate(double theta)
          {
              checkState();
              base.rotate(theta);
          }
  
          public override void rotate(double theta, double x, double y)
          {
              checkState();
              base.rotate(theta, x, y);
          }
  
          public override void scale(double scaleX, double scaleY)
          {
              checkState();
              base.scale(scaleX, scaleY);
          }
  
          public override void shear(double shearX, double shearY)
          {
              checkState();
              base.shear(shearX, shearY);
          }
  
          public override void transform(java.awt.geom.AffineTransform tx)
          {
              checkState();
              base.transform(tx);
          }
  
          public override void setTransform(java.awt.geom.AffineTransform tx)
          {
              checkState();
              base.setTransform(tx);
          }
  
          public override java.awt.geom.AffineTransform getTransform()
          {
              checkState();
              return base.getTransform();
          }
  
          public override java.awt.Paint getPaint()
          {
              checkState();
              return base.getPaint();
          }
  
          public override java.awt.Composite getComposite()
          {
              checkState();
              return base.getComposite();
          }
  
          public override void setBackground(java.awt.Color color)
          {
              checkState();
              base.setBackground(color);
          }
  
          public override java.awt.Color getBackground()
          {
              checkState();
              return base.getBackground();
          }
  
          public override java.awt.Stroke getStroke()
          {
              checkState();
              return base.getStroke();
          }
  
          public override java.awt.font.FontRenderContext getFontRenderContext()
          {
              checkState();
              return base.getFontRenderContext();
          }
  
          public override void drawGlyphVector(java.awt.font.GlyphVector gv, float x, float y)
          {
              checkState();
              base.drawGlyphVector(gv, x, y);
          }
      }
  
      /// <summary>
      /// State to store/restore the state of a NetGraphics/Graphics object
      /// </summary>
      internal class NetGraphicsState
      {
          private Brush brush;
          private Pen pen;
  
          // Graphics State
          private Matrix Transform;
          private Region Clip;
          private SmoothingMode SmoothingMode;
          private PixelOffsetMode PixelOffsetMode;
          private TextRenderingHint TextRenderingHint;
          private InterpolationMode InterpolationMode;
          private CompositingMode CompositingMode;
  
          private bool savedGraphics = false;
  
          public NetGraphicsState()
          {
          }
  
          public NetGraphicsState( NetGraphics netG )
          {
              saveGraphics(netG);
          }
  
          public void saveGraphics(NetGraphics netG)
          {
              if (netG == null )
              {
                  return;
              }
              if (netG.g != null )
              {
                  this.Transform = netG.g.Transform;
                  this.Clip = netG.g.Clip;
                  this.SmoothingMode = netG.g.SmoothingMode;
                  this.PixelOffsetMode = netG.g.PixelOffsetMode;
                  this.TextRenderingHint = netG.g.TextRenderingHint;
                  this.InterpolationMode = netG.g.InterpolationMode;
                  this.CompositingMode = netG.g.CompositingMode;
                  savedGraphics = true;
              }
              if (netG.pen != null && netG.brush != null)
              {
                  pen = (Pen)netG.pen.Clone();
                  brush = (Brush)netG.brush.Clone();
              }
          }
  
          public void restoreGraphics(NetGraphics netG)
          {
              if (netG == null)
              {
                  return;
              }
              if (netG.g != null)
              {
                  if (savedGraphics)
                  {
                      netG.g.Transform = Transform;
                      netG.g.Clip = Clip;
                      netG.g.SmoothingMode = SmoothingMode;
                      netG.g.PixelOffsetMode = PixelOffsetMode;
                      netG.g.TextRenderingHint = TextRenderingHint;
                      netG.g.InterpolationMode = InterpolationMode;
                      netG.g.CompositingMode = CompositingMode;
                  }
                  else
                  {
                      // default values that Java used
                      netG.g.InterpolationMode = InterpolationMode.NearestNeighbor;
                  }
              }
              if ( pen != null && brush != null )
              {
                  netG.pen = (Pen)pen.Clone();
                  netG.brush = (Brush)brush.Clone();
              }
              else
              {
                  netG.pen = new Pen(netG.color);
                  netG.brush = new SolidBrush(netG.color);
                  netG.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
              }
          }
      }
  
      internal abstract class NetGraphics : java.awt.Graphics2D
      {
          internal Graphics g;
          internal Graphics JGraphics { get { return g; } }
          private java.awt.Color javaColor;
          private java.awt.Paint javaPaint;
          internal Color color;
          private Color bgcolor;
          private java.awt.Font font;
          private java.awt.Stroke stroke;
          private static java.awt.BasicStroke defaultStroke = new java.awt.BasicStroke();
          private Font netfont;
          internal Brush brush;
          internal Pen pen;
          private CompositeHelper composite;
          private java.awt.Composite javaComposite = java.awt.AlphaComposite.SrcOver;
          private Object textAntialiasHint;
          private Object fractionalHint = java.awt.RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT;
  
          protected NetGraphics(Graphics g, java.awt.Font font, Color fgcolor, Color bgcolor)
          {
              if (font == null)
              {
                  font = new java.awt.Font("Dialog", java.awt.Font.PLAIN, 12);
              }
              this.font = font;
              netfont = font.getNetFont();
  			this.color = fgcolor;
              this.bgcolor = bgcolor;
              composite = CompositeHelper.Create(javaComposite, g);
              init(g);
          }
  
          protected void init(Graphics graphics)
          {
              NetGraphicsState state = new NetGraphicsState();
              state.saveGraphics(this);
              g = graphics;
              state.restoreGraphics(this);            
          }
  
          /// <summary>
          /// Get the size of the graphics. This is used as a hind for some hacks.
          /// </summary>
          /// <returns></returns>
          protected virtual SizeF GetSize() {
              return g.ClipBounds.Size;
          }
  
          public override void clearRect(int x, int y, int width, int height)
          {
              using (Brush br = bgcolor != Color.Empty ? new SolidBrush(bgcolor) : brush)
              {
                  CompositingMode tempMode = g.CompositingMode;
                  g.CompositingMode = CompositingMode.SourceCopy;
                  g.FillRectangle(br, x, y, width, height);
                  g.CompositingMode = tempMode;
              }
          }
  
          public override void clipRect(int x, int y, int w, int h)
          {
              g.IntersectClip(new Rectangle(x, y, w, h));
          }
  
          public override void clip(java.awt.Shape shape)
          {
              if (shape == null)
              {
  				// note that ComponentGraphics overrides clip() to throw a NullPointerException when shape is null
  				g.ResetClip();
              }
              else
              {
                  g.IntersectClip(new Region(J2C.ConvertShape(shape)));
              }
          }
  
          public override void dispose()
          {
              if (pen!=null) pen.Dispose();
              if (brush!=null) brush.Dispose();
              g.Dispose();
          }
  
          public override void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
          {
  			g.DrawArc(pen, x, y, width, height, 360 - startAngle - arcAngle, arcAngle);
          }
  
          public override void drawBytes(byte[] data, int offset, int length, int x, int y)
          {
              char[] chars = new char[length];
              for (int i = 0; i < length; i++)
              {
                  chars[i] = (char)data[offset + i];
              }
              drawChars(chars, 0, length, x, y);
          }
  
          public override void drawChars(char[] data, int offset, int length, int x, int y)
          {
              drawString(new String(data, offset, length), x, y);
          }
  
          public override bool drawImage(java.awt.Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, java.awt.Color color, java.awt.image.ImageObserver observer)
          {
              Image image = J2C.ConvertImage(img);
              if (image == null)
              {
                  return false;
              }
              Rectangle destRect = new Rectangle(dx1, dy1, dx2 - dx1, dy2 - dy1);
              using (Brush brush = new SolidBrush(composite.GetColor(color))) {
                  g.FillRectangle(brush, destRect);
              }
  			lock (image)
  			{
                  g.DrawImage(image, destRect, sx1, sy1, sx2 - sx1, sy2 - sy1, GraphicsUnit.Pixel, composite.GetImageAttributes());
  			}
              return true;
          }
  
          public override bool drawImage(java.awt.Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, java.awt.image.ImageObserver observer)
          {
              Image image = J2C.ConvertImage(img);
              if (image == null)
              {
                  return false;
              }
              Rectangle destRect = new Rectangle(dx1, dy1, dx2 - dx1, dy2 - dy1);
  			lock (image)
  			{
                  g.DrawImage(image, destRect, sx1, sy1, sx2 - sx1, sy2 - sy1, GraphicsUnit.Pixel, composite.GetImageAttributes());
  			}
              return true;
          }
  
          public override bool drawImage(java.awt.Image img, int x, int y, int width, int height, java.awt.Color bgcolor, java.awt.image.ImageObserver observer)
          {
  			Image image = J2C.ConvertImage(img);
  			if (image == null)
  			{
  				return false;
  			}
              using (Brush brush = new SolidBrush(composite.GetColor(bgcolor))) {
                  g.FillRectangle(brush, x, y, width, height);
              }
  			lock (image)
  			{
                  g.DrawImage(image, new Rectangle( x, y, width, height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, composite.GetImageAttributes());
  			}
  			return true;
  		}
  
          public override bool drawImage(java.awt.Image img, int x, int y, java.awt.Color bgcolor, java.awt.image.ImageObserver observer)
          {
              if (img == null) {
                  return false;
              }
              return drawImage(img, x, y, img.getWidth(observer), img.getHeight(observer), bgcolor, observer);
  		}
  
          public override bool drawImage(java.awt.Image img, int x, int y, int width, int height, java.awt.image.ImageObserver observer)
          {
  			Image image = J2C.ConvertImage(img);
  			if (image == null)
  			{
  				return false;
  			}
  			lock (image)
  			{
                  g.DrawImage(image, new Rectangle(x, y, width, height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, composite.GetImageAttributes());
  			}
  			return true;
  		}
  
          public override bool drawImage(java.awt.Image img, int x, int y, java.awt.image.ImageObserver observer)
          {
              if (img == null) {
                  return false;
              }
              return drawImage(img, x, y, img.getWidth(observer), img.getHeight(observer), observer);
  		}
  
          public override void drawLine(int x1, int y1, int x2, int y2)
          {
              // HACK DrawLine doesn't appear to draw the last pixel, so for single pixel lines, we have
              // a freaky workaround
              if (x1 == x2 && y1 == y2)
              {
                  g.DrawLine(pen, x1, y1, x1 + 0.01f, y2 + 0.01f);
              }
              else
              {
                  g.DrawLine(pen, x1, y1, x2, y2);
              }
          }
  
          public override void drawOval(int x, int y, int w, int h)
          {
              g.DrawEllipse(pen, x, y, w, h);
          }
  
          public override void drawPolygon(java.awt.Polygon polygon)
          {
              drawPolygon(polygon.xpoints, polygon.ypoints, polygon.npoints);
          }
  
          public override void drawPolygon(int[] aX, int[] aY, int aLength)
          {
              Point[] points = new Point[aLength];
              for (int i = 0; i < aLength; i++)
              {
                  points[i].X = aX[i];
                  points[i].Y = aY[i];
              }
              g.DrawPolygon(pen, points);
          }
  
          /// <summary>
          /// Draw a sequence of connected lines
          /// </summary>
          /// <param name="aX">Array of x coordinates</param>
          /// <param name="aY">Array of y coordinates</param>
          /// <param name="aLength">Length of coordinate arrays</param>
          public override void drawPolyline(int[] aX, int[] aY, int aLength)
          {
              for (int i = 0; i < aLength - 1; i++)
              {
                  Point point1 = new Point(aX[i], aY[i]);
                  Point point2 = new Point(aX[i + 1], aY[i + 1]);
                  g.DrawLine(pen, point1, point2);
              }
          }
  
          public override void drawRect(int x, int y, int width, int height)
          {
              g.DrawRectangle(pen, x, y, width, height);
          }
  
          /// <summary>
          /// Apparently there is no rounded rec function in .Net. Draw the
          /// rounded rectangle by using lines and arcs.
          /// </summary>
  		public override void drawRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)
          {
      	    using (GraphicsPath gp = J2C.ConvertRoundRect(x, y, w, h, arcWidth, arcHeight))
                  g.DrawPath(pen, gp);
          }
  
          public override void fill3DRect(int x, int y, int width, int height, bool raised)
          {
              java.awt.Paint p = getPaint();
              java.awt.Color c = getColor();
              java.awt.Color brighter = c.brighter();
              java.awt.Color darker = c.darker();
  
              if( !raised ) {
                  setColor(darker);
              } else if( p != c ) {
                  setColor(c);
              }
              fillRect(x + 1, y + 1, width - 2, height - 2);
              setColor(raised ? brighter : darker);
              fillRect(x, y, 1, height);
              fillRect(x + 1, y, width - 2, 1);
              setColor(raised ? darker : brighter);
              fillRect(x + 1, y + height - 1, width - 1, 1);
              fillRect(x + width - 1, y, 1, height - 1);
              setPaint(p);
          }
  
          public override void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
          {
  			g.FillPie(brush, x, y, width, height, 360 - startAngle - arcAngle, arcAngle);
          }
  
          public override void fillOval(int x, int y, int w, int h)
          {
              g.FillEllipse(brush, x, y, w, h);
          }
  
          public override void fillPolygon(java.awt.Polygon polygon)
          {
              fillPolygon(polygon.xpoints, polygon.ypoints, polygon.npoints);
          }
  
          public override void fillPolygon(int[] aX, int[] aY, int aLength)
          {
              Point[] points = new Point[aLength];
              for (int i = 0; i < aLength; i++)
              {
                  points[i].X = aX[i];
                  points[i].Y = aY[i];
              }
              g.FillPolygon(brush, points);
          }
  
          public override void fillRect(int x, int y, int width, int height)
          {
              g.FillRectangle(brush, x, y, width, height);
          }
  
  		public override void fillRoundRect(int x, int y, int w, int h, int arcWidth, int arcHeight)
  		{
  			GraphicsPath gp = J2C.ConvertRoundRect(x, y, w, h, arcWidth, arcHeight);
  			g.FillPath(brush, gp);
  			gp.Dispose();
  		}
  
          public override java.awt.Shape getClip()
          {
              return getClipBounds();
          }
  
          public override java.awt.Rectangle getClipBounds(java.awt.Rectangle r)
          {
              using (Region clip = g.Clip)
              {
                  if (!clip.IsInfinite(g))
                  {
                      RectangleF rec = clip.GetBounds(g);
                      r.x = (int) rec.X;
                      r.y = (int) rec.Y;
                      r.width = (int) rec.Width;
                      r.height = (int) rec.Height;
                  }
                  return r;
              }
          }
  
          public override java.awt.Rectangle getClipBounds()
          {
              using (Region clip = g.Clip)
              {
                  if (clip.IsInfinite(g))
                  {
                      return null;
                  }
                  RectangleF rec = clip.GetBounds(g);
                  return C2J.ConvertRectangle(rec);
              }
          }
  
          [Obsolete]
          public override java.awt.Rectangle getClipRect()
          {
              return getClipBounds();
          }
  
          public override java.awt.Color getColor()
          {
              if (javaColor == null)
              {
                  javaColor = composite.GetColor(color);
              }
              return javaColor;
          }
  
          public override java.awt.Font getFont()
          {
              return font;
          }
  
          public override java.awt.FontMetrics getFontMetrics(java.awt.Font f)
          {
              return sun.font.FontDesignMetrics.getMetrics(f);
          }
  
          public override java.awt.FontMetrics getFontMetrics()
          {
              return sun.font.FontDesignMetrics.getMetrics(font);
          }
  
          public override void setClip(int x, int y, int width, int height)
          {
              g.Clip = new Region(new Rectangle(x, y, width, height));
          }
  
          public override void setClip(java.awt.Shape shape)
          {
              if (shape == null)
              {
                  Region clip = g.Clip;
                  clip.MakeInfinite();
                  g.Clip = clip;
              }
              else
              {
                  g.Clip = new Region(J2C.ConvertShape(shape));
              }
          }
  
          public override void setColor(java.awt.Color color)
          {
              if (color == null || color == this.javaPaint)
              {
                  // Does not change the color, if it is null like in SunGraphics2D
                  return;
              }
              this.javaPaint = this.javaColor = color;
              this.color = composite.GetColor(color);
              if (brush is SolidBrush)
              {
                  ((SolidBrush)brush).Color = this.color;
              }
              else
              {
                  brush.Dispose();
                  brush = new SolidBrush(this.color);
              }
              pen.Color = this.color;
              pen.Brush = brush;
          }
  
          public override void setFont(java.awt.Font f)
          {
              if (f != null && f != font)
              {
                  netfont = f.getNetFont();
                  font = f;
              }
          }
  
          public override void setPaintMode()
          {
              throw new NotImplementedException();
          }
  
          public override void setXORMode(java.awt.Color param)
          {
              if( param == null ) {
                  throw new java.lang.IllegalArgumentException("null XORColor");
              }
              throw new NotImplementedException();
          }
  
          public override void translate(int x, int y)
          {
              Matrix transform = g.Transform;
              transform.Translate(x, y);
              g.Transform = transform;
          }
  
          public override void draw(java.awt.Shape shape)
          {
              using (GraphicsPath gp = J2C.ConvertShape(shape))
              {
                  g.DrawPath(pen, gp);
              }
          }
  
          public override bool drawImage(java.awt.Image img, java.awt.geom.AffineTransform xform, ImageObserver observer)
          {
              if (img == null) {
                  return true;
              }
       
              if (xform == null || xform.isIdentity()) {
                  return drawImage(img, 0, 0, null, observer);
              }
  
              NetGraphics clone = (NetGraphics)create();
              clone.transform(xform);
              bool rendered = clone.drawImage(img, 0, 0, null, observer);
              clone.dispose();
              return rendered;
          }
  
          public override void drawImage(java.awt.image.BufferedImage image, BufferedImageOp op, int x, int y)
          {
  
              if( op == null ) {
                  drawImage(image, x, y, null);
              } else {
                  if( !(op is AffineTransformOp) ) {
                      drawImage(op.filter(image, null), x, y, null);
                  } else {
                      Console.WriteLine(new System.Diagnostics.StackTrace());
                      throw new NotImplementedException();
                  }
              }
          }
  
          public override void drawRenderedImage(java.awt.image.RenderedImage img, java.awt.geom.AffineTransform xform)
          {
              if (img == null) {
                  return;
              }
      
              // BufferedImage case: use a simple drawImage call
              if (img is BufferedImage) {
                  BufferedImage bufImg = (BufferedImage)img;
                  drawImage(bufImg,xform,null);
                  return;
              }            
              throw new NotImplementedException("drawRenderedImage not implemented for images which are not BufferedImages.");
          }
  
          public override void drawRenderableImage(java.awt.image.renderable.RenderableImage image, java.awt.geom.AffineTransform xform)
          {
              throw new NotImplementedException();
          }
  
          public override void drawString(string str, int x, int y)
          {
              drawString(str, (float)x, (float)y);
          }
  
          public override void drawString(String text, float x, float y)
          {
              if (text.Length == 0)
              {
                  return;
              }
              bool fractional = isFractionalMetrics();
              StringFormat format = new StringFormat(StringFormat.GenericTypographic);
              format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoWrap | StringFormatFlags.FitBlackBox;
              format.Trimming = StringTrimming.None;
              if (fractional || !sun.font.StandardGlyphVector.isSimpleString(font, text))
              {
                  g.DrawString(text, netfont, brush, x, y - font.getSize(), format);
              }
              else
              {
                  // fixed metric for simple text, we position every character to simulate the Java behaviour
                  java.awt.font.FontRenderContext frc = new java.awt.font.FontRenderContext(null, isAntiAlias(), fractional);
                  sun.font.FontDesignMetrics metrics = sun.font.FontDesignMetrics.getMetrics(font, frc);
                  y -= font.getSize();
                  for (int i = 0; i < text.Length; i++)
                  {
                      g.DrawString(text.Substring(i, 1), netfont, brush, x, y, format);
                      x += metrics.charWidth(text[i]);
                  }
              }
          }
  
          public override void drawString(java.text.AttributedCharacterIterator iterator, int x, int y)
          {
              drawString(iterator, (float) x, (float) y);
          }
  
          public override void drawString(java.text.AttributedCharacterIterator iterator, float x, float y)
          {
              if( iterator == null ) {
                  throw new java.lang.NullPointerException("AttributedCharacterIterator is null");
              }
              if( iterator.getBeginIndex() == iterator.getEndIndex() ) {
                  return; /* nothing to draw */
              }
              java.awt.font.TextLayout tl = new java.awt.font.TextLayout(iterator, getFontRenderContext());
              tl.draw(this, x, y);
          }
  
          public override void fill(java.awt.Shape shape)
          {
              g.FillPath(brush, J2C.ConvertShape(shape));
          }
  
          public override bool hit(java.awt.Rectangle rect, java.awt.Shape s, bool onStroke)
          {
              if (onStroke)
              {
                  //TODO use stroke
                  //s = stroke.createStrokedShape(s);
              }
              return s.intersects(rect);
          }
  
          public override java.awt.GraphicsConfiguration getDeviceConfiguration()
          {
  			return new NetGraphicsConfiguration(Screen.PrimaryScreen);
          }
  
          public override void setComposite(java.awt.Composite comp)
          {
              if (comp == null)
              {
                  throw new java.lang.IllegalArgumentException("null Composite");
              }
              this.javaComposite = comp;
              composite = CompositeHelper.Create(comp,g);
              java.awt.Paint oldPaint = javaPaint;
              javaPaint = null;
              setPaint(oldPaint);
          }
  
          public override void setPaint(java.awt.Paint paint)
          {
              if (paint is java.awt.Color)
              {
                  setColor((java.awt.Color)paint);
                  return;
              }
  
              if (paint == null || this.javaPaint == paint)
              {
                  return;
              }
              this.javaPaint = paint;
  
              if (paint is java.awt.GradientPaint)
              {
                  java.awt.GradientPaint gradient = (java.awt.GradientPaint)paint;
                  LinearGradientBrush linear;
                  if (gradient.isCyclic())
                  {
                      linear = new LinearGradientBrush(
                          J2C.ConvertPoint(gradient.getPoint1()),
                          J2C.ConvertPoint(gradient.getPoint2()),
                          composite.GetColor(gradient.getColor1()),
                          composite.GetColor(gradient.getColor2()));
                  }
                  else
                  {
                      //HACK because .NET does not support continue gradient like Java else Tile Gradient
                      //that we receize the rectangle very large (factor z) and set 4 color values
                      // a exact solution will calculate the size of the Graphics with the current transform
                      Color color1 = composite.GetColor(gradient.getColor1());
                      Color color2 = composite.GetColor(gradient.getColor2());
                      float x1 = (float)gradient.getPoint1().getX();
                      float x2 = (float)gradient.getPoint2().getX();
                      float y1 = (float)gradient.getPoint1().getY();
                      float y2 = (float)gradient.getPoint2().getY();
                      float diffX = x2 - x1;
                      float diffY = y2 - y1;
                      const float z = 60; //HACK zoom factor, with a larger factor .NET will make the gradient wider.
                      linear = new LinearGradientBrush(
                          new PointF(x1 - z * diffX, y1 - z * diffY),
                          new PointF(x2 + z * diffX, y2 + z * diffY),
                          color1,
                          color1);
                      ColorBlend colorBlend = new ColorBlend(4);
                      Color[] colors = colorBlend.Colors;
                      colors[0] = colors[1] = color1;
                      colors[2] = colors[3] = color2;
                      float[] positions = colorBlend.Positions;
  
gt:                     positions[1] = z / (2 * z + 1);