1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.internal.image;
17 import java.util.zip.*;
19 import org.eclipse.swt.*;
20 import org.eclipse.swt.graphics.*;
22 final class PngEncoder extends Object {
24 static final byte SIGNATURE[] = {(byte) '\211', (byte) 'P', (byte) 'N', (byte) 'G', (byte) '\r', (byte) '\n', (byte) '\032', (byte) '\n'};
25 static final byte TAG_IHDR[] = {(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R'};
26 static final byte TAG_PLTE[] = {(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E'};
27 static final byte TAG_TRNS[] = {(byte) 't', (byte) 'R', (byte) 'N', (byte) 'S'};
28 static final byte TAG_IDAT[] = {(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T'};
29 static final byte TAG_IEND[] = {(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D'};
31 static final int NO_COMPRESSION = 0;
32 static final int BEST_SPEED = 1;
33 static final int BEST_COMPRESSION = 9;
34 static final int DEFAULT_COMPRESSION = -1;
36 ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024);
43 int width, height, bitDepth, colorType;
45 int compressionMethod = 0;
47 int interlaceMethod = 0;
49 public PngEncoder(ImageLoader loader) {
52 this.data = loader.data[0];
53 this.transparencyType = data.getTransparencyType();
55 this.width = data.width;
56 this.height = data.height;
62 if (data.palette.isDirect) {
63 if (transparencyType == SWT.TRANSPARENCY_ALPHA) {
71 if (!(colorType == 2 || colorType == 3 || colorType == 6)) SWT.error(SWT.ERROR_INVALID_IMAGE);
75 void writeShort(ByteArrayOutputStream baos, int theShort) {
77 byte byte1 = (byte) ((theShort >> 8) & 0xff);
78 byte byte2 = (byte) (theShort & 0xff);
79 byte[] temp = {byte1, byte2};
80 baos.write(temp, 0, 2);
84 void writeInt(ByteArrayOutputStream baos, int theInt) {
86 byte byte1 = (byte) ((theInt >> 24) & 0xff);
87 byte byte2 = (byte) ((theInt >> 16) & 0xff);
88 byte byte3 = (byte) ((theInt >> 8) & 0xff);
89 byte byte4 = (byte) (theInt & 0xff);
90 byte[] temp = {byte1, byte2, byte3, byte4};
91 baos.write(temp, 0, 4);
95 void writeChunk(byte[] tag, byte[] buffer) {
97 int bufferLength = (buffer != null) ? buffer.length : 0;
99 chunk = new PngChunk(bufferLength);
101 writeInt(bytes, bufferLength);
102 bytes.write(tag, 0, 4);
104 if (bufferLength != 0) {
105 bytes.write(buffer, 0, bufferLength);
106 chunk.setData(buffer);
109 chunk.setCRC(chunk.computeCRC());
111 writeInt(bytes, chunk.getCRC());
115 void writeSignature() {
117 bytes.write(SIGNATURE, 0, 8);
123 ByteArrayOutputStream baos = new ByteArrayOutputStream(13);
125 writeInt(baos, width);
126 writeInt(baos, height);
127 baos.write(bitDepth);
128 baos.write(colorType);
129 baos.write(compressionMethod);
130 baos.write(filterMethod);
131 baos.write(interlaceMethod);
133 writeChunk(TAG_IHDR, baos.toByteArray());
137 void writePalette() {
139 RGB[] RGBs = data.palette.getRGBs();
141 if (RGBs.length > 256) SWT.error(SWT.ERROR_INVALID_IMAGE);
143 ByteArrayOutputStream baos = new ByteArrayOutputStream(RGBs.length);
145 for (int i = 0; i < RGBs.length; i++) {
147 baos.write((byte) RGBs[i].red);
148 baos.write((byte) RGBs[i].green);
149 baos.write((byte) RGBs[i].blue);
153 writeChunk(TAG_PLTE, baos.toByteArray());
157 void writeTransparency() {
159 ByteArrayOutputStream baos = new ByteArrayOutputStream();
161 switch (transparencyType) {
163 case SWT.TRANSPARENCY_ALPHA:
165 int pixelValue, alphaValue;
167 byte[] alphas = new byte[data.palette.getRGBs().length];
169 for (int y = 0; y < height; y++) {
171 for (int x = 0; x < width; x++) {
173 pixelValue = data.getPixel(x, y);
174 alphaValue = data.getAlpha(x, y);
176 alphas[pixelValue] = (byte) alphaValue;
182 baos.write(alphas, 0, alphas.length);
186 case SWT.TRANSPARENCY_PIXEL:
188 int pixel = data.transparentPixel;
190 if (colorType == 2) {
192 int redMask = data.palette.redMask;
193 int redShift = data.palette.redShift;
194 int greenMask = data.palette.greenMask;
195 int greenShift = data.palette.greenShift;
196 int blueShift = data.palette.blueShift;
197 int blueMask = data.palette.blueMask;
199 int r = pixel & redMask;
200 r = (redShift < 0) ? r >>> -redShift : r << redShift;
201 int g = pixel & greenMask;
202 g = (greenShift < 0) ? g >>> -greenShift : g << greenShift;
203 int b = pixel & blueMask;
204 b = (blueShift < 0) ? b >>> -blueShift : b << blueShift;
212 if (colorType == 3) {
214 byte[] padding = new byte[pixel + 1];
216 for (int i = 0; i < pixel; i++) {
218 padding[i] = (byte) 255;
222 padding[pixel] = (byte) 0;
224 baos.write(padding, 0, padding.length);
232 writeChunk(TAG_TRNS, baos.toByteArray());
236 void writeImageData() throws IOException {
238 ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
239 OutputStream os = null;
240 switch (loader.compression) {
242 os = new DeflaterOutputStream(baos, new Deflater(NO_COMPRESSION));
245 os = new DeflaterOutputStream(baos, new Deflater(BEST_SPEED));
248 os = new DeflaterOutputStream(baos, new Deflater(BEST_COMPRESSION));
251 os = new DeflaterOutputStream(baos, new Deflater(DEFAULT_COMPRESSION));
255 if (colorType == 3) {
257 byte[] lineData = new byte[width];
259 for (int y = 0; y < height; y++) {
264 data.getPixels(0, y, width, lineData, 0);
274 int[] lineData = new int[width];
275 byte[] alphaData = null;
276 if (colorType == 6) {
277 alphaData = new byte[width];
280 int redMask = data.palette.redMask;
281 int redShift = data.palette.redShift;
282 int greenMask = data.palette.greenMask;
283 int greenShift = data.palette.greenShift;
284 int blueShift = data.palette.blueShift;
285 int blueMask = data.palette.blueMask;
287 byte[] lineBytes = new byte[width * (colorType == 6 ? 4 : 3)];
289 for (int y = 0; y < height; y++) {
294 data.getPixels(0, y, width, lineData, 0);
296 if (colorType == 6) {
297 data.getAlphas(0, y, width, alphaData, 0);
301 for (int x = 0; x < lineData.length; x++) {
303 int pixel = lineData[x];
305 int r = pixel & redMask;
306 lineBytes[offset++] = (byte) ((redShift < 0) ? r >>> -redShift
308 int g = pixel & greenMask;
309 lineBytes[offset++] = (byte) ((greenShift < 0) ? g >>> -greenShift
311 int b = pixel & blueMask;
312 lineBytes[offset++] = (byte) ((blueShift < 0) ? b >>> -blueShift
315 if (colorType == 6) {
316 lineBytes[offset++] = alphaData[x];
330 byte[] compressed = baos.toByteArray();
332 /* Use PngDeflater for J2ME. */
333 PngDeflater deflater = new PngDeflater();
334 compressed = deflater.deflate(compressed);
337 writeChunk(TAG_IDAT, compressed);
343 writeChunk(TAG_IEND, null);
347 public void encode(LEDataOutputStream outputStream) {
354 if (colorType == 3) {
358 boolean transparencyAlpha = (transparencyType == SWT.TRANSPARENCY_ALPHA);
359 boolean transparencyPixel = (transparencyType == SWT.TRANSPARENCY_PIXEL);
360 boolean type2Transparency = (colorType == 2 && transparencyPixel);
361 boolean type3Transparency = (colorType == 3 && (transparencyAlpha || transparencyPixel));
363 if (type2Transparency || type3Transparency) {
370 outputStream.write(bytes.toByteArray());
374 catch (IOException e) {
376 SWT.error(SWT.ERROR_IO, e);