com.adobe.fontengine.font.QReducer 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: QReducer.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.
*
*/
package com.adobe.fontengine.font;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/** Scan conversion by overscanning.
*
* This scan converter turns a pixel on as soon as the outline
* traverses that pixel.
*/
public class QReducer implements ScanConverter {
EdgeBuilder edgeBuilder;
public QReducer () {
edgeBuilder = new EdgeBuilder ();
edgeBuilder.setFlatness (1.0d);
}
public void setScanType (int scanType) {
// nothing
}
public OutlineConsumer2 getOutlineConsumer2 () {
return edgeBuilder;
}
public void getBitmap (BitmapConsumer bitmapConsumer) {
finish (bitmapConsumer);
}
private class EdgeBuilder extends OutlineConsumer2BaseImpl {
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 setScanType (int scanType) {
// we don't care for that
}
public void startOutline () {
}
public void startContour () {
}
public void line (double x1, double y1, double x2, double y2) {
if (y1 != y2 || ! isIntegral (y1)) {
insertEdge (new Edge (x1, y1, x2, y2)); }
}
public void quadraticCurve (double x1, double y1, double x2, double y2, double x3, double y3) {
if ( ( (x1 <= x2 && x2 <= x3)
|| (x3 <= x2 && x2 <= x1))
&& (Math.abs (x3 - x1) < THRESHOLD)
&& (Math.abs ((x3 - x1) - 2 * (x2 - x1)) <= epsilon)
&& ( (y1 <= y2 && y2 <= y3)
|| (y3 <= y2 && y2 <= y1))
&& (Math.abs (y3 - y1) < THRESHOLD)
&& (Math.abs ((y3 - y1) - 2 * (y2 - y1)) <= epsilon)) {
line (x1, y1, x3, y3); }
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 x1223 = (x12 + x23) / 2.0; double y1223 = (y12 + y23) / 2.0;
quadraticCurve (x1, y1, x12, y12, x1223, y1223);
quadraticCurve (x1223, y1223, x23, y23, x3, y3); }
}
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 () {
}
public void endOutline () {
}
}
private static class Edge implements Comparable {
final private boolean reversed; // the original line segment goes from smallest y to largest y
final private boolean endsAtHorzEdge; // largest y is exactly at pixel boundary
final private boolean startsAtHorzEdge; // smallest y is exactly at pixel boundary
final private static int LEFT = -1;
final private static int SINGLE_VERTICAL = 0;
final private static int RIGHT = 1;
final private static int SINGLE_HORIZONTAL = 2;
final private int lineType;
final private double dx, dy;
final private int endPixelX;
private double g;
final public int bottomScanLine;
final public int topScanLine;
final public boolean atVertEdge; // vertical, and on pixel boundary
public int leftPixelInCurrentScanLine, rightPixelInCurrentScanline;
public int getCntDelta (int currentScanline) {
// Returns:
// 2 if the outline crosses the bottom of the scanline and the
// top
// 1 if it crosses the bottom or the top only
// 0 if it is entirely in the scanline
// the sign is positive if the outline goes up, negative otherwise
int cntDelta = 2;
if (bottomScanLine == currentScanline && ! startsAtHorzEdge) {
cntDelta--; }
if (topScanLine == currentScanline && ! endsAtHorzEdge) {
cntDelta--; }
if (! reversed) {
cntDelta = - cntDelta; }
return cntDelta;
}
public int pixel (double v) {
int p = (int) Math.floor (v);
if (p == v) {
p--; }
return p;
}
public Edge (double x1, double y1, double x2, double y2) {
reversed = y1 > y2;
if (reversed) {
double tmp = y1; y1 = y2; y2 = tmp;
tmp = x1; x1 = x2; x2 = tmp; }
dx = x2 - x1;
dy = y2 - y1;
int startPixelX;
if (dx < 0.0d) {
atVertEdge = false;
startPixelX = pixel (x1);
endPixelX = (int) Math.floor (x2); }
else if (dx == 0.0d) {
atVertEdge = isIntegral (x1);
startPixelX = pixel (x1);
endPixelX = startPixelX; }
else { /* dx > 0 */
atVertEdge = false;
endPixelX = pixel (x2);
startPixelX = (int) Math.floor (x1); }
bottomScanLine = (int) Math.floor (y1);
startsAtHorzEdge = (isIntegral (y1));
topScanLine = pixel (y2);
endsAtHorzEdge = (isIntegral (y2));
if (bottomScanLine == topScanLine) {
lineType = SINGLE_HORIZONTAL;
leftPixelInCurrentScanLine = Math.min (startPixelX, endPixelX);
rightPixelInCurrentScanline = Math.max (startPixelX, endPixelX); }
else if (startPixelX == endPixelX) {
lineType = SINGLE_VERTICAL;
leftPixelInCurrentScanLine = startPixelX;
rightPixelInCurrentScanline = startPixelX; }
else if (startPixelX < endPixelX) {
lineType = RIGHT;
rightPixelInCurrentScanline = startPixelX;
leftPixelInCurrentScanLine = startPixelX;
g = (x1 - startPixelX - 1.0) - (y1 - bottomScanLine - 1.0) * dx / dy;
while (g >= 0) {
rightPixelInCurrentScanline++;
g -= 1; }}
else {
lineType = LEFT;
rightPixelInCurrentScanline = startPixelX;
leftPixelInCurrentScanLine = startPixelX;
g = (startPixelX - x1) + (y1 - bottomScanLine - 1.0) * dx / dy;
while (g > 0) {
leftPixelInCurrentScanLine--;
g -= 1; }}
}
public Edge moveToNextScanLine (int nextScanline) {
if (nextScanline > topScanLine) {
return null; }
if (lineType == SINGLE_VERTICAL) {
return this; }
if (topScanLine == nextScanline) {
if (lineType == RIGHT) {
leftPixelInCurrentScanLine = rightPixelInCurrentScanline;
rightPixelInCurrentScanline = endPixelX; }
else {
rightPixelInCurrentScanline = leftPixelInCurrentScanLine;
leftPixelInCurrentScanLine = endPixelX; }}
else { /* do Bresenham */
if (lineType == RIGHT) {
leftPixelInCurrentScanLine = rightPixelInCurrentScanline;
g += dx / dy;
while (g >= 0) {
rightPixelInCurrentScanline++;
g -= 1; }
rightPixelInCurrentScanline = Math.min (rightPixelInCurrentScanline, endPixelX); }
else {
rightPixelInCurrentScanline = leftPixelInCurrentScanLine;
g -= dx / dy;
while (g > 0) {
leftPixelInCurrentScanLine--;
g -= 1;}
leftPixelInCurrentScanLine = Math.max (leftPixelInCurrentScanLine, endPixelX); }}
return this;
}
public int compareTo (Object arg0) {
Edge e = (Edge) arg0;
if (e.leftPixelInCurrentScanLine < leftPixelInCurrentScanLine) {
return +1; }
else if (e.leftPixelInCurrentScanLine == leftPixelInCurrentScanLine) {
return 0; }
else {
return -1; }
}
}
private static boolean isIntegral (double x) {
return x == Math.floor (x);
}
Map/**/ scanLines = new HashMap ();
int firstScanline = Integer.MAX_VALUE;
int lastScanline = Integer.MIN_VALUE;
private void insertEdge (Edge edge) {
firstScanline = Math.min (firstScanline, edge.bottomScanLine);
lastScanline = Math.max (lastScanline, edge.topScanLine);
Integer Y = new Integer (edge.bottomScanLine);
List/**/ l = (List/**/) scanLines.get (Y);
if (l == null) {
l = new ArrayList/**/ ();
scanLines.put (Y, l);}
l.add (edge);
}
static boolean eofill = false;
private Edge[] mergeAndSort (List/**/ l1, Edge[] l2) {
int ll = (l1 == null) ? 0 : l1.size ();
for (int i = 0; i < l2.length; i++) {
if (l2[i] != null) {
ll++; }}
Edge[] result = new Edge [ll];
ll = 0;
for (int i = 0; i < l2.length; i++) {
if (l2 [i] != null) {
result [ll++] = l2 [i]; }}
if (l1 != null) {
for (Iterator it = l1.iterator(); it.hasNext(); ) {
result [ll++] = (Edge) it.next(); }}
Arrays.sort (result);
return result;
}
private void finish (BitmapConsumer consumer) {
Edge[] edges = new Edge [0];
for (int y = firstScanline; y <= lastScanline; y++) {
// add the edges that have y as their min.
edges = mergeAndSort ((List/**/) scanLines.get (new Integer (y)), edges);
boolean inScan = false;
double runStart = 0;
int cnt = 0;
int e = 0;
while (e < edges.length) {
if (!inScan) {
runStart = edges [e].leftPixelInCurrentScanLine + 1;
inScan = true; }
double runEnd = edges [e].leftPixelInCurrentScanLine;
while (e < edges.length && edges [e].leftPixelInCurrentScanLine <= runEnd) {
Edge edge = edges [e];
if (!(edge.atVertEdge)) {
runStart = Math.min (runStart, edge.leftPixelInCurrentScanLine); }
runEnd = Math.max (runEnd, edge.rightPixelInCurrentScanline);
cnt += edge.getCntDelta (y);
edges [e] = edge.moveToNextScanLine (y + 1);
e++; }
if ((eofill ? cnt & 3 : cnt) == 0) {
inScan = false;
consumer.addRun (runStart, runEnd + 1, y); }}}
}
//----------------------------------------------------------------------------
// private ScalerDebugger debugger;
public void setDebugger (ScalerDebugger outlineDebugger) {
// this.outlineDebugger = debugger;
}
}