2012-06-14 08:09:57 +09:00
|
|
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2011-10-26 10:18:22 +09:00
|
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
2012-09-01 07:48:21 +09:00
|
|
|
/* 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.
|
|
|
|
*/
|
2013-02-03 07:49:19 +09:00
|
|
|
/* globals ColorSpace, error, isArray, isStream, JpegStream, Name, Promise,
|
2014-01-04 09:17:05 +09:00
|
|
|
Stream, warn, LegacyPromise */
|
2011-10-26 10:18:22 +09:00
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2011-12-09 07:18:43 +09:00
|
|
|
var PDFImage = (function PDFImageClosure() {
|
2011-12-12 09:56:45 +09:00
|
|
|
/**
|
|
|
|
* 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) {
|
2012-01-04 07:26:19 +09:00
|
|
|
if (image instanceof JpegStream && image.isNativelyDecodable(xref, res)) {
|
2011-12-12 09:56:45 +09:00
|
|
|
// For natively supported jpegs send them to the main thread for decoding.
|
|
|
|
var dict = image.dict;
|
|
|
|
var colorSpace = dict.get('ColorSpace', 'CS');
|
2011-12-13 02:26:24 +09:00
|
|
|
colorSpace = ColorSpace.parse(colorSpace, xref, res);
|
2011-12-12 09:56:45 +09:00
|
|
|
var numComps = colorSpace.numComps;
|
2012-04-13 04:11:22 +09:00
|
|
|
handler.send('JpegDecode', [image.getIR(), numComps], function(message) {
|
2011-12-12 09:56:45 +09:00
|
|
|
var data = message.data;
|
|
|
|
var stream = new Stream(data, 0, data.length, image.dict);
|
|
|
|
promise.resolve(stream);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
promise.resolve(image);
|
|
|
|
}
|
|
|
|
}
|
2011-12-14 06:53:22 +09:00
|
|
|
/**
|
2011-12-14 07:35:46 +09:00
|
|
|
* Decode and clamp a value. The formula is different from the spec because we
|
|
|
|
* don't decode to float range [0,1], we decode it in the [0,max] range.
|
2011-12-14 06:53:22 +09:00
|
|
|
*/
|
2011-12-14 07:35:46 +09:00
|
|
|
function decodeAndClamp(value, addend, coefficient, max) {
|
2011-12-14 06:53:22 +09:00
|
|
|
value = addend + value * coefficient;
|
|
|
|
// Clamp the value to the range
|
|
|
|
return value < 0 ? 0 : value > max ? max : value;
|
|
|
|
}
|
2013-03-15 06:06:44 +09:00
|
|
|
function PDFImage(xref, res, image, inline, smask, mask, isMask) {
|
2011-10-25 08:55:23 +09:00
|
|
|
this.image = image;
|
|
|
|
if (image.getParams) {
|
|
|
|
// JPX/JPEG2000 streams directly contain bits per component
|
|
|
|
// and color space mode information.
|
2014-01-04 02:34:13 +09:00
|
|
|
warn('get params from actual stream');
|
2011-10-25 08:55:23 +09:00
|
|
|
// var bits = ...
|
|
|
|
// var colorspace = ...
|
|
|
|
}
|
|
|
|
// TODO cache rendered images?
|
|
|
|
|
|
|
|
var dict = image.dict;
|
|
|
|
this.width = dict.get('Width', 'W');
|
|
|
|
this.height = dict.get('Height', 'H');
|
|
|
|
|
|
|
|
if (this.width < 1 || this.height < 1)
|
|
|
|
error('Invalid image width: ' + this.width + ' or height: ' +
|
|
|
|
this.height);
|
|
|
|
|
|
|
|
this.interpolate = dict.get('Interpolate', 'I') || false;
|
|
|
|
this.imageMask = dict.get('ImageMask', 'IM') || false;
|
2013-07-03 04:27:06 +09:00
|
|
|
this.matte = dict.get('Matte') || false;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
var bitsPerComponent = image.bitsPerComponent;
|
|
|
|
if (!bitsPerComponent) {
|
|
|
|
bitsPerComponent = dict.get('BitsPerComponent', 'BPC');
|
|
|
|
if (!bitsPerComponent) {
|
|
|
|
if (this.imageMask)
|
|
|
|
bitsPerComponent = 1;
|
|
|
|
else
|
|
|
|
error('Bits per component missing in image: ' + this.imageMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.bpc = bitsPerComponent;
|
|
|
|
|
|
|
|
if (!this.imageMask) {
|
|
|
|
var colorSpace = dict.get('ColorSpace', 'CS');
|
|
|
|
if (!colorSpace) {
|
2014-01-04 02:34:13 +09:00
|
|
|
warn('JPX images (which don"t require color spaces');
|
2011-10-25 08:55:23 +09:00
|
|
|
colorSpace = new Name('DeviceRGB');
|
|
|
|
}
|
|
|
|
this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
|
|
|
|
this.numComps = this.colorSpace.numComps;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.decode = dict.get('Decode', 'D');
|
2011-12-14 07:35:46 +09:00
|
|
|
this.needsDecode = false;
|
2013-03-15 06:06:44 +09:00
|
|
|
if (this.decode &&
|
|
|
|
((this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode)) ||
|
|
|
|
(isMask && !ColorSpace.isDefaultDecode(this.decode, 1)))) {
|
2011-12-14 07:35:46 +09:00
|
|
|
this.needsDecode = true;
|
2011-12-14 06:53:22 +09:00
|
|
|
// Do some preprocessing to avoid more math.
|
|
|
|
var max = (1 << bitsPerComponent) - 1;
|
|
|
|
this.decodeCoefficients = [];
|
|
|
|
this.decodeAddends = [];
|
|
|
|
for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
|
|
|
|
var dmin = this.decode[i];
|
|
|
|
var dmax = this.decode[i + 1];
|
|
|
|
this.decodeCoefficients[j] = dmax - dmin;
|
|
|
|
this.decodeAddends[j] = max * dmin;
|
|
|
|
}
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2012-06-09 14:42:56 +09:00
|
|
|
if (smask) {
|
2011-12-12 09:56:45 +09:00
|
|
|
this.smask = new PDFImage(xref, res, smask, false);
|
2012-06-09 14:42:56 +09:00
|
|
|
} else if (mask) {
|
2012-08-29 09:19:31 +09:00
|
|
|
if (isStream(mask)) {
|
2013-03-15 06:06:44 +09:00
|
|
|
this.mask = new PDFImage(xref, res, mask, false, null, null, true);
|
2012-08-29 09:19:31 +09:00
|
|
|
} else {
|
|
|
|
// Color key mask (just an array).
|
|
|
|
this.mask = mask;
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
}
|
2011-12-12 09:56:45 +09:00
|
|
|
/**
|
|
|
|
* Handles processing of image data and calls the callback with an argument
|
|
|
|
* of a PDFImage when the image is ready to be used.
|
|
|
|
*/
|
2012-04-05 05:43:26 +09:00
|
|
|
PDFImage.buildImage = function PDFImage_buildImage(callback, handler, xref,
|
|
|
|
res, image, inline) {
|
2014-01-04 09:17:05 +09:00
|
|
|
var imageDataPromise = new LegacyPromise();
|
|
|
|
var smaskPromise = new LegacyPromise();
|
|
|
|
var maskPromise = new LegacyPromise();
|
2011-12-12 09:56:45 +09:00
|
|
|
// The image data and smask data may not be ready yet, wait till both are
|
|
|
|
// resolved.
|
2012-06-09 14:42:56 +09:00
|
|
|
Promise.all([imageDataPromise, smaskPromise, maskPromise]).then(
|
|
|
|
function(results) {
|
|
|
|
var imageData = results[0], smaskData = results[1], maskData = results[2];
|
2012-06-12 06:57:58 +09:00
|
|
|
var image = new PDFImage(xref, res, imageData, inline, smaskData,
|
2012-06-09 14:42:56 +09:00
|
|
|
maskData);
|
2011-12-12 09:56:45 +09:00
|
|
|
callback(image);
|
|
|
|
});
|
|
|
|
|
2011-12-13 08:09:05 +09:00
|
|
|
handleImageData(handler, xref, res, image, imageDataPromise);
|
2011-12-12 09:56:45 +09:00
|
|
|
|
2012-04-05 03:43:04 +09:00
|
|
|
var smask = image.dict.get('SMask');
|
2012-06-09 14:42:56 +09:00
|
|
|
var mask = image.dict.get('Mask');
|
|
|
|
|
|
|
|
if (smask) {
|
2011-12-12 09:56:45 +09:00
|
|
|
handleImageData(handler, xref, res, smask, smaskPromise);
|
2012-06-12 07:05:06 +09:00
|
|
|
maskPromise.resolve(null);
|
2012-06-09 14:42:56 +09:00
|
|
|
} else {
|
2011-12-12 09:56:45 +09:00
|
|
|
smaskPromise.resolve(null);
|
2012-08-29 09:19:31 +09:00
|
|
|
if (mask) {
|
|
|
|
if (isStream(mask)) {
|
|
|
|
handleImageData(handler, xref, res, mask, maskPromise);
|
|
|
|
} else if (isArray(mask)) {
|
|
|
|
maskPromise.resolve(mask);
|
|
|
|
} else {
|
|
|
|
warn('Unsupported mask format.');
|
|
|
|
maskPromise.resolve(null);
|
|
|
|
}
|
2012-06-09 14:42:56 +09:00
|
|
|
} else {
|
|
|
|
maskPromise.resolve(null);
|
|
|
|
}
|
|
|
|
}
|
2011-12-12 09:56:45 +09:00
|
|
|
};
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2011-12-15 05:27:56 +09:00
|
|
|
/**
|
|
|
|
* Resize an image using the nearest neighbor algorithm. Currently only
|
2011-12-20 07:58:01 +09:00
|
|
|
* supports one and three component images.
|
2011-12-15 05:27:56 +09:00
|
|
|
* @param {TypedArray} pixels The original image with one component.
|
2011-12-19 10:18:36 +09:00
|
|
|
* @param {Number} bpc Number of bits per component.
|
|
|
|
* @param {Number} components Number of color components, 1 or 3 is supported.
|
2011-12-15 05:27:56 +09:00
|
|
|
* @param {Number} w1 Original width.
|
|
|
|
* @param {Number} h1 Original height.
|
|
|
|
* @param {Number} w2 New width.
|
|
|
|
* @param {Number} h2 New height.
|
|
|
|
* @return {TypedArray} Resized image data.
|
|
|
|
*/
|
2012-04-05 05:43:26 +09:00
|
|
|
PDFImage.resize = function PDFImage_resize(pixels, bpc, components,
|
|
|
|
w1, h1, w2, h2) {
|
2011-12-19 10:18:36 +09:00
|
|
|
var length = w2 * h2 * components;
|
|
|
|
var temp = bpc <= 8 ? new Uint8Array(length) :
|
|
|
|
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
2011-12-15 05:27:56 +09:00
|
|
|
var xRatio = w1 / w2;
|
|
|
|
var yRatio = h1 / h2;
|
2011-12-19 10:18:36 +09:00
|
|
|
var px, py, newIndex, oldIndex;
|
2011-12-15 05:27:56 +09:00
|
|
|
for (var i = 0; i < h2; i++) {
|
|
|
|
for (var j = 0; j < w2; j++) {
|
|
|
|
px = Math.floor(j * xRatio);
|
|
|
|
py = Math.floor(i * yRatio);
|
2011-12-19 10:18:36 +09:00
|
|
|
newIndex = (i * w2) + j;
|
|
|
|
oldIndex = ((py * w1) + px);
|
|
|
|
if (components === 1) {
|
|
|
|
temp[newIndex] = pixels[oldIndex];
|
2011-12-19 10:29:08 +09:00
|
|
|
} else if (components === 3) {
|
2011-12-19 10:18:36 +09:00
|
|
|
newIndex *= 3;
|
|
|
|
oldIndex *= 3;
|
|
|
|
temp[newIndex] = pixels[oldIndex];
|
|
|
|
temp[newIndex + 1] = pixels[oldIndex + 1];
|
|
|
|
temp[newIndex + 2] = pixels[oldIndex + 2];
|
|
|
|
}
|
2011-12-15 05:27:56 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return temp;
|
|
|
|
};
|
|
|
|
|
2013-05-31 09:42:26 +09:00
|
|
|
PDFImage.createMask = function PDFImage_createMask(imgArray, width, height,
|
|
|
|
inverseDecode) {
|
2014-01-14 11:08:39 +09:00
|
|
|
// Copy imgArray into a typed array (inverting if necessary) so it can be
|
|
|
|
// transferred to the main thread.
|
|
|
|
var length = ((width + 7) >> 3) * height;
|
|
|
|
var data = new Uint8Array(length);
|
|
|
|
if (inverseDecode) {
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
data[i] = ~imgArray[i];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
data[i] = imgArray[i];
|
2013-05-31 09:42:26 +09:00
|
|
|
}
|
|
|
|
}
|
2014-01-14 11:08:39 +09:00
|
|
|
|
|
|
|
return {data: data, width: width, height: height};
|
2013-05-31 09:42:26 +09:00
|
|
|
};
|
|
|
|
|
2011-12-09 07:18:43 +09:00
|
|
|
PDFImage.prototype = {
|
2011-12-20 09:31:47 +09:00
|
|
|
get drawWidth() {
|
2011-12-19 10:18:36 +09:00
|
|
|
if (!this.smask)
|
|
|
|
return this.width;
|
|
|
|
return Math.max(this.width, this.smask.width);
|
|
|
|
},
|
2011-12-20 09:31:47 +09:00
|
|
|
get drawHeight() {
|
2011-12-19 10:18:36 +09:00
|
|
|
if (!this.smask)
|
|
|
|
return this.height;
|
|
|
|
return Math.max(this.height, this.smask.height);
|
|
|
|
},
|
2013-03-15 06:06:44 +09:00
|
|
|
decodeBuffer: function PDFImage_decodeBuffer(buffer) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var bpc = this.bpc;
|
2011-12-14 01:48:27 +09:00
|
|
|
var decodeMap = this.decode;
|
2013-03-15 06:06:44 +09:00
|
|
|
var numComps = this.numComps;
|
|
|
|
|
|
|
|
var decodeAddends, decodeCoefficients;
|
|
|
|
var decodeAddends = this.decodeAddends;
|
|
|
|
var decodeCoefficients = this.decodeCoefficients;
|
|
|
|
var max = (1 << bpc) - 1;
|
|
|
|
|
|
|
|
if (bpc === 1) {
|
|
|
|
// If the buffer needed decode that means it just needs to be inverted.
|
|
|
|
for (var i = 0, ii = buffer.length; i < ii; i++) {
|
|
|
|
buffer[i] = +!(buffer[i]);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var index = 0;
|
|
|
|
for (var i = 0, ii = this.width * this.height; i < ii; i++) {
|
|
|
|
for (var j = 0; j < numComps; j++) {
|
|
|
|
buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j],
|
|
|
|
decodeCoefficients[j], max);
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
getComponents: function PDFImage_getComponents(buffer) {
|
|
|
|
var bpc = this.bpc;
|
2011-12-14 07:35:46 +09:00
|
|
|
|
|
|
|
// This image doesn't require any extra work.
|
2013-03-15 06:06:44 +09:00
|
|
|
if (bpc === 8)
|
2011-12-14 06:53:22 +09:00
|
|
|
return buffer;
|
2011-12-14 07:35:46 +09:00
|
|
|
|
2011-12-14 06:53:22 +09:00
|
|
|
var bufferLength = buffer.length;
|
2011-10-25 08:55:23 +09:00
|
|
|
var width = this.width;
|
|
|
|
var height = this.height;
|
|
|
|
var numComps = this.numComps;
|
|
|
|
|
2011-12-14 06:53:22 +09:00
|
|
|
var length = width * height * numComps;
|
2011-10-25 08:55:23 +09:00
|
|
|
var bufferPos = 0;
|
|
|
|
var output = bpc <= 8 ? new Uint8Array(length) :
|
|
|
|
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
|
|
|
var rowComps = width * numComps;
|
2013-03-15 06:06:44 +09:00
|
|
|
|
2011-12-14 06:53:22 +09:00
|
|
|
var max = (1 << bpc) - 1;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2013-03-15 06:06:44 +09:00
|
|
|
if (bpc === 1) {
|
2011-12-14 06:53:22 +09:00
|
|
|
// Optimization for reading 1 bpc images.
|
2011-10-25 08:55:23 +09:00
|
|
|
var mask = 0;
|
|
|
|
var buf = 0;
|
|
|
|
|
|
|
|
for (var i = 0, ii = length; i < ii; ++i) {
|
2013-02-01 08:32:13 +09:00
|
|
|
if (i % rowComps === 0) {
|
2011-10-25 08:55:23 +09:00
|
|
|
mask = 0;
|
|
|
|
buf = 0;
|
|
|
|
} else {
|
|
|
|
mask >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask <= 0) {
|
|
|
|
buf = buffer[bufferPos++];
|
|
|
|
mask = 128;
|
|
|
|
}
|
|
|
|
|
2013-03-15 06:06:44 +09:00
|
|
|
output[i] = +!!(buf & mask);
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
} else {
|
2011-12-14 06:53:22 +09:00
|
|
|
// The general case that handles all other bpc values.
|
2011-10-25 08:55:23 +09:00
|
|
|
var bits = 0, buf = 0;
|
|
|
|
for (var i = 0, ii = length; i < ii; ++i) {
|
2013-02-01 08:32:13 +09:00
|
|
|
if (i % rowComps === 0) {
|
2011-10-25 08:55:23 +09:00
|
|
|
buf = 0;
|
|
|
|
bits = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (bits < bpc) {
|
|
|
|
buf = (buf << 8) | buffer[bufferPos++];
|
|
|
|
bits += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
var remainingBits = bits - bpc;
|
2011-12-14 06:53:22 +09:00
|
|
|
var value = buf >> remainingBits;
|
2013-03-15 06:06:44 +09:00
|
|
|
output[i] = value < 0 ? 0 : value > max ? max : value;
|
2011-10-25 08:55:23 +09:00
|
|
|
buf = buf & ((1 << remainingBits) - 1);
|
|
|
|
bits = remainingBits;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
},
|
2014-01-17 10:55:42 +09:00
|
|
|
fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height,
|
|
|
|
actualHeight, image) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var smask = this.smask;
|
2012-06-09 14:42:56 +09:00
|
|
|
var mask = this.mask;
|
2014-01-17 10:55:42 +09:00
|
|
|
var alphaBuf;
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
if (smask) {
|
|
|
|
var sw = smask.width;
|
|
|
|
var sh = smask.height;
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf = new Uint8Array(sw * sh);
|
|
|
|
smask.fillGrayBuffer(alphaBuf);
|
2011-12-19 10:18:36 +09:00
|
|
|
if (sw != width || sh != height)
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf = PDFImage.resize(alphaBuf, smask.bpc, 1, sw, sh, width,
|
|
|
|
height);
|
2012-06-09 14:42:56 +09:00
|
|
|
} else if (mask) {
|
2012-08-29 09:19:31 +09:00
|
|
|
if (mask instanceof PDFImage) {
|
|
|
|
var sw = mask.width;
|
|
|
|
var sh = mask.height;
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf = new Uint8Array(sw * sh);
|
2012-08-29 09:19:31 +09:00
|
|
|
mask.numComps = 1;
|
2014-01-17 10:55:42 +09:00
|
|
|
mask.fillGrayBuffer(alphaBuf);
|
2012-06-09 14:42:56 +09:00
|
|
|
|
2014-01-17 10:55:42 +09:00
|
|
|
// Need to invert values in rgbaBuf
|
2012-08-29 09:19:31 +09:00
|
|
|
for (var i = 0, ii = sw * sh; i < ii; ++i)
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf[i] = 255 - alphaBuf[i];
|
2012-06-09 14:42:56 +09:00
|
|
|
|
2012-08-29 09:19:31 +09:00
|
|
|
if (sw != width || sh != height)
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf = PDFImage.resize(alphaBuf, mask.bpc, 1, sw, sh, width,
|
|
|
|
height);
|
2012-08-29 09:19:31 +09:00
|
|
|
} else if (isArray(mask)) {
|
|
|
|
// Color key mask: if any of the compontents are outside the range
|
|
|
|
// then they should be painted.
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf = new Uint8Array(width * height);
|
2012-08-30 01:36:12 +09:00
|
|
|
var numComps = this.numComps;
|
2012-08-29 09:19:31 +09:00
|
|
|
for (var i = 0, ii = width * height; i < ii; ++i) {
|
|
|
|
var opacity = 0;
|
2012-08-30 01:36:12 +09:00
|
|
|
var imageOffset = i * numComps;
|
|
|
|
for (var j = 0; j < numComps; ++j) {
|
|
|
|
var color = image[imageOffset + j];
|
|
|
|
var maskOffset = j * 2;
|
|
|
|
if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
|
2012-08-29 09:19:31 +09:00
|
|
|
opacity = 255;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-01-17 10:55:42 +09:00
|
|
|
alphaBuf[i] = opacity;
|
2012-08-29 09:19:31 +09:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error('Unknown mask format.');
|
|
|
|
}
|
2014-01-17 10:55:42 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
if (alphaBuf) {
|
|
|
|
for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
|
|
|
|
rgbaBuf[j] = alphaBuf[i];
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
} else {
|
2014-01-17 10:55:42 +09:00
|
|
|
// Common case: no mask (and no need to allocate the extra buffer).
|
|
|
|
for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
|
|
|
|
rgbaBuf[j] = 255;
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
},
|
2013-07-03 04:27:06 +09:00
|
|
|
undoPreblend: function PDFImage_undoPreblend(buffer, width, height) {
|
|
|
|
var matte = this.smask && this.smask.matte;
|
|
|
|
if (!matte) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
function clamp(value) {
|
|
|
|
return (value < 0 ? 0 : value > 255 ? 255 : value) | 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
var matteRgb = this.colorSpace.getRgb(matte, 0);
|
|
|
|
var length = width * height * 4;
|
|
|
|
for (var i = 0; i < length; i += 4) {
|
|
|
|
var alpha = buffer[i + 3];
|
|
|
|
if (alpha === 0) {
|
|
|
|
// according formula we have to get Infinity in all components
|
|
|
|
// making it as white (tipical paper color) should be okay
|
|
|
|
buffer[i] = 255;
|
|
|
|
buffer[i + 1] = 255;
|
|
|
|
buffer[i + 2] = 255;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var k = 255 / alpha;
|
|
|
|
buffer[i] = clamp((buffer[i] - matteRgb[0]) * k + matteRgb[0]);
|
|
|
|
buffer[i + 1] = clamp((buffer[i + 1] - matteRgb[1]) * k + matteRgb[1]);
|
|
|
|
buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]);
|
|
|
|
}
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
fillRgbaBuffer: function PDFImage_fillRgbaBuffer(buffer, width, height) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var numComps = this.numComps;
|
2011-12-19 10:18:36 +09:00
|
|
|
var originalWidth = this.width;
|
|
|
|
var originalHeight = this.height;
|
2011-10-25 08:55:23 +09:00
|
|
|
var bpc = this.bpc;
|
|
|
|
|
|
|
|
// rows start at byte boundary;
|
2011-12-19 10:18:36 +09:00
|
|
|
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
|
|
|
|
var imgArray = this.getImageBytes(originalHeight * rowBytes);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2012-08-21 05:57:21 +09:00
|
|
|
// imgArray can be incomplete (e.g. after CCITT fax encoding)
|
|
|
|
var actualHeight = 0 | (imgArray.length / rowBytes *
|
|
|
|
height / originalHeight);
|
2013-03-15 06:06:44 +09:00
|
|
|
var comps = this.getComponents(imgArray);
|
2014-01-17 10:55:42 +09:00
|
|
|
|
|
|
|
// Handle opacity here since color key masking needs to be performed on
|
2013-03-15 06:06:44 +09:00
|
|
|
// undecoded values.
|
2014-01-17 10:55:42 +09:00
|
|
|
this.fillOpacity(buffer, width, height, actualHeight, comps);
|
2012-08-21 05:57:21 +09:00
|
|
|
|
2013-03-15 06:06:44 +09:00
|
|
|
if (this.needsDecode) {
|
|
|
|
this.decodeBuffer(comps);
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
|
2014-01-17 11:29:33 +09:00
|
|
|
this.colorSpace.fillRgb(buffer, originalWidth, originalHeight, width,
|
|
|
|
height, actualHeight, bpc, comps);
|
2013-07-03 04:27:06 +09:00
|
|
|
|
|
|
|
this.undoPreblend(buffer, width, actualHeight);
|
2011-10-25 08:55:23 +09:00
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
|
2011-10-25 08:55:23 +09:00
|
|
|
var numComps = this.numComps;
|
|
|
|
if (numComps != 1)
|
|
|
|
error('Reading gray scale from a color image: ' + numComps);
|
|
|
|
|
|
|
|
var width = this.width;
|
|
|
|
var height = this.height;
|
|
|
|
var bpc = this.bpc;
|
|
|
|
|
|
|
|
// rows start at byte boundary;
|
|
|
|
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
2011-12-09 14:18:04 +09:00
|
|
|
var imgArray = this.getImageBytes(height * rowBytes);
|
2011-10-25 08:55:23 +09:00
|
|
|
|
|
|
|
var comps = this.getComponents(imgArray);
|
2013-03-15 06:06:44 +09:00
|
|
|
if (this.needsDecode) {
|
|
|
|
this.decodeBuffer(comps);
|
|
|
|
}
|
2011-10-25 08:55:23 +09:00
|
|
|
var length = width * height;
|
2011-12-19 10:18:36 +09:00
|
|
|
// we aren't using a colorspace so we need to scale the value
|
|
|
|
var scale = 255 / ((1 << bpc) - 1);
|
2011-10-25 08:55:23 +09:00
|
|
|
for (var i = 0; i < length; ++i)
|
2011-12-19 10:18:36 +09:00
|
|
|
buffer[i] = (scale * comps[i]) | 0;
|
2011-12-09 05:50:34 +09:00
|
|
|
},
|
2012-12-08 03:19:43 +09:00
|
|
|
getImageData: function PDFImage_getImageData() {
|
|
|
|
var drawWidth = this.drawWidth;
|
|
|
|
var drawHeight = this.drawHeight;
|
|
|
|
var imgData = {
|
|
|
|
width: drawWidth,
|
|
|
|
height: drawHeight,
|
|
|
|
data: new Uint8Array(drawWidth * drawHeight * 4)
|
|
|
|
};
|
|
|
|
var pixels = imgData.data;
|
|
|
|
this.fillRgbaBuffer(pixels, drawWidth, drawHeight);
|
|
|
|
return imgData;
|
|
|
|
},
|
2012-04-05 05:43:26 +09:00
|
|
|
getImageBytes: function PDFImage_getImageBytes(length) {
|
2011-12-09 14:18:04 +09:00
|
|
|
this.image.reset();
|
|
|
|
return this.image.getBytes(length);
|
2011-10-25 08:55:23 +09:00
|
|
|
}
|
|
|
|
};
|
2011-12-09 07:18:43 +09:00
|
|
|
return PDFImage;
|
2011-10-25 08:55:23 +09:00
|
|
|
})();
|