Use promises to track completion of decoding.

This commit is contained in:
Brendan Dahl 2011-12-11 16:56:45 -08:00
parent 7d1cddf371
commit 683f64d54f
3 changed files with 75 additions and 46 deletions

View File

@ -219,10 +219,8 @@ var PartialEvaluator = (function partialEvaluator() {
}
fn = 'paintImageXObject';
var imageObj = new PDFImage(xref, resources, image, inline, handler);
imageObj.ready((function() {
return function(data) {
PDFImage.buildImage(function(imageObj) {
var imgData = {
width: w,
height: h,
@ -231,8 +229,7 @@ var PartialEvaluator = (function partialEvaluator() {
var pixels = imgData.data;
imageObj.fillRgbaBuffer(pixels, imageObj.decode);
handler.send('obj', [objId, 'Image', imgData]);
};
})(objId));
}, handler, xref, resources, image, inline);
}
uniquePrefix = uniquePrefix || '';

View File

@ -4,11 +4,28 @@
'use strict';
var PDFImage = (function pdfImage() {
function constructor(xref, res, image, inline, handler) {
/**
* Decode the image in the main thread if it supported. Resovles the promise
* when the image data is ready.
*/
function handleImageData(handler, xref, res, image, promise) {
if (image instanceof JpegStream && image.isNative) {
// For natively supported jpegs send them to the main thread for decoding.
var dict = image.dict;
var colorSpace = dict.get('ColorSpace', 'CS');
colorSpace = ColorSpace.parse(colorSpace, xref, res);
var numComps = colorSpace.numComps;
handler.send('jpeg_decode', [image.getIR(), numComps], function(message) {
var data = message.data;
var stream = new Stream(data, 0, data.length, image.dict);
promise.resolve(stream);
});
} else {
promise.resolve(image);
}
}
function constructor(xref, res, image, inline, smask) {
this.image = image;
this.imageReady = true;
this.smaskReady = true;
this.callbacks = [];
if (image.getParams) {
// JPX/JPEG2000 streams directly contain bits per component
@ -55,31 +72,37 @@ var PDFImage = (function pdfImage() {
this.decode = dict.get('Decode', 'D');
var mask = xref.fetchIfRef(dict.get('Mask'));
var smask = xref.fetchIfRef(dict.get('SMask'));
if (mask) {
TODO('masked images');
} else if (smask) {
this.smaskReady = false;
this.smask = new PDFImage(xref, res, smask, false, handler);
this.smask.ready(function() {
this.smaskReady = true;
if (this.isReady())
this.fireReady();
}.bind(this));
}
if (image instanceof JpegStream && image.isNative) {
this.imageReady = false;
handler.send('jpeg_decode', [image.getIR(), this.numComps], function(message) {
var data = message.data;
this.image = new Stream(data, 0, data.length);
this.imageReady = true;
if (this.isReady())
this.fireReady();
}.bind(this));
this.smask = new PDFImage(xref, res, smask, false);
}
}
/**
* Handles processing of image data and calls the callback with an argument
* of a PDFImage when the image is ready to be used.
*/
constructor.buildImage = function buildImage(callback, handler, xref, res,
image, inline) {
var promise = new Promise();
var smaskPromise = new Promise();
var promises = [promise, smaskPromise];
// The image data and smask data may not be ready yet, wait till both are
// resolved.
Promise.all(promises).then(function(results) {
var image = new PDFImage(xref, res, results[0], inline, results[1]);
callback(image);
});
handleImageData(handler, xref, res, image, promise);
var smask = xref.fetchIfRef(image.dict.get('SMask'));
if (smask)
handleImageData(handler, xref, res, smask, smaskPromise);
else
smaskPromise.resolve(null);
};
constructor.prototype = {
getComponents: function getComponents(buffer, decodeMap) {
@ -151,8 +174,6 @@ var PDFImage = (function pdfImage() {
var buf = new Uint8Array(width * height);
if (smask) {
if (!smask.isReady())
error('Soft mask is not ready.');
var sw = smask.width;
var sh = smask.height;
if (sw != this.width || sh != this.height)
@ -234,23 +255,8 @@ var PDFImage = (function pdfImage() {
buffer[i] = comps[i];
},
getImageBytes: function getImageBytes(length) {
if (!this.isReady())
error('Image is not ready to be read.');
this.image.reset();
return this.image.getBytes(length);
},
isReady: function isReady() {
return this.imageReady && this.smaskReady;
},
fireReady: function fireReady() {
for (var i = 0; i < this.callbacks.length; ++i)
this.callbacks[i]();
this.callbacks = [];
},
ready: function ready(callback) {
this.callbacks.push(callback);
if (this.isReady())
this.fireReady();
}
};
return constructor;

View File

@ -217,7 +217,33 @@ var Promise = (function promise() {
}
this.callbacks = [];
};
/**
* Builds a promise that is resolved when all the passed in promises are
* resolved.
* @param Array promises
* @return Promise
*/
Promise.all = function(promises) {
var deferred = new Promise();
var unresolved = promises.length;
var results = [];
if (unresolved === 0) {
deferred.resolve(results);
return deferred;
}
for (var i = 0; i < unresolved; ++i) {
var promise = promises[i];
promise.then((function(i) {
return function(value) {
results[i] = value;
unresolved--;
if (unresolved === 0)
deferred.resolve(results);
};
})(i));
}
return deferred;
};
Promise.prototype = {
hasData: false,