/*
 * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.java2d.pipe;

import java.awt.BasicStroke;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.IllegalPathStateException;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.ProcessPath;
import static sun.java2d.pipe.BufferedOpCodes.*;

Base class for enqueuing rendering operations in a single-threaded rendering environment. Instead of each operation being rendered immediately by the underlying graphics library, the operation will be added to the provided RenderQueue, which will be processed at a later time by a single thread. This class provides implementations of drawLine(), drawRect(), drawPoly(), fillRect(), draw(Shape), and fill(Shape), which are useful for a hardware-accelerated renderer. The other draw*() and fill*() methods simply delegate to draw(Shape) and fill(Shape), respectively.
/** * Base class for enqueuing rendering operations in a single-threaded * rendering environment. Instead of each operation being rendered * immediately by the underlying graphics library, the operation will be * added to the provided RenderQueue, which will be processed at a later * time by a single thread. * * This class provides implementations of drawLine(), drawRect(), drawPoly(), * fillRect(), draw(Shape), and fill(Shape), which are useful for a * hardware-accelerated renderer. The other draw*() and fill*() methods * simply delegate to draw(Shape) and fill(Shape), respectively. */
public abstract class BufferedRenderPipe implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, ParallelogramPipe { ParallelogramPipe aapgrampipe = new AAParallelogramPipe(); static final int BYTES_PER_POLY_POINT = 8; static final int BYTES_PER_SCANLINE = 12; static final int BYTES_PER_SPAN = 16; protected RenderQueue rq; protected RenderBuffer buf; private BufferedDrawHandler drawHandler; public BufferedRenderPipe(RenderQueue rq) { this.rq = rq; this.buf = rq.getBuffer(); this.drawHandler = new BufferedDrawHandler(); } public ParallelogramPipe getAAParallelogramPipe() { return aapgrampipe; }
Validates the state in the provided SunGraphics2D object and sets up any special resources for this operation (e.g. enabling gradient shading).
/** * Validates the state in the provided SunGraphics2D object and sets up * any special resources for this operation (e.g. enabling gradient * shading). */
protected abstract void validateContext(SunGraphics2D sg2d); protected abstract void validateContextAA(SunGraphics2D sg2d); public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) { int transx = sg2d.transX; int transy = sg2d.transY; rq.lock(); try { validateContext(sg2d); rq.ensureCapacity(20); buf.putInt(DRAW_LINE); buf.putInt(x1 + transx); buf.putInt(y1 + transy); buf.putInt(x2 + transx); buf.putInt(y2 + transy); } finally { rq.unlock(); } } public void drawRect(SunGraphics2D sg2d, int x, int y, int width, int height) { rq.lock(); try { validateContext(sg2d); rq.ensureCapacity(20); buf.putInt(DRAW_RECT); buf.putInt(x + sg2d.transX); buf.putInt(y + sg2d.transY); buf.putInt(width); buf.putInt(height); } finally { rq.unlock(); } } public void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) { rq.lock(); try { validateContext(sg2d); rq.ensureCapacity(20); buf.putInt(FILL_RECT); buf.putInt(x + sg2d.transX); buf.putInt(y + sg2d.transY); buf.putInt(width); buf.putInt(height); } finally { rq.unlock(); } } public void drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) { draw(sg2d, new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight)); } public void fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) { fill(sg2d, new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight)); } public void drawOval(SunGraphics2D sg2d, int x, int y, int width, int height) { draw(sg2d, new Ellipse2D.Float(x, y, width, height)); } public void fillOval(SunGraphics2D sg2d, int x, int y, int width, int height) { fill(sg2d, new Ellipse2D.Float(x, y, width, height)); } public void drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) { draw(sg2d, new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); } public void fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) { fill(sg2d, new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.PIE)); } protected void drawPoly(final SunGraphics2D sg2d, final int[] xPoints, final int[] yPoints, final int nPoints, final boolean isClosed) { if (xPoints == null || yPoints == null) { throw new NullPointerException("coordinate array"); } if (xPoints.length < nPoints || yPoints.length < nPoints) { throw new ArrayIndexOutOfBoundsException("coordinate array"); } if (nPoints < 2) { // render nothing return; } else if (nPoints == 2 && !isClosed) { // render a simple line drawLine(sg2d, xPoints[0], yPoints[0], xPoints[1], yPoints[1]); return; } rq.lock(); try { validateContext(sg2d); int pointBytesRequired = nPoints * BYTES_PER_POLY_POINT; int totalBytesRequired = 20 + pointBytesRequired; if (totalBytesRequired <= buf.capacity()) { if (totalBytesRequired > buf.remaining()) { // process the queue first and then enqueue the points rq.flushNow(); } buf.putInt(DRAW_POLY); // enqueue parameters buf.putInt(nPoints); buf.putInt(isClosed ? 1 : 0); buf.putInt(sg2d.transX); buf.putInt(sg2d.transY); // enqueue the points buf.put(xPoints, 0, nPoints); buf.put(yPoints, 0, nPoints); } else { // queue is too small to accomodate all points; perform the // operation directly on the queue flushing thread rq.flushAndInvokeNow(new Runnable() { public void run() { drawPoly(xPoints, yPoints, nPoints, isClosed, sg2d.transX, sg2d.transY); } }); } } finally { rq.unlock(); } } protected abstract void drawPoly(int[] xPoints, int[] yPoints, int nPoints, boolean isClosed, int transX, int transY); public void drawPolyline(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints) { drawPoly(sg2d, xPoints, yPoints, nPoints, false); } public void drawPolygon(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints) { drawPoly(sg2d, xPoints, yPoints, nPoints, true); } public void fillPolygon(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints) { fill(sg2d, new Polygon(xPoints, yPoints, nPoints)); } private class BufferedDrawHandler extends ProcessPath.DrawHandler { BufferedDrawHandler() { // these are bogus values; the caller will use validate() // to ensure that they are set properly prior to each usage super(0, 0, 0, 0); }
This method needs to be called prior to each draw/fillPath() operation to ensure the clip bounds are up to date.
/** * This method needs to be called prior to each draw/fillPath() * operation to ensure the clip bounds are up to date. */
void validate(SunGraphics2D sg2d) { Region clip = sg2d.getCompClip(); setBounds(clip.getLoX(), clip.getLoY(), clip.getHiX(), clip.getHiY(), sg2d.strokeHint); }
drawPath() support...
/** * drawPath() support... */
public void drawLine(int x1, int y1, int x2, int y2) { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacity(20); buf.putInt(DRAW_LINE); buf.putInt(x1); buf.putInt(y1); buf.putInt(x2); buf.putInt(y2); } public void drawPixel(int x, int y) { // assert rq.lock.isHeldByCurrentThread(); rq.ensureCapacity(12); buf.putInt(DRAW_PIXEL); buf.putInt(x); buf.putInt(y); }
fillPath() support...
/** * fillPath() support... */
private int scanlineCount; private int scanlineCountIndex; private int remainingScanlines; private void resetFillPath() { buf.putInt(DRAW_SCANLINES); scanlineCountIndex = buf.position(); buf.putInt(0); scanlineCount = 0; remainingScanlines = buf.remaining() / BYTES_PER_SCANLINE; } private void updateScanlineCount() { buf.putInt(scanlineCountIndex, scanlineCount); }
Called from fillPath() to indicate that we are about to start issuing drawScanline() calls.
/** * Called from fillPath() to indicate that we are about to * start issuing drawScanline() calls. */
public void startFillPath() { rq.ensureCapacity(20); // to ensure room for at least a scanline resetFillPath(); } public void drawScanline(int x1, int x2, int y) { if (remainingScanlines == 0) { updateScanlineCount(); rq.flushNow(); resetFillPath(); } buf.putInt(x1); buf.putInt(x2); buf.putInt(y); scanlineCount++; remainingScanlines--; }
Called from fillPath() to indicate that we are done issuing drawScanline() calls.
/** * Called from fillPath() to indicate that we are done * issuing drawScanline() calls. */
public void endFillPath() { updateScanlineCount(); } } protected void drawPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy) { rq.lock(); try { validateContext(sg2d); drawHandler.validate(sg2d); ProcessPath.drawPath(drawHandler, p2df, transx, transy); } finally { rq.unlock(); } } protected void fillPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy) { rq.lock(); try { validateContext(sg2d); drawHandler.validate(sg2d); drawHandler.startFillPath(); ProcessPath.fillPath(drawHandler, p2df, transx, transy); drawHandler.endFillPath(); } finally { rq.unlock(); } } private native int fillSpans(RenderQueue rq, long buf, int pos, int limit, SpanIterator si, long iterator, int transx, int transy); protected void fillSpans(SunGraphics2D sg2d, SpanIterator si, int transx, int transy) { rq.lock(); try { validateContext(sg2d); rq.ensureCapacity(24); // so that we have room for at least a span int newpos = fillSpans(rq, buf.getAddress(), buf.position(), buf.capacity(), si, si.getNativeIterator(), transx, transy); buf.position(newpos); } finally { rq.unlock(); } } public void fillParallelogram(SunGraphics2D sg2d, double x, double y, double dx1, double dy1, double dx2, double dy2) { rq.lock(); try { validateContext(sg2d); rq.ensureCapacity(28); buf.putInt(FILL_PARALLELOGRAM); buf.putFloat((float) x); buf.putFloat((float) y); buf.putFloat((float) dx1); buf.putFloat((float) dy1); buf.putFloat((float) dx2); buf.putFloat((float) dy2); } finally { rq.unlock(); } } public void drawParallelogram(SunGraphics2D sg2d, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2) { rq.lock(); try { validateContext(sg2d); rq.ensureCapacity(36); buf.putInt(DRAW_PARALLELOGRAM); buf.putFloat((float) x); buf.putFloat((float) y); buf.putFloat((float) dx1); buf.putFloat((float) dy1); buf.putFloat((float) dx2); buf.putFloat((float) dy2); buf.putFloat((float) lw1); buf.putFloat((float) lw2); } finally { rq.unlock(); } } private class AAParallelogramPipe implements ParallelogramPipe { public void fillParallelogram(SunGraphics2D sg2d, double x, double y, double dx1, double dy1, double dx2, double dy2) { rq.lock(); try { validateContextAA(sg2d); rq.ensureCapacity(28); buf.putInt(FILL_AAPARALLELOGRAM); buf.putFloat((float) x); buf.putFloat((float) y); buf.putFloat((float) dx1); buf.putFloat((float) dy1); buf.putFloat((float) dx2); buf.putFloat((float) dy2); } finally { rq.unlock(); } } public void drawParallelogram(SunGraphics2D sg2d, double x, double y, double dx1, double dy1, double dx2, double dy2, double lw1, double lw2) { rq.lock(); try { validateContextAA(sg2d); rq.ensureCapacity(36); buf.putInt(DRAW_AAPARALLELOGRAM); buf.putFloat((float) x); buf.putFloat((float) y); buf.putFloat((float) dx1); buf.putFloat((float) dy1); buf.putFloat((float) dx2); buf.putFloat((float) dy2); buf.putFloat((float) lw1); buf.putFloat((float) lw2); } finally { rq.unlock(); } } } public void draw(SunGraphics2D sg2d, Shape s) { if (sg2d.strokeState == sg2d.STROKE_THIN) { if (s instanceof Polygon) { if (sg2d.transformState < sg2d.TRANSFORM_TRANSLATESCALE) { Polygon p = (Polygon)s; drawPolygon(sg2d, p.xpoints, p.ypoints, p.npoints); return; } } Path2D.Float p2df; int transx, transy; if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) { if (s instanceof Path2D.Float) { p2df = (Path2D.Float)s; } else { p2df = new Path2D.Float(s); } transx = sg2d.transX; transy = sg2d.transY; } else { p2df = new Path2D.Float(s, sg2d.transform); transx = 0; transy = 0; } drawPath(sg2d, p2df, transx, transy); } else if (sg2d.strokeState < sg2d.STROKE_CUSTOM) { ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s); try { fillSpans(sg2d, si, 0, 0); } finally { si.dispose(); } } else { fill(sg2d, sg2d.stroke.createStrokedShape(s)); } } public void fill(SunGraphics2D sg2d, Shape s) { int transx, transy; if (sg2d.strokeState == sg2d.STROKE_THIN) { // Here we are able to use fillPath() for // high-quality fills. Path2D.Float p2df; if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) { if (s instanceof Path2D.Float) { p2df = (Path2D.Float)s; } else { p2df = new Path2D.Float(s); } transx = sg2d.transX; transy = sg2d.transY; } else { p2df = new Path2D.Float(s, sg2d.transform); transx = 0; transy = 0; } fillPath(sg2d, p2df, transx, transy); return; } AffineTransform at; if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) { // Transform (translation) will be done by FillSpans (we could // delegate to fillPolygon() here, but most hardware accelerated // libraries cannot handle non-convex polygons, so we will use // the FillSpans approach by default) at = null; transx = sg2d.transX; transy = sg2d.transY; } else { // Transform will be done by the PathIterator at = sg2d.transform; transx = transy = 0; } ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d); try { // Subtract transx/y from the SSI clip to match the // (potentially untranslated) geometry fed to it Region clip = sg2d.getCompClip(); ssi.setOutputAreaXYXY(clip.getLoX() - transx, clip.getLoY() - transy, clip.getHiX() - transx, clip.getHiY() - transy); ssi.appendPath(s.getPathIterator(at)); fillSpans(sg2d, ssi, transx, transy); } finally { ssi.dispose(); } } }