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