5180 fichiers 38.5 Mo 0 téléchargements 191 vues mis à jour il y a 5 heures ui
nitro-v36-with-renderer-v36 / src / common / native-text / NativeTextRaster.ts
NativeTextRaster.ts 10.2 Ko Télécharger le fichier
/** Distance-field and polygon rasterization of glyph outlines. */
import type {
    DistanceSetup,
    RasterEdgePoint,
    RasterGrid,
    RasterLine,
    RasterPoint,
    RasterVertex
} from './NativeTextTypes';
import { isArray, isFiniteNumber } from './NativeTextMath';

var float32 = Math.fround,
    outsideDistanceSentinel = -1e3,
    scanlineBias = 0.0010000000474974513,
    scanlineEpsilon = float32(1e-8);
export function rasterizeAdfDistances(lines: RasterLine[], setup: DistanceSetup) {
    return rasterizeAdfDistancesAtScale(lines, setup, 3);
}
export function rasterizeGrayscaleAdfDistances(lines: RasterLine[], setup: DistanceSetup) {
    return rasterizeAdfDistancesAtScale(lines, setup, 1);
}
export function rasterizeAdfDistancesAtScale(lines: RasterLine[], setup: DistanceSetup, scale: number) {
    validate(lines, setup);
    let width = setup.width * scale,
        height = setup.height,
        stepX = float32(1 / scale),
        stepY = 1,
        inverseX = scale,
        inverseY = 1,
        cutoff = float32(Math.max(Math.abs(float32(setup.outsideCutoff)), Math.abs(float32(setup.insideCutoff)))),
        values = new Float32Array(width * height);
    values.fill(outsideDistanceSentinel);
    let grid = { width: width, height: height, stepX: stepX, stepY: stepY, inverseX: inverseX, inverseY: inverseY, values: values };
    if (cutoff !== 0) {
        for (let line of lines) rasterizeLineRectangle(line, cutoff, grid);
        for (let line of lines) rasterizeEndpoint(line.to, cutoff, grid);
    }
    return applyWindingSigns(lines, grid), { width: width, height: height, values: values };
}
export function rasterizeLineRectangle(line: RasterLine, cutoff: number, grid: RasterGrid) {
    let { from: from, to: to } = line,
        dx = float32(to.x - from.x),
        dy = float32(to.y - from.y),
        length = float32(Math.sqrt(dx * dx + dy * dy));
    if (!(length > 0)) return;
    let inverseLength = float32(1 / length),
        normalX = float32(dy * inverseLength),
        normalY = float32(-dx * inverseLength);
    if (normalX === 0 || normalY === 0) {
        rasterizeAxisLine(line, cutoff, normalX, normalY, grid);
        return;
    }
    let offsetX = float32(cutoff * normalX),
        offsetY = float32(cutoff * normalY),
        vertex = (S: number, z: number, K: number) => ({ x: float32(S * grid.inverseX), y: float32(z * grid.inverseY), distance: K }),
        fromOutside = vertex(from.x - offsetX, from.y - offsetY, -cutoff),
        fromInside = vertex(from.x + offsetX, from.y + offsetY, cutoff),
        toOutside = vertex(to.x - offsetX, to.y - offsetY, -cutoff),
        toInside = vertex(to.x + offsetX, to.y + offsetY, cutoff),
        lineSlope = float32(((to.x - from.x) * grid.inverseX) / ((to.y - from.y) * grid.inverseY)),
        cutoffSlope = float32((normalX * grid.inverseX) / (normalY * grid.inverseY)),
        distanceStep = float32(grid.stepY / normalY),
        edges = [
            makeEdge(fromOutside, toOutside, lineSlope, 0),
            makeEdge(fromInside, toInside, lineSlope, 0),
            makeEdge(fromOutside, fromInside, cutoffSlope, distanceStep),
            makeEdge(toOutside, toInside, cutoffSlope, distanceStep)
        ],
        xDistanceStep = float32(normalX * grid.stepX);
    rasterizePolygonRows(edges, xDistanceStep, grid);
}
export function makeEdge(from: RasterVertex, to: RasterVertex, slope: number, distanceStep: number) {
    let top = from.y <= to.y ? from : to,
        bottom = top === from ? to : from,
        firstRow = Math.max(0, Math.ceil(top.y)),
        lastRow = Math.floor(bottom.y),
        rowOffset = float32(firstRow - top.y),
        x = float32(rowOffset * slope + top.x),
        distance = float32(rowOffset * distanceStep + top.distance),
        edge = new Map<number, RasterEdgePoint>();
    for (let row = firstRow; row <= lastRow; row++)
        edge.set(row, { x: x, distance: distance }), (x = float32(x + slope)), (distance = float32(distance + distanceStep));
    return edge;
}
export function rasterizePolygonRows(edges: Map<number, RasterEdgePoint>[], step: number, grid: RasterGrid) {
    for (let row = 0; row < grid.height; row++) {
        let intersections = [];
        for (let edge of edges) {
            let intersection = edge.get(row);
            intersection && intersections.push(intersection);
        }
        if (intersections.length < 2) continue;
        intersections.sort((edge, intersection) => edge.x - intersection.x);
        let left = intersections[0]!,
            right = intersections.at(-1)!,
            firstX = Math.max(0, Math.ceil(left.x)),
            lastX = Math.min(grid.width - 1, Math.ceil(right.x) - 1);
        if (firstX > lastX) continue;
        let distance = float32((firstX - left.x) * step + left.distance),
            index = row * grid.width + firstX;
        for (let edge = firstX; edge <= lastX; edge++, index++) {
            let intersection = -Math.abs(distance);
            Math.abs(intersection) <= Math.abs(grid.values[index]!) && (grid.values[index]! = intersection), (distance = float32(distance + step));
        }
    }
}
export function rasterizeEndpoint(point: RasterPoint, cutoff: number, grid: RasterGrid) {
    let firstX = Math.max(0, Math.ceil((point.x - cutoff) * grid.inverseX)),
        lastX = Math.min(grid.width - 1, Math.ceil((point.x + cutoff) * grid.inverseX) - 1),
        firstY = Math.max(0, Math.ceil((point.y - cutoff) * grid.inverseY)),
        lastY = Math.min(grid.height - 1, Math.ceil((point.y + cutoff) * grid.inverseY) - 1);
    if (firstX > lastX || firstY > lastY) return;
    let xOffsets = new Float32Array(lastX - firstX + 1),
        dx = float32(firstX * grid.stepX - point.x);
    for (let row = 0; row < xOffsets.length; row++) (xOffsets[row]! = dx), (dx = float32(dx + grid.stepX));
    let dy = float32(firstY * grid.stepY - point.y);
    for (let row = firstY; row <= lastY; row++) {
        let index = row * grid.width + firstX;
        for (let column = firstX; column <= lastX; column++, index++) {
            let xOffset = xOffsets[column - firstX]!,
                distanceSquared = float32(xOffset * xOffset + dy * dy);
            grid.values[index]! * grid.values[index]! >= distanceSquared && (grid.values[index]! = -float32(Math.sqrt(distanceSquared)));
        }
        dy = float32(dy + grid.stepY);
    }
}
export function rasterizeAxisLine(line: RasterLine, cutoff: number, normalX: number, normalY: number, grid: RasterGrid) {
    let s = normalX !== 0,
        o = float32((s ? line.from.x - cutoff : Math.min(line.from.x, line.to.x)) * grid.inverseX),
        d = float32((s ? line.from.x + cutoff : Math.max(line.from.x, line.to.x)) * grid.inverseX),
        c = float32((s ? Math.min(line.from.y, line.to.y) : line.from.y - cutoff) * grid.inverseY),
        f = float32((s ? Math.max(line.from.y, line.to.y) : line.from.y + cutoff) * grid.inverseY),
        b = Math.max(0, Math.ceil(o)),
        l = Math.min(grid.width - 1, Math.ceil(d) - 1),
        _ = Math.max(0, Math.ceil(c)),
        h = Math.min(grid.height - 1, Math.ceil(f) - 1);
    if (!(b > l || _ > h))
        if (s) {
            let p = float32(line.from.x - b * grid.stepX);
            for (let m = _; m <= h; m++) {
                let v = p,
                    w = m * grid.width + b;
                for (let I = b; I <= l; I++, w++) {
                    let C = -Math.abs(v);
                    Math.abs(C) <= Math.abs(grid.values[w]!) && (grid.values[w]! = C), (v = float32(v - grid.stepX));
                }
            }
        } else {
            let p = float32(line.from.y - _ * grid.stepY);
            for (let m = _; m <= h; m++) {
                let v = -Math.abs(p),
                    w = m * grid.width + b;
                for (let I = b; I <= l; I++, w++) Math.abs(v) <= Math.abs(grid.values[w]!) && (grid.values[w]! = v);
                p = float32(p - grid.stepY);
            }
        }
}
export function applyWindingSigns(lines: RasterLine[], grid: RasterGrid) {
    let windings = new Int8Array(grid.width * grid.height);
    for (let row of lines) rasterizeWindingLine(row, windings, grid);
    for (let row = 0; row < grid.height; row++) {
        let winding = 0,
            index = row * grid.width;
        for (let column = 0; column < grid.width; column++, index++)
            (winding = (winding + windings[index]!) & 255), winding !== 0 && (grid.values[index]! = -grid.values[index]!);
    }
}
export function rasterizeWindingLine(line: RasterLine, windings: Int8Array, grid: RasterGrid) {
    let t = float32(line.from.x * grid.inverseX),
        i = float32(line.to.x * grid.inverseX),
        s = biasScanY(float32(line.from.y * grid.inverseY)),
        o = biasScanY(float32(line.to.y * grid.inverseY)),
        d = Math.trunc(s),
        c = Math.trunc(o);
    if (d === c || (s < 0 && o < 0)) return;
    let f = o > s,
        l = (f ? s : o) >= 0 ? (f ? d : c) + 1 : 0,
        _ = f ? c : d;
    if (((_ = Math.min(grid.height - 1, _)), l > _)) return;
    let h = float32(i - t),
        p = float32(1 / (o - s)),
        m = float32((l - s) * p),
        v = float32(h * p),
        w = float32(h * m + t + 1),
        I = f ? 1 : -1,
        C = l * grid.width;
    for (let W = l; W <= _; W++, C += grid.width) {
        let R = Math.trunc(w);
        R >= 0 && R < grid.width && (windings[C + R]! += I), (w = float32(w + v));
    }
}
export function biasScanY(value: number) {
    let integer = Math.trunc(value),
        difference = integer - value;
    return difference < 0 && (difference = -integer - value), float32(difference) <= scanlineEpsilon ? float32(value - scanlineBias) : value;
}
export function validate(lines: RasterLine[], setup: DistanceSetup) {
    if (
        !isArray(lines) ||
        lines.some((r) => !r?.from || !r?.to || !isFiniteNumber(r.from.x) || !isFiniteNumber(r.from.y) || !isFiniteNumber(r.to.x) || !isFiniteNumber(r.to.y))
    )
        throw new TypeError('lines must contain finite from/to points');
    if (
        !setup ||
        !Number.isInteger(setup.width) ||
        setup.width < 1 ||
        !Number.isInteger(setup.height) ||
        setup.height < 1 ||
        !isFiniteNumber(setup.outsideCutoff) ||
        !isFiniteNumber(setup.insideCutoff)
    )
        throw new TypeError('setup must contain dimensions and finite cutoffs');
}
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser