to my understanding the following code
int [] pixels = image.getRaster().getPixels(0, 0, width, height, (int[])null);
should generate an array which has exactly the size width x height, but in practice it seems to be much larger, why?
image may be a bufferedimage, toolkitimage or volatileimage.
it generates a array with
new int[numBands * w * h]; // The number of bands of the image data.
from the SampleModel from your Raster
Related
I have an int array where each value stores a bitpacked rgb value (8 bits per channel) and alpha is always 255(opaque) and i want to display that in javafx.
My current approach is using a canvas like this:
GraphicsContext graphics = canvas.getGraphicsContext2D();
PixelWriter pw = graphics.getPixelWriter();
pw.setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), pixels, 0, width);
However before that i actually have to set the alpha component of each pixel by iterating each pixel and OR'ing it with a mask that turns the pixel from rgb to argb like this:
for (int i = 0; i < pixels.length; i++) {
pixels[i] = 0xFF000000 | pixels[i];
}
Is there a more efficient to do this (as the pixels array is updated many times every second)?
I was hoping there's a IntRgbInstance but unfortunately there isn't (only ByteRgbInstance)
Other approaches i've tested:
Approach 1: Creating a IntBuffer that is filled up like this:
IntBuffer buffer = IntBuffer.allocate(pixels.length * 4);
for (int pixel : pixels) {
buffer.put(0xFF000000 | pixel);
}
And then generating a PixelBuffer that uses this buffer, the pixel buffer is then used as an input to this WritableImage constructor: https://openjfx.io/javadoc/17/javafx.graphics/javafx/scene/image/WritableImage.html#%3Cinit%3E(javafx.scene.image.PixelBuffer)
and then i display that WritableImage using a ImageView
This however still didn't speed up anything(rather made it a bit slower) and im guessing that because i have to construct a new WritableImage instance each time the pixels int array is updated.
Approach 2 (that didn't work for some reason, i.e. it displayed nothing in the screen): Creating a buffer the same way as above and using that in one of the setPixels() methods that takes in a buffer:
IntBuffer buffer = IntBuffer.allocate(pixels.length * 4);
for (int pixel : pixels) {
buffer.put(0xFF000000 | pixel);
}
pw.setPixels(0, 0, width, height, PixelFormat.getIntArgbInstance(), buffer, width);
After a bit of more research i found out that i don't need to create a new WritableImage instance each time the pixels array is updated but i can just use the updateBuffer method here: https://openjfx.io/javadoc/17/javafx.graphics/javafx/scene/image/PixelBuffer.html#updateBuffer(javafx.util.Callback)
So the code currently looks like this:
pb.updateBuffer(callback -> {
buffer.clear();
for (int pixel : pixels) {
buffer.put(0xFF000000 | pixel);
}
return null;
});
Where pb, buffer is only created once like this:
IntBuffer buffer = IntBuffer.allocate(pixels.length * 4);
PixelBuffer<IntBuffer> pb = new PixelBuffer<>(width, height, buffer, PixelFormat.getIntArgbPreInstance());
view.setImage(new WritableImage(pb));
and this did indeed result in a nice speedup (close to 2x compared to my initial approach)
Maybe this https://openjfx.io/javadoc/17/javafx.graphics/javafx/scene/image/WritableImage.html#%3Cinit%3E(javafx.scene.image.PixelBuffer) is what you are looking for. You could create a PixelBuffer from an IntBuffer of your data.
I'm looking for the fastest way to write pixels on javafx.scene.image.Image. Writing to BufferedImage's backing array is much faster. At least on the test image I made it took only ~20ms for BufferedImage, WritableImage on the other hand took ~100ms. I already tried SwingFXUtils but no luck.
Code for BufferedImage (faster):
BufferedImage bi = createCompatibleImage( width, height );
WritableRaster raster = bi.getRaster();
DataBufferInt dataBuffer = (DataBufferInt) raster.getDataBuffer();
System.arraycopy( pixels, 0, dataBuffer.getData(), 0, pixels.length );
Code for WritableImage (slower):
WritableImage wi = new WritableImage( width, height );
PixelWriter pw = wi.getPixelWriter();
WritablePixelFormat<IntBuffer> pf = WritablePixelFormat.getIntArgbInstance();
pw.setPixels( 0, 0, width, height, pf, pixels, 0, width );
Maybe there's a way to write to WritableImage's backing array too?
For the performance of the pixel writer it is absolutely crucial that you pick the right pixel format. You can check what the native pixel format is via
pw.getPixelFormat().getType()
On my Mac this is PixelFormat.Type.BYTE_BGRA_PRE. If your raw data conforms to this pixel format, then the transfer to the image should be pretty fast. Otherwise the pixel data has to be converted and that takes some time.
I would like to create a BufferedImage with certin width, height (800x600) and bit depth of 24 and I would like image background to be transparent.
How do I do that?
Thanking you.
You use the constructor and pass a suitable format parameter:
new BufferedImage(w, h, BufferedImage.TYPE_?);
where TYPE_? is on of the constants that define a format with alpha channel, like TYPE_INT_ARGB.
Edit: Straight from BufferedImage
/**
* Represents an image with 8-bit RGBA color components packed into
* integer pixels. The image has a <code>DirectColorModel</code>
* with alpha. The color data in this image is considered not to be
* premultiplied with alpha. When this type is used as the
* <code>imageType</code> argument to a <code>BufferedImage</code>
* constructor, the created image is consistent with images
* created in the JDK1.1 and earlier releases.
*/
public static final int TYPE_INT_ARGB = 2;
These are the options you have with BufferedImage:
BufferedImage(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable<?,?> properties)
BufferedImage(int width, int height, int imageType)
BufferedImage(int width, int height, int imageType, IndexColorModel cm)
I want to make sure that an image in my application is not more than 200x200 px and the image size is not more than 150 kB. For example, if the file size of image is more than 150 kB i need to make it 150 kB. The image can be of type jpeg,png etc.
I have the following code for resizing an image to a given width and height
private BufferedImage resize(BufferedImage img, int newW, int newH) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
Graphics2D g = dimg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
g.dispose();
return dimg;
}
But im not sure how to go about reducing the file size to 150 kB. How to do that in java ?.Some example would be really appreciated.
Thank You
Just as an option - image magic - it also has some convenience wrappers for Java, so you can easily use it.
Does your question have any practical relevance or is it just theoretical?
A 200x200 pixel image with a colour depth of 24 bit will uncompressed require 117kB. If you use any reasonable JPEG encoder, it will also never exceed 150kB for such an image.
You can only rezise the image multiple times, to get below the determined file size.
I have found a good way to draw text in openGL by creating a Bitmap, drawing text on it, and making it into a texture.
I have screenCoordinates(320,~~480) and gameCoordinates(20,28), in which quads' dimensions are defined. Example constructor: GameObject(x, y, width, height, text).
To create the bitmap I create a Canvas with this size:
// get width and height in px (p is a Paint() object)
p.setTextSize(textSize * Main.getMainResources().getDisplayMetrics().density);
final int width = (int) p.measureText(text);
final int height = (int) (p.descent() + -p.ascent());
canvas.drawText(text, 0, -p.ascent(), p);
Create a bitmap of this, scaled to the next power of 2, and you have your texture-bitmap.
However the problem is that because the quad's dimensions are not adapted to the bitmap(text) dimensions, the text isn't very smooth. I have the possibility to change the quad's dimensions with quad.setDimensions(width, height), but what would be the size I'd have to give it, and also shouldn't I also have to worry about the textSize(sp) of the text i'm drawing?
This all is in 2D, and some simple math's might get involved, but how should I get the right dimensions?