I do a lot of game programming in my free time, and am currently working on a game engine library. Previous to this point I have made customized per game engines built straight into the application, however, to challenge my logical skills even further, I decided I wanted to make an engine that I could literally use with any game that I write, kind of like a plugin.
Before this point, I have been pulling textures in using a BufferedImage using getRBG to pull the pixel[] out and by hand writing over the background int[] with the texture int[] array in the (X,Y) position that the Renderable resided. Then when everything was written to the master int[] I would make a new BufferedImage and use setRGB using the master int[] and use a BufferStrategy and it's Graphic to drawImage of the BufferedImage. I liked this method because I felt like i had complete control over the way things were rendered, but I don't think it was very efficient.
Here is a look at the way I used to write to the master int[]
public void draw(Render render, int x, int y, float alphaMul){
for(int i=0; i<render.Width; i++){
int xPix = i + x;
if(Width - 1<xPix || xPix<0)continue;
for(int j=0; j<render.Height; j++){
int yPix = j + y;
if(Height - 1<yPix || yPix<0)continue;
int srcARGB = render.Pixels[i + j * render.Width];
int dstARGB = Pixels[xPix + yPix * Width];
int srcAlpha = (int)((0xFF & (srcARGB >> 24))*alphaMul);
int srcRed = 0xFF & (srcARGB >> 16);
int srcGreen = 0xFF & (srcARGB >> 8);
int srcBlue = 0xFF & (srcARGB);
int dstAlpha = 0xFF & (dstARGB >> 24);
int dstRed = 0xFF & (dstARGB >> 16);
int dstGreen = 0xFF & (dstARGB >> 8);
int dstBlue = 0xFF & (dstARGB);
float srcAlphaF = srcAlpha/255.0f;
float dstAlphaF = dstAlpha/255.0f;
int outAlpha = (int)((srcAlphaF + (dstAlphaF)*(1 - (srcAlphaF)))*255);
int outRed = (int)(srcRed*srcAlphaF) + (int)(dstRed * (1 - srcAlphaF));
int outGreen = (int)(srcGreen*srcAlphaF) + (int)(dstGreen * (1 - srcAlphaF));
int outBlue = (int)(srcBlue*srcAlphaF) + (int)(dstBlue * (1 - srcAlphaF));
int outARGB = (outAlpha<<24)|(outRed << 16) | (outGreen << 8) | (outBlue);
Pixels[xPix + yPix * Width] = outARGB;
}
}
}
I have recently found out it may be multitudes faster, where using drawImage I can loop through all of the Renderables and draw them as BufferedImages using their respective (X,Y) positions. But, I do not know how to work alphaBlending with that. So my questions are, how would I go about getting the results that I want?, and Would it be resource and time beneficial over my previous method?
Thanks
-Craig
Related
I am trying to render an image where I can manipulate all the pixels of the image, it works but I get 40 fps.
While with graphics.fillrect I get 4000 fps and it's really slow and I need this to make a 3D game but it's very very slow.
public class Renderer {
private BufferedImage image = TextureLoader.load("./res/image.png");
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
public void render(Graphics2D g) {
for(int x = 0; x < 1920; x++) {
for(int y = 0; y < 1080; y++) {
setRGB(x,y, Color.RED.getRGB());
}
}
g.drawImage(image, 0, 0, 1920, 1080, null);
}
public void setRGB(int x, int y, int rgb) {
int a = (y * 1920 + x) * 3;
pixels[a] = (byte) ((rgb >> 0) & 0xFF);
pixels[a + 1] = (byte) ((rgb >> 8) & 0xFF);
pixels[a + 2] = (byte) ((rgb >> 16) & 0xFF);
}
}
In general, setting every single pixel in a loop is a time consuming process. You could try to speed up your code by using various methods of WritableRaster but personally, I would use a 3d-library like LWJGL or even a 3d-game engine.
Generally it's not a good idea to use Java's provided libraries when one wants to create a 3D game. They are very limited and have poor performance. I would advise looking at third-party libraries such as LWJGL. To replace the Java graphics libraries, use OpenGL and GLFW.
There are many tutorials on OpenGL which will help you learn, such as ThinMatrix's tutorials.
If you don't want to do all the work yourself, try a game engine or framework such as libGDX or jmonkeyengine, which are already optimized and provide an easy way to make your 3D game.
I'm currently having an issue with alpha channels when reading PNG files with ImageIO.read(...)
fileInputStream = new FileInputStream(path);
BufferedImage image = ImageIO.read(fileInputStream);
//Just copying data into an integer array
int[] pixels = new int[image.getWidth() * image.getHeight()];
image.getRGB(0, 0, width, height, pixels, 0, width);
However, when trying to read values from the pixel array by bit shifting as seen below, the alpha channel is always returning -1
int a = (pixels[i] & 0xff000000) >> 24;
int r = (pixels[i] & 0xff0000) >> 16;
int g = (pixels[i] & 0xff00) >> 8;
int b = (pixels[i] & 0xff);
//a = -1, the other channels are fine
By Googling the problem I understand that the BufferedImage type needs to be defined as below to allow for the alpha channel to work:
BufferedImage image = new BufferedImage(width, height BufferedImage.TYPE_INT_ARGB);
But ImageIO.read(...) returns a BufferedImage without giving the option to specify the image type. So how can I do this?
Any help is much appreciated.
Thanks in advance
I think, your "int unpacking" code might be wrong.
I used (pixel >> 24) & 0xff (where pixel is the rgba value of a specific pixel) and it worked fine.
I compared this with the results of java.awt.Color and they worked fine.
I "stole" the "extraction" code directly from java.awt.Color, this is, yet another reason, I tend not to perform these operations this way, it's to easy to screw them up
And my awesome test code...
BufferedImage image = ImageIO.read(new File("BYO image"));
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = image.getRGB(x, y);
//value = 0xff000000 | rgba;
int a = (pixel >> 24) & 0xff;
Color color = new Color(pixel, true);
System.out.println(x + "x" + y + " = " + color.getAlpha() + "; " + a);
}
}
nb: Before some one tells that this is inefficient, I wasn't going for efficiency, I was going for quick to write
You may also want to have a look at How to convert get.rgb(x,y) integer pixel to Color(r,g,b,a) in Java?, which I also used to validate my results
I think the problem is that you're using arithmetic shift (>>) instead of logical shift (>>>). Thus 0xff000000 >> 24 becomes 0xffffffff (i.e. -1)
I have a requirement to use a native library which reads some proprietary image file formats (something about not reinventing our own wheel). The library works fine, it's just sometimes the images can get pretty big (the record I've seen being 13k x 15k pixels). The problem is my poor JVM keeps dying a painful death and / or throwing OutOfMemoryError's any time the images start getting huge.
Here's what I have running
//the bands, width, and height fields are set in the native code
//And the rawBytes array is also populated in the native code.
public BufferedImage getImage(){
int type = bands == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_INT_BRG;
BufferedImage bi = new BufferedImage(width, height, type);
ImageFilter filter = new RGBImageFilter(){
#Override
public int filterRGB(int x, int y, int rgb){
int r, g, b;
if (bands == 3) {
r = (((int) rawBytes[y * (width / bands) * 3 + x * 3 + 2]) & 0xFF) << 16;
g = (((int) rawBytes[y * (width / bands) * 3 + x * 3 + 1]) & 0xFF) << 8;
b = (((int) rawBytes[y * (width / bands) * 3 + x * 3 + 0]) & 0xFF);
} else {
b = (((int) rawBytes[y * width + x]) & 0xFF);
g = b << 8;
r = b << 16;
}
return 0xFF000000 | r | g | b;
}
};
//this is the problematic block
ImageProducer ip = new FilteredImageSource(bi.getSource(), filter);
Image i = Toolkit.getDefaultToolkit().createImage(ip);
Graphics g = bi.createGraphics();
//with this next line being where the error tends to occur.
g.drawImage(i, 0, 0, null);
return bi;
}
This snippet works great for most images so long as they're not obscenely large. Its speed is also just fine. The problem is that that Image drawing onto the BufferedImage step swallows way too much memory.
Is there a way I could skip that step and go directly from raw bytes to buffered image?
Use the RawImageInputStream from jai. This does require knowing information about the SampleModel, which you appear to have from the native code.
Another option would be to put your rawBytes into a DataBuffer, then create a WritableRaster, and finally create a BufferedImage.
This is essentially what the RawImageInputStream would do.
For some reason, I can change a buffered image by using setRGB but not by using the actual int array in the raster:
This works
BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++) {
int gray = (int) (MathUtil.noise(x, y) * 255); //I have tested the noise function, and know it works fine
img.setRGB(x, y, gray << 16 | gray << 8 | gray);
}
}
This does not
BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_RGB);
int[] data = ((DataBufferInt) img.getData().getDataBuffer()).getData();
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++) {
int gray = (int) (MathUtil.noise(x, y) * 255); //I have tested the noise function, and know it works fine
data[x + y * 32] = gray << 16 | gray << 8 | gray;
}
}
Noise function:
public static float noise(int x, int y) {
int n = x + y * 57;
n = (n << 13) ^ n;
return Math.abs((1.0f - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f));
}
EDIT
Nevermind I fixed it. I needed to use getRaster :P
Because when you call BufferedImage.getData() it is returning a copy, not the actual backing array. So any changes you make directly to that array will not be reflected in the image.
From the JavaDoc for BufferedImage.getData():
Returns:
a Raster that is a copy of the image data.
Edit What's interesting is what it says for the same method in the Java 6 JavaDoc, it's more explicit about the copy's effects. I wonder why they changed it?
Returns the image as one large tile. The Raster returned is a copy of the image data is not updated if the image is changed
Could the answer be as simple as the changes in the data array not being reflected in img object?
How do I calculate the middle gray level (max(z)+min(z)/2 over the points where the structuring element is 1 and sets the output pixel to that value?
I just know a little about how to get the RGB value each pixel by using image.getRGB(x,y). I have no idea how to get gray level value each pixel of the image and what is z in the formula and all that?
Please help me with this. Thanks in advance.
I'm going to assume that z are the pixels within your structuring element. I'm also going to assume that "structuring element" is in the case of morphology. Here are a few pointers before we start:
You can convert a colour pixel to its graylevel intensity by using the Luminance formula. By consulting the SMPTE Rec. 709 standard, the output graylevel intensity, given the RGB components is: Y = 0.2126*R + 0.7152*G + 0.0722*B.
We're going to assume that the structuring element is odd. This will allow for the symmetric analysis of the structuring element for each pixel in your image where it is placed
I'm going to assume that your image is already loaded in as a BufferedImage.
Your structuring element will be a 2D array of int.
I'm not going to process those pixels where the structuring element traverses out of bounds to make things easy.
As such, the basic algorithm is this:
For each pixel in our image, place the centre of the structuring element at this location
For each pixel location where the structuring element is 1 that coincides with this position, find the max and minimum graylevel intensity
Set the output image pixel at this location to be (max(z) + min(z)) / 2).
Without further ado:
public BufferedImage calculateMiddleGray(BufferedImage img, int[][] mask)
{
// Declare output image
BufferedImage outImg = new BufferedImage(img.getWidth(),
img.getHeight(), BufferedImage.TYPE_INT_RGB);
// For each pixel in our image...
for (int i = mask.length/2; i < img.getWidth() - mask.length/2; i++) {
for (int j = mask[0].length/2; j < img.getHeight() - mask[0].length/2; j++) {
int maxPix = -1;
int minPix = 256;
// For each pixel in the mask...
for (int x = -mask.length/2; x <= mask.length/2; x++) {
for (int y = -mask[0].length/2; y <= mask[0].length/2; y++) {
//Obtain structuring element pixel
int structPix = mask[y+mask.length/2][x+mask[0].length/2];
// If not 1, continue
if (structPix != 1)
continue;
// Get RGB pixel
int rgb = img.getRGB(i+x, j+y);
// Get red, green and blue channels individually
int redPixel = (rgb >> 16) & 0xFF;
int greenPixel = (rgb >> 8) & 0xFF;
int bluePixel = rgb & 0xFF;
// Convert to grayscale
// Performs SMPTE Rec. 709 lum. conversion using integer logic
int lum = (77*red + 150*green + 29*blue) >> 8;
// Find max and min appropriately
if (lum > maxPix)
maxPix = lum;
if (lum < minPix)
minPix = lum;
}
}
// Set output pixel
// Grayscale image has all of its RGB pixels equal
int outPixel = (maxPix + minPix) / 2;
// Cap output - Ensure we don't go out of bounds
if (outPixel > 255)
outPixel = 255;
if (outPixel < 0)
outPixel = 0;
int finalOut = (outPixel << 16) | (outPixel << 8) | outPixel;
outImg.setRGB(i, j, finalOut);
}
}
}
To call this method, create an image img using any standard method, then create a structuring element mask that is a 2D integer array. After, place this method in your class, then invoke the method by:
BufferedImage outImg = calculateMiddleGray(img, mask);
Also (and of course), make sure you import the necessary package for the BufferedImage class, or:
import java.awt.image.BufferedImage;
Note: This is untested code. Hope it works!