Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/dc/DuctusRenderingEngine.java
38829 views
/*1* Copyright (c) 2007, 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.dc;2627import java.awt.Shape;28import java.awt.BasicStroke;29import java.awt.geom.Path2D;30import java.awt.geom.PathIterator;31import java.awt.geom.AffineTransform;3233import sun.awt.geom.PathConsumer2D;34import sun.java2d.pipe.Region;35import sun.java2d.pipe.AATileGenerator;36import sun.java2d.pipe.RenderingEngine;3738import sun.dc.pr.Rasterizer;39import sun.dc.pr.PathStroker;40import sun.dc.pr.PathDasher;41import sun.dc.pr.PRException;42import sun.dc.path.PathConsumer;43import sun.dc.path.PathException;44import sun.dc.path.FastPathProducer;4546public class DuctusRenderingEngine extends RenderingEngine {47static final float PenUnits = 0.01f;48static final int MinPenUnits = 100;49static final int MinPenUnitsAA = 20;50static final float MinPenSizeAA = PenUnits * MinPenUnitsAA;5152static final float UPPER_BND = Float.MAX_VALUE / 2.0f;53static final float LOWER_BND = -UPPER_BND;5455private static final int RasterizerCaps[] = {56Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE57};5859private static final int RasterizerCorners[] = {60Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL61};6263static float[] getTransformMatrix(AffineTransform transform) {64float matrix[] = new float[4];65double dmatrix[] = new double[6];66transform.getMatrix(dmatrix);67for (int i = 0; i < 4; i++) {68matrix[i] = (float) dmatrix[i];69}70return matrix;71}7273/**74* {@inheritDoc}75*/76@Override77public Shape createStrokedShape(Shape src,78float width,79int caps,80int join,81float miterlimit,82float dashes[],83float dashphase)84{85FillAdapter filler = new FillAdapter();86PathStroker stroker = new PathStroker(filler);87PathDasher dasher = null;8889try {90PathConsumer consumer;9192stroker.setPenDiameter(width);93stroker.setPenT4(null);94stroker.setCaps(RasterizerCaps[caps]);95stroker.setCorners(RasterizerCorners[join], miterlimit);96if (dashes != null) {97dasher = new PathDasher(stroker);98dasher.setDash(dashes, dashphase);99dasher.setDashT4(null);100consumer = dasher;101} else {102consumer = stroker;103}104105feedConsumer(consumer, src.getPathIterator(null));106} finally {107stroker.dispose();108if (dasher != null) {109dasher.dispose();110}111}112113return filler.getShape();114}115116/**117* {@inheritDoc}118*/119@Override120public void strokeTo(Shape src,121AffineTransform transform,122BasicStroke bs,123boolean thin,124boolean normalize,125boolean antialias,126PathConsumer2D sr)127{128PathStroker stroker = new PathStroker(sr);129PathConsumer consumer = stroker;130131float matrix[] = null;132if (!thin) {133stroker.setPenDiameter(bs.getLineWidth());134if (transform != null) {135matrix = getTransformMatrix(transform);136}137stroker.setPenT4(matrix);138stroker.setPenFitting(PenUnits, MinPenUnits);139}140stroker.setCaps(RasterizerCaps[bs.getEndCap()]);141stroker.setCorners(RasterizerCorners[bs.getLineJoin()],142bs.getMiterLimit());143float[] dashes = bs.getDashArray();144if (dashes != null) {145PathDasher dasher = new PathDasher(stroker);146dasher.setDash(dashes, bs.getDashPhase());147if (transform != null && matrix == null) {148matrix = getTransformMatrix(transform);149}150dasher.setDashT4(matrix);151consumer = dasher;152}153154try {155PathIterator pi = src.getPathIterator(transform);156157feedConsumer(pi, consumer, normalize, 0.25f);158} catch (PathException e) {159throw new InternalError("Unable to Stroke shape ("+160e.getMessage()+")", e);161} finally {162while (consumer != null && consumer != sr) {163PathConsumer next = consumer.getConsumer();164consumer.dispose();165consumer = next;166}167}168}169170/*171* Feed a path from a PathIterator to a Ductus PathConsumer.172*/173public static void feedConsumer(PathIterator pi, PathConsumer consumer,174boolean normalize, float norm)175throws PathException176{177consumer.beginPath();178boolean pathClosed = false;179boolean skip = false;180boolean subpathStarted = false;181float mx = 0.0f;182float my = 0.0f;183float point[] = new float[6];184float rnd = (0.5f - norm);185float ax = 0.0f;186float ay = 0.0f;187188while (!pi.isDone()) {189int type = pi.currentSegment(point);190if (pathClosed == true) {191pathClosed = false;192if (type != PathIterator.SEG_MOVETO) {193// Force current point back to last moveto point194consumer.beginSubpath(mx, my);195subpathStarted = true;196}197}198if (normalize) {199int index;200switch (type) {201case PathIterator.SEG_CUBICTO:202index = 4;203break;204case PathIterator.SEG_QUADTO:205index = 2;206break;207case PathIterator.SEG_MOVETO:208case PathIterator.SEG_LINETO:209index = 0;210break;211case PathIterator.SEG_CLOSE:212default:213index = -1;214break;215}216if (index >= 0) {217float ox = point[index];218float oy = point[index+1];219float newax = (float) Math.floor(ox + rnd) + norm;220float neway = (float) Math.floor(oy + rnd) + norm;221point[index] = newax;222point[index+1] = neway;223newax -= ox;224neway -= oy;225switch (type) {226case PathIterator.SEG_CUBICTO:227point[0] += ax;228point[1] += ay;229point[2] += newax;230point[3] += neway;231break;232case PathIterator.SEG_QUADTO:233point[0] += (newax + ax) / 2;234point[1] += (neway + ay) / 2;235break;236case PathIterator.SEG_MOVETO:237case PathIterator.SEG_LINETO:238case PathIterator.SEG_CLOSE:239break;240}241ax = newax;242ay = neway;243}244}245switch (type) {246case PathIterator.SEG_MOVETO:247248/* Checking SEG_MOVETO coordinates if they are out of the249* [LOWER_BND, UPPER_BND] range. This check also handles NaN250* and Infinity values. Skipping next path segment in case of251* invalid data.252*/253if (point[0] < UPPER_BND && point[0] > LOWER_BND &&254point[1] < UPPER_BND && point[1] > LOWER_BND)255{256mx = point[0];257my = point[1];258consumer.beginSubpath(mx, my);259subpathStarted = true;260skip = false;261} else {262skip = true;263}264break;265case PathIterator.SEG_LINETO:266/* Checking SEG_LINETO coordinates if they are out of the267* [LOWER_BND, UPPER_BND] range. This check also handles NaN268* and Infinity values. Ignoring current path segment in case269* of invalid data. If segment is skipped its endpoint270* (if valid) is used to begin new subpath.271*/272if (point[0] < UPPER_BND && point[0] > LOWER_BND &&273point[1] < UPPER_BND && point[1] > LOWER_BND)274{275if (skip) {276consumer.beginSubpath(point[0], point[1]);277subpathStarted = true;278skip = false;279} else {280consumer.appendLine(point[0], point[1]);281}282}283break;284case PathIterator.SEG_QUADTO:285// Quadratic curves take two points286287/* Checking SEG_QUADTO coordinates if they are out of the288* [LOWER_BND, UPPER_BND] range. This check also handles NaN289* and Infinity values. Ignoring current path segment in case290* of invalid endpoints's data. Equivalent to the SEG_LINETO291* if endpoint coordinates are valid but there are invalid data292* among other coordinates293*/294if (point[2] < UPPER_BND && point[2] > LOWER_BND &&295point[3] < UPPER_BND && point[3] > LOWER_BND)296{297if (skip) {298consumer.beginSubpath(point[2], point[3]);299subpathStarted = true;300skip = false;301} else {302if (point[0] < UPPER_BND && point[0] > LOWER_BND &&303point[1] < UPPER_BND && point[1] > LOWER_BND)304{305consumer.appendQuadratic(point[0], point[1],306point[2], point[3]);307} else {308consumer.appendLine(point[2], point[3]);309}310}311}312break;313case PathIterator.SEG_CUBICTO:314// Cubic curves take three points315316/* Checking SEG_CUBICTO coordinates if they are out of the317* [LOWER_BND, UPPER_BND] range. This check also handles NaN318* and Infinity values. Ignoring current path segment in case319* of invalid endpoints's data. Equivalent to the SEG_LINETO320* if endpoint coordinates are valid but there are invalid data321* among other coordinates322*/323if (point[4] < UPPER_BND && point[4] > LOWER_BND &&324point[5] < UPPER_BND && point[5] > LOWER_BND)325{326if (skip) {327consumer.beginSubpath(point[4], point[5]);328subpathStarted = true;329skip = false;330} else {331if (point[0] < UPPER_BND && point[0] > LOWER_BND &&332point[1] < UPPER_BND && point[1] > LOWER_BND &&333point[2] < UPPER_BND && point[2] > LOWER_BND &&334point[3] < UPPER_BND && point[3] > LOWER_BND)335{336consumer.appendCubic(point[0], point[1],337point[2], point[3],338point[4], point[5]);339} else {340consumer.appendLine(point[4], point[5]);341}342}343}344break;345case PathIterator.SEG_CLOSE:346if (subpathStarted) {347consumer.closedSubpath();348subpathStarted = false;349pathClosed = true;350}351break;352}353pi.next();354}355356consumer.endPath();357}358359private static Rasterizer theRasterizer;360361public synchronized static Rasterizer getRasterizer() {362Rasterizer r = theRasterizer;363if (r == null) {364r = new Rasterizer();365} else {366theRasterizer = null;367}368return r;369}370371public synchronized static void dropRasterizer(Rasterizer r) {372r.reset();373theRasterizer = r;374}375376/**377* {@inheritDoc}378*/379@Override380public float getMinimumAAPenSize() {381return MinPenSizeAA;382}383384/**385* {@inheritDoc}386*/387@Override388public AATileGenerator getAATileGenerator(Shape s,389AffineTransform at,390Region clip,391BasicStroke bs,392boolean thin,393boolean normalize,394int bbox[])395{396Rasterizer r = getRasterizer();397PathIterator pi = s.getPathIterator(at);398399if (bs != null) {400float matrix[] = null;401r.setUsage(Rasterizer.STROKE);402if (thin) {403r.setPenDiameter(MinPenSizeAA);404} else {405r.setPenDiameter(bs.getLineWidth());406if (at != null) {407matrix = getTransformMatrix(at);408r.setPenT4(matrix);409}410r.setPenFitting(PenUnits, MinPenUnitsAA);411}412r.setCaps(RasterizerCaps[bs.getEndCap()]);413r.setCorners(RasterizerCorners[bs.getLineJoin()],414bs.getMiterLimit());415float[] dashes = bs.getDashArray();416if (dashes != null) {417r.setDash(dashes, bs.getDashPhase());418if (at != null && matrix == null) {419matrix = getTransformMatrix(at);420}421r.setDashT4(matrix);422}423} else {424r.setUsage(pi.getWindingRule() == PathIterator.WIND_EVEN_ODD425? Rasterizer.EOFILL426: Rasterizer.NZFILL);427}428429r.beginPath();430{431boolean pathClosed = false;432boolean skip = false;433boolean subpathStarted = false;434float mx = 0.0f;435float my = 0.0f;436float point[] = new float[6];437float ax = 0.0f;438float ay = 0.0f;439440while (!pi.isDone()) {441int type = pi.currentSegment(point);442if (pathClosed == true) {443pathClosed = false;444if (type != PathIterator.SEG_MOVETO) {445// Force current point back to last moveto point446r.beginSubpath(mx, my);447subpathStarted = true;448}449}450if (normalize) {451int index;452switch (type) {453case PathIterator.SEG_CUBICTO:454index = 4;455break;456case PathIterator.SEG_QUADTO:457index = 2;458break;459case PathIterator.SEG_MOVETO:460case PathIterator.SEG_LINETO:461index = 0;462break;463case PathIterator.SEG_CLOSE:464default:465index = -1;466break;467}468if (index >= 0) {469float ox = point[index];470float oy = point[index+1];471float newax = (float) Math.floor(ox) + 0.5f;472float neway = (float) Math.floor(oy) + 0.5f;473point[index] = newax;474point[index+1] = neway;475newax -= ox;476neway -= oy;477switch (type) {478case PathIterator.SEG_CUBICTO:479point[0] += ax;480point[1] += ay;481point[2] += newax;482point[3] += neway;483break;484case PathIterator.SEG_QUADTO:485point[0] += (newax + ax) / 2;486point[1] += (neway + ay) / 2;487break;488case PathIterator.SEG_MOVETO:489case PathIterator.SEG_LINETO:490case PathIterator.SEG_CLOSE:491break;492}493ax = newax;494ay = neway;495}496}497switch (type) {498case PathIterator.SEG_MOVETO:499500/* Checking SEG_MOVETO coordinates if they are out of the501* [LOWER_BND, UPPER_BND] range. This check also handles NaN502* and Infinity values. Skipping next path segment in case503* of invalid data.504*/505506if (point[0] < UPPER_BND && point[0] > LOWER_BND &&507point[1] < UPPER_BND && point[1] > LOWER_BND)508{509mx = point[0];510my = point[1];511r.beginSubpath(mx, my);512subpathStarted = true;513skip = false;514} else {515skip = true;516}517break;518519case PathIterator.SEG_LINETO:520/* Checking SEG_LINETO coordinates if they are out of the521* [LOWER_BND, UPPER_BND] range. This check also handles522* NaN and Infinity values. Ignoring current path segment523* in case of invalid data. If segment is skipped its524* endpoint (if valid) is used to begin new subpath.525*/526if (point[0] < UPPER_BND && point[0] > LOWER_BND &&527point[1] < UPPER_BND && point[1] > LOWER_BND)528{529if (skip) {530r.beginSubpath(point[0], point[1]);531subpathStarted = true;532skip = false;533} else {534r.appendLine(point[0], point[1]);535}536}537break;538539case PathIterator.SEG_QUADTO:540// Quadratic curves take two points541542/* Checking SEG_QUADTO coordinates if they are out of the543* [LOWER_BND, UPPER_BND] range. This check also handles544* NaN and Infinity values. Ignoring current path segment545* in case of invalid endpoints's data. Equivalent to the546* SEG_LINETO if endpoint coordinates are valid but there547* are invalid data among other coordinates548*/549if (point[2] < UPPER_BND && point[2] > LOWER_BND &&550point[3] < UPPER_BND && point[3] > LOWER_BND)551{552if (skip) {553r.beginSubpath(point[2], point[3]);554subpathStarted = true;555skip = false;556} else {557if (point[0] < UPPER_BND && point[0] > LOWER_BND &&558point[1] < UPPER_BND && point[1] > LOWER_BND)559{560r.appendQuadratic(point[0], point[1],561point[2], point[3]);562} else {563r.appendLine(point[2], point[3]);564}565}566}567break;568case PathIterator.SEG_CUBICTO:569// Cubic curves take three points570571/* Checking SEG_CUBICTO coordinates if they are out of the572* [LOWER_BND, UPPER_BND] range. This check also handles573* NaN and Infinity values. Ignoring current path segment574* in case of invalid endpoints's data. Equivalent to the575* SEG_LINETO if endpoint coordinates are valid but there576* are invalid data among other coordinates577*/578579if (point[4] < UPPER_BND && point[4] > LOWER_BND &&580point[5] < UPPER_BND && point[5] > LOWER_BND)581{582if (skip) {583r.beginSubpath(point[4], point[5]);584subpathStarted = true;585skip = false;586} else {587if (point[0] < UPPER_BND && point[0] > LOWER_BND &&588point[1] < UPPER_BND && point[1] > LOWER_BND &&589point[2] < UPPER_BND && point[2] > LOWER_BND &&590point[3] < UPPER_BND && point[3] > LOWER_BND)591{592r.appendCubic(point[0], point[1],593point[2], point[3],594point[4], point[5]);595} else {596r.appendLine(point[4], point[5]);597}598}599}600break;601case PathIterator.SEG_CLOSE:602if (subpathStarted) {603r.closedSubpath();604subpathStarted = false;605pathClosed = true;606}607break;608}609pi.next();610}611}612613try {614r.endPath();615r.getAlphaBox(bbox);616clip.clipBoxToBounds(bbox);617if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) {618dropRasterizer(r);619return null;620}621r.setOutputArea(bbox[0], bbox[1],622bbox[2] - bbox[0],623bbox[3] - bbox[1]);624} catch (PRException e) {625/*626* This exeption is thrown from the native part of the Ductus627* (only in case of a debug build) to indicate that some628* segments of the path have very large coordinates.629* See 4485298 for more info.630*/631System.err.println("DuctusRenderingEngine.getAATileGenerator: "+e);632}633634return r;635}636637/**638* {@inheritDoc}639*/640@Override641public AATileGenerator getAATileGenerator(double x, double y,642double dx1, double dy1,643double dx2, double dy2,644double lw1, double lw2,645Region clip,646int bbox[])647{648// REMIND: Deal with large coordinates!649double ldx1, ldy1, ldx2, ldy2;650boolean innerpgram = (lw1 > 0 && lw2 > 0);651652if (innerpgram) {653ldx1 = dx1 * lw1;654ldy1 = dy1 * lw1;655ldx2 = dx2 * lw2;656ldy2 = dy2 * lw2;657x -= (ldx1 + ldx2) / 2.0;658y -= (ldy1 + ldy2) / 2.0;659dx1 += ldx1;660dy1 += ldy1;661dx2 += ldx2;662dy2 += ldy2;663if (lw1 > 1 && lw2 > 1) {664// Inner parallelogram was entirely consumed by stroke...665innerpgram = false;666}667} else {668ldx1 = ldy1 = ldx2 = ldy2 = 0;669}670671Rasterizer r = getRasterizer();672673r.setUsage(Rasterizer.EOFILL);674675r.beginPath();676r.beginSubpath((float) x, (float) y);677r.appendLine((float) (x+dx1), (float) (y+dy1));678r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2));679r.appendLine((float) (x+dx2), (float) (y+dy2));680r.closedSubpath();681if (innerpgram) {682x += ldx1 + ldx2;683y += ldy1 + ldy2;684dx1 -= 2.0 * ldx1;685dy1 -= 2.0 * ldy1;686dx2 -= 2.0 * ldx2;687dy2 -= 2.0 * ldy2;688r.beginSubpath((float) x, (float) y);689r.appendLine((float) (x+dx1), (float) (y+dy1));690r.appendLine((float) (x+dx1+dx2), (float) (y+dy1+dy2));691r.appendLine((float) (x+dx2), (float) (y+dy2));692r.closedSubpath();693}694695try {696r.endPath();697r.getAlphaBox(bbox);698clip.clipBoxToBounds(bbox);699if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) {700dropRasterizer(r);701return null;702}703r.setOutputArea(bbox[0], bbox[1],704bbox[2] - bbox[0],705bbox[3] - bbox[1]);706} catch (PRException e) {707/*708* This exeption is thrown from the native part of the Ductus709* (only in case of a debug build) to indicate that some710* segments of the path have very large coordinates.711* See 4485298 for more info.712*/713System.err.println("DuctusRenderingEngine.getAATileGenerator: "+e);714}715716return r;717}718719private void feedConsumer(PathConsumer consumer, PathIterator pi) {720try {721consumer.beginPath();722boolean pathClosed = false;723float mx = 0.0f;724float my = 0.0f;725float point[] = new float[6];726727while (!pi.isDone()) {728int type = pi.currentSegment(point);729if (pathClosed == true) {730pathClosed = false;731if (type != PathIterator.SEG_MOVETO) {732// Force current point back to last moveto point733consumer.beginSubpath(mx, my);734}735}736switch (type) {737case PathIterator.SEG_MOVETO:738mx = point[0];739my = point[1];740consumer.beginSubpath(point[0], point[1]);741break;742case PathIterator.SEG_LINETO:743consumer.appendLine(point[0], point[1]);744break;745case PathIterator.SEG_QUADTO:746consumer.appendQuadratic(point[0], point[1],747point[2], point[3]);748break;749case PathIterator.SEG_CUBICTO:750consumer.appendCubic(point[0], point[1],751point[2], point[3],752point[4], point[5]);753break;754case PathIterator.SEG_CLOSE:755consumer.closedSubpath();756pathClosed = true;757break;758}759pi.next();760}761762consumer.endPath();763} catch (PathException e) {764throw new InternalError("Unable to Stroke shape ("+765e.getMessage()+")", e);766}767}768769private class FillAdapter implements PathConsumer {770boolean closed;771Path2D.Float path;772773public FillAdapter() {774// Ductus only supplies float coordinates so775// Path2D.Double is not necessary here.776path = new Path2D.Float(Path2D.WIND_NON_ZERO);777}778779public Shape getShape() {780return path;781}782783public void dispose() {784}785786public PathConsumer getConsumer() {787return null;788}789790public void beginPath() {}791792public void beginSubpath(float x0, float y0) {793if (closed) {794path.closePath();795closed = false;796}797path.moveTo(x0, y0);798}799800public void appendLine(float x1, float y1) {801path.lineTo(x1, y1);802}803804public void appendQuadratic(float xm, float ym, float x1, float y1) {805path.quadTo(xm, ym, x1, y1);806}807808public void appendCubic(float xm, float ym,809float xn, float yn,810float x1, float y1) {811path.curveTo(xm, ym, xn, yn, x1, y1);812}813814public void closedSubpath() {815closed = true;816}817818public void endPath() {819if (closed) {820path.closePath();821closed = false;822}823}824825public void useProxy(FastPathProducer proxy)826throws PathException827{828proxy.sendTo(this);829}830831public long getCPathConsumer() {832return 0;833}834}835}836837838