/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.draw2d; import java.util.ArrayList; import java.util.List; 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.Pattern; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.graphics.Transform; import org.eclipse.swt.widgets.Display; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; /** * 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) { this.left = Math.max(this.left, left); this.right = Math.min(this.right, right); this.top = Math.max(this.top, top); this.bottom = Math.min(this.bottom, bottom); if (right < left || bottom < top) { //width and height of -1 to avoid ceiling function from re-adding a pixel. this.right = left - 1; this.bottom = top - 1; } } public void scale(float horz, float vert) { left /= horz; right /= horz; top /= vert; bottom /= vert; } public void setOn(GC gc, int translateX, int translateY) { int xInt = (int)Math.floor(left); int yInt = (int)Math.floor(top); gc.setClipping(xInt + translateX, yInt + translateY, (int)Math.ceil(right) - xInt, (int)Math.ceil(bottom) - yInt); } public void translate(float dx, float dy) { 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; Pattern bgPattern; int dx, dy; Pattern fgPattern; public Object clone() throws CloneNotSupportedException { State clone = (State)super.clone(); clone.lineAttributes = SWTGraphics.clone(clone.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; /** * 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) { 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.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)) { 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(); gc.drawString(s, x + translateX, y + translateY, true); } /** * @see Graphics#drawText(String, int, int) */ public void drawText(String s, int x, int y) { checkText(); 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() { return (float[])currentState.lineAttributes.dash.clone(); } /** * @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$ } try { 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.clone()); } sharedClipping = true; stackPointer++; } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } 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; } /** * @see Graphics#scale(double) */ public void scale(double factor) { scale((float)factor, (float)factor); } /** * 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) { 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; } /** * @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); } } currentState.lineAttributes.dash = (float[])value.clone(); 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.5 */ public static LineAttributes clone(LineAttributes src) { float[] dashClone = 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); } /** * Countermeasure against LineAttributes class not having a copy by value function. * @since 3.5 */ 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; 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; } }