QReducer.java 12.4 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.adobe.fontengine.font;

import com.adobe.fontengine.font.BitmapConsumer;
import com.adobe.fontengine.font.OutlineConsumer2;
import com.adobe.fontengine.font.OutlineConsumer2BaseImpl;
import com.adobe.fontengine.font.ScalerDebugger;
import com.adobe.fontengine.font.ScanConverter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class QReducer
implements ScanConverter {
    EdgeBuilder edgeBuilder;
    Map scanLines = new HashMap();
    int firstScanline = Integer.MAX_VALUE;
    int lastScanline = Integer.MIN_VALUE;
    static boolean eofill = false;

    public QReducer() {
        this.edgeBuilder = new EdgeBuilder();
        this.edgeBuilder.setFlatness(1.0);
    }

    public void setScanType(int scanType) {
    }

    public OutlineConsumer2 getOutlineConsumer2() {
        return this.edgeBuilder;
    }

    public void getBitmap(BitmapConsumer bitmapConsumer) {
        this.finish(bitmapConsumer);
    }

    private static boolean isIntegral(double x) {
        return x == Math.floor(x);
    }

    private void insertEdge(Edge edge) {
        this.firstScanline = Math.min(this.firstScanline, edge.bottomScanLine);
        this.lastScanline = Math.max(this.lastScanline, edge.topScanLine);
        Integer Y = new Integer(edge.bottomScanLine);
        ArrayList<Edge> l = (ArrayList<Edge>)this.scanLines.get(Y);
        if (l == null) {
            l = new ArrayList<Edge>();
            this.scanLines.put(Y, l);
        }
        l.add(edge);
    }

    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) continue;
            ++ll;
        }
        Object[] result = new Edge[ll];
        ll = 0;
        for (int i2 = 0; i2 < l2.length; ++i2) {
            if (l2[i2] == null) continue;
            result[ll++] = l2[i2];
        }
        if (l1 != null) {
            for (Object result[ll++] : l1) {
            }
        }
        Arrays.sort(result);
        return result;
    }

    private void finish(BitmapConsumer consumer) {
        Edge[] edges = new Edge[]{};
        for (int y = this.firstScanline; y <= this.lastScanline; ++y) {
            edges = this.mergeAndSort((List)this.scanLines.get(new Integer(y)), edges);
            boolean inScan = false;
            double runStart = 0.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 && (double)edges[e].leftPixelInCurrentScanLine <= runEnd) {
                    Edge edge = edges[e];
                    if (!edge.atVertEdge) {
                        runStart = Math.min(runStart, (double)edge.leftPixelInCurrentScanLine);
                    }
                    runEnd = Math.max(runEnd, (double)edge.rightPixelInCurrentScanline);
                    cnt += edge.getCntDelta(y);
                    edges[e] = edge.moveToNextScanLine(y + 1);
                    ++e;
                }
                if ((eofill ? cnt & 3 : cnt) != 0) continue;
                inScan = false;
                consumer.addRun(runStart, runEnd + 1.0, y);
            }
        }
    }

    public void setDebugger(ScalerDebugger outlineDebugger) {
    }

    private static class Edge
    implements Comparable {
        private final boolean reversed;
        private final boolean endsAtHorzEdge;
        private final boolean startsAtHorzEdge;
        private static final int LEFT = -1;
        private static final int SINGLE_VERTICAL = 0;
        private static final int RIGHT = 1;
        private static final int SINGLE_HORIZONTAL = 2;
        private final int lineType;
        private final double dx;
        private final double dy;
        private final int endPixelX;
        private double g;
        public final int bottomScanLine;
        public final int topScanLine;
        public final boolean atVertEdge;
        public int leftPixelInCurrentScanLine;
        public int rightPixelInCurrentScanline;

        public int getCntDelta(int currentScanline) {
            int cntDelta = 2;
            if (this.bottomScanLine == currentScanline && !this.startsAtHorzEdge) {
                --cntDelta;
            }
            if (this.topScanLine == currentScanline && !this.endsAtHorzEdge) {
                --cntDelta;
            }
            if (!this.reversed) {
                cntDelta = - cntDelta;
            }
            return cntDelta;
        }

        public int pixel(double v) {
            int p = (int)Math.floor(v);
            if ((double)p == v) {
                --p;
            }
            return p;
        }

        public Edge(double x1, double y1, double x2, double y2) {
            int startPixelX;
            boolean bl = this.reversed = y1 > y2;
            if (this.reversed) {
                double tmp = y1;
                y1 = y2;
                y2 = tmp;
                tmp = x1;
                x1 = x2;
                x2 = tmp;
            }
            this.dx = x2 - x1;
            this.dy = y2 - y1;
            if (this.dx < 0.0) {
                this.atVertEdge = false;
                startPixelX = this.pixel(x1);
                this.endPixelX = (int)Math.floor(x2);
            } else if (this.dx == 0.0) {
                this.atVertEdge = QReducer.isIntegral(x1);
                this.endPixelX = startPixelX = this.pixel(x1);
            } else {
                this.atVertEdge = false;
                this.endPixelX = this.pixel(x2);
                startPixelX = (int)Math.floor(x1);
            }
            this.bottomScanLine = (int)Math.floor(y1);
            this.startsAtHorzEdge = QReducer.isIntegral(y1);
            this.topScanLine = this.pixel(y2);
            this.endsAtHorzEdge = QReducer.isIntegral(y2);
            if (this.bottomScanLine == this.topScanLine) {
                this.lineType = 2;
                this.leftPixelInCurrentScanLine = Math.min(startPixelX, this.endPixelX);
                this.rightPixelInCurrentScanline = Math.max(startPixelX, this.endPixelX);
            } else if (startPixelX == this.endPixelX) {
                this.lineType = 0;
                this.leftPixelInCurrentScanLine = startPixelX;
                this.rightPixelInCurrentScanline = startPixelX;
            } else if (startPixelX < this.endPixelX) {
                this.lineType = 1;
                this.rightPixelInCurrentScanline = startPixelX;
                this.leftPixelInCurrentScanLine = startPixelX;
                this.g = x1 - (double)startPixelX - 1.0 - (y1 - (double)this.bottomScanLine - 1.0) * this.dx / this.dy;
                while (this.g >= 0.0) {
                    ++this.rightPixelInCurrentScanline;
                    this.g -= 1.0;
                }
            } else {
                this.lineType = -1;
                this.rightPixelInCurrentScanline = startPixelX;
                this.leftPixelInCurrentScanLine = startPixelX;
                this.g = (double)startPixelX - x1 + (y1 - (double)this.bottomScanLine - 1.0) * this.dx / this.dy;
                while (this.g > 0.0) {
                    --this.leftPixelInCurrentScanLine;
                    this.g -= 1.0;
                }
            }
        }

        public Edge moveToNextScanLine(int nextScanline) {
            if (nextScanline > this.topScanLine) {
                return null;
            }
            if (this.lineType == 0) {
                return this;
            }
            if (this.topScanLine == nextScanline) {
                if (this.lineType == 1) {
                    this.leftPixelInCurrentScanLine = this.rightPixelInCurrentScanline;
                    this.rightPixelInCurrentScanline = this.endPixelX;
                } else {
                    this.rightPixelInCurrentScanline = this.leftPixelInCurrentScanLine;
                    this.leftPixelInCurrentScanLine = this.endPixelX;
                }
            } else if (this.lineType == 1) {
                this.leftPixelInCurrentScanLine = this.rightPixelInCurrentScanline;
                this.g += this.dx / this.dy;
                while (this.g >= 0.0) {
                    ++this.rightPixelInCurrentScanline;
                    this.g -= 1.0;
                }
                this.rightPixelInCurrentScanline = Math.min(this.rightPixelInCurrentScanline, this.endPixelX);
            } else {
                this.rightPixelInCurrentScanline = this.leftPixelInCurrentScanLine;
                this.g -= this.dx / this.dy;
                while (this.g > 0.0) {
                    --this.leftPixelInCurrentScanLine;
                    this.g -= 1.0;
                }
                this.leftPixelInCurrentScanLine = Math.max(this.leftPixelInCurrentScanLine, this.endPixelX);
            }
            return this;
        }

        public int compareTo(Object arg0) {
            Edge e = (Edge)arg0;
            if (e.leftPixelInCurrentScanLine < this.leftPixelInCurrentScanLine) {
                return 1;
            }
            if (e.leftPixelInCurrentScanLine == this.leftPixelInCurrentScanLine) {
                return 0;
            }
            return -1;
        }
    }

    private class EdgeBuilder
    extends OutlineConsumer2BaseImpl {
        private double THRESHOLD;
        private double epsilon;

        private EdgeBuilder() {
            this.THRESHOLD = 127.0;
        }

        public void setFlatness(double flatness) {
            this.epsilon = Math.max(1.220703125E-4, 1.5 * flatness / 4.0);
        }

        public void setScanType(int scanType) {
        }

        public void startOutline() {
        }

        public void startContour() {
        }

        public void line(double x1, double y1, double x2, double y2) {
            if (y1 != y2 || !QReducer.isIntegral(y1)) {
                QReducer.this.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) < this.THRESHOLD && Math.abs(x3 - x1 - 2.0 * (x2 - x1)) <= this.epsilon && (y1 <= y2 && y2 <= y3 || y3 <= y2 && y2 <= y1) && Math.abs(y3 - y1) < this.THRESHOLD && Math.abs(y3 - y1 - 2.0 * (y2 - y1)) <= this.epsilon) {
                this.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;
                this.quadraticCurve(x1, y1, x12, y12, x1223, y1223);
                this.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) < this.THRESHOLD && Math.abs(x4 - x1 - 3.0 * (x2 - x1)) <= this.epsilon && Math.abs(x4 - x1 - 3.0 * (x4 - x3)) <= this.epsilon && (y1 <= y2 && y2 <= y3 && y3 <= y4 || y4 <= y3 && y3 <= y2 && y2 <= y1) && Math.abs(y4 - y1) < this.THRESHOLD && Math.abs(y4 - y1 - 3.0 * (y2 - y1)) <= this.epsilon && Math.abs(y4 - y1 - 3.0 * (y4 - y3)) <= this.epsilon) {
                this.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;
                this.cubicCurve(x1, y1, x12, y12, x1223, y1223, xx, yy);
                this.cubicCurve(xx, yy, x2334, y2334, x34, y34, x4, y4);
            }
        }

        public void endContour() {
        }

        public void endOutline() {
        }
    }

}