Merge pull request #8491 from janpe2/jbig2Halftone-2
JBIG2 halftone regions and pattern dictionaries
This commit is contained in:
commit
ca936ee0c7
@ -111,7 +111,7 @@ var Jbig2Image = (function Jbig2ImageClosure() {
|
||||
var SegmentTypes = [
|
||||
'SymbolDictionary', null, null, null, 'IntermediateTextRegion', null,
|
||||
'ImmediateTextRegion', 'ImmediateLosslessTextRegion', null, null, null,
|
||||
null, null, null, null, null, 'patternDictionary', null, null, null,
|
||||
null, null, null, null, null, 'PatternDictionary', null, null, null,
|
||||
'IntermediateHalftoneRegion', null, 'ImmediateHalftoneRegion',
|
||||
'ImmediateLosslessHalftoneRegion', null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, 'IntermediateGenericRegion', null,
|
||||
@ -619,6 +619,148 @@ var Jbig2Image = (function Jbig2ImageClosure() {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
function decodePatternDictionary(mmr, patternWidth, patternHeight,
|
||||
maxPatternIndex, template, decodingContext) {
|
||||
let at = [];
|
||||
at.push({
|
||||
x: -patternWidth,
|
||||
y: 0,
|
||||
});
|
||||
if (template === 0) {
|
||||
at.push({
|
||||
x: -3,
|
||||
y: -1,
|
||||
});
|
||||
at.push({
|
||||
x: 2,
|
||||
y: -2,
|
||||
});
|
||||
at.push({
|
||||
x: -2,
|
||||
y: -2,
|
||||
});
|
||||
}
|
||||
let collectiveWidth = (maxPatternIndex + 1) * patternWidth;
|
||||
let collectiveBitmap = decodeBitmap(mmr, collectiveWidth, patternHeight,
|
||||
template, false, null, at,
|
||||
decodingContext);
|
||||
// Divide collective bitmap into patterns.
|
||||
let patterns = [], i = 0, patternBitmap, xMin, xMax, y;
|
||||
while (i <= maxPatternIndex) {
|
||||
patternBitmap = [];
|
||||
xMin = patternWidth * i;
|
||||
xMax = xMin + patternWidth;
|
||||
for (y = 0; y < patternHeight; y++) {
|
||||
patternBitmap.push(collectiveBitmap[y].subarray(xMin, xMax));
|
||||
}
|
||||
patterns.push(patternBitmap);
|
||||
i++;
|
||||
}
|
||||
return patterns;
|
||||
}
|
||||
|
||||
function decodeHalftoneRegion(mmr, patterns, template, regionWidth,
|
||||
regionHeight, defaultPixelValue, enableSkip,
|
||||
combinationOperator, gridWidth, gridHeight,
|
||||
gridOffsetX, gridOffsetY, gridVectorX,
|
||||
gridVectorY, decodingContext) {
|
||||
let skip = null;
|
||||
if (enableSkip) {
|
||||
throw new Jbig2Error('skip is not supported');
|
||||
}
|
||||
if (combinationOperator !== 0) {
|
||||
throw new Jbig2Error('operator ' + combinationOperator +
|
||||
' is not supported in halftone region');
|
||||
}
|
||||
|
||||
// Prepare bitmap.
|
||||
let regionBitmap = [];
|
||||
let i, j, row;
|
||||
for (i = 0; i < regionHeight; i++) {
|
||||
row = new Uint8Array(regionWidth);
|
||||
if (defaultPixelValue) {
|
||||
for (j = 0; j < regionWidth; j++) {
|
||||
row[j] = defaultPixelValue;
|
||||
}
|
||||
}
|
||||
regionBitmap.push(row);
|
||||
}
|
||||
|
||||
let numberOfPatterns = patterns.length;
|
||||
let pattern0 = patterns[0];
|
||||
let patternWidth = pattern0[0].length, patternHeight = pattern0.length;
|
||||
let bitsPerValue = log2(numberOfPatterns);
|
||||
let at = [];
|
||||
at.push({
|
||||
x: (template <= 1) ? 3 : 2,
|
||||
y: -1,
|
||||
});
|
||||
if (template === 0) {
|
||||
at.push({
|
||||
x: -3,
|
||||
y: -1,
|
||||
});
|
||||
at.push({
|
||||
x: 2,
|
||||
y: -2,
|
||||
});
|
||||
at.push({
|
||||
x: -2,
|
||||
y: -2,
|
||||
});
|
||||
}
|
||||
// Annex C. Gray-scale Image Decoding Procedure.
|
||||
let grayScaleBitPlanes = [];
|
||||
for (i = bitsPerValue - 1; i >= 0; i--) {
|
||||
grayScaleBitPlanes[i] = decodeBitmap(mmr, gridWidth, gridHeight,
|
||||
template, false, skip, at,
|
||||
decodingContext);
|
||||
}
|
||||
// 6.6.5.2 Rendering the patterns.
|
||||
let mg, ng, bit, patternIndex, patternBitmap, x, y, patternRow, regionRow;
|
||||
for (mg = 0; mg < gridHeight; mg++) {
|
||||
for (ng = 0; ng < gridWidth; ng++) {
|
||||
bit = 0;
|
||||
patternIndex = 0;
|
||||
for (j = bitsPerValue - 1; j >= 0; j--) {
|
||||
bit = grayScaleBitPlanes[j][mg][ng] ^ bit; // Gray decoding
|
||||
patternIndex |= bit << j;
|
||||
}
|
||||
patternBitmap = patterns[patternIndex];
|
||||
x = (gridOffsetX + mg * gridVectorY + ng * gridVectorX) >> 8;
|
||||
y = (gridOffsetY + mg * gridVectorX - ng * gridVectorY) >> 8;
|
||||
// Draw patternBitmap at (x, y).
|
||||
if (x >= 0 && x + patternWidth <= regionWidth && y >= 0 &&
|
||||
y + patternHeight <= regionHeight) {
|
||||
for (i = 0; i < patternHeight; i++) {
|
||||
regionRow = regionBitmap[y + i];
|
||||
patternRow = patternBitmap[i];
|
||||
for (j = 0; j < patternWidth; j++) {
|
||||
regionRow[x + j] |= patternRow[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let regionX, regionY;
|
||||
for (i = 0; i < patternHeight; i++) {
|
||||
regionY = y + i;
|
||||
if (regionY < 0 || regionY >= regionHeight) {
|
||||
continue;
|
||||
}
|
||||
regionRow = regionBitmap[regionY];
|
||||
patternRow = patternBitmap[i];
|
||||
for (j = 0; j < patternWidth; j++) {
|
||||
regionX = x + j;
|
||||
if (regionX >= 0 && regionX < regionWidth) {
|
||||
regionRow[regionX] |= patternRow[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return regionBitmap;
|
||||
}
|
||||
|
||||
function readSegmentHeader(data, start) {
|
||||
var segmentHeader = {};
|
||||
segmentHeader.number = readUint32(data, start);
|
||||
@ -850,6 +992,44 @@ var Jbig2Image = (function Jbig2ImageClosure() {
|
||||
}
|
||||
args = [textRegion, header.referredTo, data, position, end];
|
||||
break;
|
||||
case 16: // PatternDictionary
|
||||
// 7.4.4. Pattern dictionary segment syntax
|
||||
let patternDictionary = {};
|
||||
let patternDictionaryFlags = data[position++];
|
||||
patternDictionary.mmr = !!(patternDictionaryFlags & 1);
|
||||
patternDictionary.template = (patternDictionaryFlags >> 1) & 3;
|
||||
patternDictionary.patternWidth = data[position++];
|
||||
patternDictionary.patternHeight = data[position++];
|
||||
patternDictionary.maxPatternIndex = readUint32(data, position);
|
||||
position += 4;
|
||||
args = [patternDictionary, header.number, data, position, end];
|
||||
break;
|
||||
case 22: // ImmediateHalftoneRegion
|
||||
case 23: // ImmediateLosslessHalftoneRegion
|
||||
// 7.4.5 Halftone region segment syntax
|
||||
let halftoneRegion = {};
|
||||
halftoneRegion.info = readRegionSegmentInformation(data, position);
|
||||
position += RegionSegmentInformationFieldLength;
|
||||
let halftoneRegionFlags = data[position++];
|
||||
halftoneRegion.mmr = !!(halftoneRegionFlags & 1);
|
||||
halftoneRegion.template = (halftoneRegionFlags >> 1) & 3;
|
||||
halftoneRegion.enableSkip = !!(halftoneRegionFlags & 8);
|
||||
halftoneRegion.combinationOperator = (halftoneRegionFlags >> 4) & 7;
|
||||
halftoneRegion.defaultPixelValue = (halftoneRegionFlags >> 7) & 1;
|
||||
halftoneRegion.gridWidth = readUint32(data, position);
|
||||
position += 4;
|
||||
halftoneRegion.gridHeight = readUint32(data, position);
|
||||
position += 4;
|
||||
halftoneRegion.gridOffsetX = readUint32(data, position) & 0xFFFFFFFF;
|
||||
position += 4;
|
||||
halftoneRegion.gridOffsetY = readUint32(data, position) & 0xFFFFFFFF;
|
||||
position += 4;
|
||||
halftoneRegion.gridVectorX = readUint16(data, position);
|
||||
position += 2;
|
||||
halftoneRegion.gridVectorY = readUint16(data, position);
|
||||
position += 2;
|
||||
args = [halftoneRegion, header.referredTo, data, position, end];
|
||||
break;
|
||||
case 38: // ImmediateGenericRegion
|
||||
case 39: // ImmediateLosslessGenericRegion
|
||||
var genericRegion = {};
|
||||
@ -1086,6 +1266,32 @@ var Jbig2Image = (function Jbig2ImageClosure() {
|
||||
function SimpleSegmentVisitor_onImmediateLosslessTextRegion() {
|
||||
this.onImmediateTextRegion.apply(this, arguments);
|
||||
},
|
||||
onPatternDictionary(dictionary, currentSegment, data, start, end) {
|
||||
let patterns = this.patterns;
|
||||
if (!patterns) {
|
||||
this.patterns = patterns = {};
|
||||
}
|
||||
let decodingContext = new DecodingContext(data, start, end);
|
||||
patterns[currentSegment] = decodePatternDictionary(dictionary.mmr,
|
||||
dictionary.patternWidth, dictionary.patternHeight,
|
||||
dictionary.maxPatternIndex, dictionary.template, decodingContext);
|
||||
},
|
||||
onImmediateHalftoneRegion(region, referredSegments, data, start, end) {
|
||||
// HalftoneRegion refers to exactly one PatternDictionary.
|
||||
let patterns = this.patterns[referredSegments[0]];
|
||||
let regionInfo = region.info;
|
||||
let decodingContext = new DecodingContext(data, start, end);
|
||||
let bitmap = decodeHalftoneRegion(region.mmr, patterns,
|
||||
region.template, regionInfo.width, regionInfo.height,
|
||||
region.defaultPixelValue, region.enableSkip, region.combinationOperator,
|
||||
region.gridWidth, region.gridHeight, region.gridOffsetX,
|
||||
region.gridOffsetY, region.gridVectorX, region.gridVectorY,
|
||||
decodingContext);
|
||||
this.drawBitmap(regionInfo, bitmap);
|
||||
},
|
||||
onImmediateLosslessHalftoneRegion() {
|
||||
this.onImmediateHalftoneRegion.apply(this, arguments);
|
||||
},
|
||||
};
|
||||
|
||||
function Jbig2Image() {}
|
||||
|
1
test/pdfs/pr8491.pdf.link
Normal file
1
test/pdfs/pr8491.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
https://github.com/mozilla/pdf.js/files/1055278/castle_halftone.pdf
|
@ -3618,5 +3618,12 @@
|
||||
"md5": "ced0e2d88cfd5b4d3a55d937ea288af1",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "pr8491",
|
||||
"file": "pdfs/pr8491.pdf",
|
||||
"md5": "36ea2e28cd77e9e70731f574ab27cbe0",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user