Nolesh

PNG Encoder based on LibGX's Pixmap class

Apr 11th, 2013
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.39 KB | None | 0 0
  1. import java.io.ByteArrayOutputStream;
  2. import java.io.DataOutputStream;
  3. import java.io.IOException;
  4.  
  5. import com.badlogic.gdx.graphics.Pixmap;
  6.  
  7. /*
  8.  * Minimal PNG encoder to create PNG streams (and MIDP images) from RGBA arrays.
  9.  *
  10.  * Copyright 2006-2009 Christian Fröschlin
  11.  *
  12.  * www.chrfr.de
  13.  *
  14.  *
  15.  * Changelog:
  16.  *
  17.  * 09/22/08: Fixed Adler checksum calculation and byte order
  18.  *           for storing length of zlib deflate block. Thanks
  19.  *           to Miloslav Ruzicka for noting this.
  20.  *
  21.  * 05/12/09: Split PNG and ZLIB functionality into separate classes.
  22.  *           Added support for images > 64K by splitting the data into
  23.  *           multiple uncompressed deflate blocks.
  24.  *
  25.  * Terms of Use:  
  26.  *
  27.  * You may use the PNG encoder free of charge for any purpose you desire, as long
  28.  * as you do not claim credit for the original sources and agree not to hold me
  29.  * responsible for any damage arising out of its use.
  30.  *
  31.  * If you have a suitable location in GUI or documentation for giving credit,
  32.  * I'd appreciate a mention of
  33.  *
  34.  *  PNG encoder (C) 2006-2009 by Christian Fröschlin, www.chrfr.de
  35.  *
  36.  * but that's not mandatory.
  37.  *
  38.  */
  39.  
  40. public class PNG {
  41.    public static byte[] toPNG(Pixmap pixmap) throws IOException {
  42.       byte[] signature = new byte[] { (byte) 137, (byte) 80, (byte) 78,
  43.             (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 };
  44.       byte[] header = createHeaderChunk(pixmap.getWidth(), pixmap.getHeight());
  45.       byte[] data = createDataChunk(pixmap);
  46.       byte[] trailer = createTrailerChunk();
  47.  
  48.       ByteArrayOutputStream png = new ByteArrayOutputStream(signature.length
  49.             + header.length + data.length + trailer.length);
  50.       png.write(signature);
  51.       png.write(header);
  52.       png.write(data);
  53.       png.write(trailer);
  54.       return png.toByteArray();
  55.    }
  56.  
  57.    public static byte[] createHeaderChunk(int width, int height)
  58.          throws IOException {
  59.       ByteArrayOutputStream baos = new ByteArrayOutputStream(13);
  60.       DataOutputStream chunk = new DataOutputStream(baos);
  61.       chunk.writeInt(width);
  62.       chunk.writeInt(height);
  63.       chunk.writeByte(8); // Bitdepth
  64.       chunk.writeByte(6); // Colortype ARGB
  65.       chunk.writeByte(0); // Compression
  66.       chunk.writeByte(0); // Filter
  67.       chunk.writeByte(0); // Interlace
  68.       return toChunk("IHDR", baos.toByteArray());
  69.    }
  70.  
  71.    public static byte[] createDataChunk(Pixmap pixmap) throws IOException {
  72.       int width = pixmap.getWidth();
  73.       int height = pixmap.getHeight();
  74.       int source = 0;
  75.       int dest = 0;
  76.       byte[] raw = new byte[4 * (width * height) + height];
  77.       for (int y = 0; y < height; y++) {
  78.          raw[dest++] = 0; // No filter
  79.          for (int x = 0; x < width; x++) {
  80.  
  81.             // 32-bit RGBA8888
  82.             int pixel = pixmap.getPixel(x, y);
  83.  
  84.             int mask = pixel & 0xFFFFFFFF;
  85.             int rr = (mask >> 24) & 0xff;
  86.             int gg = (mask >> 16) & 0xff;
  87.             int bb = (mask >> 8) & 0xff;
  88.             int aa = (mask) & 0xff;
  89.  
  90.             if (rr < 0 || rr > 255 || gg < 0 || gg > 255 || bb < 0
  91.                   || bb > 255) {
  92.                // break ! (assert doesn't always kick-in with the
  93.                // Eclipse
  94.                // debugger...)
  95.                int divide_by_zero = 0 / 0;
  96.             }
  97.  
  98.             raw[dest++] = (byte) rr;
  99.             raw[dest++] = (byte) gg;
  100.             raw[dest++] = (byte) bb;
  101.             raw[dest++] = (byte) aa;
  102.             source++;
  103.          }
  104.       }
  105.       return toChunk("IDAT", toZLIB(raw));
  106.    }
  107.  
  108.    public static byte[] createTrailerChunk() throws IOException {
  109.       return toChunk("IEND", new byte[] {});
  110.    }
  111.  
  112.    public static byte[] toChunk(String id, byte[] raw) throws IOException {
  113.       ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 12);
  114.       DataOutputStream chunk = new DataOutputStream(baos);
  115.  
  116.       chunk.writeInt(raw.length);
  117.  
  118.       byte[] bid = new byte[4];
  119.       for (int i = 0; i < 4; i++) {
  120.          bid[i] = (byte) id.charAt(i);
  121.       }
  122.  
  123.       chunk.write(bid);
  124.  
  125.       chunk.write(raw);
  126.  
  127.       int crc = 0xFFFFFFFF;
  128.       crc = updateCRC(crc, bid);
  129.       crc = updateCRC(crc, raw);
  130.       chunk.writeInt(~crc);
  131.  
  132.       return baos.toByteArray();
  133.    }
  134.  
  135.    static int[] crcTable = null;
  136.  
  137.    public static void createCRCTable() {
  138.       crcTable = new int[256];
  139.  
  140.       for (int i = 0; i < 256; i++) {
  141.          int c = i;
  142.          for (int k = 0; k < 8; k++) {
  143.             c = ((c & 1) > 0) ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
  144.          }
  145.          crcTable[i] = c;
  146.       }
  147.    }
  148.  
  149.    public static int updateCRC(int crc, byte[] raw) {
  150.       if (crcTable == null) {
  151.          createCRCTable();
  152.       }
  153.  
  154.       for (int i = 0; i < raw.length; i++) {
  155.          crc = crcTable[(crc ^ raw[i]) & 0xFF] ^ (crc >>> 8);
  156.       }
  157.  
  158.       return crc;
  159.    }
  160.  
  161.    /*
  162.     * This method is called to encode the image data as a zlib block as
  163.     * required by the PNG specification. This file comes with a minimal ZLIB
  164.     * encoder which uses uncompressed deflate blocks (fast, short, easy, but no
  165.     * compression). If you want compression, call another encoder (such as
  166.     * JZLib?) here.
  167.     */
  168.    public static byte[] toZLIB(byte[] raw) throws IOException {
  169.       return ZLIB.toZLIB(raw);
  170.    }
  171. }
  172.  
  173. class ZLIB {
  174.    static final int BLOCK_SIZE = 32000;
  175.  
  176.    public static byte[] toZLIB(byte[] raw) throws IOException {
  177.       ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 6
  178.             + (raw.length / BLOCK_SIZE) * 5);
  179.       DataOutputStream zlib = new DataOutputStream(baos);
  180.  
  181.       byte tmp = (byte) 8;
  182.       zlib.writeByte(tmp); // CM = 8, CMINFO = 0
  183.       zlib.writeByte((31 - ((tmp << 8) % 31)) % 31); // FCHECK
  184.                                           // (FDICT/FLEVEL=0)
  185.  
  186.       int pos = 0;
  187.       while (raw.length - pos > BLOCK_SIZE) {
  188.          writeUncompressedDeflateBlock(zlib, false, raw, pos,
  189.                (char) BLOCK_SIZE);
  190.          pos += BLOCK_SIZE;
  191.       }
  192.  
  193.       writeUncompressedDeflateBlock(zlib, true, raw, pos,
  194.             (char) (raw.length - pos));
  195.  
  196.       // zlib check sum of uncompressed data
  197.       zlib.writeInt(calcADLER32(raw));
  198.  
  199.       return baos.toByteArray();
  200.    }
  201.  
  202.    private static void writeUncompressedDeflateBlock(DataOutputStream zlib,
  203.          boolean last, byte[] raw, int off, char len) throws IOException {
  204.       zlib.writeByte((byte) (last ? 1 : 0)); // Final flag, Compression type 0
  205.       zlib.writeByte((byte) (len & 0xFF)); // Length LSB
  206.       zlib.writeByte((byte) ((len & 0xFF00) >> 8)); // Length MSB
  207.       zlib.writeByte((byte) (~len & 0xFF)); // Length 1st complement LSB
  208.       zlib.writeByte((byte) ((~len & 0xFF00) >> 8)); // Length 1st complement
  209.                                           // MSB
  210.       zlib.write(raw, off, len); // Data
  211.    }
  212.  
  213.    private static int calcADLER32(byte[] raw) {
  214.       int s1 = 1;
  215.       int s2 = 0;
  216.       for (int i = 0; i < raw.length; i++) {
  217.          int abs = raw[i] >= 0 ? raw[i] : (raw[i] + 256);
  218.          s1 = (s1 + abs) % 65521;
  219.          s2 = (s2 + s1) % 65521;
  220.       }
  221.       return (s2 << 16) + s1;
  222.    }
  223. }
  224.  
  225. /**To use**/
  226. /*
  227. FileHandle image = Gdx.files.external("directory/filename.png");
  228. OutputStream stream = image.write(false);
  229. try {
  230. byte[] bytes = PNG.toPNG(pixmap);
  231. stream.write(bytes);
  232. stream.close();
  233. } catch (IOException e) {
  234. // TODO: fallback if fail
  235. }
  236. */
Advertisement
Add Comment
Please, Sign In to add comment