0001: /*
0002: *
0003: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License version
0008: * 2 only, as published by the Free Software Foundation.
0009: *
0010: * This program is distributed in the hope that it will be useful, but
0011: * WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * General Public License version 2 for more details (a copy is
0014: * included at /legal/license.txt).
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * version 2 along with this work; if not, write to the Free Software
0018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0019: * 02110-1301 USA
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0022: * Clara, CA 95054 or visit www.sun.com if you need additional
0023: * information or have any questions.
0024: */
0025:
0026: package com.sun.pisces;
0027:
0028: public class Renderer extends RendererBase {
0029:
0030: // Initial edge list size
0031: // IMPL_NOTE - restore size after growth
0032: public static final int INITIAL_EDGES = 1000;
0033:
0034: // Recommended maximum scratchpad sizes. The arrays will grow
0035: // larger if needed, but when finished() is called they will be released
0036: // if they have grown larger than these sizes.
0037: public static final int DEFAULT_INDICES_SIZE = 8192;
0038: public static final int DEFAULT_CROSSINGS_SIZE = 32 * 1024;
0039:
0040: public static final int NUM_ALPHA_ROWS = 8;
0041:
0042: // Antialiasing
0043: private int SUBPIXEL_LG_POSITIONS_X;
0044: private int SUBPIXEL_LG_POSITIONS_Y;
0045: private int SUBPIXEL_MASK_X;
0046: private int SUBPIXEL_MASK_Y;
0047: private int SUBPIXEL_POSITIONS_X;
0048: private int SUBPIXEL_POSITIONS_Y;
0049: private int MAX_AA_ALPHA;
0050: private int MAX_AA_ALPHA_DENOM;
0051: private int HALF_MAX_AA_ALPHA_DENOM;
0052: private int XSHIFT;
0053: private int YSHIFT;
0054: private int YSTEP;
0055: private int HYSTEP;
0056: private int YMASK;
0057:
0058: private static final int MIN_QUAD_OPT_WIDTH = 100 << 16;
0059:
0060: // Cache to store RLE-encoded coverage mask of the current primitive
0061: PiscesCache cache = null;
0062:
0063: // Bounds of the drawing region, at S15.16 precsion
0064: private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
0065:
0066: // Bounds of the current primitive, at subsample precision
0067: private int rasterMinX, rasterMaxX, rasterMinY, rasterMaxY;
0068:
0069: // Pixel bounding box for current primitive
0070: private int bboxX0, bboxY0, bboxX1, bboxY1;
0071:
0072: // Current winding rule
0073: private int windingRule = WIND_NON_ZERO;
0074:
0075: // Current drawing position, i.e., final point of last segment
0076: private int x0, y0;
0077:
0078: // Position of most recent 'moveTo' command
0079: private int sx0, sy0;
0080:
0081: // Buffer to be filled with one row's worth of alpha values
0082: private byte[] rowAA = null; // needs to be short if 16x16 subsampling
0083:
0084: private int[] paintBuffer = null;
0085: private int paintBufferOffset;
0086: private int paintBufferStride;
0087:
0088: // Image information
0089: private int width, height;
0090:
0091: private Object imageData;
0092: private int imageOffset;
0093: private int imageScanlineStride;
0094: private int imagePixelStride;
0095:
0096: private static final int PAINT_FLAT_COLOR = 0;
0097: private static final int PAINT_LINEAR_GRADIENT = 1;
0098: private static final int PAINT_RADIAL_GRADIENT = 2;
0099: private static final int PAINT_TEXTURE = 3;
0100:
0101: private int paintMode = PAINT_FLAT_COLOR;
0102: private int cred, cgreen, cblue, calpha;
0103: Paint paint;
0104:
0105: // Map from [0, MAX_AA_ALPHA] to [0, 256]
0106: // Used to normalize alpha processing to 8 bits
0107: private int[] alphaMap = new int[16 * 16 + 1];
0108:
0109: private int compositeRule = Blit.COMPOSITE_SRC_OVER;
0110:
0111: // Track the number of vertical extrema of the incoming edge list
0112: // in order to determine the maximum number of crossings of a
0113: // scanline
0114: private int firstOrientation = 0;
0115: private int lastOrientation = 0;
0116: private int flips = 0;
0117:
0118: // Parameters for emitRow
0119: private int rowAAOffset;
0120: private int rowNum;
0121: private int alphaWidth;
0122: private int[] minTouched = new int[NUM_ALPHA_ROWS];
0123: private int[] maxTouched = new int[NUM_ALPHA_ROWS];
0124: private int[] rowOffsets = new int[NUM_ALPHA_ROWS];
0125: private int currX, currY;
0126: private int currImageOffset;
0127:
0128: public Renderer(Object imageData, int width, int height,
0129: int imageOffset, int imageScanlineStride,
0130: int imagePixelStride, int imageType) {
0131: super (imageType);
0132: this .imageData = imageData;
0133: this .width = width;
0134: this .height = height;
0135: this .imageOffset = imageOffset;
0136: this .imageScanlineStride = imageScanlineStride;
0137: this .imagePixelStride = imagePixelStride;
0138: this .imageType = imageType;
0139:
0140: setAntialiasing(DEFAULT_SUBPIXEL_LG_POSITIONS_X,
0141: DEFAULT_SUBPIXEL_LG_POSITIONS_Y);
0142: }
0143:
0144: public void setImageData(Object imageData, int imageOffset,
0145: int imageScanlineStride, int imagePixelStride) {
0146: this .imageData = imageData;
0147: this .imageOffset = imageOffset;
0148: this .imageScanlineStride = imageScanlineStride;
0149: this .imagePixelStride = imagePixelStride;
0150: }
0151:
0152: // Create a lookup table indexed from 0 to MAX_AA_ALPHA, inclusive,
0153: // containing normalized values for (index*alpha). Normalization
0154: // maps a value from 0 to MAX_AA_ALPHA*alpha into the range 0 to 256.
0155: private void createAlphaMap(int alpha) {
0156: for (int i = 0; i <= MAX_AA_ALPHA; i++) {
0157: alphaMap[i] = (256 * i * alpha + HALF_MAX_AA_ALPHA_DENOM)
0158: / MAX_AA_ALPHA_DENOM;
0159: }
0160: }
0161:
0162: public void setAntialiasing(int subpixelLgPositionsX,
0163: int subpixelLgPositionsY) {
0164: this .SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX;
0165: this .SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY;
0166:
0167: this .SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;
0168: this .SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;
0169: this .SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
0170: this .SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
0171: this .MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);
0172: this .MAX_AA_ALPHA_DENOM = 255 * MAX_AA_ALPHA;
0173: this .HALF_MAX_AA_ALPHA_DENOM = MAX_AA_ALPHA_DENOM / 2;
0174: this .XSHIFT = 16 - SUBPIXEL_LG_POSITIONS_X;
0175: this .YSHIFT = 16 - SUBPIXEL_LG_POSITIONS_Y;
0176: this .YSTEP = 1 << YSHIFT;
0177: this .HYSTEP = 1 << (YSHIFT - 1);
0178: this .YMASK = ~(YSTEP - 1);
0179:
0180: createAlphaMap(this .calpha);
0181: }
0182:
0183: public int getSubpixelLgPositionsX() {
0184: return SUBPIXEL_LG_POSITIONS_X;
0185: }
0186:
0187: public int getSubpixelLgPositionsY() {
0188: return SUBPIXEL_LG_POSITIONS_Y;
0189: }
0190:
0191: public void setColor(int red, int green, int blue, int alpha) {
0192: if (imageType == TYPE_INT_RGB || imageType == TYPE_INT_ARGB
0193: || imageType == TYPE_BYTE_GRAY) {
0194: this .cred = red;
0195: this .cgreen = green;
0196: this .cblue = blue;
0197: } else if (imageType == TYPE_USHORT_565_RGB) {
0198: this .cred = Blit.convert8To5[red];
0199: this .cgreen = Blit.convert8To6[green];
0200: this .cblue = Blit.convert8To5[blue];
0201: }
0202:
0203: this .calpha = alpha;
0204: createAlphaMap(calpha);
0205:
0206: this .paint = null;
0207: this .paintMode = PAINT_FLAT_COLOR;
0208: }
0209:
0210: public void setPaint(Paint paint) {
0211: this .paint = paint;
0212: createAlphaMap(255);
0213:
0214: if (paint instanceof LinearGradient) {
0215: this .paintMode = PAINT_LINEAR_GRADIENT;
0216: } else if (paint instanceof RadialGradient) {
0217: this .paintMode = PAINT_RADIAL_GRADIENT;
0218: } else if (paint instanceof Texture) {
0219: this .paintMode = PAINT_TEXTURE;
0220: } else {
0221: throw new IllegalArgumentException("Unknown paint type!");
0222: }
0223: }
0224:
0225: public void beginRendering(int boundsX, int boundsY,
0226: int boundsWidth, int boundsHeight, int windingRule) {
0227: lastOrientation = 0;
0228: flips = 0;
0229:
0230: resetEdges();
0231:
0232: this .boundsMinX = boundsX << 16;
0233: this .boundsMinY = boundsY << 16;
0234: this .boundsMaxX = (boundsX + boundsWidth) << 16;
0235: this .boundsMaxY = (boundsY + boundsHeight) << 16;
0236: this .windingRule = windingRule;
0237:
0238: this .bboxX0 = boundsX;
0239: this .bboxY0 = boundsY;
0240: this .bboxX1 = boundsX + boundsWidth;
0241: this .bboxY1 = boundsY + boundsHeight;
0242: }
0243:
0244: public void moveTo(int x0, int y0) {
0245: // System.out.println("Renderer: moveTo " + x0/65536.0 + " " + y0/65536.0);
0246: this .sx0 = this .x0 = x0;
0247: this .sy0 = this .y0 = y0;
0248: this .lastOrientation = 0;
0249: }
0250:
0251: public void lineJoin() {
0252: // System.out.println("Renderer: lineJoin");
0253: // do nothing
0254: }
0255:
0256: public void lineTo(int x1, int y1) {
0257: // System.out.println("Renderer: lineTo " + x1/65536.0 + " " + y1/65536.0);
0258:
0259: // Ignore horizontal lines
0260: // Next line will count flip
0261: if (y0 == y1) {
0262: this .x0 = x1;
0263: return;
0264: }
0265:
0266: int orientation = (y0 < y1) ? 1 : -1;
0267: if (lastOrientation == 0) {
0268: firstOrientation = orientation;
0269: } else if (orientation != lastOrientation) {
0270: ++flips;
0271: }
0272: lastOrientation = orientation;
0273:
0274: // Bias Y by 1 ULP so endpoints never lie on a scanline
0275: addEdge(x0, y0 | 0x1, x1, y1 | 0x1);
0276:
0277: this .x0 = x1;
0278: this .y0 = y1;
0279: }
0280:
0281: public void close() {
0282: // System.out.println("Renderer: close");
0283:
0284: int orientation = lastOrientation;
0285: if (y0 != sy0) {
0286: orientation = (y0 < sy0) ? 1 : -1;
0287: }
0288: if (orientation != firstOrientation) {
0289: ++flips;
0290: }
0291: lineTo(sx0, sy0);
0292: }
0293:
0294: public void end() {
0295: // System.out.println("Renderer: end");
0296: // do nothing
0297: }
0298:
0299: // Scan convert a single edge
0300: private void computeCrossingsForEdge(int index, int boundsMinY,
0301: int boundsMaxY) {
0302: int iy0 = edges[index + 1];
0303: int iy1 = edges[index + 3];
0304:
0305: // Clip to valid Y range
0306: int clipy0 = (iy0 > boundsMinY) ? iy0 : boundsMinY;
0307: int clipy1 = (iy1 < boundsMaxY) ? iy1 : boundsMaxY;
0308:
0309: int minY = ((clipy0 + HYSTEP) & YMASK) + HYSTEP;
0310: int maxY = ((clipy1 - HYSTEP) & YMASK) + HYSTEP;
0311:
0312: // IMPL_NOTE - If line falls outside the valid X range, could
0313: // draw a vertical line instead
0314:
0315: // Exit if no scanlines are crossed
0316: if (minY > maxY) {
0317: return;
0318: }
0319:
0320: // Scan convert line using a DDA approach
0321:
0322: int ix0 = edges[index];
0323: int ix1 = edges[index + 2];
0324: int dx = ix1 - ix0;
0325: int dy = iy1 - iy0;
0326:
0327: // Compute first crossing point at y = minY
0328: int orientation = edges[index + 4];
0329: int y = minY;
0330: long lx = (long) (y - iy0) * dx / dy + ix0;
0331: addCrossing(y >> YSHIFT, (int) (lx >> XSHIFT), orientation);
0332:
0333: // Advance y to next scanline, exit if past endpoint
0334: y += YSTEP;
0335: if (y > maxY) {
0336: return;
0337: }
0338:
0339: // Compute xstep only if additional scanlines are crossed
0340: // For each scanline, add xstep to lx and YSTEP to y and
0341: // emit the new crossing
0342: long xstep = ((long) YSTEP * dx) / dy;
0343: for (; y <= maxY; y += YSTEP) {
0344: lx += xstep;
0345: addCrossing(y >> YSHIFT, (int) (lx >> XSHIFT), orientation);
0346: }
0347: }
0348:
0349: private void computeBounds() {
0350: rasterMinX = crossingMinX & ~SUBPIXEL_MASK_X;
0351: rasterMaxX = crossingMaxX | SUBPIXEL_MASK_X;
0352: rasterMinY = crossingMinY & ~SUBPIXEL_MASK_Y;
0353: rasterMaxY = crossingMaxY | SUBPIXEL_MASK_Y;
0354:
0355: // If nothing was drawn, we have:
0356: // minX = Integer.MAX_VALUE and maxX = Integer.MIN_VALUE
0357: // so nothing to render
0358: if (rasterMinX > rasterMaxX || rasterMinY > rasterMaxY) {
0359: rasterMinX = 0;
0360: rasterMaxX = -1;
0361: rasterMinY = 0;
0362: rasterMaxY = -1;
0363: return;
0364: }
0365:
0366: if (rasterMinX < boundsMinX >> XSHIFT) {
0367: rasterMinX = boundsMinX >> XSHIFT;
0368: }
0369: if (rasterMinY < boundsMinY >> YSHIFT) {
0370: rasterMinY = boundsMinY >> YSHIFT;
0371: }
0372: if (rasterMaxX > boundsMaxX >> XSHIFT) {
0373: rasterMaxX = boundsMaxX >> XSHIFT;
0374: }
0375: if (rasterMaxY > boundsMaxY >> YSHIFT) {
0376: rasterMaxY = boundsMaxY >> YSHIFT;
0377: }
0378: }
0379:
0380: private int clamp(int x, int min, int max) {
0381: if (x < min) {
0382: return min;
0383: } else if (x > max) {
0384: return max;
0385: }
0386: return x;
0387: }
0388:
0389: private void _endRendering() {
0390: if (flips == 0) {
0391: bboxX0 = bboxY0 = 0;
0392: bboxX1 = bboxY1 = -1;
0393: return;
0394: }
0395:
0396: // Special case for filling a single rect with a flat, opaque color
0397: if (paintMode == PAINT_FLAT_COLOR && calpha == 255
0398: && edgeIdx == 10 && edges[0] == edges[2]
0399: && edges[1] == edges[6] && edges[3] == edges[8]
0400: && edges[5] == edges[7]
0401: && Math.abs(edges[0] - edges[5]) > MIN_QUAD_OPT_WIDTH) {
0402:
0403: int x0 = edges[0] >> XSHIFT;
0404: int y0 = edges[1] >> YSHIFT;
0405: int x1 = edges[5] >> XSHIFT;
0406: int y1 = edges[3] >> YSHIFT;
0407:
0408: if (x0 > x1) {
0409: int tmp = x0;
0410: x0 = x1;
0411: x1 = tmp;
0412: }
0413: if (y0 > y1) {
0414: int tmp = y0;
0415: y0 = y1;
0416: y1 = tmp;
0417: }
0418:
0419: int bMinX = this .boundsMinX >> XSHIFT;
0420: int bMinY = this .boundsMinY >> YSHIFT;
0421: int bMaxX = this .boundsMaxX >> XSHIFT;
0422: int bMaxY = this .boundsMaxY >> YSHIFT;
0423:
0424: // Clip to image bounds in supersampled coordinates
0425: x0 = clamp(x0, bMinX, bMaxX);
0426: x1 = clamp(x1, bMinX, bMaxX);
0427: y0 = clamp(y0, bMinY, bMaxY);
0428: y1 = clamp(y1, bMinY, bMaxY);
0429:
0430: Blit.fillRectSrcOver(this , imageData, imageType,
0431: imageOffset, imageScanlineStride, imagePixelStride,
0432: width, height, x0, y0, x1, y1, cred, cgreen, cblue);
0433:
0434: bboxX0 = x0 >> SUBPIXEL_LG_POSITIONS_X;
0435: bboxY0 = y0 >> SUBPIXEL_LG_POSITIONS_Y;
0436: bboxX1 = (x1 + SUBPIXEL_POSITIONS_X - 1) >> SUBPIXEL_LG_POSITIONS_X;
0437: bboxY1 = (y1 + SUBPIXEL_POSITIONS_Y - 1) >> SUBPIXEL_LG_POSITIONS_Y;
0438:
0439: return;
0440: }
0441:
0442: int minY = (edgeMinY > boundsMinY) ? edgeMinY : boundsMinY;
0443: int maxY = (edgeMaxY < boundsMaxY) ? edgeMaxY : boundsMaxY;
0444:
0445: // Check for empty intersection of primitive with the drawing area
0446: if (minY > maxY) {
0447: bboxX0 = bboxY0 = 0;
0448: bboxX1 = bboxY1 = -1;
0449: return;
0450: }
0451:
0452: // Compute Y extent in subpixel coordinates
0453: int iminY = (minY >> YSHIFT) & ~SUBPIXEL_MASK_Y;
0454: int imaxY = (maxY >> YSHIFT) | SUBPIXEL_MASK_Y;
0455: int yextent = (imaxY - iminY) + 1;
0456:
0457: // Maximum number of crossings
0458: int size = flips * yextent;
0459:
0460: int bmax = (boundsMaxY >> YSHIFT) - 1;
0461: if (imaxY > bmax) {
0462: imaxY = bmax;
0463: }
0464:
0465: // Initialize X bounds, will be refined for each strip
0466: bboxX0 = Integer.MAX_VALUE;
0467: bboxX1 = Integer.MIN_VALUE;
0468:
0469: // Set Y bounds
0470: bboxY0 = iminY >> SUBPIXEL_LG_POSITIONS_Y;
0471: bboxY1 = (imaxY + SUBPIXEL_POSITIONS_Y - 1) >> SUBPIXEL_LG_POSITIONS_Y;
0472:
0473: // Compute number of rows that can be processing using
0474: // a crossings table no larger than DEFAULT_CROSSINGS_SIZE.
0475: // However, we must process at least one row, so we grow the table
0476: // temporarily if needed. This would require an object with a
0477: // huge number of flips.
0478: int rows = DEFAULT_CROSSINGS_SIZE
0479: / (flips * SUBPIXEL_POSITIONS_Y);
0480: rows = Math.min(rows, yextent);
0481: rows = Math.max(rows, 1);
0482: for (int i = iminY; i <= imaxY; i += rows
0483: * SUBPIXEL_POSITIONS_Y) {
0484: // Compute index of last scanline to be processed in this pass
0485: int last = Math.min(i + rows * SUBPIXEL_POSITIONS_Y - 1,
0486: imaxY);
0487: setCrossingsExtents(i, last, flips);
0488:
0489: int bminY = i << YSHIFT;
0490: int bmaxY = (last << YSHIFT) | ~YMASK;
0491:
0492: // Process edges from the edge list
0493: int maxIdx = edgeIdx;
0494: for (int index = 0; index < maxIdx; index += 5) {
0495: // Test y1 < min:
0496: //
0497: // If edge lies entirely above current strip,
0498: // discard it
0499: if (edges[index + 3] < bminY) {
0500: // Overwrite the edge with the last edge
0501: edgeIdx -= 5;
0502: int fidx = edgeIdx;
0503: int tidx = index;
0504: edges[tidx++] = edges[fidx++];
0505: edges[tidx++] = edges[fidx++];
0506: edges[tidx++] = edges[fidx++];
0507: edges[tidx++] = edges[fidx++];
0508: edges[tidx] = edges[fidx];
0509:
0510: maxIdx -= 5;
0511: index -= 5;
0512: continue;
0513: }
0514:
0515: // Test y0 > max:
0516: //
0517: // If edge lies entirely below current strip,
0518: // skip it for now
0519: if (edges[index + 1] > bmaxY) {
0520: continue;
0521: }
0522:
0523: computeCrossingsForEdge(index, bminY, bmaxY);
0524: }
0525:
0526: computeBounds();
0527: if (rasterMaxX < rasterMinX) {
0528: continue;
0529: }
0530:
0531: bboxX0 = Math.min(bboxX0,
0532: rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
0533: bboxX1 = Math
0534: .max(
0535: bboxX1,
0536: (rasterMaxX + SUBPIXEL_POSITIONS_X - 1) >> SUBPIXEL_LG_POSITIONS_X);
0537: renderStrip();
0538: }
0539:
0540: // Free up any unusually large scratchpad memory used by the
0541: // preceding primitive
0542: crossingListFinished();
0543: }
0544:
0545: public void endRendering() {
0546: _endRendering();
0547:
0548: // If a cache is present, store the bounding box in it as well
0549: if (cache != null) {
0550: cache.bboxX0 = bboxX0;
0551: cache.bboxY0 = bboxY0;
0552: cache.bboxX1 = bboxX1;
0553: cache.bboxY1 = bboxY1;
0554:
0555: cache.isValid = true;
0556: }
0557: }
0558:
0559: public void getBoundingBox(int[] bbox) {
0560: bbox[0] = bboxX0;
0561: bbox[1] = bboxY0;
0562: bbox[2] = bboxX1 - bboxX0;
0563: bbox[3] = bboxY1 - bboxY0;
0564: }
0565:
0566: private void renderStrip() {
0567: // Grow rowAA according to the raster width
0568: int width = (rasterMaxX - rasterMinX + 1) >> SUBPIXEL_LG_POSITIONS_X;
0569: alphaWidth = width;
0570:
0571: // Allocate one extra entry in rowAA to avoid a conditional in
0572: // the rendering loop
0573: int bufLen = NUM_ALPHA_ROWS * width + 1;
0574: if (this .rowAA == null || this .rowAA.length < bufLen) {
0575: this .rowAA = new byte[bufLen];
0576:
0577: this .paintBuffer = new int[bufLen];
0578: this .paintBufferOffset = 0;
0579: this .paintBufferStride = width;
0580: }
0581:
0582: // Mask to determine the relevant bit of the crossing sum
0583: // 0x1 if EVEN_ODD, all bits if NON_ZERO
0584: int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
0585:
0586: int y = 0;
0587: int prevY = rasterMinY - 1;
0588:
0589: this .currY = rasterMinY >> SUBPIXEL_LG_POSITIONS_Y;
0590: this .currX = rasterMinX >> SUBPIXEL_LG_POSITIONS_X;
0591: this .currImageOffset = imageOffset + currY
0592: * imageScanlineStride + currX * imagePixelStride;
0593: this .rowAAOffset = 0;
0594: this .rowNum = 0;
0595:
0596: int minX = Integer.MAX_VALUE;
0597: int maxX = Integer.MIN_VALUE;
0598:
0599: iterateCrossings();
0600: while (hasMoreCrossingRows()) {
0601: y = crossingY;
0602:
0603: // Emit any skipped rows
0604: for (int j = prevY + 1; j < y; j++) {
0605: if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y)
0606: || (j == rasterMaxY)) {
0607: emitRow(0, -1, false);
0608: }
0609: }
0610: prevY = y;
0611:
0612: if (crossingRowIndex < crossingRowCount) {
0613: int lx = crossings[crossingRowOffset + crossingRowIndex];
0614: lx >>= 1;
0615: int hx = crossings[crossingRowOffset + crossingRowCount
0616: - 1];
0617: hx >>= 1;
0618: int x0 = lx > rasterMinX ? lx : rasterMinX;
0619: int x1 = hx < rasterMaxX ? hx : rasterMaxX;
0620: x0 -= rasterMinX;
0621: x1 -= rasterMinX;
0622:
0623: minX = Math.min(minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
0624: maxX = Math.max(maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
0625: }
0626:
0627: int sum = 0;
0628: int prev = rasterMinX;
0629: while (crossingRowIndex < crossingRowCount) {
0630: int crxo = crossings[crossingRowOffset
0631: + crossingRowIndex];
0632: crossingRowIndex++;
0633:
0634: int crx = crxo >> 1;
0635: int crorientation = ((crxo & 0x1) == 0x1) ? 1 : -1;
0636:
0637: if ((sum & mask) != 0) {
0638: // Clip to active X range, if x1 < x0 loop will
0639: // have no effect
0640: int x0 = prev > rasterMinX ? prev : rasterMinX;
0641: int x1 = crx < rasterMaxX ? crx : rasterMaxX;
0642:
0643: // Empty spans
0644: if (x1 > x0) {
0645: x0 -= rasterMinX;
0646: x1 -= rasterMinX;
0647:
0648: // Accumulate alpha, equivalent to:
0649: // for (int x = x0; x < x1; x++) {
0650: // ++rowAA[x >> SUBPIXEL_LG_POSITIONS_X];
0651: // }
0652: //
0653: // In the middle of the span, we can update a full
0654: // pixel at a time (i.e., SUBPIXEL_POSITIONS_X
0655: // subpixels)
0656:
0657: int x = x0 >> SUBPIXEL_LG_POSITIONS_X;
0658: int xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
0659: if (x == xmaxm1) {
0660: // Start and end in same pixel
0661: rowAA[x + rowAAOffset] += x1 - x0;
0662: } else {
0663: // Start and end in different pixels
0664: rowAA[x++ + rowAAOffset] += SUBPIXEL_POSITIONS_X
0665: - (x0 & SUBPIXEL_MASK_X);
0666: int xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
0667: while (x < xmax) {
0668: rowAA[x++ + rowAAOffset] += SUBPIXEL_POSITIONS_X;
0669: }
0670: // Note - at this point it is possible that
0671: // x == width, which implies that
0672: // x1 & SUBPIXEL_MASK_X == 0. We allocate
0673: // one extra entry in rowAA so this
0674: // assignment will be harmless. The alternative
0675: // is an extra conditional here, or some other
0676: // scheme to deal with the last pixel better.
0677: rowAA[x + rowAAOffset] += x1
0678: & SUBPIXEL_MASK_X;
0679: }
0680: }
0681: }
0682: sum += crorientation;
0683: prev = crx;
0684: }
0685:
0686: // Every SUBPIXEL_POSITIONS rows, output an antialiased row
0687: if (((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y)
0688: || (y == rasterMaxY)) {
0689: emitRow(minX, maxX, false);
0690: minX = Integer.MAX_VALUE;
0691: maxX = Integer.MIN_VALUE;
0692: }
0693: }
0694:
0695: // Emit final row
0696: for (int j = prevY + 1; j <= rasterMaxY; j++) {
0697: if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y)
0698: || (j == rasterMaxY)) {
0699: emitRow(minX, maxX, false);
0700: minX = Integer.MAX_VALUE;
0701: maxX = Integer.MIN_VALUE;
0702: }
0703: }
0704:
0705: // Emit last bunch of rows
0706: if (rowAAOffset != 0) {
0707: emitRow(minX, maxX, true);
0708: }
0709: }
0710:
0711: private void clearAlpha(byte[] alpha, int alphaOffset, int width,
0712: int height, int[] minTouched, int[] maxTouched,
0713: int[] rowOffsets) {
0714: for (int j = 0; j < height; j++) {
0715: int minX = minTouched[j];
0716: int maxX = maxTouched[j];
0717: if (maxX >= minX) {
0718: int w = maxX - minX + 1;
0719: if (w + minX > width) {
0720: w = width - minX;
0721: }
0722:
0723: int aidx = alphaOffset + rowOffsets[j] + minX;
0724: for (int i = 0; i < w; i++, aidx++) {
0725: alpha[aidx] = (byte) 0;
0726: }
0727: }
0728: }
0729: }
0730:
0731: private void emitRow(int minX, int maxX, boolean forceOutput) {
0732: // Copy rowAA data into the cache if one is present
0733: if (cache != null) {
0734: if (cache.alphaWidth == 0) {
0735: cache.alphaWidth = alphaWidth;
0736: } else {
0737: if (alphaWidth != cache.alphaWidth) {
0738: throw new IllegalStateException(
0739: "alphaWidth changed!");
0740: }
0741: }
0742:
0743: int len = -1;
0744: int dstIdx = cache.alphaRLELength;
0745: if (maxX >= minX) {
0746: int srcIdx = rowAAOffset + minX;
0747: len = maxX - minX + 1;
0748:
0749: // Perform run-length encoding
0750: // and store results in the cache
0751: byte startVal = rowAA[srcIdx];
0752: int runLen = 1;
0753: for (int x = 1; x < len; x++) {
0754: byte nextVal = rowAA[srcIdx + x];
0755: if (nextVal == startVal && runLen < 255) {
0756: ++runLen;
0757: } else {
0758: cache.addRLERun(startVal, runLen);
0759:
0760: runLen = 1;
0761: startVal = nextVal;
0762: }
0763: }
0764: if (runLen > 0) {
0765: cache.addRLERun(startVal, runLen);
0766: }
0767: cache.addRLERun((byte) 0, 0);
0768: }
0769:
0770: cache.addRow(minX, dstIdx);
0771: }
0772:
0773: // Record values for later blitting
0774:
0775: this .minTouched[rowNum] = minX;
0776: this .maxTouched[rowNum] = maxX;
0777: this .rowOffsets[rowNum] = rowAAOffset;
0778:
0779: rowAAOffset += alphaWidth;
0780: rowNum++;
0781: if (forceOutput || rowNum == NUM_ALPHA_ROWS) {
0782: emitRows(rowNum);
0783: clearAlpha(rowAA, 0, alphaWidth, rowNum, minTouched,
0784: maxTouched, rowOffsets);
0785:
0786: currY += rowNum;
0787: currImageOffset += rowNum * imageScanlineStride;
0788: rowAAOffset = 0;
0789: rowNum = 0;
0790: }
0791: }
0792:
0793: private void emitRows(int alphaHeight) {
0794: if (paintMode == PAINT_FLAT_COLOR) {
0795: Blit.blit(imageData, imageType, currImageOffset,
0796: imageScanlineStride, imagePixelStride, rowAA, 0,
0797: alphaWidth, alphaHeight, minTouched, maxTouched,
0798: rowOffsets, compositeRule, cred, cgreen, cblue,
0799: calpha, alphaMap);
0800: } else {
0801: paint.paint(currX, currY, alphaWidth, alphaHeight,
0802: minTouched, maxTouched, paintBuffer,
0803: paintBufferOffset, paintBufferStride);
0804:
0805: Blit.blit(imageData, imageType, currImageOffset,
0806: imageScanlineStride, imagePixelStride, rowAA, 0,
0807: alphaWidth, alphaHeight, minTouched, maxTouched,
0808: rowOffsets, compositeRule, paintBuffer,
0809: paintBufferOffset, paintBufferStride, alphaMap);
0810: }
0811: }
0812:
0813: public void setCache(PiscesCache cache) {
0814: this .cache = cache;
0815: }
0816:
0817: public void renderFromCache(PiscesCache cache) {
0818: this .alphaWidth = cache.alphaWidth;
0819: int alphaHeight = cache.alphaHeight;
0820:
0821: int bufLen = NUM_ALPHA_ROWS * alphaWidth;
0822: if (this .rowAA == null || this .rowAA.length < bufLen) {
0823: this .rowAA = new byte[bufLen];
0824: }
0825:
0826: // Decode run-length encoded alpha mask data
0827: // The data for row j begins at cache.rowOffsetsRLE[j]
0828: // and is encoded as a set of 2-byte pairs (val, runLen)
0829: // terminated by a (0, 0) pair.
0830:
0831: int currX = cache.bboxX0;
0832: int currY = cache.bboxY0;
0833: currImageOffset = imageOffset + currY * imageScanlineStride
0834: + currX * imagePixelStride;
0835:
0836: int idx = 0;
0837: for (int j = 0; j < alphaHeight; j++) {
0838: int jj = j & (NUM_ALPHA_ROWS - 1);
0839:
0840: rowOffsets[jj] = idx - cache.minTouched[j];
0841:
0842: int pos = cache.rowOffsetsRLE[j];
0843: int len = 0;
0844:
0845: while (true) {
0846: byte val = cache.rowAARLE[pos];
0847: int runLen = cache.rowAARLE[pos + 1] & 0xff;
0848: if (runLen == 0) {
0849: break;
0850: }
0851: for (int i = 0; i < runLen; i++) {
0852: rowAA[idx++] = val;
0853: }
0854: len += runLen;
0855: pos += 2;
0856: }
0857:
0858: minTouched[jj] = cache.minTouched[j];
0859: if (len == 0) {
0860: // Empty rows have minX = Integer.MAX_VALUE,
0861: // maxX = Integer.MIN_VALUE
0862: maxTouched[jj] = Integer.MIN_VALUE;
0863: } else {
0864: maxTouched[jj] = cache.minTouched[j] + len - 1;
0865: }
0866:
0867: // Perform blitting after NUM_ALPHA_ROWS rows have
0868: // been decoded, or when we reach the last row
0869: if ((jj == NUM_ALPHA_ROWS - 1) || (j == alphaHeight - 1)) {
0870: emitRows(jj + 1);
0871: currImageOffset += (jj + 1) * imageScanlineStride;
0872: idx = 0;
0873: }
0874:
0875: }
0876:
0877: // Update the bounding box for possible retrieval via getBoundingBox
0878: this .bboxX0 = cache.bboxX0;
0879: this .bboxY0 = cache.bboxY0;
0880: this .bboxX1 = cache.bboxX1;
0881: this .bboxY1 = cache.bboxY1;
0882: }
0883:
0884: // Edge list data
0885:
0886: private int[] edges = new int[5 * INITIAL_EDGES];
0887: private int edgeIdx = 0;
0888: private int edgeMinY = Integer.MAX_VALUE;
0889: private int edgeMaxY = Integer.MIN_VALUE;
0890:
0891: private void addEdge(int x0, int y0, int x1, int y1) {
0892: int newLen = edgeIdx + 5;
0893: if (edges.length < newLen) {
0894: int[] tmp = new int[Math
0895: .max(11 * edges.length / 10, newLen)];
0896: System.arraycopy(edges, 0, tmp, 0, edgeIdx);
0897: this .edges = tmp;
0898: }
0899:
0900: int orientation = 1;
0901: if (y0 > y1) {
0902: int tmp = y0;
0903: y0 = y1;
0904: y1 = tmp;
0905:
0906: orientation = -1;
0907: }
0908:
0909: // Skip edges that don't cross a subsampled scanline
0910: int eminY = ((y0 + HYSTEP) & YMASK);
0911: int emaxY = ((y1 - HYSTEP) & YMASK);
0912: if (eminY > emaxY) {
0913: return;
0914: }
0915:
0916: if (orientation == -1) {
0917: int tmp = x0;
0918: x0 = x1;
0919: x1 = tmp;
0920: }
0921:
0922: edges[edgeIdx++] = x0;
0923: edges[edgeIdx++] = y0;
0924: edges[edgeIdx++] = x1;
0925: edges[edgeIdx++] = y1;
0926: edges[edgeIdx++] = orientation;
0927:
0928: // Update Y bounds of primitive
0929: if (y0 < edgeMinY) {
0930: edgeMinY = y0;
0931: }
0932: if (y1 > edgeMaxY) {
0933: edgeMaxY = y1;
0934: }
0935: }
0936:
0937: private void resetEdges() {
0938: this .edgeIdx = 0;
0939: this .edgeMinY = Integer.MAX_VALUE;
0940: this .edgeMaxY = Integer.MIN_VALUE;
0941: }
0942:
0943: // Crossing list data
0944:
0945: private int[] crossingIndices;
0946: private int[] crossings;
0947: private int crossingMinY;
0948: private int crossingMaxY;
0949: private int crossingMinX = Integer.MAX_VALUE;
0950: private int crossingMaxX = Integer.MIN_VALUE;
0951: private int crossingMaxXEntries;
0952: private int numCrossings = 0;
0953: private boolean crossingsSorted = false;
0954:
0955: private int crossingY;
0956: private int crossingRowCount;
0957: private int crossingRowOffset;
0958: private int crossingRowIndex;
0959:
0960: private void setCrossingsExtents(int minY, int maxY, int maxXEntries) {
0961: int yextent = maxY - minY + 1;
0962:
0963: // Grow indices array as needed
0964: if (crossingIndices == null || crossingIndices.length < yextent) {
0965: this .crossingIndices = new int[Math.max(yextent,
0966: DEFAULT_INDICES_SIZE)];
0967: }
0968: // Grow crossings array as needed
0969: if (crossings == null
0970: || crossings.length < yextent * maxXEntries) {
0971: this .crossings = new int[Math.max(yextent * maxXEntries,
0972: DEFAULT_CROSSINGS_SIZE)];
0973: }
0974: this .crossingMinY = minY;
0975: this .crossingMaxY = maxY;
0976: this .crossingMaxXEntries = maxXEntries;
0977: resetCrossings();
0978: }
0979:
0980: private void resetCrossings() {
0981: int yextent = crossingMaxY - crossingMinY + 1;
0982: int start = 0;
0983: for (int i = 0; i < yextent; i++) {
0984: crossingIndices[i] = start;
0985: start += crossingMaxXEntries;
0986: }
0987: crossingMinX = Integer.MAX_VALUE;
0988: crossingMaxX = Integer.MIN_VALUE;
0989: numCrossings = 0;
0990: crossingsSorted = false;
0991: }
0992:
0993: // Free sorting arrays if larger than maximum size
0994: private void crossingListFinished() {
0995: if (crossings.length > DEFAULT_CROSSINGS_SIZE) {
0996: crossings = new int[DEFAULT_CROSSINGS_SIZE];
0997: }
0998: if (crossingIndices.length > DEFAULT_INDICES_SIZE) {
0999: crossingIndices = new int[DEFAULT_INDICES_SIZE];
1000: }
1001: }
1002:
1003: private void sortCrossings(int[] x, int off, int len) {
1004: for (int i = off + 1; i < off + len; i++) {
1005: int j = i;
1006: int xj = x[j];
1007: int xjm1;
1008:
1009: while (j > off && (xjm1 = x[j - 1]) > xj) {
1010: x[j] = xjm1;
1011: x[j - 1] = xj;
1012: j--;
1013: }
1014: }
1015: }
1016:
1017: private void sortCrossings() {
1018: int start = 0;
1019: for (int i = 0; i <= crossingMaxY - crossingMinY; i++) {
1020: sortCrossings(crossings, start, crossingIndices[i] - start);
1021: start += crossingMaxXEntries;
1022: }
1023: }
1024:
1025: private void addCrossing(int y, int x, int orientation) {
1026: if (x < crossingMinX) {
1027: crossingMinX = x;
1028: }
1029: if (x > crossingMaxX) {
1030: crossingMaxX = x;
1031: }
1032:
1033: int index = crossingIndices[y - crossingMinY]++;
1034: x <<= 1;
1035: crossings[index] = (orientation == 1) ? (x | 0x1) : x;
1036:
1037: ++numCrossings;
1038: }
1039:
1040: private void iterateCrossings() {
1041: if (!crossingsSorted) {
1042: sortCrossings();
1043: crossingsSorted = true;
1044: }
1045: crossingY = crossingMinY - 1;
1046: crossingRowOffset = -crossingMaxXEntries;
1047: }
1048:
1049: private boolean hasMoreCrossingRows() {
1050: if (++crossingY <= crossingMaxY) {
1051: crossingRowOffset += crossingMaxXEntries;
1052: int y = crossingY - crossingMinY;
1053: crossingRowCount = crossingIndices[y] - y
1054: * crossingMaxXEntries;
1055: crossingRowIndex = 0;
1056: return true;
1057: } else {
1058: return false;
1059: }
1060: }
1061:
1062: public void clearRect(int x, int y, int w, int h) {
1063: Blit.clearRect(imageData, imageType, imageOffset,
1064: imageScanlineStride, imagePixelStride, x, y, w, h,
1065: calpha, cred, cgreen, cblue);
1066: }
1067: }
|