diff --git a/.gitignore b/.gitignore index 796b96d..523843f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/build +/build +/bin/ diff --git a/README.md b/README.md index 4bb1027..2e95f50 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,80 @@ JBIG2-Image-Decoder ============================= +This project fix bug in MMR decoding in Borisvl/JBIG2-Image-Decoder's fork JPedal's JBIG2 library. -This is a fork of [JPedal's JBIG2 library](http://www.jpedal.org/support_JBIG.php), -which aims to improve the performance of the original code. - -Results +Description ----------------------------- +A pdf [file](https://github.com/afila/JBIG2-Image-Decoder/blob/master/XeroxWorkCentre5230A_Huffman.pdf) +(version 1.4) produced by Xerox WorkCentre 5230(A) with JBIG2 compression (MMR) give an error, when attept to decode an [image stream](https://github.com/afila/JBIG2-Image-Decoder/blob/master/XeroxWorkCentre5230A_Huffman.jb2) from this file: + + Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 30447 + at org.jpedal.jbig2.io.StreamReader.readByte(StreamReader.java:86) + at org.jpedal.jbig2.decoders.JBIG2StreamDecoder.handleSegmentReferredToCountAndRententionFlags(JBIG2StreamDecoder.java:503) + at org.jpedal.jbig2.decoders.JBIG2StreamDecoder.readSegmentHeader(JBIG2StreamDecoder.java:457) + at org.jpedal.jbig2.decoders.JBIG2StreamDecoder.readSegments(JBIG2StreamDecoder.java:211) + at org.jpedal.jbig2.decoders.JBIG2StreamDecoder.decodeJBIG2(JBIG2StreamDecoder.java:173) + at org.jpedal.jbig2.JBIG2Decoder.decodeJBIG2(JBIG2Decoder.java:148) + at org.jpedal.jbig2.JBIG2Decoder.decodeJBIG2(JBIG2Decoder.java:123) + at org.jpedal.jbig2.JBIG2Decoder.decodeJBIG2(JBIG2Decoder.java:108) + at org.jpedal.jbig2.JBIG2Decoder.decodeJBIG2(JBIG2Decoder.java:98) + at org.jpedal.jbig2.examples.viewer.JBIG2Viewer.openFile(JBIG2Viewer.java:274) + at org.jpedal.jbig2.examples.viewer.JBIG2Viewer.access$0(JBIG2Viewer.java:252) + at org.jpedal.jbig2.examples.viewer.JBIG2Viewer$1.actionPerformed(JBIG2Viewer.java:163) + at javax.swing.AbstractButton.fireActionPerformed(Unknown Source) + at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source) + at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source) + at javax.swing.DefaultButtonModel.setPressed(Unknown Source) + at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source) + at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source) + at java.awt.Component.processMouseEvent(Unknown Source) + at javax.swing.JComponent.processMouseEvent(Unknown Source) + at java.awt.Component.processEvent(Unknown Source) + at java.awt.Container.processEvent(Unknown Source) + at java.awt.Component.dispatchEventImpl(Unknown Source) + at java.awt.Container.dispatchEventImpl(Unknown Source) + at java.awt.Component.dispatchEvent(Unknown Source) + at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) + at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) + at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) + at java.awt.Container.dispatchEventImpl(Unknown Source) + at java.awt.Window.dispatchEventImpl(Unknown Source) + at java.awt.Component.dispatchEvent(Unknown Source) + at java.awt.EventQueue.dispatchEventImpl(Unknown Source) + at java.awt.EventQueue.access$200(Unknown Source) + at java.awt.EventQueue$3.run(Unknown Source) + at java.awt.EventQueue$3.run(Unknown Source) + at java.security.AccessController.doPrivileged(Native Method) + at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) + at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) + at java.awt.EventQueue$4.run(Unknown Source) + at java.awt.EventQueue$4.run(Unknown Source) + at java.security.AccessController.doPrivileged(Native Method) + at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source) + at java.awt.EventQueue.dispatchEvent(Unknown Source) + at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) + at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) + at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) + at java.awt.EventDispatchThread.pumpEvents(Unknown Source) + at java.awt.EventDispatchThread.pumpEvents(Unknown Source) + at java.awt.EventDispatchThread.run(Unknown Source) + + +Reason +-------------------------------- +I think the reason in the 6.2.6 **Decoding using MMR coding** of [JBIG2 specification](http://www.hlevkin.com/Standards/fcd14492.pdf). -Version 1 (09.10.2011): +If MMR is 1, the generic region decoding procedure is identical to an MMR (ModifiedModified READ) decoder +described in ITU-T Recommendation T.6, with the following exceptions: + An invocation of the generic region decoding procedure with MMR equal to 1 `shall consume an integral +number of bytes, beginning and ending on a byte boundary`. This may involve skipping over some bits in +the last byte read. -In all samples we have tested, the decoding speed has increased at least by -factor 2.5. -In some examples a speed up by factor 4.5 was observed. +_Correct me if I wrong..._ -There is still lot of room for improvements especially in the combine method -in JBIG2Bitmap, which is most time consuming. \ No newline at end of file +Fix +-------------------------------- +```java +//GenericRegionSegment.java +//bitmap.readBitmap(useMMR, template, typicalPredictionGenericDecodingOn, false, null, genericBAdaptiveTemplateX, genericBAdaptiveTemplateY, useMMR ? 0 : length - 18); +bitmap.readBitmap(useMMR, template, typicalPredictionGenericDecodingOn, false, null, genericBAdaptiveTemplateX, genericBAdaptiveTemplateY, useMMR ? bytesRead : length - 18); +``` diff --git a/XeroxWorkCentre5230A_Huffman.jb2 b/XeroxWorkCentre5230A_Huffman.jb2 new file mode 100644 index 0000000..d8973b7 Binary files /dev/null and b/XeroxWorkCentre5230A_Huffman.jb2 differ diff --git a/XeroxWorkCentre5230A_Huffman.pdf b/XeroxWorkCentre5230A_Huffman.pdf new file mode 100644 index 0000000..81b073a Binary files /dev/null and b/XeroxWorkCentre5230A_Huffman.pdf differ diff --git a/src/org/jpedal/jbig2/decoders/JBIG2StreamDecoder.java b/src/org/jpedal/jbig2/decoders/JBIG2StreamDecoder.java index b897189..2aaf355 100644 --- a/src/org/jpedal/jbig2/decoders/JBIG2StreamDecoder.java +++ b/src/org/jpedal/jbig2/decoders/JBIG2StreamDecoder.java @@ -90,7 +90,7 @@ public class JBIG2StreamDecoder { private MMRDecoder mmrDecoder; - public static boolean debug = false; + public static boolean debug = true;// DEBUG DEBUG DEBUG DEBUG public void movePointer(int i){ reader.movePointer(i); diff --git a/src/org/jpedal/jbig2/segment/region/generic/GenericRegionSegment.java b/src/org/jpedal/jbig2/segment/region/generic/GenericRegionSegment.java index 5cfc18f..babed52 100644 --- a/src/org/jpedal/jbig2/segment/region/generic/GenericRegionSegment.java +++ b/src/org/jpedal/jbig2/segment/region/generic/GenericRegionSegment.java @@ -109,6 +109,11 @@ public void readSegment() throws IOException, JBIG2Exception { boolean typicalPredictionGenericDecodingOn = genericRegionFlags.getFlagValue(GenericRegionFlags.TPGDON) != 0; int length = segmentHeader.getSegmentDataLength(); + + /*---1 fix MMR ---*/ + int bytesRead = 0; + /*- end 1 fix MMR -*/ + if(length == -1) { /** @@ -133,7 +138,10 @@ public void readSegment() throws IOException, JBIG2Exception { match2 = 172; } - int bytesRead = 0; + /*--- 2 fix MMR ---*/ + //int bytesRead = 0; + /*- end 2 fix MMR -*/ + while(true) { short bite1 = decoder.readByte(); bytesRead++; @@ -154,9 +162,15 @@ public void readSegment() throws IOException, JBIG2Exception { JBIG2Bitmap bitmap = new JBIG2Bitmap(regionBitmapWidth, regionBitmapHeight, arithmeticDecoder, huffmanDecoder, mmrDecoder); bitmap.clear(0); - bitmap.readBitmap(useMMR, template, typicalPredictionGenericDecodingOn, false, null, genericBAdaptiveTemplateX, genericBAdaptiveTemplateY, useMMR ? 0 : length - 18); - + /*--- 3 fix MMR ---*/ + // See 6.2.6 Decoding using MMR coding + // An invocation of the generic region decoding procedure with MMR equal to 1 shall consume an integral + // number of bytes, beginning and ending on a byte boundary. This may involve skipping over some bits in the last byte read. + //bitmap.readBitmap(useMMR, template, typicalPredictionGenericDecodingOn, false, null, genericBAdaptiveTemplateX, genericBAdaptiveTemplateY, useMMR ? 0 : length - 18); + bitmap.readBitmap(useMMR, template, typicalPredictionGenericDecodingOn, false, null, genericBAdaptiveTemplateX, genericBAdaptiveTemplateY, useMMR ? bytesRead : length - 18); + + /*- end 3 fix MMR -*/ if (inlineImage) { PageInformationSegment pageSegment = decoder.findPageSegement(segmentHeader.getPageAssociation());