0
\$\begingroup\$

I'm making my first real game, in Java, and I'm currently working on the base system, for rendering images (sprites) and I wonder how I can rotate sprites.


My sprite class looks like this:

package com.game.graphical;

import com.game.location.Vector2i;

/**
 * A simple Sprite with a Texture.
 * 
 * @author TheDDestroyer12
 */
public class Sprite {

    // The Sprite's Texture
    private Texture texture;

    // The Sprite's location
    private Vector2i location;

    /**
     * Create a new Sprite.
     * 
     * @param texture The texture for the Sprite.
     * @param location The location of the Sprite.
     */
    public Sprite(Texture texture, Vector2i location) {
        // Set the Texture
        this.texture = texture;
    }

    /**
     * Move the Sprite a number of pixels in any direction, specified by the Vector2i.
     * 
     * @param change The amount of pixels to move the Sprite.
     */
    public void move(Vector2i change) {
        // Add the change to the Sprite's location
        location.add(change);
    }

    public void rotate() {
        // TODO ROTATE THE SPRITE
    }

    /**
     * Get the Sprite's Texture.
     * 
     * @return The Texture.
     */
    public Texture getTexture() {
        // Return the Texture
        return texture;
    }

    /**
     * Get the Sprite's location.
     * 
     * @return The Sprite's location.
     */
    public Vector2i getLocation() {
        // Return the location variable
        return location;
    }
}

and the class called Texture, looks like this:

package com.tdd12.darkness.graphical;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Texture {

    // The BufferedImage
    private int[] pixels;

    // The size of the Texture
    private int width, height;

    /**
     * Create a new Texture.
     */
    private Texture(BufferedImage image) {
        // Set the pixels array
        pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
        // Set the size
        width = image.getWidth();
        height = image.getHeight();
    }

    /**
     * Load a Texture from an image file.
     * 
     * @param path The path to load the image from.
     * 
     * @return The loaded Texture.
     */
    public Texture loadTexture(String path) {
        // Create a new Texture object
        Texture texture = null;
        try {
            // Load a BufferedImage from the file at the path
            BufferedImage image = ImageIO.read(new File(path));
            // Create a new Texture of the BufferedImage
            texture = new Texture(image);
        } catch(IOException e) {
            // Print an error description
            System.err.println("Texture loading failed!");
            // Print the error message
            e.printStackTrace();
        }
        // Return the Texture
        return texture;
    }

    /**
     * Get the pixels of the Texture.
     * 
     * @return The pixels as an integer array.
     */
    public int[] getPixels() {
        // Return the pixel array
        return pixels;
    }

    /**
     * Get the width of the Texture, measured in pixels.
     * 
     * @return The width of the Texture.
     */
    public int getWidth() {
        // Return the width
        return width;
    }

    /**
     * Get the height of the Texture, measured in pixels.
     * 
     * @return The height of the Texture.
     */
    public int getHeight() {
        // Return the height
        return height;
    }
}

The rendering works like this:

public void drawTexture(Texture texture, Vector2i location) {
    // Get the sprite's texture's pixels
    int[] texturePixels = texture.getPixels();
    // Store the current texture coordinates
    int tx = 0, ty = 0;
    // Store the starting location
    int x = location.x, y = location.y;
    // If the X coordinate is less than 0
    if(location.x < 0) {
        // Set it to 0
        x = 0;
    }
    // If the Y coordinate is less than 0
    if(location.y < 0) {
        // Set it to 0
        y = 0;
    }
    // Scroll through each pixel on the screen vertically (begin at the Y coordinate specified by the 'location')
    for(int xx = x; (xx < getWidth() && xx < x + texture.getWidth()); xx++) {
        // Scroll through each pixel on the screen horizontally (begin at the X coordinate specified by the 'location')
        for(int yy = y; (yy < getHeight() && yy < y + texture.getHeight()); yy++) {
            // Set each pixel to the color of the corresponding pixel on the Texture
            pixels[xx + yy * getWidth()] = texturePixels[tx + ty * texture.getWidth()];
            // Add one to the texture X coordinate
            ty++;
        }
        // Reset the ty variable
        ty = 0;
        // Add one to the texture X coordinate
        tx++;
    }
}

The array called pixels is the array of pixels from a BufferedImage, which is got by using int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(). The drawTexture method isn't completely optimized. I'm working on it, but that's the base. The process looks like this:

- Texture loading:

  • A new Texture object is created using Texture.loadTextrue(pathToTexture).
  • A BufferedImage is loaded from the file at the path specified by pathToTexture.
  • In the constructor for Texture, the pixels is loaded from the BufferedImage into an array.

- Rendering:

  • With some pretty simple loops, the color of each pixel from the Texture is applied to the corresponding spot in a larger BufferedImage's pixel array.
  • The larger BufferedImage is then being drawn to a Canvas, which is contained in a JFrame, using Canvas.getBufferStrategy().getDrawGraphics().drawImage(img, x, y, width, height, null).

The way I'm thinking of doing it

is by creating a larger array, and moving the pixels, relative to the center point. I'll try to illustrate it:

Before rotation:

enter image description here

After rotation (not correct size. Just made it to illustrate that the array size has grown):

enter image description here

I'm not using any engines. Just pure Java.


Preferbably, I would like to keep this method, and not use Graphics2D and AffineTransform, but in case that's the only solution, well, go on.

// TheDDestroyer12

\$\endgroup\$
  • 1
    \$\begingroup\$ Why would you not use Graphics2D and AffineTransform? That would perform a lot better than is possible with any custom solution. \$\endgroup\$ – msell Dec 21 '14 at 15:59
  • \$\begingroup\$ Because I want to do it with as little pre-created functions and tools as possible. I don't really know why that is, but I think it's because I like to solve problems, and because I want to experiment with different "performance alternatives". \$\endgroup\$ – Daniel Kvist Dec 21 '14 at 16:40
1
\$\begingroup\$

A rotation is essentially that for each sample in the resulting grid, find out which texels in your original texture that your filter footprint covers and interpolate them accordingly. This will be a lossy process, and if done repeatedly will quickly obliterate all interesting information in the image.

If you only look at the nearest texel, you'll get a box filter or nearest neighbour. If you look at the four closest and weigh them by distance, you get a bilinear filter. There's lots of fancy filters out there, with different tradeoffs and costs.

I recommend reading Alvy Ray Smith's memo "A pixel is not a little square", it covers a lot of fundamentals in raster images.

As a side note, a decent resource on all sorts of old-school image manipulation is the old flipcode.com archives.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.