/* * Copyright 2015 Brandon Borkholder * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jogamp.glg2d.impl; import java.awt.BasicStroke; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.util.ArrayDeque; import java.util.Deque; import org.jogamp.glg2d.GLG2DShapeHelper; import org.jogamp.glg2d.GLGraphics2D; import org.jogamp.glg2d.PathVisitor; public abstract class AbstractShapeHelper implements GLG2DShapeHelper { /** * We know this is single-threaded, so we can use these as archetypes. */ protected static final Ellipse2D.Float ELLIPSE = new Ellipse2D.Float(); protected static final RoundRectangle2D.Float ROUND_RECT = new RoundRectangle2D.Float(); protected static final Arc2D.Float ARC = new Arc2D.Float(); protected static final Rectangle2D.Float RECT = new Rectangle2D.Float(); protected static final Line2D.Float LINE = new Line2D.Float(); protected Deque<Stroke> strokeStack = new ArrayDeque<Stroke>(); public AbstractShapeHelper() { strokeStack.push(new BasicStroke()); } @Override public void setG2D(GLGraphics2D g2d) { strokeStack.clear(); strokeStack.push(new BasicStroke()); } @Override public void push(GLGraphics2D newG2d) { strokeStack.push(newG2d.getStroke()); } @Override public void pop(GLGraphics2D parentG2d) { strokeStack.pop(); } @Override public void setHint(Key key, Object value) { // nop } @Override public void resetHints() { setHint(RenderingHints.KEY_ANTIALIASING, null); } @Override public void dispose() { // nop } @Override public void setStroke(Stroke stroke) { strokeStack.pop(); strokeStack.push(stroke); } @Override public Stroke getStroke() { return strokeStack.peek(); } @Override public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight, boolean fill) { ROUND_RECT.setRoundRect(x, y, width, height, arcWidth, arcHeight); if (fill) { fill(ROUND_RECT, true); } else { draw(ROUND_RECT); } } @Override public void drawRect(int x, int y, int width, int height, boolean fill) { RECT.setRect(x, y, width, height); if (fill) { fill(RECT, true); } else { draw(RECT); } } @Override public void drawLine(int x1, int y1, int x2, int y2) { LINE.setLine(x1, y1, x2, y2); draw(LINE); } @Override public void drawOval(int x, int y, int width, int height, boolean fill) { ELLIPSE.setFrame(x, y, width, height); if (fill) { fill(ELLIPSE, true); } else { draw(ELLIPSE); } } @Override public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle, boolean fill) { ARC.setArc(x, y, width, height, startAngle, arcAngle, fill ? Arc2D.PIE : Arc2D.OPEN); if (fill) { fill(ARC, true); } else { draw(ARC); } } @Override public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { drawPoly(xPoints, yPoints, nPoints, false, false); } @Override public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints, boolean fill) { drawPoly(xPoints, yPoints, nPoints, fill, true); } protected void drawPoly(int[] xPoints, int[] yPoints, int nPoints, boolean fill, boolean close) { Path2D.Float path = new Path2D.Float(PathIterator.WIND_NON_ZERO, nPoints); path.moveTo(xPoints[0], yPoints[0]); for (int i = 1; i < nPoints; i++) { path.lineTo(xPoints[i], yPoints[i]); } if (close) { path.closePath(); } if (fill) { fill(path); } else { draw(path); } } @Override public void fill(Shape shape) { if (shape instanceof Rectangle2D || shape instanceof Ellipse2D || shape instanceof Arc2D || shape instanceof RoundRectangle2D) { fill(shape, true); } else { fill(shape, false); } } protected abstract void fill(Shape shape, boolean isDefinitelySimpleConvex); protected void traceShape(Shape shape, PathVisitor visitor) { visitShape(shape, visitor); } public static void visitShape(Shape shape, PathVisitor visitor) { PathIterator iterator = shape.getPathIterator(null); visitor.beginPoly(iterator.getWindingRule()); float[] coords = new float[10]; float[] previousVertex = new float[2]; for (; !iterator.isDone(); iterator.next()) { int type = iterator.currentSegment(coords); switch (type) { case PathIterator.SEG_MOVETO: visitor.moveTo(coords); break; case PathIterator.SEG_LINETO: visitor.lineTo(coords); break; case PathIterator.SEG_QUADTO: visitor.quadTo(previousVertex, coords); break; case PathIterator.SEG_CUBICTO: visitor.cubicTo(previousVertex, coords); break; case PathIterator.SEG_CLOSE: visitor.closeLine(); break; } switch (type) { case PathIterator.SEG_LINETO: case PathIterator.SEG_MOVETO: previousVertex[0] = coords[0]; previousVertex[1] = coords[1]; break; case PathIterator.SEG_QUADTO: previousVertex[0] = coords[2]; previousVertex[1] = coords[3]; break; case PathIterator.SEG_CUBICTO: previousVertex[0] = coords[4]; previousVertex[1] = coords[5]; break; } } visitor.endPoly(); } }