Path: blob/master/src/java.desktop/share/classes/sun/java2d/pipe/BufferedRenderPipe.java
41159 views
/*1* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.pipe;2627import java.awt.BasicStroke;28import java.awt.Polygon;29import java.awt.Shape;30import java.awt.geom.AffineTransform;31import java.awt.geom.Arc2D;32import java.awt.geom.Ellipse2D;33import java.awt.geom.Path2D;34import java.awt.geom.IllegalPathStateException;35import java.awt.geom.PathIterator;36import java.awt.geom.Rectangle2D;37import java.awt.geom.RoundRectangle2D;38import sun.java2d.SunGraphics2D;39import sun.java2d.loops.ProcessPath;40import static sun.java2d.pipe.BufferedOpCodes.*;4142/**43* Base class for enqueuing rendering operations in a single-threaded44* rendering environment. Instead of each operation being rendered45* immediately by the underlying graphics library, the operation will be46* added to the provided RenderQueue, which will be processed at a later47* time by a single thread.48*49* This class provides implementations of drawLine(), drawRect(), drawPoly(),50* fillRect(), draw(Shape), and fill(Shape), which are useful for a51* hardware-accelerated renderer. The other draw*() and fill*() methods52* simply delegate to draw(Shape) and fill(Shape), respectively.53*/54public abstract class BufferedRenderPipe55implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, ParallelogramPipe56{57ParallelogramPipe aapgrampipe = new AAParallelogramPipe();5859static final int BYTES_PER_POLY_POINT = 8;60static final int BYTES_PER_SCANLINE = 12;61static final int BYTES_PER_SPAN = 16;6263protected RenderQueue rq;64protected RenderBuffer buf;65private BufferedDrawHandler drawHandler;6667public BufferedRenderPipe(RenderQueue rq) {68this.rq = rq;69this.buf = rq.getBuffer();70this.drawHandler = new BufferedDrawHandler();71}7273public ParallelogramPipe getAAParallelogramPipe() {74return aapgrampipe;75}7677/**78* Validates the state in the provided SunGraphics2D object and sets up79* any special resources for this operation (e.g. enabling gradient80* shading).81*/82protected abstract void validateContext(SunGraphics2D sg2d);83protected abstract void validateContextAA(SunGraphics2D sg2d);8485public void drawLine(SunGraphics2D sg2d,86int x1, int y1, int x2, int y2)87{88int transx = sg2d.transX;89int transy = sg2d.transY;90rq.lock();91try {92validateContext(sg2d);93rq.ensureCapacity(20);94buf.putInt(DRAW_LINE);95buf.putInt(x1 + transx);96buf.putInt(y1 + transy);97buf.putInt(x2 + transx);98buf.putInt(y2 + transy);99} finally {100rq.unlock();101}102}103104public void drawRect(SunGraphics2D sg2d,105int x, int y, int width, int height)106{107rq.lock();108try {109validateContext(sg2d);110rq.ensureCapacity(20);111buf.putInt(DRAW_RECT);112buf.putInt(x + sg2d.transX);113buf.putInt(y + sg2d.transY);114buf.putInt(width);115buf.putInt(height);116} finally {117rq.unlock();118}119}120121public void fillRect(SunGraphics2D sg2d,122int x, int y, int width, int height)123{124rq.lock();125try {126validateContext(sg2d);127rq.ensureCapacity(20);128buf.putInt(FILL_RECT);129buf.putInt(x + sg2d.transX);130buf.putInt(y + sg2d.transY);131buf.putInt(width);132buf.putInt(height);133} finally {134rq.unlock();135}136}137138public void drawRoundRect(SunGraphics2D sg2d,139int x, int y, int width, int height,140int arcWidth, int arcHeight)141{142draw(sg2d, new RoundRectangle2D.Float(x, y, width, height,143arcWidth, arcHeight));144}145146public void fillRoundRect(SunGraphics2D sg2d,147int x, int y, int width, int height,148int arcWidth, int arcHeight)149{150fill(sg2d, new RoundRectangle2D.Float(x, y, width, height,151arcWidth, arcHeight));152}153154public void drawOval(SunGraphics2D sg2d,155int x, int y, int width, int height)156{157draw(sg2d, new Ellipse2D.Float(x, y, width, height));158}159160public void fillOval(SunGraphics2D sg2d,161int x, int y, int width, int height)162{163fill(sg2d, new Ellipse2D.Float(x, y, width, height));164}165166public void drawArc(SunGraphics2D sg2d,167int x, int y, int width, int height,168int startAngle, int arcAngle)169{170draw(sg2d, new Arc2D.Float(x, y, width, height,171startAngle, arcAngle,172Arc2D.OPEN));173}174175public void fillArc(SunGraphics2D sg2d,176int x, int y, int width, int height,177int startAngle, int arcAngle)178{179fill(sg2d, new Arc2D.Float(x, y, width, height,180startAngle, arcAngle,181Arc2D.PIE));182}183184protected void drawPoly(final SunGraphics2D sg2d,185final int[] xPoints, final int[] yPoints,186final int nPoints, final boolean isClosed)187{188if (xPoints == null || yPoints == null) {189throw new NullPointerException("coordinate array");190}191if (xPoints.length < nPoints || yPoints.length < nPoints) {192throw new ArrayIndexOutOfBoundsException("coordinate array");193}194195if (nPoints < 2) {196// render nothing197return;198} else if (nPoints == 2 && !isClosed) {199// render a simple line200drawLine(sg2d, xPoints[0], yPoints[0], xPoints[1], yPoints[1]);201return;202}203204rq.lock();205try {206validateContext(sg2d);207208int pointBytesRequired = nPoints * BYTES_PER_POLY_POINT;209int totalBytesRequired = 20 + pointBytesRequired;210211if (totalBytesRequired <= buf.capacity()) {212if (totalBytesRequired > buf.remaining()) {213// process the queue first and then enqueue the points214rq.flushNow();215}216buf.putInt(DRAW_POLY);217// enqueue parameters218buf.putInt(nPoints);219buf.putInt(isClosed ? 1 : 0);220buf.putInt(sg2d.transX);221buf.putInt(sg2d.transY);222// enqueue the points223buf.put(xPoints, 0, nPoints);224buf.put(yPoints, 0, nPoints);225} else {226// queue is too small to accommodate all points; perform the227// operation directly on the queue flushing thread228rq.flushAndInvokeNow(new Runnable() {229public void run() {230drawPoly(xPoints, yPoints,231nPoints, isClosed,232sg2d.transX, sg2d.transY);233}234});235}236} finally {237rq.unlock();238}239}240241protected abstract void drawPoly(int[] xPoints, int[] yPoints,242int nPoints, boolean isClosed,243int transX, int transY);244245public void drawPolyline(SunGraphics2D sg2d,246int[] xPoints, int[] yPoints,247int nPoints)248{249drawPoly(sg2d, xPoints, yPoints, nPoints, false);250}251252public void drawPolygon(SunGraphics2D sg2d,253int[] xPoints, int[] yPoints,254int nPoints)255{256drawPoly(sg2d, xPoints, yPoints, nPoints, true);257}258259public void fillPolygon(SunGraphics2D sg2d,260int[] xPoints, int[] yPoints,261int nPoints)262{263fill(sg2d, new Polygon(xPoints, yPoints, nPoints));264}265266private class BufferedDrawHandler267extends ProcessPath.DrawHandler268{269BufferedDrawHandler() {270// these are bogus values; the caller will use validate()271// to ensure that they are set properly prior to each usage272super(0, 0, 0, 0);273}274275/**276* This method needs to be called prior to each draw/fillPath()277* operation to ensure the clip bounds are up to date.278*/279void validate(SunGraphics2D sg2d) {280Region clip = sg2d.getCompClip();281setBounds(clip.getLoX(), clip.getLoY(),282clip.getHiX(), clip.getHiY(),283sg2d.strokeHint);284}285286/**287* drawPath() support...288*/289290public void drawLine(int x1, int y1, int x2, int y2) {291// assert rq.lock.isHeldByCurrentThread();292rq.ensureCapacity(20);293buf.putInt(DRAW_LINE);294buf.putInt(x1);295buf.putInt(y1);296buf.putInt(x2);297buf.putInt(y2);298}299300public void drawPixel(int x, int y) {301// assert rq.lock.isHeldByCurrentThread();302rq.ensureCapacity(12);303buf.putInt(DRAW_PIXEL);304buf.putInt(x);305buf.putInt(y);306}307308/**309* fillPath() support...310*/311312private int scanlineCount;313private int scanlineCountIndex;314private int remainingScanlines;315316private void resetFillPath() {317buf.putInt(DRAW_SCANLINES);318scanlineCountIndex = buf.position();319buf.putInt(0);320scanlineCount = 0;321remainingScanlines = buf.remaining() / BYTES_PER_SCANLINE;322}323324private void updateScanlineCount() {325buf.putInt(scanlineCountIndex, scanlineCount);326}327328/**329* Called from fillPath() to indicate that we are about to330* start issuing drawScanline() calls.331*/332public void startFillPath() {333rq.ensureCapacity(20); // to ensure room for at least a scanline334resetFillPath();335}336337public void drawScanline(int x1, int x2, int y) {338if (remainingScanlines == 0) {339updateScanlineCount();340rq.flushNow();341resetFillPath();342}343buf.putInt(x1);344buf.putInt(x2);345buf.putInt(y);346scanlineCount++;347remainingScanlines--;348}349350/**351* Called from fillPath() to indicate that we are done352* issuing drawScanline() calls.353*/354public void endFillPath() {355updateScanlineCount();356}357}358359protected void drawPath(SunGraphics2D sg2d,360Path2D.Float p2df, int transx, int transy)361{362rq.lock();363try {364validateContext(sg2d);365drawHandler.validate(sg2d);366ProcessPath.drawPath(drawHandler, p2df, transx, transy);367} finally {368rq.unlock();369}370}371372protected void fillPath(SunGraphics2D sg2d,373Path2D.Float p2df, int transx, int transy)374{375rq.lock();376try {377validateContext(sg2d);378drawHandler.validate(sg2d);379drawHandler.startFillPath();380ProcessPath.fillPath(drawHandler, p2df, transx, transy);381drawHandler.endFillPath();382} finally {383rq.unlock();384}385}386387private native int fillSpans(RenderQueue rq, long buf,388int pos, int limit,389SpanIterator si, long iterator,390int transx, int transy);391392protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,393int transx, int transy)394{395rq.lock();396try {397validateContext(sg2d);398rq.ensureCapacity(24); // so that we have room for at least a span399int newpos = fillSpans(rq, buf.getAddress(),400buf.position(), buf.capacity(),401si, si.getNativeIterator(),402transx, transy);403buf.position(newpos);404} finally {405rq.unlock();406}407}408409public void fillParallelogram(SunGraphics2D sg2d,410double ux1, double uy1,411double ux2, double uy2,412double x, double y,413double dx1, double dy1,414double dx2, double dy2)415{416rq.lock();417try {418validateContext(sg2d);419rq.ensureCapacity(28);420buf.putInt(FILL_PARALLELOGRAM);421buf.putFloat((float) x);422buf.putFloat((float) y);423buf.putFloat((float) dx1);424buf.putFloat((float) dy1);425buf.putFloat((float) dx2);426buf.putFloat((float) dy2);427} finally {428rq.unlock();429}430}431432public void drawParallelogram(SunGraphics2D sg2d,433double ux1, double uy1,434double ux2, double uy2,435double x, double y,436double dx1, double dy1,437double dx2, double dy2,438double lw1, double lw2)439{440rq.lock();441try {442validateContext(sg2d);443rq.ensureCapacity(36);444buf.putInt(DRAW_PARALLELOGRAM);445buf.putFloat((float) x);446buf.putFloat((float) y);447buf.putFloat((float) dx1);448buf.putFloat((float) dy1);449buf.putFloat((float) dx2);450buf.putFloat((float) dy2);451buf.putFloat((float) lw1);452buf.putFloat((float) lw2);453} finally {454rq.unlock();455}456}457458private class AAParallelogramPipe implements ParallelogramPipe {459public void fillParallelogram(SunGraphics2D sg2d,460double ux1, double uy1,461double ux2, double uy2,462double x, double y,463double dx1, double dy1,464double dx2, double dy2)465{466rq.lock();467try {468validateContextAA(sg2d);469rq.ensureCapacity(28);470buf.putInt(FILL_AAPARALLELOGRAM);471buf.putFloat((float) x);472buf.putFloat((float) y);473buf.putFloat((float) dx1);474buf.putFloat((float) dy1);475buf.putFloat((float) dx2);476buf.putFloat((float) dy2);477} finally {478rq.unlock();479}480}481482public void drawParallelogram(SunGraphics2D sg2d,483double ux1, double uy1,484double ux2, double uy2,485double x, double y,486double dx1, double dy1,487double dx2, double dy2,488double lw1, double lw2)489{490rq.lock();491try {492validateContextAA(sg2d);493rq.ensureCapacity(36);494buf.putInt(DRAW_AAPARALLELOGRAM);495buf.putFloat((float) x);496buf.putFloat((float) y);497buf.putFloat((float) dx1);498buf.putFloat((float) dy1);499buf.putFloat((float) dx2);500buf.putFloat((float) dy2);501buf.putFloat((float) lw1);502buf.putFloat((float) lw2);503} finally {504rq.unlock();505}506}507}508509public void draw(SunGraphics2D sg2d, Shape s) {510if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {511if (s instanceof Polygon) {512if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE) {513Polygon p = (Polygon)s;514drawPolygon(sg2d, p.xpoints, p.ypoints, p.npoints);515return;516}517}518Path2D.Float p2df;519int transx, transy;520if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {521if (s instanceof Path2D.Float) {522p2df = (Path2D.Float)s;523} else {524p2df = new Path2D.Float(s);525}526transx = sg2d.transX;527transy = sg2d.transY;528} else {529p2df = new Path2D.Float(s, sg2d.transform);530transx = 0;531transy = 0;532}533drawPath(sg2d, p2df, transx, transy);534} else if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) {535ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s);536try {537fillSpans(sg2d, si, 0, 0);538} finally {539si.dispose();540}541} else {542fill(sg2d, sg2d.stroke.createStrokedShape(s));543}544}545546public void fill(SunGraphics2D sg2d, Shape s) {547int transx, transy;548549if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {550// Here we are able to use fillPath() for551// high-quality fills.552Path2D.Float p2df;553if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {554if (s instanceof Path2D.Float) {555p2df = (Path2D.Float)s;556} else {557p2df = new Path2D.Float(s);558}559transx = sg2d.transX;560transy = sg2d.transY;561} else {562p2df = new Path2D.Float(s, sg2d.transform);563transx = 0;564transy = 0;565}566fillPath(sg2d, p2df, transx, transy);567return;568}569570AffineTransform at;571if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {572// Transform (translation) will be done by FillSpans (we could573// delegate to fillPolygon() here, but most hardware accelerated574// libraries cannot handle non-convex polygons, so we will use575// the FillSpans approach by default)576at = null;577transx = sg2d.transX;578transy = sg2d.transY;579} else {580// Transform will be done by the PathIterator581at = sg2d.transform;582transx = transy = 0;583}584585ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);586try {587// Subtract transx/y from the SSI clip to match the588// (potentially untranslated) geometry fed to it589Region clip = sg2d.getCompClip();590ssi.setOutputAreaXYXY(clip.getLoX() - transx,591clip.getLoY() - transy,592clip.getHiX() - transx,593clip.getHiY() - transy);594ssi.appendPath(s.getPathIterator(at));595fillSpans(sg2d, ssi, transx, transy);596} finally {597ssi.dispose();598}599}600}601602603