/******************************************************************************* * Copyright (c) 2000, 2010, 2012 IBM Corporation, Gerhardt Informatics Kft. 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 * Gerhardt Informatics Kft. - GEFGWT port *******************************************************************************/ package org.eclipse.draw2d; import java.util.ArrayList; import java.util.List; import org.eclipse.draw2d.geometry.Dimension; 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.FontMetrics; import org.eclipse.swt.graphics.GC; 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.Region; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.graphics.Transform; import org.eclipse.swt.widgets.Display; /** * A concrete implementation of <code>Graphics</code> using an SWT * <code>GC</code>. There are 2 states contained in this graphics class -- the * applied state which is the actual state of the GC and the current state which * is the current state of this graphics object. Certain properties can be * changed multiple times and the GC won't be updated until it's actually used. * <P> * WARNING: This class is not intended to be subclassed. */ public class SWTGraphics extends Graphics { /** * An internal type used to represent and update the GC's clipping. * * @since 3.1 */ interface Clipping { /** * Sets the clip's bounding rectangle into the provided argument and * returns it for convenince. * * @param rect * the rect * @return the given rect * @since 3.1 */ Rectangle getBoundingBox(Rectangle rect); Clipping getCopy(); void intersect(int left, int top, int right, int bottom); void scale(float horizontal, float vertical); void setOn(GC gc, int translateX, int translateY); void translate(float dx, float dy); } /** * Any state stored in this class is only applied when it is needed by a * specific graphics call. * * @since 3.1 */ static class LazyState { Color bgColor; Color fgColor; Font font; int graphicHints; LineAttributes lineAttributes; Clipping relativeClip; } static class RectangleClipping implements Clipping { private float top, left, bottom, right; RectangleClipping(float left, float top, float right, float bottom) { this.left = left; this.right = right; this.bottom = bottom; this.top = top; } RectangleClipping(org.eclipse.swt.graphics.Rectangle rect) { left = rect.x; top = rect.y; right = rect.x + rect.width; bottom = rect.y + rect.height; } RectangleClipping(Rectangle rect) { left = rect.x; top = rect.y; right = rect.right(); bottom = rect.bottom(); } public Rectangle getBoundingBox(Rectangle rect) { rect.x = (int) left; rect.y = (int) top; rect.width = (int) Math.ceil(right) - rect.x; rect.height = (int) Math.ceil(bottom) - rect.y; return rect; } public Clipping getCopy() { return new RectangleClipping(left, top, right, bottom); } public void intersect(int left, int top, final int right, final int bottom) { // SzI: int l = (int) Math.ceil(this.left); int t = (int) Math.ceil(this.top); this.left = Math.max(Math.min(this.right, this.left), Math.min(right, left)); this.right = Math.min(Math.max(l, this.right), Math.max(left, right)); this.top = Math.max(Math.min(this.bottom, this.top), Math.min(bottom, top)); this.bottom = Math.min(Math.max(t, this.bottom), Math.max(top, bottom)); // use left/top -1 to ensure ceiling function doesn't add a pixel if (this.right < this.left || this.bottom < this.top) { this.right = this.left - 1; this.bottom = this.top - 1; } } public void scale(float horz, float vert) { // SzI: if (horz > 0 && vert > 0) { left /= horz; right /= horz; top /= vert; bottom /= vert; } else { if (horz < 0) { float l = left; left = right / horz; right = l / horz; } if (vert < 0) { float t = top; top = bottom / vert; bottom = t / vert; } } } public void setOn(GC gc, int translateX, int translateY) { int xInt = (int) Math.floor(left); int yInt = (int) Math.floor(top); // SzI: don't translate gc.setClipping(xInt + translateX, yInt + translateY, (int) Math.ceil(right) - xInt, (int) Math.ceil(bottom) - yInt); // gc.setClipping(xInt, yInt, (int) Math.ceil(right) - xInt, // (int) Math.ceil(bottom) - yInt); } public void translate(float dx, float dy) { // SzI: left += dx; right += dx; top += dy; bottom += dy; } } /** * Contains the entire state of the Graphics. */ static class State extends LazyState implements Cloneable { float affineMatrix[]; int alpha; int dx, dy; Pattern bgPattern; Pattern fgPattern; public State cloneState() { State clone = new State(); clone.alpha = this.alpha; clone.dx = dx; clone.dy = dy; if (affineMatrix != null) { float[] amCopy = new float[this.affineMatrix.length]; System.arraycopy(this.affineMatrix, 0, amCopy, 0, this.affineMatrix.length); clone.affineMatrix = amCopy; } else { clone.affineMatrix = null; } /** * FIXME: need to create a clone of the non-primitive types * * @return */ clone.fgPattern = this.fgPattern; clone.fgColor = this.fgColor; clone.bgColor = this.bgColor; clone.font = this.font; clone.bgPattern = this.bgPattern; clone.graphicHints = this.graphicHints; clone.relativeClip = this.relativeClip.getCopy(); clone.lineAttributes = SWTGraphics.clone(this.lineAttributes); return clone; } /** * Copies all state information from the given State to this State * * @param state * The State to copy from */ public void copyFrom(State state) { bgColor = state.bgColor; fgColor = state.fgColor; lineAttributes = SWTGraphics.clone(state.lineAttributes); dx = state.dx; dy = state.dy; bgPattern = state.bgPattern; fgPattern = state.fgPattern; font = state.font; graphicHints = state.graphicHints; affineMatrix = state.affineMatrix; relativeClip = state.relativeClip; alpha = state.alpha; } } static final int AA_MASK; static final int AA_SHIFT; static final int AA_WHOLE_NUMBER = 1; static final int ADVANCED_GRAPHICS_MASK; static final int ADVANCED_HINTS_DEFAULTS; static final int ADVANCED_HINTS_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; static final int INTERPOLATION_MASK; static final int INTERPOLATION_SHIFT; static final int INTERPOLATION_WHOLE_NUMBER = 1; static final int TEXT_AA_MASK; static final int TEXT_AA_SHIFT; static final int XOR_MASK; static final int XOR_SHIFT; static { XOR_SHIFT = 3; AA_SHIFT = 8; TEXT_AA_SHIFT = 10; INTERPOLATION_SHIFT = 12; FILL_RULE_SHIFT = 14; ADVANCED_SHIFT = 15; AA_MASK = 3 << AA_SHIFT; FILL_RULE_MASK = 1 << FILL_RULE_SHIFT; // If changed to more than 1-bit, // check references! INTERPOLATION_MASK = 3 << INTERPOLATION_SHIFT; TEXT_AA_MASK = 3 << TEXT_AA_SHIFT; XOR_MASK = 1 << XOR_SHIFT; ADVANCED_GRAPHICS_MASK = 1 << ADVANCED_SHIFT; ADVANCED_HINTS_MASK = TEXT_AA_MASK | AA_MASK | INTERPOLATION_MASK; ADVANCED_HINTS_DEFAULTS = ((SWT.DEFAULT + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT) | ((SWT.DEFAULT + AA_WHOLE_NUMBER) << AA_SHIFT) | ((SWT.DEFAULT + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT); } private final LazyState appliedState = new LazyState(); private final State currentState = new State(); private boolean elementsNeedUpdate; private GC gc; private boolean sharedClipping; private List stack = new ArrayList(); private int stackPointer = 0; Transform transform; private int translateX = 0; private int translateY = 0; private boolean useGradient = false; /** * Constructs a new SWTGraphics that draws to the Canvas using the given GC. * * @param gc * the GC */ public SWTGraphics(GC gc) { this.gc = gc; init(); } /** * If the background color has changed, this change will be pushed to the * GC. Also calls {@link #checkGC()}. */ protected final void checkFill() { if (!currentState.bgColor.equals(appliedState.bgColor) && currentState.bgPattern == null && useGradient == false) { gc.setBackground(appliedState.bgColor = currentState.bgColor); } checkGC(); } /** * If the rendering hints or the clip region has changed, these changes will * be pushed to the GC. Rendering hints include anti-alias, xor, join, cap, * line style, fill rule, interpolation, and other settings. */ protected final void checkGC() { if (appliedState.relativeClip != currentState.relativeClip) { appliedState.relativeClip = currentState.relativeClip; currentState.relativeClip.setOn(gc, translateX, translateY); } if (appliedState.graphicHints != currentState.graphicHints) { reconcileHints(gc, appliedState.graphicHints, currentState.graphicHints); appliedState.graphicHints = currentState.graphicHints; } } /** * If the line width, line style, foreground or background colors have * changed, these changes will be pushed to the GC. Also calls * {@link #checkGC()}. */ protected final void checkPaint() { checkGC(); if (currentState.fgColor != null) { if (!currentState.fgColor.equals(appliedState.fgColor) && currentState.fgPattern == null) { gc.setForeground(appliedState.fgColor = currentState.fgColor); } } LineAttributes lineAttributes = currentState.lineAttributes; if (!appliedState.lineAttributes.equals(lineAttributes)) { if (getAdvanced()) { gc.setLineAttributes(lineAttributes); } else { gc.setLineWidth((int) lineAttributes.width); gc.setLineCap(lineAttributes.cap); gc.setLineJoin(lineAttributes.join); gc.setLineStyle(lineAttributes.style); if (lineAttributes.dash != null) { gc.setLineDash(convertFloatArrayToInt(lineAttributes.dash)); } } appliedState.lineAttributes = clone(lineAttributes); } if (!currentState.bgColor.equals(appliedState.bgColor) && currentState.bgPattern == null) { gc.setBackground(appliedState.bgColor = currentState.bgColor); } } /** * @since 3.1 */ private void checkSharedClipping() { if (sharedClipping) { sharedClipping = false; boolean previouslyApplied = (appliedState == currentState.relativeClip); currentState.relativeClip = currentState.relativeClip.getCopy(); if (previouslyApplied) { appliedState.relativeClip = currentState.relativeClip; } } } /** * If the font has changed, this change will be pushed to the GC. Also calls * {@link #checkPaint()} and {@link #checkFill()}. */ protected final void checkText() { checkPaint(); if (appliedState.font.equals(currentState.font) == false) { gc.setFont(appliedState.font = currentState.font); } } /** * @see Graphics#clipRect(Rectangle) */ public void clipRect(Rectangle rect) { if (currentState.relativeClip == null) { throw new IllegalStateException( "The current clipping area does not " + //$NON-NLS-1$ "support intersection."); //$NON-NLS-1$ } checkSharedClipping(); currentState.relativeClip.intersect(rect.x, rect.y, rect.right(), rect.bottom()); appliedState.relativeClip = null; } /** * @see Graphics#dispose() */ public void dispose() { while (stackPointer > 0) { popState(); } if (transform != null) { transform.dispose(); } } /** * @see Graphics#drawArc(int, int, int, int, int, int) */ public void drawArc(int x, int y, int width, int height, int offset, int length) { checkPaint(); gc.drawArc(x + translateX, y + translateY, width, height, offset, length); } /** * @see Graphics#drawFocus(int, int, int, int) */ public void drawFocus(int x, int y, int w, int h) { checkPaint(); gc.drawFocus(x + translateX, y + translateY, w + 1, h + 1); } /** * @see Graphics#drawImage(Image, int, int) */ public void drawImage(Image srcImage, int x, int y) { checkGC(); gc.drawImage(srcImage, x + translateX, y + translateY); } /** * @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */ public void drawImage(Image srcImage, int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { checkGC(); gc.drawImage(srcImage, x1, y1, w1, h1, x2 + translateX, y2 + translateY, w2, h2); } /** * @see Graphics#drawLine(int, int, int, int) */ public void drawLine(int x1, int y1, int x2, int y2) { checkPaint(); gc.drawLine(x1 + translateX, y1 + translateY, x2 + translateX, y2 + translateY); } /** * @see Graphics#drawOval(int, int, int, int) */ public void drawOval(int x, int y, int width, int height) { checkPaint(); gc.drawOval(x + translateX, y + translateY, width, height); } /** * 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 GC#getAdvanced()}. * * @see Graphics#drawPath(Path) */ public void drawPath(Path path) { checkPaint(); initTransform(false); gc.drawPath(path); } /** * @see Graphics#drawPoint(int, int) */ public void drawPoint(int x, int y) { checkPaint(); gc.drawPoint(x + translateX, y + translateY); } /** * @see Graphics#drawPolygon(int[]) */ public void drawPolygon(int[] points) { checkPaint(); try { translatePointArray(points, translateX, translateY); gc.drawPolygon(points); } finally { translatePointArray(points, -translateX, -translateY); } } /** * @see Graphics#drawPolygon(PointList) */ public void drawPolygon(PointList points) { drawPolygon(points.toIntArray()); } /** * @see Graphics#drawPolyline(int[]) */ public void drawPolyline(int[] points) { checkPaint(); try { translatePointArray(points, translateX, translateY); gc.drawPolyline(points); } finally { translatePointArray(points, -translateX, -translateY); } } /** * @see Graphics#drawPolyline(PointList) */ public void drawPolyline(PointList points) { drawPolyline(points.toIntArray()); } /** * @see Graphics#drawRectangle(int, int, int, int) */ public void drawRectangle(int x, int y, int width, int height) { checkPaint(); gc.drawRectangle(x + translateX, y + translateY, width, height); } /** * @see Graphics#drawRoundRectangle(Rectangle, int, int) */ public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { checkPaint(); gc.drawRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height, arcWidth, arcHeight); } /** * @see Graphics#drawString(String, int, int) */ public void drawString(String s, int x, int y) { checkText(); Dimension textDimension = FigureUtilities.getTextExtents(s, getFont()); y += textDimension.height; gc.drawString(s, x + translateX, y + translateY, true); } /** * @see Graphics#drawText(String, int, int) */ public void drawText(String s, int x, int y) { checkText(); // Dimension textDimension = FigureUtilities.getTextExtents(s, // getFont()); // y = y + textDimension.height; gc.drawText(s, x + translateX, y + translateY, true); } /** * @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) { // $TODO probably just call checkPaint since Font and BG color don't // apply checkText(); layout.draw(gc, x + translateX, y + translateY, selectionStart, selectionEnd, selectionForeground, selectionBackground); } /** * @see Graphics#fillArc(int, int, int, int, int, int) */ public void fillArc(int x, int y, int width, int height, int offset, int length) { checkFill(); gc.fillArc(x + translateX, y + translateY, width, height, offset, length); } /** * @see Graphics#fillGradient(int, int, int, int, boolean) */ public void fillGradient(int x, int y, int w, int h, boolean vertical) { checkPaint(); gc.fillGradientRectangle(x + translateX, y + translateY, w, h, vertical); } /** * @see Graphics#fillOval(int, int, int, int) */ public void fillOval(int x, int y, int width, int height) { checkFill(); gc.fillOval(x + translateX, y + translateY, width, height); } /** * 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 GC#getAdvanced()}. * * @see Graphics#fillPath(Path) */ public void fillPath(Path path) { checkFill(); initTransform(false); gc.fillPath(path); } /** * @see Graphics#fillPolygon(int[]) */ public void fillPolygon(int[] points) { checkFill(); try { translatePointArray(points, translateX, translateY); gc.fillPolygon(points); } finally { translatePointArray(points, -translateX, -translateY); } } /** * @see Graphics#fillPolygon(PointList) */ public void fillPolygon(PointList points) { fillPolygon(points.toIntArray()); } /** * @see Graphics#fillRectangle(int, int, int, int) */ public void fillRectangle(int x, int y, int width, int height) { checkFill(); gc.fillRectangle(x + translateX, y + translateY, width, height); } /** * @see Graphics#fillRoundRectangle(Rectangle, int, int) */ public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { checkFill(); gc.fillRoundRectangle(r.x + translateX, r.y + translateY, r.width, r.height, arcWidth, arcHeight); } /** * @see Graphics#fillString(String, int, int) */ public void fillString(String s, int x, int y) { checkText(); gc.drawString(s, x + translateX, y + translateY, false); } /** * @see Graphics#fillText(String, int, int) */ public void fillText(String s, int x, int y) { checkText(); gc.drawText(s, x + translateX, y + translateY, false); } /** * @see Graphics#getAlpha() */ public int getAlpha() { return currentState.alpha; } /** * @see Graphics#getAntialias() */ public int getAntialias() { return ((currentState.graphicHints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER; } public boolean getAdvanced() { return (currentState.graphicHints & ADVANCED_GRAPHICS_MASK) != 0; } /** * @see Graphics#getBackgroundColor() */ public Color getBackgroundColor() { return currentState.bgColor; } /** * @see Graphics#getClip(Rectangle) */ public Rectangle getClip(Rectangle rect) { if (currentState.relativeClip != null) { currentState.relativeClip.getBoundingBox(rect); return rect; } else { throw new IllegalStateException( "Clipping can no longer be queried due to transformations"); //$NON-NLS-1$ } } /** * @see Graphics#getFillRule() */ public int getFillRule() { return ((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER; } /** * @see Graphics#getFont() */ public Font getFont() { return currentState.font; } /** * @see Graphics#getFontMetrics() */ public FontMetrics getFontMetrics() { checkText(); return gc.getFontMetrics(); } /** * @see Graphics#getForegroundColor() */ public Color getForegroundColor() { return currentState.fgColor; } /** * @see Graphics#getInterpolation() */ public int getInterpolation() { return ((currentState.graphicHints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT) - INTERPOLATION_WHOLE_NUMBER; } /** * @since 3.5 */ public void getLineAttributes(LineAttributes lineAttributes) { copyLineAttributes(lineAttributes, currentState.lineAttributes); } /** * @see Graphics#getLineCap() */ public int getLineCap() { return currentState.lineAttributes.cap; } /** * @see Graphics#getLineJoin() */ public int getLineJoin() { return currentState.lineAttributes.join; } /** * @see Graphics#getLineStyle() */ public int getLineStyle() { return currentState.lineAttributes.style; } /** * @see Graphics#getLineWidth() */ public int getLineWidth() { return (int) currentState.lineAttributes.width; } public float getLineWidthFloat() { return currentState.lineAttributes.width; } public float getLineMiterLimit() { return currentState.lineAttributes.miterLimit; } /** * @since 3.5 */ public float[] getLineDash() { float[] dash = currentState.lineAttributes.dash; float[] copy = new float[dash.length]; System.arraycopy(dash, 0, copy, 0, dash.length); return copy; } /** * @since 3.5 */ public float getLineDashOffset() { return currentState.lineAttributes.dashOffset; } /** * @see Graphics#getTextAntialias() */ public int getTextAntialias() { return ((currentState.graphicHints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER; } /** * @see Graphics#getXORMode() */ public boolean getXORMode() { return (currentState.graphicHints & XOR_MASK) != 0; } /** * Called by constructor, initializes all State information for currentState */ protected void init() { currentState.bgColor = appliedState.bgColor = gc.getBackground(); currentState.fgColor = appliedState.fgColor = gc.getForeground(); currentState.font = appliedState.font = gc.getFont(); currentState.lineAttributes = gc.getLineAttributes(); appliedState.lineAttributes = clone(currentState.lineAttributes); currentState.graphicHints |= gc.getLineStyle(); currentState.graphicHints |= gc.getAdvanced() ? ADVANCED_GRAPHICS_MASK : 0; currentState.graphicHints |= gc.getXORMode() ? XOR_MASK : 0; appliedState.graphicHints = currentState.graphicHints; currentState.relativeClip = new RectangleClipping(gc.getClipping()); currentState.alpha = gc.getAlpha(); } private void initTransform(boolean force) { if (!force && translateX == 0 && translateY == 0) { return; } if (transform == null) { transform = new Transform(Display.getCurrent()); elementsNeedUpdate = true; transform.translate(translateX, translateY); translateX = 0; translateY = 0; gc.setTransform(transform); currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; } } /** * @see Graphics#popState() */ public void popState() { stackPointer--; restoreState((State) stack.get(stackPointer)); } /** * @see Graphics#pushState() */ public void pushState() { if (currentState.relativeClip == null) { throw new IllegalStateException( "The clipping has been modified in " + //$NON-NLS-1$ "a way that cannot be saved and restored."); //$NON-NLS-1$ } State s; currentState.dx = translateX; currentState.dy = translateY; if (elementsNeedUpdate) { elementsNeedUpdate = false; transform.getElements(currentState.affineMatrix = new float[6]); } if (stack.size() > stackPointer) { s = (State) stack.get(stackPointer); s.copyFrom(currentState); } else { stack.add(currentState.cloneState()); } sharedClipping = true; stackPointer++; } private static void reconcileHints(GC gc, int applied, int hints) { int changes = hints ^ applied; if ((changes & XOR_MASK) != 0) { gc.setXORMode((hints & XOR_MASK) != 0); } // Check to see if there is anything remaining changes &= ~XOR_MASK; if (changes != 0) { if ((changes & INTERPOLATION_MASK) != 0) { gc.setInterpolation(((hints & INTERPOLATION_MASK) >> INTERPOLATION_SHIFT) - INTERPOLATION_WHOLE_NUMBER); } if ((changes & FILL_RULE_MASK) != 0) { gc.setFillRule(((hints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER); } if ((changes & AA_MASK) != 0) { gc.setAntialias(((hints & AA_MASK) >> AA_SHIFT) - AA_WHOLE_NUMBER); } if ((changes & TEXT_AA_MASK) != 0) { gc.setTextAntialias(((hints & TEXT_AA_MASK) >> TEXT_AA_SHIFT) - AA_WHOLE_NUMBER); } // If advanced was flagged, but none of the conditions which trigger // advanced // actually got applied, force advanced graphics on. if ((changes & ADVANCED_GRAPHICS_MASK) != 0) { if ((hints & ADVANCED_GRAPHICS_MASK) != 0 && !gc.getAdvanced()) { gc.setAdvanced(true); } } } } /** * @see Graphics#restoreState() */ public void restoreState() { restoreState((State) stack.get(stackPointer - 1)); } /** * Sets all State information to that of the given State, called by * restoreState() * * @param s * the State */ protected void restoreState(State s) { /* * We must set the transformation matrix first since it affects things * like clipping regions and patterns. */ setAffineMatrix(s.affineMatrix); currentState.relativeClip = s.relativeClip; sharedClipping = true; // If the GC is currently advanced, but it was not when pushed, revert if (gc.getAdvanced() && (s.graphicHints & ADVANCED_GRAPHICS_MASK) == 0) { // Set applied clip to null to force a re-setting of the clipping. appliedState.relativeClip = null; gc.setAdvanced(false); appliedState.graphicHints &= ~ADVANCED_HINTS_MASK; appliedState.graphicHints |= ADVANCED_HINTS_DEFAULTS; } setBackgroundColor(s.bgColor); setBackgroundPattern(s.bgPattern); setForegroundColor(s.fgColor); setForegroundPattern(s.fgPattern); setAlpha(s.alpha); setLineAttributes(s.lineAttributes); setFont(s.font); // This method must come last because above methods will incorrectly set // advanced state setGraphicHints(s.graphicHints); translateX = currentState.dx = s.dx; translateY = currentState.dy = s.dy; } /** * 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 GC#getAdvanced()}. * * @see Graphics#rotate(float) */ public void rotate(float degrees) { // Flush clipping, patter, etc., before applying transform checkGC(); initTransform(true); transform.rotate(-degrees); gc.setTransform(transform); elementsNeedUpdate = true; // Can no longer operate or maintain clipping // appliedState.relativeClip = currentState.relativeClip = null; } double scale = 1.0; /** * @see Graphics#scale(double) */ public void scale(double factor) { scale((float) factor, (float) factor); this.scale = factor; } @Override public double getAbsoluteScale() { return scale; } /** * 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 GC#getAdvanced()}. * * @see org.eclipse.draw2d.Graphics#scale(float, float) */ public void scale(float horizontal, float vertical) { // Flush any clipping before scaling checkGC(); initTransform(true); transform.scale(horizontal, vertical); gc.setTransform(transform); elementsNeedUpdate = true; checkSharedClipping(); if (currentState.relativeClip != null) currentState.relativeClip.scale(horizontal, vertical); } private void setAffineMatrix(float[] m) { if (!elementsNeedUpdate && currentState.affineMatrix == m) return; currentState.affineMatrix = m; if (m != null) transform.setElements(m[0], m[1], m[2], m[3], m[4], m[5]); else if (transform != null) { transform.dispose(); transform = null; elementsNeedUpdate = false; } gc.setTransform(transform); } /** * 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 GC#getAdvanced()}. * * @see Graphics#setAlpha(int) */ public void setAlpha(int alpha) { currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; if (currentState.alpha != alpha) gc.setAlpha(this.currentState.alpha = 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 GC#getAdvanced()}. * * @see Graphics#setAntialias(int) */ public void setAntialias(int value) { currentState.graphicHints &= ~AA_MASK; currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << AA_SHIFT; } public void setAdvanced(boolean value) { if (value) { currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; } else { currentState.graphicHints &= ~ADVANCED_GRAPHICS_MASK; } } /** * @see Graphics#setBackgroundColor(Color) */ public void setBackgroundColor(Color color) { useGradient = false; currentState.bgColor = color; if (currentState.bgPattern != null) { currentState.bgPattern = null; // Force the BG color to be stale appliedState.bgColor = null; } } /** * @see Graphics#setBackgroundPattern(Pattern) */ public void setBackgroundPattern(Pattern pattern) { currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; if (currentState.bgPattern == pattern) { return; } currentState.bgPattern = pattern; if (pattern != null) { initTransform(true); } gc.setBackgroundPattern(pattern); } /** * 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 GC#getAdvanced()}. * * @see Graphics#setClip(Path) */ public void setClip(Path path) { initTransform(false); if (((appliedState.graphicHints ^ currentState.graphicHints) & FILL_RULE_MASK) != 0) { // If there is a pending change to the fill rule, apply it first. gc.setFillRule(((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER); // As long as the FILL_RULE is stored in a single bit, just toggling // it works. appliedState.graphicHints ^= FILL_RULE_MASK; } gc.setClipping(path); appliedState.relativeClip = currentState.relativeClip = null; } /** * Simple implementation of clipping a Path within the context of current * clipping rectangle for now (not region) <li>Note that this method wipes * out the clipping rectangle area, hence if clients need to reset it call * {@link #restoreState()} * * @see org.eclipse.draw2d.Graphics#clipPath(org.eclipse.swt.graphics.Path) */ public void clipPath(Path path) { initTransform(false); if (((appliedState.graphicHints ^ currentState.graphicHints) & FILL_RULE_MASK) != 0) { // If there is a pending change to the fill rule, apply it first. gc.setFillRule(((currentState.graphicHints & FILL_RULE_MASK) >> FILL_RULE_SHIFT) - FILL_RULE_WHOLE_NUMBER); // As long as the FILL_RULE is stored in a single bit, just toggling // it works. appliedState.graphicHints ^= FILL_RULE_MASK; } Rectangle clipping = currentState.relativeClip != null ? getClip(new Rectangle()) : new Rectangle(); if (!clipping.isEmpty()) { Path flatPath = new Path(path.getDevice(), path, 0.01f); PathData pathData = flatPath.getPathData(); flatPath.dispose(); Region region = new Region(path.getDevice()); loadPath(region, pathData.points, pathData.types); region.intersect(new org.eclipse.swt.graphics.Rectangle(clipping.x, clipping.y, clipping.width, clipping.height)); gc.setClipping(region); appliedState.relativeClip = currentState.relativeClip = null; region.dispose(); } } /** * @see Graphics#setClip(Rectangle) */ public void setClip(Rectangle rect) { currentState.relativeClip = new RectangleClipping(rect); } /** * @see Graphics#setFillRule(int) */ public void setFillRule(int rule) { currentState.graphicHints &= ~FILL_RULE_MASK; currentState.graphicHints |= (rule + FILL_RULE_WHOLE_NUMBER) << FILL_RULE_SHIFT; } /** * @see Graphics#setFont(Font) */ public void setFont(Font f) { currentState.font = f; } /** * @see Graphics#setForegroundColor(Color) */ public void setForegroundColor(Color color) { currentState.fgColor = color; if (currentState.fgPattern != null) { currentState.fgPattern = null; // Force fgColor to be stale appliedState.fgColor = null; } } /** * @see Graphics#setForegroundPattern(Pattern) */ public void setForegroundPattern(Pattern pattern) { currentState.graphicHints |= ADVANCED_GRAPHICS_MASK; if (currentState.fgPattern == pattern) { return; } currentState.fgPattern = pattern; if (pattern != null) { initTransform(true); } gc.setForegroundPattern(pattern); } private void setGraphicHints(int hints) { currentState.graphicHints = hints; } /** * 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 GC#getAdvanced()}. * * @see Graphics#setInterpolation(int) */ public void setInterpolation(int interpolation) { // values range [-1, 3] currentState.graphicHints &= ~INTERPOLATION_MASK; currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (interpolation + INTERPOLATION_WHOLE_NUMBER) << INTERPOLATION_SHIFT; } public void setLineAttributes(LineAttributes lineAttributes) { copyLineAttributes(currentState.lineAttributes, lineAttributes); } /** * @see Graphics#setLineCap(int) */ public void setLineCap(int value) { currentState.lineAttributes.cap = value; } /** * @see Graphics#setLineDash(int[]) */ public void setLineDash(int[] dashes) { float[] fArray = null; if (dashes != null) { fArray = new float[dashes.length]; for (int i = 0; i < dashes.length; i++) { fArray[i] = dashes[i]; } } setLineDash(fArray); } /** * @param value * @since 3.5 */ public void setLineDash(float[] value) { if (value != null) { // validate dash values for (int i = 0; i < value.length; i++) { if (value[i] <= 0) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } } float[] valueCopy = new float[value.length]; System.arraycopy(value, 0, valueCopy, 0, value.length); currentState.lineAttributes.dash = valueCopy; currentState.lineAttributes.style = SWT.LINE_CUSTOM; } else { currentState.lineAttributes.dash = null; currentState.lineAttributes.style = SWT.LINE_SOLID; } } /** * @since 3.5 */ public void setLineDashOffset(float value) { currentState.lineAttributes.dashOffset = value; } /** * @see Graphics#setLineJoin(int) */ public void setLineJoin(int value) { currentState.lineAttributes.join = value; } /** * @see Graphics#setLineStyle(int) */ public void setLineStyle(int value) { currentState.lineAttributes.style = value; } /** * @see Graphics#setLineWidth(int) */ public void setLineWidth(int width) { currentState.lineAttributes.width = width; } public void setLineWidthFloat(float value) { currentState.lineAttributes.width = value; } public void setLineMiterLimit(float value) { currentState.lineAttributes.miterLimit = value; } /** * 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 GC#getAdvanced()}. * * @see Graphics#setTextAntialias(int) */ public void setTextAntialias(int value) { currentState.graphicHints &= ~TEXT_AA_MASK; currentState.graphicHints |= ADVANCED_GRAPHICS_MASK | (value + AA_WHOLE_NUMBER) << TEXT_AA_SHIFT; } /** * @see Graphics#setXORMode(boolean) */ public void setXORMode(boolean xor) { currentState.graphicHints &= ~XOR_MASK; if (xor) { currentState.graphicHints |= XOR_MASK; } } /** * 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 GC#getAdvanced()}. * * @see Graphics#shear(float, float) */ public void shear(float horz, float vert) { // Flush any clipping changes before shearing checkGC(); initTransform(true); float matrix[] = new float[6]; transform.getElements(matrix); transform.setElements(matrix[0] + matrix[2] * vert, matrix[1] + matrix[3] * vert, matrix[0] * horz + matrix[2], matrix[1] * horz + matrix[3], matrix[4], matrix[5]); gc.setTransform(transform); elementsNeedUpdate = true; // Can no longer track clipping changes appliedState.relativeClip = currentState.relativeClip = null; } /** * This method may require advanced graphics support if using a transform, * in this case, a check should be made to ensure advanced graphics is * supported in the user's environment before calling this method. See * {@link GC#getAdvanced()}. * * @see Graphics#translate(int, int) */ public void translate(int dx, int dy) { if (dx == 0 && dy == 0) return; if (transform != null) { // Flush clipping, pattern, etc. before applying transform checkGC(); transform.translate(dx, dy); elementsNeedUpdate = true; gc.setTransform(transform); } else { translateX += dx; translateY += dy; } checkSharedClipping(); if (currentState.relativeClip != null) currentState.relativeClip.translate(-dx, -dy); } /** * 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 GC#getAdvanced()}. * * @see Graphics#translate(float, float) */ public void translate(float dx, float dy) { initTransform(true); checkGC(); transform.translate(dx, dy); elementsNeedUpdate = true; gc.setTransform(transform); checkSharedClipping(); if (currentState.relativeClip != null) currentState.relativeClip.translate(-dx, -dy); } private void translatePointArray(int[] points, int translateX, int translateY) { if (translateX == 0 && translateY == 0) return; for (int i = 0; (i + 1) < points.length; i += 2) { points[i] += translateX; points[i + 1] += translateY; } } /** * Countermeasure against LineAttributes class not having its own clone() * method. * * @since 3.6 */ public static LineAttributes clone(LineAttributes src) { float[] dashClone = null; if (src != null) { if (src.dash != null) { dashClone = new float[src.dash.length]; System.arraycopy(src.dash, 0, dashClone, 0, dashClone.length); } return new LineAttributes(src.width, src.cap, src.join, src.style, dashClone, src.dashOffset, src.miterLimit); } return null; } /** * Countermeasure against LineAttributes class not having a copy by value * function. * * @since 3.6 */ public static void copyLineAttributes(LineAttributes dest, LineAttributes src) { if (dest != src) { dest.cap = src.cap; dest.join = src.join; dest.miterLimit = src.miterLimit; dest.style = src.style; dest.width = src.width; dest.dashOffset = src.dashOffset; if (src.dash == null) { dest.dash = null; } else { if ((dest.dash == null) || (dest.dash.length != src.dash.length)) { dest.dash = new float[src.dash.length]; } System.arraycopy(src.dash, 0, dest.dash, 0, src.dash.length); } } } /** * Utility method for use with countermeasure against passing line * attributes to SWT forcing advanced graphics. * * @return * @since 3.2 */ private static int[] convertFloatArrayToInt(float[] fArray) { int[] iArray = null; if (fArray != null) { int arrayLen = fArray.length; iArray = new int[arrayLen]; for (int i = 0; i < arrayLen; i++) { iArray[i] = (int) fArray[i]; } } return iArray; } static void loadPath(Region region, float[] points, byte[] types) { int start = 0, end = 0; for (int i = 0; i < types.length; i++) { switch (types[i]) { case SWT.PATH_MOVE_TO: { if (start != end) { int n = 0; int[] temp = new int[end - start]; for (int k = start; k < end; k++) { temp[n++] = Math.round(points[k]); } region.add(temp); } start = end; end += 2; break; } case SWT.PATH_LINE_TO: { end += 2; break; } case SWT.PATH_CLOSE: { if (start != end) { int n = 0; int[] temp = new int[end - start]; for (int k = start; k < end; k++) { temp[n++] = Math.round(points[k]); } region.add(temp); } start = end; break; } } } if (start != end) { int n = 0; int[] temp = new int[end - start]; for (int k = start; k < end; k++) { temp[n++] = Math.round(points[k]); } region.add(temp); } } public GC getGC() { return gc; } public void setLineGradient(int i, int y, Color bgc, int j, int height, Color transformColor) { useGradient = true; gc.setFillStyle(i, y, bgc, j, height, transformColor); } }