Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 7FD42200C8E for ; Wed, 24 May 2017 20:41:01 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 7E9B6160BDB; Wed, 24 May 2017 18:41:01 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 76BC9160BB6 for ; Wed, 24 May 2017 20:41:00 +0200 (CEST) Received: (qmail 48745 invoked by uid 500); 24 May 2017 18:40:51 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 46905 invoked by uid 99); 24 May 2017 18:40:50 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 24 May 2017 18:40:50 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 29A43DFF9F; Wed, 24 May 2017 18:40:50 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: chtompki@apache.org To: commits@commons.apache.org Date: Wed, 24 May 2017 18:41:24 -0000 Message-Id: <0296a4d2383449609ba8f6e74f1e7dbe@git.apache.org> In-Reply-To: <2e70e8bd26bc450bada2a102897aebff@git.apache.org> References: <2e70e8bd26bc450bada2a102897aebff@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [36/50] [abbrv] commons-imaging git commit: 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. archived-at: Wed, 24 May 2017 18:41:01 -0000 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 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/imaging/trunk@1781770 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/2c61aaa6 Tree: http://git-wip-us.apache.org/repos/asf/commons-imaging/tree/2c61aaa6 Diff: http://git-wip-us.apache.org/repos/asf/commons-imaging/diff/2c61aaa6 Branch: refs/heads/master Commit: 2c61aaa61a74551573a7946cbf5f35727d405a59 Parents: 231f378 Author: Damjan Jovanovic Authored: Sun Feb 5 14:50:40 2017 +0000 Committer: Damjan Jovanovic Committed: Sun Feb 5 14:50:40 2017 +0000 ---------------------------------------------------------------------- .../imaging/formats/pcx/PcxImageParser.java | 58 +++++------------ .../commons/imaging/formats/pcx/PcxWriter.java | 62 +++++------------- .../commons/imaging/formats/pcx/RleReader.java | 65 +++++++++++++++++++ .../commons/imaging/formats/pcx/RleWriter.java | 68 ++++++++++++++++++++ 4 files changed, 164 insertions(+), 89 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-imaging/blob/2c61aaa6/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java index 6566078..909371e 100644 --- a/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java +++ b/src/main/java/org/apache/commons/imaging/formats/pcx/PcxImageParser.java @@ -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 ImageParser { 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 ImageParser { 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 ImageParser { 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 ImageParser { 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 ImageParser { 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 ImageParser { 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); http://git-wip-us.apache.org/repos/asf/commons-imaging/blob/2c61aaa6/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 0bc9baa..8b758a5 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 @@ -34,6 +34,7 @@ class PcxWriter { private int encoding; private int bitDepth = -1; private PixelDensity pixelDensity; + private final RleWriter rleWriter; public PcxWriter(Map 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++) { http://git-wip-us.apache.org/repos/asf/commons-imaging/blob/2c61aaa6/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java b/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java new file mode 100644 index 0000000..735a205 --- /dev/null +++ b/src/main/java/org/apache/commons/imaging/formats/pcx/RleReader.java @@ -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"); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/commons-imaging/blob/2c61aaa6/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java b/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java new file mode 100644 index 0000000..ce4e3ea --- /dev/null +++ b/src/main/java/org/apache/commons/imaging/formats/pcx/RleWriter.java @@ -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); + } + } + } +}