This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pdfbox-jbig2.git

commit 65744b46de70d7b83414c69b5ace1fba70e795f0
Author: Maruan Sahyoun <[email protected]>
AuthorDate: Tue May 12 12:43:10 2026 +0200

    PDFBOX-6199: Template 1 TPGR decoding and replace sliding window with 
readable pixel-by-pixel implementation
---
 pom.xml                                            | 112 ++++++++++++++-----
 .../GenericRefinementRegionDecodingProcedure.java  | 122 +++++++++++++++++++--
 .../org/apache/pdfbox/jbig2/SerenityTests.java     |   8 +-
 3 files changed, 203 insertions(+), 39 deletions(-)

diff --git a/pom.xml b/pom.xml
index d035e6f..bc2e316 100644
--- a/pom.xml
+++ b/pom.xml
@@ -222,12 +222,24 @@
   </scm>
        
     <dependencies>
-       <dependency>
-               <groupId>junit</groupId>
-               <artifactId>junit</artifactId>
-               <version>4.13.2</version>
-               <scope>test</scope>
-       </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+            <version>1.37</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <version>1.37</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <!-- enable the usage of external testfiles with 
-Dskip-external-testfiles=false  -->
@@ -236,24 +248,24 @@
     </properties>
 
        <build>
-               <plugins>
-                    <plugin>
-                        <artifactId>maven-enforcer-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <goals>
-                                    <goal>enforce</goal>
-                                </goals>
-                                <configuration>
-                                    <rules combine.children="append">
-                                        <requireMavenVersion>
-                                            <version>3.7.0</version>
-                                        </requireMavenVersion>
-                                    </rules>    
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
+        <plugins>
+            <plugin>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules combine.children="append">
+                                <requireMavenVersion>
+                                    <version>3.7.0</version>
+                                </requireMavenVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
                        <plugin>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
@@ -266,10 +278,10 @@
                                <artifactId>maven-release-plugin</artifactId>
                                <configuration>
                                        
<autoVersionSubmodules>true</autoVersionSubmodules>
-                                       <!-- Keep changes in the local repo, 
push will be done afterwards -->
+                    <!-- Keep changes in the local repo, push will be done 
afterwards -->
                                        <pushChanges>false</pushChanges>
                                        <localCheckout>true</localCheckout>
-                                       <!-- Use a better name for the tag -->
+                    <!-- Use a better name for the tag -->
                                        
<tagNameFormat>${project.artifactId}-${project.version}</tagNameFormat>
                                </configuration>
                                <dependencies>
@@ -420,6 +432,54 @@
                 </plugins>
             </build>
         </profile>
+    <profile>
+        <id>benchmark</id>
+        <build>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.8</source>
+                        <target>1.8</target>
+                        <debug>false</debug>
+                        <optimize>true</optimize>
+                        <annotationProcessorPaths>
+                            <path>
+                                <groupId>org.openjdk.jmh</groupId>
+                                
<artifactId>jmh-generator-annprocess</artifactId>
+                                <version>1.37</version>
+                            </path>
+                        </annotationProcessorPaths>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>exec-maven-plugin</artifactId>
+                    <version>3.1.0</version>
+                    <executions>
+                        <execution>
+                            <id>run-benchmarks</id>
+                            <phase>test</phase>
+                            <goals>
+                                <goal>java</goal>
+                            </goals>
+                            <configuration>
+                                <mainClass>org.openjdk.jmh.Main</mainClass>
+                                <classpathScope>test</classpathScope>
+                                <arguments>
+                                    <argument>.*Benchmark.*</argument>
+                                    <argument>-rf</argument>
+                                    <argument>json</argument>
+                                    <argument>-rff</argument>
+                                    
<argument>${project.build.directory}/benchmark-results.json</argument>
+                                </arguments>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+        </build>
+    </profile>
     <profile>
       <id>apache-release</id>
       <build>
diff --git 
a/src/main/java/org/apache/pdfbox/jbig2/decoder/GenericRefinementRegionDecodingProcedure.java
 
b/src/main/java/org/apache/pdfbox/jbig2/decoder/GenericRefinementRegionDecodingProcedure.java
index 1394c14..0201ec9 100644
--- 
a/src/main/java/org/apache/pdfbox/jbig2/decoder/GenericRefinementRegionDecodingProcedure.java
+++ 
b/src/main/java/org/apache/pdfbox/jbig2/decoder/GenericRefinementRegionDecodingProcedure.java
@@ -55,7 +55,7 @@ public class GenericRefinementRegionDecodingProcedure
     // 
-------------------------------------------------------------------------
 
     private static final int SLTP_CONTEXT_TEMPLATE0 = 0x100; // §6.3.5.6, 
Figure 14
-    private static final int SLTP_CONTEXT_TEMPLATE1 = 0x080; // §6.3.5.6, 
Figure 15
+    private static final int SLTP_CONTEXT_TEMPLATE1 = 0x008; // §6.3.5.6, 
Figure 15
 
     /**
      * Encapsulates the template-specific operations: context bit formation
@@ -255,17 +255,27 @@ public class GenericRefinementRegionDecodingProcedure
                 isLineTypicalPredicted ^= decodeSLTP();
             }
 
-            if (isLineTypicalPredicted == 0)
+            if (templateID == 1)
             {
-                /* 6.3.5.6 - 3 c) */
-                decodeOptimized(y, width, regionBitmap.getRowStride(),
-                        referenceBitmap.getRowStride(), paddedWidth, 
deltaRefStride, yOffset);
+                if (isLineTypicalPredicted == 0)
+                {
+                    decodeLineExplicitT1(y, width);
+                }
+                else
+                {
+                    decodeLineTPGRT1(y, width);
+                }
             }
             else
             {
-                /* 6.3.5.6 - 3 d) */
-                decodeTypicalPredictedLine(y, width, 
regionBitmap.getRowStride(),
-                        referenceBitmap.getRowStride(), paddedWidth, 
deltaRefStride);
+                // existing template 0 paths unchanged
+                if (isLineTypicalPredicted == 0) {
+                    decodeOptimized(y, width, regionBitmap.getRowStride(),
+                            referenceBitmap.getRowStride(), paddedWidth, 
deltaRefStride, yOffset);
+                } else {
+                    decodeTypicalPredictedLine(y, width, 
regionBitmap.getRowStride(),
+                            referenceBitmap.getRowStride(), paddedWidth, 
deltaRefStride);
+                }
             }
         }
 
@@ -273,6 +283,102 @@ public class GenericRefinementRegionDecodingProcedure
         return regionBitmap;
     }
 
+    // 
-------------------------------------------------------------------------
+    // Pixel accessors — §6.3.5.2 out-of-bounds rule: all outside pixels = 0
+    // 
-------------------------------------------------------------------------
+
+    private int getPixelSafe(final Bitmap bitmap, final int x, final int y)
+    {
+        if (x < 0 || y < 0)
+            return 0;
+        if (x >= bitmap.getWidth() || y >= bitmap.getHeight())
+            return 0;
+        return bitmap.getPixel(x, y);
+    }
+
+    private int getReferenceBit(final int x, final int y)
+    {
+        return getPixelSafe(referenceBitmap, x - referenceDX, y - referenceDY);
+    }
+
+    private int getRegionBit(final int x, final int y)
+    {
+        return getPixelSafe(regionBitmap, x, y);
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Template 1 context formation — §6.3.5.6, Figure 13
+    // Pixels gathered in reading order, GRREG before GRREFERENCE:
+    //   GRREG:      (x-2,y-1) (x-1,y-1) (x,y-1) (x-1,y)           → bits 9-6
+    //   GRREFERENCE:(x,y-1) (x-1,y) (x,y) (x+1,y) (x,y+1) (x+1,y+1) → bits 5-0
+    // Spec example: only ⊗=(x,y)=1 → CX="GR0000001000"=0x08 → bit 3 ✓
+    // 
-------------------------------------------------------------------------
+
+    private int buildContextT1(final int x, final int y)
+    {
+        return (getRegionBit(x - 1, y - 1) << 9)
+            | (getRegionBit(x,     y - 1) << 8)
+            | (getRegionBit(x + 1, y - 1) << 7)
+            | (getRegionBit(x - 1, y    ) << 6)
+            | (getReferenceBit(x    , y - 1) << 5)
+            | (getReferenceBit(x - 1, y    ) << 4)
+            | (getReferenceBit(x    , y    ) << 3)
+            | (getReferenceBit(x + 1, y    ) << 2)
+            | (getReferenceBit(x,     y + 1) << 1)
+            | (getReferenceBit(x + 1, y + 1));
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Template 1 — explicit decode (LTP=0 path, §6.3.5.6 step 3c)
+    // 
-------------------------------------------------------------------------
+
+    private void decodeLineExplicitT1(final int y, final int width) throws 
IOException
+    {
+        for (int x = 0; x < width; x++)
+        {
+            cx.setIndex(buildContextT1(x, y));
+            regionBitmap.setPixel(x, y, (byte) arithDecoder.decode(cx));
+        }
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Template 1 — typical prediction decode (LTP=1 path, §6.3.5.6 step 3d)
+    // TPGRPIX=1 when the 3×3 reference neighbourhood is uniform (§6.3.5.6 
3d-i)
+    // 
-------------------------------------------------------------------------
+
+    private void decodeLineTPGRT1(final int y, final int width) throws 
IOException
+    {
+        for (int x = 0; x < width; x++)
+        {
+            final int center = getReferenceBit(x, y);
+            boolean uniform = true;
+            outer:
+            for (int dy = -1; dy <= 1; dy++)
+            {
+                for (int dx = -1; dx <= 1; dx++)
+                {
+                    if (getReferenceBit(x + dx, y + dy) != center)
+                    {
+                        uniform = false;
+                        break outer;
+                    }
+                }
+            }
+
+            final int bit;
+            if (uniform)
+            {
+                bit = center;
+            }
+            else
+            {
+                cx.setIndex(buildContextT1(x, y));
+                bit = arithDecoder.decode(cx);
+            }
+            regionBitmap.setPixel(x, y, (byte) bit);
+        }
+    }
+
     // 
-------------------------------------------------------------------------
     // Private decoding helpers (§6.3.5.6 sub-steps)
     // 
-------------------------------------------------------------------------
diff --git a/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java 
b/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java
index 4a709b9..ccded2a 100644
--- a/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java
+++ b/src/test/java/org/apache/pdfbox/jbig2/SerenityTests.java
@@ -17,6 +17,7 @@
 
 package org.apache.pdfbox.jbig2;
 
+import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FilenameFilter;
@@ -25,6 +26,8 @@ import java.io.InputStream;
 import javax.imageio.ImageIO;
 import javax.imageio.stream.ImageInputStream;
 import org.apache.pdfbox.jbig2.err.JBIG2Exception;
+import org.apache.pdfbox.jbig2.image.Bitmaps;
+
 import static org.junit.Assert.assertEquals;
 import org.junit.Test;
 
@@ -59,11 +62,6 @@ public class SerenityTests
         for (File file : files)
         {
             String name = file.getName();
-            // Files that are not properly decoded yet
-            if (name.equals("bitmap-refine-template1-tpgron.jbig2"))
-            {
-                continue;
-            }
             imageIIS = ImageIO.createImageInputStream(file);
             doc = new JBIG2Document(imageIIS);
             page = doc.getPage(1);

Reply via email to