Package font

Source Code of font.TexturePack$Entry

package font;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;

/**
* packs several arbitrary sized images into a jPCT texture.
* the created texture is 2^n x 2^m as  required by jPCT. useful for
* blitting purposes.
*
* <p>see <a href='http://www.blackpawn.com/texts/lightmaps/default.html'
* target='_blank'>this page</a> for an explanation of packing algorithm</p>
*
* @author hakan eryargi (r a f t)
*/
public class TexturePack {
  /** use alpha when creating texture */
  public static boolean ALPHA_USE = true;
  /** do not use alpha when creating texture */
  public static boolean ALPHA_DONT_USE = false;

  private static final boolean DEBUG = false;

  private static int lastPackId = 0;

  private static synchronized int nextPackId() {
    return lastPackId++;
  }

  private final int packId = nextPackId();
  private final String textureName = "imagepack/" + packId;

  private final int imageType;
  private boolean packed = false;
  private final Dimension blittedSize = new Dimension();

  private final List<Entry> entries = new ArrayList<Entry>();
  private Texture texture = null;

  /**
   * creates an TexturePack using BufferedImage.TYPE_INT_ARGB as image type
   *
   * @see BufferedImage#TYPE_INT_ARGB
   */
  public TexturePack() {
    this(BufferedImage.TYPE_INT_ARGB);
  }

  /**
   * creates an TexturePack
   *
   * @param imageType must be one of the image types defined in
   *            {@link BufferedImage} class
   * @see BufferedImage
   */
  public TexturePack(int imageType) {
    this.imageType = imageType;
  }

  /**
   * add image to be included in pack.
   *
   * @return image id which can later be used for blit(..) methods
   */
  public int addImage(Image image) {
    checkPacked();

    if (image == null)
      throw new NullPointerException();
    if (image.getWidth(null) <= 0 || image.getHeight(null) <= 0)
      throw new IllegalArgumentException("width and height must be positive");

    this.entries.add(new Entry(image));
    return entries.size() - 1;
  }

  /**
   * packs images into an 2^n x 2^m size d image. after packing clears all
   * references to given images hence this method can only be called once
   *
   * @return created image
   */
  private BufferedImage packImage() {
    checkPacked();

    if (entries.isEmpty())
      throw new IllegalStateException("nothing to pack");

    int maxWidth = 0;
    int maxHeight = 0;
    int totalArea = 0;

    for (Entry entry : entries) {
      int width = entry.image.getWidth(null);
      int height = entry.image.getHeight(null);

      if (width > maxWidth)
        maxWidth = width;
      if (height > maxHeight)
        maxHeight = height;

      totalArea += width * height;
    }

    Dimension size = new Dimension(closestTwoPower(maxWidth), closestTwoPower(maxHeight));
    boolean fitAll = false;
    if (DEBUG)
      System.out.println("initial size " + size);

    loop: while (!fitAll) {
      int area = size.width * size.height;
      if (area < totalArea) {
        nextSize(size);
        if (DEBUG)
          System.out.println("enlarging to " + size);
        continue;
      }

      Node root = new Node(size.width, size.height);
      for (Entry entry : entries) {
        Node inserted = root.insert(entry);
        if (inserted == null) {
          nextSize(size);
          if (DEBUG)
            System.out.println("couldnt fit, enlarging to " + size);
          continue loop;
        }
      }
      fitAll = true;
      if (DEBUG)
        printTree(root, "", 0);
    }

    BufferedImage image = new BufferedImage(size.width, size.height, imageType);
    Graphics2D g2d = image.createGraphics();
    for (Entry entry : entries) {
      g2d.drawImage(entry.image, entry.bounds.x, entry.bounds.y, null);
      entry.image = null;
    }
    g2d.dispose();
    packed = true;
    // if (DEBUG) raft.karga.gui.Swing.showImage("texture pack", image);

    return image;
  }

  /**
   * packs image, creates a Texture out of it and adds texture to
   * TextureManager.
   *
   * @param useAlpha will created texture use alpha ?
   * @return created texture
   *
   * @see #ALPHA_USE
   * @see #ALPHA_DONT_USE
   */
  public Texture pack(boolean useAlpha) {
    if (texture != null)
      return texture;

    BufferedImage image = packImage();
    texture = new Texture(image, useAlpha);
    TextureManager.getInstance().addTexture(textureName, texture);

    return texture;
  }

  /**
   * blits one of packed images completely without scaling and with
   * transparency or additional color.
   * see {@link FrameBuffer#blit(Texture, int, int, int, int, int, int, int, int, int, boolean, Color)
   * FrameBuffer.blit(..)} for description of other parameters
   *
   * @param buffer the FrameBuffer
   * @param imageId id of packed image as returned by addImage(..)
   *
   * @return size of blitted image. note this method always returns the same
   *         array instance
   * @see FrameBuffer#blit(Texture, int, int, int, int, int, int, int, int, int, boolean, Color)
   *      FrameBuffer.blit(..)
   */
  public Dimension blit(FrameBuffer buffer, int imageId, int destX, int destY,
      int transparency, boolean additive, Color color) {
   
    if (texture == null)
      throw new IllegalStateException("not packed yet");

    Entry entry = entries.get(imageId);
    return blit(buffer, entry, destX, destY, entry.bounds.width,
        entry.bounds.height, transparency, additive, color);
  }

  /**
   * blits one of packed images completely with scaling or transparency or additional color
   * see {@link FrameBuffer#blit(Texture, int, int, int, int, int, int, int, int, int, boolean, Color)
   * FrameBuffer.blit(..)} for description of other parameters
   * 
   * @param buffer the FrameBuffer
   * @param imageId id of packed image as returned by addImage(..)
   *
   * @return size of blitted image. note this method always returns the same
   *         array instance
   * @see FrameBuffer#blit(Texture, int, int, int, int, int, int, int, int, int, boolean, Color)
   *      FrameBuffer.blit(..)
   */
  public Dimension blit(FrameBuffer buffer, int imageId, int destX, int destY,
      int destWidth, int destHeight, int transparency, boolean additive, Color color) {
   
    if (texture == null)
      throw new IllegalStateException("not packed yet");

    Entry entry = entries.get(imageId);
    return blit(buffer, entry, destX, destY, destWidth, destHeight,
        transparency, additive, color);
  }
 
  /**
   * blits one of packed images completely without scaling
   *
   * @param imageId
   *            id of packed image as returned by addImage(..)
   * @return size of blitted image. note this method always returns the same
   *         array instance
   * @see FrameBuffer#blit(Texture, int, int, int, int, int, int, boolean)
   *      FrameBuffer.blit(..)
   */
  public Dimension blit(FrameBuffer buffer, int imageId, int destX, int destY, boolean transparent) {
    if (texture == null)
      throw new IllegalStateException("not packed yet");

    Entry entry = entries.get(imageId);
    buffer.blit(texture, entry.bounds.x, entry.bounds.y, destX, destY,
        entry.bounds.width, entry.bounds.height, transparent);

    blittedSize.setSize(entry.bounds.width, entry.bounds.height);
    return blittedSize;
  }
 
  private Dimension blit(FrameBuffer buffer, Entry entry, int destX, int destY,
      int destWidth, int destHeight, int transparency, boolean additive, Color color) {
   
    buffer.blit(texture, entry.bounds.x, entry.bounds.y, destX, destY, entry.bounds.width, entry.bounds.height,
        destWidth, destHeight, transparency, additive, color);

    blittedSize.setSize(entry.bounds.width, entry.bounds.height);
    return blittedSize;
  }
 
  private void checkPacked() {
    if (packed)
      throw new IllegalStateException("already packed");
  }

  private void nextSize(Dimension size) {
    if (size.width > size.height)
      size.height <<= 1;
    else
      size.width <<= 1;
  }

  /** recursively prints placement tree starting from given node */
  private void printTree(Node node, String prefix, int depth) {
    if (node == null)
      return;

    System.out.println(depth + ":\t" + prefix + "--" + node);
    printTree(node.child[0], "  |" + prefix, depth + 1);
    printTree(node.child[1], "  |" + prefix, depth + 1);
  }

  /**
   * returns the closest power of two that is equal or greater than given
   * number
   */
  private int closestTwoPower(int i) {
    int power = 1;
    while (power < i) {
      power <<= 1;
    }
    return power;
  }

  /** contents of a node if any */
  private static class Entry {
    private final Rectangle bounds = new Rectangle();
    private Image image;

    private Entry(Image image) {
      this.image = image;
    }

    @Override
    public String toString() {
      return "Entry: " + bounds;
    }
  }

  /** a node in our placement tree */
  private static class Node {
    private final Node[] child = new Node[2];
    private final Rectangle bounds = new Rectangle();
    private Entry entry;

    private Node() {
    }

    private Node(int width, int height) {
      bounds.setBounds(0, 0, width, height);
    }

    private boolean isLeaf() {
      return (child[0] == null) && (child[1] == null);
    }

    private Node insert(Entry entry) {
      if (isLeaf()) {
        // if there's already a image here, return
        if (this.entry != null)
          return null;

        int width = entry.image.getWidth(null);
        int height = entry.image.getHeight(null);

        // (if we're too small, return)
        if ((width > bounds.width) || (height > bounds.height))
          return null;

        // (if we're just right, accept)
        if ((width == bounds.width) && (height == bounds.height)) {
          this.entry = entry;
          this.entry.bounds.setBounds(this.bounds);
          return this;
        }

        // otherwise, split this node
        child[0] = new Node();
        child[1] = new Node();

        // (decide which way to split)
        int dw = bounds.width - width;
        int dh = bounds.height - height;

        if (dw > dh) { // split horizontally
          child[0].bounds.setBounds(bounds.x, bounds.y, width, bounds.height);
          child[1].bounds.setBounds(bounds.x + width, bounds.y, bounds.width - width, bounds.height);
        } else { // split vertically
          child[0].bounds.setBounds(bounds.x, bounds.y, bounds.width, height);
          child[1].bounds.setBounds(bounds.x, bounds.y + height, bounds.width, bounds.height - height);
        }
        // insert into first child we created
        return child[0].insert(entry);
      } else {
        // try inserting into first child
        Node newNode = child[0].insert(entry);
        if (newNode != null)
          return newNode;

        // no room, insert into second
        return child[1].insert(entry);
      }
    }

    @Override
    public String toString() {
      return bounds + ((entry == null) ? " <no entry>" : " " + entry.toString());
    }
  }
}
TOP

Related Classes of font.TexturePack$Entry

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.