/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.gef.draw2d.graphics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.LineAttributes;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.PathData;
import org.eclipse.swt.graphics.Pattern;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Display;
public class ScaledGraphics extends Graphics {
/**
* For Debugging: Once ScaledGraphics is proven to be replaceable by
* {@link Graphics#scale(double)} on all supported platforms, we will turn
* this switch off. Clients should check this value before creating
* ScaledGraphics instances.
*/
public static boolean SCALED_GRAPHICS_ENABLED = false;
private static class PatternKey {
GradientPattern pattern;
double zoom;
PatternKey() {
}
PatternKey(GradientPattern pattern, double zoom) {
this.pattern = pattern;
this.zoom = zoom;
}
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || !(obj instanceof PatternKey))
return false;
PatternKey that = (PatternKey) obj;
return this.pattern.equals(that.pattern) && this.zoom == that.zoom;
}
public int hashCode() {
long bits = Double.doubleToLongBits(zoom);
int hc = (int) (bits ^ (bits >>> 32));
return pattern.hashCode() ^ hc;
}
void setValues(GradientPattern pattern, double zoom) {
this.pattern = pattern;
this.zoom = zoom;
}
}
private static class FontHeightCache {
Font font;
int height;
}
protected static class FontKey {
Font font;
int height;
protected FontKey() {/* empty constructor */
}
protected FontKey(Font font, int height) {
this.font = font;
this.height = height;
}
public boolean equals(Object obj) {
return (((FontKey) obj).font.equals(font)
&& ((FontKey) obj).height == height);
}
public int hashCode() {
return font.hashCode() ^ 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 float lineWidth;
private double zoom;
private int[] lineDash;
private Pattern background;
private Pattern foreground;
/**
* Constructs a new, uninitialized State object.
*/
protected State() {/* empty constructor */
}
/**
* 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 State(double zoom, double x, double y, Font font,
float lineWidth, int[] lineDash, Pattern background,
Pattern foreground) {
this.zoom = zoom;
this.appliedX = x;
this.appliedY = y;
this.font = font;
this.lineWidth = lineWidth;
this.lineDash = lineDash;
this.background = background;
this.foreground = foreground;
}
/**
* 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,
float lineWidth, int[] lineDash, Pattern background,
Pattern foreground) {
this.zoom = zoom;
this.appliedX = x;
this.appliedY = y;
this.font = font;
this.lineWidth = lineWidth;
this.lineDash = lineDash;
this.background = background;
this.foreground = foreground;
}
}
private static int[][] intArrayCache = new int[8][];
private final Rectangle tempRECT = new Rectangle();
static {
for (int i = 0; i < intArrayCache.length; i++)
intArrayCache[i] = new int[i + 1];
}
private boolean allowText = true;
//private static final Point PT = new Point();
private Map<FontKey, Font> fontCache = new HashMap<FontKey, Font>();
private Map<Font, FontData> fontDataCache = new HashMap<Font, FontData>();
private FontKey fontKey = new FontKey();
private double fractionalX;
private double fractionalY;
private Graphics graphics;
private FontHeightCache localCache = new FontHeightCache();
private Font localFont;
private float localLineWidth;
private List<State> stack = new ArrayList<State>();
private int stackPointer = 0;
private FontHeightCache targetCache = new FontHeightCache();
private double zoom = 1.0;
private int[] localDash = null;
private Pattern localBackground = null;
private Pattern localForeground = null;
private Map<PatternKey, GradientPattern> patternCache = new HashMap<PatternKey, GradientPattern>();
private PatternKey patternKey = new PatternKey();
/**
* Constructs a new ScaledGraphics based on the given Graphics object.
*
* @param g
* the base graphics object
*/
public ScaledGraphics(Graphics g) {
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);
}
/**
* @see Graphics#dispose()
*/
public void dispose() {
//Remove all states from the stack
while (stackPointer > 0) {
popState();
}
//Dispose fonts
for (Font font : fontCache.values()) {
font.dispose();
}
for (Pattern pattern : patternCache.values()) {
pattern.dispose();
}
// Resource manager handles fonts
}
/**
* @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 == 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) {
org.eclipse.swt.graphics.Rectangle size = srcImage.getBounds();
graphics.drawImage(srcImage, 0, 0, size.width, size.height,
(int) (Math.floor((x * zoom + fractionalX))),
(int) (Math.floor((y * zoom + fractionalY))),
(int) (Math.floor((size.width * zoom + fractionalX))),
(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" == 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((int) (Math.floor((x1 * zoom + fractionalX))),
(int) (Math.floor((y1 * zoom + fractionalY))),
(int) (Math.floor((x2 * zoom + fractionalX))),
(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#drawPoint(int, int)
*/
public void drawPoint(int x, int y) {
graphics.drawPoint((int) Math.floor(x * zoom + fractionalX),
(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),
(int) (arcWidth * zoom), (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,
(int) Math.floor(x * zoom + fractionalX),
(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 == 0)
return;
graphics.fillArc(z, offset, sweep);
}
/**
* @see Graphics#fillGradient(int, int, int, int, boolean)
*/
public void fillGradient(int x, int y, int w, int h, boolean 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#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),
(int) (arcWidth * zoom), (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();
}
private Font getCachedFont(FontKey key) {
Font font = fontCache.get(key);
if (font != null)
return font;
key = new FontKey(key.font, key.height);
Font zoomedFont = createZoomedFont(key.font, key.height);
fontCache.put(key, zoomedFont);
return zoomedFont;
}
private Font createZoomedFont(Font font, int height) {
FontData[] fontData = font.getFontData();
for (FontData f : fontData) {
f.setHeight(height);
}
return new Font(Display.getCurrent(), fontData);
}
private FontData getCachedFontData(Font f) {
FontData data = fontDataCache.get(f);
if (data != null)
return data;
data = getLocalFont().getFontData()[0];
fontDataCache.put(f, data);
return data;
}
private Pattern getCachedPattern(PatternKey key) {
GradientPattern pattern = patternCache.get(key);
if (pattern != null)
return pattern;
key = new PatternKey(key.pattern, key.zoom);
pattern = createZoomedPattern(key.pattern);
patternCache.put(key, pattern);
return pattern;
}
private GradientPattern createZoomedPattern(GradientPattern p1) {
return new GradientPattern(p1.getDevice(), (float) (zoom * p1.x1),
(float) (zoom * p1.y1), (float) (zoom * p1.x2),
(float) (zoom * p1.y2), p1.color1, p1.alpha1, p1.color2,
p1.alpha2);
}
/**
* @see Graphics#getClip(Rectangle)
*/
public Rectangle getClip(Rectangle rect) {
graphics.getClip(rect);
int x = (int) (rect.x / zoom);
int y = (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 = (int) Math.ceil(rect.right() / zoom) - x;
rect.height = (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 (int) getLocalLineWidth();
}
public float getLineWidthFloat() {
return getLocalLineWidth();
}
protected final Font getLocalFont() {
return localFont;
}
protected final float getLocalLineWidth() {
return localLineWidth;
}
/**
* @see Graphics#getTextAntialias()
*/
public int getTextAntialias() {
return graphics.getTextAntialias();
}
/**
* @see Graphics#getXORMode()
*/
public boolean getXORMode() {
return graphics.getXORMode();
}
/**
* @see Graphics#popState()
*/
public void popState() {
graphics.popState();
stackPointer--;
restoreLocalState(stack.get(stackPointer));
if (lastClipPath != null) {
lastClipPath.dispose();
lastClipPath = null;
}
}
/**
* @see Graphics#pushState()
*/
public void pushState() {
if (stack.size() > stackPointer) {
State s = stack.get(stackPointer);
s.setValues(zoom, fractionalX, fractionalY, getLocalFont(),
getLocalLineWidth(), localDash, localBackground,
localForeground);
} else {
stack.add(new State(zoom, fractionalX, fractionalY, getLocalFont(),
getLocalLineWidth(), localDash, localBackground,
localForeground));
}
stackPointer++;
graphics.pushState();
}
protected void restoreLocalState(State state) {
this.fractionalX = state.appliedX;
this.fractionalY = state.appliedY;
setScale(state.zoom);
setLocalFont(state.font);
setLocalLineWidth(state.lineWidth);
setLocalLineDash(state.lineDash);
setLocalBackgroundPattern(state.background);
setLocalForegroundPattern(state.foreground);
}
/**
* @see Graphics#restoreState()
*/
public void restoreState() {
graphics.restoreState();
restoreLocalState(stack.get(stackPointer - 1));
}
/**
* @see Graphics#scale(double)
*/
public void scale(double amount) {
setScale(zoom * amount);
}
/**
* 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 GCUtilities#supportsAdvancedGraphics()}.
*
* @see Graphics#setAlpha(int)
*/
public void setAlpha(int alpha) {
graphics.setAlpha(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 GCUtilities#supportsAdvancedGraphics()}.
*
* @see Graphics#setAntialias(int)
*/
public void setAntialias(int value) {
graphics.setAntialias(value);
}
/**
* @see Graphics#setBackgroundColor(Color)
*/
public void setBackgroundColor(Color rgb) {
graphics.setBackgroundColor(rgb);
}
/**
* @see Graphics#setClip(Rectangle)
*/
public void setClip(Rectangle r) {
graphics.setClip(zoomClipRect(r));
}
/**
* @see Graphics#setFillRule(int)
*/
public void setFillRule(int rule) {
graphics.setFillRule(rule);
}
/**
* @see Graphics#setFont(Font)
*/
public void setFont(Font f) {
setLocalFont(f);
}
/**
* @see Graphics#setForegroundColor(Color)
*/
public void setForegroundColor(Color rgb) {
graphics.setForegroundColor(rgb);
}
/**
* 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 GCUtilities#supportsAdvancedGraphics()}.
*
* @see org.eclipse.draw2d.Graphics#setInterpolation(int)
*/
public void setInterpolation(int interpolation) {
graphics.setInterpolation(interpolation);
}
/**
* @see Graphics#setLineCap(int)
*/
public void setLineCap(int cap) {
graphics.setLineCap(cap);
}
/**
* @see Graphics#setLineDash(int[])
*/
public void setLineDash(int[] dash) {
setLocalLineDash(dash);
}
private void setLocalLineDash(int[] dash) {
localDash = dash;
if (dash != null)
graphics.setLineDash(zoomDash(dash));
}
/**
* @see Graphics#setLineJoin(int)
*/
public void setLineJoin(int join) {
graphics.setLineJoin(join);
}
/**
* @see Graphics#setLineStyle(int)
*/
public void setLineStyle(int style) {
graphics.setLineStyle(style);
}
/**
* @see Graphics#setLineWidth(int)
*/
public void setLineWidth(int width) {
setLineWidthFloat(width);
}
public void setLineWidthFloat(float width) {
setLocalLineWidth(width);
}
private void setLocalFont(Font f) {
localFont = f;
graphics.setFont(zoomFont(f));
}
private void setLocalLineWidth(float width) {
localLineWidth = width;
graphics.setLineWidth((int) zoomLineWidth(width));
}
void setScale(double value) {
if (zoom == value)
return;
this.zoom = value;
graphics.setFont(zoomFont(getLocalFont()));
graphics.setLineWidth((int) zoomLineWidth(getLocalLineWidth()));
if (localDash != null)
graphics.setLineDash(zoomDash(localDash));
if (localBackground != null)
graphics.setBackgroundPattern(zoomPattern(localBackground));
if (localForeground != null)
graphics.setForegroundPattern(zoomPattern(localForeground));
}
/**
* 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 GCUtilities#supportsAdvancedGraphics()}.
*
* @see Graphics#setTextAntialias(int)
*/
public void setTextAntialias(int value) {
graphics.setTextAntialias(value);
}
/**
* @see Graphics#setXORMode(boolean)
*/
public void setXORMode(boolean 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((int) Math.floor(dxFloat),
(int) Math.floor(dyFloat));
}
private Rectangle zoomClipRect(Rectangle r) {
tempRECT.x = (int) (Math.floor(r.x * zoom + fractionalX));
tempRECT.y = (int) (Math.floor(r.y * zoom + fractionalY));
tempRECT.width = (int) (Math
.ceil(((r.x + r.width) * zoom + fractionalX))) - tempRECT.x;
tempRECT.height = (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 = (int) (Math.floor((x * zoom + fractionalX)));
tempRECT.y = (int) (Math.floor((y * zoom + fractionalY)));
tempRECT.width = (int) (Math.floor(((x + w - 1) * zoom + fractionalX)))
- tempRECT.x + 1;
tempRECT.height = (int) (Math.floor(((y + h - 1) * zoom + fractionalY)))
- tempRECT.y + 1;
return tempRECT;
}
private Font zoomFont(Font f) {
if (f == null)
f = Display.getCurrent().getSystemFont();
FontData data = getCachedFontData(f);
int zoomedFontHeight = zoomFontHeight(data.getHeight());
allowText = zoomedFontHeight > 0;
fontKey.setValues(f, zoomedFontHeight);
return getCachedFont(fontKey);
}
protected int zoomFontHeight(int height) {
return (int) (zoom * height);
}
protected float zoomLineWidth(float w) {
return (float) (zoom * 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 == points.length) {
scaled = intArrayCache[i];
// Move this integer array up one notch in the array
if (i != 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 == 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] = (int) (Math.floor((points[i] * zoom + fractionalX)));
scaled[i + 1] = (int) (Math
.floor((points[i + 1] * zoom + fractionalY)));
}
return scaled;
}
protected Rectangle zoomRect(int x, int y, int w, int h) {
tempRECT.x = (int) (Math.floor(x * zoom + fractionalX));
tempRECT.y = (int) (Math.floor(y * zoom + fractionalY));
tempRECT.width = (int) (Math.floor(((x + w) * zoom + fractionalX)))
- tempRECT.x;
tempRECT.height = (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() != -1)
zoomWidth = ((int) (layout.getWidth() * zoom));
if (zoomWidth < -1 || zoomWidth == 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 != length
&& (style = layout.getStyle(offset)) == lastStyle)
continue;
int end = offset - 1;
if (lastStyle != 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 != 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 != graphics.getFont()) {
FontMetrics metric = graphics.getFontMetrics();
targetCache.font = graphics.getFont();
targetCache.height = metric.getHeight() - metric.getDescent();
}
return new Point(((int) (Math.floor((x * zoom) + fractionalX))),
(int) (Math.floor((y + localCache.height - 1) * zoom
- targetCache.height + 1 + fractionalY)));
}
protected Graphics getGraphics() {
return graphics;
}
/**
* @see org.eclipse.draw2d.Graphics#drawPath(org.eclipse.swt.graphics.Path)
*/
@Override
public void drawPath(Path path) {
Path zoomPath = zoomPath(path);
getGraphics().drawPath(zoomPath);
zoomPath.dispose();
}
/**
* @see org.eclipse.draw2d.Graphics#fillPath(org.eclipse.swt.graphics.Path)
*/
@Override
public void fillPath(Path path) {
Path zoomPath = zoomPath(path);
getGraphics().fillPath(zoomPath);
zoomPath.dispose();
}
/**
* @param path
* @return
*/
private Path zoomPath(Path path) {
PathData data = path.getPathData();
Path newPath = new Path(path.getDevice());
int index = 0;
for (byte type : data.types) {
switch (type) {
case SWT.PATH_MOVE_TO:
newPath.moveTo((float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]));
break;
case SWT.PATH_LINE_TO:
newPath.lineTo((float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]));
break;
case SWT.PATH_CUBIC_TO:
newPath.cubicTo((float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]));
break;
case SWT.PATH_QUAD_TO:
newPath.quadTo((float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]),
(float) (zoom * data.points[index++]));
break;
case SWT.PATH_CLOSE:
newPath.close();
break;
}
}
return newPath;
}
/**
* @see org.eclipse.draw2d.Graphics#setBackgroundPattern(org.eclipse.swt.graphics.Pattern)
*/
@Override
public void setBackgroundPattern(Pattern pattern) {
setLocalBackgroundPattern(pattern);
}
/**
* @see org.eclipse.draw2d.Graphics#setForegroundPattern(org.eclipse.swt.graphics.Pattern)
*/
@Override
public void setForegroundPattern(Pattern pattern) {
setLocalForegroundPattern(pattern);
}
private void setLocalBackgroundPattern(Pattern pattern) {
localBackground = pattern;
graphics.setBackgroundPattern(zoomPattern(pattern));
}
private void setLocalForegroundPattern(Pattern pattern) {
localForeground = pattern;
graphics.setForegroundPattern(zoomPattern(pattern));
}
/**
* @param dash
* @return
*/
private int[] zoomDash(int[] dash) {
if (dash == null || dash.length == 0) {
dash = new int[1];
dash[0] = 1;
return dash;
}
int[] d = new int[dash.length];
for (int i = 0; i < d.length; i++) {
d[i] = Math.max(1, (int) (zoom * dash[i]));
}
return d;
}
protected Pattern zoomPattern(Pattern pattern) {
if (!(pattern instanceof GradientPattern))
return pattern;
patternKey.setValues((GradientPattern) pattern, zoom);
return getCachedPattern(patternKey);
}
/**
* @see org.eclipse.draw2d.Graphics#rotate(float)
*/
@Override
public void rotate(float degrees) {
graphics.rotate(degrees);
}
public void translate(float dx, float dy) {
graphics.translate(dx, dy);
}
private Path lastClipPath = null;
public void setClip(Path path) {
Path p = path == null ? null : zoomPath(path);
graphics.setClip(p);
if (lastClipPath != null) {
lastClipPath.dispose();
lastClipPath = null;
}
if (p != path) {
lastClipPath = p;
}
}
/**
* @see org.eclipse.draw2d.Graphics#clipPath(org.eclipse.swt.graphics.Path)
*/
public void clipPath(Path path) {
Path scaledPath = createScaledPath(path);
try {
graphics.clipPath(scaledPath);
} finally {
scaledPath.dispose();
}
}
/**
* Scales given path by zoom factor
*
* @param path
* Path to be scaled
* @return Scaled path
*/
private Path createScaledPath(Path path) {
PathData p = path.getPathData();
for (int i = 0; i < p.points.length; i += 2) {
p.points[i] = (float) (p.points[i] * zoom + fractionalX);
p.points[i + 1] = (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 SWT.PATH_MOVE_TO:
scaledPath.moveTo(p.points[index], p.points[index + 1]);
index += 2;
break;
case SWT.PATH_LINE_TO:
scaledPath.lineTo(p.points[index], p.points[index + 1]);
index += 2;
break;
case SWT.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 SWT.PATH_QUAD_TO:
scaledPath.quadTo(p.points[index], p.points[index + 1],
p.points[index + 2], p.points[index + 3]);
index += 4;
break;
case SWT.PATH_CLOSE:
scaledPath.close();
break;
}
}
return scaledPath;
}
// ==========================================================
// Since 3.5
// ==========================================================
public boolean getAdvanced() {
return graphics.getAdvanced();
}
public LineAttributes getLineAttributes() {
LineAttributes a = graphics.getLineAttributes();
a.width = getLocalLineWidth();
return a;
}
public float getLineMiterLimit() {
return graphics.getLineMiterLimit();
}
public void setAdvanced(boolean advanced) {
graphics.setAdvanced(advanced);
}
public void setLineMiterLimit(float miterLimit) {
graphics.setLineMiterLimit(miterLimit);
}
public void setLineAttributes(LineAttributes attributes) {
graphics.setLineAttributes(attributes);
setLocalLineWidth(attributes.width);
}
public void setLineDash(float[] value) {
graphics.setLineDash(value);
}
}