Mercurial > projects > dwt-addons
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))); +} + +}