1/* 2 * Copyright (c) 1997, 2016, 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 */ 23package org.netbeans.jemmy.util; 24 25import java.awt.Color; 26import java.awt.image.BufferedImage; 27import java.io.FileInputStream; 28import java.io.IOException; 29import java.io.InputStream; 30import java.util.zip.DataFormatException; 31import java.util.zip.Inflater; 32 33import org.netbeans.jemmy.JemmyException; 34 35/** 36 * Allows to load PNG graphical file. 37 * 38 * @author Alexandre Iline 39 */ 40public class PNGDecoder extends Object { 41 42 InputStream in; 43 44 /** 45 * Constructs a PNGDecoder object. 46 * 47 * @param in input stream to read PNG image from. 48 */ 49 public PNGDecoder(InputStream in) { 50 this.in = in; 51 } 52 53 byte read() throws IOException { 54 byte b = (byte) in.read(); 55 return b; 56 } 57 58 int readInt() throws IOException { 59 byte b[] = read(4); 60 return (((b[0] & 0xff) << 24) 61 + ((b[1] & 0xff) << 16) 62 + ((b[2] & 0xff) << 8) 63 + ((b[3] & 0xff))); 64 } 65 66 byte[] read(int count) throws IOException { 67 byte[] result = new byte[count]; 68 for (int i = 0; i < count; i++) { 69 result[i] = read(); 70 } 71 return result; 72 } 73 74 boolean compare(byte[] b1, byte[] b2) { 75 if (b1.length != b2.length) { 76 return false; 77 } 78 for (int i = 0; i < b1.length; i++) { 79 if (b1[i] != b2[i]) { 80 return false; 81 } 82 } 83 return true; 84 } 85 86 void checkEquality(byte[] b1, byte[] b2) { 87 if (!compare(b1, b2)) { 88 throw (new JemmyException("Format error")); 89 } 90 } 91 92 /** 93 * Decodes image from an input stream passed into constructor. 94 * 95 * @return a BufferedImage object 96 * @throws IOException 97 */ 98 public BufferedImage decode() throws IOException { 99 100 byte[] id = read(12); 101 checkEquality(id, new byte[]{-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13}); 102 103 byte[] ihdr = read(4); 104 checkEquality(ihdr, "IHDR".getBytes()); 105 106 int width = readInt(); 107 int height = readInt(); 108 109 BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 110 111 byte[] head = read(5); 112 int mode; 113 if (compare(head, new byte[]{1, 0, 0, 0, 0})) { 114 mode = PNGEncoder.BW_MODE; 115 } else if (compare(head, new byte[]{8, 0, 0, 0, 0})) { 116 mode = PNGEncoder.GREYSCALE_MODE; 117 } else if (compare(head, new byte[]{8, 2, 0, 0, 0})) { 118 mode = PNGEncoder.COLOR_MODE; 119 } else { 120 throw (new JemmyException("Format error")); 121 } 122 123 readInt();//!!crc 124 125 int size = readInt(); 126 127 byte[] idat = read(4); 128 checkEquality(idat, "IDAT".getBytes()); 129 130 byte[] data = read(size); 131 132 Inflater inflater = new Inflater(); 133 inflater.setInput(data, 0, size); 134 135 int color; 136 137 try { 138 switch (mode) { 139 case PNGEncoder.BW_MODE: { 140 int bytes = width / 8; 141 if ((width % 8) != 0) { 142 bytes++; 143 } 144 byte colorset; 145 byte[] row = new byte[bytes]; 146 for (int y = 0; y < height; y++) { 147 inflater.inflate(new byte[1]); 148 inflater.inflate(row); 149 for (int x = 0; x < bytes; x++) { 150 colorset = row[x]; 151 for (int sh = 0; sh < 8; sh++) { 152 if (x * 8 + sh >= width) { 153 break; 154 } 155 if ((colorset & 0x80) == 0x80) { 156 result.setRGB(x * 8 + sh, y, Color.white.getRGB()); 157 } else { 158 result.setRGB(x * 8 + sh, y, Color.black.getRGB()); 159 } 160 colorset <<= 1; 161 } 162 } 163 } 164 } 165 break; 166 case PNGEncoder.GREYSCALE_MODE: { 167 byte[] row = new byte[width]; 168 for (int y = 0; y < height; y++) { 169 inflater.inflate(new byte[1]); 170 inflater.inflate(row); 171 for (int x = 0; x < width; x++) { 172 color = row[x]; 173 result.setRGB(x, y, (color << 16) + (color << 8) + color); 174 } 175 } 176 } 177 break; 178 case PNGEncoder.COLOR_MODE: { 179 byte[] row = new byte[width * 3]; 180 for (int y = 0; y < height; y++) { 181 inflater.inflate(new byte[1]); 182 inflater.inflate(row); 183 for (int x = 0; x < width; x++) { 184 result.setRGB(x, y, 185 ((row[x * 3 + 0] & 0xff) << 16) 186 + ((row[x * 3 + 1] & 0xff) << 8) 187 + ((row[x * 3 + 2] & 0xff))); 188 } 189 } 190 } 191 } 192 } catch (DataFormatException e) { 193 throw (new JemmyException("ZIP error", e)); 194 } 195 196 readInt();//!!crc 197 readInt();//0 198 199 byte[] iend = read(4); 200 checkEquality(iend, "IEND".getBytes()); 201 202 readInt();//!!crc 203 in.close(); 204 205 return result; 206 } 207 208 /** 209 * Decodes image from file. 210 * 211 * @param fileName a file to read image from 212 * @return a BufferedImage instance. 213 */ 214 public static BufferedImage decode(String fileName) { 215 try { 216 return new PNGDecoder(new FileInputStream(fileName)).decode(); 217 } catch (IOException e) { 218 throw (new JemmyException("IOException during image reading", e)); 219 } 220 } 221 222} 223