This is a question, wether I am using the right way or not. And by the right way, I mean, is there a more memory-efficient way to do this?
I use 1 image source:
String imgurl = "imgreadertest8.png";
BufferedImage loadedimage = ImageIO.read(getClass().getResource(imgurl));
BufferedImage image = ImageIO.read(getClass().getResource(imgurl));
I draw the image in a paint() method. But sometimes I need to set the brightness:
public void setBrightness(float brightness)
{
RescaleOp rescaleOp = new RescaleOp(brightness, 15, null);
rescaleOp.filter(loadedimage, image);
}
So I read the same image, two times. One to have a 'final' BufferedImage (it's not really final... But I cannot use final in this context, because it needs to be used outside the constructor (which you can't see here, but it is)) which is used as template, and one to edit and draw.
More information: Before trying this way, I did this:
String imgurl = "imgreadertest8.png";
BufferedImage loadedimage = ImageIO.read(getClass().getResource(imgurl));
BufferedImage image = loadedimage;
But when I edit the image, it edits the loadedimage as well. Pretty much because they're the same object.
So... Is there another way to set the brightness of this image, WITHOUT loading the same image two times?
The following would copy an image.
public static BufferedImage copy(BufferedImage img) {
Hashtable<?,?> properties = new Hashtable<>();
for (String propertyName : img.getPropertyNames()) {
properties.put(propertyName, img.getProperty(propertyName));
}
return new BufferedImage(img.getColorModel(),
img.copyData(null),
img.isAlphaPremultiplied(), properties);
}
The same thing can be achieved slower but simplier by creating an empty image, drawing in the createGraphics of it (not to forget dispose). If you need a Graphics2D to process the image, that might be an option too.
Related
so I'm in a bit of a pickle. I know how to set the alpha value of a bitmap in android. What I don't know how to do is make is reversible. So, let's say someone wanted to set the alpha of an image to 50%, so they do. Now lets say they wanted to set it 75% (keep in mind, this is of the original image alpha value). Currently, what I have is a function that will set the alpha value of the current image, so it would be 75% of the 50% alpha value if that makes sense. How can I make it so that it accounts for the original image?
public Pixmap setAlpha(float newAlpha) { //integer between 0-100
if (newAlpha != alpha) { //to check if the current alpha value of the image is equal to your desired alpha. to avoid always halving you alpha value
float test = newAlpha/100.0f;
float test2 = test * 255;
alpha = test2;
Bitmap newBM = Bitmap.createBitmap(backupImg.getBitmap().getWidth(),backupImg.getBitmap().getHeight(), Bitmap.Config.ARGB_8888);
Canvas cc = new Canvas(newBM);
cc.drawARGB(0,0,0,0);
Paint newPaint = new Paint();
newPaint.setAlpha((int)test2);
cc.drawBitmap(backupImg.getBitmap(), 0, 0, newPaint);
img.setBitmap(newBM);
return img;
} else {
return img;
}
}
The Pixmap part is just a custom Bitmap class. backupImg is just a copy of img, created in the constructor of the object this function belongs to.
please keep in mind that this will be a canvas based bitmap. If I recall correctly imageView's aren't drawn on the canvas? So, as a further example. Imagine a sprite drawn to the canvas that you want to alter the alpha of. So you do it using the function I've posted. Now, let's say you want to undo the changes and restore it to the sprite's original alpha, of some other value. Well, you can't because the alpha value of the image has been changed permanently. What I want to do is store reference to the original image with another variable, and refer to that whenever I need to adjust the alpha value of the image. Hopefully that makes sense
Why don't you set alpha to the ImageView instead of setting it to a bitmap.
By setting alpha to the ImageView or say any view you can reverse it easily.
Refer to this answer in order to do it.
You can do it from xml. Just add the below line in the imageview tag:-
android:alpha="0.5"
You can set alpha between 0 to 1. Above line will set alpha to half that is 0.5.
For bitmap:
Bitmap newBitmap = Bitmap.createBitmap(originalBitmap.getWidth(),
originalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint alphaPaint = new Paint();
alphaPaint.setAlpha(75);
canvas.drawBitmap(originalBitmap, 0, 0, alphaPaint);
I've solved the problem myself. So, there is nothing wrong with the function I wrote. The problem instead lies within how Java uses pointers. because everything is passed via reference, I was actually referencing the same object, rather than creating two separate objects. So instead of:
Bitmap oldBM = new Bitmap();
Bitmap newBM = oldBM;
you would instead want to do
Bitmap oldBM = new Bitmap();
Bitmap newBM = new Bitmap(using old bitmap's value);
I've been spending the last hours trying to solve the stack trace below. With major research on here SO and also through Google, I understand the exception can mean several things:
the program can't find the requested images with the provided path;
the images are being rendered after the width and the height are generated, reason why it equals 0...
Am I missing something? I can't figure out how to solve this...
Stack
Exception in thread "main" java.lang.IllegalArgumentException: Width
(-1) and height (-1) cannot be <= 0 at
java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1016)
at java.awt.image.BufferedImage.(BufferedImage.java:331) at
tp6.Interface.toBufferedImage(Interface.java:157) at
tp6.Interface.(Interface.java:36) at
tp6.Interface.main(Interface.java:171)
tp6.Interface.toBufferedImage(Interface.java:157):
public BufferedImage toBufferedImage(Image image) {
if( image instanceof BufferedImage ) {
return( (BufferedImage)image );
} else {
image = new ImageIcon(image).getImage();
BufferedImage bufferedImage = new BufferedImage(
image.getWidth(null),
image.getHeight(null),
BufferedImage.TYPE_INT_RGB );
Graphics g = bufferedImage.createGraphics();
g.drawImage(image,0,0,null);
g.dispose();
return( bufferedImage );
}
}
tp6.Interface.(Interface.java:36)
//IMAGE JPANEL
Image map=new ImageIcon("images/main.gif").getImage();
Image digi=new ImageIcon("images/digits.gif").getImage();
BufferedImage mapmodifiable= toBufferedImage(map);
BufferedImage digits= toBufferedImage(digi);
tp6.Interface.main(Interface.java:171)
public static void main(String[] args)
{
Window windowintro = new Window( 440, 400, 1);
//INTERFACE GRAPHIC
Interface graphic=new Interface();
graphic.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
}
The reason for the exception has already been explained, Image methods getWidth(null) and getHeight(null) both return -1 when the image dimensions are not (yet) known. This is implemented so, because the old Image API in Java is asynchronous and loads image resources off the current thread. As you write, it could also happen because the image is not found.
However, as you want to use your images as BufferedImages (presumably because you want to modify them at some stage), it's better and easier to just load them using the more recent synchronous ImageIO API. In most cases, the code will be clearer and easier to understand, and more importantly; you'll get error messages right away if the image can't be found/loaded.
So, instead of:
Image map = new ImageIcon("images/main.gif").getImage();
BufferedImage mapmodifiable = toBufferedImage(map);
You can simply do:
BufferedImage mapmodifiable = ImageIO.read(new File("images/main.gif"));
PS: It is possible to convert an Image to a BufferedImage like you do in your toBufferedImage method, and using ImageIcon.getImage(..) should ensure the image was preloaded (ImageIcon internally uses a MediaTracker for preloading). However, as I say above, the old Image API is not very good at error feedback, so most likely the problem is that your image isn't found.
I was having this problem too. I solved it by adding one line of code. In your in the first line of your toBufferedImage() method you can put
while(image.getWidth() == -1);
This line will just keep looping until there is a value in getWidth() besides -1.
I found this question concerning your problem. Maybe you are using an asynchronous way to load the images. This mean the image may not be loaded yet, when you are calling getWidth(null) or getHeight(null). Since the image may not be loaded at this time, the width and height may not be known yet. This is the reason why -1 is returned.
Maybe you will get the right result if you add some delay with Thread.sleep(1000). I did not investigate it but it is definitively not a good solution. You may sleep not long enough on some systems. On other systems you may sleep unnecessary long. And since I do not know the specification very well, it may even be a valid implementation of Java if Thread.sleep blocks the process from reading the image (Does someone know it?).
I would see two ways which could be used to solve the problem:
First solution
One solution would be to load the image with blocking IO. Just like descripted in the answers of the linked question.
Second solution
Another solution would be to use an ImageObserver:
int width = getWidth(new ImageObserver() {
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
// TODO use the width when the image is loaded and the size is known
}
});
if (width != -1) {
// TODO use the width, it is already known
}
So I have an assignment where I need to create a catalog.
The catalog needs to have a list, an image and a description.
My entire code works, so I have no issue with the coding as such.
I do have an issue with the image size.
How do I take care of images on a java gui program to make them all into one size when it is running.
Please let me know :D
When you read in an image, create a new BufferedImage that is the exact size that you desire, get it's Graphics object via getGraphics(), draw the original image into the new image using Graphics#drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) where x and y are 0 and width and height are from the dimensions of the new image, dispose() of the Graphics object, and then display the new Image as an ImageIcon in a JLabel. Make sure though that the original image is the same size or larger than the new one, else your images will look gawd-awful.
For example, and note that this code may not be exactly correct since I don't have my IDE up:
BufferedImage originalImage = ImageIO.read(something); // read in original image
// create new empty image of desired size
BufferedImage newImage = new BufferedImage(desiredWidth, desiredHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = newImage.getGraphics(); // get its graphics object
// draw old image into new image
g.drawImage(originalImage, 0, 0, desiredWidth, desiredHeight, null);
g.dispose(); // get rid of Graphics object
// create ImageIcon and put in JLabel to display
Icon newIcon = new ImageIcon(newImage);
myJLabel.setIcon(newIcon);
I would propably create a JPanel to draw on one Image, and then work with the method:
myPanel.setSize(new Dimension(x,y))
or
myPanel.setPreferredSize(new Dimension....)
There is a method (image = imgobj.getScaledInstance(width, height, hints)) in awt.Image class which provides re-sizing capabilities very nicely, I always use this to re-size my images when I need. Please see here some examples :-), I hope it will work for you, it is the most convenient way to scale images I have ever seen. create a method pass the image to the method and size of the image you want and return the image back in return to reuse the code ;)
I am trying to use the underlying DataBufferByte of a BufferedImage of type TYPE_3BYTE_BGR to set pixel values as quick as possible.
Perhaps I am not understanding, but when I do the following...
byte[] imgBytes = ((DataBufferByte) img.getData().getDataBuffer()).getData();
... it seems as though I am getting a copy of the byte[] and not a reference. For example, if I run...
System.out.println(System.identityHashCode(imgBytes));
System.out.println(System.identityHashCode((DataBufferByte) img.getData().getDataBuffer()).getData());
... I get two clearly different object hashes. If I'm not mistaken, this indicates that I am not getting a reference to the underlying byte[] but rather a copy. If this is the case, how am I supposed to edit the DataBufferByte directly???
Or perhaps I am just setting the pixels wrong... When I set pixels in the imgBytes it doesn't seem to do anything to the BufferedImage. Once I get the byte[], I set each pixel value like so:
imgBytes[intOffset] = byteBlue;
imgBytes[intOffset+1] = byteGreen;
imgBytes[intOffset+2] = byteRed;
To me, this all seems fine. I can read pixels just fine this way so it seems I should be able to write them the same way!
I had the same problem. You may not use getData() but use getRaster() which gives you an array you can use to write to.
I once played around with pixel manipulations for Images in Java. Instead of directly answering your question I will offer an alternative solution to your problem. You can do the following to create an array of pixels to manipulate:
final int width = 800;
final int height = 600;
final int[] pixels = new int[width * height]; // 0xAARRGGBB
MemoryImageSource source = new MemoryImageSource(width, height, pixels, 0, width);
source.setAnimated(true);
source.setFullBufferUpdates(true);
Image image = Toolkit.getDefaultToolkit().createImage(source);
image.setAccelerationPriority(1f);
Then to draw the image, you can simply call the drawImage method from the Graphics class.
There are a few other ways to achieve what you are looking for, but this method was the simplest to me.
Here is how it's implemented in JDK7. You may have an error somewhere else if the stuff doesn't work for you.
public byte[] getData() {
theTrackable.setUntrackable();
return data;
}
I'm processing a bunch of images with some framework, and all I'm given is a bunch of BufferedImage objects. Unfortunately, these images are really dim, and I'd like to brighten them up and adjust the contrast a little.
Something like:
BufferedImage image = something.getImage();
image = new Brighten(image).brighten(0.3); // for 30%
image = new Contrast(image).contrast(0.3);
// ...
Any ideas?
That was easy, actually.
RescaleOp rescaleOp = new RescaleOp(1.2f, 15, null);
rescaleOp.filter(image, image); // Source and destination are the same.
A scaleFactor of 1.2 and offset of 15 seems to make the image about a stop brighter.
Yay!
Read more in the docs for RescaleOp.