Merge pull request #3354 from fkaelberer/fasterType3Glyph
Faster type3 glyph outlines
This commit is contained in:
commit
ee1a4cd449
147
src/canvas.js
147
src/canvas.js
@ -194,13 +194,10 @@ function compileType3Glyph(imgData) {
|
|||||||
var POINT_TO_PROCESS_LIMIT = 1000;
|
var POINT_TO_PROCESS_LIMIT = 1000;
|
||||||
|
|
||||||
var width = imgData.width, height = imgData.height;
|
var width = imgData.width, height = imgData.height;
|
||||||
var i, j;
|
var i, j, j0, width1 = width + 1;
|
||||||
// we need sparse arrays
|
var points = new Uint8Array(width1 * (height + 1));
|
||||||
var points = [];
|
var POINT_TYPES =
|
||||||
for (i = 0; i <= height; i++) {
|
new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
|
||||||
points.push([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// finding iteresting points: every point is located between mask pixels,
|
// finding iteresting points: every point is located between mask pixels,
|
||||||
// so there will be points of the (width + 1)x(height + 1) grid. Every point
|
// so there will be points of the (width + 1)x(height + 1) grid. Every point
|
||||||
// will have flags assigned based on neighboring mask pixels:
|
// will have flags assigned based on neighboring mask pixels:
|
||||||
@ -213,40 +210,41 @@ function compileType3Glyph(imgData) {
|
|||||||
// - and, intersections: 5, 10.
|
// - and, intersections: 5, 10.
|
||||||
var pos = 3, data = imgData.data, lineSize = width * 4, count = 0;
|
var pos = 3, data = imgData.data, lineSize = width * 4, count = 0;
|
||||||
if (data[3] !== 0) {
|
if (data[3] !== 0) {
|
||||||
points[0][0] = 1;
|
points[0] = 1;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
for (j = 1; j < width; j++) {
|
for (j = 1; j < width; j++) {
|
||||||
if (data[pos] !== data[pos + 4]) {
|
if (data[pos] !== data[pos + 4]) {
|
||||||
points[0][j] = data[pos] ? 2 : 1;
|
points[j] = data[pos] ? 2 : 1;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
pos += 4;
|
pos += 4;
|
||||||
}
|
}
|
||||||
if (data[pos] !== 0) {
|
if (data[pos] !== 0) {
|
||||||
points[0][j] = 2;
|
points[j] = 2;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
pos += 4;
|
pos += 4;
|
||||||
for (i = 1; i < height; i++) {
|
for (i = 1; i < height; i++) {
|
||||||
|
j0 = i * width1;
|
||||||
if (data[pos - lineSize] !== data[pos]) {
|
if (data[pos - lineSize] !== data[pos]) {
|
||||||
points[i][0] = data[pos] ? 1 : 8;
|
points[j0] = data[pos] ? 1 : 8;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
// 'sum' is the position of the current pixel configuration in the 'TYPES'
|
||||||
|
// array (in order 8-1-2-4, so we can use '>>2' to shift the column).
|
||||||
|
var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
|
||||||
for (j = 1; j < width; j++) {
|
for (j = 1; j < width; j++) {
|
||||||
var f1 = data[pos + 4] ? 1 : 0;
|
sum = (sum >> 2) + (data[pos + 4] ? 4 : 0) +
|
||||||
var f2 = data[pos] ? 1 : 0;
|
(data[pos - lineSize + 4] ? 8 : 0);
|
||||||
var f4 = data[pos - lineSize] ? 1 : 0;
|
if (POINT_TYPES[sum]) {
|
||||||
var f8 = data[pos - lineSize + 4] ? 1 : 0;
|
points[j0 + j] = POINT_TYPES[sum];
|
||||||
var fSum = f1 + f2 + f4 + f8;
|
|
||||||
if (fSum === 1 || fSum === 3 || (fSum === 2 && f1 === f4)) {
|
|
||||||
points[i][j] = f1 | (f2 << 1) | (f4 << 2) | (f8 << 3);
|
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
pos += 4;
|
pos += 4;
|
||||||
}
|
}
|
||||||
if (data[pos - lineSize] !== data[pos]) {
|
if (data[pos - lineSize] !== data[pos]) {
|
||||||
points[i][j] = data[pos] ? 2 : 4;
|
points[j0 + j] = data[pos] ? 2 : 4;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
pos += 4;
|
pos += 4;
|
||||||
@ -255,20 +253,22 @@ function compileType3Glyph(imgData) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos -= lineSize;
|
pos -= lineSize;
|
||||||
|
j0 = i * width1;
|
||||||
if (data[pos] !== 0) {
|
if (data[pos] !== 0) {
|
||||||
points[i][0] = 8;
|
points[j0] = 8;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
for (j = 1; j < width; j++) {
|
for (j = 1; j < width; j++) {
|
||||||
if (data[pos] !== data[pos + 4]) {
|
if (data[pos] !== data[pos + 4]) {
|
||||||
points[i][j] = data[pos] ? 4 : 8;
|
points[j0 + j] = data[pos] ? 4 : 8;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
pos += 4;
|
pos += 4;
|
||||||
}
|
}
|
||||||
if (data[pos] !== 0) {
|
if (data[pos] !== 0) {
|
||||||
points[i][j] = 4;
|
points[j0 + j] = 4;
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
if (count > POINT_TO_PROCESS_LIMIT) {
|
if (count > POINT_TO_PROCESS_LIMIT) {
|
||||||
@ -276,69 +276,64 @@ function compileType3Glyph(imgData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// building outlines
|
// building outlines
|
||||||
var outline = [];
|
var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
|
||||||
outline.push('c.save();');
|
var outlines = [];
|
||||||
// the path shall be painted in [0..1]x[0..1] space
|
for (i = 0; count && i <= height; i++) {
|
||||||
outline.push('c.scale(' + (1 / width) + ',' + (-1 / height) + ');');
|
var p = i * width1;
|
||||||
outline.push('c.translate(0,-' + height + ');');
|
var end = p + width;
|
||||||
outline.push('c.beginPath();');
|
while (p < end && !points[p]) {
|
||||||
for (i = 0; i <= height; i++) {
|
p++;
|
||||||
if (points[i].length === 0) {
|
}
|
||||||
|
if (p === end) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var js = null;
|
var coords = [p % width1, i];
|
||||||
for (js in points[i]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (js === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var i0 = i, j0 = (j = +js);
|
|
||||||
|
|
||||||
outline.push('c.moveTo(' + j + ',' + i + ');');
|
var type = points[p], p0 = p, pp;
|
||||||
var type = points[i][j], d = 0;
|
|
||||||
do {
|
do {
|
||||||
if (type === 5 || type === 10) {
|
var step = steps[type];
|
||||||
// line crossed: following dirrection we followed
|
do { p += step; } while (!points[p]);
|
||||||
points[i0][j0] = type | (15 ^ d); // changing direction for "future hit"
|
|
||||||
type |= d;
|
pp = points[p];
|
||||||
|
if (pp !== 5 && pp !== 10) {
|
||||||
|
// set new direction
|
||||||
|
type = pp;
|
||||||
|
// delete mark
|
||||||
|
points[p] = 0;
|
||||||
|
} else { // type is 5 or 10, ie, a crossing
|
||||||
|
// set new direction
|
||||||
|
type = pp & ((0x33 * type) >> 4);
|
||||||
|
// set new type for "future hit"
|
||||||
|
points[p] &= (type >> 2 | type << 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
coords.push(p % width1);
|
||||||
case 1:
|
coords.push((p / width1) | 0);
|
||||||
case 13:
|
--count;
|
||||||
do { i0++; } while (!points[i0][j0]);
|
} while (p0 !== p);
|
||||||
d = 9;
|
outlines.push(coords);
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
case 7:
|
|
||||||
do { i0--; } while (!points[i0][j0]);
|
|
||||||
d = 6;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
case 14:
|
|
||||||
do { j0++; } while (!points[i0][j0]);
|
|
||||||
d = 12;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
case 11:
|
|
||||||
do { j0--; } while (!points[i0][j0]);
|
|
||||||
d = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
outline.push('c.lineTo(' + j0 + ',' + i0 + ');');
|
|
||||||
|
|
||||||
type = points[i0][j0];
|
|
||||||
delete points[i0][j0];
|
|
||||||
} while (j0 !== j || i0 !== i);
|
|
||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
outline.push('c.fill();');
|
|
||||||
outline.push('c.beginPath();');
|
|
||||||
outline.push('c.restore();');
|
|
||||||
|
|
||||||
/*jshint -W054 */
|
var drawOutline = function(c) {
|
||||||
return new Function('c', outline.join('\n'));
|
c.save();
|
||||||
|
// the path shall be painted in [0..1]x[0..1] space
|
||||||
|
c.scale(1 / width, -1 / height);
|
||||||
|
c.translate(0, -height);
|
||||||
|
c.beginPath();
|
||||||
|
for (var i = 0, ii = outlines.length; i < ii; i++) {
|
||||||
|
var o = outlines[i];
|
||||||
|
c.moveTo(o[0], o[1]);
|
||||||
|
for (var j = 2, jj = o.length; j < jj; j += 2) {
|
||||||
|
c.lineTo(o[j], o[j+1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.fill();
|
||||||
|
c.beginPath();
|
||||||
|
c.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
return drawOutline;
|
||||||
}
|
}
|
||||||
|
|
||||||
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user