Home » Sprite Organizer 0.1 Alpha

Share This Post

Applications / Desktop / Main Slider / Tools

Sprite Organizer 0.1 Alpha

Organizing Sprites, Tiles, Textures etc on Image Strips or Grids.

This article is about a tool for organizing sprite or tile images into image grids or image strips. It’s useful for organizing game sprites, simulation sprites, textures or even CSS sprites used on web pages.  It has buffers which contain images. Images can be broken into cols and rows.  Cells or sprites are defined as so many pixels by so many pixels in size.  Saves and loads images.

 

Sprite Organizer Screen Shot

50×50 Pixel Tile Collection Note the selector top left cell.

 

 Features

  • Load and save single images or sprite sets.
  • Load images into one of 10 buffers (more buffers in the next versions)
  • Paste from one buffer into a grid or image strip.
  •  Divide a blank image into cols or cols and rows.
  • Sprite size is determined by the size of the grid in pixels and number of cols and rows.
  • Copy, Paste, Move and Clear sprites on a single grid or strip.
  • Undo, Redo operation (future versions)
  • Copy, Paste, Move from one image grid buffer to another image grid buffer (future feature).
  • (Upcoming) Utility class for your apps that let you load images and grab sprites, tiles or textures from the image grid or strip.
  • (Upcoming) Sprites class that extends ImageBuffers class. Buffers will be sets (grids) of sprites. Will have some basic sprite library features.
  • (Upcoming) Maps class that extends ImageBuffers class. Buffers will be layers. i.e. Fog layer, roads and rivers layer, terrain layer, units and buildings layer etc.
  • (Upcoming) Map panel for displaying a map with all layers as a single grid.
  • And! My next article will be about a Pixel painter tool that you may use to edit game tiles, sprite images, textures with.  This will be used with the Sprite organizer tool so that you may click a sprite to edit it with a pixel editor.

The classes

SpriteOrganizer class Is the main Frame which is the entry point and sets it all up.
ImagePanel class is the viewing panel for viewing the images in the buffers and editing the image grids. This class contains StatusPanel class which displays information about the buffer in view at the bottom of the frame.
ImagePanelKeyListener class handles keyboard input.
ImagePanelMouseListener class handles mouse input.
ImageBuffers class This is the most important class, it contains an array of ImageBuffer so that you may copy and paste and move or delete sprites from images. It also contains the behavior to do these task. It contains methods for loading and saving images.
It also contains the ImageBuffer class which has a BufferedImage and metadata and state info about a buffer.

UML Diagram

Right Click and Open in new windows to enlarge.

Directions

Compile all classes and run SpriteOrganizer.java.  You will also need this icon in the same folder.

Space Invader Icon

TitleBar Icon

  • 1 to 9 and 0 select up to 10 buffers.  Buffers, however, are numbered 0-9  so 1 is buffer 0 when doing the keyboard paste operation or setting up the grid. The status bar may show 1 as buffer 1. But it’s 0.
  • Click a buffer and then hit G to form a grid. Give it the number of Columns and Rows.
  • O turns gridline overlay on or off.
  • C clears a grid cell given column and row. Columns and rows start at 0 not 1!.
  • M moves a grid cell given from column and from row and to column and to row.
  • P paste the current buffer into a cell on a grid in another buffer. Given destination buffer number, column, and row.
  • L loads an image into a buffer given a file name from the same folder that app was started from.
  • S saves current buffer in view given a filename to an image file in same folder app was started from.
  • N creates a new blank buffer with size in pixels.  So if you want to use 25-pixel by 25-pixel sprites on a strip 10 sprites long then 250 pixels x 25 pixels would be the new buffer size.
  • Left-Click on a cell selects it showing selection box.
  • Shift-Left-Click copies selected cell to another cell.
  • Right-Click clears a cell.
  • Shift-Right-Click moves selected cell to another cell in the same buffer.

How to use this app.

You may use any paint program to produce images that you intend to use in a game. MS Paint for example. Say you want to use 50×50 pixel sprites or tiles. Make up a series of sprites for say a stick man walking to the right. Let’s say he has 6 images per step.  Now put all 6 images in the same folder as this app name stickman1 to stickman6. start the app and load each one into buffer 1 to 6. Switch to buffer 7 or 0 or another buffer. but let’s say 0. Hit N and tell it 300 and 150. You will see a blank white image show up. Hit G to tell it the grid size 6 columns and 3 rows. This gives us 6 times 3 in space to put 50×50 sprites. So you could also add more of the stickman walking to the left later.

Go to each buffer one at a time and hit P  then 0 for destination buffer 0. Then 0 col and 0 row for stickman1 and. Repeat for other stickman images but use 1,0 2,0 3,0 4,0 5,0  to row them up on top. At this point, you do not need to move sprites around but try out the other editing features. Select a sprite with left click and then move it to cell 1,1 for example with shift-right-click.  Use the clear sprite with right-click.  Copy a sprite with shift-click.  Finally, while buffer 0 in view hit S to save the new image grid to a file. Load the new image in MS Paint to see what you created.

Later I intend to upgrade this app to include some utility classes to aid you in using the sprite image grids in your games or apps. Buf for now if you view this source you can extract the source you need to do the use the images. It’s all there. This source loads images and extracts a tile for the editing functions.  All your game needs to do is load the image file and crop out the image to use it paste it on the game screen where it’s needed.

The Code

SpriteOrganizer.java
// Author Larry Gray CPL Common Public License  Software Developer Zone
import javax.swing.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import java.awt.image.*;

class SpriteOrganizer extends JFrame {
 // debug methods
 public void log(String s){
  System.out.println(s);
 }
 //inner class
 public class StatsPanel extends JPanel{
  JLabel statusLabel = null;
  ImageBuffers buffers=null;
  // constructor
  public StatsPanel(ImageBuffers buffers){
   this.buffers=buffers;
   statusLabel = new JLabel();
   this.setBorder(new BevelBorder(BevelBorder.LOWERED));

   this.setPreferredSize(new Dimension(this.getWidth(), 16));
   this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
   statusLabel.setHorizontalAlignment(SwingConstants.LEFT);
   this.add(statusLabel);
  }
  // ui status methods
  public void updateStats(){		
   String status="Buff:"+(buffers.currentBuffer+1)+" BufW:"+buffers.width()+" BufH:"+buffers.height();
   if(buffers.cols()!=0){
    status+=" BufC:"+buffers.cols()+" BufR:"+buffers.rows();
    status+=" SprW:"+buffers.spriteWidth()+" SprH:"+buffers.spriteHeight();
   }
   statusLabel.setText(status);
  }

 }

 // fields
 StatsPanel statusPanel = null;
 ImageBuffers buffers = null;
 //constructors
 public SpriteOrganizer(){
  buffers = new ImageBuffers(10);
        statusPanel = new StatsPanel(buffers);
        ImagePanel imagePanel= new ImagePanel(buffers);
        imagePanel.setStatsPanel(statusPanel);
  // loads some images
  //buffers.loadImage("chess.png",0,this);
  //buffers.loadImage("colorlightning.jpg",1,this);
  //buffers.loadImage("dice.jpg",2,this);
  //buffers.loadImage("lens.jpg",3,this);
  //buffers.loadImage("mountain.jpg",4,this);
  //currentImage=buffers[0];
  // finish setup of ui
  this.setIconImage(Toolkit.getDefaultToolkit().getImage("sprite.png"));
  this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  this.setLayout(new BorderLayout());

  this.add(statusPanel, BorderLayout.SOUTH);
        this.setTitle("Sprite Organizer 0.1 Alpha");
  statusPanel.updateStats();
  this.add(imagePanel);
  this.setSize(500,500);
  this.setVisible(true);
 }
 //main bootstrap method
 public static void main(String args[]){
  new SpriteOrganizer();
 }

}

ImagePanel.java
// Author Larry Gray CPL Common Public License  Software Developer Zone
import javax.swing.*;
import java.awt.image.*;
import java.awt.*;
import java.awt.event.*;

public class ImagePanel extends JPanel {
 // fields
 ImageBuffers buffers = null;	
 public SpriteOrganizer.StatsPanel statsPanel=null;
 // setup methods
 public void setStatsPanel(SpriteOrganizer.StatsPanel statsPanel){
  this.statsPanel=statsPanel;
 }
 public boolean isFocusTraversable ( ) {
  return true ;
 }
 // painting methods
 public void paintComponent(Graphics g){
  g.fillRect(0,0,500,500);
  if(buffers.buffer()==null);
  else g.drawImage(buffers.buffer(), 0,0,null);
  //graphics=g;
  g.setColor(Color.black);
  if(buffers.grid()) drawGrid(g);
  if(buffers.select()){
   drawSelectedSquare(g, buffers.getSelectedCol(), buffers.getSelectedRow());
  }	
 }
 public void drawGrid(Graphics g){
  for(int c=0;c<buffers.cols();c++){
   for(int r=0;r<buffers.rows();r++){
    drawSquare(g,c,r);   
   }
  }
  
 }
 public void drawSquare(Graphics g, int col, int row){
  g.drawRect(col*buffers.spriteWidth(),row*buffers.spriteHeight(),
   buffers.spriteWidth(),buffers.spriteHeight());
 }
 public void drawSelectedSquare(Graphics g, int col, int row){
  g.setColor(Color.black);
  g.drawRect(col*buffers.spriteWidth()+2,row*buffers.spriteHeight()+2,
   buffers.spriteWidth()-4,buffers.spriteHeight()-4);
  g.setColor(Color.white);
  g.drawRect(col*buffers.spriteWidth()+2+1,row*buffers.spriteHeight()+2+1,
   buffers.spriteWidth()-4,buffers.spriteHeight()-4);	
 }
 // constructor
 public ImagePanel(ImageBuffers buffers){
  this.buffers = buffers;
  ImagePanel thisPanel = this;
  this.addKeyListener(new ImagePanelKeyListener(buffers, this));
     this.addMouseListener(new ImagePanelMouseListener(buffers, this));
 }
 //input methods
    public int inputX(){
  String sizeX = JOptionPane.showInputDialog("Size X in Pixels?");
  return Integer.parseInt(sizeX);
 }
 public int inputY(){
  String sizeY = JOptionPane.showInputDialog("Size Y in Pixels?");
  return Integer.parseInt(sizeY);
 }
 public int inputCols(){
  String sizeCols = JOptionPane.showInputDialog("Cols?");
  return Integer.parseInt(sizeCols);
 }
 public int inputRows(){
  String sizeRows = JOptionPane.showInputDialog("Rows?");
  return Integer.parseInt(sizeRows);
 }
 public int inputBuffer(){
  String bufferNum = JOptionPane.showInputDialog("Buffer#?");
  return Integer.parseInt(bufferNum);
 }
 public int inputCol(){
  String sizeCols = JOptionPane.showInputDialog("Col?");
  return Integer.parseInt(sizeCols);
 }
 public int inputRow(){
  String sizeRows = JOptionPane.showInputDialog("Row?");
  return Integer.parseInt(sizeRows);
 }
}
ImagePanelKeyListener.java
// Author Larry Gray CPL Common Public License  Software Developer Zone
import java.awt.event.*;
import javax.swing.*;
public class ImagePanelKeyListener extends KeyAdapter{
    // fields
 ImageBuffers buffers=null;
 ImagePanel imagePanel=null;
 //constructor
 public ImagePanelKeyListener(ImageBuffers buffers, ImagePanel imagePanel){
  this.buffers=buffers;
  this.imagePanel=imagePanel;
 }
 //listener utility methods
 void changeBuffer(int buffer){
  buffers.setBuffer(buffer);
  imagePanel.repaint();
  imagePanel.statsPanel.updateStats();
 }
 void clearSprite(int c, int r){
  buffers.clearSprite(c,r);
  imagePanel.repaint();
 }
 void moveSprite(int fc, int fr, int tc, int tr ){
  buffers.moveSprite(fc,fr,tc,tr);
  imagePanel.repaint();
 }
 void loadImage(){
  String fileName = JOptionPane.showInputDialog("Enter File Name");
  if (fileName == null) {
   System.out.println("The user canceled");
  } else {
   buffers.loadImage(fileName,imagePanel);
   imagePanel.repaint();
  }
 }
 void saveImage(){
  String fileName = JOptionPane.showInputDialog("Enter File Name");
  if (fileName == null) {
   System.out.println("The user canceled");
  } else {
   buffers.saveImage(fileName);
   imagePanel.repaint();
  }
 }
 void splitImage(){
  buffers.setCols(imagePanel.inputCols());
  buffers.setRows(imagePanel.inputRows());
  imagePanel.statsPanel.updateStats();
 }
 void newBuffer(int x, int y){
  buffers.newBuffer(x,y);
  imagePanel.statsPanel.updateStats();
  imagePanel.repaint();
 }
 void pasteToBuffer(int buffer, int col, int row){
  buffers.pasteToBuffer(buffer,col,row);
  imagePanel.repaint();
 }
 // main listener methods that use utility methods
 public void keyPressed (KeyEvent e){
 }
 public void keyTyped (KeyEvent e){
  char keyTyped=e.getKeyChar();
  switch (keyTyped){
  case '1': changeBuffer(0);
   break;
  case '2': changeBuffer(1);
   break;
  case '3': changeBuffer(2);
   break;
  case '4': changeBuffer(3);
   break;
  case '5': changeBuffer(4);
   break;
  case '6': changeBuffer(5);
   break;
  case '7': changeBuffer(6);
   break;
  case '8': changeBuffer(7);
   break;
  case '9': changeBuffer(8);
   break;
  case '0': changeBuffer(9);
   break;
  case 'l': loadImage();
   break;
  case 'g': splitImage();
   break;
  case 'c': clearSprite(imagePanel.inputCol(),imagePanel.inputRow());
   break;
  case 'm': moveSprite(imagePanel.inputCol(),imagePanel.inputRow(),imagePanel.inputCol(),imagePanel.inputRow());
   break;
  case 'n': newBuffer(imagePanel.inputX(),imagePanel.inputY());
   break;
  case 'p': pasteToBuffer(imagePanel.inputBuffer(),imagePanel.inputCol(),imagePanel.inputRow());
   break;
  case 's': saveImage();
   break;
  case 'o': buffers.flipGrid();
   imagePanel.repaint();
  default : break;
  }
 }
}
ImagePanelMouseListener.java
// Author Larry Gray CPL Common Public License  Software Developer Zone
import java.awt.event.*;
import javax.swing.*;

public class ImagePanelMouseListener extends MouseAdapter {
 // debug methods
 public void log(String s){
  System.out.println(s);
 }
 //fields
 ImageBuffers buffers=null;
 ImagePanel imagePanel=null;
 // constructors
 public ImagePanelMouseListener(ImageBuffers buffers, ImagePanel imagePanel){
  this.buffers=buffers;
  this.imagePanel=imagePanel;
 }
 // adapter methods
 public void mouseClicked(MouseEvent me){
  int button = me.getButton();
  boolean leftClick=false;
  boolean rightClick=false;
  boolean shift=false;
  boolean alt=false;
  boolean control=false;
  if(button==me.BUTTON1) leftClick=true;
  //if(button==me.BUTTON2) log("Wheel Click");
  if(button==me.BUTTON3) rightClick=true;
  if( me.isAltDown()) alt=true;     
  if( me.isControlDown()) control=true;
  if( me.isShiftDown()) shift=true;
  log("shift:"+shift);
  log("leftClick:"+leftClick);
  int x = me.getX();
  int y = me.getY();
  int col = x/buffers.spriteWidth();
  int row = y/buffers.spriteHeight();
  int selectedCol=buffers.getSelectedCol();
  int selectedRow=buffers.getSelectedRow();
  boolean selected=buffers.select();
  // click handling logic
  if(selected){
   if(leftClick){
  	 if(shift){
  	  buffers.copySprite(col,row);	
     }else {
      buffers.setSelectedColRow(col,row); 
     }
   }else if(rightClick){
    if(shift){
     buffers.moveSprite(col,row);   
    }else{
   	 buffers.clearSprite(col,row);
    }  
   }
  } else {
  	  buffers.setSelectedColRow(col,row); 
  	  buffers.setSelect(true);
  }
  imagePanel.repaint();
 }
}
ImageBuffers.java
// Author Larry Gray CPL Common Public License  Software Developer Zone
import java.awt.image.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import javax.imageio.*;
import javax.imageio.stream.*;

public class ImageBuffers {
 // debug methods
 public void log(String s){
  System.out.println(s);
 }
 // inner classes
 public class ImageBuffer{
  public BufferedImage buffer = null;
  public int cols = 0;
  public int rows = 0;
  public boolean grid = false;
  public int selectedCol = 0;
  public int selectedRow = 0;
  public boolean select = false;
  public void setSelectedColRow(int col, int row){
   this.selectedCol=col;
   this.selectedRow=row;
  }
 }
 public class Tile {
  private String name = "";
  private Image tile = null;
  public Tile(String name, Image anImage){
   this.name=name;
   this.tile=anImage;
  }
  public Image getTile(){
   return tile;
  }
  public String getName(){
   return name;  
  }
 }
 // constructors
 public ImageBuffers(int numBuffers){
  buffers = new ImageBuffer[numBuffers];
  for(int i=0;i<numBuffers;i++){
   buffers[i]=new ImageBuffer();	
  }
 }
 // fields
 public ImageBuffer[] buffers = null;
 public int currentBuffer=0;
 // methods
 public void setSelectedColRow(int col, int row){
  buffers[currentBuffer].setSelectedColRow(col,row);	 
 }
 public int getSelectedCol(){
  return buffers[currentBuffer].selectedCol;	 
 }
 public int getSelectedRow(){
  return buffers[currentBuffer].selectedRow;	 
 }
 public boolean select(){
  return buffers[currentBuffer].select;	 
 }
 public void setSelect(boolean select){
  buffers[currentBuffer].select=select;	 
 }
 public void gridOn(){
  buffers[currentBuffer].grid=true;	 
 }
 public void gridOff(){
  buffers[currentBuffer].grid=false;	 
 }
 public void flipGrid(){
  if(grid())gridOff();
  else gridOn();
 }
 public boolean grid(){
  return buffers[currentBuffer].grid;
 }
 public void setBuffer(BufferedImage image){
  buffers[currentBuffer].buffer=image;	 
 }
 public void setBuffer(int buffer){
  currentBuffer=buffer;	 
 }
 public void incBuffer(){
  currentBuffer++;
 }
 public void decBuffer(){
  currentBuffer--;	 
 }
 public BufferedImage buffer(){
  return buffers[currentBuffer].buffer;	 
 }
 public BufferedImage buffer(int buffer){
  return buffers[buffer].buffer;	 
 }
 public void setCols(int cols){
  buffers[currentBuffer].cols=cols;	 
 }
 public void setRows(int rows){
  buffers[currentBuffer].rows=rows;	 
 }
 public int cols(){
  return buffers[currentBuffer].cols;	 
 }
 public int cols(int buffer){
  return buffers[buffer].cols;	 
 }
 public int rows(){
  return buffers[currentBuffer].rows;	 
 }
 public int rows(int buffer){
  return buffers[buffer].rows;	 
 }
 public int width(){
  if (buffers[currentBuffer].buffer==null) return 0;
  else return buffers[currentBuffer].buffer.getWidth();	 
 }
 public int width(int buffer){
  if (buffers[buffer].buffer==null) return 0;
  else return buffers[buffer].buffer.getWidth();	 
 }
 public int height(){
  if (buffers[currentBuffer].buffer==null) return 0;
  else return buffers[currentBuffer].buffer.getHeight();	 
 }
 public int height(int buffer){
  if (buffers[buffer].buffer==null) return 0;
  else return buffers[buffer].buffer.getHeight();	 
 }
 public int spriteWidth(){
  return width()/buffers[currentBuffer].cols;	 
 }
 public int spriteWidth(int buffer){
  return width(buffer)/buffers[buffer].cols;	 
 }
 public int spriteHeight(){
  return height()/buffers[currentBuffer].rows;	 
 }
 public int spriteHeight(int buffer){
  return height(buffer)/buffers[buffer].rows;	 
 }
 // image loader saver
 public void loadImage(String anImageFileName, int bufferNumber, Component component){
  currentBuffer=bufferNumber;
  loadImage(anImageFileName, component);
 }
 public void loadImage(String anImageFileName, Component component) {
  File anImageFile = new File(anImageFileName);
  Image image=null;
  try {
   MediaTracker mt = new MediaTracker(component);
   image = Toolkit.getDefaultToolkit().getImage(
    anImageFileName);
   mt.addImage(image, 0);
   mt.waitForAll();
  } catch (Exception e) {
   e.printStackTrace(System.err);
  } // catch
  buffers[currentBuffer].buffer = getBufferedImage(image);
 } // loadImg
 /**
 * Saves an image to a png file.
 * 
 */
 protected void saveImage(String anImageFileName) {
  File anImageFile = new File(anImageFileName);
   Iterator it = ImageIO.getImageWritersBySuffix("png");
   try {
    ImageWriter iw = (ImageWriter) it.next();
    iw.setOutput(new FileImageOutputStream(anImageFile));
    iw.write((RenderedImage) buffer());
   } catch (Exception e) {
    e.printStackTrace(System.err);
   } // catch
 } // saveImg
 // editing methods
 public void newBuffer(int x, int y){
  BufferedImage bimage = new BufferedImage(x, y, BufferedImage.TYPE_INT_ARGB);
  Graphics g = bimage.getGraphics();
  g.setColor(Color.white);
  g.fillRect(0,0,x,y);
  setBuffer(bimage);
 }	 
 public BufferedImage getBufferedImage(Image image){
  BufferedImage bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
  Graphics2D bGr = bimage.createGraphics();
  bGr.drawImage(image, 0, 0, null);
  bGr.dispose();
  return bimage;
 }
 public void clearSprite(int c, int r){
  BufferedImage bi=buffer();
  Graphics g = bi.getGraphics();
  int bufc=cols();
  int bufr=rows();
  int sprw=spriteWidth();
  int sprh=spriteHeight();
  g.fillRect(c*sprw,r*sprh,sprw,sprh);
 }
 public void moveSprite(int tc,int tr){
  moveSprite(getSelectedCol(),getSelectedRow(),tc,tr);	 
 }
 public void moveSprite(int fc, int fr, int tc, int tr ){
  int sprw=spriteWidth();
  int sprh=spriteHeight();
  BufferedImage bi=buffer();
  BufferedImage stampImage= bi.getSubimage(fc*sprw,fr*sprh,sprw,sprh);
  Graphics g = bi.getGraphics();
  g.drawImage(stampImage,tc*sprw,tr*sprh,null);
  g.fillRect(fc*sprw,fr*sprh,sprw,sprh);
 }
 public void copySprite(int tc, int tr ){
  int fc=getSelectedCol();
  int fr=getSelectedRow();
  int sprw=spriteWidth();
  int sprh=spriteHeight();
  BufferedImage bi=buffer();
  BufferedImage stampImage= bi.getSubimage(fc*sprw,fr*sprh,sprw,sprh);
  Graphics g = bi.getGraphics();
  g.drawImage(stampImage,tc*sprw,tr*sprh,null);
 }
 public void pasteToBuffer(int buffer, int col, int row){
  int sprw=spriteWidth(buffer);
  int sprh=spriteHeight(buffer);
  BufferedImage bi=buffer();
  BufferedImage stampImage= bi.getSubimage(0,0,sprw,sprh);
  BufferedImage destBuffer=buffer(buffer);
  Graphics g = destBuffer.getGraphics();
  g.drawImage(stampImage,col*sprw,row*sprh,null);
 }
}

 

 

Share This Post

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>