Does Java Graphics (Graphics2D) uses graphics card? [duplicate] - java

I'm having performance oddities with Java2D. I know of the sun.java2d.opengl VM parameter to enable 3D acceleration for 2D, but even using that has some weird issues.
Here are results of tests I ran:
Drawing a 25x18 map with 32x32 pixel tiles on a JComponent
Image 1 = .bmp format, Image 2 = A .png format
Without -Dsun.java2d.opengl=true
120 FPS using .BMP image 1
13 FPS using .PNG image 2
With -Dsun.java2d.opengl=true
12 FPS using .BMP image 1
700 FPS using .PNG image 2
Without acceleration, I'm assuming some kind of transformation is taking place with every drawImage() I do in software, and is pulling down the FPS considerably in the case of .PNG. Why though, with acceleration, would the results switch (and PNG actually performs incredibly faster)?! Craziness!
.BMP Image 1 is translated to an image type of TYPE_INT_RGB. .PNG Image 2 is translated to an image type of TYPE_CUSTOM. In order to get consistent speed with and without opengl acceleration, I have to create a new BufferedImage with an image type of TYPE_INT_ARGB, and draw Image 1 or Image 2 to this new image.
Here are the results running with that:
Without -Dsun.java2d.opengl=true
120 FPS using .BMP image 1
120 FPS using .PNG image 2
With -Dsun.java2d.opengl=true
700 FPS using .BMP image 1
700 FPS using .PNG image 2
My real question is, can I assume that TYPE_INT_ARGB will be the native image type for all systems and platforms? I'm assuming this value could be different. Is there some way for me to get the native value so that I can always create new BufferedImages for maximum performance?
Thanks in advance...

I think I found a solution by researching and putting bits and pieces together from too many Google searches.
Here it is, comments and all:
private BufferedImage toCompatibleImage(BufferedImage image)
{
// obtain the current system graphical settings
GraphicsConfiguration gfxConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
/*
* if image is already compatible and optimized for current system
* settings, simply return it
*/
if (image.getColorModel().equals(gfxConfig.getColorModel()))
return image;
// image is not optimized, so create a new image that is
BufferedImage newImage = gfxConfig.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
// get the graphics context of the new image to draw the old image on
Graphics2D g2d = newImage.createGraphics();
// actually draw the image and dispose of context no longer needed
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
// return the new optimized image
return newImage;
}
In my previous post, GraphicsConfiguration was what held the information needed to create optimized images on a system. It seems to work pretty well, but I would have thought Java would automatically do this for you. Obviously you can't get too comfortable with Java. :) I guess I ended up answering my own question. Oh well, hopefully it'll help some of you I've seen trying to make use of Java for 2D games.

Well, this is old post but I'd like to share my findings about direct drawing with Swing/AWT, without BufferedImage.
Some kind of drawing, as 3D, are better done when painting directly to a int[] buffer. Once done the images, you can use an ImageProducer instance, like MemoryImageSource, to produce images. I'm assuming you know how to perform your drawings directly, without help of Graphics/Graphics2.
/**
* How to use MemoryImageSource to render images on JPanel
* Example by A.Borges (2015)
*/
public class MyCanvas extends JPanel implements Runnable {
public int pixel[];
public int width;
public int height;
private Image imageBuffer;
private MemoryImageSource mImageProducer;
private ColorModel cm;
private Thread thread;
public MyCanvas() {
super(true);
thread = new Thread(this, "MyCanvas Thread");
}
/**
* Call it after been visible and after resizes.
*/
public void init(){
cm = getCompatibleColorModel();
width = getWidth();
height = getHeight();
int screenSize = width * height;
if(pixel == null || pixel.length < screenSize){
pixel = new int[screenSize];
}
mImageProducer = new MemoryImageSource(width, height, cm, pixel,0, width);
mImageProducer.setAnimated(true);
mImageProducer.setFullBufferUpdates(true);
imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);
if(thread.isInterrupted() || !thread.isAlive()){
thread.start();
}
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public /* abstract */ void render(){
// rubisch draw
int[] p = pixel; // this avoid crash when resizing
if(p.length != width * height) return;
for(int x=0; x < width; x++){
for(int y=0; y<height; y++){
int color = (((x + i) % 255) & 0xFF) << 16; //red
color |= (((y + j) % 255) & 0xFF) << 8; //green
color |= (((y/2 + x/2 - j) % 255) & 0xFF) ; //blue
p[ x + y * width] = color;
}
}
i += 1;
j += 1;
}
private int i=1,j=256;
#Override
public void run() {
while (true) {
// request a JPanel re-drawing
repaint();
try {Thread.sleep(5);} catch (InterruptedException e) {}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// perform draws on pixels
render();
// ask ImageProducer to update image
mImageProducer.newPixels();
// draw it on panel
g.drawImage(this.imageBuffer, 0, 0, this);
}
/**
* Overrides ImageObserver.imageUpdate.
* Always return true, assuming that imageBuffer is ready to go when called
*/
#Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
return true;
}
}// end class
Note we need unique instance of MemoryImageSource and Image. Do not create new Image or new ImageProducer for each frames, unless you have resized your JPanel. See init() method above.
In a rendering thread, ask a repaint(). On Swing, repaint() will call the overridden paintComponent(), where it call your render() method and then ask your imageProducer to update image.
With Image done, draw it with Graphics.drawImage().
To have a compatible Image, use proper ColorModel when you create your Image. I use GraphicsConfiguration.getColorModel():
/**
* Get Best Color model available for current screen.
* #return color model
*/
protected static ColorModel getCompatibleColorModel(){
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
return gfx_config.getColorModel();
}

From what I remember when I was thinking about doing graphics programming in Java, the built in libraries are slow. I was advised on GameDev.Net that anyone doing anything serious would have to use something like jogl

Related

FPS drops after loading images in java

So, I recently asked a question on how to preload images in Java (preloading images in Java) and it worked great! Until I went to play the game. Framerate dropped drastically. I don't know what it was, but basically, I have a whole sprite map loaded into an array. Each image corresponds to a three-degree rotation. So, 5 degrees would become the 3-degree image, 6 would stay the 6, and so on (I tried Math.round and it actually made the 5 and 4-degree images go to the 6-degree image, and that's more desirable, however, it's slower)
I am looking for some ways to optimize the code. Here are my angle calculations:
private float getAngle(double x, double y) {
float angle = (float) Math.toDegrees(Math.atan2(y - this.getCenterY(), x - this.getCenterX()));
if(angle < 0){
angle += 360;
}
return angle;
}
The x and y values inputted into the method are the center x and y values of the enemy being targeted by the tower performing this calculation. Then this code is executed in the draw method:
if(airID==Value.towerCannon && (airRow>0 && airRow<5) && angle>=0) {
if(angle==Canvas.rotatedAirMap.length) angle = 0;
g.drawImage(Canvas.rotatedAirMap[angle][level-1], x, y, width, height, null);
} else {
g.drawImage(Canvas.airMap[airRow][airID], x, y, width, height, null);
}
}
This will draw the appropriate, preloaded image rotated at the specified angle (The "angle" - or image identifier - is calculated when the tower shoots by dividing the result of the angle calculation by three and then casting that to an int - I could also round that value)
Any suggestions on how to optimize this so I don't get such massive frame drops? I assume the frame drops are due to the VM heap size being too small, but I've increased it and still, nothing significant happens. Any help would be greatly appreciated.
Thanks!
#VGR here is what I did with your response:
public void paintComponent(Graphics g) {
if(isFirst) { //Define stuff when isFirst is true
define(); //Sets up the image arrays
GraphicsConfiguration config = getGraphicsConfiguration();
if(config == null) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
config = env.getDefaultScreenDevice().getDefaultConfiguration();
}
BufferedImage compatibleImage = config.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
g = compatibleImage.createGraphics();
isFirst = false;
}
}
This works a little bit faster, but I had to do some workarounds. repaint() is called in the game loop (this class implements runnable) So the graphics component created by the repaint method (however that works) is the graphics I use for the whole game. Would this be the correct way to do it?
Translating images from their inherent color model to the color model of the current video mode can slow down rendering. To avoid this, you can make sure each image is compatible with the screen where your window resides:
BufferedImage image = ImageIO.read(/* ... */);
GraphicsConfiguration config = getGraphicsConfiguration();
if (config == null) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment;
config = env.getDefaultScreenDevice().getDefaultConfiguration();
}
BufferedImage compatibleImage = config.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
Graphics2D g = compatibleImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
image = compatibleImage;
If an image has the same color model as the current video mode, there is no translation needed when drawing it. It’s even possible that painting of such an image may be done entirely by the GPU.

Copy the contents of a JPanel onto a BufferedImage

On the first time through, I insert BufferedImages from a list onto my JPanel from my extended class:
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if (controlWhichImage == 1){
for (BufferedImage eachImage : docList){
g.drawImage(eachImage, 0,inty,imageWidth,imageHeight,null);
intx += eachImage.getWidth();
inty += eachImage.getHeight() * zoomAdd;
}
if (intx >= this.getWidth() || inty >= this.getHeight()){
inty = 0;
}
The next time I want to copy the contents of the JPanel to a BufferedImage:
public void recordImage(){
controlWhichImage = 2;
this.createdImage = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_ARGB);
Image halfWay = this.createImage(this.getWidth(), this.getHeight());
//now cast it from Image to bufferedImage
this.createdImage = (BufferedImage) halfWay;
}
And then, take the modified BufferedImage and draw it back onto the JPanel:
if (controlWhichImage == 2){
g.drawImage(this.createdImage,0,inty,this.getWidth(),this.getHeight(),null);
}
This second time I get a blank panel.
I hope this is clear, any help gratefully received.
Sorry for my poor explanation. I will try to make myself clearer.
On each iteration the user is able to draw on the image in the Jpanel.
What I want to do is copy the user altered jpanel into a buffered image which will then be in the Jpanel to be edited again by the user.
This continues until the user selects print.
So apart from the code that I have put here are the controls for drawing by the user, at the moment I am struggling with putting the initial updated image from the original Jpanel into a bufferedImage and then back to the JPanel.
Hope this makes it somewhat clearer
To draw to a BufferedImage, you would do something similar to what you already do in your paintComponent method, but with your BufferedImage. Perhaps a method like:
// imgW and imgH are the width and height of the desired ultimate image
public BufferedImage combineImages(List<BufferedImage> docList, int imgW, int imgH) {
// first create the main image that you want to draw to
BufferedImage mainImg = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
// get its Graphics context
Graphics g = mainImage.getGraphics();
int intx = 0;
int inty = 0;
// draw your List of images onto this main image however you want to do this
for (BufferedImage eachImage : docList){
g.drawImage(eachImage, 0,inty,imageWidth,imageHeight,null);
intx += eachImage.getWidth();
inty += eachImage.getHeight() * zoomAdd;
}
}
// anything else that you need to do
g.dispose(); // dispose of this graphics context to save resources
return mainImg;
}
You could then store the image returned into a varaible and then draw it in your JPanel if desired, or write it to disk.
If this doesn't answer your question, then again you'll want to tell more and show us your MCVE.

Quickly Render Bytes to Canvas with a Color Palette

While working on a Java application which requires rendering sprites, I thought that, instead of loading a .png or .jpg file as an Image or BufferedImage, I could load up a byte[] array containing indices for a color palette(16 colors per palette, so two pixels per byte), then render that.
The method I currently have generates a BufferedImage from the byte[] array and color palette while initializing, taking extra time to initialize but running smoothly after that, which works fine, but there are only 4 sprites in the program so far. I'm worried that when there are 100+ sprites, storing all of them as BufferedImages will be too taxing on the memory. And not only would that mean 1 BufferedImage per sprite, but actually 1 image for each sprite/palette combination I'd want to use.
This function creates the BufferedImage:
protected BufferedImage genImage(ColorPalette cp, int width, int height){ //Function to generate BufferedImage to render from the byte[]
BufferedImage ret = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); //Create the Image to return
for(int j=0; j<height; j++){ //Run a for loop for each pixel
for(int i=0; i<width; i++){
int index = (j * width + i)/2; //Get the index of the needed byte
int value = image[index] & 0x00ff; //Convert to "unsigned byte", or int
byte thing; //declare actual color index as byte
if(i % 2 == 0)thing = (byte)((value & 0b11110000) >>> 4); //If it's an even index(since it starts with 0, this includes the 1st one), get the first 4 bits of the value
else thing = (byte)(value & 0b00001111); //If it's odd, get the last four bits
ret.setRGB(i, j, cp.getColor(thing & 0x00ff).getRGB()); //Set the pixel in the image to the value in the Color Palette
}
}
return ret;
}
And this one actually renders it to the screen:
public void render(Graphics g, int x, int y){ //Graphics to render to and x/y coords
g.drawImage(texture, x, y, TILE_WIDTH, TILE_HEIGHT, null); //Render it
}
I've experimented with another method that renders from the byte[] directly w/o the need for a BufferedImage, which should theoretically succeed in saving memory by avoiding use of a BufferedImage for each sprite, but it ended up being very, very slow. It took several seconds to render each frame w/ at most 25 sprites to render on the screen! Note that g is a Graphics object.
private void drawSquare(int x, int y, int scale, Color c){ //Draw each "pixel" to scale
if(g == null){ //If null, quit
return;
}
g.setColor(c); //Set the color
for(int i=x; i<x+scale; i++){ //Loop through each pixel
if(i<0)continue;
for(int j=y; j<y+scale; j++){
if(j<0)continue;
g.fillRect(x, y, scale, scale); //Fill the rect to make the "pixel"
}
}
}
public void drawBytes(byte[] image, int x, int y, int width, int height, int scale, ColorPalette palette){ //Draw a byte[] image with given byte[], x/y coords, width/height, scale, and color palette
if(image.length < width * height / 2){ //If the image is too small, exit
return;
}
for(int j=0; j<height; j++){ //Loop through each pixel
for(int i=0; i<width; i++){
int index = (j * width + i)/2; //Get index
int value = image[index]; //get the byte
byte thing; //get the high or low value depending on even/odd
if(i % 2 == 0)thing = (byte)((value & 0b11110000) >>> 4);
else thing = (byte)(value & 0b00001111);
drawSquare((int)(x + scale * i), (int)(y + scale * j), scale, palette.getColor(thing)); //draw the pixel
}
}
}
So is there a more efficient way to render these byte[] arrays w/o the need for BufferedImage's? Or will it really not be problematic to have several hundred BufferdImage's loaded into memory?
EDIT: I've also tried doing the no-BufferedImage methods, but with g as the one large BufferedImage to which everything is rendered, and is then rendered to the Canvas. The primary difference is that g.fillRect(... is changed to g.setRGB(... in that method, but it was similarly slow.
EDIT: The images I'm dealing with are 16x16 and 32x32 pixels.
If memory usage is your main concern, I'd use BufferedImages with IndexColorModel (TYPE_BYTE_BINARY). This would perfectly reflect your byte[] image and ColorPalette, and waste very little memory. They will also be reasonably fast to draw.
This approach will use about 1/8th of the memory used by the initial use of TYPE_INT_RGB BufferedImages, because we retain the 4 bits per pixel, instead of 32 bits (an int is 32 bits) per pixel (plus some overhead for the palette, of course).
public static void main(String[] args) {
byte[] palette = new byte[16 * 3]; // 16 color palette without alpha
byte[] pixels = new byte[(16 * 16 * 4) / 8]; // 16 * 16 * 4 bit
Random random = new Random(); // For test purposes, just fill arrays with random data
random.nextBytes(palette);
random.nextBytes(pixels);
// Create ColorModel & Raster from palette and pixels
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, -1); // -1 for no transparency
DataBufferByte buffer = new DataBufferByte(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster(buffer, 16, 16, 4, null);
// Create BufferedImage from CM and Raster
final BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image: " + image); // "image: BufferedImage#...: type = 12 ..."
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setVisible(true);
}
});
}
The above code will create fully opaque (Transparency.OPAQUE) images, that will occupy the entire 16 x 16 pixel block.
If you want bitmask (Transparency.BITMASK) transparency, where all pixels are either fully opaque or fulle transparent, just change the last parameter in the IndexColorModel to the palette index you want to be fully transparent.
int transparentIndex = ...;
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, false, transparentIndex);
// ...everything else as above
This will allow your sprites to have any shape you want.
If you want translucent pixels (Transparency.TRANSLUCENT), where pixels can be semi-transparent, you can also have that. You will then have to change the palette array to 16 * 4 entries, and include a sample for the alpha value as the 4th sample for each entry (quadruple). Then invoke the IndexColorModel constructor with the last parameter set to true (hasAlpha):
byte[] palette = new byte[16 * 4]; // 16 color palette with alpha (translucency)
// ...
IndexColorModel cm = new IndexColorModel(4, 16, palette, 0, true); // true for palette with alpha samples
// ...everything else as above
This will allow smoother gradients between the transparent and non-transparent parts of the sprites. But with only 16 colors in the palette, you won't have many entries available for transparency.
Note that it is possible to re-use the Rasters and IndexColorModels here, in all of the above examples, to save further memory for images using the same palette, or even images using the same image data with different palettes. There's one caveat though, that is the images sharing rasters will be "live views" of each other, so if you make any changes to one, you will change them all. But if your images are never changed, you could exploit this fact.
That said, the above really is a compromise between saving memory and having "reasonable" performance. If performance (ie. frames per second) is more important, just ignore the memory usage, and create BufferedImages that are compatible with your graphics card's/the OS's native pixel layout. You can do this, by using component.createCompatibleImage(...) (where component is a JComponent subclass) or gfxConfig.createCompatibleImage(...) (where gfxConfig is a GraphicsConfiguration obtained from the local GraphicsEnvironment).

OutOfMemoryError: Jave heap space when jtable saved as Image

Currently I am saving a jtable as jpeg using the below method, when the dimension of the jtable became 2590, 126181, java.lang.OutOfMemoryError: Java heap space exception occurs at "BufferedImage constructor", when the size of the table is small the image gets saved successfully.
public BufferedImage saveComponentAsJPEG(JTable table, String filename) {
Dimension size = table.getSize();
BufferedImage myImage =
new BufferedImage(size.width, size.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = myImage.createGraphics();
table.paint(g2);
return myImage;
}
How to save a jtable with bigger size in pdf or jpeg image?
Updated Info:
You asked how to "split the JTable into different small images":
As you go through my code below please read my comments, they help explain what is happening and will help you grasp a better understanding of how a JTable/JComponent can be painted to lots of small images. At the heart my code is similar to yours, but there are two key points:
1) Rather than create a single large BufferedImage, I create a single small image that is then used multiple times, therefore leaving a very small memory footprint.
2) With the single image, I use Graphics.translate() to paint a small part of the JTable each time.
The following code was tested with a large JComponents (2590 x 126181) and a tile size of 200x200, and the whole process did not exceed 60mb of memory:
//width = width of tile in pixels, for minimal memory usage try 200
//height = height of tile in pixels, for minimal memory usage try 200
//saveFileLocation = folder to save image tiles
//component = The JComponent to save as tiles
public static boolean saveComponentTiles(int width, int height, String saveFileLocation, JComponent component)
{
try
{
//Calculate tile sizes
int componentWidth = component.getWidth();
int componentHeight = component.getHeight();
int horizontalTiles = (int) Math.ceil((double)componentWidth / width); //use (double) so Math.ceil works correctly.
int verticalTiles = (int) Math.ceil((double)componentHeight / height); //use (double) so Math.ceil works correctly.
System.out.println("Tiles Required (H, W): "+horizontalTiles+", verticalTiles: "+verticalTiles);
//preset arguments
BufferedImage image;
//Loop through vertical and horizontal tiles
//Draw part of the component to the image
//Save image to file
for (int h = 0; h < verticalTiles; h++)
{
for (int w = 0; w < horizontalTiles; w++)
{
//check tile size, if area to paint is smaller than image then shrink image
int imageHeight = height;
int imageWidth = width;
if (h + 1 == verticalTiles)
{
imageHeight = componentHeight - (h * height);
}
if (w + 1 == horizontalTiles)
{
imageWidth = componentWidth - (w * width);
}
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
//translate image graphics so that only the correct part of the component is panted to the image
g.translate(-(w * width), -(h * height));
component.paint(g);
//In my example I am saving the image to file, however you could throw your PDF processing code here
//Files are named as "Image.[h].[w]"
//Example: Image 8 down and 2 accross would save as "Image.8.2.png"
ImageIO.write(image, "png", new File(saveFileLocation + "Image." + h +"."+ w + ".png"));
//tidy up
g.dispose();
}
}
return true;
}
catch (IOException ex)
{
return false;
}
}
Just call it like so:
boolean result = saveComponentTiles(200, 200, saveFileLocation, jTable1);
Also if you haven't done it already, you should only call the method from a different thread because it will hang your application when dealing with large components.
If you have not picked a PDF library yet, then I highly recommend looking at iText.
Original Post:
The process you are looking for is quite simple, however it may take some work.
You were on the right track thinking about parts, but as David
mentioned you shouldn't mess with the jTable, instead you will need a
to make use of the TiledImage class, or do something yourself with
RenderedImage and Rasters.
This sort of method basically uses HDD space instead of memory and
lets you create a large image in lots of smaller parts/tiles, then
when its done you can save it all to a single image file.
This answer may also help: https://stackoverflow.com/a/14069551/1270000

Java2D Performance Issues

I'm having performance oddities with Java2D. I know of the sun.java2d.opengl VM parameter to enable 3D acceleration for 2D, but even using that has some weird issues.
Here are results of tests I ran:
Drawing a 25x18 map with 32x32 pixel tiles on a JComponent
Image 1 = .bmp format, Image 2 = A .png format
Without -Dsun.java2d.opengl=true
120 FPS using .BMP image 1
13 FPS using .PNG image 2
With -Dsun.java2d.opengl=true
12 FPS using .BMP image 1
700 FPS using .PNG image 2
Without acceleration, I'm assuming some kind of transformation is taking place with every drawImage() I do in software, and is pulling down the FPS considerably in the case of .PNG. Why though, with acceleration, would the results switch (and PNG actually performs incredibly faster)?! Craziness!
.BMP Image 1 is translated to an image type of TYPE_INT_RGB. .PNG Image 2 is translated to an image type of TYPE_CUSTOM. In order to get consistent speed with and without opengl acceleration, I have to create a new BufferedImage with an image type of TYPE_INT_ARGB, and draw Image 1 or Image 2 to this new image.
Here are the results running with that:
Without -Dsun.java2d.opengl=true
120 FPS using .BMP image 1
120 FPS using .PNG image 2
With -Dsun.java2d.opengl=true
700 FPS using .BMP image 1
700 FPS using .PNG image 2
My real question is, can I assume that TYPE_INT_ARGB will be the native image type for all systems and platforms? I'm assuming this value could be different. Is there some way for me to get the native value so that I can always create new BufferedImages for maximum performance?
Thanks in advance...
I think I found a solution by researching and putting bits and pieces together from too many Google searches.
Here it is, comments and all:
private BufferedImage toCompatibleImage(BufferedImage image)
{
// obtain the current system graphical settings
GraphicsConfiguration gfxConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
/*
* if image is already compatible and optimized for current system
* settings, simply return it
*/
if (image.getColorModel().equals(gfxConfig.getColorModel()))
return image;
// image is not optimized, so create a new image that is
BufferedImage newImage = gfxConfig.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
// get the graphics context of the new image to draw the old image on
Graphics2D g2d = newImage.createGraphics();
// actually draw the image and dispose of context no longer needed
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
// return the new optimized image
return newImage;
}
In my previous post, GraphicsConfiguration was what held the information needed to create optimized images on a system. It seems to work pretty well, but I would have thought Java would automatically do this for you. Obviously you can't get too comfortable with Java. :) I guess I ended up answering my own question. Oh well, hopefully it'll help some of you I've seen trying to make use of Java for 2D games.
Well, this is old post but I'd like to share my findings about direct drawing with Swing/AWT, without BufferedImage.
Some kind of drawing, as 3D, are better done when painting directly to a int[] buffer. Once done the images, you can use an ImageProducer instance, like MemoryImageSource, to produce images. I'm assuming you know how to perform your drawings directly, without help of Graphics/Graphics2.
/**
* How to use MemoryImageSource to render images on JPanel
* Example by A.Borges (2015)
*/
public class MyCanvas extends JPanel implements Runnable {
public int pixel[];
public int width;
public int height;
private Image imageBuffer;
private MemoryImageSource mImageProducer;
private ColorModel cm;
private Thread thread;
public MyCanvas() {
super(true);
thread = new Thread(this, "MyCanvas Thread");
}
/**
* Call it after been visible and after resizes.
*/
public void init(){
cm = getCompatibleColorModel();
width = getWidth();
height = getHeight();
int screenSize = width * height;
if(pixel == null || pixel.length < screenSize){
pixel = new int[screenSize];
}
mImageProducer = new MemoryImageSource(width, height, cm, pixel,0, width);
mImageProducer.setAnimated(true);
mImageProducer.setFullBufferUpdates(true);
imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);
if(thread.isInterrupted() || !thread.isAlive()){
thread.start();
}
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public /* abstract */ void render(){
// rubisch draw
int[] p = pixel; // this avoid crash when resizing
if(p.length != width * height) return;
for(int x=0; x < width; x++){
for(int y=0; y<height; y++){
int color = (((x + i) % 255) & 0xFF) << 16; //red
color |= (((y + j) % 255) & 0xFF) << 8; //green
color |= (((y/2 + x/2 - j) % 255) & 0xFF) ; //blue
p[ x + y * width] = color;
}
}
i += 1;
j += 1;
}
private int i=1,j=256;
#Override
public void run() {
while (true) {
// request a JPanel re-drawing
repaint();
try {Thread.sleep(5);} catch (InterruptedException e) {}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// perform draws on pixels
render();
// ask ImageProducer to update image
mImageProducer.newPixels();
// draw it on panel
g.drawImage(this.imageBuffer, 0, 0, this);
}
/**
* Overrides ImageObserver.imageUpdate.
* Always return true, assuming that imageBuffer is ready to go when called
*/
#Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
return true;
}
}// end class
Note we need unique instance of MemoryImageSource and Image. Do not create new Image or new ImageProducer for each frames, unless you have resized your JPanel. See init() method above.
In a rendering thread, ask a repaint(). On Swing, repaint() will call the overridden paintComponent(), where it call your render() method and then ask your imageProducer to update image.
With Image done, draw it with Graphics.drawImage().
To have a compatible Image, use proper ColorModel when you create your Image. I use GraphicsConfiguration.getColorModel():
/**
* Get Best Color model available for current screen.
* #return color model
*/
protected static ColorModel getCompatibleColorModel(){
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
return gfx_config.getColorModel();
}
From what I remember when I was thinking about doing graphics programming in Java, the built in libraries are slow. I was advised on GameDev.Net that anyone doing anything serious would have to use something like jogl

Categories