package com.sun.imageio.plugins.png;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Locale;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageOutputStreamImpl;
class CRC {
private static int[] crcTable = new int[256];
private int crc = 0xffffffff;
static {
for (int n = 0; n < 256; n++) {
int c = n;
for (int k = 0; k < 8; k++) {
if ((c & 1) == 1) {
c = 0xedb88320 ^ (c >>> 1);
} else {
c >>>= 1;
}
crcTable[n] = c;
}
}
}
public CRC() {}
public void reset() {
crc = 0xffffffff;
}
public void update(byte[] data, int off, int len) {
for (int n = 0; n < len; n++) {
crc = crcTable[(crc ^ data[off + n]) & 0xff] ^ (crc >>> 8);
}
}
public void update(int data) {
crc = crcTable[(crc ^ data) & 0xff] ^ (crc >>> 8);
}
public int getValue() {
return crc ^ 0xffffffff;
}
}
final class ChunkStream extends ImageOutputStreamImpl {
private ImageOutputStream stream;
private long startPos;
private CRC crc = new CRC();
public ChunkStream(int type, ImageOutputStream stream) throws IOException {
this.stream = stream;
this.startPos = stream.getStreamPosition();
stream.writeInt(-1);
writeInt(type);
}
public int read() throws IOException {
throw new RuntimeException("Method not available");
}
public int read(byte[] b, int off, int len) throws IOException {
throw new RuntimeException("Method not available");
}
public void write(byte[] b, int off, int len) throws IOException {
crc.update(b, off, len);
stream.write(b, off, len);
}
public void write(int b) throws IOException {
crc.update(b);
stream.write(b);
}
public void finish() throws IOException {
stream.writeInt(crc.getValue());
long pos = stream.getStreamPosition();
stream.seek(startPos);
stream.writeInt((int)(pos - startPos) - 12);
stream.seek(pos);
stream.flushBefore(pos);
}
protected void finalize() throws Throwable {
}
}
final class IDATOutputStream extends ImageOutputStreamImpl {
private static byte[] chunkType = {
(byte)'I', (byte)'D', (byte)'A', (byte)'T'
};
private ImageOutputStream stream;
private int chunkLength;
private long startPos;
private CRC crc = new CRC();
Deflater def = new Deflater(Deflater.BEST_COMPRESSION);
byte[] buf = new byte[512];
private int bytesRemaining;
public IDATOutputStream(ImageOutputStream stream, int chunkLength)
throws IOException {
this.stream = stream;
this.chunkLength = chunkLength;
startChunk();
}
private void startChunk() throws IOException {
crc.reset();
this.startPos = stream.getStreamPosition();
stream.writeInt(-1);
crc.update(chunkType, 0, 4);
stream.write(chunkType, 0, 4);
this.bytesRemaining = chunkLength;
}
private void finishChunk() throws IOException {
stream.writeInt(crc.getValue());
long pos = stream.getStreamPosition();
stream.seek(startPos);
stream.writeInt((int)(pos - startPos) - 12);
stream.seek(pos);
stream.flushBefore(pos);
}
public int read() throws IOException {
throw new RuntimeException("Method not available");
}
public int read(byte[] b, int off, int len) throws IOException {
throw new RuntimeException("Method not available");
}
public void write(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return;
}
if (!def.finished()) {
def.setInput(b, off, len);
while (!def.needsInput()) {
deflate();
}
}
}
public void deflate() throws IOException {
int len = def.deflate(buf, 0, buf.length);
int off = 0;
while (len > 0) {
if (bytesRemaining == 0) {
finishChunk();
startChunk();
}
int nbytes = Math.min(len, bytesRemaining);
crc.update(buf, off, nbytes);
stream.write(buf, off, nbytes);
off += nbytes;
len -= nbytes;
bytesRemaining -= nbytes;
}
}
public void write(int b) throws IOException {
byte[] wbuf = new byte[1];
wbuf[0] = (byte)b;
write(wbuf, 0, 1);
}
public void finish() throws IOException {
try {
if (!def.finished()) {
def.finish();
while (!def.finished()) {
deflate();
}
}
finishChunk();
} finally {
def.end();
}
}
protected void finalize() throws Throwable {
}
}
class PNGImageWriteParam extends ImageWriteParam {
public PNGImageWriteParam(Locale locale) {
super();
this.canWriteProgressive = true;
this.locale = locale;
}
}
public class PNGImageWriter extends ImageWriter {
ImageOutputStream stream = null;
PNGMetadata metadata = null;
int sourceXOffset = 0;
int sourceYOffset = 0;
int sourceWidth = 0;
int sourceHeight = 0;
int[] sourceBands = null;
int periodX = 1;
int periodY = 1;
int numBands;
int bpp;
RowFilter rowFilter = new RowFilter();
byte[] prevRow = null;
byte[] currRow = null;
byte[][] filteredRows = null;
int[] sampleSize = null;
int scalingBitDepth = -1;
byte[][] scale = null;
byte[] scale0 = null;
byte[][] scaleh = null;
byte[][] scalel = null;
int totalPixels;
int pixelsDone;
public PNGImageWriter(ImageWriterSpi originatingProvider) {
super(originatingProvider);
}
public void setOutput(Object output) {
super.setOutput(output);
if (output != null) {
if (!(output instanceof ImageOutputStream)) {
throw new IllegalArgumentException("output not an ImageOutputStream!");
}
this.stream = (ImageOutputStream)output;
} else {
this.stream = null;
}
}
private static int[] allowedProgressivePasses = { 1, 7 };
public ImageWriteParam getDefaultWriteParam() {
return new PNGImageWriteParam(getLocale());
}
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
return null;
}
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,
ImageWriteParam param) {
PNGMetadata m = new PNGMetadata();
m.initialize(imageType, imageType.getSampleModel().getNumBands());
return m;
}
public IIOMetadata convertStreamMetadata(IIOMetadata inData,
ImageWriteParam param) {
return null;
}
public IIOMetadata convertImageMetadata(IIOMetadata inData,
ImageTypeSpecifier imageType,
ImageWriteParam param) {
if (inData instanceof PNGMetadata) {
return (PNGMetadata)((PNGMetadata)inData).clone();
} else {
return new PNGMetadata(inData);
}
}
private void write_magic() throws IOException {
byte[] magic = { (byte)137, 80, 78, 71, 13, 10, 26, 10 };
stream.write(magic);
}
private void write_IHDR() throws IOException {
ChunkStream cs = new ChunkStream(PNGImageReader.IHDR_TYPE, stream);
cs.writeInt(metadata.IHDR_width);
cs.writeInt(metadata.IHDR_height);
cs.writeByte(metadata.IHDR_bitDepth);
cs.writeByte(metadata.IHDR_colorType);
if (metadata.IHDR_compressionMethod != 0) {
throw new IIOException(
"Only compression method 0 is defined in PNG 1.1");
}
cs.writeByte(metadata.IHDR_compressionMethod);
if (metadata.IHDR_filterMethod != 0) {
throw new IIOException(
"Only filter method 0 is defined in PNG 1.1");
}
cs.writeByte(metadata.IHDR_filterMethod);
if (metadata.IHDR_interlaceMethod < 0 ||
metadata.IHDR_interlaceMethod > 1) {
throw new IIOException(
"Only interlace methods 0 (node) and 1 (adam7) are defined in PNG 1.1");
}
cs.writeByte(metadata.IHDR_interlaceMethod);
cs.finish();
}
private void write_cHRM() throws IOException {
if (metadata.cHRM_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.cHRM_TYPE, stream);
cs.writeInt(metadata.cHRM_whitePointX);
cs.writeInt(metadata.cHRM_whitePointY);
cs.writeInt(metadata.cHRM_redX);
cs.writeInt(metadata.cHRM_redY);
cs.writeInt(metadata.cHRM_greenX);
cs.writeInt(metadata.cHRM_greenY);
cs.writeInt(metadata.cHRM_blueX);
cs.writeInt(metadata.cHRM_blueY);
cs.finish();
}
}
private void write_gAMA() throws IOException {
if (metadata.gAMA_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.gAMA_TYPE, stream);
cs.writeInt(metadata.gAMA_gamma);
cs.finish();
}
}
private void write_iCCP() throws IOException {
if (metadata.iCCP_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.iCCP_TYPE, stream);
cs.writeBytes(metadata.iCCP_profileName);
cs.writeByte(0);
cs.writeByte(metadata.iCCP_compressionMethod);
cs.write(metadata.iCCP_compressedProfile);
cs.finish();
}
}
private void write_sBIT() throws IOException {
if (metadata.sBIT_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.sBIT_TYPE, stream);
int colorType = metadata.IHDR_colorType;
if (metadata.sBIT_colorType != colorType) {
processWarningOccurred(0,
"sBIT metadata has wrong color type.\n" +
"The chunk will not be written.");
return;
}
if (colorType == PNGImageReader.PNG_COLOR_GRAY ||
colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
cs.writeByte(metadata.sBIT_grayBits);
} else if (colorType == PNGImageReader.PNG_COLOR_RGB ||
colorType == PNGImageReader.PNG_COLOR_PALETTE ||
colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {
cs.writeByte(metadata.sBIT_redBits);
cs.writeByte(metadata.sBIT_greenBits);
cs.writeByte(metadata.sBIT_blueBits);
}
if (colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA ||
colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {
cs.writeByte(metadata.sBIT_alphaBits);
}
cs.finish();
}
}
private void write_sRGB() throws IOException {
if (metadata.sRGB_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.sRGB_TYPE, stream);
cs.writeByte(metadata.sRGB_renderingIntent);
cs.finish();
}
}
private void write_PLTE() throws IOException {
if (metadata.PLTE_present) {
if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY ||
metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
processWarningOccurred(0,
"A PLTE chunk may not appear in a gray or gray alpha image.\n" +
"The chunk will not be written");
return;
}
ChunkStream cs = new ChunkStream(PNGImageReader.PLTE_TYPE, stream);
int numEntries = metadata.PLTE_red.length;
byte[] palette = new byte[numEntries*3];
int index = 0;
for (int i = 0; i < numEntries; i++) {
palette[index++] = metadata.PLTE_red[i];
palette[index++] = metadata.PLTE_green[i];
palette[index++] = metadata.PLTE_blue[i];
}
cs.write(palette);
cs.finish();
}
}
private void write_hIST() throws IOException, IIOException {
if (metadata.hIST_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.hIST_TYPE, stream);
if (!metadata.PLTE_present) {
throw new IIOException("hIST chunk without PLTE chunk!");
}
cs.writeChars(metadata.hIST_histogram,
0, metadata.hIST_histogram.length);
cs.finish();
}
}
private void write_tRNS() throws IOException, IIOException {
if (metadata.tRNS_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.tRNS_TYPE, stream);
int colorType = metadata.IHDR_colorType;
int chunkType = metadata.tRNS_colorType;
int chunkRed = metadata.tRNS_red;
int chunkGreen = metadata.tRNS_green;
int chunkBlue = metadata.tRNS_blue;
if (colorType == PNGImageReader.PNG_COLOR_RGB &&
chunkType == PNGImageReader.PNG_COLOR_GRAY) {
chunkType = colorType;
chunkRed = chunkGreen = chunkBlue =
metadata.tRNS_gray;
}
if (chunkType != colorType) {
processWarningOccurred(0,
"tRNS metadata has incompatible color type.\n" +
"The chunk will not be written.");
return;
}
if (colorType == PNGImageReader.PNG_COLOR_PALETTE) {
if (!metadata.PLTE_present) {
throw new IIOException("tRNS chunk without PLTE chunk!");
}
cs.write(metadata.tRNS_alpha);
} else if (colorType == PNGImageReader.PNG_COLOR_GRAY) {
cs.writeShort(metadata.tRNS_gray);
} else if (colorType == PNGImageReader.PNG_COLOR_RGB) {
cs.writeShort(chunkRed);
cs.writeShort(chunkGreen);
cs.writeShort(chunkBlue);
} else {
throw new IIOException("tRNS chunk for color type 4 or 6!");
}
cs.finish();
}
}
private void write_bKGD() throws IOException {
if (metadata.bKGD_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.bKGD_TYPE, stream);
int colorType = metadata.IHDR_colorType & 0x3;
int chunkType = metadata.bKGD_colorType;
int chunkRed = metadata.bKGD_red;
int chunkGreen = metadata.bKGD_red;
int chunkBlue = metadata.bKGD_red;
if (colorType == PNGImageReader.PNG_COLOR_RGB &&
chunkType == PNGImageReader.PNG_COLOR_GRAY) {
chunkType = colorType;
chunkRed = chunkGreen = chunkBlue =
metadata.bKGD_gray;
}
if (chunkType != colorType) {
processWarningOccurred(0,
"bKGD metadata has incompatible color type.\n" +
"The chunk will not be written.");
return;
}
if (colorType == PNGImageReader.PNG_COLOR_PALETTE) {
cs.writeByte(metadata.bKGD_index);
} else if (colorType == PNGImageReader.PNG_COLOR_GRAY ||
colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
cs.writeShort(metadata.bKGD_gray);
} else {
cs.writeShort(chunkRed);
cs.writeShort(chunkGreen);
cs.writeShort(chunkBlue);
}
cs.finish();
}
}
private void write_pHYs() throws IOException {
if (metadata.pHYs_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.pHYs_TYPE, stream);
cs.writeInt(metadata.pHYs_pixelsPerUnitXAxis);
cs.writeInt(metadata.pHYs_pixelsPerUnitYAxis);
cs.writeByte(metadata.pHYs_unitSpecifier);
cs.finish();
}
}
private void write_sPLT() throws IOException {
if (metadata.sPLT_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.sPLT_TYPE, stream);
cs.writeBytes(metadata.sPLT_paletteName);
cs.writeByte(0);
cs.writeByte(metadata.sPLT_sampleDepth);
int numEntries = metadata.sPLT_red.length;
if (metadata.sPLT_sampleDepth == 8) {
for (int i = 0; i < numEntries; i++) {
cs.writeByte(metadata.sPLT_red[i]);
cs.writeByte(metadata.sPLT_green[i]);
cs.writeByte(metadata.sPLT_blue[i]);
cs.writeByte(metadata.sPLT_alpha[i]);
cs.writeShort(metadata.sPLT_frequency[i]);
}
} else {
for (int i = 0; i < numEntries; i++) {
cs.writeShort(metadata.sPLT_red[i]);
cs.writeShort(metadata.sPLT_green[i]);
cs.writeShort(metadata.sPLT_blue[i]);
cs.writeShort(metadata.sPLT_alpha[i]);
cs.writeShort(metadata.sPLT_frequency[i]);
}
}
cs.finish();
}
}
private void write_tIME() throws IOException {
if (metadata.tIME_present) {
ChunkStream cs = new ChunkStream(PNGImageReader.tIME_TYPE, stream);
cs.writeShort(metadata.tIME_year);
cs.writeByte(metadata.tIME_month);
cs.writeByte(metadata.tIME_day);
cs.writeByte(metadata.tIME_hour);
cs.writeByte(metadata.tIME_minute);
cs.writeByte(metadata.tIME_second);
cs.finish();
}
}
private void write_tEXt() throws IOException {
Iterator keywordIter = metadata.tEXt_keyword.iterator();
Iterator textIter = metadata.tEXt_text.iterator();
while (keywordIter.hasNext()) {
ChunkStream cs = new ChunkStream(PNGImageReader.tEXt_TYPE, stream);
String keyword = (String)keywordIter.next();
cs.writeBytes(keyword);
cs.writeByte(0);
String text = (String)textIter.next();
cs.writeBytes(text);
cs.finish();
}
}
private byte[] deflate(byte[] b) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(baos);
dos.write(b);
dos.close();
return baos.toByteArray();
}
private void write_iTXt() throws IOException {
Iterator<String> keywordIter = metadata.iTXt_keyword.iterator();
Iterator<Boolean> flagIter = metadata.iTXt_compressionFlag.iterator();
Iterator<Integer> methodIter = metadata.iTXt_compressionMethod.iterator();
Iterator<String> languageIter = metadata.iTXt_languageTag.iterator();
Iterator<String> translatedKeywordIter =
metadata.iTXt_translatedKeyword.iterator();
Iterator<String> textIter = metadata.iTXt_text.iterator();
while (keywordIter.hasNext()) {
ChunkStream cs = new ChunkStream(PNGImageReader.iTXt_TYPE, stream);
cs.writeBytes(keywordIter.next());
cs.writeByte(0);
Boolean compressed = flagIter.next();
cs.writeByte(compressed ? 1 : 0);
cs.writeByte(methodIter.next().intValue());
cs.writeBytes(languageIter.next());
cs.writeByte(0);
cs.write(translatedKeywordIter.next().getBytes("UTF8"));
cs.writeByte(0);
String text = textIter.next();
if (compressed) {
cs.write(deflate(text.getBytes("UTF8")));
} else {
cs.write(text.getBytes("UTF8"));
}
cs.finish();
}
}
private void write_zTXt() throws IOException {
Iterator keywordIter = metadata.zTXt_keyword.iterator();
Iterator methodIter = metadata.zTXt_compressionMethod.iterator();
Iterator textIter = metadata.zTXt_text.iterator();
while (keywordIter.hasNext()) {
ChunkStream cs = new ChunkStream(PNGImageReader.zTXt_TYPE, stream);
String keyword = (String)keywordIter.next();
cs.writeBytes(keyword);
cs.writeByte(0);
int compressionMethod = ((Integer)methodIter.next()).intValue();
cs.writeByte(compressionMethod);
String text = (String)textIter.next();
cs.write(deflate(text.getBytes("ISO-8859-1")));
cs.finish();
}
}
private void writeUnknownChunks() throws IOException {
Iterator typeIter = metadata.unknownChunkType.iterator();
Iterator dataIter = metadata.unknownChunkData.iterator();
while (typeIter.hasNext() && dataIter.hasNext()) {
String type = (String)typeIter.next();
ChunkStream cs = new ChunkStream(chunkType(type), stream);
byte[] data = (byte[])dataIter.next();
cs.write(data);
cs.finish();
}
}
private static int chunkType(String typeString) {
char c0 = typeString.charAt(0);
char c1 = typeString.charAt(1);
char c2 = typeString.charAt(2);
char c3 = typeString.charAt(3);
int type = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
return type;
}
private void encodePass(ImageOutputStream os,
RenderedImage image,
int xOffset, int yOffset,
int xSkip, int ySkip) throws IOException {
int minX = sourceXOffset;
int minY = sourceYOffset;
int width = sourceWidth;
int height = sourceHeight;
xOffset *= periodX;
xSkip *= periodX;
yOffset *= periodY;
ySkip *= periodY;
int hpixels = (width - xOffset + xSkip - 1)/xSkip;
int vpixels = (height - yOffset + ySkip - 1)/ySkip;
if (hpixels == 0 || vpixels == 0) {
return;
}
xOffset *= numBands;
xSkip *= numBands;
int samplesPerByte = 8/metadata.IHDR_bitDepth;
int numSamples = width*numBands;
int[] samples = new int[numSamples];
int bytesPerRow = hpixels*numBands;
if (metadata.IHDR_bitDepth < 8) {
bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte;
} else if (metadata.IHDR_bitDepth == 16) {
bytesPerRow *= 2;
}
IndexColorModel icm_gray_alpha = null;
if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA &&
image.getColorModel() instanceof IndexColorModel)
{
bytesPerRow *= 2;
icm_gray_alpha = (IndexColorModel)image.getColorModel();
}
currRow = new byte[bytesPerRow + bpp];
prevRow = new byte[bytesPerRow + bpp];
filteredRows = new byte[5][bytesPerRow + bpp];
int bitDepth = metadata.IHDR_bitDepth;
for (int row = minY + yOffset; row < minY + height; row += ySkip) {
Rectangle rect = new Rectangle(minX, row, width, 1);
Raster ras = image.getData(rect);
if (sourceBands != null) {
ras = ras.createChild(minX, row, width, 1, minX, row,
sourceBands);
}
ras.getPixels(minX, row, width, 1, samples);
if (image.getColorModel().isAlphaPremultiplied()) {
WritableRaster wr = ras.createCompatibleWritableRaster();
wr.setPixels(wr.getMinX(), wr.getMinY(),
wr.getWidth(), wr.getHeight(),
samples);
image.getColorModel().coerceData(wr, false);
wr.getPixels(wr.getMinX(), wr.getMinY(),
wr.getWidth(), wr.getHeight(),
samples);
}
int[] paletteOrder = metadata.PLTE_order;
if (paletteOrder != null) {
for (int i = 0; i < numSamples; i++) {
samples[i] = paletteOrder[samples[i]];
}
}
int count = bpp;
int pos = 0;
int tmp = 0;
switch (bitDepth) {
case 1: case 2: case 4:
int mask = samplesPerByte - 1;
for (int s = xOffset; s < numSamples; s += xSkip) {
byte val = scale0[samples[s]];
tmp = (tmp << bitDepth) | val;
if ((pos++ & mask) == mask) {
currRow[count++] = (byte)tmp;
tmp = 0;
pos = 0;
}
}
if ((pos & mask) != 0) {
tmp <<= ((8/bitDepth) - pos)*bitDepth;
currRow[count++] = (byte)tmp;
}
break;
case 8:
if (numBands == 1) {
for (int s = xOffset; s < numSamples; s += xSkip) {
currRow[count++] = scale0[samples[s]];
if (icm_gray_alpha != null) {
currRow[count++] =
scale0[icm_gray_alpha.getAlpha(0xff & samples[s])];
}
}
} else {
for (int s = xOffset; s < numSamples; s += xSkip) {
for (int b = 0; b < numBands; b++) {
currRow[count++] = scale[b][samples[s + b]];
}
}
}
break;
case 16:
for (int s = xOffset; s < numSamples; s += xSkip) {
for (int b = 0; b < numBands; b++) {
currRow[count++] = scaleh[b][samples[s + b]];
currRow[count++] = scalel[b][samples[s + b]];
}
}
break;
}
int filterType = rowFilter.filterRow(metadata.IHDR_colorType,
currRow, prevRow,
filteredRows,
bytesPerRow, bpp);
os.write(filterType);
os.write(filteredRows[filterType], bpp, bytesPerRow);
byte[] swap = currRow;
currRow = prevRow;
prevRow = swap;
pixelsDone += hpixels;
processImageProgress(100.0F*pixelsDone/totalPixels);
if (abortRequested()) {
return;
}
}
}
private void write_IDAT(RenderedImage image) throws IOException {
IDATOutputStream ios = new IDATOutputStream(stream, 32768);
try {
if (metadata.IHDR_interlaceMethod == 1) {
for (int i = 0; i < 7; i++) {
encodePass(ios, image,
PNGImageReader.adam7XOffset[i],
PNGImageReader.adam7YOffset[i],
PNGImageReader.adam7XSubsampling[i],
PNGImageReader.adam7YSubsampling[i]);
if (abortRequested()) {
break;
}
}
} else {
encodePass(ios, image, 0, 0, 1, 1);
}
} finally {
ios.finish();
}
}
private void writeIEND() throws IOException {
ChunkStream cs = new ChunkStream(PNGImageReader.IEND_TYPE, stream);
cs.finish();
}
private boolean equals(int[] s0, int[] s1) {
if (s0 == null || s1 == null) {
return false;
}
if (s0.length != s1.length) {
return false;
}
for (int i = 0; i < s0.length; i++) {
if (s0[i] != s1[i]) {
return false;
}
}
return true;
}
private void initializeScaleTables(int[] sampleSize) {
int bitDepth = metadata.IHDR_bitDepth;
if (bitDepth == scalingBitDepth &&
equals(sampleSize, this.sampleSize)) {
return;
}
this.sampleSize = sampleSize;
this.scalingBitDepth = bitDepth;
int maxOutSample = (1 << bitDepth) - 1;
if (bitDepth <= 8) {
scale = new byte[numBands][];
for (int b = 0; b < numBands; b++) {
int maxInSample = (1 << sampleSize[b]) - 1;
int halfMaxInSample = maxInSample/2;
scale[b] = new byte[maxInSample + 1];
for (int s = 0; s <= maxInSample; s++) {
scale[b][s] =
(byte)((s*maxOutSample + halfMaxInSample)/maxInSample);
}
}
scale0 = scale[0];
scaleh = scalel = null;
} else {
scaleh = new byte[numBands][];
scalel = new byte[numBands][];
for (int b = 0; b < numBands; b++) {
int maxInSample = (1 << sampleSize[b]) - 1;
int halfMaxInSample = maxInSample/2;
scaleh[b] = new byte[maxInSample + 1];
scalel[b] = new byte[maxInSample + 1];
for (int s = 0; s <= maxInSample; s++) {
int val = (s*maxOutSample + halfMaxInSample)/maxInSample;
scaleh[b][s] = (byte)(val >> 8);
scalel[b][s] = (byte)(val & 0xff);
}
}
scale = null;
scale0 = null;
}
}
public void write(IIOMetadata streamMetadata,
IIOImage image,
ImageWriteParam param) throws IIOException {
if (stream == null) {
throw new IllegalStateException("output == null!");
}
if (image == null) {
throw new IllegalArgumentException("image == null!");
}
if (image.hasRaster()) {
throw new UnsupportedOperationException("image has a Raster!");
}
RenderedImage im = image.getRenderedImage();
SampleModel sampleModel = im.getSampleModel();
this.numBands = sampleModel.getNumBands();
this.sourceXOffset = im.getMinX();
this.sourceYOffset = im.getMinY();
this.sourceWidth = im.getWidth();
this.sourceHeight = im.getHeight();
this.sourceBands = null;
this.periodX = 1;
this.periodY = 1;
if (param != null) {
Rectangle sourceRegion = param.getSourceRegion();
if (sourceRegion != null) {
Rectangle imageBounds = new Rectangle(im.getMinX(),
im.getMinY(),
im.getWidth(),
im.getHeight());
sourceRegion = sourceRegion.intersection(imageBounds);
sourceXOffset = sourceRegion.x;
sourceYOffset = sourceRegion.y;
sourceWidth = sourceRegion.width;
sourceHeight = sourceRegion.height;
}
int gridX = param.getSubsamplingXOffset();
int gridY = param.getSubsamplingYOffset();
sourceXOffset += gridX;
sourceYOffset += gridY;
sourceWidth -= gridX;
sourceHeight -= gridY;
periodX = param.getSourceXSubsampling();
periodY = param.getSourceYSubsampling();
int[] sBands = param.getSourceBands();
if (sBands != null) {
sourceBands = sBands;
numBands = sourceBands.length;
}
}
int destWidth = (sourceWidth + periodX - 1)/periodX;
int destHeight = (sourceHeight + periodY - 1)/periodY;
if (destWidth <= 0 || destHeight <= 0) {
throw new IllegalArgumentException("Empty source region!");
}
this.totalPixels = destWidth*destHeight;
this.pixelsDone = 0;
IIOMetadata imd = image.getMetadata();
if (imd != null) {
metadata = (PNGMetadata)convertImageMetadata(imd,
ImageTypeSpecifier.createFromRenderedImage(im),
null);
} else {
metadata = new PNGMetadata();
}
if (param != null) {
switch (param.getProgressiveMode()) {
case ImageWriteParam.MODE_DEFAULT:
metadata.IHDR_interlaceMethod = 1;
break;
case ImageWriteParam.MODE_DISABLED:
metadata.IHDR_interlaceMethod = 0;
break;
}
}
metadata.initialize(new ImageTypeSpecifier(im), numBands);
metadata.IHDR_width = destWidth;
metadata.IHDR_height = destHeight;
this.bpp = numBands*((metadata.IHDR_bitDepth == 16) ? 2 : 1);
initializeScaleTables(sampleModel.getSampleSize());
clearAbortRequest();
processImageStarted(0);
try {
write_magic();
write_IHDR();
write_cHRM();
write_gAMA();
write_iCCP();
write_sBIT();
write_sRGB();
write_PLTE();
write_hIST();
write_tRNS();
write_bKGD();
write_pHYs();
write_sPLT();
write_tIME();
write_tEXt();
write_iTXt();
write_zTXt();
writeUnknownChunks();
write_IDAT(im);
if (abortRequested()) {
processWriteAborted();
} else {
writeIEND();
processImageComplete();
}
} catch (IOException e) {
throw new IIOException("I/O error writing PNG file!", e);
}
}
}