mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-23 08:38:06 +02:00
parent
1f6cc85134
commit
1ef670411a
7 changed files with 136 additions and 10 deletions
|
@ -27,6 +27,8 @@ import { Dict, isName, Ref, RefSet } from "./primitives.js";
|
|||
import { BaseStream } from "./base_stream.js";
|
||||
|
||||
const PDF_VERSION_REGEXP = /^[1-9]\.\d$/;
|
||||
const MAX_INT_32 = 2 ** 31 - 1;
|
||||
const MIN_INT_32 = -(2 ** 31);
|
||||
|
||||
function getLookupTableFactory(initializer) {
|
||||
let lookup;
|
||||
|
@ -713,6 +715,8 @@ export {
|
|||
lookupMatrix,
|
||||
lookupNormalRect,
|
||||
lookupRect,
|
||||
MAX_INT_32,
|
||||
MIN_INT_32,
|
||||
MissingDataException,
|
||||
numberToString,
|
||||
ParserEOFException,
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
*/
|
||||
|
||||
import { FeatureTest, ImageKind, shadow, warn } from "../shared/util.js";
|
||||
import { convertToRGBA } from "../shared/image_utils.js";
|
||||
import { MAX_INT_32 } from "./core_utils.js";
|
||||
|
||||
const MIN_IMAGE_DIM = 2048;
|
||||
|
||||
|
@ -172,6 +174,18 @@ class ImageResizer {
|
|||
}
|
||||
|
||||
async _createImage() {
|
||||
const { _imgData: imgData } = this;
|
||||
const { width, height } = imgData;
|
||||
|
||||
if (width * height * 4 > MAX_INT_32) {
|
||||
// The resulting RGBA image is too large.
|
||||
// We just rescale the data.
|
||||
const result = this.#rescaleImageData();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const data = this._encodeBMP();
|
||||
let decoder, imagePromise;
|
||||
|
||||
|
@ -206,8 +220,6 @@ class ImageResizer {
|
|||
}
|
||||
|
||||
const { MAX_AREA, MAX_DIM } = ImageResizer;
|
||||
const { _imgData: imgData } = this;
|
||||
const { width, height } = imgData;
|
||||
const minFactor = Math.max(
|
||||
width / MAX_DIM,
|
||||
height / MAX_DIM,
|
||||
|
@ -268,6 +280,91 @@ class ImageResizer {
|
|||
return imgData;
|
||||
}
|
||||
|
||||
#rescaleImageData() {
|
||||
const { _imgData: imgData } = this;
|
||||
const { data, width, height, kind } = imgData;
|
||||
const rgbaSize = width * height * 4;
|
||||
// K is such as width * height * 4 / 2 ** K <= 2 ** 31 - 1
|
||||
const K = Math.ceil(Math.log2(rgbaSize / MAX_INT_32));
|
||||
const newWidth = width >> K;
|
||||
const newHeight = height >> K;
|
||||
let rgbaData;
|
||||
let maxHeight = height;
|
||||
|
||||
// We try to allocate the buffer with the maximum size but it can fail.
|
||||
try {
|
||||
rgbaData = new Uint8Array(rgbaSize);
|
||||
} catch {
|
||||
// n is such as 2 ** n - 1 > width * height * 4
|
||||
let n = Math.floor(Math.log2(rgbaSize + 1));
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
rgbaData = new Uint8Array(2 ** n - 1);
|
||||
break;
|
||||
} catch {
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
maxHeight = Math.floor((2 ** n - 1) / (width * 4));
|
||||
const newSize = width * maxHeight * 4;
|
||||
if (newSize < rgbaData.length) {
|
||||
rgbaData = new Uint8Array(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
const src32 = new Uint32Array(rgbaData.buffer);
|
||||
const dest32 = new Uint32Array(newWidth * newHeight);
|
||||
|
||||
let srcPos = 0;
|
||||
let newIndex = 0;
|
||||
const step = Math.ceil(height / maxHeight);
|
||||
const remainder = height % maxHeight === 0 ? height : height % maxHeight;
|
||||
for (let k = 0; k < step; k++) {
|
||||
const h = k < step - 1 ? maxHeight : remainder;
|
||||
({ srcPos } = convertToRGBA({
|
||||
kind,
|
||||
src: data,
|
||||
dest: src32,
|
||||
width,
|
||||
height: h,
|
||||
inverseDecode: this._isMask,
|
||||
srcPos,
|
||||
}));
|
||||
|
||||
for (let i = 0, ii = h >> K; i < ii; i++) {
|
||||
const buf = src32.subarray((i << K) * width);
|
||||
for (let j = 0; j < newWidth; j++) {
|
||||
dest32[newIndex++] = buf[j << K];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImageResizer.needsToBeResized(newWidth, newHeight)) {
|
||||
imgData.data = dest32;
|
||||
imgData.width = newWidth;
|
||||
imgData.height = newHeight;
|
||||
imgData.kind = ImageKind.RGBA_32BPP;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const canvas = new OffscreenCanvas(newWidth, newHeight);
|
||||
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
||||
ctx.putImageData(
|
||||
new ImageData(new Uint8ClampedArray(dest32.buffer), newWidth, newHeight),
|
||||
0,
|
||||
0
|
||||
);
|
||||
imgData.data = null;
|
||||
imgData.bitmap = canvas.transferToImageBitmap();
|
||||
imgData.width = newWidth;
|
||||
imgData.height = newHeight;
|
||||
|
||||
return imgData;
|
||||
}
|
||||
|
||||
_encodeBMP() {
|
||||
const { width, height, kind } = this._imgData;
|
||||
let data = this._imgData.data;
|
||||
|
|
|
@ -14,7 +14,14 @@
|
|||
*/
|
||||
|
||||
import { BaseException, shadow } from "../shared/util.js";
|
||||
import { log2, readInt8, readUint16, readUint32 } from "./core_utils.js";
|
||||
import {
|
||||
log2,
|
||||
MAX_INT_32,
|
||||
MIN_INT_32,
|
||||
readInt8,
|
||||
readUint16,
|
||||
readUint32,
|
||||
} from "./core_utils.js";
|
||||
import { ArithmeticDecoder } from "./arithmetic_decoder.js";
|
||||
import { CCITTFaxDecoder } from "./ccitt.js";
|
||||
|
||||
|
@ -52,9 +59,6 @@ class DecodingContext {
|
|||
}
|
||||
}
|
||||
|
||||
const MAX_INT_32 = 2 ** 31 - 1;
|
||||
const MIN_INT_32 = -(2 ** 31);
|
||||
|
||||
// Annex A. Arithmetic Integer Decoding Procedure
|
||||
// A.2 Procedure for decoding values
|
||||
function decodeInteger(contextCache, procedure, decoder) {
|
||||
|
|
|
@ -77,7 +77,8 @@ function convertRGBToRGBA({
|
|||
height,
|
||||
}) {
|
||||
let i = 0;
|
||||
const len32 = src.length >> 2;
|
||||
const len = width * height * 3;
|
||||
const len32 = len >> 2;
|
||||
const src32 = new Uint32Array(src.buffer, srcPos, len32);
|
||||
|
||||
if (FeatureTest.isLittleEndian) {
|
||||
|
@ -94,7 +95,7 @@ function convertRGBToRGBA({
|
|||
dest[destPos + 3] = (s3 >>> 8) | 0xff000000;
|
||||
}
|
||||
|
||||
for (let j = i * 4, jj = src.length; j < jj; j += 3) {
|
||||
for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
|
||||
dest[destPos++] =
|
||||
src[j] | (src[j + 1] << 8) | (src[j + 2] << 16) | 0xff000000;
|
||||
}
|
||||
|
@ -110,13 +111,13 @@ function convertRGBToRGBA({
|
|||
dest[destPos + 3] = (s3 << 8) | 0xff;
|
||||
}
|
||||
|
||||
for (let j = i * 4, jj = src.length; j < jj; j += 3) {
|
||||
for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
|
||||
dest[destPos++] =
|
||||
(src[j] << 24) | (src[j + 1] << 16) | (src[j + 2] << 8) | 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return { srcPos, destPos };
|
||||
return { srcPos: srcPos + len, destPos };
|
||||
}
|
||||
|
||||
function grayToRGBA(src, dest) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue