Skip to content

Commit 5789afd

Browse files
committed
Create the css color to use with the canvas in the worker
It slightly reduces the time spent to draw and the memory used.
1 parent 60574fb commit 5789afd

File tree

7 files changed

+82
-83
lines changed

7 files changed

+82
-83
lines changed

src/core/colorspace.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
MathClamp,
2222
shadow,
2323
unreachable,
24+
Util,
2425
warn,
2526
} from "../shared/util.js";
2627
import { BaseStream } from "./base_stream.js";
@@ -116,6 +117,8 @@ function copyRgbaImage(src, dest, alpha01) {
116117
}
117118

118119
class ColorSpace {
120+
static #rgbBuf = new Uint8ClampedArray(3);
121+
119122
constructor(name, numComps) {
120123
if (
121124
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
@@ -132,10 +135,14 @@ class ColorSpace {
132135
* located in the src array starting from the srcOffset. Returns the array
133136
* of the rgb components, each value ranging from [0,255].
134137
*/
135-
getRgb(src, srcOffset) {
136-
const rgb = new Uint8ClampedArray(3);
137-
this.getRgbItem(src, srcOffset, rgb, 0);
138-
return rgb;
138+
getRgb(src, srcOffset, output = new Uint8ClampedArray(3)) {
139+
this.getRgbItem(src, srcOffset, output, 0);
140+
return output;
141+
}
142+
143+
getRgbHex(src, srcOffset) {
144+
const buffer = this.getRgb(src, srcOffset, ColorSpace.#rgbBuf);
145+
return Util.makeHexColor(buffer[0], buffer[1], buffer[2]);
139146
}
140147

141148
/**

src/core/evaluator.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ class PartialEvaluator {
507507

508508
if (smask?.backdrop) {
509509
colorSpace ||= ColorSpaceUtils.rgb;
510-
smask.backdrop = colorSpace.getRgb(smask.backdrop, 0);
510+
smask.backdrop = colorSpace.getRgbHex(smask.backdrop, 0);
511511
}
512512

513513
operatorList.addOp(OPS.beginGroup, [groupOptions]);
@@ -1546,7 +1546,7 @@ class PartialEvaluator {
15461546
localTilingPatternCache.getByRef(rawPattern);
15471547
if (localTilingPattern) {
15481548
try {
1549-
const color = cs.base ? cs.base.getRgb(args, 0) : null;
1549+
const color = cs.base ? cs.base.getRgbHex(args, 0) : null;
15501550
const tilingPatternIR = getTilingPatternIR(
15511551
localTilingPattern.operatorListIR,
15521552
localTilingPattern.dict,
@@ -1565,7 +1565,7 @@ class PartialEvaluator {
15651565
const typeNum = dict.get("PatternType");
15661566

15671567
if (typeNum === PatternType.TILING) {
1568-
const color = cs.base ? cs.base.getRgb(args, 0) : null;
1568+
const color = cs.base ? cs.base.getRgbHex(args, 0) : null;
15691569
return this.handleTilingType(
15701570
fn,
15711571
color,
@@ -2000,47 +2000,47 @@ class PartialEvaluator {
20002000
}
20012001
case OPS.setFillColor:
20022002
cs = stateManager.state.fillColorSpace;
2003-
args = cs.getRgb(args, 0);
2003+
args = [cs.getRgbHex(args, 0)];
20042004
fn = OPS.setFillRGBColor;
20052005
break;
20062006
case OPS.setStrokeColor:
20072007
cs = stateManager.state.strokeColorSpace;
2008-
args = cs.getRgb(args, 0);
2008+
args = [cs.getRgbHex(args, 0)];
20092009
fn = OPS.setStrokeRGBColor;
20102010
break;
20112011
case OPS.setFillGray:
20122012
stateManager.state.fillColorSpace = ColorSpaceUtils.gray;
2013-
args = ColorSpaceUtils.gray.getRgb(args, 0);
2013+
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
20142014
fn = OPS.setFillRGBColor;
20152015
break;
20162016
case OPS.setStrokeGray:
20172017
stateManager.state.strokeColorSpace = ColorSpaceUtils.gray;
2018-
args = ColorSpaceUtils.gray.getRgb(args, 0);
2018+
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
20192019
fn = OPS.setStrokeRGBColor;
20202020
break;
20212021
case OPS.setFillCMYKColor:
20222022
stateManager.state.fillColorSpace = ColorSpaceUtils.cmyk;
2023-
args = ColorSpaceUtils.cmyk.getRgb(args, 0);
2023+
args = [ColorSpaceUtils.cmyk.getRgbHex(args, 0)];
20242024
fn = OPS.setFillRGBColor;
20252025
break;
20262026
case OPS.setStrokeCMYKColor:
20272027
stateManager.state.strokeColorSpace = ColorSpaceUtils.cmyk;
2028-
args = ColorSpaceUtils.cmyk.getRgb(args, 0);
2028+
args = [ColorSpaceUtils.cmyk.getRgbHex(args, 0)];
20292029
fn = OPS.setStrokeRGBColor;
20302030
break;
20312031
case OPS.setFillRGBColor:
20322032
stateManager.state.fillColorSpace = ColorSpaceUtils.rgb;
2033-
args = ColorSpaceUtils.rgb.getRgb(args, 0);
2033+
args = [ColorSpaceUtils.rgb.getRgbHex(args, 0)];
20342034
break;
20352035
case OPS.setStrokeRGBColor:
20362036
stateManager.state.strokeColorSpace = ColorSpaceUtils.rgb;
2037-
args = ColorSpaceUtils.rgb.getRgb(args, 0);
2037+
args = [ColorSpaceUtils.rgb.getRgbHex(args, 0)];
20382038
break;
20392039
case OPS.setFillColorN:
20402040
cs = stateManager.state.patternFillColorSpace;
20412041
if (!cs) {
20422042
if (isNumberArray(args, null)) {
2043-
args = ColorSpaceUtils.gray.getRgb(args, 0);
2043+
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
20442044
fn = OPS.setFillRGBColor;
20452045
break;
20462046
}
@@ -2065,14 +2065,14 @@ class PartialEvaluator {
20652065
);
20662066
return;
20672067
}
2068-
args = cs.getRgb(args, 0);
2068+
args = [cs.getRgbHex(args, 0)];
20692069
fn = OPS.setFillRGBColor;
20702070
break;
20712071
case OPS.setStrokeColorN:
20722072
cs = stateManager.state.patternStrokeColorSpace;
20732073
if (!cs) {
20742074
if (isNumberArray(args, null)) {
2075-
args = ColorSpaceUtils.gray.getRgb(args, 0);
2075+
args = [ColorSpaceUtils.gray.getRgbHex(args, 0)];
20762076
fn = OPS.setStrokeRGBColor;
20772077
break;
20782078
}
@@ -2097,7 +2097,7 @@ class PartialEvaluator {
20972097
);
20982098
return;
20992099
}
2100-
args = cs.getRgb(args, 0);
2100+
args = [cs.getRgbHex(args, 0)];
21012101
fn = OPS.setStrokeRGBColor;
21022102
break;
21032103

src/core/pattern.js

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -198,19 +198,20 @@ class RadialAxialShading extends BaseShading {
198198

199199
const color = new Float32Array(cs.numComps),
200200
ratio = new Float32Array(1);
201-
let rgbColor;
202201

203202
let iBase = 0;
204203
ratio[0] = t0;
205204
fn(ratio, 0, color, 0);
206-
let rgbBase = cs.getRgb(color, 0);
207-
const cssColorBase = Util.makeHexColor(rgbBase[0], rgbBase[1], rgbBase[2]);
208-
colorStops.push([0, cssColorBase]);
205+
const rgbBuffer = new Uint8ClampedArray(3);
206+
cs.getRgb(color, 0, rgbBuffer);
207+
let [rBase, gBase, bBase] = rgbBuffer;
208+
colorStops.push([0, Util.makeHexColor(rBase, gBase, bBase)]);
209209

210210
let iPrev = 1;
211211
ratio[0] = t0 + step;
212212
fn(ratio, 0, color, 0);
213-
let rgbPrev = cs.getRgb(color, 0);
213+
cs.getRgb(color, 0, rgbBuffer);
214+
let [rPrev, gPrev, bPrev] = rgbBuffer;
214215

215216
// Slopes are rise / run.
216217
// A max slope is from the least value the base component could have been
@@ -221,63 +222,66 @@ class RadialAxialShading extends BaseShading {
221222
// so the conservative deltas are +-1 (+-.5 for base and -+.5 for current).
222223

223224
// The run is iPrev - iBase = 1, so omitted.
224-
let maxSlopeR = rgbPrev[0] - rgbBase[0] + 1;
225-
let maxSlopeG = rgbPrev[1] - rgbBase[1] + 1;
226-
let maxSlopeB = rgbPrev[2] - rgbBase[2] + 1;
227-
let minSlopeR = rgbPrev[0] - rgbBase[0] - 1;
228-
let minSlopeG = rgbPrev[1] - rgbBase[1] - 1;
229-
let minSlopeB = rgbPrev[2] - rgbBase[2] - 1;
225+
let maxSlopeR = rPrev - rBase + 1;
226+
let maxSlopeG = gPrev - gBase + 1;
227+
let maxSlopeB = bPrev - bBase + 1;
228+
let minSlopeR = rPrev - rBase - 1;
229+
let minSlopeG = gPrev - gBase - 1;
230+
let minSlopeB = bPrev - bBase - 1;
230231

231232
for (let i = 2; i < NUMBER_OF_SAMPLES; i++) {
232233
ratio[0] = t0 + i * step;
233234
fn(ratio, 0, color, 0);
234-
rgbColor = cs.getRgb(color, 0);
235+
cs.getRgb(color, 0, rgbBuffer);
236+
const [r, g, b] = rgbBuffer;
235237

236238
// Keep going if the maximum minimum slope <= the minimum maximum slope.
237239
// Otherwise add a rgbPrev color stop and make it the new base.
238240

239241
const run = i - iBase;
240-
maxSlopeR = Math.min(maxSlopeR, (rgbColor[0] - rgbBase[0] + 1) / run);
241-
maxSlopeG = Math.min(maxSlopeG, (rgbColor[1] - rgbBase[1] + 1) / run);
242-
maxSlopeB = Math.min(maxSlopeB, (rgbColor[2] - rgbBase[2] + 1) / run);
243-
minSlopeR = Math.max(minSlopeR, (rgbColor[0] - rgbBase[0] - 1) / run);
244-
minSlopeG = Math.max(minSlopeG, (rgbColor[1] - rgbBase[1] - 1) / run);
245-
minSlopeB = Math.max(minSlopeB, (rgbColor[2] - rgbBase[2] - 1) / run);
242+
maxSlopeR = Math.min(maxSlopeR, (r - rBase + 1) / run);
243+
maxSlopeG = Math.min(maxSlopeG, (g - gBase + 1) / run);
244+
maxSlopeB = Math.min(maxSlopeB, (b - bBase + 1) / run);
245+
minSlopeR = Math.max(minSlopeR, (r - rBase - 1) / run);
246+
minSlopeG = Math.max(minSlopeG, (g - gBase - 1) / run);
247+
minSlopeB = Math.max(minSlopeB, (b - bBase - 1) / run);
246248

247249
const slopesExist =
248250
minSlopeR <= maxSlopeR &&
249251
minSlopeG <= maxSlopeG &&
250252
minSlopeB <= maxSlopeB;
251253

252254
if (!slopesExist) {
253-
const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);
255+
const cssColor = Util.makeHexColor(rPrev, gPrev, bPrev);
254256
colorStops.push([iPrev / NUMBER_OF_SAMPLES, cssColor]);
255257

256258
// TODO: When fn frequency is high (iPrev - iBase === 1 twice in a row),
257259
// send the color space and function to do the sampling display side.
258260

259261
// The run is i - iPrev = 1, so omitted.
260-
maxSlopeR = rgbColor[0] - rgbPrev[0] + 1;
261-
maxSlopeG = rgbColor[1] - rgbPrev[1] + 1;
262-
maxSlopeB = rgbColor[2] - rgbPrev[2] + 1;
263-
minSlopeR = rgbColor[0] - rgbPrev[0] - 1;
264-
minSlopeG = rgbColor[1] - rgbPrev[1] - 1;
265-
minSlopeB = rgbColor[2] - rgbPrev[2] - 1;
262+
maxSlopeR = r - rPrev + 1;
263+
maxSlopeG = g - gPrev + 1;
264+
maxSlopeB = b - bPrev + 1;
265+
minSlopeR = r - rPrev - 1;
266+
minSlopeG = g - gPrev - 1;
267+
minSlopeB = b - bPrev - 1;
266268

267269
iBase = iPrev;
268-
rgbBase = rgbPrev;
270+
rBase = rPrev;
271+
gBase = gPrev;
272+
bBase = bPrev;
269273
}
270274

271275
iPrev = i;
272-
rgbPrev = rgbColor;
276+
rPrev = r;
277+
gPrev = g;
278+
bPrev = b;
273279
}
274-
const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);
275-
colorStops.push([1, cssColor]);
280+
colorStops.push([1, Util.makeHexColor(rPrev, gPrev, bPrev)]);
276281

277282
let background = "transparent";
278283
if (dict.has("Background")) {
279-
rgbColor = cs.getRgb(dict.get("Background"), 0);
280-
background = Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]);
284+
background = cs.getRgbHex(dict.get("Background"), 0);
281285
}
282286

283287
if (!extendStart) {

src/display/canvas.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,6 @@ class CanvasGraphics {
13111311
let maskY = layerOffsetY - maskOffsetY;
13121312

13131313
if (backdrop) {
1314-
const backdropRGB = Util.makeHexColor(...backdrop);
13151314
if (
13161315
maskX < 0 ||
13171316
maskY < 0 ||
@@ -1326,7 +1325,7 @@ class CanvasGraphics {
13261325
const ctx = canvas.context;
13271326
ctx.drawImage(maskCanvas, -maskX, -maskY);
13281327
ctx.globalCompositeOperation = "destination-atop";
1329-
ctx.fillStyle = backdropRGB;
1328+
ctx.fillStyle = backdrop;
13301329
ctx.fillRect(0, 0, width, height);
13311330
ctx.globalCompositeOperation = "source-over";
13321331

@@ -1340,7 +1339,7 @@ class CanvasGraphics {
13401339
clip.rect(maskX, maskY, width, height);
13411340
maskCtx.clip(clip);
13421341
maskCtx.globalCompositeOperation = "destination-atop";
1343-
maskCtx.fillStyle = backdropRGB;
1342+
maskCtx.fillStyle = backdrop;
13441343
maskCtx.fillRect(maskX, maskY, width, height);
13451344
maskCtx.restore();
13461345
}
@@ -2193,12 +2192,8 @@ class CanvasGraphics {
21932192
this.current.patternFill = true;
21942193
}
21952194

2196-
setStrokeRGBColor(r, g, b) {
2197-
this.ctx.strokeStyle = this.current.strokeColor = Util.makeHexColor(
2198-
r,
2199-
g,
2200-
b
2201-
);
2195+
setStrokeRGBColor(color) {
2196+
this.ctx.strokeStyle = this.current.strokeColor = color;
22022197
this.current.patternStroke = false;
22032198
}
22042199

@@ -2207,8 +2202,8 @@ class CanvasGraphics {
22072202
this.current.patternStroke = false;
22082203
}
22092204

2210-
setFillRGBColor(r, g, b) {
2211-
this.ctx.fillStyle = this.current.fillColor = Util.makeHexColor(r, g, b);
2205+
setFillRGBColor(color) {
2206+
this.ctx.fillStyle = this.current.fillColor = color;
22122207
this.current.patternFill = false;
22132208
}
22142209

src/display/pattern_helper.js

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -694,19 +694,14 @@ class TilingPattern {
694694
current = graphics.current;
695695
switch (paintType) {
696696
case PaintType.COLORED:
697-
const ctx = this.ctx;
698-
context.fillStyle = ctx.fillStyle;
699-
context.strokeStyle = ctx.strokeStyle;
700-
current.fillColor = ctx.fillStyle;
701-
current.strokeColor = ctx.strokeStyle;
697+
const { fillStyle, strokeStyle } = this.ctx;
698+
context.fillStyle = current.fillColor = fillStyle;
699+
context.strokeStyle = current.strokeColor = strokeStyle;
702700
break;
703701
case PaintType.UNCOLORED:
704-
const cssColor = Util.makeHexColor(color[0], color[1], color[2]);
705-
context.fillStyle = cssColor;
706-
context.strokeStyle = cssColor;
702+
context.fillStyle = context.strokeStyle = color;
707703
// Set color needed by image masks (fixes issues 3226 and 8741).
708-
current.fillColor = cssColor;
709-
current.strokeColor = cssColor;
704+
current.fillColor = current.strokeColor = color;
710705
break;
711706
default:
712707
throw new FormatError(`Unsupported paint type: ${paintType}`);

0 commit comments

Comments
 (0)