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.AWTException;
050: import java.awt.Component;
051: import java.awt.Rectangle;
052: import java.awt.Robot;
053: import java.awt.Toolkit;
054:
055: import java.awt.image.BufferedImage;
056:
057: import java.io.BufferedOutputStream;
058: import java.io.ByteArrayOutputStream;
059: import java.io.IOException;
060: import java.io.FileOutputStream;
061: import java.io.OutputStream;
062:
063: import java.util.zip.CRC32;
064: import java.util.zip.Deflater;
065: import java.util.zip.DeflaterOutputStream;
066: import java.util.zip.Inflater;
067:
068: /** This class allows to encode BufferedImage into B/W, greyscale or true color PNG
069: * image format with maximum compression.<br>
070: * It also provides complete functionality for capturing full screen, part of
071: * screen or single component, encoding and saving captured image info PNG file.
072: * @author Adam Sotona
073: * @version 1.0 */
074: public class PNGEncoder extends Object {
075:
076: /** black and white image mode. */
077: public static final byte BW_MODE = 0;
078: /** grey scale image mode. */
079: public static final byte GREYSCALE_MODE = 1;
080: /** full color image mode. */
081: public static final byte COLOR_MODE = 2;
082:
083: OutputStream out;
084: CRC32 crc;
085: byte mode;
086:
087: /** public constructor of PNGEncoder class with greyscale mode by default.
088: * @param out output stream for PNG image format to write into
089: */
090: public PNGEncoder(OutputStream out) {
091: this (out, GREYSCALE_MODE);
092: }
093:
094: /** public constructor of PNGEncoder class.
095: * @param out output stream for PNG image format to write into
096: * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE
097: */
098: public PNGEncoder(OutputStream out, byte mode) {
099: crc = new CRC32();
100: this .out = out;
101: if (mode < 0 || mode > 2)
102: throw new IllegalArgumentException("Unknown color mode");
103: this .mode = mode;
104: }
105:
106: void write(int i) throws IOException {
107: byte b[] = { (byte) ((i >> 24) & 0xff),
108: (byte) ((i >> 16) & 0xff), (byte) ((i >> 8) & 0xff),
109: (byte) (i & 0xff) };
110: write(b);
111: }
112:
113: void write(byte b[]) throws IOException {
114: out.write(b);
115: crc.update(b);
116: }
117:
118: /** main encoding method (stays blocked till encoding is finished).
119: * @param image BufferedImage to encode
120: * @throws IOException IOException
121: */
122: public void encode(BufferedImage image) throws IOException {
123: int width = image.getWidth(null);
124: int height = image.getHeight(null);
125: final byte id[] = { -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0,
126: 13 };
127: write(id);
128: crc.reset();
129: write("IHDR".getBytes());
130: write(width);
131: write(height);
132: byte head[] = null;
133: switch (mode) {
134: case BW_MODE:
135: head = new byte[] { 1, 0, 0, 0, 0 };
136: break;
137: case GREYSCALE_MODE:
138: head = new byte[] { 8, 0, 0, 0, 0 };
139: break;
140: case COLOR_MODE:
141: head = new byte[] { 8, 2, 0, 0, 0 };
142: break;
143: }
144: write(head);
145: write((int) crc.getValue());
146: ByteArrayOutputStream compressed = new ByteArrayOutputStream(
147: 65536);
148: BufferedOutputStream bos = new BufferedOutputStream(
149: new DeflaterOutputStream(compressed, new Deflater(9)));
150: int pixel;
151: int color;
152: int colorset;
153: switch (mode) {
154: case BW_MODE:
155: int rest = width % 8;
156: int bytes = width / 8;
157: for (int y = 0; y < height; y++) {
158: bos.write(0);
159: for (int x = 0; x < bytes; x++) {
160: colorset = 0;
161: for (int sh = 0; sh < 8; sh++) {
162: pixel = image.getRGB(x * 8 + sh, y);
163: color = ((pixel >> 16) & 0xff);
164: color += ((pixel >> 8) & 0xff);
165: color += (pixel & 0xff);
166: colorset <<= 1;
167: if (color >= 3 * 128)
168: colorset |= 1;
169: }
170: bos.write((byte) colorset);
171: }
172: if (rest > 0) {
173: colorset = 0;
174: for (int sh = 0; sh < width % 8; sh++) {
175: pixel = image.getRGB(bytes * 8 + sh, y);
176: color = ((pixel >> 16) & 0xff);
177: color += ((pixel >> 8) & 0xff);
178: color += (pixel & 0xff);
179: colorset <<= 1;
180: if (color >= 3 * 128)
181: colorset |= 1;
182: }
183: colorset <<= 8 - rest;
184: bos.write((byte) colorset);
185: }
186: }
187: break;
188: case GREYSCALE_MODE:
189: for (int y = 0; y < height; y++) {
190: bos.write(0);
191: for (int x = 0; x < width; x++) {
192: pixel = image.getRGB(x, y);
193: color = ((pixel >> 16) & 0xff);
194: color += ((pixel >> 8) & 0xff);
195: color += (pixel & 0xff);
196: bos.write((byte) (color / 3));
197: }
198: }
199: break;
200: case COLOR_MODE:
201: for (int y = 0; y < height; y++) {
202: bos.write(0);
203: for (int x = 0; x < width; x++) {
204: pixel = image.getRGB(x, y);
205: bos.write((byte) ((pixel >> 16) & 0xff));
206: bos.write((byte) ((pixel >> 8) & 0xff));
207: bos.write((byte) (pixel & 0xff));
208: }
209: }
210: break;
211: }
212: bos.close();
213: write(compressed.size());
214: crc.reset();
215: write("IDAT".getBytes());
216: write(compressed.toByteArray());
217: write((int) crc.getValue());
218: write(0);
219: crc.reset();
220: write("IEND".getBytes());
221: write((int) crc.getValue());
222: out.close();
223: }
224:
225: /** Static method performing screen capture into PNG image format file with given fileName.
226: * @param rect Rectangle of screen to be captured
227: * @param fileName file name for screen capture PNG image file */
228: public static void captureScreen(Rectangle rect, String fileName) {
229: captureScreen(rect, fileName, GREYSCALE_MODE);
230: }
231:
232: /** Static method performing screen capture into PNG image format file with given fileName.
233: * @param rect Rectangle of screen to be captured
234: * @param mode image color mode
235: * @param fileName file name for screen capture PNG image file */
236: public static void captureScreen(Rectangle rect, String fileName,
237: byte mode) {
238: try {
239: BufferedImage capture = new Robot()
240: .createScreenCapture(rect);
241: BufferedOutputStream file = new BufferedOutputStream(
242: new FileOutputStream(fileName));
243: PNGEncoder encoder = new PNGEncoder(file, mode);
244: encoder.encode(capture);
245: } catch (AWTException awte) {
246: awte.printStackTrace();
247: } catch (IOException ioe) {
248: ioe.printStackTrace();
249: }
250: }
251:
252: /** Static method performing one component screen capture into PNG image format file with given fileName.
253: * @param comp Component to be captured
254: * @param fileName String image target filename */
255: public static void captureScreen(Component comp, String fileName) {
256: captureScreen(comp, fileName, GREYSCALE_MODE);
257: }
258:
259: /** Static method performing one component screen capture into PNG image format file with given fileName.
260: * @param comp Component to be captured
261: * @param fileName String image target filename
262: * @param mode image color mode */
263: public static void captureScreen(Component comp, String fileName,
264: byte mode) {
265: captureScreen(new Rectangle(comp.getLocationOnScreen(), comp
266: .getSize()), fileName, mode);
267: }
268:
269: /** Static method performing whole screen capture into PNG image format file with given fileName.
270: * @param fileName String image target filename */
271: public static void captureScreen(String fileName) {
272: captureScreen(fileName, GREYSCALE_MODE);
273: }
274:
275: /** Static method performing whole screen capture into PNG image format file with given fileName.
276: * @param fileName String image target filename
277: * @param mode image color mode */
278: public static void captureScreen(String fileName, byte mode) {
279: captureScreen(new Rectangle(Toolkit.getDefaultToolkit()
280: .getScreenSize()), fileName, mode);
281: }
282: }
|