Merge pull request #8491 from janpe2/jbig2Halftone-2

JBIG2 halftone regions and pattern dictionaries
This commit is contained in:
Jonas Jenwald 2017-08-23 00:13:43 +02:00 committed by GitHub
commit ca936ee0c7
3 changed files with 215 additions and 1 deletions

View File

@ -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() {}

View File

@ -0,0 +1 @@
https://github.com/mozilla/pdf.js/files/1055278/castle_halftone.pdf

View File

@ -3618,5 +3618,12 @@
"md5": "ced0e2d88cfd5b4d3a55d937ea288af1",
"rounds": 1,
"type": "eq"
},
{ "id": "pr8491",
"file": "pdfs/pr8491.pdf",
"md5": "36ea2e28cd77e9e70731f574ab27cbe0",
"rounds": 1,
"link": true,
"type": "eq"
}
]