Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/pisces/Renderer.java
38918 views
/*1* Copyright (c) 2007, 2011, 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.pisces;2627import sun.awt.geom.PathConsumer2D;2829final class Renderer implements PathConsumer2D {3031private class ScanlineIterator {3233private int[] crossings;3435// crossing bounds. The bounds are not necessarily tight (the scan line36// at minY, for example, might have no crossings). The x bounds will37// be accumulated as crossings are computed.38private final int maxY;39private int nextY;4041// indices into the segment pointer lists. They indicate the "active"42// sublist in the segment lists (the portion of the list that contains43// all the segments that cross the next scan line).44private int edgeCount;45private int[] edgePtrs;4647private static final int INIT_CROSSINGS_SIZE = 10;4849// Preconditions: Only subpixel scanlines in the range50// (start <= subpixel_y <= end) will be evaluated. No51// edge may have a valid (i.e. inside the supplied clip)52// crossing that would be generated outside that range.53private ScanlineIterator(int start, int end) {54crossings = new int[INIT_CROSSINGS_SIZE];55edgePtrs = new int[INIT_CROSSINGS_SIZE];5657nextY = start;58maxY = end;59edgeCount = 0;60}6162private int next() {63int cury = nextY++;64int bucket = cury - boundsMinY;65int count = this.edgeCount;66int ptrs[] = this.edgePtrs;67int bucketcount = edgeBucketCounts[bucket];68if ((bucketcount & 0x1) != 0) {69int newCount = 0;70for (int i = 0; i < count; i++) {71int ecur = ptrs[i];72if (edges[ecur+YMAX] > cury) {73ptrs[newCount++] = ecur;74}75}76count = newCount;77}78ptrs = Helpers.widenArray(ptrs, count, bucketcount >> 1);79for (int ecur = edgeBuckets[bucket]; ecur != NULL; ecur = (int)edges[ecur+NEXT]) {80ptrs[count++] = ecur;81// REMIND: Adjust start Y if necessary82}83this.edgePtrs = ptrs;84this.edgeCount = count;85// if ((count & 0x1) != 0) {86// System.out.println("ODD NUMBER OF EDGES!!!!");87// }88int xings[] = this.crossings;89if (xings.length < count) {90this.crossings = xings = new int[ptrs.length];91}92for (int i = 0; i < count; i++) {93int ecur = ptrs[i];94float curx = edges[ecur+CURX];95int cross = ((int) curx) << 1;96edges[ecur+CURX] = curx + edges[ecur+SLOPE];97if (edges[ecur+OR] > 0) {98cross |= 1;99}100int j = i;101while (--j >= 0) {102int jcross = xings[j];103if (jcross <= cross) {104break;105}106xings[j+1] = jcross;107ptrs[j+1] = ptrs[j];108}109xings[j+1] = cross;110ptrs[j+1] = ecur;111}112return count;113}114115private boolean hasNext() {116return nextY < maxY;117}118119private int curY() {120return nextY - 1;121}122}123124125//////////////////////////////////////////////////////////////////////////////126// EDGE LIST127//////////////////////////////////////////////////////////////////////////////128// TODO(maybe): very tempting to use fixed point here. A lot of opportunities129// for shifts and just removing certain operations altogether.130131// common to all types of input path segments.132private static final int YMAX = 0;133private static final int CURX = 1;134// NEXT and OR are meant to be indices into "int" fields, but arrays must135// be homogenous, so every field is a float. However floats can represent136// exactly up to 26 bit ints, so we're ok.137private static final int OR = 2;138private static final int SLOPE = 3;139private static final int NEXT = 4;140141private float edgeMinY = Float.POSITIVE_INFINITY;142private float edgeMaxY = Float.NEGATIVE_INFINITY;143private float edgeMinX = Float.POSITIVE_INFINITY;144private float edgeMaxX = Float.NEGATIVE_INFINITY;145146private static final int SIZEOF_EDGE = 5;147// don't just set NULL to -1, because we want NULL+NEXT to be negative.148private static final int NULL = -SIZEOF_EDGE;149private float[] edges = null;150private static final int INIT_NUM_EDGES = 8;151private int[] edgeBuckets = null;152private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed)153private int numEdges;154155private static final float DEC_BND = 20f;156private static final float INC_BND = 8f;157158// each bucket is a linked list. this method adds eptr to the159// start of the "bucket"th linked list.160private void addEdgeToBucket(final int eptr, final int bucket) {161edges[eptr+NEXT] = edgeBuckets[bucket];162edgeBuckets[bucket] = eptr;163edgeBucketCounts[bucket] += 2;164}165166// Flattens using adaptive forward differencing. This only carries out167// one iteration of the AFD loop. All it does is update AFD variables (i.e.168// X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).169private void quadBreakIntoLinesAndAdd(float x0, float y0,170final Curve c,171final float x2, final float y2)172{173final float QUAD_DEC_BND = 32;174final int countlg = 4;175int count = 1 << countlg;176int countsq = count * count;177float maxDD = Math.max(c.dbx / countsq, c.dby / countsq);178while (maxDD > QUAD_DEC_BND) {179maxDD /= 4;180count <<= 1;181}182183countsq = count * count;184final float ddx = c.dbx / countsq;185final float ddy = c.dby / countsq;186float dx = c.bx / countsq + c.cx / count;187float dy = c.by / countsq + c.cy / count;188189while (count-- > 1) {190float x1 = x0 + dx;191dx += ddx;192float y1 = y0 + dy;193dy += ddy;194addLine(x0, y0, x1, y1);195x0 = x1;196y0 = y1;197}198addLine(x0, y0, x2, y2);199}200201// x0, y0 and x3,y3 are the endpoints of the curve. We could compute these202// using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce203// numerical errors, and our callers already have the exact values.204// Another alternative would be to pass all the control points, and call c.set205// here, but then too many numbers are passed around.206private void curveBreakIntoLinesAndAdd(float x0, float y0,207final Curve c,208final float x3, final float y3)209{210final int countlg = 3;211int count = 1 << countlg;212213// the dx and dy refer to forward differencing variables, not the last214// coefficients of the "points" polynomial215float dddx, dddy, ddx, ddy, dx, dy;216dddx = 2f * c.dax / (1 << (3 * countlg));217dddy = 2f * c.day / (1 << (3 * countlg));218219ddx = dddx + c.dbx / (1 << (2 * countlg));220ddy = dddy + c.dby / (1 << (2 * countlg));221dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);222dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);223224// we use x0, y0 to walk the line225float x1 = x0, y1 = y0;226while (count > 0) {227while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {228dddx /= 8;229dddy /= 8;230ddx = ddx/4 - dddx;231ddy = ddy/4 - dddy;232dx = (dx - ddx) / 2;233dy = (dy - ddy) / 2;234count <<= 1;235}236// can only do this on even "count" values, because we must divide count by 2237while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {238dx = 2 * dx + ddx;239dy = 2 * dy + ddy;240ddx = 4 * (ddx + dddx);241ddy = 4 * (ddy + dddy);242dddx = 8 * dddx;243dddy = 8 * dddy;244count >>= 1;245}246count--;247if (count > 0) {248x1 += dx;249dx += ddx;250ddx += dddx;251y1 += dy;252dy += ddy;253ddy += dddy;254} else {255x1 = x3;256y1 = y3;257}258addLine(x0, y0, x1, y1);259x0 = x1;260y0 = y1;261}262}263264private void addLine(float x1, float y1, float x2, float y2) {265float or = 1; // orientation of the line. 1 if y increases, 0 otherwise.266if (y2 < y1) {267or = y2; // no need to declare a temp variable. We have or.268y2 = y1;269y1 = or;270or = x2;271x2 = x1;272x1 = or;273or = 0;274}275final int firstCrossing = Math.max((int)Math.ceil(y1), boundsMinY);276final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY);277if (firstCrossing >= lastCrossing) {278return;279}280if (y1 < edgeMinY) { edgeMinY = y1; }281if (y2 > edgeMaxY) { edgeMaxY = y2; }282283final float slope = (x2 - x1) / (y2 - y1);284285if (slope > 0) { // <==> x1 < x2286if (x1 < edgeMinX) { edgeMinX = x1; }287if (x2 > edgeMaxX) { edgeMaxX = x2; }288} else {289if (x2 < edgeMinX) { edgeMinX = x2; }290if (x1 > edgeMaxX) { edgeMaxX = x1; }291}292293final int ptr = numEdges * SIZEOF_EDGE;294edges = Helpers.widenArray(edges, ptr, SIZEOF_EDGE);295numEdges++;296edges[ptr+OR] = or;297edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope;298edges[ptr+SLOPE] = slope;299edges[ptr+YMAX] = lastCrossing;300final int bucketIdx = firstCrossing - boundsMinY;301addEdgeToBucket(ptr, bucketIdx);302edgeBucketCounts[lastCrossing - boundsMinY] |= 1;303}304305// END EDGE LIST306//////////////////////////////////////////////////////////////////////////////307308309public static final int WIND_EVEN_ODD = 0;310public static final int WIND_NON_ZERO = 1;311312// Antialiasing313final private int SUBPIXEL_LG_POSITIONS_X;314final private int SUBPIXEL_LG_POSITIONS_Y;315final private int SUBPIXEL_POSITIONS_X;316final private int SUBPIXEL_POSITIONS_Y;317final private int SUBPIXEL_MASK_X;318final private int SUBPIXEL_MASK_Y;319final int MAX_AA_ALPHA;320321// Cache to store RLE-encoded coverage mask of the current primitive322PiscesCache cache;323324// Bounds of the drawing region, at subpixel precision.325private final int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;326327// Current winding rule328private final int windingRule;329330// Current drawing position, i.e., final point of last segment331private float x0, y0;332333// Position of most recent 'moveTo' command334private float pix_sx0, pix_sy0;335336public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY,337int pix_boundsX, int pix_boundsY,338int pix_boundsWidth, int pix_boundsHeight,339int windingRule)340{341this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX;342this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY;343this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;344this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;345this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);346this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);347this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);348349this.windingRule = windingRule;350351this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X;352this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y;353this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X;354this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y;355356edges = new float[INIT_NUM_EDGES * SIZEOF_EDGE];357numEdges = 0;358edgeBuckets = new int[boundsMaxY - boundsMinY];359java.util.Arrays.fill(edgeBuckets, NULL);360edgeBucketCounts = new int[edgeBuckets.length + 1];361}362363private float tosubpixx(float pix_x) {364return pix_x * SUBPIXEL_POSITIONS_X;365}366private float tosubpixy(float pix_y) {367return pix_y * SUBPIXEL_POSITIONS_Y;368}369370public void moveTo(float pix_x0, float pix_y0) {371closePath();372this.pix_sx0 = pix_x0;373this.pix_sy0 = pix_y0;374this.y0 = tosubpixy(pix_y0);375this.x0 = tosubpixx(pix_x0);376}377378public void lineTo(float pix_x1, float pix_y1) {379float x1 = tosubpixx(pix_x1);380float y1 = tosubpixy(pix_y1);381addLine(x0, y0, x1, y1);382x0 = x1;383y0 = y1;384}385386private Curve c = new Curve();387@Override public void curveTo(float x1, float y1,388float x2, float y2,389float x3, float y3)390{391final float xe = tosubpixx(x3);392final float ye = tosubpixy(y3);393c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), tosubpixx(x2), tosubpixy(y2), xe, ye);394curveBreakIntoLinesAndAdd(x0, y0, c, xe, ye);395x0 = xe;396y0 = ye;397}398399@Override public void quadTo(float x1, float y1, float x2, float y2) {400final float xe = tosubpixx(x2);401final float ye = tosubpixy(y2);402c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);403quadBreakIntoLinesAndAdd(x0, y0, c, xe, ye);404x0 = xe;405y0 = ye;406}407408public void closePath() {409// lineTo expects its input in pixel coordinates.410lineTo(pix_sx0, pix_sy0);411}412413public void pathDone() {414closePath();415}416417418@Override419public long getNativeConsumer() {420throw new InternalError("Renderer does not use a native consumer.");421}422423private void _endRendering(final int pix_bboxx0, final int pix_bboxx1,424int ymin, int ymax)425{426// Mask to determine the relevant bit of the crossing sum427// 0x1 if EVEN_ODD, all bits if NON_ZERO428int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;429430// add 2 to better deal with the last pixel in a pixel row.431int width = pix_bboxx1 - pix_bboxx0;432int[] alpha = new int[width+2];433434int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X;435int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X;436437// Now we iterate through the scanlines. We must tell emitRow the coord438// of the first non-transparent pixel, so we must keep accumulators for439// the first and last pixels of the section of the current pixel row440// that we will emit.441// We also need to accumulate pix_bbox*, but the iterator does it442// for us. We will just get the values from it once this loop is done443int pix_maxX = Integer.MIN_VALUE;444int pix_minX = Integer.MAX_VALUE;445446int y = boundsMinY; // needs to be declared here so we emit the last row properly.447ScanlineIterator it = this.new ScanlineIterator(ymin, ymax);448for ( ; it.hasNext(); ) {449int numCrossings = it.next();450int[] crossings = it.crossings;451y = it.curY();452453if (numCrossings > 0) {454int lowx = crossings[0] >> 1;455int highx = crossings[numCrossings - 1] >> 1;456int x0 = Math.max(lowx, bboxx0);457int x1 = Math.min(highx, bboxx1);458459pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X);460pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);461}462463int sum = 0;464int prev = bboxx0;465for (int i = 0; i < numCrossings; i++) {466int curxo = crossings[i];467int curx = curxo >> 1;468// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.469int crorientation = ((curxo & 0x1) << 1) - 1;470if ((sum & mask) != 0) {471int x0 = Math.max(prev, bboxx0);472int x1 = Math.min(curx, bboxx1);473if (x0 < x1) {474x0 -= bboxx0; // turn x0, x1 from coords to indeces475x1 -= bboxx0; // in the alpha array.476477int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X;478int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;479480if (pix_x == pix_xmaxm1) {481// Start and end in same pixel482alpha[pix_x] += (x1 - x0);483alpha[pix_x+1] -= (x1 - x0);484} else {485int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;486alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X);487alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X);488alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X);489alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X);490}491}492}493sum += crorientation;494prev = curx;495}496497// even if this last row had no crossings, alpha will be zeroed498// from the last emitRow call. But this doesn't matter because499// maxX < minX, so no row will be emitted to the cache.500if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) {501emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);502pix_minX = Integer.MAX_VALUE;503pix_maxX = Integer.MIN_VALUE;504}505}506507// Emit final row508if (pix_maxX >= pix_minX) {509emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);510}511}512513public void endRendering() {514int spminX = Math.max((int)Math.ceil(edgeMinX), boundsMinX);515int spmaxX = Math.min((int)Math.ceil(edgeMaxX), boundsMaxX);516int spminY = Math.max((int)Math.ceil(edgeMinY), boundsMinY);517int spmaxY = Math.min((int)Math.ceil(edgeMaxY), boundsMaxY);518519int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;520int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;521int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;522int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;523524if (pminX > pmaxX || pminY > pmaxY) {525this.cache = new PiscesCache(boundsMinX >> SUBPIXEL_LG_POSITIONS_X,526boundsMinY >> SUBPIXEL_LG_POSITIONS_Y,527boundsMaxX >> SUBPIXEL_LG_POSITIONS_X,528boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y);529return;530}531532this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY);533_endRendering(pminX, pmaxX, spminY, spmaxY);534}535536public PiscesCache getCache() {537if (cache == null) {538throw new InternalError("cache not yet initialized");539}540return cache;541}542543private void emitRow(int[] alphaRow, int pix_y, int pix_from, int pix_to) {544// Copy rowAA data into the cache if one is present545if (cache != null) {546if (pix_to >= pix_from) {547cache.startRow(pix_y, pix_from);548549// Perform run-length encoding and store results in the cache550int from = pix_from - cache.bboxX0;551int to = pix_to - cache.bboxX0;552553int runLen = 1;554int startVal = alphaRow[from];555for (int i = from + 1; i <= to; i++) {556int nextVal = startVal + alphaRow[i];557if (nextVal == startVal) {558runLen++;559} else {560cache.addRLERun(startVal, runLen);561runLen = 1;562startVal = nextVal;563}564}565cache.addRLERun(startVal, runLen);566}567}568java.util.Arrays.fill(alphaRow, 0);569}570}571572573