Mercurial > projects > dwt-addons
view dwtx/draw2d/ScaledGraphics.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 2d6540440fe6 |
children |
line wrap: on
line source
/******************************************************************************* * 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 static int[][] intArrayCache(){ if( intArrayCache_ is null ){ synchronized( ScaledGraphics.classinfo ){ if( intArrayCache_ is null ){ intArrayCache_ = new int[][](8); for (int i = 0; i < intArrayCache_.length; i++) intArrayCache_[i] = new int[i + 1]; } } } return intArrayCache_; } private const Rectangle tempRECT; 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) { // instance init tempRECT = new Rectangle(); localCache = new FontHeightCache(); targetCache = new FontHeightCache(); stack = new ArrayList(); fontKey = new FontKey(); fontDataCache = new HashMap(); fontCache = new HashMap(); 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; default: } } 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))); } }