You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by da...@apache.org on 2017/02/05 14:50:41 UTC

svn commit: r1781770 - in /commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx: PcxImageParser.java PcxWriter.java RleReader.java RleWriter.java

Author: damjan
Date: Sun Feb  5 14:50:40 2017
New Revision: 1781770

URL: http://svn.apache.org/viewvc?rev=1781770&view=rev
Log:
Allow PCX's RLE compression to span multiple lines when reading and
when writing. This should not only allow us to read such images, but also
compress the images we write better.

Patch by: me

Added:
    commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java   (with props)
    commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java   (with props)
Modified:
    commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java
    commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java

Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java?rev=1781770&r1=1781769&r2=1781770&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java Sun Feb  5 14:50:40 2017
@@ -17,7 +17,6 @@
 package org.apache.commons.imaging.formats.pcx;
 
 import static org.apache.commons.imaging.ImagingConstants.PARAM_KEY_STRICT;
-import static org.apache.commons.imaging.common.BinaryFunctions.readByte;
 import static org.apache.commons.imaging.common.BinaryFunctions.readBytes;
 import static org.apache.commons.imaging.common.BinaryFunctions.skipBytes;
 import static org.apache.commons.imaging.common.ByteConversions.toUInt16;
@@ -299,43 +298,6 @@ public class PcxImageParser extends Imag
         return true;
     }
 
-    private void readScanLine(final PcxHeader pcxHeader, final InputStream is,
-            final byte[] samples) throws IOException, ImageReadException {
-        if (pcxHeader.encoding == PcxHeader.ENCODING_UNCOMPRESSED) {
-            int r;
-            for (int bytesRead = 0; bytesRead < samples.length; bytesRead += r) {
-                r = is.read(samples, bytesRead, samples.length - bytesRead);
-                if (r < 0) {
-                    throw new ImageReadException(
-                            "Premature end of file reading image data");
-                }
-            }
-        } else {
-            if (pcxHeader.encoding == PcxHeader.ENCODING_RLE) {
-                for (int bytesRead = 0; bytesRead < samples.length;) {
-                    final byte b = readByte("Pixel", is, "Error reading image data");
-                    int count;
-                    byte sample;
-                    if ((b & 0xc0) == 0xc0) {
-                        count = b & 0x3f;
-                        sample = readByte("Pixel", is,
-                                "Error reading image data");
-                    } else {
-                        count = 1;
-                        sample = b;
-                    }
-                    for (int i = 0; i < count && bytesRead + i < samples.length; i++) {
-                        samples[bytesRead + i] = sample;
-                    }
-                    bytesRead += count;
-                }
-            } else {
-                throw new ImageReadException("Invalid PCX encoding "
-                        + pcxHeader.encoding);
-            }
-        }
-    }
-
     private int[] read256ColorPalette(final InputStream stream) throws IOException {
         final byte[] paletteBytes = readBytes("Palette", stream, 769,
                 "Error reading palette");
@@ -371,7 +333,17 @@ public class PcxImageParser extends Imag
         if (ySize < 0) {
             throw new ImageReadException("Image height is negative");
         }
-
+        if (pcxHeader.nPlanes <= 0 || 4 < pcxHeader.nPlanes) {
+            throw new ImageReadException("Unsupported/invalid image with " + pcxHeader.nPlanes + " planes");
+        }
+        final RleReader rleReader;
+        if (pcxHeader.encoding == PcxHeader.ENCODING_UNCOMPRESSED) {
+            rleReader = new RleReader(false);
+        } else if (pcxHeader.encoding == PcxHeader.ENCODING_RLE) {
+            rleReader = new RleReader(true);
+        } else {
+            throw new ImageReadException("Unsupported/invalid image encoding " + pcxHeader.encoding);
+        }
         final int scanlineLength = pcxHeader.bytesPerLine * pcxHeader.nPlanes;
         final byte[] scanline = new byte[scanlineLength];
         if ((pcxHeader.bitsPerPixel == 1 || pcxHeader.bitsPerPixel == 2
@@ -380,7 +352,7 @@ public class PcxImageParser extends Imag
             final int bytesPerImageRow = (xSize * pcxHeader.bitsPerPixel + 7) / 8;
             final byte[] image = new byte[ySize * bytesPerImageRow];
             for (int y = 0; y < ySize; y++) {
-                readScanLine(pcxHeader, is, scanline);
+                rleReader.read(is, scanline);
                 System.arraycopy(scanline, 0, image, y * bytesPerImageRow,
                         bytesPerImageRow);
             }
@@ -429,7 +401,7 @@ public class PcxImageParser extends Imag
                     BufferedImage.TYPE_BYTE_BINARY, colorModel);
             final byte[] unpacked = new byte[xSize];
             for (int y = 0; y < ySize; y++) {
-                readScanLine(pcxHeader, is, scanline);
+                rleReader.read(is, scanline);
                 int nextByte = 0;
                 Arrays.fill(unpacked, (byte) 0);
                 for (int plane = 0; plane < pcxHeader.nPlanes; plane++) {
@@ -449,7 +421,7 @@ public class PcxImageParser extends Imag
             image[1] = new byte[xSize * ySize];
             image[2] = new byte[xSize * ySize];
             for (int y = 0; y < ySize; y++) {
-                readScanLine(pcxHeader, is, scanline);
+                rleReader.read(is, scanline);
                 System.arraycopy(scanline, 0, image[0], y * xSize, xSize);
                 System.arraycopy(scanline, pcxHeader.bytesPerLine, image[1], y
                         * xSize, xSize);
@@ -471,7 +443,7 @@ public class PcxImageParser extends Imag
             final int rowLength = 3 * xSize;
             final byte[] image = new byte[rowLength * ySize];
             for (int y = 0; y < ySize; y++) {
-                readScanLine(pcxHeader, is, scanline);
+                rleReader.read(is, scanline);
                 if (pcxHeader.bitsPerPixel == 24) {
                     System.arraycopy(scanline, 0, image, y * rowLength,
                             rowLength);

Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java?rev=1781770&r1=1781769&r2=1781770&view=diff
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java (original)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java Sun Feb  5 14:50:40 2017
@@ -34,6 +34,7 @@ class PcxWriter {
     private int encoding;
     private int bitDepth = -1;
     private PixelDensity pixelDensity;
+    private final RleWriter rleWriter;
 
     public PcxWriter(Map<String, Object> params) throws ImageWriteException {
         // make copy of params; we'll clear keys as we consume them.
@@ -60,6 +61,11 @@ class PcxWriter {
                 }
             }
         }
+        if (encoding == PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED) {
+            rleWriter = new RleWriter(false);
+        } else {
+            rleWriter = new RleWriter(true);
+        }
 
         if (params.containsKey(PcxConstants.PARAM_KEY_PCX_BIT_DEPTH)) {
             final Object value = params.remove(PcxConstants.PARAM_KEY_PCX_BIT_DEPTH);
@@ -93,47 +99,6 @@ class PcxWriter {
         }
     }
 
-    private void writeScanLine(final BinaryOutputStream bos, final byte[] scanline)
-            throws IOException, ImageWriteException {
-        if (encoding == PcxImageParser.PcxHeader.ENCODING_UNCOMPRESSED) {
-            bos.write(scanline);
-        } else {
-            if (encoding == PcxImageParser.PcxHeader.ENCODING_RLE) {
-                int previousByte = -1;
-                int repeatCount = 0;
-                for (final byte element : scanline) {
-                    if ((element & 0xff) == previousByte
-                            && repeatCount < 63) {
-                        ++repeatCount;
-                    } else {
-                        if (repeatCount > 0) {
-                            if (repeatCount == 1
-                                    && (previousByte & 0xc0) != 0xc0) {
-                                bos.write(previousByte);
-                            } else {
-                                bos.write(0xc0 | repeatCount);
-                                bos.write(previousByte);
-                            }
-                        }
-                        previousByte = 0xff & element;
-                        repeatCount = 1;
-                    }
-                }
-                if (repeatCount > 0) {
-                    if (repeatCount == 1 && (previousByte & 0xc0) != 0xc0) {
-                        bos.write(previousByte);
-                    } else {
-                        bos.write(0xc0 | repeatCount);
-                        bos.write(previousByte);
-                    }
-                }
-            } else {
-                throw new ImageWriteException("Invalid PCX encoding "
-                        + encoding);
-            }
-        }
-    }
-
     public void writeImage(final BufferedImage src, final OutputStream os)
             throws ImageWriteException, IOException {
         final PaletteFactory paletteFactory = new PaletteFactory();
@@ -206,8 +171,9 @@ class PcxWriter {
                 rgbBytes[4 * x + 2] = (byte) ((rgbs[x] >> 16) & 0xff);
                 rgbBytes[4 * x + 3] = 0;
             }
-            writeScanLine(bos, rgbBytes);
+            rleWriter.write(bos, rgbBytes);
         }
+        rleWriter.flush(bos);
     }
 
     private void write24BppPCX(final BufferedImage src, final BinaryOutputStream bos)
@@ -243,8 +209,9 @@ class PcxWriter {
                 rgbBytes[bytesPerLine + x] = (byte) ((rgbs[x] >> 8) & 0xff);
                 rgbBytes[2 * bytesPerLine + x] = (byte) (rgbs[x] & 0xff);
             }
-            writeScanLine(bos, rgbBytes);
+            rleWriter.write(bos, rgbBytes);
         }
+        rleWriter.flush(bos);
     }
 
     private void writeBlackAndWhitePCX(final BufferedImage src,
@@ -292,8 +259,9 @@ class PcxWriter {
                 }
                 row[x / 8] |= (bit << (7 - (x % 8)));
             }
-            writeScanLine(bos, row);
+            rleWriter.write(bos, row);
         }
+        rleWriter.flush(bos);
     }
 
     private void write16ColorPCX(final BufferedImage src, final SimplePalette palette,
@@ -344,8 +312,9 @@ class PcxWriter {
                 final int index = palette.getPaletteIndex(0xffffff & argb);
                 indeces[x / 2] |= (index << 4 * (1 - (x % 2)));
             }
-            writeScanLine(bos, indeces);
+            rleWriter.write(bos, indeces);
         }
+        rleWriter.flush(bos);
     }
 
     private void write256ColorPCX(final BufferedImage src, final SimplePalette palette,
@@ -379,8 +348,9 @@ class PcxWriter {
                 final int index = palette.getPaletteIndex(0xffffff & argb);
                 indeces[x] = (byte) index;
             }
-            writeScanLine(bos, indeces);
+            rleWriter.write(bos, indeces);
         }
+        rleWriter.flush(bos);
         // palette
         bos.write(12);
         for (int i = 0; i < 256; i++) {

Added: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java?rev=1781770&view=auto
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java (added)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java Sun Feb  5 14:50:40 2017
@@ -0,0 +1,65 @@
+/*
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  under the License.
+ */
+
+package org.apache.commons.imaging.formats.pcx;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.common.BinaryFunctions;
+
+class RleReader {
+    private final boolean isCompressed;
+    private int count;
+    private byte sample;
+    
+    RleReader(final boolean isCompressed) {
+        this.isCompressed = isCompressed;
+    }
+    
+    void read(final InputStream is, final byte[] samples) throws IOException, ImageReadException {
+        if (isCompressed) {
+            final int prefill = Math.min(count, samples.length);
+            Arrays.fill(samples, 0, prefill, sample);
+            count -= prefill;
+            
+            for (int bytesRead = prefill; bytesRead < samples.length;) {
+                final byte b = BinaryFunctions.readByte("RleByte", is, "Error reading image data");
+                if ((b & 0xc0) == 0xc0) {
+                    count = b & 0x3f;
+                    sample = BinaryFunctions.readByte("RleValue", is, "Error reading image data");
+                } else {
+                    count = 1;
+                    sample = b;
+                }
+                final int samplesToAdd = Math.min(count, samples.length - bytesRead);
+                Arrays.fill(samples, bytesRead, bytesRead + samplesToAdd, sample);
+                bytesRead += samplesToAdd;
+                count -= samplesToAdd;
+            }
+        } else {
+            int r;
+            for (int bytesRead = 0; bytesRead < samples.length; bytesRead += r) {
+                r = is.read(samples, bytesRead, samples.length - bytesRead);
+                if (r < 0) {
+                    throw new ImageReadException(
+                            "Premature end of file reading image data");
+                }
+            }
+        }
+    }
+}

Propchange: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java
URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java?rev=1781770&view=auto
==============================================================================
--- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java (added)
+++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java Sun Feb  5 14:50:40 2017
@@ -0,0 +1,68 @@
+/*
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *  under the License.
+ */
+
+package org.apache.commons.imaging.formats.pcx;
+
+import java.io.IOException;
+
+import org.apache.commons.imaging.ImageWriteException;
+import org.apache.commons.imaging.common.BinaryOutputStream;
+
+class RleWriter {
+    private final boolean isCompressed;
+    private int previousByte = -1;
+    private int repeatCount = 0;
+
+    RleWriter(final boolean isCompressed) {
+        this.isCompressed = isCompressed;
+    }
+    
+    void write(final BinaryOutputStream bos, final byte[] samples)
+            throws IOException, ImageWriteException {
+        if (isCompressed) {
+            for (final byte element : samples) {
+                if ((element & 0xff) == previousByte
+                        && repeatCount < 63) {
+                    ++repeatCount;
+                } else {
+                    if (repeatCount > 0) {
+                        if (repeatCount == 1
+                                && (previousByte & 0xc0) != 0xc0) {
+                            bos.write(previousByte);
+                        } else {
+                            bos.write(0xc0 | repeatCount);
+                            bos.write(previousByte);
+                        }
+                    }
+                    previousByte = 0xff & element;
+                    repeatCount = 1;
+                }
+            }
+        } else {
+            bos.write(samples);
+        }
+    }
+    
+    void flush(final BinaryOutputStream bos) throws IOException {
+        if (repeatCount > 0) {
+            if (repeatCount == 1 && (previousByte & 0xc0) != 0xc0) {
+                bos.write(previousByte);
+            } else {
+                bos.write(0xc0 | repeatCount);
+                bos.write(previousByte);
+            }
+        }
+    }
+}

Propchange: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native