mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-04-18 03:04:07 +02:00
Merge pull request #21086 from Snuffleupagus/JpegImage-DataView
Re-factor the `JpegImage` class, and related code, to use `DataView`s when reading data
This commit is contained in:
commit
5089cceec7
@ -17,7 +17,6 @@ import { assert, BaseException, warn } from "../shared/util.js";
|
||||
import { ColorSpaceUtils } from "./colorspace_utils.js";
|
||||
import { DeviceCmykCS } from "./colorspace.js";
|
||||
import { grayToRGBA } from "../shared/image_utils.js";
|
||||
import { readUint16 } from "./core_utils.js";
|
||||
|
||||
class JpegError extends BaseException {
|
||||
constructor(msg) {
|
||||
@ -122,6 +121,7 @@ function getBlockBufferOffset(component, row, col) {
|
||||
|
||||
function decodeScan(
|
||||
data,
|
||||
view,
|
||||
offset,
|
||||
frame,
|
||||
components,
|
||||
@ -151,7 +151,7 @@ function decodeScan(
|
||||
if (nextByte === /* DNL = */ 0xdc && parseDNLMarker) {
|
||||
offset += 2; // Skip marker length.
|
||||
|
||||
const scanLines = readUint16(data, offset);
|
||||
const scanLines = view.getUint16(offset);
|
||||
offset += 2;
|
||||
if (scanLines > 0 && scanLines !== frame.scanLines) {
|
||||
throw new DNLMarkerError(
|
||||
@ -434,7 +434,7 @@ function decodeScan(
|
||||
|
||||
// find marker
|
||||
bitsCount = 0;
|
||||
fileMarker = findNextFileMarker(data, offset);
|
||||
fileMarker = findNextFileMarker(data, view, offset);
|
||||
if (!fileMarker) {
|
||||
break; // Reached the end of the image data without finding any marker.
|
||||
}
|
||||
@ -717,14 +717,14 @@ function buildComponentData(frame, component) {
|
||||
return component.blockData;
|
||||
}
|
||||
|
||||
function findNextFileMarker(data, currentPos, startPos = currentPos) {
|
||||
function findNextFileMarker(data, view, currentPos, startPos = currentPos) {
|
||||
const maxPos = data.length - 1;
|
||||
let newPos = startPos < currentPos ? startPos : currentPos;
|
||||
|
||||
if (currentPos >= maxPos) {
|
||||
return null; // Don't attempt to read non-existent data and just return.
|
||||
}
|
||||
const currentMarker = readUint16(data, currentPos);
|
||||
const currentMarker = view.getUint16(currentPos);
|
||||
if (currentMarker >= 0xffc0 && currentMarker <= 0xfffe) {
|
||||
return {
|
||||
invalid: null,
|
||||
@ -732,12 +732,12 @@ function findNextFileMarker(data, currentPos, startPos = currentPos) {
|
||||
offset: currentPos,
|
||||
};
|
||||
}
|
||||
let newMarker = readUint16(data, newPos);
|
||||
let newMarker = view.getUint16(newPos);
|
||||
while (!(newMarker >= 0xffc0 && newMarker <= 0xfffe)) {
|
||||
if (++newPos >= maxPos) {
|
||||
return null; // Don't attempt to read non-existent data and just return.
|
||||
}
|
||||
newMarker = readUint16(data, newPos);
|
||||
newMarker = view.getUint16(newPos);
|
||||
}
|
||||
return {
|
||||
invalid: currentMarker.toString(16),
|
||||
@ -769,12 +769,12 @@ function prepareComponents(frame) {
|
||||
frame.mcusPerColumn = mcusPerColumn;
|
||||
}
|
||||
|
||||
function readDataBlock(data, offset) {
|
||||
const length = readUint16(data, offset);
|
||||
function readDataBlock(data, view, offset) {
|
||||
const length = view.getUint16(offset);
|
||||
offset += 2;
|
||||
let endOffset = offset + length - 2;
|
||||
|
||||
const fileMarker = findNextFileMarker(data, endOffset, offset);
|
||||
const fileMarker = findNextFileMarker(data, view, endOffset, offset);
|
||||
if (fileMarker?.invalid) {
|
||||
warn(
|
||||
"readDataBlock - incorrect length, current marker is: " +
|
||||
@ -791,12 +791,12 @@ function readDataBlock(data, offset) {
|
||||
};
|
||||
}
|
||||
|
||||
function skipData(data, offset) {
|
||||
const length = readUint16(data, offset);
|
||||
function skipData(data, view, offset) {
|
||||
const length = view.getUint16(offset);
|
||||
offset += 2;
|
||||
const endOffset = offset + length - 2;
|
||||
|
||||
const fileMarker = findNextFileMarker(data, endOffset, offset);
|
||||
const fileMarker = findNextFileMarker(data, view, endOffset, offset);
|
||||
if (fileMarker?.invalid) {
|
||||
return fileMarker.offset;
|
||||
}
|
||||
@ -810,15 +810,16 @@ class JpegImage {
|
||||
}
|
||||
|
||||
static canUseImageDecoder(data, colorTransform = -1) {
|
||||
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
let exifOffsets = null;
|
||||
let offset = 0;
|
||||
let numComponents = null;
|
||||
let fileMarker = readUint16(data, offset);
|
||||
let fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
if (fileMarker !== /* SOI (Start of Image) = */ 0xffd8) {
|
||||
throw new JpegError("SOI not found");
|
||||
}
|
||||
fileMarker = readUint16(data, offset);
|
||||
fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
|
||||
markerLoop: while (fileMarker !== /* EOI (End of Image) = */ 0xffd9) {
|
||||
@ -826,7 +827,11 @@ class JpegImage {
|
||||
case 0xffe1: // APP1 - Exif
|
||||
// TODO: Remove this once https://github.com/w3c/webcodecs/issues/870
|
||||
// is fixed.
|
||||
const { appData, oldOffset, newOffset } = readDataBlock(data, offset);
|
||||
const { appData, oldOffset, newOffset } = readDataBlock(
|
||||
data,
|
||||
view,
|
||||
offset
|
||||
);
|
||||
offset = newOffset;
|
||||
|
||||
// 'Exif\x00\x00'
|
||||
@ -845,7 +850,7 @@ class JpegImage {
|
||||
// since that can modify the original PDF document.
|
||||
exifOffsets = { exifStart: oldOffset + 6, exifEnd: newOffset };
|
||||
}
|
||||
fileMarker = readUint16(data, offset);
|
||||
fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
continue;
|
||||
case 0xffc0: // SOF0 (Start of Frame, Baseline DCT)
|
||||
@ -864,8 +869,8 @@ class JpegImage {
|
||||
}
|
||||
break;
|
||||
}
|
||||
offset = skipData(data, offset);
|
||||
fileMarker = readUint16(data, offset);
|
||||
offset = skipData(data, view, offset);
|
||||
fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
}
|
||||
if (numComponents === 4) {
|
||||
@ -878,6 +883,8 @@ class JpegImage {
|
||||
}
|
||||
|
||||
parse(data, { dnlScanLines = null } = {}) {
|
||||
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
||||
const maxOffset = data.length - 1;
|
||||
let offset = 0;
|
||||
let jfif = null;
|
||||
let adobe = null;
|
||||
@ -887,12 +894,12 @@ class JpegImage {
|
||||
const huffmanTablesAC = [],
|
||||
huffmanTablesDC = [];
|
||||
|
||||
let fileMarker = readUint16(data, offset);
|
||||
let fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
if (fileMarker !== /* SOI (Start of Image) = */ 0xffd8) {
|
||||
throw new JpegError("SOI not found");
|
||||
}
|
||||
fileMarker = readUint16(data, offset);
|
||||
fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
|
||||
markerLoop: while (fileMarker !== /* EOI (End of Image) = */ 0xffd9) {
|
||||
@ -915,7 +922,7 @@ class JpegImage {
|
||||
case 0xffee: // APP14
|
||||
case 0xffef: // APP15
|
||||
case 0xfffe: // COM (Comment)
|
||||
const { appData, newOffset } = readDataBlock(data, offset);
|
||||
const { appData, newOffset } = readDataBlock(data, view, offset);
|
||||
offset = newOffset;
|
||||
|
||||
if (fileMarker === 0xffe0) {
|
||||
@ -962,7 +969,7 @@ class JpegImage {
|
||||
break;
|
||||
|
||||
case 0xffdb: // DQT (Define Quantization Tables)
|
||||
const quantizationTablesLength = readUint16(data, offset);
|
||||
const quantizationTablesLength = view.getUint16(offset);
|
||||
offset += 2;
|
||||
const quantizationTablesEnd = quantizationTablesLength + offset - 2;
|
||||
let z;
|
||||
@ -979,7 +986,7 @@ class JpegImage {
|
||||
// 16 bit values
|
||||
for (j = 0; j < 64; j++) {
|
||||
z = dctZigZag[j];
|
||||
tableData[z] = readUint16(data, offset);
|
||||
tableData[z] = view.getUint16(offset);
|
||||
offset += 2;
|
||||
}
|
||||
} else {
|
||||
@ -1001,10 +1008,10 @@ class JpegImage {
|
||||
frame.extended = fileMarker === 0xffc1;
|
||||
frame.progressive = fileMarker === 0xffc2;
|
||||
frame.precision = data[offset++];
|
||||
const sofScanLines = readUint16(data, offset);
|
||||
const sofScanLines = view.getUint16(offset);
|
||||
offset += 2;
|
||||
frame.scanLines = dnlScanLines || sofScanLines;
|
||||
frame.samplesPerLine = readUint16(data, offset);
|
||||
frame.samplesPerLine = view.getUint16(offset);
|
||||
offset += 2;
|
||||
frame.components = [];
|
||||
frame.componentIds = {};
|
||||
@ -1037,7 +1044,7 @@ class JpegImage {
|
||||
break;
|
||||
|
||||
case 0xffc4: // DHT (Define Huffman Tables)
|
||||
const huffmanLength = readUint16(data, offset);
|
||||
const huffmanLength = view.getUint16(offset);
|
||||
offset += 2;
|
||||
for (i = 2; i < huffmanLength; ) {
|
||||
const huffmanTableSpec = data[offset++];
|
||||
@ -1061,7 +1068,7 @@ class JpegImage {
|
||||
case 0xffdd: // DRI (Define Restart Interval)
|
||||
offset += 2; // Skip marker length.
|
||||
|
||||
resetInterval = readUint16(data, offset);
|
||||
resetInterval = view.getUint16(offset);
|
||||
offset += 2;
|
||||
break;
|
||||
|
||||
@ -1092,6 +1099,7 @@ class JpegImage {
|
||||
try {
|
||||
const processed = decodeScan(
|
||||
data,
|
||||
view,
|
||||
offset,
|
||||
frame,
|
||||
components,
|
||||
@ -1133,6 +1141,7 @@ class JpegImage {
|
||||
// `startPos = offset - 3` when looking for the next valid marker.
|
||||
const nextFileMarker = findNextFileMarker(
|
||||
data,
|
||||
view,
|
||||
/* currentPos = */ offset - 2,
|
||||
/* startPos = */ offset - 3
|
||||
);
|
||||
@ -1144,7 +1153,7 @@ class JpegImage {
|
||||
offset = nextFileMarker.offset;
|
||||
break;
|
||||
}
|
||||
if (!nextFileMarker || offset >= data.length - 1) {
|
||||
if (!nextFileMarker || offset >= maxOffset) {
|
||||
warn(
|
||||
"JpegImage.parse - reached the end of the image data " +
|
||||
"without finding an EOI marker (0xFFD9)."
|
||||
@ -1155,8 +1164,13 @@ class JpegImage {
|
||||
"JpegImage.parse - unknown marker: " + fileMarker.toString(16)
|
||||
);
|
||||
}
|
||||
fileMarker = readUint16(data, offset);
|
||||
offset += 2;
|
||||
|
||||
if (offset < maxOffset) {
|
||||
fileMarker = view.getUint16(offset);
|
||||
offset += 2;
|
||||
} else {
|
||||
fileMarker = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!frame) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user