blob: 0c280ee6f6e12ccd38e75baf46488e83fe922a95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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.draw2d.rap.swt.graphics.LineAttributes;
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.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;
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);
// 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) {
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);
// UNSUPPORTED - api is not implemented in RAP
// 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();
org.eclipse.swt.graphics.LineAttributes lineAttributes = gc.getLineAttributes();
currentState.lineAttributes = new LineAttributes(lineAttributes.width,lineAttributes.cap,lineAttributes.join);
appliedState.lineAttributes = clone(currentState.lineAttributes);
// UNSUPPORTED - api is not implemented in RAP
// 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;
// UNSUPPORTED - api is not implemented in RAP
// 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;
// UNSUPPORTED - api is not implemented in RAP
// 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) {
// UNSUPPORTED - api is not implemented in RAP
// 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 & 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);
// UNSUPPORTED - api is not implemented in RAP
// 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);
// UNSUPPORTED - api is not implemented in RAP
// 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;
}
// UNSUPPORTED - api is not implemented in RAP
// 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);
}
// UNSUPPORTED - api is not implemented in RAP
// 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.
// UNSUPPORTED - api is not implemented in RAP
// 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.
// UNSUPPORTED - api is not implemented in RAP
// 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()) {
// UNSUPPORTED - api is not implemented in RAP
// Path flatPath = new Path(path.getDevice(), path, 0.01f);
// PathData pathData = flatPath.getPathData();
// flatPath.dispose();
Region region = new Region(path.getDevice());
// UNSUPPORTED - api is not implemented in RAP
// 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.getBounds());
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);
}
// UNSUPPORTED - api is not implemented in RAP
// 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 = org.eclipse.draw2d.rap.swt.SWT.LINE_CUSTOM;
} else {
currentState.lineAttributes.dash = null;
currentState.lineAttributes.style = org.eclipse.draw2d.rap.swt.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]);
// UNSUPPORTED - api is not implemented in RAP
// 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;
// UNSUPPORTED - api is not implemented in RAP
// 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;
// UNSUPPORTED - api is not implemented in RAP
// 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.dash != null) {
dashClone = new float[src.dash.length];
System.arraycopy(src.dash, 0, dashClone, 0, dashClone.length);
}
// UNSUPPORTED - api is not implemented in RAP
// return new LineAttributes(src.width, src.cap, src.join, src.style,
// dashClone, src.dashOffset, src.miterLimit);
return new LineAttributes(src.width, src.cap, src.join);
}
/**
* 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);
}
}
}