Author: damjan Date: Sun Feb 5 17:07:51 2017 New Revision: 1781785 URL: http://svn.apache.org/viewvc?rev=1781785&view=rev Log: 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 Modified: commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.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/PcxConstants.java URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java?rev=1781785&r1=1781784&r2=1781785&view=diff ============================================================================== --- commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java (original) +++ commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxConstants.java Sun Feb 5 17:07:51 2017 @@ -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() { } } 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=1781785&r1=1781784&r2=1781785&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 17:07:51 2017 @@ -33,6 +33,7 @@ import org.apache.commons.imaging.palett 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) { @@ -316,6 +336,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 {