From 36593d6bbc563a0892f94888d2b244f711ef3cef Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Thu, 26 Oct 2017 13:00:09 +0200 Subject: [PATCH 1/2] Move `JpegStream` and `JpxStream` to their own files --- src/core/evaluator.js | 3 +- src/core/image.js | 3 +- src/core/jpeg_stream.js | 112 +++++++++++++++++++++++++++ src/core/jpx_stream.js | 92 ++++++++++++++++++++++ src/core/parser.js | 6 +- src/core/stream.js | 167 +--------------------------------------- 6 files changed, 213 insertions(+), 170 deletions(-) create mode 100644 src/core/jpeg_stream.js create mode 100644 src/core/jpx_stream.js diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 3389f7a30..6d0d3969e 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -20,7 +20,7 @@ import { UNSUPPORTED_FEATURES, Util, warn } from '../shared/util'; import { CMapFactory, IdentityCMap } from './cmap'; -import { DecodeStream, JpegStream, Stream } from './stream'; +import { DecodeStream, Stream } from './stream'; import { Dict, isCmd, isDict, isEOF, isName, isRef, isStream, Name } from './primitives'; @@ -44,6 +44,7 @@ import { ColorSpace } from './colorspace'; import { getGlyphsUnicode } from './glyphlist'; import { getMetrics } from './metrics'; import { isPDFFunction } from './function'; +import { JpegStream } from './jpeg_stream'; import { MurmurHash3_64 } from './murmurhash3'; import { PDFImage } from './image'; diff --git a/src/core/image.js b/src/core/image.js index 9010bf970..cfc6555c8 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -14,9 +14,10 @@ */ import { assert, FormatError, ImageKind, info, warn } from '../shared/util'; -import { DecodeStream, JpegStream } from './stream'; import { isStream, Name } from './primitives'; import { ColorSpace } from './colorspace'; +import { DecodeStream } from './stream'; +import { JpegStream } from './jpeg_stream'; import { JpxImage } from './jpx'; var PDFImage = (function PDFImageClosure() { diff --git a/src/core/jpeg_stream.js b/src/core/jpeg_stream.js new file mode 100644 index 000000000..e99a5ea82 --- /dev/null +++ b/src/core/jpeg_stream.js @@ -0,0 +1,112 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createObjectURL, shadow } from '../shared/util'; +import { DecodeStream } from './stream'; +import { isDict } from './primitives'; +import { JpegImage } from './jpg'; + +/** + * Depending on the type of JPEG a JpegStream is handled in different ways. For + * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image + * data is stored and then loaded by the browser. For unsupported JPEG's we use + * a library to decode these images and the stream behaves like all the other + * DecodeStreams. + */ +var JpegStream = (function JpegStreamClosure() { + function JpegStream(stream, maybeLength, dict, params) { + // Some images may contain 'junk' before the SOI (start-of-image) marker. + // Note: this seems to mainly affect inline images. + var ch; + while ((ch = stream.getByte()) !== -1) { + if (ch === 0xFF) { // Find the first byte of the SOI marker (0xFFD8). + stream.skip(-1); // Reset the stream position to the SOI. + break; + } + } + this.stream = stream; + this.maybeLength = maybeLength; + this.dict = dict; + this.params = params; + + DecodeStream.call(this, maybeLength); + } + + JpegStream.prototype = Object.create(DecodeStream.prototype); + + Object.defineProperty(JpegStream.prototype, 'bytes', { + get: function JpegStream_bytes() { + // If this.maybeLength is null, we'll get the entire stream. + return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); + }, + configurable: true, + }); + + JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(req) { + if (this.bufferLength) { + return; + } + var jpegImage = new JpegImage(); + + // Checking if values need to be transformed before conversion. + var decodeArr = this.dict.getArray('Decode', 'D'); + if (this.forceRGB && Array.isArray(decodeArr)) { + var bitsPerComponent = this.dict.get('BitsPerComponent') || 8; + var decodeArrLength = decodeArr.length; + var transform = new Int32Array(decodeArrLength); + var transformNeeded = false; + var maxValue = (1 << bitsPerComponent) - 1; + for (var i = 0; i < decodeArrLength; i += 2) { + transform[i] = ((decodeArr[i + 1] - decodeArr[i]) * 256) | 0; + transform[i + 1] = (decodeArr[i] * maxValue) | 0; + if (transform[i] !== 256 || transform[i + 1] !== 0) { + transformNeeded = true; + } + } + if (transformNeeded) { + jpegImage.decodeTransform = transform; + } + } + // Fetching the 'ColorTransform' entry, if it exists. + if (isDict(this.params)) { + var colorTransform = this.params.get('ColorTransform'); + if (Number.isInteger(colorTransform)) { + jpegImage.colorTransform = colorTransform; + } + } + + jpegImage.parse(this.bytes); + var data = jpegImage.getData(this.drawWidth, this.drawHeight, + this.forceRGB); + this.buffer = data; + this.bufferLength = data.length; + this.eof = true; + }; + + JpegStream.prototype.getBytes = function JpegStream_getBytes(length) { + this.ensureBuffer(); + return this.buffer; + }; + + JpegStream.prototype.getIR = function JpegStream_getIR(forceDataSchema) { + return createObjectURL(this.bytes, 'image/jpeg', forceDataSchema); + }; + + return JpegStream; +})(); + +export { + JpegStream, +}; diff --git a/src/core/jpx_stream.js b/src/core/jpx_stream.js new file mode 100644 index 000000000..556469c1c --- /dev/null +++ b/src/core/jpx_stream.js @@ -0,0 +1,92 @@ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DecodeStream } from './stream'; +import { JpxImage } from './jpx'; +import { shadow } from '../shared/util'; + +/** + * For JPEG 2000's we use a library to decode these images and + * the stream behaves like all the other DecodeStreams. + */ +var JpxStream = (function JpxStreamClosure() { + function JpxStream(stream, maybeLength, dict, params) { + this.stream = stream; + this.maybeLength = maybeLength; + this.dict = dict; + this.params = params; + + DecodeStream.call(this, maybeLength); + } + + JpxStream.prototype = Object.create(DecodeStream.prototype); + + Object.defineProperty(JpxStream.prototype, 'bytes', { + get: function JpxStream_bytes() { + // If this.maybeLength is null, we'll get the entire stream. + return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); + }, + configurable: true, + }); + + JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) { + if (this.bufferLength) { + return; + } + + var jpxImage = new JpxImage(); + jpxImage.parse(this.bytes); + + var width = jpxImage.width; + var height = jpxImage.height; + var componentsCount = jpxImage.componentsCount; + var tileCount = jpxImage.tiles.length; + if (tileCount === 1) { + this.buffer = jpxImage.tiles[0].items; + } else { + var data = new Uint8ClampedArray(width * height * componentsCount); + + for (var k = 0; k < tileCount; k++) { + var tileComponents = jpxImage.tiles[k]; + var tileWidth = tileComponents.width; + var tileHeight = tileComponents.height; + var tileLeft = tileComponents.left; + var tileTop = tileComponents.top; + + var src = tileComponents.items; + var srcPosition = 0; + var dataPosition = (width * tileTop + tileLeft) * componentsCount; + var imgRowSize = width * componentsCount; + var tileRowSize = tileWidth * componentsCount; + + for (var j = 0; j < tileHeight; j++) { + var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize); + data.set(rowBytes, dataPosition); + srcPosition += tileRowSize; + dataPosition += imgRowSize; + } + } + this.buffer = data; + } + this.bufferLength = this.buffer.length; + this.eof = true; + }; + + return JpxStream; +})(); + +export { + JpxStream, +}; diff --git a/src/core/parser.js b/src/core/parser.js index 54cfc3568..e6e60cd49 100644 --- a/src/core/parser.js +++ b/src/core/parser.js @@ -14,8 +14,8 @@ */ import { - Ascii85Stream, AsciiHexStream, FlateStream, JpegStream, JpxStream, LZWStream, - NullStream, PredictorStream, RunLengthStream + Ascii85Stream, AsciiHexStream, FlateStream, LZWStream, NullStream, + PredictorStream, RunLengthStream } from './stream'; import { assert, FormatError, info, isNum, isString, MissingDataException, StreamType, @@ -26,6 +26,8 @@ import { } from './primitives'; import { CCITTFaxStream } from './ccitt_stream'; import { Jbig2Stream } from './jbig2_stream'; +import { JpegStream } from './jpeg_stream'; +import { JpxStream } from './jpx_stream'; var MAX_LENGTH_TO_CACHE = 1000; diff --git a/src/core/stream.js b/src/core/stream.js index fbf957e8a..4bc5eb57e 100644 --- a/src/core/stream.js +++ b/src/core/stream.js @@ -13,12 +13,8 @@ * limitations under the License. */ -import { - createObjectURL, FormatError, isSpace, shadow, stringToBytes, Util -} from '../shared/util'; +import { FormatError, isSpace, stringToBytes, Util } from '../shared/util'; import { isDict } from './primitives'; -import { JpegImage } from './jpg'; -import { JpxImage } from './jpx'; var Stream = (function StreamClosure() { function Stream(arrayBuffer, start, length, dict) { @@ -871,165 +867,6 @@ var PredictorStream = (function PredictorStreamClosure() { return PredictorStream; })(); -/** - * Depending on the type of JPEG a JpegStream is handled in different ways. For - * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image - * data is stored and then loaded by the browser. For unsupported JPEG's we use - * a library to decode these images and the stream behaves like all the other - * DecodeStreams. - */ -var JpegStream = (function JpegStreamClosure() { - function JpegStream(stream, maybeLength, dict, params) { - // Some images may contain 'junk' before the SOI (start-of-image) marker. - // Note: this seems to mainly affect inline images. - var ch; - while ((ch = stream.getByte()) !== -1) { - if (ch === 0xFF) { // Find the first byte of the SOI marker (0xFFD8). - stream.skip(-1); // Reset the stream position to the SOI. - break; - } - } - this.stream = stream; - this.maybeLength = maybeLength; - this.dict = dict; - this.params = params; - - DecodeStream.call(this, maybeLength); - } - - JpegStream.prototype = Object.create(DecodeStream.prototype); - - Object.defineProperty(JpegStream.prototype, 'bytes', { - get: function JpegStream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. - return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); - }, - configurable: true, - }); - - JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(req) { - if (this.bufferLength) { - return; - } - var jpegImage = new JpegImage(); - - // Checking if values need to be transformed before conversion. - var decodeArr = this.dict.getArray('Decode', 'D'); - if (this.forceRGB && Array.isArray(decodeArr)) { - var bitsPerComponent = this.dict.get('BitsPerComponent') || 8; - var decodeArrLength = decodeArr.length; - var transform = new Int32Array(decodeArrLength); - var transformNeeded = false; - var maxValue = (1 << bitsPerComponent) - 1; - for (var i = 0; i < decodeArrLength; i += 2) { - transform[i] = ((decodeArr[i + 1] - decodeArr[i]) * 256) | 0; - transform[i + 1] = (decodeArr[i] * maxValue) | 0; - if (transform[i] !== 256 || transform[i + 1] !== 0) { - transformNeeded = true; - } - } - if (transformNeeded) { - jpegImage.decodeTransform = transform; - } - } - // Fetching the 'ColorTransform' entry, if it exists. - if (isDict(this.params)) { - var colorTransform = this.params.get('ColorTransform'); - if (Number.isInteger(colorTransform)) { - jpegImage.colorTransform = colorTransform; - } - } - - jpegImage.parse(this.bytes); - var data = jpegImage.getData(this.drawWidth, this.drawHeight, - this.forceRGB); - this.buffer = data; - this.bufferLength = data.length; - this.eof = true; - }; - - JpegStream.prototype.getBytes = function JpegStream_getBytes(length) { - this.ensureBuffer(); - return this.buffer; - }; - - JpegStream.prototype.getIR = function JpegStream_getIR(forceDataSchema) { - return createObjectURL(this.bytes, 'image/jpeg', forceDataSchema); - }; - - return JpegStream; -})(); - -/** - * For JPEG 2000's we use a library to decode these images and - * the stream behaves like all the other DecodeStreams. - */ -var JpxStream = (function JpxStreamClosure() { - function JpxStream(stream, maybeLength, dict, params) { - this.stream = stream; - this.maybeLength = maybeLength; - this.dict = dict; - this.params = params; - - DecodeStream.call(this, maybeLength); - } - - JpxStream.prototype = Object.create(DecodeStream.prototype); - - Object.defineProperty(JpxStream.prototype, 'bytes', { - get: function JpxStream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. - return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); - }, - configurable: true, - }); - - JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) { - if (this.bufferLength) { - return; - } - - var jpxImage = new JpxImage(); - jpxImage.parse(this.bytes); - - var width = jpxImage.width; - var height = jpxImage.height; - var componentsCount = jpxImage.componentsCount; - var tileCount = jpxImage.tiles.length; - if (tileCount === 1) { - this.buffer = jpxImage.tiles[0].items; - } else { - var data = new Uint8ClampedArray(width * height * componentsCount); - - for (var k = 0; k < tileCount; k++) { - var tileComponents = jpxImage.tiles[k]; - var tileWidth = tileComponents.width; - var tileHeight = tileComponents.height; - var tileLeft = tileComponents.left; - var tileTop = tileComponents.top; - - var src = tileComponents.items; - var srcPosition = 0; - var dataPosition = (width * tileTop + tileLeft) * componentsCount; - var imgRowSize = width * componentsCount; - var tileRowSize = tileWidth * componentsCount; - - for (var j = 0; j < tileHeight; j++) { - var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize); - data.set(rowBytes, dataPosition); - srcPosition += tileRowSize; - dataPosition += imgRowSize; - } - } - this.buffer = data; - } - this.bufferLength = this.buffer.length; - this.eof = true; - }; - - return JpxStream; -})(); - var DecryptStream = (function DecryptStreamClosure() { function DecryptStream(str, maybeLength, decrypt) { this.str = str; @@ -1414,8 +1251,6 @@ export { DecryptStream, DecodeStream, FlateStream, - JpegStream, - JpxStream, NullStream, PredictorStream, RunLengthStream, From de5297b9ea28c5a508304c7270bc708effd42892 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Thu, 26 Oct 2017 13:15:57 +0200 Subject: [PATCH 2/2] Fix the interface of `JpegStream`/`JpxStream`/`Jbig2Stream` to agree with the other `DecodeStream`s The interface of all of the "image" streams look kind of weird, and I'm actually a bit surprised that there hasn't been any errors because of it. For example: None of them actually implement `readBlock` methods, and it seems more luck that anything else that we're not calling `getBytes()` (without providing a length) for those streams, since that would trigger a code-path in `getBytes` that assumes `readBlock` to exist. To address this long-standing issue, the `ensureBuffer` methods are thus renamed to `readBlock`. Furthermore, the new `ensureBuffer` methods are now no-ops. Finally, this patch also replaces `var` with `let` in a number of places. --- src/core/jbig2_stream.js | 29 ++++++++++++---------- src/core/jpeg_stream.js | 43 ++++++++++++++++++--------------- src/core/jpx_stream.js | 52 +++++++++++++++++++++------------------- 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/core/jbig2_stream.js b/src/core/jbig2_stream.js index ed7bec65a..1e3736b6a 100644 --- a/src/core/jbig2_stream.js +++ b/src/core/jbig2_stream.js @@ -22,7 +22,7 @@ import { shadow } from '../shared/util'; * For JBIG2's we use a library to decode these images and * the stream behaves like all the other DecodeStreams. */ -var Jbig2Stream = (function Jbig2StreamClosure() { +let Jbig2Stream = (function Jbig2StreamClosure() { function Jbig2Stream(stream, maybeLength, dict, params) { this.stream = stream; this.maybeLength = maybeLength; @@ -36,36 +36,39 @@ var Jbig2Stream = (function Jbig2StreamClosure() { Object.defineProperty(Jbig2Stream.prototype, 'bytes', { get() { - // If this.maybeLength is null, we'll get the entire stream. + // If `this.maybeLength` is null, we'll get the entire stream. return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); }, configurable: true, }); - Jbig2Stream.prototype.ensureBuffer = function(req) { - if (this.bufferLength) { + Jbig2Stream.prototype.ensureBuffer = function(requested) { + // No-op, since `this.readBlock` will always parse the entire image and + // directly insert all of its data into `this.buffer`. + }; + + Jbig2Stream.prototype.readBlock = function() { + if (this.eof) { return; } + let jbig2Image = new Jbig2Image(); - var jbig2Image = new Jbig2Image(); - - var chunks = []; + let chunks = []; if (isDict(this.params)) { - var globalsStream = this.params.get('JBIG2Globals'); + let globalsStream = this.params.get('JBIG2Globals'); if (isStream(globalsStream)) { - var globals = globalsStream.getBytes(); + let globals = globalsStream.getBytes(); chunks.push({ data: globals, start: 0, end: globals.length, }); } } chunks.push({ data: this.bytes, start: 0, end: this.bytes.length, }); - var data = jbig2Image.parseChunks(chunks); - var dataLength = data.length; + let data = jbig2Image.parseChunks(chunks); + let dataLength = data.length; // JBIG2 had black as 1 and white as 0, inverting the colors - for (var i = 0; i < dataLength; i++) { + for (let i = 0; i < dataLength; i++) { data[i] ^= 0xFF; } - this.buffer = data; this.bufferLength = dataLength; this.eof = true; diff --git a/src/core/jpeg_stream.js b/src/core/jpeg_stream.js index e99a5ea82..75936ab8f 100644 --- a/src/core/jpeg_stream.js +++ b/src/core/jpeg_stream.js @@ -21,15 +21,15 @@ import { JpegImage } from './jpg'; /** * Depending on the type of JPEG a JpegStream is handled in different ways. For * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image - * data is stored and then loaded by the browser. For unsupported JPEG's we use + * data is stored and then loaded by the browser. For unsupported JPEG's we use * a library to decode these images and the stream behaves like all the other * DecodeStreams. */ -var JpegStream = (function JpegStreamClosure() { +let JpegStream = (function JpegStreamClosure() { function JpegStream(stream, maybeLength, dict, params) { // Some images may contain 'junk' before the SOI (start-of-image) marker. // Note: this seems to mainly affect inline images. - var ch; + let ch; while ((ch = stream.getByte()) !== -1) { if (ch === 0xFF) { // Find the first byte of the SOI marker (0xFFD8). stream.skip(-1); // Reset the stream position to the SOI. @@ -48,27 +48,32 @@ var JpegStream = (function JpegStreamClosure() { Object.defineProperty(JpegStream.prototype, 'bytes', { get: function JpegStream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. + // If `this.maybeLength` is null, we'll get the entire stream. return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); }, configurable: true, }); - JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(req) { - if (this.bufferLength) { + JpegStream.prototype.ensureBuffer = function(requested) { + // No-op, since `this.readBlock` will always parse the entire image and + // directly insert all of its data into `this.buffer`. + }; + + JpegStream.prototype.readBlock = function() { + if (this.eof) { return; } - var jpegImage = new JpegImage(); + let jpegImage = new JpegImage(); // Checking if values need to be transformed before conversion. - var decodeArr = this.dict.getArray('Decode', 'D'); + let decodeArr = this.dict.getArray('Decode', 'D'); if (this.forceRGB && Array.isArray(decodeArr)) { - var bitsPerComponent = this.dict.get('BitsPerComponent') || 8; - var decodeArrLength = decodeArr.length; - var transform = new Int32Array(decodeArrLength); - var transformNeeded = false; - var maxValue = (1 << bitsPerComponent) - 1; - for (var i = 0; i < decodeArrLength; i += 2) { + let bitsPerComponent = this.dict.get('BitsPerComponent') || 8; + let decodeArrLength = decodeArr.length; + let transform = new Int32Array(decodeArrLength); + let transformNeeded = false; + let maxValue = (1 << bitsPerComponent) - 1; + for (let i = 0; i < decodeArrLength; i += 2) { transform[i] = ((decodeArr[i + 1] - decodeArr[i]) * 256) | 0; transform[i + 1] = (decodeArr[i] * maxValue) | 0; if (transform[i] !== 256 || transform[i + 1] !== 0) { @@ -81,26 +86,26 @@ var JpegStream = (function JpegStreamClosure() { } // Fetching the 'ColorTransform' entry, if it exists. if (isDict(this.params)) { - var colorTransform = this.params.get('ColorTransform'); + let colorTransform = this.params.get('ColorTransform'); if (Number.isInteger(colorTransform)) { jpegImage.colorTransform = colorTransform; } } jpegImage.parse(this.bytes); - var data = jpegImage.getData(this.drawWidth, this.drawHeight, + let data = jpegImage.getData(this.drawWidth, this.drawHeight, this.forceRGB); this.buffer = data; this.bufferLength = data.length; this.eof = true; }; - JpegStream.prototype.getBytes = function JpegStream_getBytes(length) { - this.ensureBuffer(); + JpegStream.prototype.getBytes = function(length) { + this.readBlock(); return this.buffer; }; - JpegStream.prototype.getIR = function JpegStream_getIR(forceDataSchema) { + JpegStream.prototype.getIR = function(forceDataSchema = false) { return createObjectURL(this.bytes, 'image/jpeg', forceDataSchema); }; diff --git a/src/core/jpx_stream.js b/src/core/jpx_stream.js index 556469c1c..7e564196f 100644 --- a/src/core/jpx_stream.js +++ b/src/core/jpx_stream.js @@ -21,7 +21,7 @@ import { shadow } from '../shared/util'; * For JPEG 2000's we use a library to decode these images and * the stream behaves like all the other DecodeStreams. */ -var JpxStream = (function JpxStreamClosure() { +let JpxStream = (function JpxStreamClosure() { function JpxStream(stream, maybeLength, dict, params) { this.stream = stream; this.maybeLength = maybeLength; @@ -35,44 +35,48 @@ var JpxStream = (function JpxStreamClosure() { Object.defineProperty(JpxStream.prototype, 'bytes', { get: function JpxStream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. + // If `this.maybeLength` is null, we'll get the entire stream. return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); }, configurable: true, }); - JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) { - if (this.bufferLength) { + JpxStream.prototype.ensureBuffer = function(requested) { + // No-op, since `this.readBlock` will always parse the entire image and + // directly insert all of its data into `this.buffer`. + }; + + JpxStream.prototype.readBlock = function() { + if (this.eof) { return; } - - var jpxImage = new JpxImage(); + let jpxImage = new JpxImage(); jpxImage.parse(this.bytes); - var width = jpxImage.width; - var height = jpxImage.height; - var componentsCount = jpxImage.componentsCount; - var tileCount = jpxImage.tiles.length; + let width = jpxImage.width; + let height = jpxImage.height; + let componentsCount = jpxImage.componentsCount; + let tileCount = jpxImage.tiles.length; if (tileCount === 1) { this.buffer = jpxImage.tiles[0].items; } else { - var data = new Uint8ClampedArray(width * height * componentsCount); + let data = new Uint8ClampedArray(width * height * componentsCount); - for (var k = 0; k < tileCount; k++) { - var tileComponents = jpxImage.tiles[k]; - var tileWidth = tileComponents.width; - var tileHeight = tileComponents.height; - var tileLeft = tileComponents.left; - var tileTop = tileComponents.top; + for (let k = 0; k < tileCount; k++) { + let tileComponents = jpxImage.tiles[k]; + let tileWidth = tileComponents.width; + let tileHeight = tileComponents.height; + let tileLeft = tileComponents.left; + let tileTop = tileComponents.top; - var src = tileComponents.items; - var srcPosition = 0; - var dataPosition = (width * tileTop + tileLeft) * componentsCount; - var imgRowSize = width * componentsCount; - var tileRowSize = tileWidth * componentsCount; + let src = tileComponents.items; + let srcPosition = 0; + let dataPosition = (width * tileTop + tileLeft) * componentsCount; + let imgRowSize = width * componentsCount; + let tileRowSize = tileWidth * componentsCount; - for (var j = 0; j < tileHeight; j++) { - var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize); + for (let j = 0; j < tileHeight; j++) { + let rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize); data.set(rowBytes, dataPosition); srcPosition += tileRowSize; dataPosition += imgRowSize;