mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-26 10:08:06 +02:00
[api-minor] Generate images in the worker instead of the main thread.
We introduced the use of OffscreenCanvas in #14754 and this patch aims to use them for all kind of images. It'll slightly improve performances (and maybe slightly decrease memory use). Since an image can be rendered in using some transfer maps but because of OffscreenCanvas we don't have the underlying pixels array the transfer maps stuff is re-implemented in using the SVG filter feComponentTransfer.
This commit is contained in:
parent
9640add1f7
commit
fd03cd5493
13 changed files with 700 additions and 89 deletions
|
@ -13,8 +13,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assert, FormatError, ImageKind, info, warn } from "../shared/util.js";
|
||||
import { applyMaskImageData } from "../shared/image_utils.js";
|
||||
import {
|
||||
assert,
|
||||
FeatureTest,
|
||||
FormatError,
|
||||
ImageKind,
|
||||
info,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import {
|
||||
convertBlackAndWhiteToRGBA,
|
||||
convertToRGBA,
|
||||
} from "../shared/image_utils.js";
|
||||
import { BaseStream } from "./base_stream.js";
|
||||
import { ColorSpace } from "./colorspace.js";
|
||||
import { DecodeStream } from "./decode_stream.js";
|
||||
|
@ -364,11 +374,12 @@ class PDFImage {
|
|||
const canvas = new OffscreenCanvas(width, height);
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imgData = ctx.createImageData(width, height);
|
||||
applyMaskImageData({
|
||||
convertBlackAndWhiteToRGBA({
|
||||
src: imgArray,
|
||||
dest: imgData.data,
|
||||
width,
|
||||
height,
|
||||
nonBlackColor: 0,
|
||||
inverseDecode,
|
||||
});
|
||||
|
||||
|
@ -641,7 +652,7 @@ class PDFImage {
|
|||
}
|
||||
}
|
||||
|
||||
createImageData(forceRGBA = false) {
|
||||
createImageData(forceRGBA = false, isOffscreenCanvasSupported = false) {
|
||||
const drawWidth = this.drawWidth;
|
||||
const drawHeight = this.drawHeight;
|
||||
const imgData = {
|
||||
|
@ -686,8 +697,12 @@ class PDFImage {
|
|||
drawWidth === originalWidth &&
|
||||
drawHeight === originalHeight
|
||||
) {
|
||||
const data = this.getImageBytes(originalHeight * rowBytes, {});
|
||||
if (isOffscreenCanvasSupported) {
|
||||
return this.createBitmap(kind, originalWidth, originalHeight, data);
|
||||
}
|
||||
imgData.kind = kind;
|
||||
imgData.data = this.getImageBytes(originalHeight * rowBytes, {});
|
||||
imgData.data = data;
|
||||
|
||||
if (this.needsDecode) {
|
||||
// Invert the buffer (which must be grayscale if we reached here).
|
||||
|
@ -704,21 +719,52 @@ class PDFImage {
|
|||
}
|
||||
if (this.image instanceof JpegStream && !this.smask && !this.mask) {
|
||||
let imageLength = originalHeight * rowBytes;
|
||||
switch (this.colorSpace.name) {
|
||||
case "DeviceGray":
|
||||
// Avoid truncating the image, since `JpegImage.getData`
|
||||
// will expand the image data when `forceRGB === true`.
|
||||
imageLength *= 3;
|
||||
/* falls through */
|
||||
case "DeviceRGB":
|
||||
case "DeviceCMYK":
|
||||
imgData.kind = ImageKind.RGB_24BPP;
|
||||
imgData.data = this.getImageBytes(imageLength, {
|
||||
if (isOffscreenCanvasSupported) {
|
||||
let isHandled = false;
|
||||
switch (this.colorSpace.name) {
|
||||
case "DeviceGray":
|
||||
// Avoid truncating the image, since `JpegImage.getData`
|
||||
// will expand the image data when `forceRGB === true`.
|
||||
imageLength *= 4;
|
||||
isHandled = true;
|
||||
break;
|
||||
case "DeviceRGB":
|
||||
imageLength = (imageLength / 3) * 4;
|
||||
isHandled = true;
|
||||
break;
|
||||
case "DeviceCMYK":
|
||||
isHandled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isHandled) {
|
||||
const rgba = this.getImageBytes(imageLength, {
|
||||
drawWidth,
|
||||
drawHeight,
|
||||
forceRGB: true,
|
||||
forceRGBA: true,
|
||||
});
|
||||
return imgData;
|
||||
return this.createBitmap(
|
||||
ImageKind.RGBA_32BPP,
|
||||
drawWidth,
|
||||
drawHeight,
|
||||
rgba
|
||||
);
|
||||
}
|
||||
} else {
|
||||
switch (this.colorSpace.name) {
|
||||
case "DeviceGray":
|
||||
imageLength *= 3;
|
||||
/* falls through */
|
||||
case "DeviceRGB":
|
||||
case "DeviceCMYK":
|
||||
imgData.kind = ImageKind.RGB_24BPP;
|
||||
imgData.data = this.getImageBytes(imageLength, {
|
||||
drawWidth,
|
||||
drawHeight,
|
||||
forceRGB: true,
|
||||
});
|
||||
return imgData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -735,32 +781,45 @@ class PDFImage {
|
|||
// If opacity data is present, use RGBA_32BPP form. Otherwise, use the
|
||||
// more compact RGB_24BPP form if allowable.
|
||||
let alpha01, maybeUndoPreblend;
|
||||
|
||||
let canvas, ctx, canvasImgData, data;
|
||||
if (isOffscreenCanvasSupported) {
|
||||
canvas = new OffscreenCanvas(drawWidth, drawHeight);
|
||||
ctx = canvas.getContext("2d");
|
||||
canvasImgData = ctx.createImageData(drawWidth, drawHeight);
|
||||
data = canvasImgData.data;
|
||||
}
|
||||
|
||||
imgData.kind = ImageKind.RGBA_32BPP;
|
||||
|
||||
if (!forceRGBA && !this.smask && !this.mask) {
|
||||
imgData.kind = ImageKind.RGB_24BPP;
|
||||
imgData.data = new Uint8ClampedArray(drawWidth * drawHeight * 3);
|
||||
alpha01 = 0;
|
||||
if (!isOffscreenCanvasSupported) {
|
||||
imgData.kind = ImageKind.RGB_24BPP;
|
||||
data = new Uint8ClampedArray(drawWidth * drawHeight * 3);
|
||||
alpha01 = 0;
|
||||
} else {
|
||||
const arr = new Uint32Array(data.buffer);
|
||||
arr.fill(FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff);
|
||||
alpha01 = 1;
|
||||
}
|
||||
maybeUndoPreblend = false;
|
||||
} else {
|
||||
imgData.kind = ImageKind.RGBA_32BPP;
|
||||
imgData.data = new Uint8ClampedArray(drawWidth * drawHeight * 4);
|
||||
if (!isOffscreenCanvasSupported) {
|
||||
data = new Uint8ClampedArray(drawWidth * drawHeight * 4);
|
||||
}
|
||||
|
||||
alpha01 = 1;
|
||||
maybeUndoPreblend = true;
|
||||
|
||||
// Color key masking (opacity) must be performed before decoding.
|
||||
this.fillOpacity(
|
||||
imgData.data,
|
||||
drawWidth,
|
||||
drawHeight,
|
||||
actualHeight,
|
||||
comps
|
||||
);
|
||||
this.fillOpacity(data, drawWidth, drawHeight, actualHeight, comps);
|
||||
}
|
||||
|
||||
if (this.needsDecode) {
|
||||
this.decodeBuffer(comps);
|
||||
}
|
||||
this.colorSpace.fillRgb(
|
||||
imgData.data,
|
||||
data,
|
||||
originalWidth,
|
||||
originalHeight,
|
||||
drawWidth,
|
||||
|
@ -771,9 +830,23 @@ class PDFImage {
|
|||
alpha01
|
||||
);
|
||||
if (maybeUndoPreblend) {
|
||||
this.undoPreblend(imgData.data, drawWidth, actualHeight);
|
||||
this.undoPreblend(data, drawWidth, actualHeight);
|
||||
}
|
||||
|
||||
if (isOffscreenCanvasSupported) {
|
||||
ctx.putImageData(canvasImgData, 0, 0);
|
||||
const bitmap = canvas.transferToImageBitmap();
|
||||
|
||||
return {
|
||||
data: null,
|
||||
width: drawWidth,
|
||||
height: drawHeight,
|
||||
bitmap,
|
||||
interpolate: this.interpolate,
|
||||
};
|
||||
}
|
||||
|
||||
imgData.data = data;
|
||||
return imgData;
|
||||
}
|
||||
|
||||
|
@ -833,13 +906,49 @@ class PDFImage {
|
|||
}
|
||||
}
|
||||
|
||||
createBitmap(kind, width, height, src) {
|
||||
const canvas = new OffscreenCanvas(width, height);
|
||||
const ctx = canvas.getContext("2d");
|
||||
let imgData;
|
||||
if (kind === ImageKind.RGBA_32BPP) {
|
||||
imgData = new ImageData(src, width, height);
|
||||
} else {
|
||||
imgData = ctx.createImageData(width, height);
|
||||
convertToRGBA({
|
||||
kind,
|
||||
src,
|
||||
dest: new Uint32Array(imgData.data.buffer),
|
||||
width,
|
||||
height,
|
||||
inverseDecode: this.needsDecode,
|
||||
});
|
||||
}
|
||||
ctx.putImageData(imgData, 0, 0);
|
||||
const bitmap = canvas.transferToImageBitmap();
|
||||
|
||||
return {
|
||||
data: null,
|
||||
width,
|
||||
height,
|
||||
bitmap,
|
||||
interpolate: this.interpolate,
|
||||
};
|
||||
}
|
||||
|
||||
getImageBytes(
|
||||
length,
|
||||
{ drawWidth, drawHeight, forceRGB = false, internal = false }
|
||||
{
|
||||
drawWidth,
|
||||
drawHeight,
|
||||
forceRGBA = false,
|
||||
forceRGB = false,
|
||||
internal = false,
|
||||
}
|
||||
) {
|
||||
this.image.reset();
|
||||
this.image.drawWidth = drawWidth || this.width;
|
||||
this.image.drawHeight = drawHeight || this.height;
|
||||
this.image.forceRGBA = !!forceRGBA;
|
||||
this.image.forceRGB = !!forceRGB;
|
||||
const imageBytes = this.image.getBytes(length);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue