I have some java code that needs to programmatically render text onto an image. So I use BufferedImage, and write text on the Graphics object.
However, when configuring the font instance, one would specify the font size in points. When a piece of text is rendered onto an image, AWT will translate the points into pixels, based on the resolution of the Graphics object. I don't want to get myself involved in computing the pixel/point ratio, since it's really the task for the AWT. The image that is being produced is for a high resolution device (higher than any desktop monitors).
But, I don't seem to find a way to specify what the resolution of the Graphics is. It inherits it from the local graphics environment, which is beyond my control. I don't really want this code to be dependent on anything local, and I'm not even sure it's "sane", to use local graphics environment for determining the resolution of off screen rasters, who knows what people would want them for.
So, any way I can specify the resolution for an off screen image of any kind (preferably the one that can create Graphics object so I can use standard AWT rendering API)?
(update)
Here is a (rather long) sample problem that renders a piece of text on an image, with predefined font size in pixels (effectively, the target device DPI is 72). What bugs me, is that I have to use local screen DPI to make the calculation of the font size in points, though I'm not using the screen in any way, so it's not relevant, and plain fails on headless systems all together. What I would loved in this case instead, is being able to create an off screen image (graphics, raster), with DPI of 72, which would make points, by value, be equal to pixels.
Sample way to run the code:
$ java FontDisplay Monospace 150 "Cat in a bag" 1.png
This would render "Cat in a bag message", with font size of 150 pixels, on a 150 pixel tall image, and save the result in 1.png.
import java.awt.*;
import java.awt.image.*;
import java.awt.font.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import java.io.*;
import java.util.*;
public class FontDisplay {
public static void main(String a[]) throws Exception {
// args: <font_name> <pixel_height> <text> <image_file>
// image file must have supported extension.
int height = Integer.parseInt(a[1]);
String text = a[2];
BufferedImage bi = new BufferedImage(1, 1,
BufferedImage.TYPE_INT_ARGB);
int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
System.out.println("dpi : "+dpi);
float points = (float)height * 72.0F / (float)dpi;
System.out.println("points : "+points);
Map m = new HashMap();
m.put(TextAttribute.FAMILY, a[0]);
m.put(TextAttribute.SIZE, points);
Font f = Font.getFont(m);
if (f == null) {
throw new Exception("Font "+a[0]+" not found on your system");
}
Graphics2D g = bi.createGraphics();
FontMetrics fm = g.getFontMetrics(f);
int w = fm.charsWidth(text.toCharArray(), 0, text.length());
bi = new BufferedImage(w, height, BufferedImage.TYPE_INT_ARGB);
g = bi.createGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, w, height);
g.setColor(Color.WHITE);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
g.setFont(f);
g.drawString(text, 0, fm.getMaxAscent());
String fName = a[3];
String ext = fName.substring(fName.lastIndexOf('.')+1).toLowerCase();
File file = new File(fName);
ImageWriter iw = ImageIO.getImageWritersBySuffix(ext).next();
ImageOutputStream ios = ImageIO.createImageOutputStream(file);
iw.setOutput(ios);
iw.write(bi);
ios.flush();
ios.close();
}
}
Comparing points to pixels is like kg to Newton where the acceleration may give varying conversions. AWT lets you elect a device (screen, printer), but in your case you definitely have to determine your ratio.
You may of course use Photoshop or Gimp and create a normative image for java.
After elaborated question:
Ah, I think I see the misunderstanding. An image does only concern pixels, never points, mm, DPI, or whatever. (Sometimes only as metainfo added separately to the image.)
So if you know the DPI of your device, the inches you want to use, then the dots/pixels are clear. points/dpi may shed more light.
Related
I'm trying to code a class to seam carve images in x and y direction. The x direction is working, and to reduce the y direction I thought about simply rotating the image 90° and run the same code over the already rescaled image (in x direction only) and after that, rotate it back to its initial state.
I found something with AffineTransform and tried it. It actually produced a rotated image, but messed up the colors and I don't know why.
This is all the code:
import java.awt.image.BufferedImage;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.io.File;
import java.io.IOException;
import javafx.scene.paint.Color;
import javax.imageio.ImageIO;
public class example {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
BufferedImage imgIn = ImageIO.read(new File("landscape.jpg"));
BufferedImage imgIn2 = imgIn;
AffineTransform tx = new AffineTransform();
tx.rotate(Math.PI/2, imgIn2.getWidth() / 2, imgIn2.getHeight() / 2);//(radian,arbit_X,arbit_Y)
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
BufferedImage last = op.filter(imgIn2, null);//(sourse,destination)
ImageIO.write(last, "JPEG", new File("distortedColors.jpg"));
}
}
Just alter the filename in
BufferedImage imgIn = ImageIO.read(new File("landscape.jpg")); and try it.
When executed, you get 4 images: a heatmap, an image with seams in it and a rescaled image. The last image is a test to see if the rotation worked and it should show a rotated image but with distorted colors...
Help would be greatly appreciated!
EDIT:
The problem is with the AffineTransformOp You need :
AffineTransformOp.TYPE_NEAREST_NEIGHBOR
instead of the BILINEAR you have now.
Second paragraph from documentation hints towards this.
This class uses an affine transform to perform a linear mapping from
2D coordinates in the source image or Raster to 2D coordinates in the
destination image or Raster. The type of interpolation that is used is
specified through a constructor, either by a RenderingHints object or
by one of the integer interpolation types defined in this class. If a
RenderingHints object is specified in the constructor, the
interpolation hint and the rendering quality hint are used to set the
interpolation type for this operation.
The color rendering hint and
the dithering hint can be used when color conversion is required. Note
that the following constraints have to be met: The source and
destination must be different. For Raster objects, the number of bands
in the source must be equal to the number of bands in the destination.
So this works
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
It seems like there's a color conversion happening due to passing null to op.filter(imgIn2, null);.
If you change it like that it should work:
BufferedImage last = new BufferedImage( imgIn2.getWidth(), imgIn2.getHeight(), imgIn2.getType() );
op.filter(imgIn2, last );
Building on what bhavya said...
Keep it simple and you should use the dimensions expected from the operation:
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
BufferedImage destinationImage = op.filter(bImage, op.createCompatibleDestImage(bImage, null));
I am trying to get the value of the White Colored pixel from a GrayScale image and replace it with another Color but when I run my code, the whole GrayScale image is transfered to another Color. Can anyone please tell me where is fault in the code or how can I get my desired results??
This is the code...
public class gray {
public static void main (String args[])throws IOException{
int width;
int height;
BufferedImage myImage = null;
File f = new File("E:\\eclipse\\workspace\\Graphs\\src\\ColorToGray\\1.png");
myImage = ImageIO.read(f);
width = myImage.getWidth();
height = myImage.getHeight();
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
int pixels[];
pixels = new int[width * height];
myImage.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == 0xFFFFFF) {
pixels[i] = 0x000000FF;
}
}
File f2 = new File("E:\\eclipse\\workspace\\Graphs\\src\\ColorToGray\\out 1.png");
image.setRGB(0, 0, width, height, pixels, 0, width);
ImageIO.write( image, "jpg", f2);
}
}
Image Before:
Image Before Output
Image After:
Image After Output
I looked into it, and found a bunch of problems.
First of all, when specifying the filename to save, you supply a ".png" extension, but when you call the ImageIO.write() function, you specify file type "jpg". That tends not to work very well. If you try to open up the resulting file, most programs will give you a "this is not a valid .PNG file" error. Windows explorer tries to be smart, and re-interprets the .PNG as a .JPG, but this spared you from the chance of discovering your mistake.
This takes care of the strange redness problem.
However, if you specify "png" in ImageIO.write(), you still don't get the right image. One would expect an image that looks mostly like the original, with just a few patches of blue there where bright white used to be, but instead what we get is an overall brighter version of the original image.
I do not have enough time to look into your original image to find out what is really wrong with it, but I suspect that it is actually a bright image with an alpha mask that makes it look less bright, AND there is something wrong with the way the image gets saved that strips away alpha information, thus the apparent added brightness.
So, I tried your code with another image that I know has no tricks in it, and still your code did not appear to do anything. It turns out that the ARGB format of the int values you get from myImage.getRGB(); returns 255 for "A", which means that you need to be checking for 0xFFFFFFFF, not 0x00FFFFFF.
And of course when you replace a value, you must replace it with 0xFF0000FF, specifying a full alpha value. Replacing a pixel with 0x000000FF has no visible effect, because regardless of the high blue value, alpha is zero, so the pixel would be rendered transparent.
I need to resize PNG, JPEG and GIF files. How can I do this using Java?
FWIW I just released (Apache 2, hosted on GitHub) a simple image-scaling library for Java called imgscalr (available on Maven central).
The library implements a few different approaches to image-scaling (including Chris Campbell's incremental approach with a few minor enhancements) and will either pick the most optimal approach for you if you ask it to, or give you the fastest or best looking (if you ask for that).
Usage is dead-simple, just a bunch of static methods. The simplest use-case is:
BufferedImage scaledImage = Scalr.resize(myImage, 200);
All operations maintain the image's original proportions, so in this case you are asking imgscalr to resize your image within a bounds of 200 pixels wide and 200 pixels tall and by default it will automatically select the best-looking and fastest approach for that since it wasn't specified.
I realize on the outset this looks like self-promotion (it is), but I spent my fair share of time googling this exact same subject and kept coming up with different results/approaches/thoughts/suggestions and decided to sit down and write a simple implementation that would address that 80-85% use-cases where you have an image and probably want a thumbnail for it -- either as fast as possible or as good-looking as possible (for those that have tried, you'll notice doing a Graphics.drawImage even with BICUBIC interpolation to a small enough image, it still looks like garbage).
After loading the image you can try:
BufferedImage createResizedCopy(Image originalImage,
int scaledWidth, int scaledHeight,
boolean preserveAlpha)
{
System.out.println("resizing...");
int imageType = preserveAlpha ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, imageType);
Graphics2D g = scaledBI.createGraphics();
if (preserveAlpha) {
g.setComposite(AlphaComposite.Src);
}
g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
return scaledBI;
}
Thumbnailator is an open-source image resizing library for Java with a fluent interface, distributed under the MIT license.
I wrote this library because making high-quality thumbnails in Java can be surprisingly difficult, and the resulting code could be pretty messy. With Thumbnailator, it's possible to express fairly complicated tasks using a simple fluent API.
A simple example
For a simple example, taking a image and resizing it to 100 x 100 (preserving the aspect ratio of the original image), and saving it to an file can achieved in a single statement:
Thumbnails.of("path/to/image")
.size(100, 100)
.toFile("path/to/thumbnail");
An advanced example
Performing complex resizing tasks is simplified with Thumbnailator's fluent interface.
Let's suppose we want to do the following:
take the images in a directory and,
resize them to 100 x 100, with the aspect ratio of the original image,
save them all to JPEGs with quality settings of 0.85,
where the file names are taken from the original with thumbnail. appended to the beginning
Translated to Thumbnailator, we'd be able to perform the above with the following:
Thumbnails.of(new File("path/to/directory").listFiles())
.size(100, 100)
.outputFormat("JPEG")
.outputQuality(0.85)
.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
A note about image quality and speed
This library also uses the progressive bilinear scaling method highlighted in Filthy Rich Clients by Chet Haase and Romain Guy in order to generate high-quality thumbnails while ensuring acceptable runtime performance.
You don't need a library to do this. You can do it with Java itself.
Chris Campbell has an excellent and detailed write-up on scaling images - see this article.
Chet Haase and Romain Guy also have a detailed and very informative write-up of image scaling in their book, Filthy Rich Clients.
Java Advanced Imaging is now open source, and provides the operations you need.
If you are dealing with large images or want a nice looking result it's not a trivial task in java. Simply doing it via a rescale op via Graphics2D will not create a high quality thumbnail. You can do it using JAI, but it requires more work than you would imagine to get something that looks good and JAI has a nasty habit of blowing our your JVM with OutOfMemory errors.
I suggest using ImageMagick as an external executable if you can get away with it. Its simple to use and it does the job right so that you don't have to.
If, having imagemagick installed on your maschine is an option, I recommend im4java. It is a very thin abstraction layer upon the command line interface, but does its job very well.
The Java API does not provide a standard scaling feature for images and downgrading image quality.
Because of this I tried to use cvResize from JavaCV but it seems to cause problems.
I found a good library for image scaling: simply add the dependency for "java-image-scaling" in your pom.xml.
<dependency>
<groupId>com.mortennobel</groupId>
<artifactId>java-image-scaling</artifactId>
<version>0.8.6</version>
</dependency>
In the maven repository you will get the recent version for this.
Ex. In your java program
ResampleOp resamOp = new ResampleOp(50, 40);
BufferedImage modifiedImage = resamOp.filter(originalBufferedImage, null);
You could try to use GraphicsMagick Image Processing System with im4java as a comand-line interface for Java.
There are a lot of advantages of GraphicsMagick, but one for all:
GM is used to process billions of
files at the world's largest photo
sites (e.g. Flickr and Etsy).
Image Magick has been mentioned. There is a JNI front end project called JMagick. It's not a particularly stable project (and Image Magick itself has been known to change a lot and even break compatibility). That said, we've had good experience using JMagick and a compatible version of Image Magick in a production environment to perform scaling at a high throughput, low latency rate. Speed was substantially better then with an all Java graphics library that we previously tried.
http://www.jmagick.org/index.html
Simply use Burkhard's answer but add this line after creating the graphics:
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
You could also set the value to BICUBIC, it will produce a better quality image but is a more expensive operation. There are other rendering hints you can set but I have found that interpolation produces the most notable effect.
Keep in mind if you want to zoom in in a lot, java code most likely will be very slow. I find larger images start to produce lag around 300% zoom even with all rendering hints set to optimize for speed over quality.
You can use Marvin (pure Java image processing framework) for this kind of operation:
http://marvinproject.sourceforge.net
Scale plug-in:
http://marvinproject.sourceforge.net/en/plugins/scale.html
It turns out that writing a performant scaler is not trivial. I did it once for an open source project: ImageScaler.
In principle 'java.awt.Image#getScaledInstance(int, int, int)' would do the job as well, but there is a nasty bug with this - refer to my link for details.
I have developed a solution with the freely available classes ( AnimatedGifEncoder, GifDecoder, and LWZEncoder) available for handling GIF Animation.
You can download the jgifcode jar and run the GifImageUtil class.
Link: http://www.jgifcode.com
you can use following popular product: thumbnailator
If you dont want to import imgScalr like #Riyad Kalla answer above which i tested too works fine, you can do this
taken from Peter Walser answer #Peter Walser on another issue though:
/**
* utility method to get an icon from the resources of this class
* #param name the name of the icon
* #return the icon, or null if the icon wasn't found.
*/
public Icon getIcon(String name) {
Icon icon = null;
URL url = null;
ImageIcon imgicon = null;
BufferedImage scaledImage = null;
try {
url = getClass().getResource(name);
icon = new ImageIcon(url);
if (icon == null) {
System.out.println("Couldn't find " + url);
}
BufferedImage bi = new BufferedImage(
icon.getIconWidth(),
icon.getIconHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
// paint the Icon to the BufferedImage.
icon.paintIcon(null, g, 0,0);
g.dispose();
bi = resizeImage(bi,30,30);
scaledImage = bi;// or replace with this line Scalr.resize(bi, 30,30);
imgicon = new ImageIcon(scaledImage);
} catch (Exception e) {
System.out.println("Couldn't find " + getClass().getName() + "/" + name);
e.printStackTrace();
}
return imgicon;
}
public static BufferedImage resizeImage (BufferedImage image, int areaWidth, int areaHeight) {
float scaleX = (float) areaWidth / image.getWidth();
float scaleY = (float) areaHeight / image.getHeight();
float scale = Math.min(scaleX, scaleY);
int w = Math.round(image.getWidth() * scale);
int h = Math.round(image.getHeight() * scale);
int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
boolean scaleDown = scale < 1;
if (scaleDown) {
// multi-pass bilinear div 2
int currentW = image.getWidth();
int currentH = image.getHeight();
BufferedImage resized = image;
while (currentW > w || currentH > h) {
currentW = Math.max(w, currentW / 2);
currentH = Math.max(h, currentH / 2);
BufferedImage temp = new BufferedImage(currentW, currentH, type);
Graphics2D g2 = temp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(resized, 0, 0, currentW, currentH, null);
g2.dispose();
resized = temp;
}
return resized;
} else {
Object hint = scale > 2 ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_BILINEAR;
BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resized.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(image, 0, 0, w, h, null);
g2.dispose();
return resized;
}
}
Try this folowing method :
ImageIcon icon = new ImageIcon("image.png");
Image img = icon.getImage();
Image newImg = img.getScaledInstance(350, 350, java.evt.Image.SCALE_SMOOTH);
icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, "image on The frame", "Display Image", JOptionPane.INFORMATION_MESSAGE, icon);
you can also use
Process p = Runtime.getRuntime().exec("convert " + origPath + " -resize 75% -quality 70 " + largePath + "");
p.waitFor();
Design jLabel first:
JLabel label1 = new JLabel("");
label1.setHorizontalAlignment(SwingConstants.CENTER);
label1.setBounds(628, 28, 169, 125);
frame1.getContentPane().add(label1); //frame1 = "Jframe name"
Then you can code below code(add your own height and width):
ImageIcon imageIcon1 = new ImageIcon(new ImageIcon("add location url").getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT)); //100, 100 add your own size
label1.setIcon(imageIcon1);
** Important update, see below! **
I am creating a program that changes the pixels of a BufferedImage to a certain color when that pixel fulfills a set of conditions in Java. However, when I write the image to disk, the pixels that should be colored are instead black.
First I define the color, using RGB codes:
Color purple = new Color(82, 0, 99);
int PURPLE = purple.getRGB();
Then I read the image I want to alter from a File into a BufferedImage called "blank":
BufferedImage blank = ImageIO.read(new File("some path"));
Now, loop through the pixels, and when a pixel at location (x, y) matches a criteria, change its color to purple:
blank.setRGB(x, y, PURPLE);
Now, write "blank" to the disk.
File output = new File("some other path");
ImageIO.write(blankIn, "png", output); // try-catch blocks intentionally left out
The resulting file should be "blank" with some purple pixels, but the pixels in question are instead black. I know for a fact that the issue is with setRGB and NOT any import or export functions, because "blank" itself is a color image, and gets written to file as such. I read around and saw a lot of posts recommending that I use Graphics2D and to avoid setRGB, but with no discussion of pixel-by-pixel color changing.
I also tried direct bit manipulation, like this:
blank.setRGB(x, y, ((82 << 16) + (0 << 8) + 99));
I'm probably doing that wrong, but if I put it in correctly it wouldn't matter, because the pixels are getting set to transparent when I do this (regardless of what the numbers say, which is very strange, to say the least).
** When I try this:
blank.setRGB(x, y, Color.RED.getRGB());
My output file is grayscale, so that means setRGB is, in fact, modifying my picture in grayscale. I think this is actually a rather simple issue, but the solution eludes me.
Based on the insights in https://stackoverflow.com/a/21981173 that you found yourself ... (a few minutes after posting the question) ... it seems that it should be sufficient to simply convert the image into ARGB directly after it was loaded:
public static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
The original image that was imported into Java was actually grayscale, so when Java read it into the BufferedImage, it simply imported it as a grayscale BufferedImage. By adding a very small but imperceptible colored dot in the corner of my image, I was able to get Java to output a correctly colored image.
Unfortunately, this is only a half solution, because I do not know how to fix this programmatically.
SOLUTION:
Convert the BufferedImage from grayscale to ARGB with this snippet:
BufferedImage blank2 = blank;
// Create temporary copy of blank
blank = new BufferedImage(blank.getWidth(), blank.getHeight(), BufferedImage.TYPE_INT_ARGB);
// Recreate blank as an ARGB BufferedImage
ColorConvertOp convert = new ColorConvertOp(null);
// Now create a ColorConvertOp object
convert.filter(blank2, blank);
// Convert blank2 to the blank color scheme and hold it in blank
You want to add this right after blank = ImageIO.read(new File("some path")).
I am a relative newbie in game programming. I know how to draw pixels to a BufferedImage using setPixel(). It is horribly slow on larger formats so I moved on and found VolatileImage (took me a week or so). It is fairly easy to draw lines, strings, rects, etc but I can't draw individual pixels. I already tried using drawLine(x,y,x,y) but I get 3-4 FPS on an 800x600 image.
The fact that java didn't include setPixel() or setRGB() in the VolatileImage makes me pretty angry and confused.
I have 4 questions:
Is there a way to draw individual pixels on a VolatileImage? (on 1440x900 formats with FPS > 40)
Can I draw pixels in a BufferedImage with a faster method? (same 1440x900, FPS > 40)
Is there any other way to draw pixels fast enough for 3D games?
Can I make my BufferedImage hardware accelerated( tried using setAccelerationPriority(1F) but it doesn't work)
Please if you have any idea tell me. I can't continue making my game wihout this information. I already made 3D rendering algorithms but i need to be able to draw fast pixels. I have got a good feeling about this game.
Here's the code if it can help you help me:
public static void drawImageRendered (int x, int y, int w, int h) { // This is just a method to test the performance
int a[] = new int[3]; // The array containing R, G and B value for each pixel
bImg = Launcher.contObj.getGraphicsConfiguration().createCompatibleImage(800, 600); // Creates a compatible image for the JPanel object i am working with (800x600)
bImg.setAccelerationPriority(1F); // I am trying to get this image accelerated
WritableRaster wr = bImg.getRaster(); // The image's writable raster
for (int i = 0; i < bImg.getWidth(); i++) {
for (int j = 0; j < bImg.getHeight(); j++) {
a[0] = i % 256;
a[2] = j % 256;
a[1] = (j * i) % 256;
wr.setPixel(i, j, a); // Sets the pixels (You get a nice pattern)
}
}
g.drawImage(bImg, x, y, w, h, null);
}
I would much prefer not using OpenGL or any other external libraries, just plain Java.
Well you're basically drawing one pixel after the other using the CPU. There's no way that this can be accelerated, thus such a method does simply not make any sense for a VolatileImage. The low FPS you get suggest that this even causes a significant overhead, as each pixel drawing operation is sent to the graphics card (with information such as location & colour), which takes longer than to modify 3 or 4 bytes of RAM.
I suggest to either stop drawing each pixel separately or to figure out a way to make your drawing algorithm run directly on the graphics card (which most likely requires another language than Java).
It's been over 4 years since this post got an answer. I was looking for an answer to this question as well and stumbled on this post. After some more searching, I got it to work. Below I'll post the source to rendering pixels with a VolatileImage.
It seems Java hides our ability to plot pixels directly to a VolatileImage, but we can draw buffered images to it. For good reason. Using the software to plot a pixel doesn't really help with acceleration(in Java it seems). If you can plot pixels to a BufferedImage, and then render it on a VolatileImage, you may get a speed bonus since it's hardware accelerated from that point.
The source down below is a self-contained example. You can copy-pasta practically all of it to your project and run it.
https://github.com/Miekpeeps/JavaSnippets-repo/blob/master/src/graphics_rendering/pixels_03/PlottingVolatile.java
In the constructor I save the Graphics environment of the app/game.
private GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
private GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
Then, when I call a method to enable hardware we create a buffer. I set the transparency to Opaque. In my little engine, I deal with transparency/alpha blending on another thread in the pipeline.
public void setHardwareAcceleration(Boolean hw)
{
useHW = hw;
if (hw)
{
vbuffer = gc.createCompatibleVolatileImage(width, height, Transparency.OPAQUE);
System.setProperty("sun.java2d.opengl", hw.toString()); // may not be needed.
}
}
For each frame I update, I get the Graphics from the VolatileImage and render my buffer there. Nothing gets rendered if I dont flush().
#Override
public void paintComponent(Graphics g)
{
if(useHW)
{
g = vbuffer.getGraphics();
g.drawImage(buffer, 0, 0, null);
vbuffer.flush();
}
else
{
g.drawImage(buffer, 0, 0, null);
buffer.flush();
}
}
There is still a little bit of overhead when calling to plot a pixel on the BufferedImage writable raster. But when we update the screen, we get a speed boost when using the Volatile image instead of using the Buffered image.
Hope this helps some folks out. Cheers.