Author: damjan Date: Thu Feb 9 00:03:32 2017 New Revision: 1782282 URL: http://svn.apache.org/viewvc?rev=1782282&view=rev Log: Add support for writing 4 color PCX images, and clean up and simplify the writing code.
Patch by: me Modified: 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/PcxWriter.java URL: http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/pcx/PcxWriter.java?rev=1782282&r1=1782281&r2=1782282&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 Thu Feb 9 00:03:32 2017 @@ -32,8 +32,8 @@ import org.apache.commons.imaging.palett class PcxWriter { private int encoding; - private int bitDepth = -1; - private int planes = -1; + private int bitDepthWanted = -1; + private int planesWanted = -1; private PixelDensity pixelDensity; private final RleWriter rleWriter; @@ -75,7 +75,7 @@ class PcxWriter { throw new ImageWriteException( "Invalid bit depth parameter: " + value); } - bitDepth = ((Number) value).intValue(); + bitDepthWanted = ((Number) value).intValue(); } } @@ -86,7 +86,7 @@ class PcxWriter { throw new ImageWriteException( "Invalid planes parameter: " + value); } - planes = ((Number) value).intValue(); + planesWanted = ((Number) value).intValue(); } } @@ -110,29 +110,40 @@ class PcxWriter { throw new ImageWriteException("Unknown parameter: " + firstKey); } } - + public void writeImage(final BufferedImage src, final OutputStream os) throws ImageWriteException, IOException { final PaletteFactory paletteFactory = new PaletteFactory(); final SimplePalette palette = paletteFactory.makeExactRgbPaletteSimple(src, 256); final BinaryOutputStream bos = new BinaryOutputStream(os, ByteOrder.LITTLE_ENDIAN); - if (palette == null || bitDepth == 24 || bitDepth == 32) { - if (bitDepth == 32) { - write32BppPCX(src, bos); + final int bitDepth; + final int planes; + if (palette == null || bitDepthWanted == 24 || bitDepthWanted == 32) { + if (bitDepthWanted == 32) { + bitDepth = 32; + planes = 1; } else { - write24BppPCX(src, bos); + bitDepth = 8; + planes = 3; } - } else if (palette.length() > 16 || bitDepth == 8) { - write256ColorPCX(src, palette, bos); - } else if (palette.length() > 8 || bitDepth == 4) { - if (planes == 1) { - write16ColorPCXIn1Plane(src, palette, bos); + } else if (palette.length() > 16 || bitDepthWanted == 8) { + bitDepth = 8; + planes = 1; + } else if (palette.length() > 8 || bitDepthWanted == 4) { + if (planesWanted == 1) { + bitDepth = 4; + planes = 1; } else { - write16ColorPCXIn4Planes(src, palette, bos); + bitDepth = 1; + planes = 4; } - } else if (palette.length() > 2 || bitDepth == 3) { - write8ColorPcx(src, palette, bos); + } else if (palette.length() > 4 || bitDepthWanted == 3) { + bitDepth = 1; + planes = 3; + } else if (palette.length() > 2 || bitDepthWanted == 2) { + bitDepth = 1; + planes = 2; } else { boolean onlyBlackAndWhite = true; if (palette.length() >= 1) { @@ -148,147 +159,20 @@ class PcxWriter { } } if (onlyBlackAndWhite) { - writeBlackAndWhitePCX(src, bos); + bitDepth = 1; + planes = 1; } else { - write8ColorPcx(src, palette, bos); - } - } - } - - private void write32BppPCX(final BufferedImage src, final BinaryOutputStream bos) - throws ImageWriteException, IOException { - final int bytesPerLine = src.getWidth() % 2 == 0 ? src.getWidth() : src.getWidth() + 1; - - // PCX header - bos.write(10); // manufacturer - bos.write(5); // version - bos.write(encoding); // encoding - bos.write(32); // 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(new byte[48]); // 16 color palette - bos.write(0); // reserved - bos.write(1); // 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 int[] rgbs = new int[src.getWidth()]; - final byte[] rgbBytes = new byte[4 * bytesPerLine]; - for (int y = 0; y < src.getHeight(); y++) { - src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth()); - for (int x = 0; x < rgbs.length; x++) { - rgbBytes[4 * x + 0] = (byte) (rgbs[x] & 0xff); - rgbBytes[4 * x + 1] = (byte) ((rgbs[x] >> 8) & 0xff); - rgbBytes[4 * x + 2] = (byte) ((rgbs[x] >> 16) & 0xff); - rgbBytes[4 * x + 3] = 0; - } - rleWriter.write(bos, rgbBytes); - } - rleWriter.flush(bos); - } - - private void write24BppPCX(final BufferedImage src, final BinaryOutputStream bos) - throws ImageWriteException, IOException { - final int bytesPerLine = src.getWidth() % 2 == 0 ? src.getWidth() : src.getWidth() + 1; - - // PCX header - bos.write(10); // manufacturer - bos.write(5); // version - bos.write(encoding); // encoding - bos.write(8); // 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(new byte[48]); // 16 color palette - bos.write(0); // reserved - bos.write(3); // 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 int[] rgbs = new int[src.getWidth()]; - final byte[] rgbBytes = new byte[3 * bytesPerLine]; - for (int y = 0; y < src.getHeight(); y++) { - src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth()); - for (int x = 0; x < rgbs.length; x++) { - rgbBytes[x] = (byte) ((rgbs[x] >> 16) & 0xff); - rgbBytes[bytesPerLine + x] = (byte) ((rgbs[x] >> 8) & 0xff); - rgbBytes[2 * bytesPerLine + x] = (byte) (rgbs[x] & 0xff); + bitDepth = 1; + planes = 2; } - rleWriter.write(bos, rgbBytes); } - rleWriter.flush(bos); - } - - private void writeBlackAndWhitePCX(final BufferedImage src, - final BinaryOutputStream bos) - throws ImageWriteException, IOException { - int bytesPerLine = (src.getWidth() + 7) / 8; - if (bytesPerLine % 2 != 0) { - ++bytesPerLine; - } - - // PCX header - bos.write(10); // manufacturer - bos.write(3); // version - it seems some apps only open - // black and white files if the version is 3... - 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(new byte[48]); // 16 color palette - bos.write(0); // reserved - bos.write(1); // 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[] row = new byte[bytesPerLine]; - for (int y = 0; y < src.getHeight(); y++) { - Arrays.fill(row, (byte) 0); - for (int x = 0; x < src.getWidth(); x++) { - final int rgb = 0xffffff & src.getRGB(x, y); - int bit; - if (rgb == 0x000000) { - bit = 0; - } else if (rgb == 0xffffff) { - bit = 1; - } else { - throw new ImageWriteException( - "Pixel neither black nor white"); - } - row[x / 8] |= (bit << (7 - (x % 8))); - } - rleWriter.write(bos, row); - } - rleWriter.flush(bos); - } - - 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) { - ++bytesPerLine; + + int bytesPerLine = (bitDepth * src.getWidth() + 7) / 8; + if ((bytesPerLine % 2) != 0) { + // must be even: + bytesPerLine++; } - + final byte[] palette16 = new byte[16 * 3]; for (int i = 0; i < 16; i++) { int rgb; @@ -304,9 +188,9 @@ class PcxWriter { // PCX header bos.write(10); // manufacturer - bos.write(5); // version + bos.write(bitDepth == 1 && planes == 1 ? 3 : 5); // version. Some apps only open black and white PCX with version=3. bos.write(encoding); // encoding - bos.write(4); // bits per pixel + bos.write(bitDepth); // bits per pixel bos.write2Bytes(0); // xMin bos.write2Bytes(0); // yMin bos.write2Bytes(src.getWidth() - 1); // xMax @@ -315,198 +199,127 @@ class PcxWriter { bos.write2Bytes((short) Math.round(pixelDensity.verticalDensityInches())); // vDpi bos.write(palette16); // 16 color palette bos.write(0); // reserved - bos.write(1); // planes + bos.write(planes); // 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[] indeces = new byte[bytesPerLine]; - for (int y = 0; y < src.getHeight(); y++) { - Arrays.fill(indeces, (byte) 0); - for (int x = 0; x < src.getWidth(); x++) { - final int argb = src.getRGB(x, y); - final int index = palette.getPaletteIndex(0xffffff & argb); - indeces[x / 2] |= (index << 4 * (1 - (x % 2))); - } - rleWriter.write(bos, indeces); - } - 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; + if (bitDepth == 32) { + writePixels32(src, bytesPerLine, bos); + } else { + writePixels(src, bitDepth, planes, bytesPerLine, palette, bos); } - - 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; + + if (bitDepth == 8 && planes == 1) { + // 256 color palette + bos.write(12); + for (int i = 0; i < 256; i++) { + int rgb; + if (i < palette.length()) { + rgb = palette.getEntry(i); + } else { + rgb = 0; + } + bos.write((rgb >> 16) & 0xff); + bos.write((rgb >> 8) & 0xff); + bos.write(rgb & 0xff); } - 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]); - + } + + private void writePixels(final BufferedImage src, final int bitDepth, final int planes, + final int bytesPerLine, final SimplePalette palette, final BinaryOutputStream bos) throws IOException, ImageWriteException { final byte[] plane0 = new byte[bytesPerLine]; final byte[] plane1 = new byte[bytesPerLine]; final byte[] plane2 = new byte[bytesPerLine]; final byte[] plane3 = new byte[bytesPerLine]; + final byte[][] allPlanes = { plane0, plane1, plane2, plane3 }; + 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 write8ColorPcx(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 < 8; i++) { - int rgb; - if (i < palette.length()) { - rgb = palette.getEntry(i); - } else { - rgb = 0; + for (int i = 0; i < planes; i++) { + Arrays.fill(allPlanes[i], (byte)0); + } + + if (bitDepth == 1 && planes == 1) { + for (int x = 0; x < src.getWidth(); x++) { + final int rgb = 0xffffff & src.getRGB(x, y); + int bit; + if (rgb == 0x000000) { + bit = 0; + } else { + bit = 1; + } + plane0[x >>> 3] |= (bit << (7 - (x & 7))); + } + } else if (bitDepth == 1 && planes == 2) { + 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)); + } + } else if (bitDepth == 1 && planes == 3) { + 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)); + } + } else if (bitDepth == 1 && planes == 4) { + 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)); + } + } else if (bitDepth == 4 && planes == 1) { + for (int x = 0; x < src.getWidth(); x++) { + final int argb = src.getRGB(x, y); + final int index = palette.getPaletteIndex(0xffffff & argb); + plane0[x >>> 1] |= (index << 4 * (1 - (x & 1))); + } + } else if (bitDepth == 8 && planes == 1) { + for (int x = 0; x < src.getWidth(); x++) { + final int argb = src.getRGB(x, y); + final int index = palette.getPaletteIndex(0xffffff & argb); + plane0[x] = (byte) index; + } + } else if (bitDepth == 8 && planes == 3) { + for (int x = 0; x < src.getWidth(); x++) { + final int argb = src.getRGB(x, y); + plane0[x] = (byte) (argb >>> 16); + plane1[x] = (byte) (argb >>> 8); + plane2[x] = (byte) argb; + } + } + + for (int i = 0; i < planes; i++) { + rleWriter.write(bos, allPlanes[i]); } - 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(3); // 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]; - for (int y = 0; y < src.getHeight(); y++) { - Arrays.fill(plane0, (byte)0); - Arrays.fill(plane1, (byte)0); - Arrays.fill(plane2, (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)); - } - rleWriter.write(bos, plane0); - rleWriter.write(bos, plane1); - rleWriter.write(bos, plane2); } 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; - - // PCX header - bos.write(10); // manufacturer - bos.write(5); // version - bos.write(encoding); // encoding - bos.write(8); // 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(new byte[48]); // 16 color palette - bos.write(0); // reserved - bos.write(1); // 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[] indeces = new byte[bytesPerLine]; + private void writePixels32(final BufferedImage src, final int bytesPerLine, + final BinaryOutputStream bos) throws IOException, ImageWriteException { + + final int[] rgbs = new int[src.getWidth()]; + final byte[] plane = new byte[4 * bytesPerLine]; for (int y = 0; y < src.getHeight(); y++) { - for (int x = 0; x < src.getWidth(); x++) { - final int argb = src.getRGB(x, y); - final int index = palette.getPaletteIndex(0xffffff & argb); - indeces[x] = (byte) index; + src.getRGB(0, y, src.getWidth(), 1, rgbs, 0, src.getWidth()); + for (int x = 0; x < rgbs.length; x++) { + plane[4 * x + 0] = (byte) rgbs[x]; + plane[4 * x + 1] = (byte) (rgbs[x] >> 8); + plane[4 * x + 2] = (byte) (rgbs[x] >> 16); + plane[4 * x + 3] = 0; } - rleWriter.write(bos, indeces); + rleWriter.write(bos, plane); } rleWriter.flush(bos); - // palette - bos.write(12); - for (int i = 0; i < 256; i++) { - int rgb; - if (i < palette.length()) { - rgb = palette.getEntry(i); - } else { - rgb = 0; - } - bos.write((rgb >> 16) & 0xff); - bos.write((rgb >> 8) & 0xff); - bos.write(rgb & 0xff); - } } }