| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.internal.image; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.ImageData; |
| import org.eclipse.swt.graphics.ImageLoader; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.internal.Compatibility; |
| |
| final class PngEncoder extends Object { |
| |
| static final byte SIGNATURE[] = {(byte) '\211', (byte) 'P', (byte) 'N', (byte) 'G', (byte) '\r', (byte) '\n', (byte) '\032', (byte) '\n'}; |
| static final byte TAG_IHDR[] = {(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R'}; |
| static final byte TAG_PLTE[] = {(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E'}; |
| static final byte TAG_TRNS[] = {(byte) 't', (byte) 'R', (byte) 'N', (byte) 'S'}; |
| static final byte TAG_IDAT[] = {(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T'}; |
| static final byte TAG_IEND[] = {(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D'}; |
| |
| ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024); |
| PngChunk chunk; |
| |
| ImageLoader loader; |
| ImageData data; |
| int transparencyType; |
| |
| int width, height, bitDepth, colorType; |
| |
| int compressionMethod = 0; |
| int filterMethod = 0; |
| int interlaceMethod = 0; |
| |
| public PngEncoder(ImageLoader loader) { |
| |
| this.loader = loader; |
| this.data = loader.data[0]; |
| this.transparencyType = data.getTransparencyType(); |
| |
| this.width = data.width; |
| this.height = data.height; |
| |
| this.bitDepth = 8; |
| |
| this.colorType = 2; |
| |
| if (data.palette.isDirect) { |
| if (transparencyType == SWT.TRANSPARENCY_ALPHA) { |
| this.colorType = 6; |
| } |
| } |
| else { |
| this.colorType = 3; |
| } |
| |
| if (!(colorType == 2 || colorType == 3 || colorType == 6)) SWT.error(SWT.ERROR_INVALID_IMAGE); |
| |
| } |
| |
| void writeShort(ByteArrayOutputStream baos, int theShort) { |
| |
| byte byte1 = (byte) ((theShort >> 8) & 0xff); |
| byte byte2 = (byte) (theShort & 0xff); |
| byte[] temp = {byte1, byte2}; |
| baos.write(temp, 0, 2); |
| |
| } |
| |
| void writeInt(ByteArrayOutputStream baos, int theInt) { |
| |
| byte byte1 = (byte) ((theInt >> 24) & 0xff); |
| byte byte2 = (byte) ((theInt >> 16) & 0xff); |
| byte byte3 = (byte) ((theInt >> 8) & 0xff); |
| byte byte4 = (byte) (theInt & 0xff); |
| byte[] temp = {byte1, byte2, byte3, byte4}; |
| baos.write(temp, 0, 4); |
| |
| } |
| |
| void writeChunk(byte[] tag, byte[] buffer) { |
| |
| int bufferLength = (buffer != null) ? buffer.length : 0; |
| |
| chunk = new PngChunk(bufferLength); |
| |
| writeInt(bytes, bufferLength); |
| bytes.write(tag, 0, 4); |
| chunk.setType(tag); |
| if (bufferLength != 0) { |
| bytes.write(buffer, 0, bufferLength); |
| chunk.setData(buffer); |
| } |
| else { |
| chunk.setCRC(chunk.computeCRC()); |
| } |
| writeInt(bytes, chunk.getCRC()); |
| |
| } |
| |
| void writeSignature() { |
| |
| bytes.write(SIGNATURE, 0, 8); |
| |
| } |
| |
| void writeHeader() { |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(13); |
| |
| writeInt(baos, width); |
| writeInt(baos, height); |
| baos.write(bitDepth); |
| baos.write(colorType); |
| baos.write(compressionMethod); |
| baos.write(filterMethod); |
| baos.write(interlaceMethod); |
| |
| writeChunk(TAG_IHDR, baos.toByteArray()); |
| |
| } |
| |
| void writePalette() { |
| |
| RGB[] RGBs = data.palette.getRGBs(); |
| |
| if (RGBs.length > 256) SWT.error(SWT.ERROR_INVALID_IMAGE); |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(RGBs.length); |
| |
| for (int i = 0; i < RGBs.length; i++) { |
| |
| baos.write((byte) RGBs[i].red); |
| baos.write((byte) RGBs[i].green); |
| baos.write((byte) RGBs[i].blue); |
| |
| } |
| |
| writeChunk(TAG_PLTE, baos.toByteArray()); |
| |
| } |
| |
| void writeTransparency() { |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| switch (transparencyType) { |
| |
| case SWT.TRANSPARENCY_ALPHA: |
| |
| int pixelValue, alphaValue; |
| |
| byte[] alphas = new byte[data.palette.getRGBs().length]; |
| |
| for (int y = 0; y < height; y++) { |
| |
| for (int x = 0; x < width; x++) { |
| |
| pixelValue = data.getPixel(x, y); |
| alphaValue = data.getAlpha(x, y); |
| |
| alphas[pixelValue] = (byte) alphaValue; |
| |
| } |
| |
| } |
| |
| baos.write(alphas, 0, alphas.length); |
| |
| break; |
| |
| case SWT.TRANSPARENCY_PIXEL: |
| |
| int pixel = data.transparentPixel; |
| |
| if (colorType == 2) { |
| |
| int redMask = data.palette.redMask; |
| int redShift = data.palette.redShift; |
| int greenMask = data.palette.greenMask; |
| int greenShift = data.palette.greenShift; |
| int blueShift = data.palette.blueShift; |
| int blueMask = data.palette.blueMask; |
| |
| int r = pixel & redMask; |
| r = (redShift < 0) ? r >>> -redShift : r << redShift; |
| int g = pixel & greenMask; |
| g = (greenShift < 0) ? g >>> -greenShift : g << greenShift; |
| int b = pixel & blueMask; |
| b = (blueShift < 0) ? b >>> -blueShift : b << blueShift; |
| |
| writeShort(baos, r); |
| writeShort(baos, g); |
| writeShort(baos, b); |
| |
| } |
| |
| if (colorType == 3) { |
| |
| byte[] padding = new byte[pixel + 1]; |
| |
| for (int i = 0; i < pixel; i++) { |
| |
| padding[i] = (byte) 255; |
| |
| } |
| |
| padding[pixel] = (byte) 0; |
| |
| baos.write(padding, 0, padding.length); |
| |
| } |
| |
| break; |
| |
| } |
| |
| writeChunk(TAG_TRNS, baos.toByteArray()); |
| |
| } |
| |
| void writeImageData() throws IOException { |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); |
| OutputStream os = Compatibility.newDeflaterOutputStream(baos); |
| if (os == null) os = baos; |
| |
| if (colorType == 3) { |
| |
| byte[] lineData = new byte[width]; |
| |
| for (int y = 0; y < height; y++) { |
| |
| int filter = 0; |
| os.write(filter); |
| |
| data.getPixels(0, y, width, lineData, 0); |
| |
| for (int x = 0; x < lineData.length; x++) { |
| |
| os.write(lineData[x]); |
| |
| } |
| |
| } |
| |
| } |
| |
| else { |
| |
| int[] lineData = new int[width]; |
| byte[] alphaData = null; |
| if (colorType == 6) { |
| alphaData = new byte[width]; |
| } |
| |
| int redMask = data.palette.redMask; |
| int redShift = data.palette.redShift; |
| int greenMask = data.palette.greenMask; |
| int greenShift = data.palette.greenShift; |
| int blueShift = data.palette.blueShift; |
| int blueMask = data.palette.blueMask; |
| |
| for (int y = 0; y < height; y++) { |
| |
| int filter = 0; |
| os.write(filter); |
| |
| data.getPixels(0, y, width, lineData, 0); |
| |
| if (colorType == 6) { |
| data.getAlphas(0, y, width, alphaData, 0); |
| } |
| |
| for (int x = 0; x < lineData.length; x++) { |
| |
| int pixel = lineData[x]; |
| |
| int r = pixel & redMask; |
| r = (redShift < 0) ? r >>> -redShift : r << redShift; |
| int g = pixel & greenMask; |
| g = (greenShift < 0) ? g >>> -greenShift : g << greenShift; |
| int b = pixel & blueMask; |
| b = (blueShift < 0) ? b >>> -blueShift : b << blueShift; |
| |
| os.write(r); |
| os.write(g); |
| os.write(b); |
| |
| if (colorType == 6) { |
| os.write(alphaData[x]); |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| os.flush(); |
| os.close(); |
| |
| byte[] compressed = baos.toByteArray(); |
| if (os == baos) { |
| PngDeflater deflater = new PngDeflater(); |
| compressed = deflater.deflate(compressed); |
| } |
| |
| writeChunk(TAG_IDAT, compressed); |
| |
| } |
| |
| void writeEnd() { |
| |
| writeChunk(TAG_IEND, null); |
| |
| } |
| |
| public void encode(LEDataOutputStream outputStream) { |
| |
| try { |
| |
| writeSignature(); |
| writeHeader(); |
| |
| if (colorType == 3) { |
| writePalette(); |
| } |
| |
| boolean transparencyAlpha = (transparencyType == SWT.TRANSPARENCY_ALPHA); |
| boolean transparencyPixel = (transparencyType == SWT.TRANSPARENCY_PIXEL); |
| boolean type2Transparency = (colorType == 2 && transparencyPixel); |
| boolean type3Transparency = (colorType == 3 && (transparencyAlpha || transparencyPixel)); |
| |
| if (type2Transparency || type3Transparency) { |
| writeTransparency(); |
| } |
| |
| writeImageData(); |
| writeEnd(); |
| |
| outputStream.write(bytes.toByteArray()); |
| |
| } |
| |
| catch (IOException e) { |
| |
| SWT.error(SWT.ERROR_IO, e); |
| |
| } |
| |
| } |
| |
| } |