diff dwtx/draw2d/ScaledGraphics.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 2d6540440fe6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/ScaledGraphics.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,885 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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.ScaledGraphics;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.Image;
+import dwt.graphics.Path;
+import dwt.graphics.PathData;
+import dwt.graphics.TextLayout;
+import dwt.graphics.TextStyle;
+import dwt.widgets.Display;
+static import dwt.graphics.Rectangle;
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.geometry.Rectangle;
+import dwtx.draw2d.Graphics;
+import dwtx.draw2d.FigureUtilities;
+
+/**
+ * A Graphics object able to scale all operations based on the current scale factor.
+ */
+public class ScaledGraphics
+    : Graphics
+{
+alias Graphics.translate translate;
+alias Graphics.fillRectangle fillRectangle;
+
+private static class FontHeightCache {
+    Font font;
+    int height;
+}
+
+static class FontKey {
+    Font font;
+    int height;
+    protected this() { }
+    protected this(Font font, int height) {
+        this.font = font;
+        this.height = height;
+    }
+
+    public override int opEquals(Object obj) {
+        return ((cast(FontKey)obj).font.opEquals(font)
+                && (cast(FontKey)obj).height is height);
+    }
+
+    public override hash_t toHash() {
+        return font.toHash() ^ height;
+    }
+
+    protected void setValues(Font font, int height) {
+        this.font = font;
+        this.height = height;
+    }
+}
+
+/**
+ * The internal state of the scaled graphics.
+ */
+protected static class State {
+    private double appliedX;
+    private double appliedY;
+    private Font font;
+    private int lineWidth;
+    private double zoom;
+
+    /**
+     * Constructs a new, uninitialized State object.
+     */
+    protected this() { }
+
+    /**
+     * Constructs a new State object and initializes the properties based on the given
+     * values.
+     *
+     * @param zoom the zoom factor
+     * @param x the x offset
+     * @param y the y offset
+     * @param font the font
+     * @param lineWidth the line width
+     */
+    protected this(double zoom, double x, double y, Font font, int lineWidth) {
+        this.zoom = zoom;
+        this.appliedX = x;
+        this.appliedY = y;
+        this.font = font;
+        this.lineWidth = lineWidth;
+    }
+
+    /**
+     * Sets all the properties of the state object.
+     * @param zoom the zoom factor
+     * @param x the x offset
+     * @param y the y offset
+     * @param font the font
+     * @param lineWidth the line width
+     */
+    protected void setValues(double zoom, double x, double y,
+                                Font font, int lineWidth) {
+        this.zoom = zoom;
+        this.appliedX = x;
+        this.appliedY = y;
+        this.font = font;
+        this.lineWidth = lineWidth;
+    }
+}
+
+private static int[][] intArrayCache;
+private final Rectangle tempRECT;
+
+static this(){
+    intArrayCache = new int[][](8);
+    for (int i = 0; i < intArrayCache.length; i++)
+        intArrayCache[i] = new int[i + 1];
+}
+private void instanceInit(){
+    tempRECT = new Rectangle();
+    localCache = new FontHeightCache();
+    targetCache = new FontHeightCache();
+    stack = new ArrayList();
+    fontKey = new FontKey();
+    fontDataCache = new HashMap();
+    fontCache = new HashMap();
+}
+private bool allowText = true;
+//private static final Point PT = new Point();
+private Map fontCache;
+private Map fontDataCache;
+private FontKey fontKey;
+private double fractionalX;
+private double fractionalY;
+private Graphics graphics;
+private FontHeightCache localCache;
+private Font localFont;
+private int localLineWidth;
+private List stack;
+private int stackPointer = 0;
+private FontHeightCache targetCache;
+
+double zoom = 1.0;
+
+/**
+ * Constructs a new ScaledGraphics based on the given Graphics object.
+ * @param g the base graphics object
+ */
+public this(Graphics g) {
+    instanceInit();
+    graphics = g;
+    localFont = g.getFont();
+    localLineWidth = g.getLineWidth();
+}
+
+/** @see Graphics#clipRect(Rectangle) */
+public void clipRect(Rectangle r) {
+    graphics.clipRect(zoomClipRect(r));
+}
+
+Font createFont(FontData data) {
+    return new Font(Display.getCurrent(), data);
+}
+
+private Path createScaledPath(Path path) {
+    PathData p = path.getPathData();
+    for (int i = 0; i < p.points.length; i += 2) {
+        p.points[i] = cast(float) (p.points[i] * zoom + fractionalX);
+        p.points[i + 1] = cast(float) (p.points[i + 1] * zoom + fractionalY);
+    }
+    Path scaledPath = new Path(path.getDevice());
+    int index = 0;
+    for (int i = 0; i < p.types.length; i++) {
+        byte type = p.types[i];
+        switch (type) {
+            case DWT.PATH_MOVE_TO:
+                scaledPath.moveTo(p.points[index], p.points[index + 1]);
+                index += 2;
+                break;
+            case DWT.PATH_LINE_TO:
+                scaledPath.lineTo(p.points[index], p.points[index + 1]);
+                index += 2;
+                break;
+            case DWT.PATH_CUBIC_TO:
+                scaledPath.cubicTo(p.points[index], p.points[index + 1],
+                        p.points[index + 2], p.points[index + 3], p.points[index + 4],
+                        p.points[index + 5]);
+                index += 6;
+                break;
+            case DWT.PATH_QUAD_TO:
+                scaledPath.quadTo(p.points[index], p.points[index + 1],
+                        p.points[index + 2], p.points[index + 3]);
+                index += 4;
+                break;
+            case DWT.PATH_CLOSE:
+                scaledPath.close();
+                break;
+        }
+    }
+    return scaledPath;
+}
+
+/** @see Graphics#dispose() */
+public void dispose() {
+    //Remove all states from the stack
+    while (stackPointer > 0) {
+        popState();
+    }
+
+    //Dispose fonts
+    Iterator iter = fontCache.values().iterator();
+    while (iter.hasNext()) {
+        Font font = (cast(Font)iter.next());
+        font.dispose();
+    }
+
+}
+
+/** @see Graphics#drawArc(int, int, int, int, int, int) */
+public void drawArc(int x, int y, int w, int h, int offset, int sweep) {
+    Rectangle z = zoomRect(x, y, w, h);
+    if (z.isEmpty() || sweep is 0)
+        return;
+    graphics.drawArc(z, offset, sweep);
+}
+
+/** @see Graphics#drawFocus(int, int, int, int) */
+public void drawFocus(int x, int y, int w, int h) {
+    graphics.drawFocus(zoomRect(x, y, w, h));
+}
+
+/** @see Graphics#drawImage(Image, int, int) */
+public void drawImage(Image srcImage, int x, int y) {
+    dwt.graphics.Rectangle.Rectangle size = srcImage.getBounds();
+    graphics.drawImage(srcImage, 0, 0, size.width, size.height,
+        cast(int)(Math.floor((x * zoom + fractionalX))),
+        cast(int)(Math.floor((y * zoom + fractionalY))),
+        cast(int)(Math.floor((size.width * zoom + fractionalX))),
+        cast(int)(Math.floor((size.height * zoom + fractionalY))));
+}
+
+/** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */
+public void drawImage(Image srcImage, int sx, int sy, int sw, int sh,
+                                        int tx, int ty, int tw, int th) {
+    //"t" is target rectangle, "s" = source
+
+    Rectangle t = zoomRect(tx, ty, tw, th);
+    if (!t.isEmpty())
+        graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, t.height);
+}
+
+/** @see Graphics#drawLine(int, int, int, int) */
+public void drawLine(int x1, int y1, int x2, int y2) {
+    graphics.drawLine(
+        cast(int)(Math.floor((x1 * zoom + fractionalX))),
+        cast(int)(Math.floor((y1 * zoom + fractionalY))),
+        cast(int)(Math.floor((x2 * zoom + fractionalX))),
+        cast(int)(Math.floor((y2 * zoom + fractionalY))));
+}
+
+/** @see Graphics#drawOval(int, int, int, int) */
+public void drawOval(int x, int y, int w, int h) {
+    graphics.drawOval(zoomRect(x, y, w, h));
+}
+
+/** @see Graphics#drawPath(Path) */
+public void drawPath(Path path) {
+    Path scaledPath = createScaledPath(path);
+    try {
+        graphics.drawPath(scaledPath);
+    } finally {
+        scaledPath.dispose();
+    }
+}
+
+/** @see Graphics#drawPoint(int, int) */
+public void drawPoint(int x, int y) {
+    graphics.drawPoint(cast(int)Math.floor(x * zoom + fractionalX),
+            cast(int)Math.floor(y * zoom + fractionalY));
+}
+
+/**
+ * @see Graphics#drawPolygon(int[])
+ */
+public void drawPolygon(int[] points) {
+    graphics.drawPolygon(zoomPointList(points));
+}
+
+/** @see Graphics#drawPolygon(PointList) */
+public void drawPolygon(PointList points) {
+    graphics.drawPolygon(zoomPointList(points.toIntArray()));
+}
+
+/**
+ * @see Graphics#drawPolyline(int[])
+ */
+public void drawPolyline(int[] points) {
+    graphics.drawPolyline(zoomPointList(points));
+}
+
+/** @see Graphics#drawPolyline(PointList) */
+public void drawPolyline(PointList points) {
+    graphics.drawPolyline(zoomPointList(points.toIntArray()));
+}
+
+/** @see Graphics#drawRectangle(int, int, int, int) */
+public void drawRectangle(int x, int y, int w, int h) {
+    graphics.drawRectangle(zoomRect(x, y, w, h));
+}
+
+/** @see Graphics#drawRoundRectangle(Rectangle, int, int) */
+public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height),
+        cast(int)(arcWidth * zoom),
+        cast(int)(arcHeight * zoom));
+}
+
+/** @see Graphics#drawString(String, int, int) */
+public void drawString(String s, int x, int y) {
+    if (allowText)
+        graphics.drawString(s, zoomTextPoint(x, y));
+}
+
+/** @see Graphics#drawText(String, int, int) */
+public void drawText(String s, int x, int y) {
+    if (allowText)
+        graphics.drawText(s, zoomTextPoint(x, y));
+}
+
+/**
+ * @see Graphics#drawText(String, int, int, int)
+ */
+public void drawText(String s, int x, int y, int style) {
+    if (allowText)
+        graphics.drawText(s, zoomTextPoint(x, y), style);
+}
+
+/**
+ * @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) {
+    TextLayout scaled = zoomTextLayout(layout);
+    graphics.drawTextLayout(scaled,
+            cast(int)Math.floor(x * zoom + fractionalX),
+            cast(int)Math.floor(y * zoom + fractionalY),
+            selectionStart, selectionEnd, selectionBackground, selectionForeground);
+    scaled.dispose();
+}
+
+/** @see Graphics#fillArc(int, int, int, int, int, int) */
+public void fillArc(int x, int y, int w, int h, int offset, int sweep) {
+    Rectangle z = zoomFillRect(x, y, w, h);
+    if (z.isEmpty() || sweep is 0)
+        return;
+    graphics.fillArc(z, offset, sweep);
+}
+
+/** @see Graphics#fillGradient(int, int, int, int, bool) */
+public void fillGradient(int x, int y, int w, int h, bool vertical) {
+    graphics.fillGradient(zoomFillRect(x, y, w, h), vertical);
+}
+
+/** @see Graphics#fillOval(int, int, int, int) */
+public void fillOval(int x, int y, int w, int h) {
+    graphics.fillOval(zoomFillRect(x, y, w, h));
+}
+
+/** @see Graphics#fillPath(Path) */
+public void fillPath(Path path) {
+    Path scaledPath = createScaledPath(path);
+    try {
+        graphics.fillPath(scaledPath);
+    } finally {
+        scaledPath.dispose();
+    }
+}
+
+/**
+ * @see Graphics#fillPolygon(int[])
+ */
+public void fillPolygon(int[] points) {
+    graphics.fillPolygon(zoomPointList(points));
+}
+
+/** @see Graphics#fillPolygon(PointList) */
+public void fillPolygon(PointList points) {
+    graphics.fillPolygon(zoomPointList(points.toIntArray()));
+}
+
+/** @see Graphics#fillRectangle(int, int, int, int) */
+public void fillRectangle(int x, int y, int w, int h) {
+    graphics.fillRectangle(zoomFillRect(x, y, w, h));
+}
+
+/** @see Graphics#fillRoundRectangle(Rectangle, int, int) */
+public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+    graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height),
+        cast(int)(arcWidth * zoom),
+        cast(int)(arcHeight * zoom));
+}
+
+/** @see Graphics#fillString(String, int, int) */
+public void fillString(String s, int x, int y) {
+    if (allowText)
+        graphics.fillString(s, zoomTextPoint(x, y));
+}
+
+/** @see Graphics#fillText(String, int, int) */
+public void fillText(String s, int x, int y) {
+    if (allowText)
+        graphics.fillText(s, zoomTextPoint(x, y));
+}
+
+/**
+ * @see Graphics#getAbsoluteScale()
+ */
+public double getAbsoluteScale() {
+    return zoom * graphics.getAbsoluteScale();
+}
+
+/**
+ * @see Graphics#getAlpha()
+ */
+public int getAlpha() {
+    return graphics.getAlpha();
+}
+
+/**
+ * @see Graphics#getAntialias()
+ */
+public int getAntialias() {
+    return graphics.getAntialias();
+}
+
+/** @see Graphics#getBackgroundColor() */
+public Color getBackgroundColor() {
+    return graphics.getBackgroundColor();
+}
+
+Font getCachedFont(FontKey key) {
+    Font font = cast(Font)fontCache.get(key);
+    if (font !is null)
+        return font;
+    key = new FontKey(key.font, key.height);
+    FontData data = key.font.getFontData()[0];
+    data.setHeight(key.height);
+    Font zoomedFont = createFont(data);
+    fontCache.put(key, zoomedFont);
+    return zoomedFont;
+}
+
+FontData getCachedFontData(Font f) {
+    FontData data = cast(FontData)fontDataCache.get(f);
+    if (data !is null)
+        return data;
+    data = getLocalFont().getFontData()[0];
+    fontDataCache.put(f, data);
+    return data;
+}
+
+/** @see Graphics#getClip(Rectangle) */
+public Rectangle getClip(Rectangle rect) {
+    graphics.getClip(rect);
+    int x = cast(int)(rect.x / zoom);
+    int y = cast(int)(rect.y / zoom);
+    /*
+     * If the clip rectangle is queried, perform an inverse zoom, and take the ceiling of
+     * the resulting double. This is necessary because forward scaling essentially performs
+     * a floor() function. Without this, figures will think that they don't need to paint
+     * when actually they do.
+     */
+    rect.width = cast(int)Math.ceil(rect.right() / zoom) - x;
+    rect.height = cast(int)Math.ceil(rect.bottom() / zoom) - y;
+    rect.x = x;
+    rect.y = y;
+    return rect;
+}
+
+/**
+ * @see Graphics#getFillRule()
+ */
+public int getFillRule() {
+    return graphics.getFillRule();
+}
+
+/** @see Graphics#getFont() */
+public Font getFont() {
+    return getLocalFont();
+}
+
+/** @see Graphics#getFontMetrics() */
+public FontMetrics getFontMetrics() {
+    return FigureUtilities.getFontMetrics(localFont);
+}
+
+/** @see Graphics#getForegroundColor() */
+public Color getForegroundColor() {
+    return graphics.getForegroundColor();
+}
+
+/**
+ * @see Graphics#getInterpolation()
+ */
+public int getInterpolation() {
+    return graphics.getInterpolation();
+}
+
+/**
+ * @see Graphics#getLineCap()
+ */
+public int getLineCap() {
+    return graphics.getLineCap();
+}
+
+/**
+ * @see Graphics#getLineJoin()
+ */
+public int getLineJoin() {
+    return graphics.getLineJoin();
+}
+
+/** @see Graphics#getLineStyle() */
+public int getLineStyle() {
+    return graphics.getLineStyle();
+}
+
+/** @see Graphics#getLineWidth() */
+public int getLineWidth() {
+    return getLocalLineWidth();
+}
+
+private Font getLocalFont() {
+    return localFont;
+}
+
+private int getLocalLineWidth() {
+    return localLineWidth;
+}
+
+/**
+ * @see Graphics#getTextAntialias()
+ */
+public int getTextAntialias() {
+    return graphics.getTextAntialias();
+}
+
+/** @see Graphics#getXORMode() */
+public bool getXORMode() {
+    return graphics.getXORMode();
+}
+
+/** @see Graphics#popState() */
+public void popState() {
+    graphics.popState();
+    stackPointer--;
+    restoreLocalState(cast(State)stack.get(stackPointer));
+}
+
+/** @see Graphics#pushState() */
+public void pushState() {
+    State s;
+    if (stack.size() > stackPointer) {
+        s = cast(State)stack.get(stackPointer);
+        s.setValues(zoom, fractionalX, fractionalY, getLocalFont(), localLineWidth);
+    } else {
+        stack.add(new State(zoom, fractionalX, fractionalY, getLocalFont(),
+                                localLineWidth));
+    }
+    stackPointer++;
+
+    graphics.pushState();
+}
+
+private void restoreLocalState(State state) {
+    this.fractionalX = state.appliedX;
+    this.fractionalY = state.appliedY;
+    setScale(state.zoom);
+    setLocalFont(state.font);
+    setLocalLineWidth(state.lineWidth);
+}
+
+/** @see Graphics#restoreState() */
+public void restoreState() {
+    graphics.restoreState();
+    restoreLocalState(cast(State)stack.get(stackPointer - 1));
+}
+
+/** @see Graphics#scale(double) */
+public void scale(double amount) {
+    setScale(zoom * amount);
+}
+
+/**
+ * @see Graphics#setAlphacast(int)
+ */
+public void setAlpha(int alpha) {
+    graphics.setAlpha(alpha);
+}
+
+/**
+ * @see Graphics#setAntialiascast(int)
+ */
+public void setAntialias(int value) {
+    graphics.setAntialias(value);
+}
+
+/** @see Graphics#setBackgroundColor(Color) */
+public void setBackgroundColor(Color rgb) {
+    graphics.setBackgroundColor(rgb);
+}
+
+/** @see Graphics#setClip(Path) */
+public void setClip(Path path) {
+    Path scaledPath = createScaledPath(path);
+    try {
+        graphics.setClip(scaledPath);
+    } finally {
+        scaledPath.dispose();
+    }
+}
+
+/** @see Graphics#setClip(Rectangle) */
+public void setClip(Rectangle r) {
+    graphics.setClip(zoomClipRect(r));
+}
+
+/**
+ * @see Graphics#setFillRulecast(int)
+ */
+public void setFillRule(int rule) {
+    graphics.setFillRule(rule);
+}
+
+/** @see Graphics#setFontcast(Font) */
+public void setFont(Font f) {
+    setLocalFont(f);
+}
+
+/** @see Graphics#setForegroundColor(Color) */
+public void setForegroundColor(Color rgb) {
+    graphics.setForegroundColor(rgb);
+}
+
+/**
+ * @see dwtx.draw2d.Graphics#setInterpolationcast(int)
+ */
+public void setInterpolation(int interpolation) {
+    graphics.setInterpolation(interpolation);
+}
+
+/**
+ * @see Graphics#setLineCapcast(int)
+ */
+public void setLineCap(int cap) {
+    graphics.setLineCap(cap);
+}
+
+/**
+ * @see Graphics#setLineDash(int[])
+ */
+public void setLineDash(int[] dash) {
+    graphics.setLineDash(dash);
+}
+
+/**
+ * @see Graphics#setLineJoincast(int)
+ */
+public void setLineJoin(int join) {
+    graphics.setLineJoin(join);
+}
+
+/** @see Graphics#setLineStylecast(int) */
+public void setLineStyle(int style) {
+    graphics.setLineStyle(style);
+}
+
+/** @see Graphics#setLineWidthcast(int) */
+public void setLineWidth(int width) {
+    setLocalLineWidth(width);
+}
+
+private void setLocalFont(Font f) {
+    localFont = f;
+    graphics.setFont(zoomFont(f));
+}
+
+private void setLocalLineWidth(int width) {
+    localLineWidth = width;
+    graphics.setLineWidth(zoomLineWidth(width));
+}
+
+void setScale(double value) {
+    if (zoom is value)
+        return;
+    this.zoom = value;
+    graphics.setFont(zoomFont(getLocalFont()));
+    graphics.setLineWidth(zoomLineWidth(localLineWidth));
+}
+
+/**
+ * @see Graphics#setTextAntialiascast(int)
+ */
+public void setTextAntialias(int value) {
+    graphics.setTextAntialias(value);
+}
+
+/** @see Graphics#setXORMode(bool) */
+public void setXORMode(bool b) {
+    graphics.setXORMode(b);
+}
+
+/** @see Graphics#translate(int, int) */
+public void translate(int dx, int dy) {
+    // fractionalX/Y is the fractional part left over from previous
+    // translates that gets lost in the integer approximation.
+    double dxFloat = dx * zoom + fractionalX;
+    double dyFloat = dy * zoom + fractionalY;
+    fractionalX = dxFloat - Math.floor(dxFloat);
+    fractionalY = dyFloat - Math.floor(dyFloat);
+    graphics.translate(cast(int)Math.floor(dxFloat), cast(int)Math.floor(dyFloat));
+}
+
+/** @see Graphics#translate(float, float) */
+public void translate(float dx, float dy) {
+    double dxFloat = dx * zoom + fractionalX;
+    double dyFloat = dy * zoom + fractionalY;
+    fractionalX = dxFloat - Math.floor(dxFloat);
+    fractionalY = dyFloat - Math.floor(dyFloat);
+    graphics.translate(cast(int)Math.floor(dxFloat), cast(int)Math.floor(dyFloat));
+}
+
+private Rectangle zoomClipRect(Rectangle r) {
+    tempRECT.x = cast(int)(Math.floor(r.x * zoom + fractionalX));
+    tempRECT.y = cast(int)(Math.floor(r.y * zoom + fractionalY));
+    tempRECT.width = cast(int)(Math.ceil(((r.x + r.width) * zoom + fractionalX))) - tempRECT.x;
+    tempRECT.height = cast(int)(Math.ceil(((r.y + r.height) * zoom + fractionalY))) - tempRECT.y;
+    return tempRECT;
+}
+
+private Rectangle zoomFillRect(int x, int y, int w, int h) {
+    tempRECT.x = cast(int)(Math.floor((x * zoom + fractionalX)));
+    tempRECT.y = cast(int)(Math.floor((y * zoom + fractionalY)));
+    tempRECT.width = cast(int)(Math.floor(((x + w - 1) * zoom + fractionalX))) - tempRECT.x + 1;
+    tempRECT.height = cast(int)(Math.floor(((y + h - 1) * zoom + fractionalY))) - tempRECT.y + 1;
+    return tempRECT;
+}
+
+Font zoomFont(Font f) {
+    if (f is null)
+        f = Display.getCurrent().getSystemFont();
+    FontData data = getCachedFontData(f);
+    int zoomedFontHeight = zoomFontHeight(data.getHeight());
+    allowText = zoomedFontHeight > 0;
+    fontKey.setValues(f, zoomedFontHeight);
+    return getCachedFont(fontKey);
+}
+
+int zoomFontHeight(int height) {
+    return cast(int)(zoom * height);
+}
+
+int zoomLineWidth(int w) {
+    return w;
+}
+
+private int[] zoomPointList(int[] points) {
+    int[] scaled = null;
+
+    // Look in cache for a integer array with the same length as 'points'
+    for (int i = 0; i < intArrayCache.length; i++) {
+        if (intArrayCache[i].length is points.length) {
+            scaled = intArrayCache[i];
+
+            // Move this integer array up one notch in the array
+            if (i !is 0) {
+                int[] temp = intArrayCache[i - 1];
+                intArrayCache[i - 1] = scaled;
+                intArrayCache[i] = temp;
+            }
+        }
+    }
+
+    // If no match is found, take the one that is last and resize it.
+    if (scaled is null) {
+        intArrayCache[intArrayCache.length - 1] = new int[points.length];
+        scaled = intArrayCache[intArrayCache.length - 1];
+    }
+
+    // Scale the points
+    for (int i = 0; (i + 1) < points.length; i += 2) {
+        scaled[i] = cast(int)(Math.floor((points[i] * zoom + fractionalX)));
+        scaled[i + 1] = cast(int)(Math.floor((points[i + 1] * zoom + fractionalY)));
+    }
+    return scaled;
+}
+
+private Rectangle zoomRect(int x, int y, int w, int h) {
+    tempRECT.x = cast(int)(Math.floor(x * zoom + fractionalX));
+    tempRECT.y = cast(int)(Math.floor(y * zoom + fractionalY));
+    tempRECT.width = cast(int)(Math.floor(((x + w) * zoom + fractionalX))) - tempRECT.x;
+    tempRECT.height = cast(int)(Math.floor(((y + h) * zoom + fractionalY))) - tempRECT.y;
+    return tempRECT;
+}
+
+private TextLayout zoomTextLayout(TextLayout layout) {
+    TextLayout zoomed = new TextLayout(Display.getCurrent());
+    zoomed.setText(layout.getText());
+
+    int zoomWidth = -1;
+
+    if (layout.getWidth() !is -1)
+        zoomWidth = (cast(int)(layout.getWidth() * zoom));
+
+    if (zoomWidth < -1 || zoomWidth is 0)
+        return null;
+
+    zoomed.setFont(zoomFont(layout.getFont()));
+    zoomed.setAlignment(layout.getAlignment());
+    zoomed.setAscent(layout.getAscent());
+    zoomed.setDescent(layout.getDescent());
+    zoomed.setOrientation(layout.getOrientation());
+    zoomed.setSegments(layout.getSegments());
+    zoomed.setSpacing(layout.getSpacing());
+    zoomed.setTabs(layout.getTabs());
+
+    zoomed.setWidth(zoomWidth);
+    int length = layout.getText().length;
+    if (length > 0) {
+        int start = 0, offset = 1;
+        TextStyle style = null, lastStyle = layout.getStyle(0);
+        for (; offset <= length; offset++) {
+            if (offset !is length
+                    && (style = layout.getStyle(offset)) is lastStyle)
+                continue;
+            int end = offset - 1;
+
+            if (lastStyle !is null) {
+                TextStyle zoomedStyle = new TextStyle(zoomFont(lastStyle.font),
+                        lastStyle.foreground, lastStyle.background);
+                zoomedStyle.metrics = lastStyle.metrics;
+                zoomedStyle.rise = lastStyle.rise;
+                zoomedStyle.strikeout = lastStyle.strikeout;
+                zoomedStyle.underline = lastStyle.underline;
+                zoomed.setStyle(zoomedStyle, start, end);
+            }
+            lastStyle = style;
+            start = offset;
+        }
+    }
+    return zoomed;
+}
+
+private Point zoomTextPoint(int x, int y) {
+    if (localCache.font !is localFont) {
+        //Font is different, re-calculate its height
+        FontMetrics metric = FigureUtilities.getFontMetrics(localFont);
+        localCache.height = metric.getHeight() - metric.getDescent();
+        localCache.font = localFont;
+    }
+    if (targetCache.font !is graphics.getFont()) {
+        FontMetrics metric = graphics.getFontMetrics();
+        targetCache.font = graphics.getFont();
+        targetCache.height = metric.getHeight() - metric.getDescent();
+    }
+    return new Point((cast(int)(Math.floor((x * zoom) + fractionalX))),
+                        cast(int)(Math.floor((y + localCache.height - 1) * zoom
+                                            - targetCache.height + 1 + fractionalY)));
+}
+
+}