: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
v0 = dctSqrt2 * p0 + 2048 >> 12;
v1 = dctSqrt2 * p4 + 2048 >> 12;
v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;
v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;
v0 = (v0 + v1 + 1 >> 1) + 4112;
t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;
v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;
t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
blockData[blockBufferOffset + col] = p0;
blockData[blockBufferOffset + col + 8] = p1;
blockData[blockBufferOffset + col + 16] = p2;
blockData[blockBufferOffset + col + 24] = p3;
blockData[blockBufferOffset + col + 32] = p4;
blockData[blockBufferOffset + col + 40] = p5;
blockData[blockBufferOffset + col + 48] = p6;
blockData[blockBufferOffset + col + 56] = p7;
function buildComponentData(frame, component) {
const blocksPerLine = component.blocksPerLine;
const blocksPerColumn = component.blocksPerColumn;
const computationBuffer = new Int16Array(64);
for (let blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
for (let blockCol = 0; blockCol < blocksPerLine; blockCol++) {
const offset = getBlockBufferOffset(component, blockRow, blockCol);
quantizeAndInverse(component, offset, computationBuffer);
return component.blockData;
function findNextFileMarker(data, currentPos, startPos = currentPos) {
const maxPos = data.length - 1;
let newPos = startPos < currentPos ? startPos : currentPos;
if (currentPos >= maxPos) {
const currentMarker = readUint16(data, currentPos);
if (currentMarker >= 0xffc0 && currentMarker <= 0xfffe) {
let newMarker = readUint16(data, newPos);
while (!(newMarker >= 0xffc0 && newMarker <= 0xfffe)) {
if (++newPos >= maxPos) {
newMarker = readUint16(data, newPos);
invalid: currentMarker.toString(16),
this._decodeTransform = decodeTransform;
this._colorTransform = colorTransform;
function readDataBlock() {
const length = readUint16(data, offset);
let endOffset = offset + length - 2;
const fileMarker = findNextFileMarker(data, endOffset, offset);
if (fileMarker?.invalid) {
warn("readDataBlock - incorrect length, current marker is: " + fileMarker.invalid);
endOffset = fileMarker.offset;
const array = data.subarray(offset, endOffset);
function prepareComponents(frame) {
const mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
const mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
for (const component of frame.components) {
const blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
const blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
const blocksPerLineForMcu = mcusPerLine * component.h;
const blocksPerColumnForMcu = mcusPerColumn * component.v;
const blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
component.blockData = new Int16Array(blocksBufferSize);
component.blocksPerLine = blocksPerLine;
component.blocksPerColumn = blocksPerColumn;
frame.mcusPerLine = mcusPerLine;
frame.mcusPerColumn = mcusPerColumn;
let frame, resetInterval;
const quantizationTables = [];
const huffmanTablesAC = [],
let fileMarker = readUint16(data, offset);
if (fileMarker !== 0xffd8) {
throw new JpegError("SOI not found");
fileMarker = readUint16(data, offset);
markerLoop: while (fileMarker !== 0xffd9) {
const appData = readDataBlock();
if (fileMarker === 0xffe0) {
if (appData[0] === 0x4a && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {
densityUnits: appData[7],
xDensity: appData[8] << 8 | appData[9],
yDensity: appData[10] << 8 | appData[11],
thumbHeight: appData[13],
thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
if (fileMarker === 0xffee) {
if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6f && appData[3] === 0x62 && appData[4] === 0x65) {
version: appData[5] << 8 | appData[6],
flags0: appData[7] << 8 | appData[8],
flags1: appData[9] << 8 | appData[10],
transformCode: appData[11]
const quantizationTablesLength = readUint16(data, offset);
const quantizationTablesEnd = quantizationTablesLength + offset - 2;
while (offset < quantizationTablesEnd) {
const quantizationTableSpec = data[offset++];
const tableData = new Uint16Array(64);
if (quantizationTableSpec >> 4 === 0) {
for (j = 0; j < 64; j++) {
tableData[z] = data[offset++];
} else if (quantizationTableSpec >> 4 === 1) {
for (j = 0; j < 64; j++) {
tableData[z] = readUint16(data, offset);
throw new JpegError("DQT - invalid table spec");
quantizationTables[quantizationTableSpec & 15] = tableData;
throw new JpegError("Only single frame JPEGs supported");
frame.extended = fileMarker === 0xffc1;
frame.progressive = fileMarker === 0xffc2;
frame.precision = data[offset++];
const sofScanLines = readUint16(data, offset);
frame.scanLines = dnlScanLines || sofScanLines;
frame.samplesPerLine = readUint16(data, offset);
const componentsCount = data[offset++];
for (i = 0; i < componentsCount; i++) {
const componentId = data[offset];
const h = data[offset + 1] >> 4;
const v = data[offset + 1] & 15;
const qId = data[offset + 2];
l = frame.components.push({
frame.componentIds[componentId] = l - 1;
prepareComponents(frame);
const huffmanLength = readUint16(data, offset);
for (i = 2; i < huffmanLength;) {
const huffmanTableSpec = data[offset++];
const codeLengths = new Uint8Array(16);
for (j = 0; j < 16; j++, offset++) {
codeLengthSum += codeLengths[j] = data[offset];
const huffmanValues = new Uint8Array(codeLengthSum);
for (j = 0; j < codeLengthSum; j++, offset++) {
huffmanValues[j] = data[offset];
(huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
resetInterval = readUint16(data, offset);
const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines;
const selectorsCount = data[offset++],
for (i = 0; i < selectorsCount; i++) {
const index = data[offset++];
const componentIndex = frame.componentIds[index];
const component = frame.components[componentIndex];
const tableSpec = data[offset++];
component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
components.push(component);
const spectralStart = data[offset++],
spectralEnd = data[offset++],
successiveApproximation = data[offset++];
const processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker);
if (ex instanceof DNLMarkerError) {
warn(`${ex.message} -- attempting to re-parse the JPEG image.`);
return this.parse(data, {
dnlScanLines: ex.scanLines
} else if (ex instanceof EOIMarkerError) {
warn(`${ex.message} -- ignoring the rest of the image data.`);
if (data[offset] !== 0xff) {
const nextFileMarker = findNextFileMarker(data, offset - 2, offset - 3);
if (nextFileMarker?.invalid) {
warn("JpegImage.parse - unexpected data, current marker is: " + nextFileMarker.invalid);
offset = nextFileMarker.offset;
if (!nextFileMarker || offset >= data.length - 1) {
warn("JpegImage.parse - reached the end of the image data " + "without finding an EOI marker (0xFFD9).");
throw new JpegError("JpegImage.parse - unknown marker: " + fileMarker.toString(16));
fileMarker = readUint16(data, offset);
throw new JpegError("JpegImage.parse - no frame data found.");
this.width = frame.samplesPerLine;
this.height = frame.scanLines;
for (const component of frame.components) {
const quantizationTable = quantizationTables[component.quantizationId];
component.quantizationTable = quantizationTable;
output: buildComponentData(frame, component),
scaleX: component.h / frame.maxH,
scaleY: component.v / frame.maxV,
blocksPerLine: component.blocksPerLine,
blocksPerColumn: component.blocksPerColumn
this.numComponents = this.components.length;
_getLinearizedBlockData(width, height, isSourcePDF = false) {
const scaleX = this.width / width,
scaleY = this.height / height;
let component, componentScaleX, componentScaleY, blocksPerScanline;
const numComponents = this.components.length;
const dataLength = width * height * numComponents;
const data = new Uint8ClampedArray(dataLength);
const xScaleBlockOffset = new Uint32Array(width);
const mask3LSB = 0xfffffff8;
for (i = 0; i < numComponents; i++) {
component = this.components[i];
componentScaleX = component.scaleX * scaleX;
componentScaleY = component.scaleY * scaleY;
output = component.output;
blocksPerScanline = component.blocksPerLine + 1 << 3;
if (componentScaleX !== lastComponentScaleX) {
for (x = 0; x < width; x++) {
j = 0 | x * componentScaleX;
xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;
lastComponentScaleX = componentScaleX;
for (y = 0; y < height; y++) {
j = 0 | y * componentScaleY;
index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;
for (x = 0; x < width; x++) {
data[offset] = output[index + xScaleBlockOffset[x]];
let transform = this._decodeTransform;
if (!isSourcePDF && numComponents === 4 && !transform) {
transform = new Int32Array([-256, 255, -256, 255, -256, 255, -256, 255]);
for (i = 0; i < dataLength;) {
for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
get _isColorConversionNeeded() {
return !!this.adobe.transformCode;
if (this.numComponents === 3) {
if (this._colorTransform === 0) {
} else if (this.components[0].index === 0x52 && this.components[1].index === 0x47 && this.components[2].index === 0x42) {
if (this._colorTransform === 1) {
for (let i = 0, length = data.length; i < length; i += 3) {
data[i] = Y - 179.456 + 1.402 * Cr;
data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;
data[i + 2] = Y - 226.816 + 1.772 * Cb;