Create the css color to use with the canvas in the worker
It slightly reduces the time spent to draw and the memory used.
This commit is contained in:
parent
60574fb7e3
commit
5789afd3f8
@ -21,6 +21,7 @@ import {
|
||||
MathClamp,
|
||||
shadow,
|
||||
unreachable,
|
||||
Util,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { BaseStream } from "./base_stream.js";
|
||||
@ -116,6 +117,8 @@ function copyRgbaImage(src, dest, alpha01) {
|
||||
}
|
||||
|
||||
class ColorSpace {
|
||||
static #rgbBuf = new Uint8ClampedArray(3);
|
||||
|
||||
constructor(name, numComps) {
|
||||
if (
|
||||
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
|
||||
@ -132,10 +135,14 @@ class ColorSpace {
|
||||
* located in the src array starting from the srcOffset. Returns the array
|
||||
* of the rgb components, each value ranging from [0,255].
|
||||
*/
|
||||
getRgb(src, srcOffset) {
|
||||
const rgb = new Uint8ClampedArray(3);
|
||||
this.getRgbItem(src, srcOffset, rgb, 0);
|
||||
return rgb;
|
||||
getRgb(src, srcOffset, output = new Uint8ClampedArray(3)) {
|
||||
this.getRgbItem(src, srcOffset, output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
getRgbHex(src, srcOffset) {
|
||||
const buffer = this.getRgb(src, srcOffset, ColorSpace.#rgbBuf);
|
||||
return Util.makeHexColor(buffer[0], buffer[1], buffer[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -507,7 +507,7 @@ class PartialEvaluator {
|
||||
|
||||
if (smask?.backdrop) {
|
||||
colorSpace ||= ColorSpaceUtils.rgb;
|
||||
smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);
|
||||
smask.backdrop = colorSpace.getRgbHex(smask.backdrop, 0);
|
||||
}
|
||||
|
||||
operatorList.addOp(OPS.beginGroup, [groupOptions]);
|
||||
@ -1546,7 +1546,7 @@ class PartialEvaluator {
|
||||
localTilingPatternCache.getByRef(rawPattern);
|
||||
if (localTilingPattern) {
|
||||
try {
|
||||
const color = cs.base ? cs.base.getRgb(args, 0) : null;
|
||||
const color = cs.base ? cs.base.getRgbHex(args, 0) : null;
|
||||
const tilingPatternIR = getTilingPatternIR(
|
||||
localTilingPattern.operatorListIR,
|
||||
localTilingPattern.dict,
|
||||
@ -1565,7 +1565,7 @@ class PartialEvaluator {
|
||||
const typeNum = dict.get("PatternType");
|
||||
|
||||
if (typeNum === PatternType.TILING) {
|
||||
const color = cs.base ? cs.base.getRgb(args, 0) : null;
|
||||
const color = cs.base ? cs.base.getRgbHex(args, 0) : null;
|
||||
return this.handleTilingType(
|
||||
fn,
|
||||
color,
|
||||
@ -2000,47 +2000,47 @@ class PartialEvaluator {
|
||||
}
|
||||
case OPS.setFillColor:
|
||||
cs = stateManager.state.fillColorSpace;
|
||||
args = cs.getRgb(args, 0);
|
||||
args = [cs.getRgbHex(args, 0)];
|
||||
fn = OPS.setFillRGBColor;
|
||||
break;
|
||||
case OPS.setStrokeColor:
|
||||
cs = stateManager.state.strokeColorSpace;
|
||||
args = cs.getRgb(args, 0);
|
||||
args = [cs.getRgbHex(args, 0)];
|
||||
fn = OPS.setStrokeRGBColor;
|
||||
break;
|
||||
case OPS.setFillGray:
|
||||
stateManager.state.fillColorSpace = ColorSpaceUtils.gray;
|
||||
args = ColorSpaceUtils.gray.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
|
||||
fn = OPS.setFillRGBColor;
|
||||
break;
|
||||
case OPS.setStrokeGray:
|
||||
stateManager.state.strokeColorSpace = ColorSpaceUtils.gray;
|
||||
args = ColorSpaceUtils.gray.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
|
||||
fn = OPS.setStrokeRGBColor;
|
||||
break;
|
||||
case OPS.setFillCMYKColor:
|
||||
stateManager.state.fillColorSpace = ColorSpaceUtils.cmyk;
|
||||
args = ColorSpaceUtils.cmyk.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.cmyk.getRgbHex(args, 0)];
|
||||
fn = OPS.setFillRGBColor;
|
||||
break;
|
||||
case OPS.setStrokeCMYKColor:
|
||||
stateManager.state.strokeColorSpace = ColorSpaceUtils.cmyk;
|
||||
args = ColorSpaceUtils.cmyk.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.cmyk.getRgbHex(args, 0)];
|
||||
fn = OPS.setStrokeRGBColor;
|
||||
break;
|
||||
case OPS.setFillRGBColor:
|
||||
stateManager.state.fillColorSpace = ColorSpaceUtils.rgb;
|
||||
args = ColorSpaceUtils.rgb.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.rgb.getRgbHex(args, 0)];
|
||||
break;
|
||||
case OPS.setStrokeRGBColor:
|
||||
stateManager.state.strokeColorSpace = ColorSpaceUtils.rgb;
|
||||
args = ColorSpaceUtils.rgb.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.rgb.getRgbHex(args, 0)];
|
||||
break;
|
||||
case OPS.setFillColorN:
|
||||
cs = stateManager.state.patternFillColorSpace;
|
||||
if (!cs) {
|
||||
if (isNumberArray(args, null)) {
|
||||
args = ColorSpaceUtils.gray.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
|
||||
fn = OPS.setFillRGBColor;
|
||||
break;
|
||||
}
|
||||
@ -2065,14 +2065,14 @@ class PartialEvaluator {
|
||||
);
|
||||
return;
|
||||
}
|
||||
args = cs.getRgb(args, 0);
|
||||
args = [cs.getRgbHex(args, 0)];
|
||||
fn = OPS.setFillRGBColor;
|
||||
break;
|
||||
case OPS.setStrokeColorN:
|
||||
cs = stateManager.state.patternStrokeColorSpace;
|
||||
if (!cs) {
|
||||
if (isNumberArray(args, null)) {
|
||||
args = ColorSpaceUtils.gray.getRgb(args, 0);
|
||||
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
|
||||
fn = OPS.setStrokeRGBColor;
|
||||
break;
|
||||
}
|
||||
@ -2097,7 +2097,7 @@ class PartialEvaluator {
|
||||
);
|
||||
return;
|
||||
}
|
||||
args = cs.getRgb(args, 0);
|
||||
args = [cs.getRgbHex(args, 0)];
|
||||
fn = OPS.setStrokeRGBColor;
|
||||
break;
|
||||
|
||||
|
||||
@ -198,19 +198,20 @@ class RadialAxialShading extends BaseShading {
|
||||
|
||||
const color = new Float32Array(cs.numComps),
|
||||
ratio = new Float32Array(1);
|
||||
let rgbColor;
|
||||
|
||||
let iBase = 0;
|
||||
ratio[0] = t0;
|
||||
fn(ratio, 0, color, 0);
|
||||
let rgbBase = cs.getRgb(color, 0);
|
||||
const cssColorBase = Util.makeHexColor(rgbBase[0], rgbBase[1], rgbBase[2]);
|
||||
colorStops.push([0, cssColorBase]);
|
||||
const rgbBuffer = new Uint8ClampedArray(3);
|
||||
cs.getRgb(color, 0, rgbBuffer);
|
||||
let [rBase, gBase, bBase] = rgbBuffer;
|
||||
colorStops.push([0, Util.makeHexColor(rBase, gBase, bBase)]);
|
||||
|
||||
let iPrev = 1;
|
||||
ratio[0] = t0 + step;
|
||||
fn(ratio, 0, color, 0);
|
||||
let rgbPrev = cs.getRgb(color, 0);
|
||||
cs.getRgb(color, 0, rgbBuffer);
|
||||
let [rPrev, gPrev, bPrev] = rgbBuffer;
|
||||
|
||||
// Slopes are rise / run.
|
||||
// A max slope is from the least value the base component could have been
|
||||
@ -221,28 +222,29 @@ class RadialAxialShading extends BaseShading {
|
||||
// so the conservative deltas are +-1 (+-.5 for base and -+.5 for current).
|
||||
|
||||
// The run is iPrev - iBase = 1, so omitted.
|
||||
let maxSlopeR = rgbPrev[0] - rgbBase[0] + 1;
|
||||
let maxSlopeG = rgbPrev[1] - rgbBase[1] + 1;
|
||||
let maxSlopeB = rgbPrev[2] - rgbBase[2] + 1;
|
||||
let minSlopeR = rgbPrev[0] - rgbBase[0] - 1;
|
||||
let minSlopeG = rgbPrev[1] - rgbBase[1] - 1;
|
||||
let minSlopeB = rgbPrev[2] - rgbBase[2] - 1;
|
||||
let maxSlopeR = rPrev - rBase + 1;
|
||||
let maxSlopeG = gPrev - gBase + 1;
|
||||
let maxSlopeB = bPrev - bBase + 1;
|
||||
let minSlopeR = rPrev - rBase - 1;
|
||||
let minSlopeG = gPrev - gBase - 1;
|
||||
let minSlopeB = bPrev - bBase - 1;
|
||||
|
||||
for (let i = 2; i < NUMBER_OF_SAMPLES; i++) {
|
||||
ratio[0] = t0 + i * step;
|
||||
fn(ratio, 0, color, 0);
|
||||
rgbColor = cs.getRgb(color, 0);
|
||||
cs.getRgb(color, 0, rgbBuffer);
|
||||
const [r, g, b] = rgbBuffer;
|
||||
|
||||
// Keep going if the maximum minimum slope <= the minimum maximum slope.
|
||||
// Otherwise add a rgbPrev color stop and make it the new base.
|
||||
|
||||
const run = i - iBase;
|
||||
maxSlopeR = Math.min(maxSlopeR, (rgbColor[0] - rgbBase[0] + 1) / run);
|
||||
maxSlopeG = Math.min(maxSlopeG, (rgbColor[1] - rgbBase[1] + 1) / run);
|
||||
maxSlopeB = Math.min(maxSlopeB, (rgbColor[2] - rgbBase[2] + 1) / run);
|
||||
minSlopeR = Math.max(minSlopeR, (rgbColor[0] - rgbBase[0] - 1) / run);
|
||||
minSlopeG = Math.max(minSlopeG, (rgbColor[1] - rgbBase[1] - 1) / run);
|
||||
minSlopeB = Math.max(minSlopeB, (rgbColor[2] - rgbBase[2] - 1) / run);
|
||||
maxSlopeR = Math.min(maxSlopeR, (r - rBase + 1) / run);
|
||||
maxSlopeG = Math.min(maxSlopeG, (g - gBase + 1) / run);
|
||||
maxSlopeB = Math.min(maxSlopeB, (b - bBase + 1) / run);
|
||||
minSlopeR = Math.max(minSlopeR, (r - rBase - 1) / run);
|
||||
minSlopeG = Math.max(minSlopeG, (g - gBase - 1) / run);
|
||||
minSlopeB = Math.max(minSlopeB, (b - bBase - 1) / run);
|
||||
|
||||
const slopesExist =
|
||||
minSlopeR <= maxSlopeR &&
|
||||
@ -250,34 +252,36 @@ class RadialAxialShading extends BaseShading {
|
||||
minSlopeB <= maxSlopeB;
|
||||
|
||||
if (!slopesExist) {
|
||||
const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);
|
||||
const cssColor = Util.makeHexColor(rPrev, gPrev, bPrev);
|
||||
colorStops.push([iPrev / NUMBER_OF_SAMPLES, cssColor]);
|
||||
|
||||
// TODO: When fn frequency is high (iPrev - iBase === 1 twice in a row),
|
||||
// send the color space and function to do the sampling display side.
|
||||
|
||||
// The run is i - iPrev = 1, so omitted.
|
||||
maxSlopeR = rgbColor[0] - rgbPrev[0] + 1;
|
||||
maxSlopeG = rgbColor[1] - rgbPrev[1] + 1;
|
||||
maxSlopeB = rgbColor[2] - rgbPrev[2] + 1;
|
||||
minSlopeR = rgbColor[0] - rgbPrev[0] - 1;
|
||||
minSlopeG = rgbColor[1] - rgbPrev[1] - 1;
|
||||
minSlopeB = rgbColor[2] - rgbPrev[2] - 1;
|
||||
maxSlopeR = r - rPrev + 1;
|
||||
maxSlopeG = g - gPrev + 1;
|
||||
maxSlopeB = b - bPrev + 1;
|
||||
minSlopeR = r - rPrev - 1;
|
||||
minSlopeG = g - gPrev - 1;
|
||||
minSlopeB = b - bPrev - 1;
|
||||
|
||||
iBase = iPrev;
|
||||
rgbBase = rgbPrev;
|
||||
rBase = rPrev;
|
||||
gBase = gPrev;
|
||||
bBase = bPrev;
|
||||
}
|
||||
|
||||
iPrev = i;
|
||||
rgbPrev = rgbColor;
|
||||
rPrev = r;
|
||||
gPrev = g;
|
||||
bPrev = b;
|
||||
}
|
||||
const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);
|
||||
colorStops.push([1, cssColor]);
|
||||
colorStops.push([1, Util.makeHexColor(rPrev, gPrev, bPrev)]);
|
||||
|
||||
let background = "transparent";
|
||||
if (dict.has("Background")) {
|
||||
rgbColor = cs.getRgb(dict.get("Background"), 0);
|
||||
background = Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]);
|
||||
background = cs.getRgbHex(dict.get("Background"), 0);
|
||||
}
|
||||
|
||||
if (!extendStart) {
|
||||
|
||||
@ -1311,7 +1311,6 @@ class CanvasGraphics {
|
||||
let maskY = layerOffsetY - maskOffsetY;
|
||||
|
||||
if (backdrop) {
|
||||
const backdropRGB = Util.makeHexColor(...backdrop);
|
||||
if (
|
||||
maskX < 0 ||
|
||||
maskY < 0 ||
|
||||
@ -1326,7 +1325,7 @@ class CanvasGraphics {
|
||||
const ctx = canvas.context;
|
||||
ctx.drawImage(maskCanvas, -maskX, -maskY);
|
||||
ctx.globalCompositeOperation = "destination-atop";
|
||||
ctx.fillStyle = backdropRGB;
|
||||
ctx.fillStyle = backdrop;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
|
||||
@ -1340,7 +1339,7 @@ class CanvasGraphics {
|
||||
clip.rect(maskX, maskY, width, height);
|
||||
maskCtx.clip(clip);
|
||||
maskCtx.globalCompositeOperation = "destination-atop";
|
||||
maskCtx.fillStyle = backdropRGB;
|
||||
maskCtx.fillStyle = backdrop;
|
||||
maskCtx.fillRect(maskX, maskY, width, height);
|
||||
maskCtx.restore();
|
||||
}
|
||||
@ -2193,12 +2192,8 @@ class CanvasGraphics {
|
||||
this.current.patternFill = true;
|
||||
}
|
||||
|
||||
setStrokeRGBColor(r, g, b) {
|
||||
this.ctx.strokeStyle = this.current.strokeColor = Util.makeHexColor(
|
||||
r,
|
||||
g,
|
||||
b
|
||||
);
|
||||
setStrokeRGBColor(color) {
|
||||
this.ctx.strokeStyle = this.current.strokeColor = color;
|
||||
this.current.patternStroke = false;
|
||||
}
|
||||
|
||||
@ -2207,8 +2202,8 @@ class CanvasGraphics {
|
||||
this.current.patternStroke = false;
|
||||
}
|
||||
|
||||
setFillRGBColor(r, g, b) {
|
||||
this.ctx.fillStyle = this.current.fillColor = Util.makeHexColor(r, g, b);
|
||||
setFillRGBColor(color) {
|
||||
this.ctx.fillStyle = this.current.fillColor = color;
|
||||
this.current.patternFill = false;
|
||||
}
|
||||
|
||||
|
||||
@ -694,19 +694,14 @@ class TilingPattern {
|
||||
current = graphics.current;
|
||||
switch (paintType) {
|
||||
case PaintType.COLORED:
|
||||
const ctx = this.ctx;
|
||||
context.fillStyle = ctx.fillStyle;
|
||||
context.strokeStyle = ctx.strokeStyle;
|
||||
current.fillColor = ctx.fillStyle;
|
||||
current.strokeColor = ctx.strokeStyle;
|
||||
const { fillStyle, strokeStyle } = this.ctx;
|
||||
context.fillStyle = current.fillColor = fillStyle;
|
||||
context.strokeStyle = current.strokeColor = strokeStyle;
|
||||
break;
|
||||
case PaintType.UNCOLORED:
|
||||
const cssColor = Util.makeHexColor(color[0], color[1], color[2]);
|
||||
context.fillStyle = cssColor;
|
||||
context.strokeStyle = cssColor;
|
||||
context.fillStyle = context.strokeStyle = color;
|
||||
// Set color needed by image masks (fixes issues 3226 and 8741).
|
||||
current.fillColor = cssColor;
|
||||
current.strokeColor = cssColor;
|
||||
current.fillColor = current.strokeColor = color;
|
||||
break;
|
||||
default:
|
||||
throw new FormatError(`Unsupported paint type: ${paintType}`);
|
||||
|
||||
@ -1783,7 +1783,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
|
||||
expect(opList.argsArray[1]).toEqual(["#1a334c"]);
|
||||
});
|
||||
|
||||
it("should render auto-sized text for printing", async function () {
|
||||
@ -2664,7 +2664,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList1.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
|
||||
expect(opList1.argsArray[1]).toEqual(["#1a334c"]);
|
||||
|
||||
annotationStorage.set(annotation.data.id, { value: false });
|
||||
|
||||
@ -2687,7 +2687,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList2.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
|
||||
expect(opList2.argsArray[1]).toEqual(["#4c331a"]);
|
||||
});
|
||||
|
||||
it("should render checkboxes for printing twice", async function () {
|
||||
@ -2748,9 +2748,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList.argsArray[1]).toEqual(
|
||||
new Uint8ClampedArray([26, 51, 76])
|
||||
);
|
||||
expect(opList.argsArray[1]).toEqual(["#1a334c"]);
|
||||
}
|
||||
});
|
||||
|
||||
@ -2809,7 +2807,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
|
||||
expect(opList.argsArray[1]).toEqual(["#1a334c"]);
|
||||
});
|
||||
|
||||
it("should save checkboxes", async function () {
|
||||
@ -3052,7 +3050,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList1.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
|
||||
expect(opList1.argsArray[1]).toEqual(["#1a334c"]);
|
||||
|
||||
annotationStorage.set(annotation.data.id, { value: false });
|
||||
|
||||
@ -3075,7 +3073,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList2.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
|
||||
expect(opList2.argsArray[1]).toEqual(["#4c331a"]);
|
||||
});
|
||||
|
||||
it("should render radio buttons for printing using normal appearance", async function () {
|
||||
@ -3134,7 +3132,7 @@ describe("annotation", function () {
|
||||
[1, 0, 0, 1, 0, 0],
|
||||
false,
|
||||
]);
|
||||
expect(opList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
|
||||
expect(opList.argsArray[1]).toEqual(["#4c331a"]);
|
||||
});
|
||||
|
||||
it("should save radio buttons", async function () {
|
||||
@ -4677,7 +4675,7 @@ describe("annotation", function () {
|
||||
// LineJoin.
|
||||
expect(opList.argsArray[3]).toEqual([1]);
|
||||
// Color.
|
||||
expect(opList.argsArray[4]).toEqual(new Uint8ClampedArray([0, 255, 0]));
|
||||
expect(opList.argsArray[4]).toEqual(["#00ff00"]);
|
||||
// Path.
|
||||
expect(opList.argsArray[5][0]).toEqual(OPS.stroke);
|
||||
expect(opList.argsArray[5][1]).toEqual([
|
||||
|
||||
@ -798,7 +798,7 @@ describe("api", function () {
|
||||
]);
|
||||
expect(opList.argsArray).toEqual([
|
||||
[0.5],
|
||||
new Uint8ClampedArray([255, 0, 0]),
|
||||
["#ff0000"],
|
||||
[
|
||||
OPS.closeStroke,
|
||||
[
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user