From 921b33825340d27deec2883ded21cb7434decc94 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Mon, 31 Dec 2012 16:39:15 +0100 Subject: Update PNGJ 0.85 -> 1.12 (w/ interlace read support) ; Added PNG Interlace read tests (TestPNGTextureFromFileNEWT) --- .../classes/jogamp/opengl/util/pngj/PngWriter.java | 677 ++++++++++++++------- 1 file changed, 444 insertions(+), 233 deletions(-) (limited to 'src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java') diff --git a/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java b/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java index ee8472bf0..601cd96c0 100644 --- a/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java +++ b/src/jogl/classes/jogamp/opengl/util/pngj/PngWriter.java @@ -7,51 +7,84 @@ import java.util.List; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; +import jogamp.opengl.util.pngj.ImageLine.SampleType; import jogamp.opengl.util.pngj.chunks.ChunkCopyBehaviour; import jogamp.opengl.util.pngj.chunks.ChunkHelper; -import jogamp.opengl.util.pngj.chunks.ChunkList; +import jogamp.opengl.util.pngj.chunks.ChunksList; +import jogamp.opengl.util.pngj.chunks.ChunksListForWrite; import jogamp.opengl.util.pngj.chunks.PngChunk; import jogamp.opengl.util.pngj.chunks.PngChunkIEND; import jogamp.opengl.util.pngj.chunks.PngChunkIHDR; +import jogamp.opengl.util.pngj.chunks.PngChunkSkipped; import jogamp.opengl.util.pngj.chunks.PngChunkTextVar; import jogamp.opengl.util.pngj.chunks.PngMetadata; - /** - * Writes a PNG image, line by line. + * Writes a PNG image */ public class PngWriter { public final ImageInfo imgInfo; - protected int compLevel = 6; // zip compression level 0 - 9 - private int deflaterStrategy = Deflater.FILTERED; - protected FilterWriteStrategy filterStrat; + private final String filename; // optional, can be a description + + /** + * last read row number, starting from 0 + */ + protected int rowNum = -1; + + private final ChunksListForWrite chunksList; + private final PngMetadata metadata; // high level wrapper over chunkList + + /** + * Current chunk grounp, (0-6) already read or reading + *

+ * see {@link ChunksList} + */ protected int currentChunkGroup = -1; - protected int rowNum = -1; // current line number - // current line, one (packed) sample per element (layout differnt from rowb!) - protected int[] scanline = null; - protected byte[] rowb = null; // element 0 is filter type! - protected byte[] rowbprev = null; // rowb prev - protected byte[] rowbfilter = null; // current line with filter + /** + * PNG filter strategy + */ + protected FilterWriteStrategy filterStrat; - protected final OutputStream os; - protected final String filename; // optional, can be a description + /** + * zip compression level 0 - 9 + */ + private int compLevel = 6; + private boolean shouldCloseStream = true; // true: closes stream after ending write private PngIDatChunkOutputStream datStream; + private DeflaterOutputStream datStreamDeflated; - private final ChunkList chunkList; - private final PngMetadata metadata; // high level wrapper over chunkList + /** + * Deflate algortithm compression strategy + */ + private int deflaterStrategy = Deflater.FILTERED; + + private int[] histox = new int[256]; // auxiliar buffer, only used by reportResultsForFilter + + private int idatMaxSize = 0; // 0=use default (PngIDatChunkOutputStream 32768) + + private final OutputStream os; + + protected byte[] rowb = null; // element 0 is filter type! + protected byte[] rowbfilter = null; // current line with filter + + protected byte[] rowbprev = null; // rowb prev + + // this only influences the 1-2-4 bitdepth format - and if we pass a ImageLine to writeRow, this is ignored + private boolean unpackedMode = false; public PngWriter(OutputStream outputStream, ImageInfo imgInfo) { this(outputStream, imgInfo, "[NO FILENAME AVAILABLE]"); } /** - * Constructs a new PngWriter from a output stream. + * Constructs a new PngWriter from a output stream. After construction nothing is writen yet. You still can set some + * parameters (compression, filters) and queue chunks before start writing the pixels. *

* See also FileHelper.createPngWriter() if available. * @@ -67,171 +100,156 @@ public class PngWriter { this.os = outputStream; this.imgInfo = imgInfo; // prealloc - scanline = new int[imgInfo.samplesPerRowP]; rowb = new byte[imgInfo.bytesPerRow + 1]; rowbprev = new byte[rowb.length]; rowbfilter = new byte[rowb.length]; - datStream = new PngIDatChunkOutputStream(this.os); - chunkList = new ChunkList(imgInfo); - metadata = new PngMetadata(chunkList, false); - filterStrat = new FilterWriteStrategy(imgInfo, FilterType.FILTER_DEFAULT); + chunksList = new ChunksListForWrite(imgInfo); + metadata = new PngMetadata(chunksList); + filterStrat = new FilterWriteStrategy(imgInfo, FilterType.FILTER_DEFAULT); // can be changed } - /** - * Write id signature and also "IHDR" chunk - */ - private void writeSignatureAndIHDR() { - currentChunkGroup = ChunkList.CHUNK_GROUP_0_IDHR; - if (datStreamDeflated == null) { - Deflater def = new Deflater(compLevel); - def.setStrategy(deflaterStrategy); - datStreamDeflated = new DeflaterOutputStream(datStream, def, 8192); + private void init() { + datStream = new PngIDatChunkOutputStream(this.os, idatMaxSize); + Deflater def = new Deflater(compLevel); + def.setStrategy(deflaterStrategy); + datStreamDeflated = new DeflaterOutputStream(datStream, def); + writeSignatureAndIHDR(); + writeFirstChunks(); + } + + private void reportResultsForFilter(int rown, FilterType type, boolean tentative) { + Arrays.fill(histox, 0); + int s = 0, v; + for (int i = 1; i <= imgInfo.bytesPerRow; i++) { + v = rowbfilter[i]; + if (v < 0) + s -= (int) v; + else + s += (int) v; + histox[v & 0xFF]++; } - PngHelper.writeBytes(os, PngHelper.pngIdBytes); // signature - PngChunkIHDR ihdr = new PngChunkIHDR(imgInfo); - // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html - ihdr.setCols(imgInfo.cols); - ihdr.setRows(imgInfo.rows); - ihdr.setBitspc(imgInfo.bitDepth); - int colormodel = 0; - if (imgInfo.alpha) - colormodel += 0x04; - if (imgInfo.indexed) - colormodel += 0x01; - if (!imgInfo.greyscale) - colormodel += 0x02; - ihdr.setColormodel(colormodel); - ihdr.setCompmeth(0); // compression method 0=deflate - ihdr.setFilmeth(0); // filter method (0) - ihdr.setInterlaced(0); // we never interlace - ihdr.createChunk().writeChunk(os); + filterStrat.fillResultsForFilter(rown, type, s, histox, tentative); + } + private void writeEndChunk() { + PngChunkIEND c = new PngChunkIEND(imgInfo); + c.createRawChunk().writeChunk(os); } private void writeFirstChunks() { int nw = 0; - currentChunkGroup = ChunkList.CHUNK_GROUP_1_AFTERIDHR; - nw = chunkList.writeChunks(os, currentChunkGroup); - currentChunkGroup = ChunkList.CHUNK_GROUP_2_PLTE; - nw = chunkList.writeChunks(os, currentChunkGroup); + currentChunkGroup = ChunksList.CHUNK_GROUP_1_AFTERIDHR; + nw = chunksList.writeChunks(os, currentChunkGroup); + currentChunkGroup = ChunksList.CHUNK_GROUP_2_PLTE; + nw = chunksList.writeChunks(os, currentChunkGroup); if (nw > 0 && imgInfo.greyscale) throw new PngjOutputException("cannot write palette for this format"); if (nw == 0 && imgInfo.indexed) throw new PngjOutputException("missing palette"); - currentChunkGroup = ChunkList.CHUNK_GROUP_3_AFTERPLTE; - nw = chunkList.writeChunks(os, currentChunkGroup); - currentChunkGroup = ChunkList.CHUNK_GROUP_4_IDAT; + currentChunkGroup = ChunksList.CHUNK_GROUP_3_AFTERPLTE; + nw = chunksList.writeChunks(os, currentChunkGroup); + currentChunkGroup = ChunksList.CHUNK_GROUP_4_IDAT; } private void writeLastChunks() { // not including end - currentChunkGroup = ChunkList.CHUNK_GROUP_5_AFTERIDAT; - chunkList.writeChunks(os, currentChunkGroup); + currentChunkGroup = ChunksList.CHUNK_GROUP_5_AFTERIDAT; + chunksList.writeChunks(os, currentChunkGroup); // should not be unwriten chunks - List pending = chunkList.getQueuedChunks(); + List pending = chunksList.getQueuedChunks(); if (!pending.isEmpty()) throw new PngjOutputException(pending.size() + " chunks were not written! Eg: " + pending.get(0).toString()); - currentChunkGroup = ChunkList.CHUNK_GROUP_6_END; - } - - private void writeEndChunk() { - PngChunkIEND c = new PngChunkIEND(imgInfo); - c.createChunk().writeChunk(os); + currentChunkGroup = ChunksList.CHUNK_GROUP_6_END; } /** - * Writes a full image row. This must be called sequentially from n=0 to n=rows-1 One integer per sample , in the - * natural order: R G B R G B ... (or R G B A R G B A... if has alpha) The values should be between 0 and 255 for 8 - * bitspc images, and between 0- 65535 form 16 bitspc images (this applies also to the alpha channel if present) The - * array can be reused. - * - * @param newrow - * Array of pixel values - * @param rown - * Row number, from 0 (top) to rows-1 (bottom). This is just used as a check. Pass -1 if you want to - * autocompute it + * Write id signature and also "IHDR" chunk */ - public void writeRow(int[] newrow, int rown) { - if (rown == 0) { - writeSignatureAndIHDR(); - writeFirstChunks(); - } - if (rown < -1 || rown > imgInfo.rows) - throw new RuntimeException("invalid value for row " + rown); - rowNum++; - if (rown >= 0 && rowNum != rown) - throw new RuntimeException("rows must be written in strict consecutive order: tried to write row " + rown - + ", expected=" + rowNum); - scanline = newrow; - // swap - byte[] tmp = rowb; - rowb = rowbprev; - rowbprev = tmp; - convertRowToBytes(); - filterRow(rown); - try { - datStreamDeflated.write(rowbfilter, 0, imgInfo.bytesPerRow + 1); - } catch (IOException e) { - throw new PngjOutputException(e); - } - } + private void writeSignatureAndIHDR() { + currentChunkGroup = ChunksList.CHUNK_GROUP_0_IDHR; - /** - * Same as writeRow(int[] newrow, int rown), but does not check row number - * - * @param newrow - */ - public void writeRow(int[] newrow) { - writeRow(newrow, -1); - } + PngHelperInternal.writeBytes(os, PngHelperInternal.getPngIdSignature()); // signature + PngChunkIHDR ihdr = new PngChunkIHDR(imgInfo); + // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html + ihdr.setCols(imgInfo.cols); + ihdr.setRows(imgInfo.rows); + ihdr.setBitspc(imgInfo.bitDepth); + int colormodel = 0; + if (imgInfo.alpha) + colormodel += 0x04; + if (imgInfo.indexed) + colormodel += 0x01; + if (!imgInfo.greyscale) + colormodel += 0x02; + ihdr.setColormodel(colormodel); + ihdr.setCompmeth(0); // compression method 0=deflate + ihdr.setFilmeth(0); // filter method (0) + ihdr.setInterlaced(0); // we never interlace + ihdr.createRawChunk().writeChunk(os); - /** - * Writes line. See writeRow(int[] newrow, int rown) - */ - public void writeRow(ImageLine imgline, int rownumber) { - writeRow(imgline.scanline, rownumber); } - /** - * Writes line, checks that the row number is consistent with that of the ImageLine See writeRow(int[] newrow, int - * rown) - * - * @deprecated Better use writeRow(ImageLine imgline, int rownumber) - */ - public void writeRow(ImageLine imgline) { - writeRow(imgline.scanline, imgline.getRown()); - } + protected void encodeRowFromByte(byte[] row) { + if (row.length == imgInfo.samplesPerRowPacked) { + // some duplication of code - because this case is typical and it works faster this way + int j = 1; + if (imgInfo.bitDepth <= 8) { + for (byte x : row) { // optimized + rowb[j++] = x; + } + } else { // 16 bitspc + for (byte x : row) { // optimized + rowb[j] = x; + j += 2; + } + } + } else { + // perhaps we need to pack? + if (row.length >= imgInfo.samplesPerRow && unpackedMode) + ImageLine.packInplaceByte(imgInfo, row, row, false); // row is packed in place! + if (imgInfo.bitDepth <= 8) { + for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) { + rowb[j++] = row[i]; + } + } else { // 16 bitspc + for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) { + rowb[j++] = row[i]; + rowb[j++] = 0; + } + } - /** - * Finalizes the image creation and closes the stream. This MUST be called after writing the lines. - */ - public void end() { - if (rowNum != imgInfo.rows - 1) - throw new PngjOutputException("all rows have not been written"); - try { - datStreamDeflated.finish(); - datStream.flush(); - writeLastChunks(); - writeEndChunk(); - os.close(); - } catch (IOException e) { - throw new PngjOutputException(e); } } - private int[] histox = new int[256]; // auxiliar buffer, only used by reportResultsForFilter - - private void reportResultsForFilter(int rown, FilterType type, boolean tentative) { - Arrays.fill(histox, 0); - int s = 0, v; - for (int i = 1; i <= imgInfo.bytesPerRow; i++) { - v = rowbfilter[i]; - if (v < 0) - s -= (int) v; - else - s += (int) v; - histox[v & 0xFF]++; + protected void encodeRowFromInt(int[] row) { + // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html + if (row.length == imgInfo.samplesPerRowPacked) { + // some duplication of code - because this case is typical and it works faster this way + int j = 1; + if (imgInfo.bitDepth <= 8) { + for (int x : row) { // optimized + rowb[j++] = (byte) x; + } + } else { // 16 bitspc + for (int x : row) { // optimized + rowb[j++] = (byte) (x >> 8); + rowb[j++] = (byte) (x); + } + } + } else { + // perhaps we need to pack? + if (row.length >= imgInfo.samplesPerRow && unpackedMode) + ImageLine.packInplaceInt(imgInfo, row, row, false); // row is packed in place! + if (imgInfo.bitDepth <= 8) { + for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) { + rowb[j++] = (byte) (row[i]); + } + } else { // 16 bitspc + for (int i = 0, j = 1; i < imgInfo.samplesPerRowPacked; i++) { + rowb[j++] = (byte) (row[i] >> 8); + rowb[j++] = (byte) (row[i]); + } + } } - filterStrat.fillResultsForFilter(rown, type, s, histox, tentative); } private void filterRow(int rown) { @@ -268,123 +286,98 @@ public class PngWriter { filterRowPaeth(); break; default: - throw new PngjOutputException("Filter type " + filterType + " not implemented"); + throw new PngjUnsupportedException("Filter type " + filterType + " not implemented"); } reportResultsForFilter(rown, filterType, false); } - protected int sumRowbfilter() { // sums absolute value - int s = 0; - for (int i = 1; i <= imgInfo.bytesPerRow; i++) - if (rowbfilter[i] < 0) - s -= (int) rowbfilter[i]; - else - s += (int) rowbfilter[i]; - return s; + private void prepareEncodeRow(int rown) { + if (datStream == null) + init(); + rowNum++; + if (rown >= 0 && rowNum != rown) + throw new PngjOutputException("rows must be written in order: expected:" + rowNum + " passed:" + rown); + // swap + byte[] tmp = rowb; + rowb = rowbprev; + rowbprev = tmp; } - protected void filterRowNone() { - for (int i = 1; i <= imgInfo.bytesPerRow; i++) { - rowbfilter[i] = (byte) rowb[i]; + private void filterAndSend(int rown) { + filterRow(rown); + try { + datStreamDeflated.write(rowbfilter, 0, imgInfo.bytesPerRow + 1); + } catch (IOException e) { + throw new PngjOutputException(e); } } - protected void filterRowSub() { - int i, j; - for (i = 1; i <= imgInfo.bytesPixel; i++) - rowbfilter[i] = (byte) rowb[i]; - for (j = 1, i = imgInfo.bytesPixel + 1; i <= imgInfo.bytesPerRow; i++, j++) { - rowbfilter[i] = (byte) (rowb[i] - rowb[j]); + protected void filterRowAverage() { + int i, j, imax; + imax = imgInfo.bytesPerRow; + for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imax; i++, j++) { + rowbfilter[i] = (byte) (rowb[i] - ((rowbprev[i] & 0xFF) + (j > 0 ? (rowb[j] & 0xFF) : 0)) / 2); } } - protected void filterRowUp() { + protected void filterRowNone() { for (int i = 1; i <= imgInfo.bytesPerRow; i++) { - rowbfilter[i] = (byte) (rowb[i] - rowbprev[i]); - } - } - - protected void filterRowAverage() { - int i, j; - for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imgInfo.bytesPerRow; i++, j++) { - rowbfilter[i] = (byte) (rowb[i] - ((rowbprev[i] & 0xFF) + (j > 0 ? (rowb[j] & 0xFF) : 0)) / 2); + rowbfilter[i] = (byte) rowb[i]; } } protected void filterRowPaeth() { - int i, j; - for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imgInfo.bytesPerRow; i++, j++) { - rowbfilter[i] = (byte) (rowb[i] - FilterType.filterPaethPredictor(j > 0 ? (rowb[j] & 0xFF) : 0, - rowbprev[i] & 0xFF, j > 0 ? (rowbprev[j] & 0xFF) : 0)); + int i, j, imax; + imax = imgInfo.bytesPerRow; + for (j = 1 - imgInfo.bytesPixel, i = 1; i <= imax; i++, j++) { + // rowbfilter[i] = (byte) (rowb[i] - PngHelperInternal.filterPaethPredictor(j > 0 ? (rowb[j] & 0xFF) : 0, + // rowbprev[i] & 0xFF, j > 0 ? (rowbprev[j] & 0xFF) : 0)); + rowbfilter[i] = (byte) PngHelperInternal.filterRowPaeth(rowb[i], j > 0 ? (rowb[j] & 0xFF) : 0, + rowbprev[i] & 0xFF, j > 0 ? (rowbprev[j] & 0xFF) : 0); } } - protected void convertRowToBytes() { - // http://www.libpng.org/pub/png/spec/1.2/PNG-DataRep.html + protected void filterRowSub() { int i, j; - if (imgInfo.bitDepth <= 8) { - for (i = 0, j = 1; i < imgInfo.samplesPerRowP; i++) { - rowb[j++] = (byte) (scanline[i]); - } - } else { // 16 bitspc - for (i = 0, j = 1; i < imgInfo.samplesPerRowP; i++) { - // x = (int) (scanline[i]) & 0xFFFF; - rowb[j++] = (byte) (scanline[i] >> 8); - rowb[j++] = (byte) (scanline[i]); - } + for (i = 1; i <= imgInfo.bytesPixel; i++) + rowbfilter[i] = (byte) rowb[i]; + for (j = 1, i = imgInfo.bytesPixel + 1; i <= imgInfo.bytesPerRow; i++, j++) { + // !!! rowbfilter[i] = (byte) (rowb[i] - rowb[j]); + rowbfilter[i] = (byte) PngHelperInternal.filterRowSub(rowb[i], rowb[j]); } } - // /// several getters / setters - all this setters are optional - - /** - * Filename or description, from the optional constructor argument. - */ - public String getFilename() { - return filename; + protected void filterRowUp() { + for (int i = 1; i <= imgInfo.bytesPerRow; i++) { + // rowbfilter[i] = (byte) (rowb[i] - rowbprev[i]); !!! + rowbfilter[i] = (byte) PngHelperInternal.filterRowUp(rowb[i], rowbprev[i]); + } } - /** - * Sets internal prediction filter type, or strategy to choose it. - *

- * This must be called just after constructor, before starting writing. - *

- * See also setCompLevel() - * - * @param filterType - * One of the five prediction types or strategy to choose it (see PngFilterType) Recommended - * values: DEFAULT (default) or AGGRESIVE - */ - public void setFilterType(FilterType filterType) { - filterStrat = new FilterWriteStrategy(imgInfo, filterType); + protected int sumRowbfilter() { // sums absolute value + int s = 0; + for (int i = 1; i <= imgInfo.bytesPerRow; i++) + if (rowbfilter[i] < 0) + s -= (int) rowbfilter[i]; + else + s += (int) rowbfilter[i]; + return s; } /** - * Sets compression level of ZIP algorithm. + * copy chunks from reader - copy_mask : see ChunksToWrite.COPY_XXX *

- * This must be called just after constructor, before starting writing. + * If we are after idat, only considers those chunks after IDAT in PngReader *

- * See also setFilterType() - * - * @param compLevel - * between 0 and 9 (default:6 , recommended: 6 or more) - */ - public void setCompLevel(int compLevel) { - if (compLevel < 0 || compLevel > 9) - throw new PngjException("Compression level invalid (" + compLevel + ") Must be 0..9"); - this.compLevel = compLevel; - } - - /** - * copy chunks from reader - copy_mask : see ChunksToWrite.COPY_XXX - * - * If we are after idat, only considers those chunks after IDAT in PngReader TODO: this should be more customizable + * TODO: this should be more customizable */ private void copyChunks(PngReader reader, int copy_mask, boolean onlyAfterIdat) { - boolean idatDone = currentChunkGroup >= ChunkList.CHUNK_GROUP_4_IDAT; + boolean idatDone = currentChunkGroup >= ChunksList.CHUNK_GROUP_4_IDAT; + if (onlyAfterIdat && reader.getCurrentChunkGroup() < ChunksList.CHUNK_GROUP_6_END) + throw new PngjExceptionInternal("tried to copy last chunks but reader has not ended"); for (PngChunk chunk : reader.getChunksList().getChunks()) { int group = chunk.getChunkGroup(); - if (group < ChunkList.CHUNK_GROUP_4_IDAT && idatDone) + if (group < ChunksList.CHUNK_GROUP_4_IDAT && idatDone) continue; boolean copy = false; if (chunk.crit) { @@ -413,9 +406,11 @@ public class PngWriter { && !(ChunkHelper.isUnknown(chunk) || text || chunk.id.equals(ChunkHelper.hIST) || chunk.id .equals(ChunkHelper.tIME))) copy = true; + if (chunk instanceof PngChunkSkipped) + copy = false; } if (copy) { - chunkList.queueChunk(PngChunk.cloneChunk(chunk, imgInfo), !chunk.allowsMultiple(), false); + chunksList.queue(PngChunk.cloneChunk(chunk, imgInfo)); } } } @@ -451,12 +446,228 @@ public class PngWriter { copyChunks(reader, copy_mask, true); } - public ChunkList getChunkList() { - return chunkList; + /** + * Computes compressed size/raw size, approximate. + *

+ * Actually: compressed size = total size of IDAT data , raw size = uncompressed pixel bytes = rows * (bytesPerRow + + * 1). + * + * This must be called after pngw.end() + */ + public double computeCompressionRatio() { + if (currentChunkGroup < ChunksList.CHUNK_GROUP_6_END) + throw new PngjOutputException("must be called after end()"); + double compressed = (double) datStream.getCountFlushed(); + double raw = (imgInfo.bytesPerRow + 1) * imgInfo.rows; + return compressed / raw; } + /** + * Finalizes the image creation and closes the stream. This MUST be called after writing the lines. + */ + public void end() { + if (rowNum != imgInfo.rows - 1) + throw new PngjOutputException("all rows have not been written"); + try { + datStreamDeflated.finish(); + datStream.flush(); + writeLastChunks(); + writeEndChunk(); + if (shouldCloseStream) + os.close(); + } catch (IOException e) { + throw new PngjOutputException(e); + } + } + + /** + * returns the chunks list (queued and writen chunks) + */ + public ChunksListForWrite getChunksList() { + return chunksList; + } + + /** + * Filename or description, from the optional constructor argument. + */ + public String getFilename() { + return filename; + } + + /** + * High level wrapper over chunksList for metadata handling + */ public PngMetadata getMetadata() { return metadata; } + /** + * Sets compression level of ZIP algorithm. + *

+ * This must be called just after constructor, before starting writing. + *

+ * See also setFilterType() + * + * @param compLevel + * between 0 and 9 (default:6 , recommended: 6 or more) + */ + public void setCompLevel(int compLevel) { + if (compLevel < 0 || compLevel > 9) + throw new PngjOutputException("Compression level invalid (" + compLevel + ") Must be 0..9"); + this.compLevel = compLevel; + } + + /** + * Sets internal prediction filter type, or strategy to choose it. + *

+ * This must be called just after constructor, before starting writing. + *

+ * See also setCompLevel() + * + * @param filterType + * One of the five prediction types or strategy to choose it (see PngFilterType) Recommended + * values: DEFAULT (default) or AGGRESIVE + */ + public void setFilterType(FilterType filterType) { + filterStrat = new FilterWriteStrategy(imgInfo, filterType); + } + + /** + * Sets maximum size of IDAT fragments. This has little effect on performance you should rarely call this + *

+ * + * @param idatMaxSize + * default=0 : use defaultSize (32K) + */ + public void setIdatMaxSize(int idatMaxSize) { + this.idatMaxSize = idatMaxSize; + } + + /** + * if true, input stream will be closed after ending write + *

+ * default=true + */ + public void setShouldCloseStream(boolean shouldCloseStream) { + this.shouldCloseStream = shouldCloseStream; + } + + /** + * Deflater strategy: one of Deflater.FILTERED Deflater.HUFFMAN_ONLY Deflater.DEFAULT_STRATEGY + *

+ * Default: Deflater.FILTERED . This should be changed very rarely. + */ + public void setDeflaterStrategy(int deflaterStrategy) { + this.deflaterStrategy = deflaterStrategy; + } + + /** + * Writes line, checks that the row number is consistent with that of the ImageLine See writeRow(int[] newrow, int + * rown) + * + * @deprecated Better use writeRow(ImageLine imgline, int rownumber) + */ + public void writeRow(ImageLine imgline) { + writeRow(imgline.scanline, imgline.getRown()); + } + + /** + * Writes line. See writeRow(int[] newrow, int rown) + * + * The packed flag of the imageline is honoured! + * + * @see #writeRowInt(int[], int) + */ + public void writeRow(ImageLine imgline, int rownumber) { + unpackedMode = imgline.samplesUnpacked; + if (imgline.sampleType == SampleType.INT) + writeRowInt(imgline.scanline, rownumber); + else + writeRowByte(imgline.scanlineb, rownumber); + } + + /** + * Same as writeRow(int[] newrow, int rown), but does not check row number + * + * @param newrow + */ + public void writeRow(int[] newrow) { + writeRow(newrow, -1); + } + + /** + * Alias to writeRowInt + * + * @see #writeRowInt(int[], int) + */ + public void writeRow(int[] newrow, int rown) { + writeRowInt(newrow, rown); + } + + /** + * Writes a full image row. + *

+ * This must be called sequentially from n=0 to n=rows-1 One integer per sample , in the natural order: R G B R G B + * ... (or R G B A R G B A... if has alpha) The values should be between 0 and 255 for 8 bitspc images, and between + * 0- 65535 form 16 bitspc images (this applies also to the alpha channel if present) The array can be reused. + *

+ * Warning: the array might be modified in some cases (unpacked row with low bitdepth) + *

+ * + * @param newrow + * Array of pixel values. Warning: the array size should be exact (samplesPerRowP) + * @param rown + * Row number, from 0 (top) to rows-1 (bottom). This is just used as a check. Pass -1 if you want to + * autocompute it + */ + public void writeRowInt(int[] newrow, int rown) { + prepareEncodeRow(rown); + encodeRowFromInt(newrow); + filterAndSend(rown); + } + + /** + * Same semantics as writeRowInt but using bytes. Each byte is still a sample. If 16bitdepth, we are passing only + * the most significant byte (and hence losing some info) + * + * @see PngWriter#writeRowInt(int[], int) + */ + public void writeRowByte(byte[] newrow, int rown) { + prepareEncodeRow(rown); + encodeRowFromByte(newrow); + filterAndSend(rown); + } + + /** + * Writes all the pixels, calling writeRowInt() for each image row + */ + public void writeRowsInt(int[][] image) { + for (int i = 0; i < imgInfo.rows; i++) + writeRowInt(image[i], i); + } + + /** + * Writes all the pixels, calling writeRowByte() for each image row + */ + public void writeRowsByte(byte[][] image) { + for (int i = 0; i < imgInfo.rows; i++) + writeRowByte(image[i], i); + } + + public boolean isUnpackedMode() { + return unpackedMode; + } + + /** + * If false (default), and image has bitdepth 1-2-4, the scanlines passed are assumed to be already packed. + *

+ * If true, each element is a sample, the writer will perform the packing if necessary. + *

+ * Warning: when using {@link #writeRow(ImageLine, int)} (recommended) the packed flag of the ImageLine + * object overrides (and overwrites!) this field. + */ + public void setUseUnPackedMode(boolean useUnpackedMode) { + this.unpackedMode = useUnpackedMode; + } + } -- cgit v1.2.3