1:
37:
38:
39: package ;
40:
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47:
48:
51: public final class ScanlineConverter
52: {
53:
54:
57: private static int FIXED_DIGITS = 6;
58:
59:
62: private static int ONE = Fixed.fixedValue(FIXED_DIGITS, 1);
63:
64:
67: private static int Y_RESOLUTION = 4;
68:
69:
72: private int numScanlines;
73:
74:
80: private Scanline[] scanlines;
81:
82:
87: private int upperBounds;
88:
89:
94: private int resolution;
95:
96:
100: private int halfStep;
101:
102:
106: private float[] coords;
107:
108:
111: private ActiveEdges activeEdges;
112:
113: private PolyEdge edgePool;
114: private PolyEdge edgePoolLast;
115:
116: private int minY;
117: private int maxY;
118: private int minX;
119: private int maxX;
120:
121:
124: private ScanlineCoverage scanlineCoverage;
125:
126:
129: ScanlineConverter()
130: {
131: scanlines = new Scanline[10];
132: coords = new float[6];
133: activeEdges = new ActiveEdges();
134: edgePool = new PolyEdge();
135: edgePoolLast = edgePool;
136: scanlineCoverage = new ScanlineCoverage();
137: }
138:
139:
147: public void renderShape(Pixelizer p, Shape shape, Shape clip,
148: AffineTransform trans, int res, RenderingHints hints)
149: {
150:
151:
152:
153:
154: clear();
155: setResolution(res);
156:
157: boolean haveClip = clip != null;
158:
159:
160: float flatness = Fixed.floatValue(FIXED_DIGITS, resolution / 2);
161: PathIterator path = shape.getPathIterator(trans, flatness);
162: addShape(path, false);
163: if (haveClip)
164: {
165: path= clip.getPathIterator(trans, flatness);
166: addShape(path, true);
167: }
168:
169: setUpperBounds(minY);
170:
171: PolyEdge edge = edgePool;
172: while (edge != edgePoolLast)
173: {
174: addEdge(edge);
175: edge = edge.poolNext;
176: }
177:
178: int y = upperBounds;
179: int index;
180: activeEdges.clear();
181:
182: Scanline scanline = null;
183: int lastRealY = Fixed.intValue(FIXED_DIGITS, y);
184: while (y <= maxY)
185: {
186:
187: index = scanlineIndex(y);
188:
189:
190: scanline = index < scanlines.length ? scanlines[index] : null;
191: if (scanline != null)
192: {
193: edge = scanline.getEdges();
194: while (edge != null)
195: {
196: activeEdges.add(edge);
197: edge = edge.scanlineNext;
198: }
199: }
200:
201:
202:
203: activeEdges.intersectSortAndPack(FIXED_DIGITS, y + halfStep);
204:
205:
206: int realY = Fixed.intValue(FIXED_DIGITS, y + resolution);
207: boolean push = lastRealY != realY;
208: doScanline(p, y, push, haveClip);
209:
210:
211:
212:
213: y += resolution;
214: lastRealY = realY;
215:
216: }
217: }
218:
219:
222: private void clear()
223: {
224:
225: edgePoolLast = edgePool;
226:
227:
228: for (int i = scanlines.length - 1; i >= 0 ; i--)
229: {
230: Scanline sl = scanlines[i];
231: if (sl != null)
232: sl.clear();
233: }
234:
235:
236: scanlineCoverage.clear();
237:
238:
239: minY = Integer.MAX_VALUE;
240: maxY = Integer.MIN_VALUE;
241: minX = Integer.MAX_VALUE;
242: maxX = Integer.MIN_VALUE;
243: }
244:
245:
254: private void doScanline(Pixelizer p, int y, boolean push,
255: boolean haveClip)
256: {
257:
258: scanlineCoverage.rewind();
259:
260:
261:
262: boolean inClip = ! haveClip;
263: boolean inShape = false;
264: PolyEdge lastEdge = null;
265: int numEdges = activeEdges.getNumActiveEdges();
266: for (int i = 0; i < numEdges; i++)
267: {
268: PolyEdge edge = activeEdges.getActiveEdge(i);
269: if (inClip && inShape)
270: {
271: assert lastEdge != null;
272: int x0 = lastEdge.xIntersection;
273: int x1 = edge.xIntersection;
274: assert x0 <= x1;
275:
276: int pix0 = Fixed.intValue(FIXED_DIGITS, x0);
277: int pix1 = Fixed.intValue(FIXED_DIGITS, x1);
278: int frac0 = ONE - Fixed.trunc(FIXED_DIGITS, x0);
279: int frac1 = ONE - Fixed.trunc(FIXED_DIGITS, x1);
280:
281: frac0 = frac0 >> (FIXED_DIGITS - Y_RESOLUTION);
282: frac1 = frac1 >> (FIXED_DIGITS - Y_RESOLUTION);
283: scanlineCoverage.add(pix0, 1 * (1 << Y_RESOLUTION), frac0);
284: scanlineCoverage.add(pix1, -1 * (1 << Y_RESOLUTION), -frac1);
285: }
286: if (edge.isClip)
287: inClip = ! inClip;
288: else
289: inShape = ! inShape;
290:
291: lastEdge = edge;
292: }
293:
294:
295: if (push && ! scanlineCoverage.isEmpty())
296: {
297: p.renderScanline(Fixed.intValue(FIXED_DIGITS, y), scanlineCoverage);
298: scanlineCoverage.clear();
299: }
300: }
301:
302:
303:
309: private void setResolution(int res)
310: {
311: int scanlinesPerPixel = 1 << res;
312: int one = Fixed.fixedValue(FIXED_DIGITS, 1);
313: resolution = one / (scanlinesPerPixel);
314: halfStep = resolution / 2;
315:
316: scanlineCoverage.setMaxCoverage(scanlinesPerPixel << Y_RESOLUTION);
317: }
318:
319:
324: private void setUpperBounds(int y0)
325: {
326: upperBounds = fit(y0);
327: }
328:
329:
335: private void addShape(PathIterator path, boolean clip)
336: {
337: int startX = 0;
338: int startY = 0;
339: int lastX = 0;
340: int lastY = 0;
341: while (! path.isDone())
342: {
343: int type = path.currentSegment(coords);
344: switch (type)
345: {
346: case PathIterator.SEG_MOVETO:
347: startX = lastX = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
348: startY = lastY = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
349: minY = Math.min(startY, minY);
350: maxY = Math.max(startY, maxY);
351: minX = Math.min(startX, minX);
352: maxX = Math.max(startX, maxX);
353: break;
354: case PathIterator.SEG_LINETO:
355: int x = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
356: int y = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
357: edgePoolAdd(lastX, lastY, x, y, clip);
358: lastX = x;
359: lastY = y;
360: minY = Math.min(lastY, minY);
361: maxY = Math.max(lastY, maxY);
362: minX = Math.min(lastX, minX);
363: maxX = Math.max(lastX, maxX);
364: break;
365: case PathIterator.SEG_CLOSE:
366: edgePoolAdd(lastX, lastY, startX, startY, clip);
367: lastX = startX;
368: lastY = startY;
369: break;
370: case PathIterator.SEG_CUBICTO:
371: case PathIterator.SEG_QUADTO:
372: default:
373: assert false;
374: }
375: path.next();
376: }
377: }
378:
379:
382: private void addEdge(PolyEdge edge)
383: {
384:
385: int upper = Math.min(edge.y0, edge.y1);
386:
387: int index = scanlineIndex(upper);
388:
389: if (index >= scanlines.length)
390: {
391: int oldSize = scanlines.length;
392: int newSize = Math.max(oldSize + oldSize / 2 + 1, index + 10);
393: Scanline[] newScanlines = new Scanline[newSize];
394: System.arraycopy(scanlines, 0, newScanlines, 0, oldSize);
395: scanlines = newScanlines;
396: }
397:
398:
399: if (scanlines[index] == null)
400: {
401: scanlines[index] = new Scanline();
402: }
403: scanlines[index].addEdge(edge);
404: }
405:
406:
413: private int fit(int y)
414: {
415: int val1 = Fixed.div(FIXED_DIGITS, y, resolution);
416: int rounded = Fixed.round(FIXED_DIGITS, val1);
417: return Fixed.mul(FIXED_DIGITS, rounded, resolution);
418: }
419:
420:
427: private int scanlineIndex(int y)
428: {
429: int fitted = fit(y);
430:
431: return (fitted - upperBounds)/ resolution;
432: }
433:
434: private void edgePoolAdd(int x0, int y0, int x1, int y1, boolean clip)
435: {
436:
437: if (y0 != y1)
438: {
439: edgePoolLast.init(FIXED_DIGITS, x0, y0, x1, y1, clip);
440: if (edgePoolLast.poolNext == null)
441: {
442: edgePoolLast.poolNext = new PolyEdge();
443: }
444: edgePoolLast = edgePoolLast.poolNext;
445: }
446: }
447: }