Package com.badlogic.gdx.graphics

Source Code of com.badlogic.gdx.graphics.PixmapIO

package com.badlogic.gdx.graphics;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.utils.GdxRuntimeException;

/** Class with static methods to read and write Pixmaps to a custom zlib based format. First three integers define the width,
* height and format, remaining bytes are zlib compressed pixels.
* @author mzechner
* @author Nathan Sweet */
public class PixmapIO {
  /** Writes the {@link Pixmap} to the given file using a custom compression scheme. To be able to load the Pixmap to a Texture,
   * use ".cim" as the file suffix! Throws a GdxRuntimeException in case the Pixmap couldn't be written to the file.
   * @param file the file to write the Pixmap to */
  static public void writeCIM (FileHandle file, Pixmap pixmap) {
    CIM.write(file, pixmap);
  }

  /** Reads the {@link Pixmap} from the given file, assuming the Pixmap was written with the
   * {@link PixmapIO#writeCIM(FileHandle, Pixmap)} method. Throws a GdxRuntimeException in case the file couldn't be read.
   * @param file the file to read the Pixmap from */
  static public Pixmap readCIM (FileHandle file) {
    return CIM.read(file);
  }

  /** Writes the pixmap as a PNG. Note this method uses quite a bit of working memory. {@link #writeCIM(FileHandle, Pixmap)} is
   * faster if the file does not need to be read outside of libgdx. */
  static public void writePNG (FileHandle file, Pixmap pixmap) {
    try {
      file.writeBytes(PNG.write(pixmap), false);
    } catch (IOException ex) {
      throw new GdxRuntimeException("Error writing PNG: " + file, ex);
    }
  }

  /** @author mzechner */
  static private class CIM {
    static private final int BUFFER_SIZE = 32000;
    static private final byte[] writeBuffer = new byte[BUFFER_SIZE];
    static private final byte[] readBuffer = new byte[BUFFER_SIZE];

    static public void write (FileHandle file, Pixmap pixmap) {
      DataOutputStream out = null;

      try {
        // long start = System.nanoTime();
        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(file.write(false));
        out = new DataOutputStream(deflaterOutputStream);
        out.writeInt(pixmap.getWidth());
        out.writeInt(pixmap.getHeight());
        out.writeInt(Format.toGdx2DPixmapFormat(pixmap.getFormat()));

        ByteBuffer pixelBuf = pixmap.getPixels();
        pixelBuf.position(0);
        pixelBuf.limit(pixelBuf.capacity());

        int remainingBytes = pixelBuf.capacity() % BUFFER_SIZE;
        int iterations = pixelBuf.capacity() / BUFFER_SIZE;

        synchronized (writeBuffer) {
          for (int i = 0; i < iterations; i++) {
            pixelBuf.get(writeBuffer);
            out.write(writeBuffer);
          }

          pixelBuf.get(writeBuffer, 0, remainingBytes);
          out.write(writeBuffer, 0, remainingBytes);
        }

        pixelBuf.position(0);
        pixelBuf.limit(pixelBuf.capacity());
        // Gdx.app.log("PixmapIO", "write (" + file.name() + "):" + (System.nanoTime() - start) / 1000000000.0f + ", " +
        // Thread.currentThread().getName());
      } catch (Exception e) {
        throw new GdxRuntimeException("Couldn't write Pixmap to file '" + file + "'", e);
      } finally {
        if (out != null) try {
          out.close();
        } catch (Exception e) {
        }
      }
    }

    static public Pixmap read (FileHandle file) {
      DataInputStream in = null;

      try {
        // long start = System.nanoTime();
        in = new DataInputStream(new InflaterInputStream(new BufferedInputStream(file.read())));
        int width = in.readInt();
        int height = in.readInt();
        Format format = Format.fromGdx2DPixmapFormat(in.readInt());
        Pixmap pixmap = new Pixmap(width, height, format);

        ByteBuffer pixelBuf = pixmap.getPixels();
        pixelBuf.position(0);
        pixelBuf.limit(pixelBuf.capacity());

        synchronized (readBuffer) {
          int readBytes = 0;
          while ((readBytes = in.read(readBuffer)) > 0) {
            pixelBuf.put(readBuffer, 0, readBytes);
          }
        }

        pixelBuf.position(0);
        pixelBuf.limit(pixelBuf.capacity());
        // Gdx.app.log("PixmapIO", "read:" + (System.nanoTime() - start) / 1000000000.0f);
        return pixmap;
      } catch (Exception e) {
        throw new GdxRuntimeException("Couldn't read Pixmap from file '" + file + "'", e);
      } finally {
        if (in != null) try {
          in.close();
        } catch (Exception e) {
        }
      }
    }
  }

  /** Minimal PNG encoder to create PNG streams (and MIDP images) from RGBA arrays.<br>
   * Copyright 2006-2009 Christian Fröschlin www.chrfr.de<br>
   * Terms of Use: You may use the PNG encoder free of charge for any purpose you desire, as long as you do not claim credit for
   * the original sources and agree not to hold me responsible for any damage arising out of its use.<br>
   * If you have a suitable location in GUI or documentation for giving credit, I'd appreciate a non-mandatory mention of:<br>
   * PNG encoder (C) 2006-2009 by Christian Fröschlin, www.chrfr.de */
  static private class PNG {
    static int[] crcTable;
    static final int ZLIB_BLOCK_SIZE = 32000;

    static byte[] write (Pixmap pixmap) throws IOException {
      byte[] signature = new byte[] {(byte)137, (byte)80, (byte)78, (byte)71, (byte)13, (byte)10, (byte)26, (byte)10};
      byte[] header = PNG.createHeaderChunk(pixmap.getWidth(), pixmap.getHeight());
      byte[] data = PNG.createDataChunk(pixmap);
      byte[] trailer = PNG.createTrailerChunk();

      ByteArrayOutputStream png = new ByteArrayOutputStream(signature.length + header.length + data.length + trailer.length);
      png.write(signature);
      png.write(header);
      png.write(data);
      png.write(trailer);
      return png.toByteArray();
    }

    static private byte[] createHeaderChunk (int width, int height) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream(13);
      DataOutputStream chunk = new DataOutputStream(baos);
      chunk.writeInt(width);
      chunk.writeInt(height);
      chunk.writeByte(8); // Bitdepth
      chunk.writeByte(6); // Colortype ARGB
      chunk.writeByte(0); // Compression
      chunk.writeByte(0); // Filter
      chunk.writeByte(0); // Interlace
      return toChunk("IHDR", baos.toByteArray());
    }

    static private byte[] createDataChunk (Pixmap pixmap) throws IOException {
      int width = pixmap.getWidth();
      int height = pixmap.getHeight();
      int dest = 0;
      byte[] raw = new byte[4 * width * height + height];
      for (int y = 0; y < height; y++) {
        raw[dest++] = 0; // No filter
        for (int x = 0; x < width; x++) {
          // 32-bit RGBA8888
          int pixel = pixmap.getPixel(x, y);

          int mask = pixel & 0xFFFFFFFF;
          int rr = mask >> 24 & 0xff;
          int gg = mask >> 16 & 0xff;
          int bb = mask >> 8 & 0xff;
          int aa = mask & 0xff;

          raw[dest++] = (byte)rr;
          raw[dest++] = (byte)gg;
          raw[dest++] = (byte)bb;
          raw[dest++] = (byte)aa;
        }
      }
      return toChunk("IDAT", toZLIB(raw));
    }

    static private byte[] createTrailerChunk () throws IOException {
      return toChunk("IEND", new byte[] {});
    }

    static private byte[] toChunk (String id, byte[] raw) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 12);
      DataOutputStream chunk = new DataOutputStream(baos);

      chunk.writeInt(raw.length);

      byte[] bid = new byte[4];
      for (int i = 0; i < 4; i++) {
        bid[i] = (byte)id.charAt(i);
      }

      chunk.write(bid);

      chunk.write(raw);

      int crc = 0xFFFFFFFF;
      crc = updateCRC(crc, bid);
      crc = updateCRC(crc, raw);
      chunk.writeInt(~crc);

      return baos.toByteArray();
    }

    static private void createCRCTable () {
      crcTable = new int[256];
      for (int i = 0; i < 256; i++) {
        int c = i;
        for (int k = 0; k < 8; k++)
          c = (c & 1) > 0 ? 0xedb88320 ^ c >>> 1 : c >>> 1;
        crcTable[i] = c;
      }
    }

    static private int updateCRC (int crc, byte[] raw) {
      if (crcTable == null) createCRCTable();
      for (byte element : raw)
        crc = crcTable[(crc ^ element) & 0xFF] ^ crc >>> 8;
      return crc;
    }

    /*
     * This method is called to encode the image data as a zlib block as required by the PNG specification. This file comes with
     * a minimal ZLIB encoder which uses uncompressed deflate blocks (fast, short, easy, but no compression). If you want
     * compression, call another encoder (such as JZLib?) here.
     */
    static private byte[] toZLIB (byte[] raw) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 6 + raw.length / ZLIB_BLOCK_SIZE * 5);
      DataOutputStream zlib = new DataOutputStream(baos);

      byte tmp = (byte)8;
      zlib.writeByte(tmp); // CM = 8, CMINFO = 0
      zlib.writeByte((31 - (tmp << 8) % 31) % 31); // FCHECK
      // (FDICT/FLEVEL=0)

      int pos = 0;
      while (raw.length - pos > ZLIB_BLOCK_SIZE) {
        writeUncompressedDeflateBlock(zlib, false, raw, pos, (char)ZLIB_BLOCK_SIZE);
        pos += ZLIB_BLOCK_SIZE;
      }

      writeUncompressedDeflateBlock(zlib, true, raw, pos, (char)(raw.length - pos));

      // zlib check sum of uncompressed data
      zlib.writeInt(calcADLER32(raw));

      return baos.toByteArray();
    }

    static private void writeUncompressedDeflateBlock (DataOutputStream zlib, boolean last, byte[] raw, int off, char len)
      throws IOException {
      zlib.writeByte((byte)(last ? 1 : 0)); // Final flag, Compression type 0
      zlib.writeByte((byte)(len & 0xFF)); // Length LSB
      zlib.writeByte((byte)((len & 0xFF00) >> 8)); // Length MSB
      zlib.writeByte((byte)(~len & 0xFF)); // Length 1st complement LSB
      zlib.writeByte((byte)((~len & 0xFF00) >> 8)); // Length 1st complement
      // MSB
      zlib.write(raw, off, len); // Data
    }

    static private int calcADLER32 (byte[] raw) {
      int s1 = 1;
      int s2 = 0;
      for (byte abs : raw) {
        if (abs < 0) abs += 256;
        s1 = (s1 + abs) % 65521;
        s2 = (s2 + s1) % 65521;
      }
      return (s2 << 16) + s1;
    }
  }
}
TOP

Related Classes of com.badlogic.gdx.graphics.PixmapIO

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.