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