What is the best way to read pixels of a JavaFX Canvas? - java

I want to get the color for specific coordinates inside a Canvas. I already tried getting a snapshot using this code:
WritableImage snap = gc.getCanvas().snapshot(null, null);
snap.getPixelReader().getArgb(x, y); //This just gets the color without assigning it.
But it just takes too much time for my application. I was wondering if there is any other way to access the color of a pixel for which I know the coordinates.

A Canvas buffers the drawing instructions prescribed by invoking the methods of a GraphicsContext. There are no pixels to read until the Canvas is rendered in a later pulse, and the internal format of the instruction buffer is not exposed in the API.
If a snapshot() of the Canvas is feasible, a rendered pixel may be examined using the resulting image's PixelReader.
int aRGB = image.getPixelReader().getArgb(x, y);
This example focuses on a single pixel. This example displays the ARGB BlendMode result in a TextField and a Tooltip as the mouse moves on the Canvas. More examples may be found here.
As an alternative, consider drawing into a BufferedImage, illustrated here, which allows access to the image's pixels directly and via its WritableRaster. Adding the following line to this complete example outputs the expected value for opaque red in ARGB order: ffff0000.
System.out.println(Integer.toHexString(bi.getRGB(50, 550)));

public class Pixel
{
private static final SnapshotParameters SP = new SnapshotParameters();
private static final WritableImage WI = new WritableImage(1, 1);
private static final PixelReader PR = WI.getPixelReader();
private Pixel()
{
}
public static int getArgb(Node n, double x, double y)
{
synchronized (WI)
{
Rectangle2D r = new Rectangle2D(x, y, 1, 1);
SP.setViewport(r);
n.snapshot(SP, WI);
return PR.getArgb(0, 0);
}
}
public static Color getColor(Node n, double x, double y)
{
synchronized (WI)
{
Rectangle2D r = new Rectangle2D(x, y, 1, 1);
SP.setViewport(r);
n.snapshot(SP, WI);
return PR.getColor(0, 0);
}
}
}

Related

How can I create a Rectangle with a blend mode using a Graphics2D object in Java?

I am attempting to create a day/night cycle in a simple RPG-style game with Java. I have reached a point where I have included a low-alpha color overlay will change hue over time to simulate this:
// Render the overlay rectangle.
public void render(Graphics2D g) {
// Set the color to yellow with ~50% opacity.
g.setColor(new Color(255, 255, 0, 125));
// Draw the rectangle.
g.fillRect(0, 0, handler.getWidth(), handler.getHeight());
g.dispose();
}
While this works, I would like to introduce blending modes (i.e. add, subtract, screen, soft light) into my code to increase the day/night cycle's realism.
Here's what I have tried, but to no avail:
Researching JavaFX's blending mode options. I don't want to switch to JavaFX.
Creating an all-white image that overlays the screen and uses setComposite() and setXORMode() from a different StackOverflow answer.
Using an image with a BlendingMode and setComposite() from the package org.jdesktop.swingx.graphics.BlendComposite.
My desired code is something similar to what follows, but I will take what I can get.
public void render(Graphics2D g) {
g.setColor(new Color(255, 255, 0, 125));
g.setBlendMode(ADD);
g.fillRect(0, 0, handler.getWidth(), handler.getHeight());
g.dispose();
}
One possibility to solve this would be to buffer your screen in a BufferedImage and then implement your own drawing methods(This definitely isn't a fast way to do this as it throws away all hardware-acceleration. If you need to render a big screen this can be laggy depending on your system and your implementation.):
class MyGraphics {
public enum BlendingMode {
NORMAL, ADD, …
}
public BufferedImage buffer;
private BlendingMode bm = BlendingMode.NORMAL;
private int color = 0;
public MyGraphics(BufferedImage buf) {
buffer = buf; // Initialize it with a bufferedImage of your panel size.
}
public void setBlendMode(BlendingMode b) {
bm = b;
}
private int applyBlendingMode(int imgColor1, int newColor) {
int rImg, rNew, gImg, gNew, bImg, bNew, alphaImg, alphaNew; // You can intialize those using bitshifts from the color integers.
switch(bm) {
// Implement all your blending modes here.
}
// And don't forget to return the result!
}
public void setColor(int c) {
color = c;
}
public void fillRect(int x0, int y0, int width, int height) {
for(int x = x0; x < x0+width; x++) {
for(int y = y0; y < y0+height; y++) {
buffer.setRGB(x, y, applyBlendingMode(buffer.getRGB(x, y), color));
}
}
}
}
If the setRGB and getRGB are working to slow for you, you might speed it up by working directly on the BufferedImage's data array(see here for further info).
Also keep in mind, that if you want to use normal blending mode, you can still do it the normal way using buffer.getGraphics() to get your Graphics object. This should be faster in most cases.
After you are done with implementing your blending-modes and maybe some further functionality to draw more then a rectangle you can just simply draw the underlying BufferedImage on your screen using Graphics.drawImage(buffer, 0, 0);

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

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

Is it possible to modify colors of an image in java Swing?

I am animating a simulation, and the images I'm using for my entities are small PNG files that are in black and white (if that makes any difference). I would like to know if there is any way to change the color of the image (e.g. change the black to red, or overlay a red filter over the black) once it's been imported as a BufferedImage? Or is my best bet to rather create separate color versions of the icon outside of Java and importing them as separate images?
you can use the
java.awt.image.RGBImageFilter
class
I personally haven`t played with that, I went another way, which is getting the icon into an array of pixels(ints) and manipulating this on my own.
With that , you can create an instance of ImageSourceInt (as I call it) passing an ImageIcon, fiddling around with it
(canvasData is the pixels-array in ARGB-int format - 1byte per channel each = 4 bytes = 1 int)
and retrieve a BufferedImage (a view, not a copy) back for use with Swing.
For the latter, call the getReferenceImage() method.
By the way, if you dont know about scanSize (common term in imaging) the just consider it as the width of the image.
(Sorry for the bad code formatting, I`m new here)
public class ImageSourceInt implements ImageSource {
int[] canvasData;
int width;
int height;
int scanSize;
int lineCount;
/**
* #param source make sure it is loaded or this ImageSource will be empty.<br>
* sizeIncrementWidth and sizeIncrementHeight are set to 1
*/
public ImageSourceInt(ImageIcon source){
if (source == null) {
this.canvasData = new int[0];
return;
}
this.width = source.getIconWidth();
this.height = source.getIconHeight();
this.scanSize = source.getIconWidth();
this.lineCount = source.getIconHeight();
this.canvasData = new int[this.width*this.height];
// PixelGrabber(Image img, int x, int y, int w, int h, int[] pix, int
// off, int scansize)
PixelGrabber grabber = new PixelGrabber(source.getImage(), 0,
0, this.width, this.height, this.canvasData, 0, this.scanSize);
try {
grabber.grabPixels();
} catch (InterruptedException e) {
e.printStackTrace();// must not be...
}
}
/**
* #return a BufferedImage with the data of this ImageSource (referenced)<br>
* IMPORTANT: if the size changed, the BufferedImage will get invalid, causing strange effects or exceptions
*/
public BufferedImage getReferenceImage(){
DataBuffer buf = new DataBufferInt(this.canvasData, this.canvasData.length);
WritableRaster wRaster = Raster.createPackedRaster(buf, this.getWidth(), this.getHeight(), this.getScanSize(),
new int[]{0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000}, new Point());
BufferedImage bi = new BufferedImage(ColorModel.getRGBdefault(), wRaster, false, null);
return bi;
}
}

How do I add a spotlights to an image

I have an image that I want to show some 'spotlights' on, like they do on TV. The rest of the image should be darker than the original, and the person that I'm spotlighting should be normal. I have the x,y and radius of the spotlight, but I'm not sure how to change the brightness at that location.
Also, if I have two spotlights and they intersect, the intersection should be brighter than either of the spotlights.
Use RescaleOp on the original image and subimages. Given that you have a buffered image (called biDest) that contains the image, call RescaleOp(0.6, 0, null) on it to make it darker. Then, to add a (rectangular) spotlight, call the following:
public void spotLight(int x, int y, int w, int h)
{
BufferedImage i = biDest.getSubimage(x, y, w, h);
RescaleOp rescale = new RescaleOp(SPOTLIGHT_BRIGHTNESS, 0, null);
rescale.filter(i, i);
repaint();
}
A simple way is to convert the color to HSL, lower L to darken, increase to lighten, then convert back to RGB and set the pixel.
http://www.mpa-garching.mpg.de/MPA-GRAPHICS/hsl-rgb.html

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