This commit is contained in:
崮生(子虚) 2026-04-09 10:21:09 +08:00
parent d43255aba0
commit 2aa6852dca
10 changed files with 264 additions and 102 deletions

View File

@ -12,20 +12,35 @@ exports.default = pathCeil;
/**
* 对path坐标进行调整
*
* @param {Array} contour 轮廓点数组
* @param {Array} contour 轮廓点数组对象格式或扁平格式 [x, y, onCurve, ...]
* @param {number} point 四舍五入的点数
* @return {Object} contour 坐标点
*/
function pathCeil(contour, point) {
var p;
for (var i = 0, l = contour.length; i < l; i++) {
p = contour[i];
if (!point) {
p.x = Math.round(p.x);
p.y = Math.round(p.y);
} else {
p.x = Number(p.x.toFixed(point));
p.y = Number(p.y.toFixed(point));
if (!contour.length) {
return contour;
}
/* 优化66: 检测扁平格式 - 第一个元素是 number 则为扁平格式 */
if (typeof contour[0] === 'number') {
for (var i = 0, l = contour.length; i < l; i += 3) {
if (!point) {
contour[i] = Math.round(contour[i]);
contour[i + 1] = Math.round(contour[i + 1]);
} else {
contour[i] = Number(contour[i].toFixed(point));
contour[i + 1] = Number(contour[i + 1].toFixed(point));
}
}
} else {
for (var i = 0, l = contour.length; i < l; i++) {
var p = contour[i];
if (!point) {
p.x = Math.round(p.x);
p.y = Math.round(p.y);
} else {
p.x = Number(p.x.toFixed(point));
p.y = Number(p.y.toFixed(point));
}
}
}
return contour;

View File

@ -22,25 +22,35 @@ exports.default = transform;
/**
* 图形仿射矩阵变换
*
* @param {Array.<Object>} contour 轮廓点
* @param {Array} contour 轮廓点对象格式或扁平格式 [x, y, onCurve, ...]
* @param {number} a m11
* @param {number} b m12
* @param {number} c m21
* @param {number} d m22
* @param {number} e dx
* @param {number} f dy
* @return {Array.<Object>} contour 轮廓点
* @return {Array} contour 轮廓点
*/
function transform(contour, a, b, c, d, e, f) {
var x;
var y;
var p;
for (var i = 0, l = contour.length; i < l; i++) {
p = contour[i];
x = p.x;
y = p.y;
p.x = x * a + y * c + e;
p.y = x * b + y * d + f;
if (!contour.length) {
return contour;
}
/* 优化66: 检测扁平格式 - 第一个元素是 number 则为扁平格式 */
if (typeof contour[0] === 'number') {
for (var i = 0, l = contour.length; i < l; i += 3) {
var x = contour[i];
var y = contour[i + 1];
contour[i] = x * a + y * c + e;
contour[i + 1] = x * b + y * d + f;
}
} else {
for (var i = 0, l = contour.length; i < l; i++) {
var p = contour[i];
var x = p.x;
var y = p.y;
p.x = x * a + y * c + e;
p.y = x * b + y * d + f;
}
}
return contour;
}

View File

@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = reducePath;
var _reducePathFlat = _interopRequireDefault(require("./reducePathFlat"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @file 缩减path大小去除冗余节点
* @author mengke01(kekee000@gmail.com)
@ -38,13 +40,17 @@ function redundant(prev, p, next) {
/**
* 缩减glyf去除冗余节点
*
* @param {Array} contour 路径对象
* @param {Array} contour 路径对象对象格式或扁平格式 [x, y, onCurve, ...]
* @return {Array} 路径对象
*/
function reducePath(contour) {
if (!contour.length) {
return contour;
}
/* 优化66: 检测扁平格式 - 第一个元素是 number 则为扁平格式 */
if (typeof contour[0] === 'number') {
return (0, _reducePathFlat.default)(contour);
}
var prev;
var next;
var p;

View File

@ -90,25 +90,26 @@ function parseSimpleGlyf(reader, glyf) {
reader.offset = vOffset - view.byteOffset;
/* 优化9: 一次遍历构建 contours消除中间数组 */
/* 优化66: 扁平 contours [x, y, onCurve, x, y, onCurve, ...],消除大量小对象 */
if (numberOfCoordinates > 0) {
var endPtsOfContours = glyf.endPtsOfContours;
var contours = new Array(endPtsOfContours.length);
var start = 0;
for (var ci = 0, cl = endPtsOfContours.length; ci < cl; ci++) {
var end = endPtsOfContours[ci] + 1;
var contour = new Array(end - start);
var numPoints = end - start;
var contour = new Array(numPoints * 3);
var ki = 0;
for (var pi = start; pi < end; pi++) {
contour[pi - start] = {
x: xArr[pi],
y: yArr[pi],
onCurve: !!(flags[pi] & ONCURVE)
};
contour[ki++] = xArr[pi];
contour[ki++] = yArr[pi];
contour[ki++] = !!(flags[pi] & ONCURVE);
}
contours[ci] = contour;
start = end;
}
glyf.contours = contours;
glyf._flatContours = true;
}
return glyf;
}

View File

@ -12,7 +12,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
*/
/**
* 优化33+38+39+40+48+49+57: getFlagsAndSize 单遍扫描替代两遍扫描
* 优化33+38+39+40+48+49+57+66: getFlagsAndSize 单遍扫描替代两遍扫描支持扁平 contours
*/
function getFlagsAndSize(glyf, glyfSupport, hinting) {
if (!glyf.contours || glyf.contours.length === 0) {
@ -36,70 +36,128 @@ function getFlagsAndSize(glyf, glyfSupport, hinting) {
var prevFlag = -1;
var repeatPoint = -1;
/* 优化66: 检测扁平格式 */
var isFlat = glyf._flatContours;
/* 单次遍历: delta坐标计算 + flag压缩 + 坐标编码 + 大小累加 */
var encodedCoordSize = 0;
for (var j = 0, cl = contours.length; j < cl; j++) {
var contour = contours[j];
for (var i = 0, l = contour.length; i < l; i++) {
var point = contour[i];
var px = point.x;
var py = point.y;
var dx, dy;
var flag = point.onCurve ? ONCURVE : 0;
if (isFlat) {
/* 优化66: 扁平格式每3个元素为一个点 [x, y, onCurve, ...] */
for (var i = 0, l = contour.length; i < l; i += 3) {
var px = contour[i];
var py = contour[i + 1];
var onCurve = contour[i + 2];
var dx, dy;
var flag = onCurve ? ONCURVE : 0;
if (isFirst) {
dx = px;
dy = py;
isFirst = false;
} else {
dx = px - prevX;
dy = py - prevY;
}
prevX = px;
prevY = py;
var xEncoded = 0;
var yEncoded = 0;
if (dx === 0) {
flag += XSAME;
} else if (-0xFF <= dx && dx <= 0xFF) {
flag += XSHORT;
if (dx > 0) flag += XSAME;
xEncoded = Math.abs(dx);
xCoordC.push(xEncoded);
encodedCoordSize += 1;
} else {
xCoordC.push(dx);
encodedCoordSize += 2;
}
if (dy === 0) {
flag += YSAME;
} else if (-0xFF <= dy && dy <= 0xFF) {
flag += YSHORT;
if (dy > 0) flag += YSAME;
yEncoded = Math.abs(dy);
yCoordC.push(yEncoded);
encodedCoordSize += 1;
} else {
yCoordC.push(dy);
encodedCoordSize += 2;
}
/* REPEAT 压缩 */
if (flag === prevFlag && !isFirst) {
if (repeatPoint === -1) {
repeatPoint = flagsC.length - 1;
flagsC[repeatPoint] |= REPEAT;
flagsC.push(1);
if (isFirst) {
dx = px;
dy = py;
isFirst = false;
} else {
++flagsC[repeatPoint + 1];
dx = px - prevX;
dy = py - prevY;
}
prevX = px;
prevY = py;
if (dx === 0) {
flag += XSAME;
} else if (-0xFF <= dx && dx <= 0xFF) {
flag += XSHORT;
if (dx > 0) flag += XSAME;
xCoordC.push(Math.abs(dx));
encodedCoordSize += 1;
} else {
xCoordC.push(dx);
encodedCoordSize += 2;
}
if (dy === 0) {
flag += YSAME;
} else if (-0xFF <= dy && dy <= 0xFF) {
flag += YSHORT;
if (dy > 0) flag += YSAME;
yCoordC.push(Math.abs(dy));
encodedCoordSize += 1;
} else {
yCoordC.push(dy);
encodedCoordSize += 2;
}
/* REPEAT 压缩 */
if (flag === prevFlag && !isFirst) {
if (repeatPoint === -1) {
repeatPoint = flagsC.length - 1;
flagsC[repeatPoint] |= REPEAT;
flagsC.push(1);
} else {
++flagsC[repeatPoint + 1];
}
} else {
repeatPoint = -1;
flagsC.push(prevFlag = flag);
}
}
} else {
for (var i = 0, l = contour.length; i < l; i++) {
var point = contour[i];
var px = point.x;
var py = point.y;
var dx, dy;
var flag = point.onCurve ? ONCURVE : 0;
if (isFirst) {
dx = px;
dy = py;
isFirst = false;
} else {
dx = px - prevX;
dy = py - prevY;
}
prevX = px;
prevY = py;
if (dx === 0) {
flag += XSAME;
} else if (-0xFF <= dx && dx <= 0xFF) {
flag += XSHORT;
if (dx > 0) flag += XSAME;
xCoordC.push(Math.abs(dx));
encodedCoordSize += 1;
} else {
xCoordC.push(dx);
encodedCoordSize += 2;
}
if (dy === 0) {
flag += YSAME;
} else if (-0xFF <= dy && dy <= 0xFF) {
flag += YSHORT;
if (dy > 0) flag += YSAME;
yCoordC.push(Math.abs(dy));
encodedCoordSize += 1;
} else {
yCoordC.push(dy);
encodedCoordSize += 2;
}
/* REPEAT 压缩 */
if (flag === prevFlag && !isFirst) {
if (repeatPoint === -1) {
repeatPoint = flagsC.length - 1;
flagsC[repeatPoint] |= REPEAT;
flagsC.push(1);
} else {
++flagsC[repeatPoint + 1];
}
} else {
repeatPoint = -1;
flagsC.push(prevFlag = flag);
}
} else {
repeatPoint = -1;
flagsC.push(prevFlag = flag);
}
}
}

View File

@ -96,11 +96,13 @@ function write(writer, ttf) {
}
}
} else {
/* 优化32: endPtsOfContours 直接 view 写入 */
/* 优化32+66: endPtsOfContours 直接 view 写入,支持扁平格式 */
var contours = glyf.contours || [];
var endPts = -1;
/* 优化66: 扁平格式 contour.length 是点的3倍 */
var isFlat = glyf._flatContours;
for (var ci = 0, cl = contours.length; ci < cl; ci++) {
endPts += contours[ci].length;
endPts += isFlat ? contours[ci].length / 3 : contours[ci].length;
view.setUint16(pos, endPts, false);
pos += 2;
}

View File

@ -39,10 +39,19 @@ function compound2simpleglyf(glyf, ttf, recrusive) {
(0, _transformGlyfContours.default)(glyf, ttf, contoursList, glyfIndex);
if (recrusive) {
Object.keys(contoursList).forEach(function (index) {
(0, _compound2simple.default)(ttf.glyf[index], contoursList[index]);
var target = ttf.glyf[index];
(0, _compound2simple.default)(target, contoursList[index]);
/* 优化66: 检测扁平格式并设置标记 */
if (target.contours && target.contours.length && typeof target.contours[0][0] === 'number') {
target._flatContours = true;
}
});
} else {
(0, _compound2simple.default)(glyf, contoursList[glyfIndex]);
/* 优化66: 检测扁平格式并设置标记 */
if (glyf.contours && glyf.contours.length && typeof glyf.contours[0][0] === 'number') {
glyf._flatContours = true;
}
}
return glyf;
}

View File

@ -6,12 +6,41 @@ Object.defineProperty(exports, "__esModule", {
exports.default = optimizettf;
var _reduceGlyf = _interopRequireDefault(require("./reduceGlyf"));
var _pathCeil = _interopRequireDefault(require("../../graphics/pathCeil"));
var _reducePathFlat = _interopRequireDefault(require("../../graphics/reducePathFlat"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @file 对ttf对象进行优化查找错误去除冗余点
* @author mengke01(kekee000@gmail.com)
*/
/**
* 优化17+66: 扁平格式单次遍历 pathCeil + reducePath
* 先四舍五入坐标再去除冗余点一次循环完成
*/
function ceilAndReduceFlat(glyf) {
var contours = glyf.contours;
for (var j = contours.length - 1; j >= 0; j--) {
var contour = contours[j];
/* 先原地四舍五入 */
for (var i = 0, l = contour.length; i < l; i += 3) {
contour[i] = Math.round(contour[i]);
contour[i + 1] = Math.round(contour[i + 1]);
}
/* 再去除冗余点 */
contour = (0, _reducePathFlat.default)(contour);
/* 空轮廓:扁平格式 <= 6 元素2个点 */
if (contour.length <= 6) {
contours.splice(j, 1);
} else {
contours[j] = contour;
}
}
if (0 === contours.length) {
delete glyf.contours;
}
return glyf;
}
/**
* 对ttf对象进行优化
*
@ -37,12 +66,17 @@ function optimizettf(ttf) {
});
}
if (!glyf.compound && glyf.contours) {
// 整数化
glyf.contours.forEach(function (contour) {
(0, _pathCeil.default)(contour);
});
// 缩减glyf
(0, _reduceGlyf.default)(glyf);
if (glyf._flatContours) {
/* 优化17+66: 扁平格式单次遍历 pathCeil + reduceGlyf */
ceilAndReduceFlat(glyf);
} else {
/* 整数化 */
glyf.contours.forEach(function (contour) {
(0, _pathCeil.default)(contour);
});
/* 缩减glyf */
(0, _reduceGlyf.default)(glyf);
}
}
// 整数化

View File

@ -20,11 +20,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function reduceGlyf(glyf) {
var contours = glyf.contours;
var contour;
/* 优化66: 扁平格式下 contour.length 是点的3倍 */
var isFlat = glyf._flatContours;
var minLen = isFlat ? 6 : 2;
for (var j = contours.length - 1; j >= 0; j--) {
contour = (0, _reducePath.default)(contours[j]);
// 空轮廓
if (contour.length <= 2) {
/* 空轮廓:扁平格式 <= 6 元素2个点对象格式 <= 2 个点 */
if (contour.length <= minLen) {
contours.splice(j, 1);
continue;
}

View File

@ -13,6 +13,20 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
* @author mengke01(kekee000@gmail.com)
*/
/**
* 优化15+66: 扁平格式单次遍历 pathTransform + pathCeil
* 先仿射变换坐标再四舍五入一次循环完成
*/
function transformAndCeilFlat(contour, a, b, c, d, e, f) {
for (var i = 0, l = contour.length; i < l; i += 3) {
var x = contour[i];
var y = contour[i + 1];
contour[i] = Math.round(x * a + y * c + e);
contour[i + 1] = Math.round(x * b + y * d + f);
}
return contour;
}
/**
* 转换复合字形轮廓结果保存在contoursList中并返回当前glyf的轮廓
*
@ -40,12 +54,22 @@ function transformGlyfContours(glyf, ttf) {
transformGlyfContours(glyph, ttf, contoursList, g.glyphIndex);
}
// 这里需要进行matrix变换需要复制一份
var contours = (0, _lang.clone)(glyph.compound ? contoursList[g.glyphIndex] || [] : glyph.contours);
var sourceContours = glyph.compound ? contoursList[g.glyphIndex] || [] : glyph.contours;
var transform = g.transform;
for (var i = 0, l = contours.length; i < l; i++) {
(0, _pathTransform.default)(contours[i], transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
compoundContours.push((0, _pathCeil.default)(contours[i]));
if (sourceContours.length && typeof sourceContours[0][0] === 'number') {
/* 优化14+15+66: 扁平格式 - 浅拷贝 + 单次遍历 transform+ceil */
for (var i = 0, l = sourceContours.length; i < l; i++) {
var contour = sourceContours[i].slice();
compoundContours.push(transformAndCeilFlat(contour, transform.a, transform.b, transform.c, transform.d, transform.e, transform.f));
}
} else {
/* 传统对象格式 - 深拷贝 + 分别调用 transform+ceil */
var contours = (0, _lang.clone)(sourceContours);
for (var i = 0, l = contours.length; i < l; i++) {
(0, _pathTransform.default)(contours[i], transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
compoundContours.push((0, _pathCeil.default)(contours[i]));
}
}
});