Advice on a faster way to check every pixel in an image.

Look at the Image source, linked above.

	public Color getColor(int x, int y) {
		if (pixelData == null) {
			pixelData = texture.getTextureData();
		}

So getTextureData() is definitely the way to go.

Ah, indeed. So like this then:

public byte[] getMask(Image img) {
    int stride = 4; // number of channels
    byte[] data = img.getTexture().getTextureData();
    for(int i = 1; i < data.length; i += stride) { // i = 0 if RGBA, i = 1 if ARGB
        data[i] = (data[i] & 0xFF) + 1 >> 8; // fixed this, now actually returns 1 only if data[i] = 255
    }
    return data;
}

b = byte (method or array), then (b+1)>>8 yields 1 for 0xff and 0 otherwise. Only 0xff will overflow into the ninth bit. Likewise for 32-bit array accesses. d = ((r & 0xFF0000) + 0x10000) >> 24 if ‘alpha’ can be any value…the and can be removed if A is insured to be zero. If A is 0xff, the simply change the additive constant.

Need to either & 0xFF or make sure that the byte is expanded to an int (EDIT: that is what’s killing it), doing (b+1)>>8 on a raw byte does not yield that behavior, oddly enough. Tripped me up earlier.

Sorry to double post, but I realized my implementation wasn’t collapsing the array. (it would work, but you’d need other code that uses the mask to stride through it in the same manner, this one follows principal of least astonishment while still being pretty fast ;))


//assumes ARGB
public byte[] getMask(Image img) {
    int stride = 4;
    byte[] data = img.getTexture().getTextureData();
    byte[] mask = new byte[img.getWidth() * img.getHeight()];
    for(int i = 0; i < mask.length;) {
        mask[i++] = (data[i * stride] & 0xFF) + 1 >> 8;
    }
    return mask;
}

Try that out and tell me how it goes. Index mask as usual with mask[(x + y * w)]

Thanks for all the help. You guys gave me great insight into how byte arrays work, I never could figure them out in the past. The whole concept of holding X and Y values (and the pixel’s color too!) in a single array is just mind bogglingly confusing to me.

Sadly though, I realized the major issue isnt coming from getColor, it’s coming from where getColor has to get it’s data. I was pulling the source image from a very large spritesheet and the problem is when .getColor (or any of your methods) are used it has to grab that value off a huge 1024x512 spritesheet, and there in lies the slowdown. I wasn’t realizing that even though I am only looking at a 32x32 image I pulled from the sheet, when it has to calculate color values it has to load up the entire sheet into memory. Thus, the major slowdown. Problem with that is, even your guy’s methods of snagging the byte array still has similar issues because of that. So your methods are a lot faster, but not fast enough!

In my tests with my current code, if I use a single 32x32 image I can run the code literally about 200 times faster. It executes using my old slow code countless times faster than even your guy’s versions. But, that’s not acceptable, I still need the source information to exist on the sprite sheet.

So, I’ve have a possible idea that may actually be even faster than the byte array examples you’ve been showing me. Basically it involves creating the data only once, based off the image (sort of a “first time load” deal) and saving it as a string of 0s and 1s in a .properties file. Then, I can build the array in-game off a 1024-digit long (height*width) properties key and skipping the need to even look at the image anymore.

Once that’s done, I can add a check on game load to just doublecheck all the last modified dates on the images I’m working with, if they’ve changed I can rebuild the data in the properties files, if they haven’t, just use the existing binary keys.

The final product should mean that the images are only touched once, ever. Unless I update the image, then the program will detect the change and regenerate the files specific to that one sprite sheet.

But even if my idea works out, I’ll probably still use the help you’ve given me and use your methods to do the initial build, because it’s still faster than mine. :smiley:

Are they all tiles of the same size, and right up against each other on the sheet?


class ImageMaskFactory { //or something
    private byte[] sheetData;
    private int w, tileSize;

    public ImageMaskFactory(Image spriteSheet, int tileSize) { //tileSize is in pixels
        this.tileSize = tileSize;
        w = spriteSheet.getWidth();
        sheetData = spriteSheet.getTexture().getTextureData();
    }

    public byte[] getMask(int xSheetLocation, int ySheetLocation) {
        int stride = 4;
        int offset = xSheetLocation * tileSize + ySheetLocation * w * tileSize;
        byte[] mask = new byte[tileSize * tileSize];
        for(int i = 0; i < mask.length;) {
            mask[i++] = (sheetData[(offset + i % tileSize + i / tileSize * w) * stride] & 0xFF) + 1 >> 8; // I believe this is correct
        }
        return mask; //returns tileSize x tileSize mask cut out from the sheet buffer. Nifty!
    }

}

And if getMask() is called frequently with the same tile coords, you could set up a mask cache as well, trading memory for more speed.

Just make sure, you load the image only once at startup and always use the same instance and you should be fine…

I don’t know why nobody ever thought of this yet, but why not multi-thread?

You could (with java 8) use a parallel stream + map() :slight_smile:

Doubt this would benefit much as it’s a memory-bound operation, passing the buffers around from thread to thread is likely to only exacerbate the problem. That’s how it works in my head anyway. As always, needs a benchmark to really be able know anything.

I think the Arrays would then be loaded into the CPU’s cache, so it might be faster in the end.
Also, why should they need to pass buffers around?

I load them only once on-demand, basically when the class needs an image, it searches to see if that sheet is loaded yet, if it isnt, it loads it. If it is, it grabs it and uses it. :stuck_out_tongue:

The problem isn’t so much loading the sheet as it is using .getColor() 1024 times on a 32x32 sprite that’s located on a 1024x512 sheet. Basically I’m not working with a 32x32 image, I’m working with a 1024x512 one and java really doesn’t like that. :confused:

My new solution has eliminated the problem though, I now have it loading pixel perfect maps for over 200-300 sprites in under 2 seconds. :smiley: Basically it looks to see if a properties file exists with the data, if it does it just fetches a binary string and uses that to build the map. If it doesn’t it’ll rebuild a new map using my old method. (<-- More a fail-safe, because once they file is generated there’s no reason to make a new one) But that only happens one time ever unless you delete the file or the program detects the date modified on the sprite sheet changes, suggesting a new .properties file should be generated. So from the player’s perspective, these one-time generations should in theory never have to happen, because the game will be distributed with those properties files already generated anyway unless they start making mods for my game and adding new sprite sheets.

Yeah, I attempted to multithread it a while back and ran into a problem; LWJGL’s Graphics can only be ran on one thread at a time, and .getColor needs Graphics to function. So the most critical laggy step was the one I couldnt move to the thread. :confused:

After looking at some of your ideas (Like getting the byte[] array and uses that instead) I could pass it to it’s own thread now though, but really at this point there’s not much reason to with my newer system in place. :smiley:

good if it works for you now, but you for sure made a mistake somewhere, because a 1024x512 pixel image is peanuts… Also loading one bigger file is waaaaaaay faster than 300 small properties files - creating pixel-perfect maps for 512 sprites from a single sheet should be doable in millis, not in seconds…

Not when you’re loading 10~ of these sheets (depending on the spritesheet it needs to grab) about 300~ times and include the time you need to iterate through an entire 12 layer 64x64 TiledMap (aka 49152 tiles to check) looking for the ones you need to load in the first place. :smiley:

Quick question:

For an image where the width and height are both less than 256, is using bytes faster than using ints when reading in pixel data? (Assuming that reading speeds are NOT negligible, and we’re probably going to optimize until we reach assembly instructions.)

(Bytes reading per byte) versus (Integers reading 4 bytes, and then bitmasking the values for specific byte values)?

Bytes are going to be faster because Slick (OpenGL) gives us the data prepackaged as a byte array.
Talking Java2D, BufferedImage.TYPE_4BYTE_ABGR, or 3_BYTE_BGR (using same method that I posted above, plus standard DataBuffer routine) is still faster than bitmasking an int[], I have tested it.
It’s not much faster, but it makes sense, as even though you are trading potentially more cache misses (more array lookups), you don’t do all the bitmasking and shifting, and the cache miss rate doesn’t really increase in practice as you are reading a continuous block of memory.

Java per se has no problem with handling 0.5 megapixel images.

Lets take a step back here. You’re loading images with LWJGL and then extracting their contents to process them. I’m going to guess two things: firstly, that these images serve only for collision testing, and secondly that LWJGL loads them into texture memory on the graphics card.

If I’m right then the solution is to skip the graphics card. If you load the images using javax.imageio.ImageIO then you get java.awt.image.BufferedImage instances and you can call getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) to extract your sprite from the image.

For completeness, there may also be a way in LWJGL to create a new 32x32 texture, paint the appropriate segment of your sprite sheet onto it, and then fetch just that back from the graphics card; but I can’t see any good reason to do that.

You’re reloading the same image multiple times?

No, I’m reading 10 different sprite sheets 300 times.

Those are all made up numbers anyway, the real numbers are closer to 2 dozen sheets, and how many times is dependent on how many objects are on the map that need to be scanned.