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