com.adobe.fontengine.font.TTScan Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* File: TTScan.java
*
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2006 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or
* copyright law. Dissemination of this information or reproduction of this
* material is strictly forbidden unless prior written permission is
* obtained from Adobe Systems Incorporated.
*
*/
/*
* Adobe Patent and/or Adobe Patent Pending invention included within this file:
*
* Adobe patent application tracking # P002,
* entitled "Dropout-free center point fill method for displaying characters"
* inventors: William Paxton and Stephen Schiller
* Issued US Patent 5,200,740 on April 6, 1993.
*/
/*
* Based on code with the following copyright notices:
*
* Copyright (c) 1987-1990, 1992 by Apple Computer, Inc., all rights reserved.
* Copyright (c) 1989-2002. Microsoft Corporation, all rights reserved.
*/
package com.adobe.fontengine.font;
import com.adobe.fontengine.math.F26Dot6;
import com.adobe.fontengine.math.Math2;
/** Scan conversion by center scan.
*
* This implentation follows very closely the Microsoft TrueType
* scan conversion process.
*/
public final class TTScan implements ScanConverter {
private int /*26.6*/ scanlineAbove (int /*26.6*/ y) {
return F26Dot6.roundHalfUp (y);
}
private int /*26.6*/ scanlineBelow (int /*26.6*/ y) {
return F26Dot6.roundHalfDown (y) - F26Dot6.ONE;
}
private static class Direction {
public final boolean isUp;
public final boolean isDown;
public final boolean isLeft;
public final boolean isRight;
public final String name;
private Direction (String name, boolean isUp, boolean isDown, boolean isLeft, boolean isRight) {
this.name = name;
this.isUp = isUp;
this.isDown = isDown;
this.isLeft = isLeft;
this.isRight = isRight;
}
public static Direction ofSegment (double x1, double y1, double x2, double y2) {
if (x1 < x2) {
if (y1 < y2) {
return NE; }
else if (y1 == y2) {
return E; }
else {
return SE; }}
else if (x1 == x2) {
if (y1 < y2) {
return N; }
else if (y1 == y2) {
return NO_MOVE; }
else {
return S; }}
else {
if (y1 < y2) {
return NW; }
else if (y1 == y2) {
return W; }
else {
return SW; }}
}
static final Direction E = new Direction ("E", false, false, false, true);
static final Direction NE = new Direction ("NE", true, false, false, true);
static final Direction N = new Direction ("N", true, false, false, false);
static final Direction NW = new Direction ("NW", true, false, true, false);
static final Direction W = new Direction ("W", false, false, true, false);
static final Direction SW = new Direction ("SW", false, true, true, false);
static final Direction S = new Direction ("S", false, true, false, false);
static final Direction SE = new Direction ("SE", false, true, false, true);
static final Direction NONE = new Direction ("NONE", false, false, false, false);
static final Direction NO_MOVE = new Direction ("NO_MOVE", false, false, false, false);
};
private static class F26Dot6List {
int /*26.6*/ [] values;
public void add (int /*26.6*/ v) {
if (values == null) {
values = new int /*26.6*/[] {v}; }
else {
int /*26.6*/[] newValues = new int /*26.6*/ [values.length + 1];
boolean inserted = false;
int k = 0;
for (int i = 0; i < values.length; i++) {
if (! inserted && v < values [i]) {
newValues [k++] = v;
inserted = true; }
newValues [k++] = values [i]; }
if (! inserted) {
newValues [k++] = v; }
values = newValues; }
}
}
private static class ScanLine {
F26Dot6List onCrosses = new F26Dot6List ();
F26Dot6List offCrosses = new F26Dot6List ();
public void addCross (int /*26.6*/ coord, boolean on) {
if (on) {
onCrosses.add (coord); }
else {
offCrosses.add (coord); }
}
}
private class ScanLines {
public int firstScanLineCoordI = Integer.MAX_VALUE;
public ScanLine[] scanLines = null;
public final boolean horizontal;
public ScanLines (boolean horizontal) {
this.horizontal = horizontal;
}
public void addCross (int /*26.6*/ coordOnScanLine, int scanLineCoordI, boolean on) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
if (horizontal) {
outlineDebugger.ttScanHCross (coordOnScanLine, scanLineCoordI, on); }
else {
outlineDebugger.ttScanVCross (coordOnScanLine, scanLineCoordI, on); }}
if (scanLines == null) {
scanLines = new ScanLine [1];
firstScanLineCoordI = scanLineCoordI; }
else if (scanLineCoordI < firstScanLineCoordI) {
ScanLine[] newScanLines = new ScanLine [firstScanLineCoordI - scanLineCoordI + scanLines.length];
System.arraycopy (scanLines, 0, newScanLines, firstScanLineCoordI - scanLineCoordI, scanLines.length);
scanLines = newScanLines;
firstScanLineCoordI = scanLineCoordI; }
else if (firstScanLineCoordI + scanLines.length - 1 < scanLineCoordI) {
ScanLine[] newScanLines = new ScanLine [scanLineCoordI - firstScanLineCoordI + 1];
System.arraycopy (scanLines, 0, newScanLines, 0, scanLines.length);
scanLines = newScanLines; }
ScanLine scanLine = scanLines [scanLineCoordI - firstScanLineCoordI];
if (scanLine == null) {
scanLine = new ScanLine ();
scanLines [scanLineCoordI - firstScanLineCoordI] = scanLine; }
scanLine.addCross (coordOnScanLine, on);
}
public int countCrosses (int scanLineCoord, int v) {
if (scanLineCoord < firstScanLineCoordI || firstScanLineCoordI + scanLines.length <= scanLineCoord) {
return 0; }
ScanLine r = scanLines [scanLineCoord - firstScanLineCoordI];
if (r == null) {
return 0; }
int crossings = 0;
int /*26.6*/ vv = F26Dot6.fromInt (v);
for (int i = 0; i < r.onCrosses.values.length; i++) {
if (vv - F26Dot6.ONE_HALF <= r.onCrosses.values [i] && r.onCrosses.values [i] < vv + F26Dot6.ONE_HALF) {
crossings++; }
if (vv - F26Dot6.ONE_HALF <= r.offCrosses.values [i] && r.offCrosses.values [i] < vv + F26Dot6.ONE_HALF) {
crossings++; }}
return crossings;
}
}
private ScanLines hScanLines;
private ScanLines vScanLines;
private int scanType = 2;
//============================================================================
private class CrossBuilder extends OutlineConsumer2BaseImpl {
int /*26.6*/ xmin;
int /*26.6*/ ymin;
int /*26.6*/ xmax;
int /*26.6*/ ymax;
boolean zeroDimension;
Direction currentDir;
Direction firstSegmentDir;
int /*26.6*/ firstSegmentX;
int /*26.6*/ firstSegmentY;
public CrossBuilder () {
}
//--------------------------------------------------------------------------
private double THRESHOLD = 127.0;
private double epsilon;
public void setFlatness (double flatness) {
epsilon = Math.max (1.0d/8192, 1.5d * flatness / 4);
}
public void startOutline () {
xmin = F26Dot6.MAX_VALUE;
ymin = F26Dot6.MAX_VALUE;
xmax = F26Dot6.MIN_VALUE;
ymax = F26Dot6.MIN_VALUE;
hScanLines = new ScanLines (true);
vScanLines = new ScanLines (false);
}
public void startContour () {
currentDir = Direction.NONE;
}
public void line (int /*26.6*/ x1, int /*26.6*/ y1, int /*26.6*/ x2, int /*26.6*/ y2) {
xmin = F26Dot6.min (x1, F26Dot6.min (x2, xmin));
ymin = F26Dot6.min (y1, F26Dot6.min (y2, ymin));
xmax = F26Dot6.max (x1, F26Dot6.max (x2, xmax));
ymax = F26Dot6.max (y1, F26Dot6.max (y2, ymax));
Direction thisSegmentDir = Direction.ofSegment (x1, y1, x2, y2);
if (thisSegmentDir == Direction.NO_MOVE) {
return; }
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLine (x1, y1, x2, y2); }
doLine (thisSegmentDir, x1, y1, x2, y2);
if (currentDir == Direction.NONE) {
firstSegmentX = x1;
firstSegmentY = y1;
firstSegmentDir = thisSegmentDir; }
else {
endPointH (currentDir, x1, y1, thisSegmentDir);
if (isFixing (scanType)) {
endPointV (currentDir, x1, y1, thisSegmentDir); }}
currentDir = thisSegmentDir; }
private void doLine (Direction thisSegmentDir, int /*26.6*/ x1, int /*26.6*/ y1, int /*26.6*/ x2, int /*26.6*/ y2) {
int quadrant;
long q;
int /*26.6*/ fxYScan;
int /*26.6*/ fxYInit;
int lYScan, lYSteps, lYIncr, lYOffset;
int /*26.6*/ fxYY2;
if (! thisSegmentDir.isDown) { /* if going up or flat */
quadrant = 1;
q = 0;
fxYScan = scanlineAbove (y1) + F26Dot6.ONE_HALF; /* first scanline to cross */
fxYInit = fxYScan - y1; /* first y step */
lYScan = F26Dot6.toInt (fxYScan);
lYSteps = F26Dot6.toInt (scanlineBelow (y2)) - lYScan + 1;
lYIncr = 1;
lYOffset = 0; /* no reflection */
fxYY2 = y2 - y1; } /* translate */
else { /* if going down */
quadrant = 4;
q = 1; /* to include pixel centers */
fxYScan = scanlineBelow (y1) + F26Dot6.ONE_HALF; /* first scanline to cross */
fxYInit = y1 - fxYScan; /* first y step */
lYScan = F26Dot6.toInt (fxYScan);
lYSteps = lYScan - F26Dot6.toInt (scanlineAbove (y2)) + 1;
lYIncr = -1;
lYOffset = 1; /* reflection correction */
fxYY2 = y1 - y2; } /* translate and reflect */
if (y2 == y1) { /* if horizontal line */
if (! isFixing (scanType)) { /* if no dropout control */
return; } /* if only horiz scan, done */
// correction on y1 to include pix centers
lYScan = F26Dot6.toInt (scanlineAbove (x2 < x1 ? (y1 - 1) : y1));
lYSteps = 0; }
/* check x coordinates */
int /*26.6*/ fxXScan;
int /*26.6*/ fxXInit;
int lXScan, lXSteps, lXIncr, lXOffset;
int /*26.6*/ fxXX2;
if (! thisSegmentDir.isLeft) { /* if going right or vertical */
fxXScan = scanlineAbove (x1) + F26Dot6.ONE_HALF; /* first scanline to cross */
fxXInit = fxXScan - x1; /* first x step */
lXScan = F26Dot6.toInt (fxXScan);
lXSteps = F26Dot6.toInt (scanlineBelow (x2)) - lXScan + 1;
lXIncr = 1;
lXOffset = 0; /* no reflection */
fxXX2 = x2 - x1; } /* translate */
else { /* if going left */
q = 1 - q; /* reverse it */
quadrant = (quadrant == 1) ? 2 : 3; /* negative x choices */
fxXScan = scanlineBelow (x1) + F26Dot6.ONE_HALF; /* first scanline to cross */
fxXInit = x1 - fxXScan; /* first x step */
lXScan = F26Dot6.toInt (fxXScan);
lXSteps = lXScan - F26Dot6.toInt (scanlineAbove (x2)) + 1;
lXIncr = -1;
lXOffset = 1; /* reflection correction */
fxXX2 = x1 - x2; } /* translate and reflect */
if (x2 == x1) { /* if vertical line */
// correction on x1 to include pix centers
lXScan = F26Dot6.toInt (scanlineAbove (y2 > y1 ? (x1 - 1) : x1));
lXSteps = 0; }
if (y1 == y2) { /* if horizontal line */
if (isFixing (scanType)) {
for (int i = 0; i < lXSteps; i++) {
vScanLines.addCross (F26Dot6.fromInt (lYScan), lXScan, thisSegmentDir.isLeft);
lXScan += lXIncr; }}} /* advance x scan + or - */
else if (x1 == x2) { /* if vertical line */
for (int i = 0; i < lYSteps; i++) { /* then blast a column */
hScanLines.addCross (F26Dot6.fromInt (lXScan), lYScan, thisSegmentDir.isUp);
lYScan += lYIncr; }} /* advance y scan + or - */
else { /* handle general case: line is neither horizontal nor vertical */
q += (((long) fxXX2) * fxYInit) - (((long) fxYY2) * fxXInit); /* cross product init */
long lDQy = fxXX2 << 6;
long lDQx = -fxYY2 << 6;
for (int i = 0; i < (lXSteps + lYSteps); i++) {
if (q > 0) { /* if left of line */
if (isFixing (scanType)) {
vScanLines.addCross (F26Dot6.fromInt (lYScan + lYOffset), lXScan, thisSegmentDir.isLeft); }
lXScan += lXIncr; /* advance x scan + or - */
q += lDQx; }
else { /* if right of line */
hScanLines.addCross (F26Dot6.fromInt (lXScan + lXOffset), lYScan, thisSegmentDir.isUp);
lYScan += lYIncr; /* advance y scan + or - */
q += lDQy; }}}
}
int depth = 0;
public void quadraticCurve (int /*26.6*/ x1, int /*26.6*/ y1,
int /*26.6*/ x2, int /*26.6*/ y2,
int /*26.6*/ x3, int /*26.6*/ y3) {
depth = 0;
doQuadraticCurve (x1, y1, x2, y2, x3, y3);
}
public void doQuadraticCurve (int /*26.6*/ x1, int /*26.6*/ y1,
int /*26.6*/ x2, int /*26.6*/ y2,
int /*26.6*/ x3, int /*26.6*/ y3) {
depth++;
if(depth >100) {
System.err.println ("dpeth!"); }
if (! ((y1 <= y2 && y2 <= y3) || (y3 <= y2 && y2 <= y1))) {
int /*26.6*/ num = (y2 - y1);
int /*26.6*/ denom = num - (y3 - y2);
int /*26.6*/ x4 = x1 + F26Dot6.multiplyDivide (x2 - x1, num, denom);
int /*26.6*/ x6 = x2 + F26Dot6.multiplyDivide ((x3 - x2), num , denom);
int /*26.6*/ x5 = x4 + F26Dot6.multiplyDivide ((x6 - x4), num , denom);
int /*26.6*/ y456 = y1 + F26Dot6.multiplyDivide ((y2 - y1), num , denom);
doQuadraticCurve (x1, y1, x4, y456, x5, y456);
doQuadraticCurve (x5, y456, x6, y456, x3, y3);
return; }
if (! ((x1 <= x2 && x2 <= x3) || (x3 <= x2 && x2 <= x1))) {
int /*26.6*/ num = (x2 - x1);
int /*26.6*/ denom = num - (x3 - x2);
int /*26.6*/ y4 = y1 + F26Dot6.multiplyDivide ((y2 - y1), num, denom);
int /*26.6*/ y6 = y2 + F26Dot6.multiplyDivide ((y3 - y2), num, denom);
int /*26.6*/ y5 = y4 + F26Dot6.multiplyDivide ((y6 - y4), num, denom);
int /*26.6*/ x456 = x1 + F26Dot6.multiplyDivide ((x2 - x1), num, denom);
doQuadraticCurve (x1, y1, x456, y4, x456, y5);
doQuadraticCurve (x456, y5, x456, y6, x3, y3);
return; }
/* if the curve is too large, break it in parts */
if ((Math.abs(x3 - x1) > 3200) || (Math.abs(y3 - y1) > 3200))
{
int /*26.6 */ fxX4 = (x1 + x2) >> 1; /* first segment mid point */
int /*26.6 */ fxY4 = (y1 + y2) >> 1;
int /*26.6 */ fxX6 = (x2 + x3) >> 1; /* second segment mid point */
int /*26.6 */ fxY6 = (y2 + y3) >> 1;
int /*26.6 */ fxX5 = (fxX4 + fxX6) >> 1; /* mid segment mid point */
int /*26.6 */ fxY5 = (fxY4 + fxY6) >> 1;
doQuadraticCurve(x1, y1, fxX4, fxY4, fxX5, fxY5);
doQuadraticCurve(fxX5, fxY5, fxX6, fxY6, x3, y3);
return;
}
Direction thisSegmentDir = Direction.ofSegment (x1, y1, x3, y3);
if (thisSegmentDir == Direction.NO_MOVE) {
return; }
xmin = F26Dot6.min (x1, F26Dot6.min (x3, xmin));
ymin = F26Dot6.min (y1, F26Dot6.min (y3, ymin));
xmax = F26Dot6.max (x1, F26Dot6.max (x3, xmax));
ymax = F26Dot6.max (y1, F26Dot6.max (y3, ymax));
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanQuadCurve (x1, y1, x2, y2, x3, y3); }
int /*26.6*/ yinit, yy2, yy3;
int /*26.6*/ xinit, xx2, xx3;
int q;
int i_xscan, i_xstop, xincr, xoffset;
int i_yscan, i_ystop, yincr, yoffset;
if (thisSegmentDir.isUp) {
q = 0;
int /*26.6*/ yscan = scanlineAbove (y1) + F26Dot6.ONE_HALF;
yinit = yscan - y1;
i_yscan = F26Dot6.toInt (yscan);
i_ystop = F26Dot6.toInt (scanlineBelow (y3) + F26Dot6.ONE_HALF) + 1;
yincr = 1;
yoffset = 0;
yy2 = y2 - y1;
yy3 = y3 - y1; }
else {
q = 1;
int /*26.6*/ yscan = scanlineBelow (y1) + F26Dot6.ONE_HALF;
yinit = y1 - yscan;
i_yscan = F26Dot6.toInt (yscan);
i_ystop = F26Dot6.toInt (scanlineAbove (y3) + F26Dot6.ONE_HALF) - 1;
yincr = -1;
yoffset = 1;
yy2 = y1 - y2;
yy3 = y1 - y3; }
if (thisSegmentDir.isRight) {
int /*26.6*/ xscan = scanlineAbove (x1)+ F26Dot6.ONE_HALF;
xinit = xscan - x1;
i_xscan = F26Dot6.toInt (xscan);
i_xstop = F26Dot6.toInt (scanlineBelow (x3) + F26Dot6.ONE_HALF) + 1;
xincr = 1;
xoffset = 0;
xx2 = x2 - x1;
xx3 = x3 - x1; }
else {
q = 1-q;
int /*26.6*/ xscan = scanlineBelow (x1)+ F26Dot6.ONE_HALF;
xinit = x1 - xscan;
i_xscan = F26Dot6.toInt (xscan);
i_xstop = F26Dot6.toInt (scanlineAbove (x3) + F26Dot6.ONE_HALF) - 1;
xincr = -1;
xoffset = 1;
xx2 = x1 - x2;
xx3 = x1 - x3; }
int /*20.12*/ alpha = 2 * (xx2 * yy3 - yy2 * xx3);
if (alpha == 0) {
line (x1, y1, x3, y3);
return; }
int aBits = Math2.powerOf2 (alpha);
int xyBits = Math2.powerOf2 (xx3 > yy3 ? xx3 : yy3);
int zShift = zShiftTable [aBits + xyBits];
int zBits = 6 /*of 26.6.*/ - zShift;
if (zShift > 0) {
int zRound = 1 << (zShift -1);
xx2 = (xx2 + zRound) >> zShift;
yy2 = (yy2 + zRound) >> zShift;
xx3 = (xx3 + zRound) >> zShift;
yy3 = (yy3 + zRound) >> zShift;
xinit = (xinit + zRound) >> zShift;
yinit = (yinit + zRound) >> zShift;
alpha = 2 * (xx2 * yy3 - yy2 * xx3); }
int /*26.6*/ ax = xx3 - xx2 * 2;
int /*26.6*/ ay = yy3 - yy2 * 2;
int /*20.12*/ r = ay * ay;
int /*20.12*/ s2 = - ax * ay;
int /*20.12*/ t = ax * ax;
int /*14.18*/ u2 = yy2 * alpha;
int /*14.18*/ v2 = - xx2 * alpha;
int zSubpix = 1 << zBits;
int dqx, dqy, ddqx, ddqy, rZ, sZ, tZ;
if (xyBits <= 7) {
q += (r * xinit + (s2 << 1) * yinit + (u2 << 1)) * xinit
+ (t * yinit + (v2 << 1)) * yinit; /*8.24*/
dqx = (r * ((xinit << 1) + zSubpix) + (s2 << 1) * yinit + (u2 << 1)) << zBits;
dqy = (t * ((yinit << 1) + zSubpix) + (s2 << 1) * xinit + (v2 << 1)) << zBits;
rZ = r << (zBits << 1);
sZ = (s2 << 1) << (zBits << 1);
tZ = t << (zBits << 1); }
else {
q += (((r >> 1) * xinit + s2 * yinit + u2) >> zBits) * xinit
+ (((t >> 1) * yinit + v2) >> zBits) * yinit;
dqx = r * (xinit + (zSubpix >> 1)) + s2 * yinit + u2;
dqy = t * (yinit + (zSubpix >> 1)) + s2 * xinit + v2;
rZ = r << (zBits - 1);
sZ = s2 << (zBits );
tZ = t << (zBits - 1); }
ddqx = 2 * rZ;
ddqy = 2 * tZ;
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" alpha=0x" + Integer.toHexString (alpha));
outlineDebugger.ttScanLog (" xscan=" + i_xscan + ", yscan=" + i_yscan
+ ", xoffset=" + xoffset + ", yoffset=" + yoffset
+ ", xinit=" + F26Dot6.toString (xinit)
+ ", yinit=" + F26Dot6.toString (yinit));
outlineDebugger.ttScanLog (" ax=0x" + Integer.toHexString(ax)
+ ", ay=0x" + Integer.toHexString(ay)
+ ", r=0x" + Integer.toHexString(r)
+ ", s2=0x" + Integer.toHexString(s2)
+ ", t=0x" + Integer.toHexString(t)
+ ", u2=0x" + Integer.toHexString(u2)
+ ", v2=0x" + Integer.toHexString(v2));
outlineDebugger.ttScanLog (" dqx=0x" + Integer.toHexString(dqx)
+ ", dqy=0x" + Integer.toHexString(dqy)
+ ", ddqx=0x" + Integer.toHexString(ddqx)
+ ", ddqy=0x" + Integer.toHexString(ddqy)
+ ", rZ=0x" + Integer.toHexString(rZ)
+ ", sZ=0x" + Integer.toHexString(sZ)
+ ", tZ=0x" + Integer.toHexString(tZ)); }
if (alpha > 0) {
while (i_xscan != i_xstop && i_yscan != i_ystop) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" q=0x" + Integer.toHexString (q)
+ ", dqx=0x" + Integer.toHexString (dqx)
+ ", dqy=0x" + Integer.toHexString (dqy)
+ ", ddqx=0x" + Integer.toHexString (ddqx)
+ ", ddqy=0x" + Integer.toHexString (ddqy)); }
if (q < 0 || dqy > tZ) {
if (isFixing (scanType)) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" vCross " + F26Dot6.toString (getY (F26Dot6.fromInt (i_xscan) + F26Dot6.ONE_HALF, x1, y1, x2, y2, x3, y3))); }
vScanLines.addCross (F26Dot6.fromInt (i_yscan + yoffset), i_xscan, thisSegmentDir.isLeft); }
i_xscan += xincr;
q += dqx;
dqx += ddqx;
dqy += sZ; }
else {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" hCross " + F26Dot6.toString (getX (F26Dot6.fromInt (i_yscan) + F26Dot6.ONE_HALF, x1, y1, x2, y2, x3, y3))); }
hScanLines.addCross (F26Dot6.fromInt (i_xscan + xoffset), i_yscan, thisSegmentDir.isUp);
i_yscan += yincr;
q += dqy;
dqy += ddqy;
dqx += sZ; }}}
else {
while (i_xscan != i_xstop && i_yscan != i_ystop) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" q=0x" + Integer.toHexString (q)
+ ", dqx=0x" + Integer.toHexString (dqx)
+ ", dqy=0x" + Integer.toHexString (dqy)
+ ", ddqx=0x" + Integer.toHexString (ddqx)
+ ", ddqy=0x" + Integer.toHexString (ddqy)); }
if (q < 0 || dqx > rZ) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" hCross " + F26Dot6.toString (getX (F26Dot6.fromInt (i_yscan) + F26Dot6.ONE_HALF, x1, y1, x2, y2, x3, y3))); }
hScanLines.addCross (F26Dot6.fromInt (i_xscan + xoffset), i_yscan, thisSegmentDir.isUp);
i_yscan += yincr;
q += dqy;
dqy += ddqy;
dqx += sZ; }
else {
if (isFixing (scanType)) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" vCross " + F26Dot6.toString (getY (F26Dot6.fromInt (i_xscan) + F26Dot6.ONE_HALF, x1, y1, x2, y2, x3, y3))); }
vScanLines.addCross (F26Dot6.fromInt (i_yscan + yoffset), i_xscan, thisSegmentDir.isLeft); }
i_xscan += xincr;
q += dqx;
dqx += ddqx;
dqy += sZ; }}}
while (i_xscan != i_xstop) {
if (isFixing (scanType)) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" vCross " + F26Dot6.toString (getY (F26Dot6.fromInt (i_xscan) + F26Dot6.ONE_HALF, x1, y1, x2, y2, x3, y3))); }
vScanLines.addCross (F26Dot6.fromInt (i_yscan + yoffset), i_xscan, thisSegmentDir.isLeft); }
i_xscan += xincr; }
while (i_yscan != i_ystop) {
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanLog (" hCross " + F26Dot6.toString (getX (F26Dot6.fromInt (i_yscan) + F26Dot6.ONE_HALF, x1, y1, x2, y2, x3, y3))); }
hScanLines.addCross (F26Dot6.fromInt (i_xscan + xoffset), i_yscan, thisSegmentDir.isUp);
i_yscan += yincr; }
if (currentDir == Direction.NONE) {
firstSegmentX = x1;
firstSegmentY = y1;
firstSegmentDir = thisSegmentDir; }
else {
endPointH (currentDir, x1, y1, thisSegmentDir);
if (isFixing (scanType)) {
endPointV (currentDir, x1, y1, thisSegmentDir); }}
currentDir = thisSegmentDir;
}
public void cubicCurve (double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
if ( ( (x1 <= x2 && x2 <= x3 && x3 <= x4)
|| (x4 <= x3 && x3 <= x2 && x2 <= x1))
&& (Math.abs (x4 - x1) < THRESHOLD)
&& (Math.abs ((x4 - x1) - 3 * (x2 - x1)) <= epsilon)
&& (Math.abs ((x4 - x1) - 3 * (x4 - x3)) <= epsilon)
&& ( (y1 <= y2 && y2 <= y3 && y3 <= y4)
|| (y4 <= y3 && y3 <= y2 && y2 <= y1))
&& (Math.abs (y4 - y1) < THRESHOLD)
&& (Math.abs ((y4 - y1) - 3 * (y2 - y1)) <= epsilon)
&& (Math.abs ((y4 - y1) - 3 * (y4 - y3)) <= epsilon)) {
line (x1, y1, x4, y4); }
else {
double x12 = (x2 + x1) / 2.0; double y12 = (y2 + y1) / 2.0;
double x23 = (x3 + x2) / 2.0; double y23 = (y3 + y2) / 2.0;
double x34 = (x4 + x3) / 2.0; double y34 = (y4 + y3) / 2.0;
double x1223 = (x23 + x12) / 2.0; double y1223 = (y23 + y12) / 2.0;
double x2334 = (x34 + x23) / 2.0; double y2334 = (y34 + y23) / 2.0;
double xx = (x1223 + x2334) / 2.0; double yy = (y1223 + y2334) / 2.0;
cubicCurve (x1, y1, x12, y12, x1223, y1223, xx, yy);
cubicCurve (xx, yy, x2334, y2334, x34, y34, x4, y4); }
}
public void endContour () {
if (currentDir != Direction.NONE) {
endPointH (currentDir, firstSegmentX, firstSegmentY, firstSegmentDir);
if (isFixing (scanType)) {
endPointV (currentDir, firstSegmentX, firstSegmentY, firstSegmentDir); }
currentDir = Direction.NONE; }
}
public void endOutline () {
xmin = F26Dot6.roundHalfDown (xmin);
ymin = F26Dot6.roundHalfDown (ymin);
xmax = F26Dot6.roundHalfUp (xmax);
ymax = F26Dot6.roundHalfUp (ymax);
zeroDimension = (xmin == xmax) || (ymin == ymax);
}
void endPointH (Direction beforeDir, int /*26.6*/ x, int /*26.6*/ y, Direction afterDir) {
int yI = F26Dot6.toInt (F26Dot6.floor (y));
if (F26Dot6.fromInt (yI) + F26Dot6.ONE_HALF != y) {
return; }
if (afterDir.isUp) {
if (beforeDir.isUp) {
hScanLines.addCross (x, yI, true); }
else if (beforeDir.isDown) {
hScanLines.addCross (x, yI, true);
hScanLines.addCross (x, yI, false); }
else {
if (beforeDir.isLeft) {
hScanLines.addCross (x, yI, true); }}}
else if (afterDir.isDown) {
if (beforeDir.isUp) {
hScanLines.addCross (x, yI, true);
hScanLines.addCross (x, yI, false); }
else if (beforeDir.isDown) {
hScanLines.addCross (x, yI, false); }
else {
if (beforeDir.isRight) {
hScanLines.addCross (x, yI, false); }}}
else {
if (beforeDir.isUp) {
if (afterDir.isRight) {
hScanLines.addCross (x, yI, true); }}
else if (beforeDir.isDown) {
if (afterDir.isLeft) {
hScanLines.addCross (x, yI, false); }}
else {
if (beforeDir.isRight && afterDir.isLeft) {
hScanLines.addCross (x, yI, false); }
else if (beforeDir.isLeft && afterDir.isRight) {
hScanLines.addCross (x, yI, true); }}}
}
void endPointV (Direction beforeDir, int /*26.6*/ x, int /*26.6*/ y, Direction afterDir) {
int xI = F26Dot6.toInt (F26Dot6.floor (x));
if (F26Dot6.fromInt (xI) + F26Dot6.ONE_HALF != x) {
return; }
if (afterDir.isLeft) {
if (beforeDir.isLeft) {
vScanLines.addCross (y, xI, true); }
else if (beforeDir.isRight) {
vScanLines.addCross (y, xI, true);
vScanLines.addCross (y, xI, false); }
else {
if (beforeDir.isDown) {
vScanLines.addCross (y, xI, true); }}}
else if (afterDir.isRight) {
if (beforeDir.isLeft) {
vScanLines.addCross (y, xI, true);
vScanLines.addCross (y, xI, false); }
else if (beforeDir.isRight) {
vScanLines.addCross (y, xI, false); }
else {
if (beforeDir.isUp) {
vScanLines.addCross (y, xI, false); }}}
else {
if (beforeDir.isLeft) {
if (afterDir.isUp) {
vScanLines.addCross (y, xI, true); }}
else if (beforeDir.isRight) {
if (afterDir.isDown) {
vScanLines.addCross (y, xI, false); }}
else {
if (beforeDir.isUp && afterDir.isDown) {
vScanLines.addCross (y, xI, false); }
else if (beforeDir.isDown && afterDir.isUp) {
vScanLines.addCross (y, xI, true); }}}
}
final int zShiftTable [] = { /* for precision adjustment */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 19 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
1, 1, 1, 2, 2, 2, 3, 3 /* 30 - 34 */
};
private int /*26.6*/ getY (int x, int /*26.6*/ x1, int /*26.6*/ y1, int /*26.6*/ x2, int /*26.6*/ y2, int /*26.6*/ x3, int /*26.6*/ y3) {
return 0;
// int xmid = (x1 + 2*x2 + x3 + 1) / 4;
// int ymid = (y1 + 2*y2 + y3 + 1) / 4;
// if (x == xmid) {
// return ymid; }
// else if ((x < xmid && x1 <= x2 && x2 <= x3) || (xmid < x && x3 <= x2 && x2 <= x1)) {
// return getY (x, x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, xmid, ymid); }
// else {
// return getY (x, xmid, ymid, (x2 + x3) / 2, (y2 + y3) / 2, x3, y3); }
}
private int /*26.6*/ getX (int y, int /*26.6*/ x1, int /*26.6*/ y1, int /*26.6*/ x2, int /*26.6*/ y2, int /*26.6*/ x3, int /*26.6*/ y3) {
return 0;
// int xmid = (x1 + 2*x2 + x3 + 1) / 4;
// int ymid = (y1 + 2*y2 + y3 + 1) / 4;
// if (y == ymid) {
// return xmid; }
// else if ((y < ymid && y1 <= y2 && y2 <= y3) || (ymid < y && y3 <= y2 && y2 <= y1)) {
// return getX (y, x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, xmid, ymid); }
// else {
// return getX (y, xmid, ymid, (x2 + x3) / 2, (y2 + y3) / 2, x3, y3); }
}
}
//--------------------------------------------------------------------------
private CrossBuilder crossBuilder;
public TTScan () {
crossBuilder = new CrossBuilder ();
crossBuilder.setFlatness (1.0d);
}
public void setScanType (int scanType) {
this.scanType = scanType;
if (ScalerDebugger.debugOn && outlineDebugger != null) {
outlineDebugger.ttScanScanType (scanType); }
}
public OutlineConsumer2 getOutlineConsumer2 () {
return crossBuilder;
}
public void getBitmap (BitmapConsumer dest) {
if (crossBuilder.zeroDimension) {
scanType = scanType & ~ 1; }
if (! checkCrosses ()) {
return; }
Bitmap bitmap = new Bitmap (F26Dot6.toInt (crossBuilder.xmin),
F26Dot6.toInt (crossBuilder.xmax),
F26Dot6.toInt (crossBuilder.ymin),
F26Dot6.toInt (crossBuilder.ymax));
buildInitialRuns (bitmap);
if (isFixing (scanType)) {
fixHDropouts (bitmap);
fixVDropouts (bitmap); }
bitmap.emitBitmap (dest);
}
private boolean checkCrosses () {
if (hScanLines.scanLines != null) {
for (int s = 0; s < hScanLines.scanLines.length; s++) {
ScanLine scanLine = hScanLines.scanLines [s];
if (scanLine != null
&& (scanLine.onCrosses == null || scanLine.offCrosses == null
|| scanLine.onCrosses.values == null || scanLine.offCrosses.values == null
|| scanLine.onCrosses.values.length != scanLine.offCrosses.values.length)) {
System.err.println ("****************** h crosses not paired!");
return false; }}}
if (vScanLines.scanLines != null) {
for (int s = 0; s < vScanLines.scanLines.length; s++) {
ScanLine scanLine = vScanLines.scanLines [s];
if (scanLine != null
&& (scanLine.onCrosses == null || scanLine.offCrosses == null
|| scanLine.onCrosses.values == null || scanLine.offCrosses.values == null
|| scanLine.onCrosses.values.length != scanLine.offCrosses.values.length)) {
System.err.println ("****************** v crosses not paired!");
return false; }}}
return true;
}
private void buildInitialRuns (Bitmap bitmap) {
if (hScanLines.scanLines == null) {
return; }
for (int s = 0; s < hScanLines.scanLines.length; s++) {
ScanLine scanLine = hScanLines.scanLines [s];
if (scanLine == null) {
continue; }
int y = hScanLines.firstScanLineCoordI + s;
for (int i = 0; i < scanLine.onCrosses.values.length; i++) {
int /*26.6*/ x1 = scanLine.onCrosses.values [i];
int /*26.6*/ x2 = scanLine.offCrosses.values [i];
if (x1 > x2) {
int /*26.6*/ tmp = x1; x1 = x2; x2 = tmp; }
bitmap.paintBlack (F26Dot6.toInt (F26Dot6.roundHalfDown (x1)),
F26Dot6.toInt (F26Dot6.roundHalfUp (x2)),
y); }}
}
private void fixHDropouts (Bitmap bitmap) {
if (hScanLines.scanLines == null) {
return; }
for (int s = 0; s < hScanLines.scanLines.length; s++) {
ScanLine scanLine = hScanLines.scanLines [s];
if (scanLine == null) {
continue; }
int y = hScanLines.firstScanLineCoordI + s;
for (int i = 0; i < scanLine.onCrosses.values.length; i++) {
int /*26.6*/ x1 = scanLine.onCrosses.values [i];
int /*26.6*/ x2 = scanLine.offCrosses.values [i];
if (x1 > x2) {
int /*26.6*/ tmp = x1; x1 = x2; x2 = tmp; }
int x = F26Dot6.toInt (scanlineAbove (x1));
if (x <= F26Dot6.toInt (scanlineBelow (x2))) {
continue; }
if (bitmap.isBlack (x - 1, y)
|| bitmap.isBlack (x, y)) {
continue; }
if (isExcludingStubs (scanType)) {
int crossings = 0; // above
crossings += vScanLines.countCrosses (x - 1, y + 1);
crossings += hScanLines.countCrosses (y + 1, x);
crossings += vScanLines.countCrosses (x, y + 1);
if (crossings < 2) {
continue; }
crossings = 0; // below
crossings += vScanLines.countCrosses (x - 1, y);
crossings += hScanLines.countCrosses (y - 1, x);
crossings += vScanLines.countCrosses (x, y);
if (crossings < 2) {
continue; }}
int pixel;
if (isSmart (scanType)) {
pixel = F26Dot6.toInt (F26Dot6.floor ((scanLine.onCrosses.values [i] + scanLine.offCrosses.values [i]) / 2 )); }
else {
pixel = x - 1; }
if (pixel < F26Dot6.toInt (crossBuilder.xmin)) {
pixel++; }
if (F26Dot6.toInt (crossBuilder.xmax) < pixel) {
pixel--; }
bitmap.paintBlack (pixel, y); }}
}
private void fixVDropouts (Bitmap bitmap) {
if (vScanLines.scanLines == null) {
return; }
for (int s = 0; s < vScanLines.scanLines.length; s++) {
ScanLine scanLine = vScanLines.scanLines [s];
if (scanLine == null) {
continue; }
int x = vScanLines.firstScanLineCoordI + s;
for (int i = scanLine.onCrosses.values.length - 1; i >= 0; i--) {
int /*26.6*/ y1 = scanLine.onCrosses.values [i];
int /*26.6*/ y2 = scanLine.offCrosses.values [i];
if (y1 > y2) {
int /*26.6*/ tmp = y1; y1 = y2; y2 = tmp; }
int y = F26Dot6.toInt (scanlineAbove (y1));
if (y <= F26Dot6.toInt (scanlineBelow (y2))) {
continue; }
if (bitmap.isBlack (x, y - 1)
|| bitmap.isBlack (x, y)) {
continue; }
if (isExcludingStubs (scanType)) {
int crossings = 0; // left
crossings += hScanLines.countCrosses (y - 1, x);
crossings += vScanLines.countCrosses (x - 1, y);
crossings += hScanLines.countCrosses (y, x);
if (crossings < 2) {
continue; }
crossings = 0; // right
crossings += hScanLines.countCrosses (y - 1, x + 1);
crossings += vScanLines.countCrosses (x + 1, y);
crossings += hScanLines.countCrosses (y, x + 1);
if (crossings < 2) {
continue; }}
int pixel;
if (isSmart (scanType)) {
pixel = F26Dot6.toInt (F26Dot6.floor ((scanLine.onCrosses.values [i] + scanLine.offCrosses.values [i]) / 2 )); }
else {
pixel = y - 1; }
if (pixel < F26Dot6.toInt (crossBuilder.ymin)) {
pixel++; }
if (F26Dot6.toInt (crossBuilder.ymax) < pixel) {
pixel--; }
bitmap.paintBlack (x, pixel); }}
}
//----------------------------------------------------------------------------
private boolean isFixing (int scanType) {
return scanType == 0 || scanType == 1 || scanType == 4 || scanType == 5;
}
private boolean isSmart (int scanType) {
return scanType == 4 || scanType == 5;
}
private boolean isExcludingStubs (int scanType) {
return scanType == 1 || scanType == 5;
}
//----------------------------------------------------------------------------
private static class Bitmap {
boolean[][] pixels;
int blX;
int blY;
int width;
public Bitmap (int xmin, int xmax, int ymin, int ymax) {
if (xmin == Integer.MAX_VALUE) {
pixels = null;
return; }
blX = xmin;
blY = ymin;
pixels = new boolean [xmax - xmin + 1][];
width = ymax - ymin + 1;
for (int x = 0; x < pixels.length; x++) {
pixels [x] = new boolean [width]; }
}
public boolean isBlack (int x, int y) {
int iX = x - blX;
int iY = y - blY;
if (iX < 0 || pixels.length <= iX || iY < 0 || width <= iY) {
return false; }
return pixels [iX] [iY];
}
public void paintBlack (int x, int y) {
pixels [x-blX] [y-blY] = true;
}
protected void paintBlack (int xOn, int xOff, int y) {
for (int x = xOn; x < xOff; x++) {
paintBlack (x, y); }
}
protected void emitBitmap (BitmapConsumer bitmapConsumer) {
if (pixels == null) {
return; }
for (int x = 0; x < pixels.length; x++) {
for (int y = 0; y < width; y++) {
if (pixels [x] [y]) {
bitmapConsumer.addRun (blX + x, blX + x + 1, blY + y); }}}
}
}
//----------------------------------------------------------------------------
private ScalerDebugger outlineDebugger;
public void setDebugger (ScalerDebugger outlineDebugger) {
this.outlineDebugger = outlineDebugger;
}
}