/* ****************************************************************************** * Copyright (c) 2006-2013 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.ui.exports.vector.graphics; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Composite; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.util.Stack; import org.eclipse.core.runtime.Platform; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.SWTGraphics; import org.eclipse.draw2d.TextUtilities; import org.eclipse.draw2d.geometry.Dimension; 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.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; 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.widgets.Display; import org.xmind.gef.draw2d.graphics.GradientPattern; /** * @author Jason Wong */ public class GraphicsToGraphics2DAdaptor extends Graphics { private static class State { public int translateX = 0; public int translateY = 0; /** * clipping rectangle x coordinate */ public int clipX = 0; /** * clipping rectangle y coordinate */ public int clipY = 0; /** * clipping rectangle width */ public int clipW = 0; /** * clipping rectangle height */ public int clipH = 0; /** Font value **/ /** * cached font */ public Font font; /** * cached xor mode value */ public boolean XorMode = false; /** * cached foreground color */ public Color fgColor; /** * cached background color */ public Color bgColor; /** * cached foreground color */ public Pattern fgPattern; /** * cached background pattern */ public Pattern bgPattern; /** * cached alpha value */ public int alpha; /** * Line attributes value */ public LineAttributes lineAttributes = new LineAttributes(1); int graphicHints; /** * Copy the values from a given state to this state * * @param state * the state to copy from */ public void copyFrom(State state) { translateX = state.translateX; translateY = state.translateY; clipX = state.clipX; clipY = state.clipY; clipW = state.clipW; clipH = state.clipH; font = state.font; fgColor = state.fgColor; bgColor = state.bgColor; bgPattern = state.bgPattern; fgPattern = state.fgPattern; XorMode = state.XorMode; alpha = state.alpha; graphicHints = state.graphicHints; lineAttributes = SWTGraphics.clone(state.lineAttributes); } } static final int ADVANCED_GRAPHICS_MASK; static final int ADVANCED_SHIFT; static final int FILL_RULE_MASK; static final int FILL_RULE_SHIFT; static final int FILL_RULE_WHOLE_NUMBER = -1; /* * It's consistent with SWTGraphics flags in case some other flags from * SWTGraphics need to be here */ static { FILL_RULE_SHIFT = 14; ADVANCED_SHIFT = 15; FILL_RULE_MASK = 1 << FILL_RULE_SHIFT; // If changed to more than 1-bit, // check references! ADVANCED_GRAPHICS_MASK = 1 << ADVANCED_SHIFT; } private SWTGraphics swtGraphics; private Graphics2D graphics2D; private BasicStroke stroke; private Stack<State> states = new Stack<State>(); private final State currentState = new State(); private final State appliedState = new State(); /** * Some strings, Asian string in particular, are painted differently between * SWT and AWT. SWT falls back to some default locale font if Asian string * cannot be painted with the current font - this is done via the platform. * AWT, unlike platform biased SWT, does not. Hence, Asian string widths are * very different between SWT and AWT. To workaround the issue, if the flag * below is set to <code>true</code> then once SWT and AWT string width are * not equal, a bitmap of the SWT string will be painted. Otherwise the * string is always painted with AWT Graphics 2D string rendering. */ protected boolean paintNotCompatibleStringsAsBitmaps = true; @SuppressWarnings("unused") private static final TextUtilities TEXT_UTILITIES = new TextUtilities(); private Rectangle relativeClipRegion; private org.eclipse.swt.graphics.Rectangle viewBox; private Image image; /** * x coordinate for graphics translation */ private int transX = 0; /** * y coordinate for graphics translation */ private int transY = 0; /** * current rotation angle */ private float angle; /** * The x coordinate of the rotation point */ private int rotateX; /** * The y coordinate of the rotation point */ private int rotateY; /** * horizontal scale */ private float horizontalScale = 1.0f; /** * vertical scale */ private float verticalScale = 1.0f; /** * Constructor * * @param graphics * the <code>Graphics2D</code> object that this object is * delegating calls to. * @param viewPort * the <code>Rectangle</code> that defines the logical area being * rendered by the graphics object. */ public GraphicsToGraphics2DAdaptor(Graphics2D graphics, Rectangle viewPort, Display display) { this(graphics, new org.eclipse.swt.graphics.Rectangle(viewPort.x, viewPort.y, viewPort.width, viewPort.height), display); } private Display display; private static int rotateOrientation = 0; /** * Alternate Constructor that takes an swt Rectangle * * @param graphics * the <code>Graphics2D</code> object that this object is * delegating calls to. * @param viewPort * the <code>org.eclipse.swt.graphics.Rectangle</code> that * defines the logical area being rendered by the graphics * object. */ public GraphicsToGraphics2DAdaptor(Graphics2D graphics, org.eclipse.swt.graphics.Rectangle viewPort, Display display) { this.display = display; // Save the ViewPort to add to the root DOM element viewBox = viewPort; // Create the SWT Graphics Object createSWTGraphics(); // Initialize the SVG Graphics Object initSVGGraphics(graphics); // Initialize the States init(); } /** * This is a helper method used to create the SWT Graphics object */ private void createSWTGraphics() { // we need this temp Rect just to instantiate an swt image in order to // keep // state, the size of this Rect is of no consequence and we just set it // to // such a small size in order to minimize memory allocation org.eclipse.swt.graphics.Rectangle tempRect = new org.eclipse.swt.graphics.Rectangle( 0, 0, 10, 10); image = new Image(display, tempRect); GC gc = new GC(image); swtGraphics = new SWTGraphics(gc); } /** * Create the SVG graphics object and initializes it with the current line * style and width */ private void initSVGGraphics(Graphics2D graphics) { this.graphics2D = graphics; relativeClipRegion = new Rectangle(viewBox.x, viewBox.y, viewBox.width, viewBox.height); // Initialize the line style and width stroke = new BasicStroke(swtGraphics.getLineWidth(), BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, null, 0); LineAttributes lineAttributes = new LineAttributes(1); swtGraphics.getLineAttributes(lineAttributes); setLineAttributes(lineAttributes); setFillRule(swtGraphics.getFillRule()); setAdvanced(swtGraphics.getAdvanced()); getGraphics2D().setStroke(stroke); } /** * This method should only be called by the constructor. Initializes state * information for the currentState */ private void init() { // Initialize drawing styles setForegroundColor(getForegroundColor()); setBackgroundColor(getBackgroundColor()); setXORMode(getXORMode()); // Initialize Font setFont(getFont()); currentState.font = appliedState.font = getFont(); // Initialize translations currentState.translateX = appliedState.translateX = transX; currentState.translateY = appliedState.translateY = transY; // Initialize Clip Regions currentState.clipX = appliedState.clipX = relativeClipRegion.x; currentState.clipY = appliedState.clipY = relativeClipRegion.y; currentState.clipW = appliedState.clipW = relativeClipRegion.width; currentState.clipH = appliedState.clipH = relativeClipRegion.height; currentState.alpha = appliedState.alpha = getAlpha(); } /** * Verifies that the applied state is up to date with the current state and * updates the applied state accordingly. */ protected void checkState() { if (appliedState.font != currentState.font) { appliedState.font = currentState.font; setFont(currentState.font); } if (appliedState.clipX != currentState.clipX || appliedState.clipY != currentState.clipY || appliedState.clipW != currentState.clipW || appliedState.clipH != currentState.clipH) { appliedState.clipX = currentState.clipX; appliedState.clipY = currentState.clipY; appliedState.clipW = currentState.clipW; appliedState.clipH = currentState.clipH; if (rotateOrientation != 0) { if (currentState.bgColor.equals(currentState.fgColor)) { double p = Math.pow(currentState.clipW, 2) + Math.pow(currentState.clipH, 2); int newWidth = Math.round((float) Math.sqrt(p)); currentState.clipX = currentState.clipX - (newWidth - currentState.clipW) / 2; currentState.clipW = newWidth; } } // Adjust the clip for SVG getGraphics2D().setClip(currentState.clipX - 1, currentState.clipY - 1, currentState.clipW + 2, currentState.clipH + 2); } if (appliedState.alpha != currentState.alpha) { appliedState.alpha = currentState.alpha; setAlpha(currentState.alpha); } appliedState.graphicHints = currentState.graphicHints; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#clipRect(org.eclipse.draw2d.geometry. * Rectangle ) */ @Override public void clipRect(Rectangle rect) { relativeClipRegion.intersect(rect); setClipAbsolute(relativeClipRegion.x + transX, relativeClipRegion.y + transY, relativeClipRegion.width, relativeClipRegion.height); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#dispose() */ @Override public void dispose() { rotateOrientation = 0; swtGraphics.dispose(); if (image != null) { image.dispose(); } states.clear(); } /** * This method is used to convert an SWT Color to an AWT Color. * * @param toConvert * SWT Color to convert * @return AWT Color */ protected java.awt.Color getColor(Color toConvert) { return new java.awt.Color(toConvert.getRed(), toConvert.getGreen(), toConvert.getBlue(), getAlpha()); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawArc(int, int, int, int, int, int) */ @Override public void drawArc(int x, int y, int width, int height, int startAngle, int endAngle) { Arc2D arc = new Arc2D.Float(x + transX, y + transY, width - 1, height, startAngle, endAngle, Arc2D.OPEN); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(arc); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillArc(int, int, int, int, int, int) */ @Override public void fillArc(int x, int y, int w, int h, int offset, int length) { Arc2D arc = new Arc2D.Float(x + transX, y + transY, w, h, offset, length, Arc2D.OPEN); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getBackgroundColor())); getGraphics2D().fill(arc); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawFocus(int, int, int, int) */ @Override public void drawFocus(int x, int y, int w, int h) { drawRectangle(x, y, w, h); } @Override public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) { checkState(); if (!layout.getBounds().isEmpty()) { Image image = new Image(display, layout.getBounds().width, layout.getBounds().height); GC gc = new GC(image); cloneGC(gc); layout.draw(gc, 0, 0, selectionStart, selectionEnd, selectionForeground, selectionBackground); ImageData imageData = image.getImageData(); imageData.transparentPixel = imageData.palette .getPixel(getBackgroundColor().getRGB()); gc.dispose(); image.dispose(); getGraphics2D().drawImage( ImageConverter.convertFromImageData(imageData), x + transX, y + transY, null); } } private void cloneGC(GC gc) { gc.setAdvanced(getAdvanced()); gc.setAlpha(getAlpha()); gc.setAntialias(getAntialias()); gc.setFillRule(getFillRule()); gc.setFont(getFont()); gc.setInterpolation(getInterpolation()); gc.setLineAttributes(getLineAttributes()); gc.setTextAntialias(getTextAntialias()); gc.setBackground(getBackgroundColor()); gc.setForeground(getForegroundColor()); } @Override public int getInterpolation() { return swtGraphics.getInterpolation(); } @Override public LineAttributes getLineAttributes() { LineAttributes la = new LineAttributes(1); swtGraphics.getLineAttributes(la); return la; } @Override public int getTextAntialias() { return swtGraphics.getTextAntialias(); } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#drawImage(org.eclipse.swt.graphics.Image, * int, int) */ @Override public void drawImage(Image srcImage, int xpos, int ypos) { // Translate the Coordinates xpos += transX; ypos += transY; // Convert the SWT Image into an AWT BufferedImage BufferedImage toDraw = ImageConverter.convert(srcImage); checkState(); getGraphics2D().drawImage(toDraw, new AffineTransform(1f, 0f, 0f, 1f, xpos, ypos), null); } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#drawImage(org.eclipse.swt.graphics.Image, * int, int, int, int, int, int, int, int) */ @Override public void drawImage(Image srcImage, int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { x2 += transX; y2 += transY; BufferedImage toDraw = ImageConverter.convert(srcImage); checkState(); getGraphics2D().drawImage(toDraw, x2, y2, w2, h2, null); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawLine(int, int, int, int) */ @Override public void drawLine(int x1, int y1, int x2, int y2) { Line2D line = new Line2D.Float(x1 + transX, y1 + transY, x2 + transX, y2 + transY); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(line); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawOval(int, int, int, int) */ @Override public void drawOval(int x, int y, int w, int h) { Ellipse2D ellipse = new Ellipse2D.Float(x + transX, y + transY, w, h); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(ellipse); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillOval(int, int, int, int) */ @Override public void fillOval(int x, int y, int w, int h) { Ellipse2D ellipse = new Ellipse2D.Float(x + transX, y + transY, w - 1, h - 1); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getBackgroundColor())); getGraphics2D().fill(ellipse); } private Polygon createPolygon(PointList pointList) { Polygon toCreate = new Polygon(); for (int i = 0; i < pointList.size(); i++) { Point pt = pointList.getPoint(i); toCreate.addPoint(pt.x + transX, pt.y + transY); } return toCreate; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawPolygon(org.eclipse.draw2d.geometry. * PointList ) */ @Override public void drawPolygon(PointList pointList) { checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(createPolygon(pointList)); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillPolygon(org.eclipse.draw2d.geometry. * PointList ) */ @Override public void fillPolygon(PointList pointList) { checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getBackgroundColor())); getGraphics2D().fill(createPolygon(pointList)); } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#drawPolyline(org.eclipse.draw2d.geometry. * PointList) */ @Override public void drawPolyline(PointList pointList) { // Draw polylines as a series of lines for (int x = 1; x < pointList.size(); x++) { Point p1 = pointList.getPoint(x - 1); Point p2 = pointList.getPoint(x); drawLine(p1.x, p1.y, p2.x, p2.y); } } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawRectangle(int, int, int, int) */ @Override public void drawRectangle(int x, int y, int w, int h) { Rectangle2D rect = new Rectangle2D.Float(x + transX, y + transY, w, h); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(rect); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillRectangle(int, int, int, int) */ @Override public void fillRectangle(int x, int y, int width, int height) { Rectangle2D rect = new Rectangle2D.Float(x + transX, y + transY, width, height); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getBackgroundColor())); getGraphics2D().fill(rect); } public void fillRectangle(int width, int height, Color bgColor) { Rectangle2D rect = new Rectangle2D.Float(0, 0, width, height); checkState(); getGraphics2D().setPaint(getColor(bgColor)); getGraphics2D().fill(rect); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawRoundRectangle(org.eclipse.draw2d. * geometry .Rectangle, int, int) */ @Override public void drawRoundRectangle(Rectangle rect, int arcWidth, int arcHeight) { RoundRectangle2D roundRect = new RoundRectangle2D.Float(rect.x + transX, rect.y + transY, rect.width, rect.height, arcWidth, arcHeight); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(roundRect); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillRoundRectangle(org.eclipse.draw2d. * geometry .Rectangle, int, int) */ @Override public void fillRoundRectangle(Rectangle rect, int arcWidth, int arcHeight) { RoundRectangle2D roundRect = new RoundRectangle2D.Float(rect.x + transX, rect.y + transY, rect.width, rect.height, arcWidth, arcHeight); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getBackgroundColor())); getGraphics2D().fill(roundRect); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawText(java.lang.String, int, int) */ @Override public void drawText(String s, int x, int y) { drawString(s, x, y); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawString(java.lang.String, int, int) */ // private static Dimension swtStringSize; @Override public void drawString(String s, int x, int y) { if (s == null) return; java.awt.FontMetrics metrics = getGraphics2D().getFontMetrics(); int stringLength = metrics.stringWidth(s); // if (!this.display.isDisposed()) { // Runnable runnable = new Runnable() { // public void run() { // swtStringSize = TEXT_UTILITIES.getStringExtents(str, // swtGraphics.getFont()); // } // }; // display.syncExec(runnable); // } float xpos = x + transX; float ypos = y + transY; int lineWidth; // if (paintNotCompatibleStringsAsBitmaps // && Math.abs(swtStringSize.width - stringLength) > 2) { // // create SWT bitmap of the string then // Image image = new Image(display, swtStringSize.width + 1, // swtStringSize.height + 1); // GC gc = new GC(image); // gc.setForeground(getForegroundColor()); // gc.setBackground(getBackgroundColor()); // gc.setAntialias(getAntialias()); // gc.setFont(getFont()); // gc.drawString(s, 0, 0); // gc.dispose(); // ImageData data = image.getImageData(); // image.dispose(); // RGB backgroundRGB = getBackgroundColor().getRGB(); // for (int i = 0; i < data.width; i++) { // for (int j = 0; j < data.height; j++) { // if (data.palette.getRGB(data.getPixel(i, j)).equals( // backgroundRGB)) { // data.setAlpha(i, j, 0); // } else { // data.setAlpha(i, j, 255); // } // } // } // getGraphics2D().drawImage( // ImageConverter.convertFromImageData(data), // new AffineTransform(1f, 0f, 0f, 1f, xpos, ypos), null); // stringLength = swtStringSize.width; // } else { ypos += metrics.getAscent(); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().drawString(s, xpos, ypos); if (Platform.OS_LINUX.equals(Platform.getOS())) swtGraphics.drawString(s, (int) xpos, (int) ypos); // } if (isFontUnderlined(getFont())) { int baseline = y + metrics.getAscent(); lineWidth = getLineWidth(); setLineWidth(1); drawLine(x, baseline, x + stringLength, baseline); setLineWidth(lineWidth); } if (isFontStrikeout(getFont())) { int strikeline = y + (metrics.getHeight() / 2); lineWidth = getLineWidth(); setLineWidth(1); drawLine(x, strikeline, x + stringLength, strikeline); setLineWidth(lineWidth); } } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillString(java.lang.String, int, int) */ @Override public void fillString(String s, int x, int y) { // Not implemented } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillText(java.lang.String, int, int) */ @Override public void fillText(String s, int x, int y) { // Not implemented } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getBackgroundColor() */ @Override public Color getBackgroundColor() { return swtGraphics.getBackgroundColor(); } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#getClip(org.eclipse.draw2d.geometry.Rectangle * ) */ @Override public Rectangle getClip(Rectangle rect) { rect.setBounds(relativeClipRegion); return rect; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getFont() */ @Override public Font getFont() { return swtGraphics.getFont(); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getFontMetrics() */ @Override public FontMetrics getFontMetrics() { return swtGraphics.getFontMetrics(); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getForegroundColor() */ @Override public Color getForegroundColor() { return swtGraphics.getForegroundColor(); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getLineStyle() */ @Override public int getLineStyle() { return swtGraphics.getLineStyle(); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getLineWidth() */ @Override public int getLineWidth() { return swtGraphics.getLineWidth(); } @Override public float getLineWidthFloat() { return swtGraphics.getLineWidthFloat(); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getXORMode() */ @Override public boolean getXORMode() { return swtGraphics.getXORMode(); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#popState() */ @Override public void popState() { swtGraphics.popState(); restoreState(states.pop()); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#pushState() */ @Override public void pushState() { swtGraphics.pushState(); if (angle != 0) { getGraphics2D().rotate(Math.toRadians(360 - angle), rotateX, rotateY); angle = 0; } // Make a copy of the current state and push it onto the stack State toPush = new State(); toPush.copyFrom(currentState); states.push(toPush); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#restoreState() */ @Override public void restoreState() { swtGraphics.restoreState(); restoreState(states.peek()); } private void restoreState(State state) { setBackgroundColor(state.bgColor); setForegroundColor(state.fgColor); setBackgroundPattern(state.bgPattern); setForegroundPattern(state.fgPattern); setLineAttributes(state.lineAttributes); setXORMode(state.XorMode); setClipAbsolute(state.clipX, state.clipY, state.clipW, state.clipH); transX = currentState.translateX = state.translateX; transY = currentState.translateY = state.translateY; relativeClipRegion.x = state.clipX - transX; relativeClipRegion.y = state.clipY - transY; relativeClipRegion.width = state.clipW; relativeClipRegion.height = state.clipH; currentState.font = state.font; currentState.alpha = state.alpha; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#scale(double) */ @Override public void scale(double amount) { scale((float) amount, (float) amount); } @Override public void scale(float horizontal, float vertical) { if (horizontal == -1 || vertical == -1) { horizontalScale *= horizontal; verticalScale *= vertical; } else { horizontalScale = horizontal; verticalScale = vertical; } } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#setBackgroundColor(org.eclipse.swt.graphics * .Color) */ @Override public void setBackgroundColor(Color rgb) { currentState.bgColor = rgb; swtGraphics.setBackgroundColor(rgb); } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#setClip(org.eclipse.draw2d.geometry.Rectangle * ) */ @Override public void setClip(Rectangle rect) { relativeClipRegion.x = rect.x; relativeClipRegion.y = rect.y; relativeClipRegion.width = rect.width; relativeClipRegion.height = rect.height; setClipAbsolute(rect.x + transX, rect.y + transY, rect.width, rect.height); } /** * Sets the current clip values * * @param x * the x value * @param y * the y value * @param width * the width value * @param height * the height value */ private void setClipAbsolute(int x, int y, int width, int height) { currentState.clipX = x; currentState.clipY = y; currentState.clipW = width; currentState.clipH = height; } private boolean isFontUnderlined(Font f) { return false; } private boolean isFontStrikeout(Font f) { return false; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setFont(org.eclipse.swt.graphics.Font) */ @Override public void setFont(Font f) { swtGraphics.setFont(f); currentState.font = f; FontData[] fontInfo = f.getFontData(); if (fontInfo[0] != null) { int height = fontInfo[0].getHeight(); // float fsize = height * (float) display.getDPI().x / 72.0f; float fsize = height * 72.0f / 72.0f; height = Math.round(fsize); int style = fontInfo[0].getStyle(); boolean bItalic = (style & SWT.ITALIC) == SWT.ITALIC; boolean bBold = (style & SWT.BOLD) == SWT.BOLD; String faceName = fontInfo[0].getName(); int escapement = 0; boolean bUnderline = isFontUnderlined(f); boolean bStrikeout = isFontStrikeout(f); GdiFont font = new GdiFont(height, bItalic, bUnderline, bStrikeout, bBold, faceName, escapement); getGraphics2D().setFont(font.getFont()); } } /* * (non-Javadoc) * @see * org.eclipse.draw2d.Graphics#setForegroundColor(org.eclipse.swt.graphics * .Color) */ @Override public void setForegroundColor(Color rgb) { currentState.fgColor = rgb; swtGraphics.setForegroundColor(rgb); } /** * Sets the dash pattern when the custom line style is in use. Because this * feature is rarely used, the dash pattern may not be preserved when * calling {@link #pushState()} and {@link #popState()}. * * @param dash * the pixel pattern */ @Override public void setLineDash(int[] dash) { float dashFlt[] = new float[dash.length]; for (int i = 0; i < dash.length; i++) { dashFlt[i] = dash[i]; } setLineDash(dashFlt); } @Override public void setLineDash(float[] dash) { currentState.lineAttributes.dash = dash; setLineStyle(SWTGraphics.LINE_CUSTOM); swtGraphics.setLineDash(dash); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setLineStyle(int) */ @Override public void setLineStyle(int style) { currentState.lineAttributes.style = style; swtGraphics.setLineStyle(style); } /** * ignored */ @Override public void setLineMiterLimit(float miterLimit) { // do nothing swtGraphics.setLineMiterLimit(miterLimit); } /** * ignored */ @Override public void setLineCap(int cap) { // do nothing } /** * ignored */ @Override public void setLineJoin(int join) { // do nothing } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setLineWidth(int) */ @Override public void setLineWidth(int width) { setLineWidthFloat(width); } @Override public void setLineWidthFloat(float width) { currentState.lineAttributes.width = width; swtGraphics.setLineWidthFloat(width); } @Override public void setLineAttributes(LineAttributes lineAttributes) { SWTGraphics.copyLineAttributes(currentState.lineAttributes, lineAttributes); swtGraphics.setLineAttributes(lineAttributes); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setXORMode(boolean) */ @Override public void setXORMode(boolean xorMode) { currentState.XorMode = xorMode; swtGraphics.setXORMode(xorMode); } /** * Sets the current translation values * * @param x * the x translation value * @param y * the y translation value */ private void setTranslation(int x, int y) { transX = currentState.translateX = x; transY = currentState.translateY = y; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#translate(int, int) */ @Override public void translate(int dx, int dy) { swtGraphics.translate(dx, dy); setTranslation(transX + dx, transY + dy); relativeClipRegion.x -= dx; relativeClipRegion.y -= dy; } @Override public void translate(float dx, float dy) { dx *= horizontalScale; dy *= verticalScale; swtGraphics.translate(dx, dy); setTranslation(transX + (int) dx, transY + (int) dy); relativeClipRegion.x -= dx; relativeClipRegion.y -= dy; } /** * @return the <code>Graphics2D</code> that this is delegating to. */ protected Graphics2D getGraphics2D() { return graphics2D; } /** * @return Returns the swtGraphics. */ private SWTGraphics getSWTGraphics() { return swtGraphics; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillGradient(int, int, int, int, * boolean) */ @Override public void fillGradient(int x, int y, int w, int h, boolean vertical) { GradientPaint gradient; checkState(); // Gradients in SWT start with Foreground Color and end at Background java.awt.Color start = getColor(getSWTGraphics().getForegroundColor()); java.awt.Color stop = getColor(getSWTGraphics().getBackgroundColor()); // Create the Gradient based on horizontal or vertical if (vertical) { gradient = new GradientPaint(x + transX, y + transY, start, x + transX, y + h + transY, stop); } else { gradient = new GradientPaint(x + transX, y + transY, start, x + w + transX, y + transY, stop); } Paint oldPaint = getGraphics2D().getPaint(); getGraphics2D().setPaint(gradient); getGraphics2D() .fill(new Rectangle2D.Double(x + transX, y + transY, w, h)); getGraphics2D().setPaint(oldPaint); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#drawPath(org.eclipse.swt.graphics.Path) */ @Override public void drawPath(Path path) { GeneralPath pathAWT = createPathAWT(path); // getGraphics2D().draw(pathAWT); checkState(); getGraphics2D().setPaint(getColor(swtGraphics.getForegroundColor())); getGraphics2D().setStroke(createStroke()); getGraphics2D().draw(pathAWT); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#fillPath(org.eclipse.swt.graphics.Path) */ @Override public void fillPath(Path path) { checkState(); if (currentState.bgPattern == null || !(currentState.bgPattern instanceof GradientPattern)) { getGraphics2D() .setPaint(getColor(swtGraphics.getBackgroundColor())); } else { GradientPattern gp = (GradientPattern) currentState.bgPattern; getGraphics2D().setPaint(getColor(gp.color2)); } getGraphics2D().fill(createPathAWT(path)); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setClip(org.eclipse.swt.graphics.Path) */ @Override public void setClip(Path path) { if (((appliedState.graphicHints ^ currentState.graphicHints) & FILL_RULE_MASK) != 0) { // If there is a pending change to the fill rule, apply it first. // As long as the FILL_RULE is stored in a single bit, just toggling // it works. appliedState.graphicHints ^= FILL_RULE_MASK; } getGraphics2D().setClip(createPathAWT(path)); appliedState.clipX = currentState.clipX = 0; appliedState.clipY = currentState.clipY = 0; appliedState.clipW = currentState.clipW = 0; appliedState.clipH = currentState.clipH = 0; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getFillRule() */ @Override public int getFillRule() { return ((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setFillRule(int) */ @Override public void setFillRule(int rule) { currentState.graphicHints &= ~FILL_RULE_MASK; currentState.graphicHints |= (rule + FILL_RULE_WHOLE_NUMBER) << FILL_RULE_SHIFT; } private GeneralPath createPathAWT(Path path) { GeneralPath pathAWT = new GeneralPath(); PathData pathData = path.getPathData(); int idx = 0; for (int i = 0; i < pathData.types.length; i++) { switch (pathData.types[i]) { case SWT.PATH_MOVE_TO: pathAWT.moveTo( pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY); break; case SWT.PATH_LINE_TO: pathAWT.lineTo( pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY); break; case SWT.PATH_CUBIC_TO: pathAWT.curveTo( pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY, pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY, pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY); break; case SWT.PATH_QUAD_TO: pathAWT.quadTo( pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY, pathData.points[idx++] * horizontalScale + transX, pathData.points[idx++] * verticalScale + transY); break; case SWT.PATH_CLOSE: pathAWT.closePath(); break; default: dispose(); SWT.error(SWT.ERROR_INVALID_ARGUMENT); } } int swtWindingRule = ((appliedState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER; if (swtWindingRule == SWT.FILL_WINDING) { pathAWT.setWindingRule(GeneralPath.WIND_NON_ZERO); } else if (swtWindingRule == SWT.FILL_EVEN_ODD) { pathAWT.setWindingRule(GeneralPath.WIND_EVEN_ODD); } else { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } return pathAWT; } /* * (non-Javadoc) * @see org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal. * DrawableRenderedImage #allowDelayRender() */ public boolean shouldAllowDelayRender() { return false; } /* * (non-Javadoc) * @see org.eclipse.gmf.runtime.draw2d.ui.render.awt.internal. * DrawableRenderedImage #getMaximumRenderSize() */ public Dimension getMaximumRenderSize() { return null; } /** * Accessor method to return the translation offset for the graphics object * * @return <code>Point</code> x coordinate for graphics translation */ protected Point getTranslationOffset() { return new Point(transX, transY); } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#getAntialias() */ @Override public int getAntialias() { Object antiAlias = getGraphics2D() .getRenderingHint(RenderingHints.KEY_ANTIALIASING); if (antiAlias != null) { if (antiAlias.equals(RenderingHints.VALUE_ANTIALIAS_ON)) return SWT.ON; else if (antiAlias.equals(RenderingHints.VALUE_ANTIALIAS_OFF)) return SWT.OFF; else if (antiAlias.equals(RenderingHints.VALUE_ANTIALIAS_DEFAULT)) return SWT.DEFAULT; } return SWT.DEFAULT; } /* * (non-Javadoc) * @see org.eclipse.draw2d.Graphics#setAntialias(int) */ @Override public void setAntialias(int value) { if (value == SWT.ON) { getGraphics2D().setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } else if (value == SWT.OFF) { getGraphics2D().setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); } setAdvanced(true); } @Override public int getAlpha() { return swtGraphics.getAlpha(); } @Override public void setAlpha(int alpha) { alpha = Math.min(255, alpha); swtGraphics.setAlpha(alpha); currentState.alpha = alpha; Composite composite = getGraphics2D().getComposite(); if (composite instanceof AlphaComposite) { AlphaComposite newComposite = AlphaComposite.getInstance( ((AlphaComposite) composite).getRule(), (float) alpha / (float) 255); getGraphics2D().setComposite(newComposite); } } protected BasicStroke getStroke() { return stroke; } protected void setStroke(BasicStroke stroke) { this.stroke = stroke; getGraphics2D().setStroke(stroke); } /** * Sets and retirns AWT Stroke based on the value of * <code>LineAttributes</code> within the current state object * * @return the new AWT stroke */ private Stroke createStroke() { float factor = currentState.lineAttributes.width > 0 ? currentState.lineAttributes.width : 3; float awt_dash[]; int awt_cap; int awt_join; switch (currentState.lineAttributes.style) { case SWTGraphics.LINE_DASH: awt_dash = new float[] { factor * 6, factor * 3 }; break; case SWTGraphics.LINE_DASHDOT: awt_dash = new float[] { factor * 3, factor, factor, factor }; break; case SWTGraphics.LINE_DASHDOTDOT: awt_dash = new float[] { factor * 3, factor, factor, factor, factor, factor }; break; case SWTGraphics.LINE_DOT: awt_dash = new float[] { factor, factor }; break; case SWTGraphics.LINE_CUSTOM: awt_dash = currentState.lineAttributes.dash; break; default: awt_dash = null; } switch (currentState.lineAttributes.cap) { case SWT.CAP_FLAT: awt_cap = BasicStroke.CAP_BUTT; break; case SWT.CAP_ROUND: awt_cap = BasicStroke.CAP_ROUND; break; case SWT.CAP_SQUARE: awt_cap = BasicStroke.CAP_SQUARE; break; default: awt_cap = BasicStroke.CAP_BUTT; } switch (currentState.lineAttributes.join) { case SWT.JOIN_BEVEL: awt_join = BasicStroke.JOIN_BEVEL; break; case SWT.JOIN_MITER: awt_join = BasicStroke.JOIN_MITER; break; case SWT.JOIN_ROUND: awt_join = BasicStroke.JOIN_ROUND; default: awt_join = BasicStroke.JOIN_MITER; } /* * SWT paints line width == 0 as if it is == 1, so AWT is synced up with * that below. */ stroke = new BasicStroke( currentState.lineAttributes.width != 0 ? currentState.lineAttributes.width : 1, awt_cap, awt_join, currentState.lineAttributes.miterLimit, awt_dash, currentState.lineAttributes.dashOffset); return stroke; } @Override public boolean getAdvanced() { return (currentState.graphicHints & ADVANCED_GRAPHICS_MASK) != 0; } @Override public void setAdvanced(boolean value) { if (value) { currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; } else { currentState.graphicHints &= ~ADVANCED_GRAPHICS_MASK; } } @Override public void clipPath(Path path) { if (((appliedState.graphicHints ^ currentState.graphicHints) & FILL_RULE_MASK) != 0) { // If there is a pending change to the fill rule, apply it first. // As long as the FILL_RULE is stored in a single bit, just toggling // it works. appliedState.graphicHints ^= FILL_RULE_MASK; } setClip(path); getGraphics2D().clipRect(relativeClipRegion.x + transX, relativeClipRegion.y + transY, relativeClipRegion.width, relativeClipRegion.height); java.awt.Rectangle bounds = getGraphics2D().getClip().getBounds(); relativeClipRegion = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height); } @Override public void rotate(float degrees) { if (rotateOrientation == 0) { if (degrees > 0) rotateOrientation = 1; else if (degrees < 0) rotateOrientation = -1; } /* * Method was introduced to fix Bug 368146. With this at place no * exceptions happens during SVG export (which happened as soon as a * rotatable object like an ellipse is contained in the diagram), but * the object is still not rotated in the exported SVG graphics. */ if (swtGraphics.getAdvanced()) { swtGraphics.rotate(degrees); } /* * The rotation has to be forwarded to the SVG Graphics object. This * rotation is stateful, all drawing actions thereafter will be rotated. * Thus the rotation coordinates have to be remembered and the rotation * needs to be inverted before the next object is drawn. The inverted * rotation is hence triggered in pushState(). Fix for Bug 369241 */ rotateDetail(degrees); if (this.angle == 0.0) { rotateOrientation = 0 - rotateOrientation; rotateDetail(degrees); } } private void rotateDetail(float degrees) { if ((rotateOrientation == 1 && degrees > 0) || (rotateOrientation == -1 && degrees < 0)) { getGraphics2D().rotate(Math.toRadians(degrees), currentState.translateX, currentState.translateY); this.angle = degrees; this.rotateX = currentState.translateX; this.rotateY = currentState.translateY; } } @Override public int getLineCap() { return SWT.CAP_FLAT; } @Override public int getLineJoin() { return SWT.JOIN_MITER; } @Override public float getLineMiterLimit() { return 0; } @Override public void setBackgroundPattern(Pattern pattern) { currentState.bgPattern = pattern; } @Override public void setForegroundPattern(Pattern pattern) { currentState.fgPattern = pattern; } @Override public void setInterpolation(int interpolation) { // do nothing } @Override public void setLineDashOffset(float value) { // do nothing } @Override public void setTextAntialias(int value) { // do nothing } @Override public void shear(float horz, float vert) { // do nothing } }