Merge pull request #13377 from Snuffleupagus/pattern-class
Re-factor and convert the code in `src/core/pattern.js` to use standard classes
This commit is contained in:
commit
d2e7161f2c
@ -17,6 +17,7 @@ import {
|
|||||||
assert,
|
assert,
|
||||||
FormatError,
|
FormatError,
|
||||||
info,
|
info,
|
||||||
|
shadow,
|
||||||
unreachable,
|
unreachable,
|
||||||
UNSUPPORTED_FEATURES,
|
UNSUPPORTED_FEATURES,
|
||||||
Util,
|
Util,
|
||||||
@ -36,22 +37,12 @@ const ShadingType = {
|
|||||||
TENSOR_PATCH_MESH: 7,
|
TENSOR_PATCH_MESH: 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Pattern = (function PatternClosure() {
|
class Pattern {
|
||||||
// Constructor should define this.getPattern
|
constructor() {
|
||||||
// eslint-disable-next-line no-shadow
|
unreachable("Cannot initialize Pattern.");
|
||||||
function Pattern() {
|
|
||||||
unreachable("should not call Pattern constructor");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype = {
|
static parseShading(
|
||||||
// Input: current Canvas context
|
|
||||||
// Output: the appropriate fillStyle or strokeStyle
|
|
||||||
getPattern: function Pattern_getPattern(ctx) {
|
|
||||||
unreachable(`Should not call Pattern.getStyle: ${ctx}`);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.parseShading = function (
|
|
||||||
shading,
|
shading,
|
||||||
matrix,
|
matrix,
|
||||||
xref,
|
xref,
|
||||||
@ -67,8 +58,7 @@ const Pattern = (function PatternClosure() {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case ShadingType.AXIAL:
|
case ShadingType.AXIAL:
|
||||||
case ShadingType.RADIAL:
|
case ShadingType.RADIAL:
|
||||||
// Both radial and axial shadings are handled by RadialAxial shading.
|
return new RadialAxialShading(
|
||||||
return new Shadings.RadialAxial(
|
|
||||||
dict,
|
dict,
|
||||||
matrix,
|
matrix,
|
||||||
xref,
|
xref,
|
||||||
@ -80,7 +70,7 @@ const Pattern = (function PatternClosure() {
|
|||||||
case ShadingType.LATTICE_FORM_MESH:
|
case ShadingType.LATTICE_FORM_MESH:
|
||||||
case ShadingType.COONS_PATCH_MESH:
|
case ShadingType.COONS_PATCH_MESH:
|
||||||
case ShadingType.TENSOR_PATCH_MESH:
|
case ShadingType.TENSOR_PATCH_MESH:
|
||||||
return new Shadings.Mesh(
|
return new MeshShading(
|
||||||
shading,
|
shading,
|
||||||
matrix,
|
matrix,
|
||||||
xref,
|
xref,
|
||||||
@ -99,22 +89,33 @@ const Pattern = (function PatternClosure() {
|
|||||||
featureId: UNSUPPORTED_FEATURES.shadingPattern,
|
featureId: UNSUPPORTED_FEATURES.shadingPattern,
|
||||||
});
|
});
|
||||||
warn(ex);
|
warn(ex);
|
||||||
return new Shadings.Dummy();
|
return new DummyShading();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
return Pattern;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const Shadings = {};
|
class BaseShading {
|
||||||
|
// A small number to offset the first/last color stops so we can insert ones
|
||||||
|
// to support extend. Number.MIN_VALUE is too small and breaks the extend.
|
||||||
|
static get SMALL_NUMBER() {
|
||||||
|
return shadow(this, "SMALL_NUMBER", 1e-6);
|
||||||
|
}
|
||||||
|
|
||||||
// A small number to offset the first/last color stops so we can insert ones to
|
constructor() {
|
||||||
// support extend. Number.MIN_VALUE is too small and breaks the extend.
|
if (this.constructor === BaseShading) {
|
||||||
Shadings.SMALL_NUMBER = 1e-6;
|
unreachable("Cannot initialize BaseShading.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIR() {
|
||||||
|
unreachable("Abstract method `getIR` called.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Radial and axial shading have very similar implementations
|
// Radial and axial shading have very similar implementations
|
||||||
// If needed, the implementations can be broken into two classes
|
// If needed, the implementations can be broken into two classes.
|
||||||
Shadings.RadialAxial = (function RadialAxialClosure() {
|
class RadialAxialShading extends BaseShading {
|
||||||
function RadialAxial(
|
constructor(
|
||||||
dict,
|
dict,
|
||||||
matrix,
|
matrix,
|
||||||
xref,
|
xref,
|
||||||
@ -122,10 +123,10 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
|||||||
pdfFunctionFactory,
|
pdfFunctionFactory,
|
||||||
localColorSpaceCache
|
localColorSpaceCache
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
this.matrix = matrix;
|
this.matrix = matrix;
|
||||||
this.coordsArr = dict.getArray("Coords");
|
this.coordsArr = dict.getArray("Coords");
|
||||||
this.shadingType = dict.get("ShadingType");
|
this.shadingType = dict.get("ShadingType");
|
||||||
this.type = "Pattern";
|
|
||||||
const cs = ColorSpace.parse({
|
const cs = ColorSpace.parse({
|
||||||
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
|
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
|
||||||
xref,
|
xref,
|
||||||
@ -133,7 +134,6 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
|||||||
pdfFunctionFactory,
|
pdfFunctionFactory,
|
||||||
localColorSpaceCache,
|
localColorSpaceCache,
|
||||||
});
|
});
|
||||||
this.cs = cs;
|
|
||||||
const bbox = dict.getArray("BBox");
|
const bbox = dict.getArray("BBox");
|
||||||
if (Array.isArray(bbox) && bbox.length === 4) {
|
if (Array.isArray(bbox) && bbox.length === 4) {
|
||||||
this.bbox = Util.normalizeRect(bbox);
|
this.bbox = Util.normalizeRect(bbox);
|
||||||
@ -213,19 +213,18 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
|||||||
// Insert a color stop at the front and offset the first real color stop
|
// Insert a color stop at the front and offset the first real color stop
|
||||||
// so it doesn't conflict with the one we insert.
|
// so it doesn't conflict with the one we insert.
|
||||||
colorStops.unshift([0, background]);
|
colorStops.unshift([0, background]);
|
||||||
colorStops[1][0] += Shadings.SMALL_NUMBER;
|
colorStops[1][0] += BaseShading.SMALL_NUMBER;
|
||||||
}
|
}
|
||||||
if (!extendEnd) {
|
if (!extendEnd) {
|
||||||
// Same idea as above in extendStart but for the end.
|
// Same idea as above in extendStart but for the end.
|
||||||
colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
|
colorStops[colorStops.length - 1][0] -= BaseShading.SMALL_NUMBER;
|
||||||
colorStops.push([1, background]);
|
colorStops.push([1, background]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.colorStops = colorStops;
|
this.colorStops = colorStops;
|
||||||
}
|
}
|
||||||
|
|
||||||
RadialAxial.prototype = {
|
getIR() {
|
||||||
getIR: function RadialAxial_getIR() {
|
|
||||||
const coordsArr = this.coordsArr;
|
const coordsArr = this.coordsArr;
|
||||||
const shadingType = this.shadingType;
|
const shadingType = this.shadingType;
|
||||||
let type, p0, p1, r0, r1;
|
let type, p0, p1, r0, r1;
|
||||||
@ -256,16 +255,13 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
|||||||
r1,
|
r1,
|
||||||
this.matrix,
|
this.matrix,
|
||||||
];
|
];
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return RadialAxial;
|
// All mesh shadings. For now, they will be presented as set of the triangles
|
||||||
})();
|
|
||||||
|
|
||||||
// All mesh shading. For now, they will be presented as set of the triangles
|
|
||||||
// to be drawn on the canvas and rgb color for each vertex.
|
// to be drawn on the canvas and rgb color for each vertex.
|
||||||
Shadings.Mesh = (function MeshClosure() {
|
class MeshStreamReader {
|
||||||
function MeshStreamReader(stream, context) {
|
constructor(stream, context) {
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.buffer = 0;
|
this.buffer = 0;
|
||||||
@ -278,7 +274,7 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
? new Float32Array(csNumComps)
|
? new Float32Array(csNumComps)
|
||||||
: this.tmpCompsBuf;
|
: this.tmpCompsBuf;
|
||||||
}
|
}
|
||||||
MeshStreamReader.prototype = {
|
|
||||||
get hasData() {
|
get hasData() {
|
||||||
if (this.stream.end) {
|
if (this.stream.end) {
|
||||||
return this.stream.pos < this.stream.end;
|
return this.stream.pos < this.stream.end;
|
||||||
@ -293,8 +289,9 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
this.buffer = nextByte;
|
this.buffer = nextByte;
|
||||||
this.bufferLength = 8;
|
this.bufferLength = 8;
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
readBits: function MeshStreamReader_readBits(n) {
|
|
||||||
|
readBits(n) {
|
||||||
let buffer = this.buffer;
|
let buffer = this.buffer;
|
||||||
let bufferLength = this.bufferLength;
|
let bufferLength = this.bufferLength;
|
||||||
if (n === 32) {
|
if (n === 32) {
|
||||||
@ -331,15 +328,18 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
this.bufferLength = bufferLength;
|
this.bufferLength = bufferLength;
|
||||||
this.buffer = buffer & ((1 << bufferLength) - 1);
|
this.buffer = buffer & ((1 << bufferLength) - 1);
|
||||||
return buffer >> bufferLength;
|
return buffer >> bufferLength;
|
||||||
},
|
}
|
||||||
align: function MeshStreamReader_align() {
|
|
||||||
|
align() {
|
||||||
this.buffer = 0;
|
this.buffer = 0;
|
||||||
this.bufferLength = 0;
|
this.bufferLength = 0;
|
||||||
},
|
}
|
||||||
readFlag: function MeshStreamReader_readFlag() {
|
|
||||||
|
readFlag() {
|
||||||
return this.readBits(this.context.bitsPerFlag);
|
return this.readBits(this.context.bitsPerFlag);
|
||||||
},
|
}
|
||||||
readCoordinate: function MeshStreamReader_readCoordinate() {
|
|
||||||
|
readCoordinate() {
|
||||||
const bitsPerCoordinate = this.context.bitsPerCoordinate;
|
const bitsPerCoordinate = this.context.bitsPerCoordinate;
|
||||||
const xi = this.readBits(bitsPerCoordinate);
|
const xi = this.readBits(bitsPerCoordinate);
|
||||||
const yi = this.readBits(bitsPerCoordinate);
|
const yi = this.readBits(bitsPerCoordinate);
|
||||||
@ -352,8 +352,9 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
xi * scale * (decode[1] - decode[0]) + decode[0],
|
xi * scale * (decode[1] - decode[0]) + decode[0],
|
||||||
yi * scale * (decode[3] - decode[2]) + decode[2],
|
yi * scale * (decode[3] - decode[2]) + decode[2],
|
||||||
];
|
];
|
||||||
},
|
}
|
||||||
readComponents: function MeshStreamReader_readComponents() {
|
|
||||||
|
readComponents() {
|
||||||
const numComps = this.context.numComps;
|
const numComps = this.context.numComps;
|
||||||
const bitsPerComponent = this.context.bitsPerComponent;
|
const bitsPerComponent = this.context.bitsPerComponent;
|
||||||
const scale =
|
const scale =
|
||||||
@ -371,12 +372,141 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
this.context.colorFn(components, 0, color, 0);
|
this.context.colorFn(components, 0, color, 0);
|
||||||
}
|
}
|
||||||
return this.context.colorSpace.getRgb(color, 0);
|
return this.context.colorSpace.getRgb(color, 0);
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function decodeType4Shading(mesh, reader) {
|
const getB = (function getBClosure() {
|
||||||
const coords = mesh.coords;
|
function buildB(count) {
|
||||||
const colors = mesh.colors;
|
const lut = [];
|
||||||
|
for (let i = 0; i <= count; i++) {
|
||||||
|
const t = i / count,
|
||||||
|
t_ = 1 - t;
|
||||||
|
lut.push(
|
||||||
|
new Float32Array([
|
||||||
|
t_ * t_ * t_,
|
||||||
|
3 * t * t_ * t_,
|
||||||
|
3 * t * t * t_,
|
||||||
|
t * t * t,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return lut;
|
||||||
|
}
|
||||||
|
const cache = [];
|
||||||
|
|
||||||
|
return function (count) {
|
||||||
|
if (!cache[count]) {
|
||||||
|
cache[count] = buildB(count);
|
||||||
|
}
|
||||||
|
return cache[count];
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
class MeshShading extends BaseShading {
|
||||||
|
static get MIN_SPLIT_PATCH_CHUNKS_AMOUNT() {
|
||||||
|
return shadow(this, "MIN_SPLIT_PATCH_CHUNKS_AMOUNT", 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get MAX_SPLIT_PATCH_CHUNKS_AMOUNT() {
|
||||||
|
return shadow(this, "MAX_SPLIT_PATCH_CHUNKS_AMOUNT", 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count of triangles per entire mesh bounds.
|
||||||
|
static get TRIANGLE_DENSITY() {
|
||||||
|
return shadow(this, "TRIANGLE_DENSITY", 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
stream,
|
||||||
|
matrix,
|
||||||
|
xref,
|
||||||
|
resources,
|
||||||
|
pdfFunctionFactory,
|
||||||
|
localColorSpaceCache
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
if (!isStream(stream)) {
|
||||||
|
throw new FormatError("Mesh data is not a stream");
|
||||||
|
}
|
||||||
|
const dict = stream.dict;
|
||||||
|
this.matrix = matrix;
|
||||||
|
this.shadingType = dict.get("ShadingType");
|
||||||
|
const bbox = dict.getArray("BBox");
|
||||||
|
if (Array.isArray(bbox) && bbox.length === 4) {
|
||||||
|
this.bbox = Util.normalizeRect(bbox);
|
||||||
|
} else {
|
||||||
|
this.bbox = null;
|
||||||
|
}
|
||||||
|
const cs = ColorSpace.parse({
|
||||||
|
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
|
||||||
|
xref,
|
||||||
|
resources,
|
||||||
|
pdfFunctionFactory,
|
||||||
|
localColorSpaceCache,
|
||||||
|
});
|
||||||
|
this.background = dict.has("Background")
|
||||||
|
? cs.getRgb(dict.get("Background"), 0)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const fnObj = dict.getRaw("Function");
|
||||||
|
const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
|
||||||
|
|
||||||
|
this.coords = [];
|
||||||
|
this.colors = [];
|
||||||
|
this.figures = [];
|
||||||
|
|
||||||
|
const decodeContext = {
|
||||||
|
bitsPerCoordinate: dict.get("BitsPerCoordinate"),
|
||||||
|
bitsPerComponent: dict.get("BitsPerComponent"),
|
||||||
|
bitsPerFlag: dict.get("BitsPerFlag"),
|
||||||
|
decode: dict.getArray("Decode"),
|
||||||
|
colorFn: fn,
|
||||||
|
colorSpace: cs,
|
||||||
|
numComps: fn ? 1 : cs.numComps,
|
||||||
|
};
|
||||||
|
const reader = new MeshStreamReader(stream, decodeContext);
|
||||||
|
|
||||||
|
let patchMesh = false;
|
||||||
|
switch (this.shadingType) {
|
||||||
|
case ShadingType.FREE_FORM_MESH:
|
||||||
|
this._decodeType4Shading(reader);
|
||||||
|
break;
|
||||||
|
case ShadingType.LATTICE_FORM_MESH:
|
||||||
|
const verticesPerRow = dict.get("VerticesPerRow") | 0;
|
||||||
|
if (verticesPerRow < 2) {
|
||||||
|
throw new FormatError("Invalid VerticesPerRow");
|
||||||
|
}
|
||||||
|
this._decodeType5Shading(reader, verticesPerRow);
|
||||||
|
break;
|
||||||
|
case ShadingType.COONS_PATCH_MESH:
|
||||||
|
this._decodeType6Shading(reader);
|
||||||
|
patchMesh = true;
|
||||||
|
break;
|
||||||
|
case ShadingType.TENSOR_PATCH_MESH:
|
||||||
|
this._decodeType7Shading(reader);
|
||||||
|
patchMesh = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unreachable("Unsupported mesh type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patchMesh) {
|
||||||
|
// Dirty bounds calculation, to determine how dense the triangles will be.
|
||||||
|
this._updateBounds();
|
||||||
|
for (let i = 0, ii = this.figures.length; i < ii; i++) {
|
||||||
|
this._buildFigureFromPatch(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Calculate bounds.
|
||||||
|
this._updateBounds();
|
||||||
|
|
||||||
|
this._packData();
|
||||||
|
}
|
||||||
|
|
||||||
|
_decodeType4Shading(reader) {
|
||||||
|
const coords = this.coords;
|
||||||
|
const colors = this.colors;
|
||||||
const operators = [];
|
const operators = [];
|
||||||
const ps = []; // not maintaining cs since that will match ps
|
const ps = []; // not maintaining cs since that will match ps
|
||||||
let verticesLeft = 0; // assuming we have all data to start a new triangle
|
let verticesLeft = 0; // assuming we have all data to start a new triangle
|
||||||
@ -411,16 +541,16 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
|
|
||||||
reader.align();
|
reader.align();
|
||||||
}
|
}
|
||||||
mesh.figures.push({
|
this.figures.push({
|
||||||
type: "triangles",
|
type: "triangles",
|
||||||
coords: new Int32Array(ps),
|
coords: new Int32Array(ps),
|
||||||
colors: new Int32Array(ps),
|
colors: new Int32Array(ps),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeType5Shading(mesh, reader, verticesPerRow) {
|
_decodeType5Shading(reader, verticesPerRow) {
|
||||||
const coords = mesh.coords;
|
const coords = this.coords;
|
||||||
const colors = mesh.colors;
|
const colors = this.colors;
|
||||||
const ps = []; // not maintaining cs since that will match ps
|
const ps = []; // not maintaining cs since that will match ps
|
||||||
while (reader.hasData) {
|
while (reader.hasData) {
|
||||||
const coord = reader.readCoordinate();
|
const coord = reader.readCoordinate();
|
||||||
@ -429,7 +559,7 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
coords.push(coord);
|
coords.push(coord);
|
||||||
colors.push(color);
|
colors.push(color);
|
||||||
}
|
}
|
||||||
mesh.figures.push({
|
this.figures.push({
|
||||||
type: "lattice",
|
type: "lattice",
|
||||||
coords: new Int32Array(ps),
|
coords: new Int32Array(ps),
|
||||||
colors: new Int32Array(ps),
|
colors: new Int32Array(ps),
|
||||||
@ -437,158 +567,10 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;
|
_decodeType6Shading(reader) {
|
||||||
const MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;
|
|
||||||
|
|
||||||
const TRIANGLE_DENSITY = 20; // count of triangles per entire mesh bounds
|
|
||||||
|
|
||||||
const getB = (function getBClosure() {
|
|
||||||
function buildB(count) {
|
|
||||||
const lut = [];
|
|
||||||
for (let i = 0; i <= count; i++) {
|
|
||||||
const t = i / count,
|
|
||||||
t_ = 1 - t;
|
|
||||||
lut.push(
|
|
||||||
new Float32Array([
|
|
||||||
t_ * t_ * t_,
|
|
||||||
3 * t * t_ * t_,
|
|
||||||
3 * t * t * t_,
|
|
||||||
t * t * t,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return lut;
|
|
||||||
}
|
|
||||||
const cache = [];
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
return function getB(count) {
|
|
||||||
if (!cache[count]) {
|
|
||||||
cache[count] = buildB(count);
|
|
||||||
}
|
|
||||||
return cache[count];
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
function buildFigureFromPatch(mesh, index) {
|
|
||||||
const figure = mesh.figures[index];
|
|
||||||
assert(figure.type === "patch", "Unexpected patch mesh figure");
|
|
||||||
|
|
||||||
const coords = mesh.coords,
|
|
||||||
colors = mesh.colors;
|
|
||||||
const pi = figure.coords;
|
|
||||||
const ci = figure.colors;
|
|
||||||
|
|
||||||
const figureMinX = Math.min(
|
|
||||||
coords[pi[0]][0],
|
|
||||||
coords[pi[3]][0],
|
|
||||||
coords[pi[12]][0],
|
|
||||||
coords[pi[15]][0]
|
|
||||||
);
|
|
||||||
const figureMinY = Math.min(
|
|
||||||
coords[pi[0]][1],
|
|
||||||
coords[pi[3]][1],
|
|
||||||
coords[pi[12]][1],
|
|
||||||
coords[pi[15]][1]
|
|
||||||
);
|
|
||||||
const figureMaxX = Math.max(
|
|
||||||
coords[pi[0]][0],
|
|
||||||
coords[pi[3]][0],
|
|
||||||
coords[pi[12]][0],
|
|
||||||
coords[pi[15]][0]
|
|
||||||
);
|
|
||||||
const figureMaxY = Math.max(
|
|
||||||
coords[pi[0]][1],
|
|
||||||
coords[pi[3]][1],
|
|
||||||
coords[pi[12]][1],
|
|
||||||
coords[pi[15]][1]
|
|
||||||
);
|
|
||||||
let splitXBy = Math.ceil(
|
|
||||||
((figureMaxX - figureMinX) * TRIANGLE_DENSITY) /
|
|
||||||
(mesh.bounds[2] - mesh.bounds[0])
|
|
||||||
);
|
|
||||||
splitXBy = Math.max(
|
|
||||||
MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
|
|
||||||
Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy)
|
|
||||||
);
|
|
||||||
let splitYBy = Math.ceil(
|
|
||||||
((figureMaxY - figureMinY) * TRIANGLE_DENSITY) /
|
|
||||||
(mesh.bounds[3] - mesh.bounds[1])
|
|
||||||
);
|
|
||||||
splitYBy = Math.max(
|
|
||||||
MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
|
|
||||||
Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy)
|
|
||||||
);
|
|
||||||
|
|
||||||
const verticesPerRow = splitXBy + 1;
|
|
||||||
const figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
|
|
||||||
const figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
|
|
||||||
let k = 0;
|
|
||||||
const cl = new Uint8Array(3),
|
|
||||||
cr = new Uint8Array(3);
|
|
||||||
const c0 = colors[ci[0]],
|
|
||||||
c1 = colors[ci[1]],
|
|
||||||
c2 = colors[ci[2]],
|
|
||||||
c3 = colors[ci[3]];
|
|
||||||
const bRow = getB(splitYBy),
|
|
||||||
bCol = getB(splitXBy);
|
|
||||||
for (let row = 0; row <= splitYBy; row++) {
|
|
||||||
cl[0] = ((c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy) | 0;
|
|
||||||
cl[1] = ((c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy) | 0;
|
|
||||||
cl[2] = ((c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy) | 0;
|
|
||||||
|
|
||||||
cr[0] = ((c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy) | 0;
|
|
||||||
cr[1] = ((c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy) | 0;
|
|
||||||
cr[2] = ((c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy) | 0;
|
|
||||||
|
|
||||||
for (let col = 0; col <= splitXBy; col++, k++) {
|
|
||||||
if (
|
|
||||||
(row === 0 || row === splitYBy) &&
|
|
||||||
(col === 0 || col === splitXBy)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let x = 0,
|
|
||||||
y = 0;
|
|
||||||
let q = 0;
|
|
||||||
for (let i = 0; i <= 3; i++) {
|
|
||||||
for (let j = 0; j <= 3; j++, q++) {
|
|
||||||
const m = bRow[row][i] * bCol[col][j];
|
|
||||||
x += coords[pi[q]][0] * m;
|
|
||||||
y += coords[pi[q]][1] * m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
figureCoords[k] = coords.length;
|
|
||||||
coords.push([x, y]);
|
|
||||||
figureColors[k] = colors.length;
|
|
||||||
const newColor = new Uint8Array(3);
|
|
||||||
newColor[0] = ((cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy) | 0;
|
|
||||||
newColor[1] = ((cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy) | 0;
|
|
||||||
newColor[2] = ((cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy) | 0;
|
|
||||||
colors.push(newColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
figureCoords[0] = pi[0];
|
|
||||||
figureColors[0] = ci[0];
|
|
||||||
figureCoords[splitXBy] = pi[3];
|
|
||||||
figureColors[splitXBy] = ci[1];
|
|
||||||
figureCoords[verticesPerRow * splitYBy] = pi[12];
|
|
||||||
figureColors[verticesPerRow * splitYBy] = ci[2];
|
|
||||||
figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
|
|
||||||
figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
|
|
||||||
|
|
||||||
mesh.figures[index] = {
|
|
||||||
type: "lattice",
|
|
||||||
coords: figureCoords,
|
|
||||||
colors: figureColors,
|
|
||||||
verticesPerRow,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeType6Shading(mesh, reader) {
|
|
||||||
// A special case of Type 7. The p11, p12, p21, p22 automatically filled
|
// A special case of Type 7. The p11, p12, p21, p22 automatically filled
|
||||||
const coords = mesh.coords;
|
const coords = this.coords;
|
||||||
const colors = mesh.colors;
|
const colors = this.colors;
|
||||||
const ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
|
const ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
|
||||||
const cs = new Int32Array(4); // c00, c30, c03, c33
|
const cs = new Int32Array(4); // c00, c30, c03, c33
|
||||||
while (reader.hasData) {
|
while (reader.hasData) {
|
||||||
@ -709,7 +691,7 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
3 * (coords[ps[2]][1] + coords[ps[8]][1])) /
|
3 * (coords[ps[2]][1] + coords[ps[8]][1])) /
|
||||||
9,
|
9,
|
||||||
]);
|
]);
|
||||||
mesh.figures.push({
|
this.figures.push({
|
||||||
type: "patch",
|
type: "patch",
|
||||||
coords: new Int32Array(ps), // making copies of ps and cs
|
coords: new Int32Array(ps), // making copies of ps and cs
|
||||||
colors: new Int32Array(cs),
|
colors: new Int32Array(cs),
|
||||||
@ -717,9 +699,9 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeType7Shading(mesh, reader) {
|
_decodeType7Shading(reader) {
|
||||||
const coords = mesh.coords;
|
const coords = this.coords;
|
||||||
const colors = mesh.colors;
|
const colors = this.colors;
|
||||||
const ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
|
const ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
|
||||||
const cs = new Int32Array(4); // c00, c30, c03, c33
|
const cs = new Int32Array(4); // c00, c30, c03, c33
|
||||||
while (reader.hasData) {
|
while (reader.hasData) {
|
||||||
@ -779,7 +761,7 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
cs[0] = cs[1]; cs[1] = ci + 1;
|
cs[0] = cs[1]; cs[1] = ci + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mesh.figures.push({
|
this.figures.push({
|
||||||
type: "patch",
|
type: "patch",
|
||||||
coords: new Int32Array(ps), // making copies of ps and cs
|
coords: new Int32Array(ps), // making copies of ps and cs
|
||||||
colors: new Int32Array(cs),
|
colors: new Int32Array(cs),
|
||||||
@ -787,35 +769,150 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBounds(mesh) {
|
_buildFigureFromPatch(index) {
|
||||||
let minX = mesh.coords[0][0],
|
const figure = this.figures[index];
|
||||||
minY = mesh.coords[0][1],
|
assert(figure.type === "patch", "Unexpected patch mesh figure");
|
||||||
|
|
||||||
|
const coords = this.coords,
|
||||||
|
colors = this.colors;
|
||||||
|
const pi = figure.coords;
|
||||||
|
const ci = figure.colors;
|
||||||
|
|
||||||
|
const figureMinX = Math.min(
|
||||||
|
coords[pi[0]][0],
|
||||||
|
coords[pi[3]][0],
|
||||||
|
coords[pi[12]][0],
|
||||||
|
coords[pi[15]][0]
|
||||||
|
);
|
||||||
|
const figureMinY = Math.min(
|
||||||
|
coords[pi[0]][1],
|
||||||
|
coords[pi[3]][1],
|
||||||
|
coords[pi[12]][1],
|
||||||
|
coords[pi[15]][1]
|
||||||
|
);
|
||||||
|
const figureMaxX = Math.max(
|
||||||
|
coords[pi[0]][0],
|
||||||
|
coords[pi[3]][0],
|
||||||
|
coords[pi[12]][0],
|
||||||
|
coords[pi[15]][0]
|
||||||
|
);
|
||||||
|
const figureMaxY = Math.max(
|
||||||
|
coords[pi[0]][1],
|
||||||
|
coords[pi[3]][1],
|
||||||
|
coords[pi[12]][1],
|
||||||
|
coords[pi[15]][1]
|
||||||
|
);
|
||||||
|
let splitXBy = Math.ceil(
|
||||||
|
((figureMaxX - figureMinX) * MeshShading.TRIANGLE_DENSITY) /
|
||||||
|
(this.bounds[2] - this.bounds[0])
|
||||||
|
);
|
||||||
|
splitXBy = Math.max(
|
||||||
|
MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
|
||||||
|
Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy)
|
||||||
|
);
|
||||||
|
let splitYBy = Math.ceil(
|
||||||
|
((figureMaxY - figureMinY) * MeshShading.TRIANGLE_DENSITY) /
|
||||||
|
(this.bounds[3] - this.bounds[1])
|
||||||
|
);
|
||||||
|
splitYBy = Math.max(
|
||||||
|
MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
|
||||||
|
Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy)
|
||||||
|
);
|
||||||
|
|
||||||
|
const verticesPerRow = splitXBy + 1;
|
||||||
|
const figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
|
||||||
|
const figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
|
||||||
|
let k = 0;
|
||||||
|
const cl = new Uint8Array(3),
|
||||||
|
cr = new Uint8Array(3);
|
||||||
|
const c0 = colors[ci[0]],
|
||||||
|
c1 = colors[ci[1]],
|
||||||
|
c2 = colors[ci[2]],
|
||||||
|
c3 = colors[ci[3]];
|
||||||
|
const bRow = getB(splitYBy),
|
||||||
|
bCol = getB(splitXBy);
|
||||||
|
for (let row = 0; row <= splitYBy; row++) {
|
||||||
|
cl[0] = ((c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy) | 0;
|
||||||
|
cl[1] = ((c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy) | 0;
|
||||||
|
cl[2] = ((c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy) | 0;
|
||||||
|
|
||||||
|
cr[0] = ((c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy) | 0;
|
||||||
|
cr[1] = ((c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy) | 0;
|
||||||
|
cr[2] = ((c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy) | 0;
|
||||||
|
|
||||||
|
for (let col = 0; col <= splitXBy; col++, k++) {
|
||||||
|
if (
|
||||||
|
(row === 0 || row === splitYBy) &&
|
||||||
|
(col === 0 || col === splitXBy)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let x = 0,
|
||||||
|
y = 0;
|
||||||
|
let q = 0;
|
||||||
|
for (let i = 0; i <= 3; i++) {
|
||||||
|
for (let j = 0; j <= 3; j++, q++) {
|
||||||
|
const m = bRow[row][i] * bCol[col][j];
|
||||||
|
x += coords[pi[q]][0] * m;
|
||||||
|
y += coords[pi[q]][1] * m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
figureCoords[k] = coords.length;
|
||||||
|
coords.push([x, y]);
|
||||||
|
figureColors[k] = colors.length;
|
||||||
|
const newColor = new Uint8Array(3);
|
||||||
|
newColor[0] = ((cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy) | 0;
|
||||||
|
newColor[1] = ((cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy) | 0;
|
||||||
|
newColor[2] = ((cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy) | 0;
|
||||||
|
colors.push(newColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
figureCoords[0] = pi[0];
|
||||||
|
figureColors[0] = ci[0];
|
||||||
|
figureCoords[splitXBy] = pi[3];
|
||||||
|
figureColors[splitXBy] = ci[1];
|
||||||
|
figureCoords[verticesPerRow * splitYBy] = pi[12];
|
||||||
|
figureColors[verticesPerRow * splitYBy] = ci[2];
|
||||||
|
figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
|
||||||
|
figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
|
||||||
|
|
||||||
|
this.figures[index] = {
|
||||||
|
type: "lattice",
|
||||||
|
coords: figureCoords,
|
||||||
|
colors: figureColors,
|
||||||
|
verticesPerRow,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateBounds() {
|
||||||
|
let minX = this.coords[0][0],
|
||||||
|
minY = this.coords[0][1],
|
||||||
maxX = minX,
|
maxX = minX,
|
||||||
maxY = minY;
|
maxY = minY;
|
||||||
for (let i = 1, ii = mesh.coords.length; i < ii; i++) {
|
for (let i = 1, ii = this.coords.length; i < ii; i++) {
|
||||||
const x = mesh.coords[i][0],
|
const x = this.coords[i][0],
|
||||||
y = mesh.coords[i][1];
|
y = this.coords[i][1];
|
||||||
minX = minX > x ? x : minX;
|
minX = minX > x ? x : minX;
|
||||||
minY = minY > y ? y : minY;
|
minY = minY > y ? y : minY;
|
||||||
maxX = maxX < x ? x : maxX;
|
maxX = maxX < x ? x : maxX;
|
||||||
maxY = maxY < y ? y : maxY;
|
maxY = maxY < y ? y : maxY;
|
||||||
}
|
}
|
||||||
mesh.bounds = [minX, minY, maxX, maxY];
|
this.bounds = [minX, minY, maxX, maxY];
|
||||||
}
|
}
|
||||||
|
|
||||||
function packData(mesh) {
|
_packData() {
|
||||||
let i, ii, j, jj;
|
let i, ii, j, jj;
|
||||||
|
|
||||||
const coords = mesh.coords;
|
const coords = this.coords;
|
||||||
const coordsPacked = new Float32Array(coords.length * 2);
|
const coordsPacked = new Float32Array(coords.length * 2);
|
||||||
for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
|
for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
|
||||||
const xy = coords[i];
|
const xy = coords[i];
|
||||||
coordsPacked[j++] = xy[0];
|
coordsPacked[j++] = xy[0];
|
||||||
coordsPacked[j++] = xy[1];
|
coordsPacked[j++] = xy[1];
|
||||||
}
|
}
|
||||||
mesh.coords = coordsPacked;
|
this.coords = coordsPacked;
|
||||||
|
|
||||||
const colors = mesh.colors;
|
const colors = this.colors;
|
||||||
const colorsPacked = new Uint8Array(colors.length * 3);
|
const colorsPacked = new Uint8Array(colors.length * 3);
|
||||||
for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
|
for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
|
||||||
const c = colors[i];
|
const c = colors[i];
|
||||||
@ -823,9 +920,9 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
colorsPacked[j++] = c[1];
|
colorsPacked[j++] = c[1];
|
||||||
colorsPacked[j++] = c[2];
|
colorsPacked[j++] = c[2];
|
||||||
}
|
}
|
||||||
mesh.colors = colorsPacked;
|
this.colors = colorsPacked;
|
||||||
|
|
||||||
const figures = mesh.figures;
|
const figures = this.figures;
|
||||||
for (i = 0, ii = figures.length; i < ii; i++) {
|
for (i = 0, ii = figures.length; i < ii; i++) {
|
||||||
const figure = figures[i],
|
const figure = figures[i],
|
||||||
ps = figure.coords,
|
ps = figure.coords,
|
||||||
@ -837,97 +934,7 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Mesh(
|
getIR() {
|
||||||
stream,
|
|
||||||
matrix,
|
|
||||||
xref,
|
|
||||||
resources,
|
|
||||||
pdfFunctionFactory,
|
|
||||||
localColorSpaceCache
|
|
||||||
) {
|
|
||||||
if (!isStream(stream)) {
|
|
||||||
throw new FormatError("Mesh data is not a stream");
|
|
||||||
}
|
|
||||||
const dict = stream.dict;
|
|
||||||
this.matrix = matrix;
|
|
||||||
this.shadingType = dict.get("ShadingType");
|
|
||||||
this.type = "Pattern";
|
|
||||||
const bbox = dict.getArray("BBox");
|
|
||||||
if (Array.isArray(bbox) && bbox.length === 4) {
|
|
||||||
this.bbox = Util.normalizeRect(bbox);
|
|
||||||
} else {
|
|
||||||
this.bbox = null;
|
|
||||||
}
|
|
||||||
const cs = ColorSpace.parse({
|
|
||||||
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
|
|
||||||
xref,
|
|
||||||
resources,
|
|
||||||
pdfFunctionFactory,
|
|
||||||
localColorSpaceCache,
|
|
||||||
});
|
|
||||||
this.cs = cs;
|
|
||||||
this.background = dict.has("Background")
|
|
||||||
? cs.getRgb(dict.get("Background"), 0)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const fnObj = dict.getRaw("Function");
|
|
||||||
const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
|
|
||||||
|
|
||||||
this.coords = [];
|
|
||||||
this.colors = [];
|
|
||||||
this.figures = [];
|
|
||||||
|
|
||||||
const decodeContext = {
|
|
||||||
bitsPerCoordinate: dict.get("BitsPerCoordinate"),
|
|
||||||
bitsPerComponent: dict.get("BitsPerComponent"),
|
|
||||||
bitsPerFlag: dict.get("BitsPerFlag"),
|
|
||||||
decode: dict.getArray("Decode"),
|
|
||||||
colorFn: fn,
|
|
||||||
colorSpace: cs,
|
|
||||||
numComps: fn ? 1 : cs.numComps,
|
|
||||||
};
|
|
||||||
const reader = new MeshStreamReader(stream, decodeContext);
|
|
||||||
|
|
||||||
let patchMesh = false;
|
|
||||||
switch (this.shadingType) {
|
|
||||||
case ShadingType.FREE_FORM_MESH:
|
|
||||||
decodeType4Shading(this, reader);
|
|
||||||
break;
|
|
||||||
case ShadingType.LATTICE_FORM_MESH:
|
|
||||||
const verticesPerRow = dict.get("VerticesPerRow") | 0;
|
|
||||||
if (verticesPerRow < 2) {
|
|
||||||
throw new FormatError("Invalid VerticesPerRow");
|
|
||||||
}
|
|
||||||
decodeType5Shading(this, reader, verticesPerRow);
|
|
||||||
break;
|
|
||||||
case ShadingType.COONS_PATCH_MESH:
|
|
||||||
decodeType6Shading(this, reader);
|
|
||||||
patchMesh = true;
|
|
||||||
break;
|
|
||||||
case ShadingType.TENSOR_PATCH_MESH:
|
|
||||||
decodeType7Shading(this, reader);
|
|
||||||
patchMesh = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
unreachable("Unsupported mesh type.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (patchMesh) {
|
|
||||||
// dirty bounds calculation for determining, how dense shall be triangles
|
|
||||||
updateBounds(this);
|
|
||||||
for (let i = 0, ii = this.figures.length; i < ii; i++) {
|
|
||||||
buildFigureFromPatch(this, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// calculate bounds
|
|
||||||
updateBounds(this);
|
|
||||||
|
|
||||||
packData(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh.prototype = {
|
|
||||||
getIR: function Mesh_getIR() {
|
|
||||||
return [
|
return [
|
||||||
"Mesh",
|
"Mesh",
|
||||||
this.shadingType,
|
this.shadingType,
|
||||||
@ -939,24 +946,14 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
this.bbox,
|
this.bbox,
|
||||||
this.background,
|
this.background,
|
||||||
];
|
];
|
||||||
},
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return Mesh;
|
|
||||||
})();
|
|
||||||
|
|
||||||
Shadings.Dummy = (function DummyClosure() {
|
|
||||||
function Dummy() {
|
|
||||||
this.type = "Pattern";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dummy.prototype = {
|
class DummyShading extends BaseShading {
|
||||||
getIR: function Dummy_getIR() {
|
getIR() {
|
||||||
return ["Dummy"];
|
return ["Dummy"];
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
return Dummy;
|
|
||||||
})();
|
|
||||||
|
|
||||||
function getTilingPatternIR(operatorList, dict, color) {
|
function getTilingPatternIR(operatorList, dict, color) {
|
||||||
const matrix = dict.getArray("Matrix");
|
const matrix = dict.getArray("Matrix");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user