refactored image drawing code
This commit is contained in:
parent
25c27cbda1
commit
1f4fcfbd69
322
pdf.js
322
pdf.js
@ -4234,23 +4234,11 @@ var CanvasGraphics = (function() {
|
|||||||
|
|
||||||
paintImageXObject: function(ref, image, inline) {
|
paintImageXObject: function(ref, image, inline) {
|
||||||
this.save();
|
this.save();
|
||||||
if (image.getParams) {
|
|
||||||
// JPX/JPEG2000 streams directly contain bits per component
|
|
||||||
// and color space mode information.
|
|
||||||
TODO("get params from actual stream");
|
|
||||||
// var bits = ...
|
|
||||||
// var colorspace = ...
|
|
||||||
}
|
|
||||||
// TODO cache rendered images?
|
|
||||||
|
|
||||||
|
var ctx = this.ctx;
|
||||||
var dict = image.dict;
|
var dict = image.dict;
|
||||||
var w = dict.get2("Width", "W");
|
var w = dict.get2("Width", "W");
|
||||||
var h = dict.get2("Height", "H");
|
var h = dict.get2("Height", "H");
|
||||||
|
|
||||||
if (w < 1 || h < 1)
|
|
||||||
error("Invalid image width or height");
|
|
||||||
|
|
||||||
var ctx = this.ctx;
|
|
||||||
// scale the image to the unit square
|
// scale the image to the unit square
|
||||||
ctx.scale(1/w, -1/h);
|
ctx.scale(1/w, -1/h);
|
||||||
|
|
||||||
@ -4265,154 +4253,15 @@ var CanvasGraphics = (function() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var interpolate = dict.get2("Interpolate", "I");
|
var imageObj = new PDFImage(this.xref, this.res, image, inline);
|
||||||
if (!IsBool(interpolate))
|
|
||||||
interpolate = false;
|
|
||||||
var imageMask = dict.get2("ImageMask", "IM");
|
|
||||||
if (!IsBool(imageMask))
|
|
||||||
imageMask = false;
|
|
||||||
|
|
||||||
var bitsPerComponent = image.bitsPerComponent;
|
|
||||||
if (!bitsPerComponent) {
|
|
||||||
bitsPerComponent = dict.get2("BitsPerComponent", "BPC");
|
|
||||||
if (!bitsPerComponent) {
|
|
||||||
if (imageMask)
|
|
||||||
bitsPerComponent = 1;
|
|
||||||
else
|
|
||||||
error("Bits per component missing in image");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitsPerComponent !== 8) {
|
|
||||||
TODO("Support bpc="+ bitsPerComponent);
|
|
||||||
this.restore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xref = this.xref;
|
|
||||||
var colorSpaces = this.colorSpaces;
|
|
||||||
|
|
||||||
if (imageMask) {
|
|
||||||
error("support image masks");
|
|
||||||
}
|
|
||||||
|
|
||||||
// actual image
|
|
||||||
var csStream = dict.get2("ColorSpace", "CS");
|
|
||||||
csStream = xref.fetchIfRef(csStream);
|
|
||||||
if (IsName(csStream) && inline)
|
|
||||||
csStream = colorSpaces.get(csStream);
|
|
||||||
|
|
||||||
var colorSpace = new ColorSpace(xref, csStream);
|
|
||||||
var decode = dict.get2("Decode", "D");
|
|
||||||
|
|
||||||
TODO("create color map");
|
|
||||||
|
|
||||||
var mask = image.dict.get("Mask");
|
|
||||||
mask = xref.fetchIfRef(mask);
|
|
||||||
var smask = image.dict.get("SMask");
|
|
||||||
smask = xref.fetchIfRef(smask);
|
|
||||||
|
|
||||||
if (IsStream(smask)) {
|
|
||||||
if (inline)
|
|
||||||
error("cannot combine smask and inlining");
|
|
||||||
|
|
||||||
var maskDict = smask.dict;
|
|
||||||
var maskW = maskDict.get2("Width", "W");
|
|
||||||
var maskH = maskDict.get2("Height", "H");
|
|
||||||
if (!IsNum(maskW) || !IsNum(maskH) || maskW < 1 || maskH < 1)
|
|
||||||
error("Invalid image width or height");
|
|
||||||
if (maskW !== w || maskH !== h)
|
|
||||||
error("Invalid image width or height");
|
|
||||||
|
|
||||||
var maskInterpolate = maskDict.get2("Interpolate", "I");
|
|
||||||
if (!IsBool(maskInterpolate))
|
|
||||||
maskInterpolate = false;
|
|
||||||
|
|
||||||
var maskBPC = maskDict.get2("BitsPerComponent", "BPC");
|
|
||||||
if (!maskBPC)
|
|
||||||
error("Invalid image mask bpc");
|
|
||||||
|
|
||||||
var maskCsStream = maskDict.get2("ColorSpace", "CS");
|
|
||||||
maskCsStream = xref.fetchIfRef(maskCsStream);
|
|
||||||
var maskColorSpace = new ColorSpace(xref, maskCsStream);
|
|
||||||
if (maskColorSpace.mode !== "DeviceGray")
|
|
||||||
error("Invalid color space for smask");
|
|
||||||
|
|
||||||
var maskDecode = maskDict.get2("Decode", "D");
|
|
||||||
if (maskDecode)
|
|
||||||
TODO("Handle mask decode");
|
|
||||||
// handle matte object
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmpCanvas = new this.ScratchCanvas(w, h);
|
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||||
var tmpCtx = tmpCanvas.getContext("2d");
|
var tmpCtx = tmpCanvas.getContext("2d");
|
||||||
var imgData = tmpCtx.getImageData(0, 0, w, h);
|
var imgData = tmpCtx.getImageData(0, 0, w, h);
|
||||||
var pixels = imgData.data;
|
var pixels = imgData.data;
|
||||||
|
|
||||||
if (bitsPerComponent != 8)
|
imageObj.fillRgbaBuffer(pixels);
|
||||||
error("unhandled number of bits per component");
|
|
||||||
|
|
||||||
if (smask) {
|
|
||||||
if (maskColorSpace.numComps != 1)
|
|
||||||
error("Incorrect number of components in smask");
|
|
||||||
|
|
||||||
var numComps = colorSpace.numComps;
|
|
||||||
var imgArray = image.getBytes(numComps * w * h);
|
|
||||||
var imgIdx = 0;
|
|
||||||
|
|
||||||
var smArray = smask.getBytes(w * h);
|
|
||||||
var smIdx = 0;
|
|
||||||
|
|
||||||
var length = 4 * w * h;
|
|
||||||
switch (numComps) {
|
|
||||||
case 1:
|
|
||||||
for (var i = 0; i < length; i += 4) {
|
|
||||||
var p = imgArray[imgIdx++];
|
|
||||||
pixels[i] = p;
|
|
||||||
pixels[i+1] = p;
|
|
||||||
pixels[i+2] = p;
|
|
||||||
pixels[i+3] = smArray[smIdx++];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
for (var i = 0; i < length; i += 4) {
|
|
||||||
pixels[i] = imgArray[imgIdx++];
|
|
||||||
pixels[i+1] = imgArray[imgIdx++];
|
|
||||||
pixels[i+2] = imgArray[imgIdx++];
|
|
||||||
pixels[i+3] = smArray[smIdx++];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
TODO("Images with "+ numComps + " components per pixel");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var numComps = colorSpace.numComps;
|
|
||||||
var imgArray = image.getBytes(numComps * w * h);
|
|
||||||
var imgIdx = 0;
|
|
||||||
|
|
||||||
var length = 4 * w * h;
|
|
||||||
switch (numComps) {
|
|
||||||
case 1:
|
|
||||||
for (var i = 0; i < length; i += 4) {
|
|
||||||
var p = imgArray[imgIdx++];
|
|
||||||
pixels[i] = p;
|
|
||||||
pixels[i+1] = p;
|
|
||||||
pixels[i+2] = p;
|
|
||||||
pixels[i+3] = 255;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
for (var i = 0; i < length; i += 4) {
|
|
||||||
pixels[i] = imgArray[imgIdx++];
|
|
||||||
pixels[i+1] = imgArray[imgIdx++];
|
|
||||||
pixels[i+2] = imgArray[imgIdx++];
|
|
||||||
pixels[i+3] = 255;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
TODO("Images with "+ numComps + " components per pixel");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpCtx.putImageData(imgData, 0, 0);
|
tmpCtx.putImageData(imgData, 0, 0);
|
||||||
ctx.drawImage(tmpCanvas, 0, -h);
|
ctx.drawImage(tmpCanvas, 0, -h);
|
||||||
this.restore();
|
this.restore();
|
||||||
@ -4541,6 +4390,169 @@ var ColorSpace = (function() {
|
|||||||
return constructor;
|
return constructor;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
var PDFImage = (function() {
|
||||||
|
function constructor(xref, res, image, inline) {
|
||||||
|
this.image = image;
|
||||||
|
if (image.getParams) {
|
||||||
|
// JPX/JPEG2000 streams directly contain bits per component
|
||||||
|
// and color space mode information.
|
||||||
|
TODO("get params from actual stream");
|
||||||
|
// var bits = ...
|
||||||
|
// var colorspace = ...
|
||||||
|
}
|
||||||
|
// TODO cache rendered images?
|
||||||
|
|
||||||
|
var dict = image.dict;
|
||||||
|
this.width = dict.get2("Width", "W");
|
||||||
|
this.height = dict.get2("Height", "H");
|
||||||
|
|
||||||
|
if (this.width < 1 || this.height < 1)
|
||||||
|
error("Invalid image width or height");
|
||||||
|
|
||||||
|
this.interpolate = dict.get2("Interpolate", "I") || false;
|
||||||
|
this.imageMask = dict.get2("ImageMask", "IM") || false;
|
||||||
|
|
||||||
|
var bitsPerComponent = image.bitsPerComponent;
|
||||||
|
if (!bitsPerComponent) {
|
||||||
|
bitsPerComponent = dict.get2("BitsPerComponent", "BPC");
|
||||||
|
if (!bitsPerComponent) {
|
||||||
|
if (this.imageMask)
|
||||||
|
bitsPerComponent = 1;
|
||||||
|
else
|
||||||
|
error("Bits per component missing in image");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.bpc = bitsPerComponent;
|
||||||
|
|
||||||
|
var colorSpaces = res.get("ColorSpace");
|
||||||
|
var csStream = xref.fetchIfRef(dict.get2("ColorSpace", "CS"));
|
||||||
|
if (IsName(csStream) && inline)
|
||||||
|
csStream = colorSpaces.get(csStream);
|
||||||
|
this.colorSpace = new ColorSpace(xref, csStream);
|
||||||
|
|
||||||
|
this.numComps = this.colorSpace.numComps;
|
||||||
|
this.decode = dict.get2("Decode", "D");
|
||||||
|
|
||||||
|
var mask = xref.fetchIfRef(image.dict.get("Mask"));
|
||||||
|
var smask = xref.fetchIfRef(image.dict.get("SMask"));
|
||||||
|
|
||||||
|
if (mask) {
|
||||||
|
TODO("masked images");
|
||||||
|
} else if (smask) {
|
||||||
|
this.smask = new PDFImage(xref, res, smask);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
getCompFunction: function getCompFunction(bpc, width, numComps, buffer) {
|
||||||
|
var bufferPos = 0;
|
||||||
|
if (bpc == 8) {
|
||||||
|
var getComp = function() {
|
||||||
|
return buffer[bufferPos++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||||
|
var bits = 0;
|
||||||
|
var buf = 0;
|
||||||
|
var getComp = function() {
|
||||||
|
while (bits < bpc) {
|
||||||
|
buf = (buf << 8) | buffer[bufferPos++];
|
||||||
|
bits += 8;
|
||||||
|
}
|
||||||
|
var remainingBits = bits - bpc;
|
||||||
|
var ret = buf >> remainingBits;
|
||||||
|
|
||||||
|
if (bufferPos % rowBytes == 0) {
|
||||||
|
buf = 0;
|
||||||
|
bits = 0;
|
||||||
|
} else {
|
||||||
|
buf = buf & ((1 << remainingBits) - 1);
|
||||||
|
bits = remainingBits;
|
||||||
|
}
|
||||||
|
return Math.round(255 * ret / ((1 << bpc) - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getComp;
|
||||||
|
},
|
||||||
|
getOpacityFunction: function getOpacityFunction() {
|
||||||
|
var smask = this.smask;
|
||||||
|
if (smask) {
|
||||||
|
var w = smask.width;
|
||||||
|
var h = smask.height;
|
||||||
|
if (w != this.width || h != this.height)
|
||||||
|
error("smask dimensions do not match image dimensions");
|
||||||
|
|
||||||
|
var buf = new Uint8Array(w * h);
|
||||||
|
smask.fillGrayBuffer(buf);
|
||||||
|
var bufPos = 0;
|
||||||
|
var opacity = function() {
|
||||||
|
return buf[bufPos++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var opacity = function() { return 255; }
|
||||||
|
}
|
||||||
|
return opacity;
|
||||||
|
},
|
||||||
|
fillRgbaBuffer: function fillRgbaBuffer(buffer) {
|
||||||
|
var numComps = this.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;
|
||||||
|
var imgArray = this.image.getBytes(height * rowBytes);
|
||||||
|
var imgPos = 0;
|
||||||
|
|
||||||
|
var getComp = this.getCompFunction(bpc, width, numComps, imgArray)
|
||||||
|
var getOpacity = this.getOpacityFunction();
|
||||||
|
var length = width * height * 4;
|
||||||
|
|
||||||
|
switch (numComps) {
|
||||||
|
case 1:
|
||||||
|
for (var i = 0; i < length; i += 4) {
|
||||||
|
var p = getComp();
|
||||||
|
buffer[i] = p;
|
||||||
|
buffer[i+1] = p;
|
||||||
|
buffer[i+2] = p;
|
||||||
|
buffer[i+3] = getOpacity();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
for (var i = 0; i < length; i += 4) {
|
||||||
|
buffer[i] = getComp();
|
||||||
|
buffer[i+1] = getComp();
|
||||||
|
buffer[i+2] = getComp();
|
||||||
|
buffer[i+3] = getOpacity();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TODO("Images with "+ numComps + " components per pixel");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fillGrayBuffer: function fillGrayScaleBuffer(buffer) {
|
||||||
|
var numComps = this.numComps;
|
||||||
|
if (numComps != 1)
|
||||||
|
error("Reading gray scale from a color image");
|
||||||
|
|
||||||
|
var width = this.width;
|
||||||
|
var height = this.height;
|
||||||
|
var bpc = this.bpc;
|
||||||
|
|
||||||
|
// rows start at byte boundary;
|
||||||
|
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||||
|
var imgArray = this.image.getBytes(height * rowBytes);
|
||||||
|
|
||||||
|
var getComp = this.getCompFunction(bpc, width, numComps, imgArray)
|
||||||
|
var length = width * height;
|
||||||
|
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
buffer[i] = getComp();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
var PDFFunction = (function() {
|
var PDFFunction = (function() {
|
||||||
function constructor(xref, fn) {
|
function constructor(xref, fn) {
|
||||||
var dict = fn.dict;
|
var dict = fn.dict;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user