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 int numScanlines;
  68: 
  69:   
  75:   private Scanline[] scanlines;
  76: 
  77:   
  82:   private int upperBounds;
  83: 
  84:   
  89:   private int resolution;
  90: 
  91:   
  94:   private int yResolution;
  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, int yRes,
 149:                           RenderingHints hints)
 150:   {
 151:     
 152:     
 153: 
 154:     
 155:     clear();
 156:     setResolution(res, yRes);
 157: 
 158:     boolean haveClip = clip != null;
 159: 
 160:     
 161:     float flatness = Fixed.floatValue(FIXED_DIGITS, resolution / 2);
 162:     PathIterator path = shape.getPathIterator(trans, flatness);
 163:     addShape(path, false);
 164:     if (haveClip)
 165:       {
 166:         path= clip.getPathIterator(trans, flatness);
 167:         addShape(path, true);
 168:       }
 169: 
 170:     setUpperBounds(minY);
 171: 
 172:     PolyEdge edge = edgePool;
 173:     while (edge != edgePoolLast)
 174:       {
 175:         addEdge(edge);
 176:         edge = edge.poolNext;
 177:       }
 178: 
 179:     int y = upperBounds;
 180:     int index;
 181:     activeEdges.clear();
 182:     
 183:     Scanline scanline = null;
 184:     int lastRealY = Fixed.intValue(FIXED_DIGITS, y);
 185:     while (y <= maxY)
 186:       {
 187:         
 188:         index = scanlineIndex(y);
 189:         
 190:         
 191:         scanline = index < scanlines.length ? scanlines[index] : null;
 192:         if (scanline != null)
 193:           {
 194:             edge = scanline.getEdges();
 195:             while (edge != null)
 196:               {
 197:                 activeEdges.add(edge);
 198:                 edge = edge.scanlineNext;
 199:               }
 200:           }
 201: 
 202:         
 203:         
 204:         activeEdges.intersectSortAndPack(FIXED_DIGITS, y + halfStep);
 205: 
 206:         
 207:         int realY = Fixed.intValue(FIXED_DIGITS, y + resolution);
 208:         boolean push = lastRealY != realY;
 209: 
 210:         doScanline(p, y, push, haveClip);
 211: 
 212:         
 213:         
 214:         
 215:         y += resolution;
 216:         lastRealY = realY;
 217: 
 218:       }
 219:   }
 220: 
 221:   
 224:   private void clear()
 225:   {
 226:     
 227:     edgePoolLast = edgePool;
 228: 
 229:     
 230:     for (int i = scanlines.length - 1; i >= 0 ; i--)
 231:       {
 232:         Scanline sl = scanlines[i];
 233:         if (sl != null)
 234:           sl.clear();
 235:       }
 236: 
 237:     
 238:     scanlineCoverage.clear();
 239: 
 240:     
 241:     minY = Integer.MAX_VALUE;
 242:     maxY = Integer.MIN_VALUE;
 243:     minX = Integer.MAX_VALUE;
 244:     maxX = Integer.MIN_VALUE;
 245:   }
 246: 
 247:   
 256:   private void doScanline(Pixelizer p, int y, boolean push,
 257:                           boolean haveClip)
 258:   {
 259:     
 260:     scanlineCoverage.rewind();
 261: 
 262:     
 263:     
 264:     boolean inClip = ! haveClip;
 265:     boolean inShape = false;
 266:     PolyEdge lastEdge = null;
 267:     int numEdges = activeEdges.getNumActiveEdges();
 268:     for (int i = 0; i < numEdges; i++)
 269:       {
 270:         PolyEdge edge = activeEdges.getActiveEdge(i);
 271:         if (inClip && inShape)
 272:           {
 273:             assert lastEdge != null;
 274:             int x0 = lastEdge.xIntersection;
 275:             int x1 = edge.xIntersection;
 276:             assert x0 <= x1;
 277: 
 278:             int pix0 = Fixed.intValue(FIXED_DIGITS, x0);
 279:             int pix1 = Fixed.intValue(FIXED_DIGITS, x1);
 280:             int frac0 = ONE - Fixed.trunc(FIXED_DIGITS, x0);
 281:             int frac1 = ONE - Fixed.trunc(FIXED_DIGITS, x1);
 282:             
 283:             frac0 = frac0 >> (FIXED_DIGITS - yResolution);
 284:             frac1 = frac1 >> (FIXED_DIGITS - yResolution);
 285:             scanlineCoverage.add(pix0, 1 * (1 << yResolution), frac0);
 286:             scanlineCoverage.add(pix1, -1 * (1 << yResolution), -frac1);
 287:           }
 288:         if (edge.isClip)
 289:           inClip = ! inClip;
 290:         else
 291:           inShape = ! inShape;
 292: 
 293:         lastEdge = edge;
 294:       }
 295: 
 296:     
 297:     if (push && ! scanlineCoverage.isEmpty())
 298:       {
 299:         p.renderScanline(Fixed.intValue(FIXED_DIGITS, y), scanlineCoverage);
 300:         scanlineCoverage.clear();
 301:       }
 302:   }
 303: 
 304: 
 305:   
 311:   private void setResolution(int res, int yRes)
 312:   {
 313:     int scanlinesPerPixel = 1 << res;
 314:     int one = Fixed.fixedValue(FIXED_DIGITS, 1);
 315:     resolution = one / (scanlinesPerPixel);
 316:     halfStep = resolution / 2;
 317: 
 318:     scanlineCoverage.setMaxCoverage(scanlinesPerPixel << yResolution);
 319: 
 320:     yResolution = yRes;
 321:   }
 322: 
 323:   
 328:   private void setUpperBounds(int y0)
 329:   {
 330:     upperBounds = fit(y0);
 331:   }
 332: 
 333:   
 339:   private void addShape(PathIterator path, boolean clip)
 340:   {
 341:     int startX = 0;
 342:     int startY = 0;
 343:     int lastX = 0;
 344:     int lastY = 0;
 345:     while (! path.isDone())
 346:       {
 347:         int type = path.currentSegment(coords);
 348:         switch (type)
 349:           {
 350:             case PathIterator.SEG_MOVETO:
 351:               startX = lastX = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
 352:               startY = lastY = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
 353:               minY = Math.min(startY, minY);
 354:               maxY = Math.max(startY, maxY);
 355:               minX = Math.min(startX, minX);
 356:               maxX = Math.max(startX, maxX);
 357:               break;
 358:             case PathIterator.SEG_LINETO:
 359:               int x = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
 360:               int y = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
 361:               edgePoolAdd(lastX, lastY, x, y, clip);
 362:               lastX = x;
 363:               lastY = y;
 364:               minY = Math.min(lastY, minY);
 365:               maxY = Math.max(lastY, maxY);
 366:               minX = Math.min(lastX, minX);
 367:               maxX = Math.max(lastX, maxX);
 368:               break;
 369:             case PathIterator.SEG_CLOSE:
 370:               edgePoolAdd(lastX, lastY, startX, startY, clip);
 371:               lastX = startX;
 372:               lastY = startY;
 373:               break;
 374:             case PathIterator.SEG_CUBICTO:
 375:             case PathIterator.SEG_QUADTO:
 376:             default:
 377:               assert false;
 378:           }
 379:         path.next();
 380:       }
 381:   }
 382: 
 383:   
 386:   private void addEdge(PolyEdge edge)
 387:   {
 388:     
 389:     int upper = Math.min(edge.y0, edge.y1);
 390:     
 391:     int index = scanlineIndex(upper);
 392:     
 393:     if (index >= scanlines.length)
 394:       {
 395:         int oldSize = scanlines.length;
 396:         int newSize = Math.max(oldSize + oldSize / 2 + 1, index + 10);
 397:         Scanline[] newScanlines = new Scanline[newSize];
 398:         System.arraycopy(scanlines, 0, newScanlines, 0, oldSize);
 399:         scanlines = newScanlines;
 400:       }
 401: 
 402:     
 403:     if (scanlines[index] == null)
 404:       {
 405:         scanlines[index] = new Scanline();
 406:       }
 407:     scanlines[index].addEdge(edge);
 408:   }
 409: 
 410:   
 417:   private int fit(int y)
 418:   {
 419:     int val1 = Fixed.div(FIXED_DIGITS, y, resolution);
 420:     int rounded = Fixed.round(FIXED_DIGITS, val1);
 421:     return Fixed.mul(FIXED_DIGITS, rounded, resolution);
 422:   }
 423: 
 424:   
 431:   private int scanlineIndex(int y)
 432:   {
 433:     int fitted = fit(y);
 434:     
 435:     return (fitted - upperBounds)/ resolution;
 436:   }
 437: 
 438:   private void edgePoolAdd(int x0, int y0, int x1, int y1, boolean clip)
 439:   {
 440:     
 441:     if (y0 != y1)
 442:       {
 443:         edgePoolLast.init(FIXED_DIGITS, x0, y0, x1, y1, clip);
 444:         if (edgePoolLast.poolNext == null)
 445:           {
 446:             edgePoolLast.poolNext = new PolyEdge();
 447:           }
 448:         edgePoolLast = edgePoolLast.poolNext;
 449:       }
 450:   }
 451: }