History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/java2d/pisces/Renderer.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/java2d/pisces/Renderer.java+571−0
@@ -0,0 +1,571 @@
1+/*
2+ * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.java2d.pisces;
27+
28+import sun.awt.geom.PathConsumer2D;
29+
30+final class Renderer implements PathConsumer2D {
31+
32+ private class ScanlineIterator {
33+
34+ private int[] crossings;
35+
36+ // crossing bounds. The bounds are not necessarily tight (the scan line
37+ // at minY, for example, might have no crossings). The x bounds will
38+ // be accumulated as crossings are computed.
39+ private final int maxY;
40+ private int nextY;
41+
42+ // indices into the segment pointer lists. They indicate the "active"
43+ // sublist in the segment lists (the portion of the list that contains
44+ // all the segments that cross the next scan line).
45+ private int edgeCount;
46+ private int[] edgePtrs;
47+
48+ private static final int INIT_CROSSINGS_SIZE = 10;
49+
50+ // Preconditions: Only subpixel scanlines in the range
51+ // (start <= subpixel_y <= end) will be evaluated. No
52+ // edge may have a valid (i.e. inside the supplied clip)
53+ // crossing that would be generated outside that range.
54+ private ScanlineIterator(int start, int end) {
55+ crossings = new int[INIT_CROSSINGS_SIZE];
56+ edgePtrs = new int[INIT_CROSSINGS_SIZE];
57+
58+ nextY = start;
59+ maxY = end;
60+ edgeCount = 0;
61+ }
62+
63+ private int next() {
64+ int cury = nextY++;
65+ int bucket = cury - boundsMinY;
66+ int count = this.edgeCount;
67+ int ptrs[] = this.edgePtrs;
68+ int bucketcount = edgeBucketCounts[bucket];
69+ if ((bucketcount & 0x1) != 0) {
70+ int newCount = 0;
71+ for (int i = 0; i < count; i++) {
72+ int ecur = ptrs[i];
73+ if (edges[ecur+YMAX] > cury) {
74+ ptrs[newCount++] = ecur;
75+ }
76+ }
77+ count = newCount;
78+ }
79+ ptrs = Helpers.widenArray(ptrs, count, bucketcount >> 1);
80+ for (int ecur = edgeBuckets[bucket]; ecur != NULL; ecur = (int)edges[ecur+NEXT]) {
81+ ptrs[count++] = ecur;
82+ // REMIND: Adjust start Y if necessary
83+ }
84+ this.edgePtrs = ptrs;
85+ this.edgeCount = count;
86+// if ((count & 0x1) != 0) {
87+// System.out.println("ODD NUMBER OF EDGES!!!!");
88+// }
89+ int xings[] = this.crossings;
90+ if (xings.length < count) {
91+ this.crossings = xings = new int[ptrs.length];
92+ }
93+ for (int i = 0; i < count; i++) {
94+ int ecur = ptrs[i];
95+ float curx = edges[ecur+CURX];
96+ int cross = ((int) curx) << 1;
97+ edges[ecur+CURX] = curx + edges[ecur+SLOPE];
98+ if (edges[ecur+OR] > 0) {
99+ cross |= 1;
100+ }
101+ int j = i;
102+ while (--j >= 0) {
103+ int jcross = xings[j];
104+ if (jcross <= cross) {
105+ break;
106+ }
107+ xings[j+1] = jcross;
108+ ptrs[j+1] = ptrs[j];
109+ }
110+ xings[j+1] = cross;
111+ ptrs[j+1] = ecur;
112+ }
113+ return count;
114+ }
115+
116+ private boolean hasNext() {
117+ return nextY < maxY;
118+ }
119+
120+ private int curY() {
121+ return nextY - 1;
122+ }
123+ }
124+
125+
126+//////////////////////////////////////////////////////////////////////////////
127+// EDGE LIST
128+//////////////////////////////////////////////////////////////////////////////
129+// TODO(maybe): very tempting to use fixed point here. A lot of opportunities
130+// for shifts and just removing certain operations altogether.
131+
132+ // common to all types of input path segments.
133+ private static final int YMAX = 0;
134+ private static final int CURX = 1;
135+ // NEXT and OR are meant to be indices into "int" fields, but arrays must
136+ // be homogenous, so every field is a float. However floats can represent
137+ // exactly up to 26 bit ints, so we're ok.
138+ private static final int OR = 2;
139+ private static final int SLOPE = 3;
140+ private static final int NEXT = 4;
141+
142+ private float edgeMinY = Float.POSITIVE_INFINITY;
143+ private float edgeMaxY = Float.NEGATIVE_INFINITY;
144+ private float edgeMinX = Float.POSITIVE_INFINITY;
145+ private float edgeMaxX = Float.NEGATIVE_INFINITY;
146+
147+ private static final int SIZEOF_EDGE = 5;
148+ // don't just set NULL to -1, because we want NULL+NEXT to be negative.
149+ private static final int NULL = -SIZEOF_EDGE;
150+ private float[] edges = null;
151+ private static final int INIT_NUM_EDGES = 8;
152+ private int[] edgeBuckets = null;
153+ private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed)
154+ private int numEdges;
155+
156+ private static final float DEC_BND = 20f;
157+ private static final float INC_BND = 8f;
158+
159+ // each bucket is a linked list. this method adds eptr to the
160+ // start of the "bucket"th linked list.
161+ private void addEdgeToBucket(final int eptr, final int bucket) {
162+ edges[eptr+NEXT] = edgeBuckets[bucket];
163+ edgeBuckets[bucket] = eptr;
164+ edgeBucketCounts[bucket] += 2;
165+ }
166+
167+ // Flattens using adaptive forward differencing. This only carries out
168+ // one iteration of the AFD loop. All it does is update AFD variables (i.e.
169+ // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
170+ private void quadBreakIntoLinesAndAdd(float x0, float y0,
171+ final Curve c,
172+ final float x2, final float y2)
173+ {
174+ final float QUAD_DEC_BND = 32;
175+ final int countlg = 4;
176+ int count = 1 << countlg;
177+ int countsq = count * count;
178+ float maxDD = Math.max(c.dbx / countsq, c.dby / countsq);
179+ while (maxDD > QUAD_DEC_BND) {
180+ maxDD /= 4;
181+ count <<= 1;
182+ }
183+
184+ countsq = count * count;
185+ final float ddx = c.dbx / countsq;
186+ final float ddy = c.dby / countsq;
187+ float dx = c.bx / countsq + c.cx / count;
188+ float dy = c.by / countsq + c.cy / count;
189+
190+ while (count-- > 1) {
191+ float x1 = x0 + dx;
192+ dx += ddx;
193+ float y1 = y0 + dy;
194+ dy += ddy;
195+ addLine(x0, y0, x1, y1);
196+ x0 = x1;
197+ y0 = y1;
198+ }
199+ addLine(x0, y0, x2, y2);
200+ }
201+
202+ // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these
203+ // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce
204+ // numerical errors, and our callers already have the exact values.
205+ // Another alternative would be to pass all the control points, and call c.set
206+ // here, but then too many numbers are passed around.
207+ private void curveBreakIntoLinesAndAdd(float x0, float y0,
208+ final Curve c,
209+ final float x3, final float y3)
210+ {
211+ final int countlg = 3;
212+ int count = 1 << countlg;
213+
214+ // the dx and dy refer to forward differencing variables, not the last
215+ // coefficients of the "points" polynomial
216+ float dddx, dddy, ddx, ddy, dx, dy;
217+ dddx = 2f * c.dax / (1 << (3 * countlg));
218+ dddy = 2f * c.day / (1 << (3 * countlg));
219+
220+ ddx = dddx + c.dbx / (1 << (2 * countlg));
221+ ddy = dddy + c.dby / (1 << (2 * countlg));
222+ dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
223+ dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
224+
225+ // we use x0, y0 to walk the line
226+ float x1 = x0, y1 = y0;
227+ while (count > 0) {
228+ while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
229+ dddx /= 8;
230+ dddy /= 8;
231+ ddx = ddx/4 - dddx;
232+ ddy = ddy/4 - dddy;
233+ dx = (dx - ddx) / 2;
234+ dy = (dy - ddy) / 2;
235+ count <<= 1;
236+ }
237+ // can only do this on even "count" values, because we must divide count by 2
238+ while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
239+ dx = 2 * dx + ddx;
240+ dy = 2 * dy + ddy;
241+ ddx = 4 * (ddx + dddx);
242+ ddy = 4 * (ddy + dddy);
243+ dddx = 8 * dddx;
244+ dddy = 8 * dddy;
245+ count >>= 1;
246+ }
247+ count--;
248+ if (count > 0) {
249+ x1 += dx;
250+ dx += ddx;
251+ ddx += dddx;
252+ y1 += dy;
253+ dy += ddy;
254+ ddy += dddy;
255+ } else {
256+ x1 = x3;
257+ y1 = y3;
258+ }
259+ addLine(x0, y0, x1, y1);
260+ x0 = x1;
261+ y0 = y1;
262+ }
263+ }
264+
265+ private void addLine(float x1, float y1, float x2, float y2) {
266+ float or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
267+ if (y2 < y1) {
268+ or = y2; // no need to declare a temp variable. We have or.
269+ y2 = y1;
270+ y1 = or;
271+ or = x2;
272+ x2 = x1;
273+ x1 = or;
274+ or = 0;
275+ }
276+ final int firstCrossing = Math.max((int)Math.ceil(y1), boundsMinY);
277+ final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY);
278+ if (firstCrossing >= lastCrossing) {
279+ return;
280+ }
281+ if (y1 < edgeMinY) { edgeMinY = y1; }
282+ if (y2 > edgeMaxY) { edgeMaxY = y2; }
283+
284+ final float slope = (x2 - x1) / (y2 - y1);
285+
286+ if (slope > 0) { // <==> x1 < x2
287+ if (x1 < edgeMinX) { edgeMinX = x1; }
288+ if (x2 > edgeMaxX) { edgeMaxX = x2; }
289+ } else {
290+ if (x2 < edgeMinX) { edgeMinX = x2; }
291+ if (x1 > edgeMaxX) { edgeMaxX = x1; }
292+ }
293+
294+ final int ptr = numEdges * SIZEOF_EDGE;
295+ edges = Helpers.widenArray(edges, ptr, SIZEOF_EDGE);
296+ numEdges++;
297+ edges[ptr+OR] = or;
298+ edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope;
299+ edges[ptr+SLOPE] = slope;
300+ edges[ptr+YMAX] = lastCrossing;
301+ final int bucketIdx = firstCrossing - boundsMinY;
302+ addEdgeToBucket(ptr, bucketIdx);
303+ edgeBucketCounts[lastCrossing - boundsMinY] |= 1;
304+ }
305+
306+// END EDGE LIST
307+//////////////////////////////////////////////////////////////////////////////
308+
309+
310+ public static final int WIND_EVEN_ODD = 0;
311+ public static final int WIND_NON_ZERO = 1;
312+
313+ // Antialiasing
314+ final private int SUBPIXEL_LG_POSITIONS_X;
315+ final private int SUBPIXEL_LG_POSITIONS_Y;
316+ final private int SUBPIXEL_POSITIONS_X;
317+ final private int SUBPIXEL_POSITIONS_Y;
318+ final private int SUBPIXEL_MASK_X;
319+ final private int SUBPIXEL_MASK_Y;
320+ final int MAX_AA_ALPHA;
321+
322+ // Cache to store RLE-encoded coverage mask of the current primitive
323+ PiscesCache cache;
324+
325+ // Bounds of the drawing region, at subpixel precision.
326+ private final int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
327+
328+ // Current winding rule
329+ private final int windingRule;
330+
331+ // Current drawing position, i.e., final point of last segment
332+ private float x0, y0;
333+
334+ // Position of most recent 'moveTo' command
335+ private float pix_sx0, pix_sy0;
336+
337+ public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY,
338+ int pix_boundsX, int pix_boundsY,
339+ int pix_boundsWidth, int pix_boundsHeight,
340+ int windingRule)
341+ {
342+ this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX;
343+ this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY;
344+ this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;
345+ this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;
346+ this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
347+ this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
348+ this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);
349+
350+ this.windingRule = windingRule;
351+
352+ this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X;
353+ this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y;
354+ this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X;
355+ this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y;
356+
357+ edges = new float[INIT_NUM_EDGES * SIZEOF_EDGE];
358+ numEdges = 0;
359+ edgeBuckets = new int[boundsMaxY - boundsMinY];
360+ java.util.Arrays.fill(edgeBuckets, NULL);
361+ edgeBucketCounts = new int[edgeBuckets.length + 1];
362+ }
363+
364+ private float tosubpixx(float pix_x) {
365+ return pix_x * SUBPIXEL_POSITIONS_X;
366+ }
367+ private float tosubpixy(float pix_y) {
368+ return pix_y * SUBPIXEL_POSITIONS_Y;
369+ }
370+
371+ public void moveTo(float pix_x0, float pix_y0) {
372+ closePath();
373+ this.pix_sx0 = pix_x0;
374+ this.pix_sy0 = pix_y0;
375+ this.y0 = tosubpixy(pix_y0);
376+ this.x0 = tosubpixx(pix_x0);
377+ }
378+
379+ public void lineTo(float pix_x1, float pix_y1) {
380+ float x1 = tosubpixx(pix_x1);
381+ float y1 = tosubpixy(pix_y1);
382+ addLine(x0, y0, x1, y1);
383+ x0 = x1;
384+ y0 = y1;
385+ }
386+
387+ private Curve c = new Curve();
388+ @Override public void curveTo(float x1, float y1,
389+ float x2, float y2,
390+ float x3, float y3)
391+ {
392+ final float xe = tosubpixx(x3);
393+ final float ye = tosubpixy(y3);
394+ c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), tosubpixx(x2), tosubpixy(y2), xe, ye);
395+ curveBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
396+ x0 = xe;
397+ y0 = ye;
398+ }
399+
400+ @Override public void quadTo(float x1, float y1, float x2, float y2) {
401+ final float xe = tosubpixx(x2);
402+ final float ye = tosubpixy(y2);
403+ c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye);
404+ quadBreakIntoLinesAndAdd(x0, y0, c, xe, ye);
405+ x0 = xe;
406+ y0 = ye;
407+ }
408+
409+ public void closePath() {
410+ // lineTo expects its input in pixel coordinates.
411+ lineTo(pix_sx0, pix_sy0);
412+ }
413+
414+ public void pathDone() {
415+ closePath();
416+ }
417+
418+
419+ @Override
420+ public long getNativeConsumer() {
421+ throw new InternalError("Renderer does not use a native consumer.");
422+ }
423+
424+ private void _endRendering(final int pix_bboxx0, final int pix_bboxx1,
425+ int ymin, int ymax)
426+ {
427+ // Mask to determine the relevant bit of the crossing sum
428+ // 0x1 if EVEN_ODD, all bits if NON_ZERO
429+ int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
430+
431+ // add 2 to better deal with the last pixel in a pixel row.
432+ int width = pix_bboxx1 - pix_bboxx0;
433+ int[] alpha = new int[width+2];
434+
435+ int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X;
436+ int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X;
437+
438+ // Now we iterate through the scanlines. We must tell emitRow the coord
439+ // of the first non-transparent pixel, so we must keep accumulators for
440+ // the first and last pixels of the section of the current pixel row
441+ // that we will emit.
442+ // We also need to accumulate pix_bbox*, but the iterator does it
443+ // for us. We will just get the values from it once this loop is done
444+ int pix_maxX = Integer.MIN_VALUE;
445+ int pix_minX = Integer.MAX_VALUE;
446+
447+ int y = boundsMinY; // needs to be declared here so we emit the last row properly.
448+ ScanlineIterator it = this.new ScanlineIterator(ymin, ymax);
449+ for ( ; it.hasNext(); ) {
450+ int numCrossings = it.next();
451+ int[] crossings = it.crossings;
452+ y = it.curY();
453+
454+ if (numCrossings > 0) {
455+ int lowx = crossings[0] >> 1;
456+ int highx = crossings[numCrossings - 1] >> 1;
457+ int x0 = Math.max(lowx, bboxx0);
458+ int x1 = Math.min(highx, bboxx1);
459+
460+ pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
461+ pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
462+ }
463+
464+ int sum = 0;
465+ int prev = bboxx0;
466+ for (int i = 0; i < numCrossings; i++) {
467+ int curxo = crossings[i];
468+ int curx = curxo >> 1;
469+ // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
470+ int crorientation = ((curxo & 0x1) << 1) - 1;
471+ if ((sum & mask) != 0) {
472+ int x0 = Math.max(prev, bboxx0);
473+ int x1 = Math.min(curx, bboxx1);
474+ if (x0 < x1) {
475+ x0 -= bboxx0; // turn x0, x1 from coords to indeces
476+ x1 -= bboxx0; // in the alpha array.
477+
478+ int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X;
479+ int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
480+
481+ if (pix_x == pix_xmaxm1) {
482+ // Start and end in same pixel
483+ alpha[pix_x] += (x1 - x0);
484+ alpha[pix_x+1] -= (x1 - x0);
485+ } else {
486+ int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
487+ alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X);
488+ alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X);
489+ alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X);
490+ alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X);
491+ }
492+ }
493+ }
494+ sum += crorientation;
495+ prev = curx;
496+ }
497+
498+ // even if this last row had no crossings, alpha will be zeroed
499+ // from the last emitRow call. But this doesn't matter because
500+ // maxX < minX, so no row will be emitted to the cache.
501+ if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) {
502+ emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
503+ pix_minX = Integer.MAX_VALUE;
504+ pix_maxX = Integer.MIN_VALUE;
505+ }
506+ }
507+
508+ // Emit final row
509+ if (pix_maxX >= pix_minX) {
510+ emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
511+ }
512+ }
513+
514+ public void endRendering() {
515+ int spminX = Math.max((int)Math.ceil(edgeMinX), boundsMinX);
516+ int spmaxX = Math.min((int)Math.ceil(edgeMaxX), boundsMaxX);
517+ int spminY = Math.max((int)Math.ceil(edgeMinY), boundsMinY);
518+ int spmaxY = Math.min((int)Math.ceil(edgeMaxY), boundsMaxY);
519+
520+ int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
521+ int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
522+ int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
523+ int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
524+
525+ if (pminX > pmaxX || pminY > pmaxY) {
526+ this.cache = new PiscesCache(boundsMinX >> SUBPIXEL_LG_POSITIONS_X,
527+ boundsMinY >> SUBPIXEL_LG_POSITIONS_Y,
528+ boundsMaxX >> SUBPIXEL_LG_POSITIONS_X,
529+ boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y);
530+ return;
531+ }
532+
533+ this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY);
534+ _endRendering(pminX, pmaxX, spminY, spmaxY);
535+ }
536+
537+ public PiscesCache getCache() {
538+ if (cache == null) {
539+ throw new InternalError("cache not yet initialized");
540+ }
541+ return cache;
542+ }
543+
544+ private void emitRow(int[] alphaRow, int pix_y, int pix_from, int pix_to) {
545+ // Copy rowAA data into the cache if one is present
546+ if (cache != null) {
547+ if (pix_to >= pix_from) {
548+ cache.startRow(pix_y, pix_from);
549+
550+ // Perform run-length encoding and store results in the cache
551+ int from = pix_from - cache.bboxX0;
552+ int to = pix_to - cache.bboxX0;
553+
554+ int runLen = 1;
555+ int startVal = alphaRow[from];
556+ for (int i = from + 1; i <= to; i++) {
557+ int nextVal = startVal + alphaRow[i];
558+ if (nextVal == startVal) {
559+ runLen++;
560+ } else {
561+ cache.addRLERun(startVal, runLen);
562+ runLen = 1;
563+ startVal = nextVal;
564+ }
565+ }
566+ cache.addRLERun(startVal, runLen);
567+ }
568+ }
569+ java.util.Arrays.fill(alphaRow, 0);
570+ }
571+}