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