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. Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package org.jemmy.image.pixel;
  26 
  27 import java.io.*;
  28 import java.util.zip.CRC32;
  29 import java.util.zip.Deflater;
  30 import java.util.zip.DeflaterOutputStream;
  31 import org.jemmy.image.pixel.Raster.Component;
  32 
  33 /**
  34  *
  35  * @author shura
  36  */
  37 public class PNGSaver {
  38 
  39     /**
  40      * black and white image mode.
  41      */
  42     public static final byte BW_MODE = 0;
  43     /**
  44      * grey scale image mode.
  45      */
  46     public static final byte GREYSCALE_MODE = 1;
  47     /**
  48      * full color image mode.
  49      */
  50     public static final byte COLOR_MODE = 2;
  51     OutputStream out;
  52     CRC32 crc;
  53     byte mode;
  54 
  55     public PNGSaver(File file) throws FileNotFoundException {
  56         this(new FileOutputStream(file));
  57     }
  58 
  59     /**
  60      * public constructor of PNGEncoder class with greyscale mode by default.
  61      *
  62      * @param out output stream for PNG image format to write into
  63      */
  64     public PNGSaver(OutputStream out) {
  65         this(out, COLOR_MODE);
  66     }
  67 
  68     /**
  69      * public constructor of PNGEncoder class.
  70      *
  71      * @param out output stream for PNG image format to write into
  72      * @param mode BW_MODE, GREYSCALE_MODE or COLOR_MODE
  73      */
  74     public PNGSaver(OutputStream out, byte mode) {
  75         crc = new CRC32();
  76         this.out = out;
  77         if (mode < 0 || mode > 2) {
  78             throw new IllegalArgumentException("Unknown color mode");
  79         }
  80         this.mode = mode;
  81     }
  82 
  83     void write(int i) throws IOException {
  84         byte b[] = {(byte) ((i >> 24) & 0xff), (byte) ((i >> 16) & 0xff), (byte) ((i >> 8) & 0xff), (byte) (i & 0xff)};
  85         write(b);
  86     }
  87 
  88     void write(byte b[]) throws IOException {
  89         out.write(b);
  90         crc.update(b);
  91     }
  92 
  93     /**
  94      * main encoding method (stays blocked till encoding is finished).
  95      *
  96      * @param image BufferedImage to encode
  97      * @throws IOException IOException
  98      */
  99     public void encode(Raster image) throws IOException {
 100         encode(image, true);
 101     }
 102 
 103     /**
 104      * main encoding method (stays blocked till encoding is finished).
 105      *
 106      * @param image BufferedImage to encode
 107      * @param closeStream requests method to close the stream after the image is
 108      * written
 109      * @throws IOException IOException
 110      */
 111     private void encode(Raster image, boolean closeStream) throws IOException {
 112         int width = image.getSize().width;
 113         int height = image.getSize().height;
 114         final byte id[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13};
 115         write(id);
 116         crc.reset();
 117         write("IHDR".getBytes());
 118         write(width);
 119         write(height);
 120         byte head[] = null;
 121         switch (mode) {
 122             case BW_MODE:
 123                 head = new byte[]{1, 0, 0, 0, 0};
 124                 break;
 125             case GREYSCALE_MODE:
 126                 head = new byte[]{8, 0, 0, 0, 0};
 127                 break;
 128             case COLOR_MODE:
 129                 head = new byte[]{8, 2, 0, 0, 0};
 130                 break;
 131         }
 132         write(head);
 133         write((int) crc.getValue());
 134         ByteArrayOutputStream compressed = new ByteArrayOutputStream(65536);
 135         BufferedOutputStream bos = new BufferedOutputStream(new DeflaterOutputStream(compressed, new Deflater(9)));
 136         int pixel;
 137         int color;
 138         int colorset;
 139         switch (mode) {
 140             case BW_MODE:
 141                 int rest = width % 8;
 142                 int bytes = width / 8;
 143                 for (int y = 0; y < height; y++) {
 144                     bos.write(0);
 145                     for (int x = 0; x < bytes; x++) {
 146                         colorset = 0;
 147                         for (int sh = 0; sh < 8; sh++) {
 148                             pixel = getRGB(image, x * 8 + sh, y);
 149                             color = ((pixel >> 16) & 0xff);
 150                             color += ((pixel >> 8) & 0xff);
 151                             color += (pixel & 0xff);
 152                             colorset <<= 1;
 153                             if (color >= 3 * 128) {
 154                                 colorset |= 1;
 155                             }
 156                         }
 157                         bos.write((byte) colorset);
 158                     }
 159                     if (rest > 0) {
 160                         colorset = 0;
 161                         for (int sh = 0; sh < width % 8; sh++) {
 162                             pixel = getRGB(image, bytes * 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                         }
 171                         colorset <<= 8 - rest;
 172                         bos.write((byte) colorset);
 173                     }
 174                 }
 175                 break;
 176             case GREYSCALE_MODE:
 177                 for (int y = 0; y < height; y++) {
 178                     bos.write(0);
 179                     for (int x = 0; x < width; x++) {
 180                         pixel = getRGB(image, x, y);
 181                         color = ((pixel >> 16) & 0xff);
 182                         color += ((pixel >> 8) & 0xff);
 183                         color += (pixel & 0xff);
 184                         bos.write((byte) (color / 3));
 185                     }
 186                 }
 187                 break;
 188             case COLOR_MODE:
 189                 for (int y = 0; y < height; y++) {
 190                     bos.write(0);
 191                     for (int x = 0; x < width; x++) {
 192                         pixel = getRGB(image, x, y);
 193                         bos.write((byte) ((pixel >> 16) & 0xff));
 194                         bos.write((byte) ((pixel >> 8) & 0xff));
 195                         bos.write((byte) (pixel & 0xff));
 196                     }
 197                 }
 198                 break;
 199         }
 200         bos.close();
 201         write(compressed.size());
 202         crc.reset();
 203         write("IDAT".getBytes());
 204         write(compressed.toByteArray());
 205         write((int) crc.getValue());
 206         write(0);
 207         crc.reset();
 208         write("IEND".getBytes());
 209         write((int) crc.getValue());
 210         out.flush();
 211         if (closeStream) {
 212             out.close();
 213         }
 214     }
 215     static final Component[] RGB = new Component[]{
 216         Component.RED, Component.GREEN, Component.BLUE};
 217 
 218     private int getRGB(Raster image, int x, int y) {
 219         int res = 0;
 220         double[] colors = new double[image.getSupported().length];
 221         image.getColors(x, y, colors);
 222         for (Component c : RGB) {
 223             res <<= 8;
 224             res |= (int)(colors[PixelImageComparator.arrayIndexOf(image.getSupported(), c)] * 0xFF);
 225         }
 226         return res;
 227     }
 228 }