Allow giving PCX a hint as to the number of planes to use, and by default,
write 16 color images using 4 planes of 1 bit instead of 1 plane of 4 bits,
as GIMP and Apache OpenOffice support the former but not the latter.

Patch by: me


git-svn-id: 
https://svn.apache.org/repos/asf/commons/proper/imaging/trunk@1781785 
13f79535-47bb-0310-9956-ffa450edef68


Project: http://git-wip-us.apache.org/repos/asf/commons-imaging/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-imaging/commit/e745858e
Tree: http://git-wip-us.apache.org/repos/asf/commons-imaging/tree/e745858e
Diff: http://git-wip-us.apache.org/repos/asf/commons-imaging/diff/e745858e

Branch: refs/heads/master
Commit: e745858e4942c5faa73af661b441e8c0d55d8b09
Parents: 2c61aaa
Author: Damjan Jovanovic <[email protected]>
Authored: Sun Feb 5 17:07:51 2017 +0000
Committer: Damjan Jovanovic <[email protected]>
Committed: Sun Feb 5 17:07:51 2017 +0000

----------------------------------------------------------------------
 .../imaging/formats/pcx/PcxConstants.java       |  2 +
 .../commons/imaging/formats/pcx/PcxWriter.java  | 91 +++++++++++++++++++-
 2 files changed, 90 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-imaging/blob/e745858e/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java 
b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
index 864123a..a491786 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java
@@ -22,6 +22,8 @@ public final class PcxConstants {
 
     public static final String PARAM_KEY_PCX_BIT_DEPTH = "PCX_BIT_DEPTH";
 
+    public static final String PARAM_KEY_PCX_PLANES = "PCX_PLANES";
+    
     private PcxConstants() {
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-imaging/blob/e745858e/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java 
b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
index 8b758a5..3468df4 100644
--- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
+++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java
@@ -33,6 +33,7 @@ import org.apache.commons.imaging.palette.SimplePalette;
 class PcxWriter {
     private int encoding;
     private int bitDepth = -1;
+    private int planes = -1;
     private PixelDensity pixelDensity;
     private final RleWriter rleWriter;
 
@@ -77,6 +78,17 @@ class PcxWriter {
                 bitDepth = ((Number) value).intValue();
             }
         }
+        
+        if (params.containsKey(PcxConstants.PARAM_KEY_PCX_PLANES)) {
+            final Object value = 
params.remove(PcxConstants.PARAM_KEY_PCX_PLANES);
+            if (value != null) {
+                if (!(value instanceof Number)) {
+                    throw new ImageWriteException(
+                            "Invalid planes parameter: " + value);
+                }
+                planes = ((Number) value).intValue();
+            }
+        }
 
         if (params.containsKey(ImagingConstants.PARAM_KEY_PIXEL_DENSITY)) {
             final Object value = 
params.remove(ImagingConstants.PARAM_KEY_PIXEL_DENSITY);
@@ -114,7 +126,11 @@ class PcxWriter {
         } else if (palette.length() > 16 || bitDepth == 8) {
             write256ColorPCX(src, palette, bos);
         } else if (palette.length() > 2 || bitDepth == 4) {
-            write16ColorPCX(src, palette, bos);
+            if (planes == 1) {
+                write16ColorPCXIn1Plane(src, palette, bos);
+            } else {
+                write16ColorPCXIn4Planes(src, palette, bos);
+            }
         } else {
             boolean onlyBlackAndWhite = true;
             if (palette.length() >= 1) {
@@ -132,7 +148,11 @@ class PcxWriter {
             if (onlyBlackAndWhite) {
                 writeBlackAndWhitePCX(src, bos);
             } else {
-                write16ColorPCX(src, palette, bos);
+                if (planes == 1) {
+                    write16ColorPCXIn1Plane(src, palette, bos);
+                } else {
+                    write16ColorPCXIn4Planes(src, palette, bos);
+                }
             }
         }
     }
@@ -264,7 +284,7 @@ class PcxWriter {
         rleWriter.flush(bos);
     }
 
-    private void write16ColorPCX(final BufferedImage src, final SimplePalette 
palette,
+    private void write16ColorPCXIn1Plane(final BufferedImage src, final 
SimplePalette palette,
             final BinaryOutputStream bos) throws ImageWriteException, 
IOException {
         int bytesPerLine = (src.getWidth() + 1) / 2;
         if (bytesPerLine % 2 != 0) {
@@ -317,6 +337,71 @@ class PcxWriter {
         rleWriter.flush(bos);
     }
 
+    private void write16ColorPCXIn4Planes(final BufferedImage src, final 
SimplePalette palette,
+            final BinaryOutputStream bos) throws ImageWriteException, 
IOException {
+        int bytesPerLine = (src.getWidth() + 7) / 8;
+        if (bytesPerLine % 2 != 0) {
+            ++bytesPerLine;
+        }
+
+        final byte[] palette16 = new byte[16 * 3];
+        for (int i = 0; i < 16; i++) {
+            int rgb;
+            if (i < palette.length()) {
+                rgb = palette.getEntry(i);
+            } else {
+                rgb = 0;
+            }
+            palette16[3 * i + 0] = (byte) (0xff & (rgb >> 16));
+            palette16[3 * i + 1] = (byte) (0xff & (rgb >> 8));
+            palette16[3 * i + 2] = (byte) (0xff & rgb);
+        }
+
+        // PCX header
+        bos.write(10); // manufacturer
+        bos.write(5); // version
+        bos.write(encoding); // encoding
+        bos.write(1); // bits per pixel
+        bos.write2Bytes(0); // xMin
+        bos.write2Bytes(0); // yMin
+        bos.write2Bytes(src.getWidth() - 1); // xMax
+        bos.write2Bytes(src.getHeight() - 1); // yMax
+        bos.write2Bytes((short) 
Math.round(pixelDensity.horizontalDensityInches())); // hDpi
+        bos.write2Bytes((short) 
Math.round(pixelDensity.verticalDensityInches())); // vDpi
+        bos.write(palette16); // 16 color palette
+        bos.write(0); // reserved
+        bos.write(4); // planes
+        bos.write2Bytes(bytesPerLine); // bytes per line
+        bos.write2Bytes(1); // palette info
+        bos.write2Bytes(0); // hScreenSize
+        bos.write2Bytes(0); // vScreenSize
+        bos.write(new byte[54]);
+
+        final byte[] plane0 = new byte[bytesPerLine];
+        final byte[] plane1 = new byte[bytesPerLine];
+        final byte[] plane2 = new byte[bytesPerLine];
+        final byte[] plane3 = new byte[bytesPerLine];
+        for (int y = 0; y < src.getHeight(); y++) {
+            Arrays.fill(plane0, (byte)0);
+            Arrays.fill(plane1, (byte)0);
+            Arrays.fill(plane2, (byte)0);
+            Arrays.fill(plane3, (byte)0);
+            for (int x = 0; x < src.getWidth(); x++) {
+                final int argb = src.getRGB(x, y);
+                final int index = palette.getPaletteIndex(0xffffff & argb);
+                plane0[x >>> 3] |= (index & 1) << (7 - (x & 7));
+                plane1[x >>> 3] |= ((index & 2) >> 1) << (7 - (x & 7));
+                plane2[x >>> 3] |= ((index & 4) >> 2) << (7 - (x & 7));
+                plane3[x >>> 3] |= ((index & 8) >> 3) << (7 - (x & 7));
+            }
+            rleWriter.write(bos, plane0);
+            rleWriter.write(bos, plane1);
+            rleWriter.write(bos, plane2);
+            rleWriter.write(bos, plane3);
+        }
+        rleWriter.flush(bos);
+    }
+
     private void write256ColorPCX(final BufferedImage src, final SimplePalette 
palette,
             final BinaryOutputStream bos) throws ImageWriteException, 
IOException {
         final int bytesPerLine = src.getWidth() % 2 == 0 ? src.getWidth() : 
src.getWidth() + 1;

Reply via email to