1 /* 2 * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package org.jemmy.image.pixel; 26 27 import java.io.*; 28 import java.util.zip.CRC32; 29 import java.util.zip.Deflater; 30 import java.util.zip.DeflaterOutputStream; 31 import org.jemmy.image.pixel.Raster.Component; 32 33 /** 34 * 35 * @author shura 36 */ 37 public class PNGSaver { 38 39 /** 40 * black and white image mode. 41 */ 42 public static final byte BW_MODE = 0; 43 /** 44 * grey scale image mode. 45 */ 46 public static final byte GREYSCALE_MODE = 1; 47 /** 48 * full color image mode. 49 */ 50 public static final byte COLOR_MODE = 2; 51 OutputStream out; 52 CRC32 crc; 53 byte mode; 54 55 public PNGSaver(File file) throws FileNotFoundException { 56 this(new FileOutputStream(file)); 57 } 58 59 /** 60 * public constructor of PNGEncoder class with greyscale mode by default. 61 * 62 * @param out output stream for PNG image format to write into 63 */ 64 public PNGSaver(OutputStream out) { 65 this(out, COLOR_MODE); 66 } 67 68 /** 69 * public constructor of PNGEncoder class. 70 * 71 * @param out output stream for PNG image format to write into 72 * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE 73 */ 74 public PNGSaver(OutputStream out, byte mode) { 75 crc = new CRC32(); 76 this.out = out; 77 if (mode < 0 || mode > 2) { 78 throw new IllegalArgumentException("Unknown color mode"); 79 } 80 this.mode = mode; 81 } 82 83 void write(int i) throws IOException { 84 byte b[] = {(byte) ((i >> 24) & 0xff), (byte) ((i >> 16) & 0xff), (byte) ((i >> 8) & 0xff), (byte) (i & 0xff)}; 85 write(b); 86 } 87 88 void write(byte b[]) throws IOException { 89 out.write(b); 90 crc.update(b); 91 } 92 93 /** 94 * main encoding method (stays blocked till encoding is finished). 95 * 96 * @param image BufferedImage to encode 97 * @throws IOException IOException 98 */ 99 public void encode(Raster image) throws IOException { 100 encode(image, true); 101 } 102 103 /** 104 * main encoding method (stays blocked till encoding is finished). 105 * 106 * @param image BufferedImage to encode 107 * @param closeStream requests method to close the stream after the image is 108 * written 109 * @throws IOException IOException 110 */ 111 private void encode(Raster image, boolean closeStream) throws IOException { 112 int width = image.getSize().width; 113 int height = image.getSize().height; 114 final byte id[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13}; 115 write(id); 116 crc.reset(); 117 write("IHDR".getBytes()); 118 write(width); 119 write(height); 120 byte head[] = null; 121 switch (mode) { 122 case BW_MODE: 123 head = new byte[]{1, 0, 0, 0, 0}; 124 break; 125 case GREYSCALE_MODE: 126 head = new byte[]{8, 0, 0, 0, 0}; 127 break; 128 case COLOR_MODE: 129 head = new byte[]{8, 2, 0, 0, 0}; 130 break; 131 } 132 write(head); 133 write((int) crc.getValue()); 134 ByteArrayOutputStream compressed = new ByteArrayOutputStream(65536); 135 BufferedOutputStream bos = new BufferedOutputStream(new DeflaterOutputStream(compressed, new Deflater(9))); 136 int pixel; 137 int color; 138 int colorset; 139 switch (mode) { 140 case BW_MODE: 141 int rest = width % 8; 142 int bytes = width / 8; 143 for (int y = 0; y < height; y++) { 144 bos.write(0); 145 for (int x = 0; x < bytes; x++) { 146 colorset = 0; 147 for (int sh = 0; sh < 8; sh++) { 148 pixel = getRGB(image, x * 8 + sh, y); 149 color = ((pixel >> 16) & 0xff); 150 color += ((pixel >> 8) & 0xff); 151 color += (pixel & 0xff); 152 colorset <<= 1; 153 if (color >= 3 * 128) { 154 colorset |= 1; 155 } 156 } 157 bos.write((byte) colorset); 158 } 159 if (rest > 0) { 160 colorset = 0; 161 for (int sh = 0; sh < width % 8; sh++) { 162 pixel = getRGB(image, bytes * 8 + sh, y); 163 color = ((pixel >> 16) & 0xff); 164 color += ((pixel >> 8) & 0xff); 165 color += (pixel & 0xff); 166 colorset <<= 1; 167 if (color >= 3 * 128) { 168 colorset |= 1; 169 } 170 } 171 colorset <<= 8 - rest; 172 bos.write((byte) colorset); 173 } 174 } 175 break; 176 case GREYSCALE_MODE: 177 for (int y = 0; y < height; y++) { 178 bos.write(0); 179 for (int x = 0; x < width; x++) { 180 pixel = getRGB(image, x, y); 181 color = ((pixel >> 16) & 0xff); 182 color += ((pixel >> 8) & 0xff); 183 color += (pixel & 0xff); 184 bos.write((byte) (color / 3)); 185 } 186 } 187 break; 188 case COLOR_MODE: 189 for (int y = 0; y < height; y++) { 190 bos.write(0); 191 for (int x = 0; x < width; x++) { 192 pixel = getRGB(image, x, y); 193 bos.write((byte) ((pixel >> 16) & 0xff)); 194 bos.write((byte) ((pixel >> 8) & 0xff)); 195 bos.write((byte) (pixel & 0xff)); 196 } 197 } 198 break; 199 } 200 bos.close(); 201 write(compressed.size()); 202 crc.reset(); 203 write("IDAT".getBytes()); 204 write(compressed.toByteArray()); 205 write((int) crc.getValue()); 206 write(0); 207 crc.reset(); 208 write("IEND".getBytes()); 209 write((int) crc.getValue()); 210 out.flush(); 211 if (closeStream) { 212 out.close(); 213 } 214 } 215 static final Component[] RGB = new Component[]{ 216 Component.RED, Component.GREEN, Component.BLUE}; 217 218 private int getRGB(Raster image, int x, int y) { 219 int res = 0; 220 double[] colors = new double[image.getSupported().length]; 221 image.getColors(x, y, colors); 222 for (Component c : RGB) { 223 res <<= 8; 224 res |= (int)(colors[PixelImageComparator.arrayIndexOf(image.getSupported(), c)] * 0xFF); 225 } 226 return res; 227 } 228 }