diff dwtx/draw2d/SWTGraphics.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children 1082a0fc2bb8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/SWTGraphics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,1220 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.draw2d.SWTGraphics;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+static import dwt.graphics.Rectangle;
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Path;
+import dwt.graphics.Pattern;
+import dwt.graphics.TextLayout;
+import dwt.graphics.Transform;
+import dwt.widgets.Display;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Graphics;
+
+/**
+ * A concrete implementation of <code>Graphics</code> using an DWT
+ * <code>GC</code>. There are 2 states contained in this graphics class -- the
+ * applied state which is the actual state of the GC and the current state which
+ * is the current state of this graphics object.  Certain properties can be
+ * changed multiple times and the GC won't be updated until it's actually used.
+ * <P>
+ * WARNING: This class is not intended to be subclassed.
+ */
+public class SWTGraphics
+    : Graphics
+{
+
+/**
+ * An internal type used to represent and update the GC's clipping.
+ * @since 3.1
+ */
+interface Clipping {
+    /**
+     * Sets the clip's bounding rectangle into the provided argument and returns it for convenince.
+     * @param rect the rect
+     * @return the given rect
+     * @since 3.1
+     */
+    Rectangle getBoundingBox(Rectangle rect);
+    Clipping getCopy();
+    void intersect(int left, int top, int right, int bottom);
+    void scale(float horizontal, float vertical);
+    void setOn(GC gc, int translateX, int translateY);
+    void translate(float dx, float dy);
+}
+
+/**
+ * Any state stored in this class is only applied when it is needed by a
+ * specific graphics call.
+ * @since 3.1
+ */
+static class LazyState {
+    Color bgColor;
+    Color fgColor;
+    Font font;
+    int graphicHints;
+    int lineWidth;
+    Clipping relativeClip;
+}
+
+static class RectangleClipping : Clipping {
+    private float top, left, bottom, right;
+
+    this(float left, float top, float right, float bottom) {
+        this.left = left;
+        this.right = right;
+        this.bottom = bottom;
+        this.top = top;
+    }
+
+    this(dwt.graphics.Rectangle.Rectangle rect) {
+        left = rect.x;
+        top = rect.y;
+        right = rect.x + rect.width;
+        bottom = rect.y + rect.height;
+    }
+
+    this(Rectangle rect) {
+        left = rect.x;
+        top = rect.y;
+        right = rect.right();
+        bottom = rect.bottom();
+    }
+
+    public Rectangle getBoundingBox(Rectangle rect) {
+        rect.x = cast(int)left;
+        rect.y = cast(int)top;
+        rect.width = cast(int)Math.ceil(right) - rect.x;
+        rect.height = cast(int)Math.ceil(bottom) - rect.y;
+        return rect;
+    }
+
+    public Clipping getCopy() {
+        return new RectangleClipping(left, top, right, bottom);
+    }
+
+    public void intersect(int left, int top, int right, int bottom) {
+        this.left = Math.max(this.left, left);
+        this.right = Math.min(this.right, right);
+        this.top = Math.max(this.top, top);
+        this.bottom = Math.min(this.bottom, bottom);
+        if (right < left || bottom < top) {
+            //width and height of -1 to avoid ceiling function from re-adding a pixel.
+            this.right = left - 1;
+            this.bottom = top - 1;
+        }
+    }
+
+    public void scale(float horz, float vert) {
+        left /= horz;
+        right /= horz;
+        top /= vert;
+        bottom /= vert;
+    }
+
+    public void setOn(GC gc, int translateX, int translateY) {
+        int xInt = cast(int)Math.floor(left);
+        int yInt = cast(int)Math.floor(top);
+        gc.setClipping(xInt + translateX, yInt + translateY,
+                cast(int)Math.ceil(right) - xInt,
+                cast(int)Math.ceil(bottom) - yInt);
+    }
+
+    public void translate(float dx, float dy) {
+        left += dx;
+        right += dx;
+        top += dy;
+        bottom += dy;
+    }
+}
+
+/**
+ * Contains the entire state of the Graphics.
+ */
+static class State
+    : LazyState
+    , Cloneable
+{
+    float affineMatrix[];
+    int alpha;
+    Pattern bgPattern;
+    int dx, dy;
+
+    Pattern fgPattern;
+    int lineDash[];
+
+    this(){
+    }
+
+    public Object clone() {
+        auto res = new State;
+        res.affineMatrix = this.affineMatrix.dup;
+        res.alpha        = this.alpha;
+        res.bgPattern    = this.bgPattern;
+        res.dx           = this.dx;
+        res.dy           = this.dy;
+        res.fgPattern    = this.fgPattern;
+        res.lineDash     = this.lineDash.dup;
+        return res;
+    }
+
+    /**
+     * Copies all state information from the given State to this State
+     * @param state The State to copy from
+     */
+    public void copyFrom(State state) {
+        bgColor = state.bgColor;
+        fgColor = state.fgColor;
+        lineWidth = state.lineWidth;
+        dx = state.dx;
+        dy = state.dy;
+        bgPattern = state.bgPattern;
+        fgPattern = state.fgPattern;
+        font = state.font;
+        lineDash = state.lineDash;
+        graphicHints = state.graphicHints;
+        affineMatrix = state.affineMatrix;
+        relativeClip = state.relativeClip;
+        alpha = state.alpha;
+    }
+}
+
+static const int AA_MASK;
+static const int AA_SHIFT;
+static const int AA_WHOLE_NUMBER = 1;
+static const int ADVANCED_GRAPHICS_MASK;
+static const int ADVANCED_HINTS_DEFAULTS;
+static const int ADVANCED_HINTS_MASK;
+static const int ADVANCED_SHIFT;
+static const int CAP_MASK;
+static const int CAP_SHIFT;
+static const int FILL_RULE_MASK;
+static const int FILL_RULE_SHIFT;
+static const int FILL_RULE_WHOLE_NUMBER = -1;
+static const int INTERPOLATION_MASK;
+static const int INTERPOLATION_SHIFT;
+static const int INTERPOLATION_WHOLE_NUMBER = 1;
+static const int JOIN_MASK;
+static const int JOIN_SHIFT;
+static const int LINE_STYLE_MASK;
+
+static const int TEXT_AA_MASK;
+static const int TEXT_AA_SHIFT;
+static const int XOR_MASK;
+static const int XOR_SHIFT;
+
+static this() {
+    XOR_SHIFT = 3;
+    CAP_SHIFT = 4;
+    JOIN_SHIFT = 6;
+    AA_SHIFT = 8;
+    TEXT_AA_SHIFT = 10;
+    INTERPOLATION_SHIFT = 12;
+    FILL_RULE_SHIFT = 14;
+    ADVANCED_SHIFT = 15;
+
+    LINE_STYLE_MASK = 7;
+    AA_MASK = 3 << AA_SHIFT;
+    CAP_MASK = 3 << CAP_SHIFT;
+    FILL_RULE_MASK = 1 << FILL_RULE_SHIFT;
+    INTERPOLATION_MASK = 3 << INTERPOLATION_SHIFT;
+    JOIN_MASK = 3 << JOIN_SHIFT;
+    TEXT_AA_MASK = 3 << TEXT_AA_SHIFT;
+    XOR_MASK = 1 << XOR_SHIFT;
+    ADVANCED_GRAPHICS_MASK = 1 << ADVANCED_SHIFT;
+
+    ADVANCED_HINTS_MASK = TEXT_AA_MASK | AA_MASK | INTERPOLATION_MASK;
+    ADVANCED_HINTS_DEFAULTS = ((DWT.DEFAULT + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT)
+            | ((DWT.DEFAULT + AA_WHOLE_NUMBER) << AA_SHIFT)
+            | ((DWT.DEFAULT + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT);
+}
+
+private const LazyState appliedState;
+private const State currentState;
+
+private bool elementsNeedUpdate;
+private GC gc;
+
+private bool sharedClipping;
+private List stack;
+
+private int stackPointer = 0;
+Transform transform;
+private int translateX = 0;
+private int translateY = 0;
+
+/**
+ * Constructs a new SWTGraphics that draws to the Canvas using the given GC.
+ * @param gc the GC
+ */
+public this(GC gc) {
+    appliedState = new LazyState();
+    currentState = new State();
+    stack = new ArrayList();
+    this.gc = gc;
+    init();
+}
+
+/**
+ * If the background color has changed, this change will be pushed to the GC.  Also calls
+ * {@link #checkGC()}.
+ */
+protected final void checkFill() {
+    if (!currentState.bgColor.opEquals(appliedState.bgColor) && currentState.bgPattern is null)
+        gc.setBackground(appliedState.bgColor = currentState.bgColor);
+    checkGC();
+}
+
+/**
+ * If the rendering hints or the clip region has changed, these changes will be pushed to
+ * the GC. Rendering hints include anti-alias, xor, join, cap, line style, fill rule,
+ * interpolation, and other settings.
+ */
+protected /+final+/ void checkGC() {
+    if (appliedState.relativeClip !is currentState.relativeClip) {
+        appliedState.relativeClip = currentState.relativeClip;
+        currentState.relativeClip.setOn(gc, translateX, translateY);
+    }
+    if (appliedState.graphicHints !is currentState.graphicHints) {
+        reconcileHints(appliedState.graphicHints, currentState.graphicHints);
+        appliedState.graphicHints = currentState.graphicHints;
+    }
+}
+
+/**
+ * If the line width, line style, foreground or background colors have changed, these
+ * changes will be pushed to the GC.  Also calls {@link #checkGC()}.
+ */
+protected /+final+/ void checkPaint() {
+    checkGC();
+    if (!currentState.fgColor.opEquals(appliedState.fgColor) && currentState.fgPattern is null)
+        gc.setForeground(appliedState.fgColor = currentState.fgColor);
+    if (appliedState.lineWidth !is currentState.lineWidth)
+        gc.setLineWidth(appliedState.lineWidth = currentState.lineWidth);
+    if (!currentState.bgColor.opEquals(appliedState.bgColor) && currentState.bgPattern is null)
+        gc.setBackground(appliedState.bgColor = currentState.bgColor);
+}
+
+/**
+ * @since 3.1
+ */
+private void checkSharedClipping() {
+    if (sharedClipping) {
+        sharedClipping = false;
+
+        bool previouslyApplied = (appliedState is currentState.relativeClip);
+        currentState.relativeClip = currentState.relativeClip.getCopy();
+        if (previouslyApplied)
+            appliedState.relativeClip = currentState.relativeClip;
+    }
+}
+
+/**
+ * If the font has changed, this change will be pushed to the GC.  Also calls
+ * {@link #checkPaint()} and {@link #checkFill()}.
+ */
+protected /+final+/ void checkText() {
+    checkPaint();
+    if (!appliedState.font.opEquals(currentState.font))
+        gc.setFont(appliedState.font = currentState.font);
+}
+
+/**
+ * @see Graphics#clipRect(Rectangle)
+ */
+public void clipRect(Rectangle rect) {
+    if (currentState.relativeClip is null)
+        throw new IllegalStateException("The current clipping area does not " //$NON-NLS-1$
+        "support intersection."); //$NON-NLS-1$
+    checkSharedClipping();
+    currentState.relativeClip.intersect(rect.x, rect.y, rect.right(), rect.bottom());
+    appliedState.relativeClip = null;
+}
+
+/**
+ * @see Graphics#dispose()
+ */
+public void dispose() {
+    while (stackPointer > 0)
+        popState();
+    if (transform !is null)
+        transform.dispose();
+}
+
+/**
+ * @see Graphics#drawArc(int, int, int, int, int, int)
+ */
+public void drawArc(int x, int y, int width, int height, int offset, int length) {
+    checkPaint();
+    gc.drawArc(x + translateX, y + translateY, width, height, offset, length);
+}
+
+/**
+ * @see Graphics#drawFocus(int, int, int, int)
+ */
+public void drawFocus(int x, int y, int w, int h) {
+    checkPaint();
+    gc.drawFocus(x + translateX, y + translateY, w + 1, h + 1);
+}
+
+/**
+ * @see Graphics#drawImage(Image, int, int)
+ */
+public void drawImage(Image srcImage, int x, int y) {
+    checkGC();
+    gc.drawImage(srcImage, x + translateX, y + translateY);
+}
+
+/**
+ * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int)
+ */
+public void drawImage(Image srcImage,
+        int x1, int y1, int w1, int h1,
+        int x2, int y2, int w2, int h2) {
+    checkGC();
+    gc.drawImage(srcImage, x1, y1, w1, h1, x2 + translateX, y2 + translateY, w2, h2);
+}
+
+/**
+ * @see Graphics#drawLine(int, int, int, int)
+ */
+public void drawLine(int x1, int y1, int x2, int y2) {
+    checkPaint();
+    gc.drawLine(x1 + translateX, y1 + translateY, x2 + translateX, y2 + translateY);
+}
+
+/**
+ * @see Graphics#drawOval(int, int, int, int)
+ */
+public void drawOval(int x, int y, int width, int height) {
+    checkPaint();
+    gc.drawOval(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#drawPath(Path)
+ */
+public void drawPath(Path path) {
+    checkPaint();
+    initTransform(false);
+    gc.drawPath(path);
+}
+
+/**
+ * @see Graphics#drawPoint(int, int)
+ */
+public void drawPoint(int x, int y) {
+    checkPaint();
+    gc.drawPoint(x + translateX, y + translateY);
+}
+
+/**
+ * @see Graphics#drawPolygon(int[])
+ */
+public void drawPolygon(int[] points) {
+    checkPaint();
+    try {
+        translatePointArray(points, translateX, translateY);
+        gc.drawPolygon(points);
+    } finally {
+        translatePointArray(points, -translateX, -translateY);
+    }
+}
+
+/**
+ * @see Graphics#drawPolygon(PointList)
+ */
+public void drawPolygon(PointList points) {
+    drawPolygon(points.toIntArray());
+}
+
+/**
+ * @see Graphics#drawPolyline(int[])
+ */
+public void drawPolyline(int[] points) {
+    checkPaint();
+    try {
+        translatePointArray(points, translateX, translateY);
+        gc.drawPolyline(points);
+    } finally {
+        translatePointArray(points, -translateX, -translateY);
+    }
+}
+
+/**
+ * @see Graphics#drawPolyline(PointList)
+ */
+public void drawPolyline(PointList points) {
+    drawPolyline(points.toIntArray());
+}
+
+/**
+ * @see Graphics#drawRectangle(int, int, int, int)
+ */
+public void drawRectangle(int x, int y, int width, int height) {
+    checkPaint();
+    gc.drawRectangle(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * @see Graphics#drawRoundRectangle(Rectangle, int, int)
+ */
+public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    checkPaint();
+    gc.drawRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height,
+                            arcWidth, arcHeight);
+}
+
+/**
+ * @see Graphics#drawString(String, int, int)
+ */
+public void drawString(String s, int x, int y) {
+    checkText();
+    gc.drawString(s, x + translateX, y + translateY, true);
+}
+
+/**
+ * @see Graphics#drawText(String, int, int)
+ */
+public void drawText(String s, int x, int y) {
+    checkText();
+    gc.drawText(s, x + translateX, y + translateY, true);
+}
+
+/**
+ * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, Color)
+ */
+public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart,
+        int selectionEnd, Color selectionForeground, Color selectionBackground) {
+    //$TODO probably just call checkPaint since Font and BG color don't apply
+    checkText();
+    layout.draw(gc, x + translateX, y + translateY, selectionStart, selectionEnd,
+            selectionForeground, selectionBackground);
+}
+
+/**
+ * @see Graphics#fillArc(int, int, int, int, int, int)
+ */
+public void fillArc(int x, int y, int width, int height, int offset, int length) {
+    checkFill();
+    gc.fillArc(x + translateX, y + translateY, width, height, offset, length);
+}
+
+/**
+ * @see Graphics#fillGradient(int, int, int, int, bool)
+ */
+public void fillGradient(int x, int y, int w, int h, bool vertical) {
+    checkPaint();
+    gc.fillGradientRectangle(x + translateX, y + translateY, w, h, vertical);
+}
+
+/**
+ * @see Graphics#fillOval(int, int, int, int)
+ */
+public void fillOval(int x, int y, int width, int height) {
+    checkFill();
+    gc.fillOval(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#fillPath(Path)
+ */
+public void fillPath(Path path) {
+    checkFill();
+    initTransform(false);
+    gc.fillPath(path);
+}
+
+/**
+ * @see Graphics#fillPolygon(int[])
+ */
+public void fillPolygon(int[] points) {
+    checkFill();
+    try {
+        translatePointArray(points, translateX, translateY);
+        gc.fillPolygon(points);
+    } finally {
+        translatePointArray(points, -translateX, -translateY);
+    }
+}
+
+/**
+ * @see Graphics#fillPolygon(PointList)
+ */
+public void fillPolygon(PointList points) {
+    fillPolygon(points.toIntArray());
+}
+
+/**
+ * @see Graphics#fillRectangle(int, int, int, int)
+ */
+public void fillRectangle(int x, int y, int width, int height) {
+    checkFill();
+    gc.fillRectangle(x + translateX, y + translateY, width, height);
+}
+
+/**
+ * @see Graphics#fillRoundRectangle(Rectangle, int, int)
+ */
+public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    checkFill();
+    gc.fillRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height,
+                            arcWidth, arcHeight);
+}
+
+/**
+ * @see Graphics#fillString(String, int, int)
+ */
+public void fillString(String s, int x, int y) {
+    checkText();
+    gc.drawString(s, x + translateX, y + translateY, false);
+}
+
+/**
+ * @see Graphics#fillText(String, int, int)
+ */
+public void fillText(String s, int x, int y) {
+    checkText();
+    gc.drawText(s, x + translateX, y + translateY, false);
+}
+
+/**
+ * @see Graphics#getAlpha()
+ */
+public int getAlpha() {
+    return currentState.alpha;
+}
+
+/**
+ * @see Graphics#getAntialias()
+ */
+public int getAntialias() {
+    return ((currentState.graphicHints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getBackgroundColor()
+ */
+public Color getBackgroundColor() {
+    return currentState.bgColor;
+}
+
+/**
+ * @see Graphics#getClip(Rectangle)
+ */
+public Rectangle getClip(Rectangle rect) {
+    if (currentState.relativeClip !is null) {
+        currentState.relativeClip.getBoundingBox(rect);
+        return rect;
+    }
+    throw new IllegalStateException(
+            "Clipping can no longer be queried due to transformations"); //$NON-NLS-1$
+}
+
+/**
+ * @see Graphics#getFillRule()
+ */
+public int getFillRule() {
+    return ((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getFont()
+ */
+public Font getFont() {
+    return currentState.font;
+}
+
+/**
+ * @see Graphics#getFontMetrics()
+ */
+public FontMetrics getFontMetrics() {
+    checkText();
+    return gc.getFontMetrics();
+}
+
+/**
+ * @see Graphics#getForegroundColor()
+ */
+public Color getForegroundColor() {
+    return currentState.fgColor;
+}
+
+/**
+ * @see Graphics#getInterpolation()
+ */
+public int getInterpolation() {
+    return ((currentState.graphicHints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT)
+            - INTERPOLATION_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getLineCap()
+ */
+public int getLineCap() {
+    return (currentState.graphicHints & CAP_MASK) >> CAP_SHIFT;
+}
+
+/**
+ * @see Graphics#getLineJoin()
+ */
+public int getLineJoin() {
+    return (currentState.graphicHints & JOIN_MASK) >> JOIN_SHIFT;
+}
+
+/**
+ * @see Graphics#getLineStyle()
+ */
+public int getLineStyle() {
+    return currentState.graphicHints & LINE_STYLE_MASK;
+}
+
+/**
+ * @see Graphics#getLineWidth()
+ */
+public int getLineWidth() {
+    return currentState.lineWidth;
+}
+
+/**
+ * @see Graphics#getTextAntialias()
+ */
+public int getTextAntialias() {
+    return ((currentState.graphicHints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER;
+}
+
+/**
+ * @see Graphics#getXORMode()
+ */
+public bool getXORMode() {
+    return (currentState.graphicHints & XOR_MASK) !is 0;
+}
+
+/**
+ * Called by constructor, initializes all State information for currentState
+ */
+protected void init() {
+    currentState.bgColor = appliedState.bgColor = gc.getBackground();
+    currentState.fgColor = appliedState.fgColor = gc.getForeground();
+    currentState.font = appliedState.font = gc.getFont();
+    currentState.lineWidth = appliedState.lineWidth = gc.getLineWidth();
+    currentState.graphicHints |= gc.getLineStyle();
+    currentState.graphicHints |= gc.getLineCap() << CAP_SHIFT;
+    currentState.graphicHints |= gc.getLineJoin() << JOIN_SHIFT;
+    currentState.graphicHints |= gc.getAdvanced() ? ADVANCED_GRAPHICS_MASK : 0;
+    currentState.graphicHints |= gc.getXORMode() ? XOR_MASK : 0;
+
+    appliedState.graphicHints = currentState.graphicHints;
+
+    currentState.relativeClip = new RectangleClipping(gc.getClipping());
+    currentState.lineDash = gc.getLineDash();
+    currentState.alpha = gc.getAlpha();
+}
+
+private void initTransform(bool force) {
+    if (!force && translateX is 0 && translateY is 0)
+        return;
+    if (transform is null) {
+        transform = new Transform(Display.getCurrent());
+        elementsNeedUpdate = true;
+        transform.translate(translateX, translateY);
+        translateX = 0;
+        translateY = 0;
+        gc.setTransform(transform);
+        currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    }
+}
+
+/**
+ * @see Graphics#popState()
+ */
+public void popState() {
+    stackPointer--;
+    restoreState(cast(State)stack.get(stackPointer));
+}
+
+/**
+ * @see Graphics#pushState()
+ */
+public void pushState() {
+    if (currentState.relativeClip is null)
+        throw new IllegalStateException("The clipping has been modified in" //$NON-NLS-1$
+                "a way that cannot be saved and restored."); //$NON-NLS-1$
+//     try {
+        State s;
+        currentState.dx = translateX;
+        currentState.dy = translateY;
+
+        if (elementsNeedUpdate) {
+            elementsNeedUpdate = false;
+            transform.getElements(currentState.affineMatrix = new float[6]);
+        }
+        if (stack.size() > stackPointer) {
+            s = cast(State)stack.get(stackPointer);
+            s.copyFrom(currentState);
+        } else {
+            stack.add(currentState.clone());
+        }
+        sharedClipping = true;
+        stackPointer++;
+//     } catch (CloneNotSupportedException e) {
+//         throw new RuntimeException(e);
+//     }
+}
+
+private void reconcileHints(int applied, int hints) {
+    if (applied !is hints) {
+        int changes = hints ^ applied;
+
+        if ((changes & LINE_STYLE_MASK) !is 0)
+            gc.setLineStyle(hints & LINE_STYLE_MASK);
+
+        if ((changes & XOR_MASK) !is 0)
+            gc.setXORMode((hints & XOR_MASK) !is 0);
+
+        //Check to see if there is anything remaining
+        changes &= ~(XOR_MASK | LINE_STYLE_MASK);
+        if (changes is 0)
+            return;
+
+        if ((changes & JOIN_MASK) !is 0)
+            gc.setLineJoin((hints & JOIN_MASK) >> JOIN_SHIFT);
+        if ((changes & CAP_MASK) !is 0)
+            gc.setLineCap((hints & CAP_MASK) >> CAP_SHIFT);
+
+        if ((changes & INTERPOLATION_MASK) !is 0)
+            gc.setInterpolation(((hints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT) - INTERPOLATION_WHOLE_NUMBER);
+
+        if ((changes & FILL_RULE_MASK) !is 0)
+            gc.setFillRule(((hints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER);
+
+        if ((changes & AA_MASK) !is 0)
+            gc.setAntialias(((hints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER);
+        if ((changes & TEXT_AA_MASK) !is 0)
+            gc.setTextAntialias(((hints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER);
+
+        // If advanced was flagged, but none of the conditions which trigger advanced
+        // actually got applied, force advanced graphics on.
+        if ((changes & ADVANCED_GRAPHICS_MASK) !is 0) {
+            if ((hints & ADVANCED_GRAPHICS_MASK) !is 0 && !gc.getAdvanced())
+                gc.setAdvanced(true);
+        }
+    }
+}
+
+/**
+ * @see Graphics#restoreState()
+ */
+public void restoreState() {
+    restoreState(cast(State)stack.get(stackPointer - 1));
+}
+
+/**
+ * Sets all State information to that of the given State, called by restoreState()
+ * @param s the State
+ */
+protected void restoreState(State s) {
+    /*
+     * We must set the transformation matrix first since it affects things like clipping
+     * regions and patterns.
+     */
+    setAffineMatrix(s.affineMatrix);
+    currentState.relativeClip = s.relativeClip;
+    sharedClipping = true;
+
+    //If the GC is currently advanced, but it was not when pushed, revert
+    if (gc.getAdvanced() && (s.graphicHints & ADVANCED_GRAPHICS_MASK) is 0) {
+        //Set applied clip to null to force a re-setting of the clipping.
+        appliedState.relativeClip = null;
+        gc.setAdvanced(false);
+        appliedState.graphicHints &= ~ADVANCED_HINTS_MASK;
+        appliedState.graphicHints |= ADVANCED_HINTS_DEFAULTS;
+    }
+
+    setBackgroundColor(s.bgColor);
+    setBackgroundPattern(s.bgPattern);
+
+    setForegroundColor(s.fgColor);
+    setForegroundPattern(s.fgPattern);
+
+    setAlpha(s.alpha);
+    setLineWidth(s.lineWidth);
+    setFont(s.font);
+    //This method must come last because above methods will incorrectly set advanced state
+    setGraphicHints(s.graphicHints);
+
+    translateX = currentState.dx = s.dx;
+    translateY = currentState.dy = s.dy;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#rotate(float)
+ */
+public void rotate(float degrees) {
+    //Flush clipping, patter, etc., before applying transform
+    checkGC();
+    initTransform(true);
+    transform.rotate(degrees);
+    gc.setTransform(transform);
+    elementsNeedUpdate = true;
+
+    //Can no longer operate or maintain clipping
+    appliedState.relativeClip = currentState.relativeClip = null;
+}
+
+/**
+ * @see Graphics#scale(double)
+ */
+public void scale(double factor) {
+    scale(cast(float)factor, cast(float)factor);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see dwtx.draw2d.Graphics#scale(float, float)
+ */
+public void scale(float horizontal, float vertical) {
+    //Flush any clipping before scaling
+    checkGC();
+
+    initTransform(true);
+    transform.scale(horizontal, vertical);
+    gc.setTransform(transform);
+    elementsNeedUpdate = true;
+
+    checkSharedClipping();
+    if (currentState.relativeClip !is null)
+        currentState.relativeClip.scale(horizontal, vertical);
+}
+
+private void setAffineMatrix(float[] m) {
+    if (!elementsNeedUpdate && currentState.affineMatrix is m)
+        return;
+    currentState.affineMatrix = m;
+    if (m !is null)
+        transform.setElements(m[0], m[1], m[2], m[3], m[4], m[5]);
+    else if (transform !is null) {
+        transform.dispose();
+        transform = null;
+        elementsNeedUpdate = false;
+    }
+    gc.setTransform(transform);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setAlpha(int)
+ */
+public void setAlpha(int alpha) {
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    if (currentState.alpha !is alpha)
+        gc.setAlpha(this.currentState.alpha = alpha);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setAntialias(int)
+ */
+public void setAntialias(int value) {
+    currentState.graphicHints &= ~AA_MASK;
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << AA_SHIFT;
+}
+
+/**
+ * @see Graphics#setBackgroundColor(Color)
+ */
+public void setBackgroundColor(Color color) {
+    currentState.bgColor = color;
+    if (currentState.bgPattern !is null) {
+        currentState.bgPattern = null;
+        //Force the BG color to be stale
+        appliedState.bgColor = null;
+    }
+}
+
+/**
+ * @see Graphics#setBackgroundPattern(Pattern)
+ */
+public void setBackgroundPattern(Pattern pattern) {
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    if (currentState.bgPattern is pattern)
+        return;
+    currentState.bgPattern = pattern;
+
+    if (pattern !is null)
+        initTransform(true);
+    gc.setBackgroundPattern(pattern);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setClip(Path)
+ */
+public void setClip(Path path) {
+    initTransform(false);
+    gc.setClipping(path);
+    appliedState.relativeClip = currentState.relativeClip = null;
+}
+
+/**
+ * @see Graphics#setClip(Rectangle)
+ */
+public void setClip(Rectangle rect) {
+    currentState.relativeClip = new RectangleClipping(rect);
+}
+
+/**
+ * @see Graphics#setFillRule(int)
+ */
+public void setFillRule(int rule) {
+    currentState.graphicHints &= ~FILL_RULE_MASK;
+    currentState.graphicHints |= (rule + FILL_RULE_WHOLE_NUMBER) << FILL_RULE_SHIFT;
+}
+
+/**
+ * @see Graphics#setFont(Font)
+ */
+public void setFont(Font f) {
+    currentState.font = f;
+}
+
+/**
+ * @see Graphics#setForegroundColor(Color)
+ */
+public void setForegroundColor(Color color) {
+    currentState.fgColor = color;
+    if (currentState.fgPattern !is null) {
+        currentState.fgPattern = null;
+        //Force fgColor to be stale
+        appliedState.fgColor = null;
+    }
+}
+
+/**
+ * @see Graphics#setForegroundPattern(Pattern)
+ */
+public void setForegroundPattern(Pattern pattern) {
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK;
+    if (currentState.fgPattern is pattern)
+        return;
+    currentState.fgPattern = pattern;
+
+    if (pattern !is null)
+        initTransform(true);
+    gc.setForegroundPattern(pattern);
+}
+
+private void setGraphicHints(int hints) {
+    currentState.graphicHints = hints;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setInterpolation(int)
+ */
+public void setInterpolation(int interpolation) {
+    //values range [-1, 3]
+    currentState.graphicHints &= ~INTERPOLATION_MASK;
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (interpolation + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT;
+}
+
+/**
+ * @see Graphics#setLineCap(int)
+ */
+public void setLineCap(int cap) {
+    currentState.graphicHints &= ~CAP_MASK;
+    currentState.graphicHints |= cap << CAP_SHIFT;
+}
+
+/**
+ * @see Graphics#setLineDash(int[])
+ */
+public void setLineDash(int[] dashes) {
+    if (dashes !is null) {
+        int copy[] = new int[dashes.length];
+        for (int i = 0; i < dashes.length; i++) {
+            int dash = dashes[i];
+            if (dash <= 0)
+                DWT.error(DWT.ERROR_INVALID_ARGUMENT);
+            copy[i] = dash;
+        }
+        currentState.lineDash = copy;
+        setLineStyle(DWT.LINE_CUSTOM);
+        gc.setLineDash(copy);
+    } else {
+        currentState.lineDash = null;
+        setLineStyle(DWT.LINE_SOLID);
+    }
+}
+
+/**
+ * @see Graphics#setLineJoin(int)
+ */
+public void setLineJoin(int join) {
+    currentState.graphicHints &= ~JOIN_MASK;
+    currentState.graphicHints |= join << JOIN_SHIFT;
+}
+
+/**
+ * @see Graphics#setLineStyle(int)
+ */
+public void setLineStyle(int style) {
+    currentState.graphicHints &= ~LINE_STYLE_MASK;
+    currentState.graphicHints |= style;
+}
+
+/**
+ * @see Graphics#setLineWidth(int)
+ */
+public void setLineWidth(int width) {
+    currentState.lineWidth = width;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#setTextAntialias(int)
+ */
+public void setTextAntialias(int value) {
+    currentState.graphicHints &= ~TEXT_AA_MASK;
+    currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT;
+}
+
+/**
+ * @see Graphics#setXORMode(bool)
+ */
+public void setXORMode(bool xor) {
+    currentState.graphicHints &= ~XOR_MASK;
+    if (xor)
+        currentState.graphicHints |= XOR_MASK;
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#shear(float, float)
+ */
+public void shear(float horz, float vert) {
+    //Flush any clipping changes before shearing
+    checkGC();
+    initTransform(true);
+    float matrix[] = new float[6];
+    transform.getElements(matrix);
+    transform.setElements(
+            matrix[0] + matrix[2] * vert,
+            matrix[1] + matrix[3] * vert,
+            matrix[0] * horz + matrix[2],
+            matrix[1] * horz + matrix[3],
+            matrix[4],
+            matrix[5]);
+
+    gc.setTransform(transform);
+    elementsNeedUpdate = true;
+    //Can no longer track clipping changes
+    appliedState.relativeClip = currentState.relativeClip = null;
+}
+
+/**
+ * This method may require advanced graphics support if using a transform,
+ * in this case, a check should be made to ensure advanced graphics is
+ * supported in the user's environment before calling this method. See
+ * {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#translate(int, int)
+ */
+public void translate(int dx, int dy) {
+    if (dx is 0 && dy is 0)
+        return;
+    if (transform !is null) {
+        //Flush clipping, pattern, etc. before applying transform
+        checkGC();
+        transform.translate(dx, dy);
+        elementsNeedUpdate = true;
+        gc.setTransform(transform);
+    } else {
+        translateX += dx;
+        translateY += dy;
+    }
+    checkSharedClipping();
+    if (currentState.relativeClip !is null)
+        currentState.relativeClip.translate(-dx, -dy);
+}
+
+/**
+ * This method requires advanced graphics support. A check should be made to
+ * ensure advanced graphics is supported in the user's environment before
+ * calling this method. See {@link GC#getAdvanced()}.
+ *
+ * @see Graphics#translate(float, float)
+ */
+public void translate(float dx, float dy) {
+    initTransform(true);
+    checkGC();
+    transform.translate(dx, dy);
+    elementsNeedUpdate = true;
+    gc.setTransform(transform);
+    checkSharedClipping();
+    if (currentState.relativeClip !is null)
+        currentState.relativeClip.translate(-dx, -dy);
+}
+
+private void translatePointArray(int[] points, int translateX, int translateY) {
+    if (translateX is 0 && translateY is 0)
+        return;
+    for (int i = 0; (i + 1) < points.length; i += 2) {
+        points[i] += translateX;
+        points[i + 1] += translateY;
+    }
+}
+
+}