001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s): Alexandre Iline.
025: *
026: * The Original Software is the Jemmy library.
027: * The Initial Developer of the Original Software is Alexandre Iline.
028: * All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: *
042: *
043: * $Id$ $Revision$ $Date$
044: *
045: */
046:
047: package org.netbeans.jemmy.util;
048:
049: import java.awt.Color;
050:
051: import java.awt.image.BufferedImage;
052:
053: import java.io.InputStream;
054: import java.io.IOException;
055: import java.io.FileInputStream;
056:
057: import org.netbeans.jemmy.JemmyException;
058:
059: import java.util.zip.DataFormatException;
060: import java.util.zip.Inflater;
061:
062: /**
063: * Allows to load PNG graphical file.
064: * @author Alexandre Iline
065: */
066: public class PNGDecoder extends Object {
067:
068: InputStream in;
069:
070: /**
071: * Constructs a PNGDecoder object.
072: * @param in input stream to read PNG image from.
073: */
074: public PNGDecoder(InputStream in) {
075: this .in = in;
076: }
077:
078: byte read() throws IOException {
079: byte b = (byte) in.read();
080: return (b);
081: }
082:
083: int readInt() throws IOException {
084: byte b[] = read(4);
085: return (((b[0] & 0xff) << 24) + ((b[1] & 0xff) << 16)
086: + ((b[2] & 0xff) << 8) + ((b[3] & 0xff)));
087: }
088:
089: byte[] read(int count) throws IOException {
090: byte[] result = new byte[count];
091: for (int i = 0; i < count; i++) {
092: result[i] = read();
093: }
094: return (result);
095: }
096:
097: boolean compare(byte[] b1, byte[] b2) {
098: if (b1.length != b2.length) {
099: return (false);
100: }
101: for (int i = 0; i < b1.length; i++) {
102: if (b1[i] != b2[i]) {
103: return (false);
104: }
105: }
106: return (true);
107: }
108:
109: void checkEquality(byte[] b1, byte[] b2) {
110: if (!compare(b1, b2)) {
111: throw (new JemmyException("Format error"));
112: }
113: }
114:
115: /**
116: * Decodes image from an input stream passed into constructor.
117: * @return a BufferedImage object
118: * @throws IOException
119: */
120: public BufferedImage decode() throws IOException {
121:
122: byte[] id = read(12);
123: checkEquality(id, new byte[] { -119, 80, 78, 71, 13, 10, 26,
124: 10, 0, 0, 0, 13 });
125:
126: byte[] ihdr = read(4);
127: checkEquality(ihdr, "IHDR".getBytes());
128:
129: int width = readInt();
130: int height = readInt();
131:
132: BufferedImage result = new BufferedImage(width, height,
133: BufferedImage.TYPE_INT_RGB);
134:
135: byte[] head = read(5);
136: int mode;
137: if (compare(head, new byte[] { 1, 0, 0, 0, 0 })) {
138: mode = PNGEncoder.BW_MODE;
139: } else if (compare(head, new byte[] { 8, 0, 0, 0, 0 })) {
140: mode = PNGEncoder.GREYSCALE_MODE;
141: } else if (compare(head, new byte[] { 8, 2, 0, 0, 0 })) {
142: mode = PNGEncoder.COLOR_MODE;
143: } else {
144: throw (new JemmyException("Format error"));
145: }
146:
147: readInt();//!!crc
148:
149: int size = readInt();
150:
151: byte[] idat = read(4);
152: checkEquality(idat, "IDAT".getBytes());
153:
154: byte[] data = read(size);
155:
156: Inflater inflater = new Inflater();
157: inflater.setInput(data, 0, size);
158:
159: int color;
160:
161: try {
162: switch (mode) {
163: case PNGEncoder.BW_MODE: {
164: int bytes = (int) (width / 8);
165: if ((width % 8) != 0) {
166: bytes++;
167: }
168: byte colorset;
169: byte[] row = new byte[bytes];
170: for (int y = 0; y < height; y++) {
171: inflater.inflate(new byte[1]);
172: inflater.inflate(row);
173: for (int x = 0; x < bytes; x++) {
174: colorset = row[x];
175: for (int sh = 0; sh < 8; sh++) {
176: if (x * 8 + sh >= width) {
177: break;
178: }
179: if ((colorset & 0x80) == 0x80) {
180: result.setRGB(x * 8 + sh, y,
181: Color.white.getRGB());
182: } else {
183: result.setRGB(x * 8 + sh, y,
184: Color.black.getRGB());
185: }
186: colorset <<= 1;
187: }
188: }
189: }
190: }
191: break;
192: case PNGEncoder.GREYSCALE_MODE: {
193: byte[] row = new byte[width];
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: color = row[x];
199: result.setRGB(x, y, (color << 16)
200: + (color << 8) + color);
201: }
202: }
203: }
204: break;
205: case PNGEncoder.COLOR_MODE: {
206: byte[] row = new byte[width * 3];
207: for (int y = 0; y < height; y++) {
208: inflater.inflate(new byte[1]);
209: inflater.inflate(row);
210: for (int x = 0; x < width; x++) {
211: result
212: .setRGB(
213: x,
214: y,
215: ((row[x * 3 + 0] & 0xff) << 16)
216: + ((row[x * 3 + 1] & 0xff) << 8)
217: + ((row[x * 3 + 2] & 0xff)));
218: }
219: }
220: }
221: }
222: } catch (DataFormatException e) {
223: throw (new JemmyException("ZIP error", e));
224: }
225:
226: readInt();//!!crc
227: readInt();//0
228:
229: byte[] iend = read(4);
230: checkEquality(iend, "IEND".getBytes());
231:
232: readInt();//!!crc
233: in.close();
234:
235: return (result);
236: }
237:
238: /**
239: * Decodes image from file.
240: * @param fileName a file to read image from
241: * @return a BufferedImage instance.
242: */
243: public static BufferedImage decode(String fileName) {
244: try {
245: return (new PNGDecoder(new FileInputStream(fileName))
246: .decode());
247: } catch (IOException e) {
248: throw (new JemmyException(
249: "IOException during image reading", e));
250: }
251: }
252:
253: }
|