I'm trying to change the transparency of an image over time, and I'm doing this with the method drawImage() from java.awt.Graphics. I know there's a lot of different answers online about how to do this, but I can't find a one that is simple enough for me to understand and implement.
Let's just say I have a BufferedImage image, and I want to draw this image with a 50% opacity. How would I initialize image and what would I do to adjust the alpha level of the image when I draw it. It would be great if I could use the method drawImage() and do something with that to change the transparency of the image, but it's probably not that simple.
Never tried it, but I think the basic code would be:
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(ac);
g2d.drawImage(...);
Using image filter.
float[] scales = { 1f, 1f, 1f, 0.1f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);
g2d.drawImage(buffimg, rop, 0, 0);
4th element in scales array is transparency, this value goes between 0 - 1
Answer by camickr will make the entire component apply the alpha including all inner components. But that will be much faster.
Warning: Use Image Filters with Care
ref: http://www.informit.com/articles/article.aspx?p=1013851&seqNum=2
Related
My question is: a bitmap had to be square or is possible delete the invisible parts around the colored image? I have a bitmap in a SurfaceView with an hand in the center and i want calculate the bounds of this hand deleting the invisible around it, cause i have problems with the onClick Method. Without calculate every X and Y, is possible know the bounds of the hand with a Method or other things? Thanks in advance.
The best thing you could do would be to take the picture of the hand and crop it down to the size of the hand.
This way, you'll have a smaller file and won't have to implement some kind of code work around.
About your first question: any bitmap that retains some level of transparency has to have an alpha channel so in your case a hand has an alpha channel thus you cannot just delete those alpha pixels, because if you do the transparent part will remain black. You will have to use either ARGB_4444 format or ARGB_8888 format to retain this alpha channel. As far as getting the bitmaps bounds, use a Rect or some bounding shape maybe an oval, to accurately know if your finger is touching it, you can't just know exactly if your touch is within the bounds of this hand because this hand image, at certain portions contain different widths, and heights, however you can test if your touch is touching your hand exactly by using pixel perfect collision. Here is how it works:
class drawingView extends View {
Rect rect = new Rect();
Bitmap bitmap = yourHand;
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, x, y, null);
rect.set(x, y, bitmap.getWidth(), bitmap.getHeight());
invalidate();
}
public void onClick() {
if(rect.contains(event.getX(), event.getY(), && bitmap.getPixel(event.getX() - rect.left, event.getY() - rect.top) != Color.TRANSPARENT) {
// you know you exactly touched the hand even out of the transparent region
}
}
I started you off I'm sure you will understand what's going on here.
Hope this helps :)
The idea is to use a png file with the right transparency in place, which means the part around the hand should have the transparency .
Now for retaining the transparency of the image when using the BitmapFactory,make sure that your image is pulled in as ARGB_8888
I posted a question about drawing text onto a pixmap to generate dynamic textures at runtime, and that problem was resolved here
Is there an easy way that I can tint the font that I copy to a new pixmap, or do I need to do it manually pixel by pixel? I can't find anything obvious in the libgdx library or on Google.
My current process is:
create a new pixmap
draw a character onto that pixmap
convert the pixmap into a texture for rendering.
At some point in that process I'm like to be able to tint the font at runtime.
Code updated to test Jyro117's answer. (Not working, see image below)
// Set up the base image
Pixmap newPixmap = new Pixmap(backImage.getWidth(),
backImage.getHeight(), Format.RGBA8888);
newPixmap.drawPixmap(backImage, 0, 0);
// Copy out the letter from our fontsheet
// and draw the char to a new pixmap
Glyph glyph = fontData.getGlyph(getLetterToDraw().charAt(0));
Pixmap charPixmap = new Pixmap(glyph.width, glyph.height,
Format.RGBA8888);
charPixmap.drawPixmap(fontPixmap, 0, 0, glyph.srcX, glyph.srcY,
glyph.width, glyph.height);
// try to tint it?!
charPixmap.setColor(1, 0, 0, 100);
charPixmap.fillRectangle(0, 0, newPixmap.getWidth(),
newPixmap.getHeight());
// copy the character to our new bitmap
newPixmap.drawPixmap(charPixmap, (BLOCK_SIZE - glyph.width) / 2,
(BLOCK_SIZE - glyph.height) / 2);
texture = new Texture(newPixmap);
newPixmap.dispose();
charPixmap.dispose();
Pixmap has all the functions you need to tint it.
Get a pixmap of the font (in your case fontPixmap)
Make sure alpha blending is enabled.
Set the color, (setting alpha to the desired level)
Draw rectangle over entire font image (0,0,width,height)
Now the pixmap of your font has the desired tinting
Hopefully that clears it up for you.
Edit:
Alright, so I see what exactly you mean, it would appear there is no way by default to change the 'type' of blending for Pixmap. At least without me looking into the native code under the covers. BitmapFont gets around this by using vertex data for the glyph and renders each one as a shape instead of image. So in order to do this you will have to do it with two passes:
First we fill the pixmap with the desired color and alpha (note: the values for this are between 0f and 1f, so your alpha should be something like 0.3f not 100)
Next you iterate over all pixels of the image and are looking for pixels which have their alpha value below a certain threshold (should be around 0.3f). Then you could either set the alpha of those pixels below the threshold to 0, or maybe do something fancier like Math.pow(alpha, 3), etc. Basically the alpha you just added to the image (using source over blending) you want to reduce/remove, but only for pixels which are supposed to be transparent. You may have to play around with different values or reduction techniques to get it looking correctly.
Note: You can iterate over the pixel data by calling getPixel() or get the Bytebuffer with getPixels() and the 4th byte will be the pixel's alpha value between 0 and 255.
Edit 2:
I just thought of an alternative way to do this is to use a Frame Buffer.
Render your font texture onto the frame buffer.
Draw a colored rectangle with alpha over the entire frame buffer using the proper alpha blending function (Not sure which one off the top of my head but you want one which only applies if dst has an alpha greater than 0).
Grab the texture from the frame buffer and use that as your tinted font.
For a Java program ("regular" program, not Applet and/or Android etc.), I have PNGs with transparency which are to be loaded and drawn atop other pictures, which is working perfectly fine. Now, for the case that there is no other picture below them (the determination of whether that is the case is not an issue, the respective code is already workingly implemented), I want them made opaque, but not in the sense of simply setting a background color; since the png source files concerned are fully colored, just with reduced opacity (alpha value) on them, it would, I guess, be no problem to simply increase the opacity of all the colors to 100%, I just don't know the commands to do it. Preferred and so far targeted way of doing it is by having them drawn in a BufferedImage, then modified respectively, but as said: How?
TLDR: images from .png with <100% opacity (same value on whole pic), how to make opaque in Java, preferrably with BufferedImage or even simpler
thanks in advance for any answers
You can try RescaleOp to scale the alpha component all the way to opaque:
float[] scales = { 1f, 1f, 1f, 0f }; // R, G, B, A
float[] offsets = {0f, 0f, 0f, 255f}; // R, G, B, A
RescaleOp rescaler = new RescaleOp(scales, offsets, null);
BufferedImage opaque = rescaler.filter(original, null);
See this Java 2D tutorial for more info.
I've got this picture:
Imagine this as a 4x4 tile world of which the tiles are 32x32 pixels each.
then take a look at picture:
Look at the stone tile, it has edges which are outside the grid. Can I use a bufferedImage for this or do I need to do something else to make this work?
If so, could you help me with it by explain it because I've got no clue on how to achieve this is my game?
another example picture:
Let me explain it More Clearly... 1st image = Grid, 2nd Image = Tile Overlapping the grid... (thats what i want to have because then it's a new tile which I can use to make my game look better!), 3d Image = An example of how it would tile!
To completely eliminate boundary artifact, you can use Penrose tiles.
You can mitigate the edge artifact using anti-aliasing. This example uses TexturePaint with three variant shades of each color: original, darker and lighter. You can experiment with a larger number of shades for better results.
Simply use PhotoShop to edit the image to 32x32 pixel size....then use it in your game....
/////////////EDITED//////////////////
As shown here, AffineTransformOp offers the additional flexibility of choosing the interpolation type.
BufferedImage before = getBufferedImage(encoded);
int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(2.0, 2.0);
AffineTransformOp scaleOp =
new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);
One attempted approach was to use TexturePaint and g.fillRect() to paint the image. This however requires you to create a new TexturePaint and Rectangle2D object each time you paint an image, which isn't ideal - and doesn't help anyway.
When I use g.drawImage(BufferedImage,...), the rotated images appear to be blurred/soft.
I'm familiar with RenderingHints and double-buffering (which is what I'm doing, I think), I just find it difficult to believe that you can't easily and efficiently rotate an image in Java that produces sharp results.
Code for using TexturePaint looks something like this.
Grahics2D g2d = (Graphics2D)g;
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);
I'm using AffineTransform to rotate a hand of cards into a fan.
What would be the best approach to paint good-looking images quickly?
Here is a screenshot:
The 9 is crisp but the rest of the cards are definitely not as sharp.
It could be possible that the problem lies in when I create each card image and store it in an array.
Here's how I'm doing it at the moment:
// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);
Graphics2D g = img.createGraphics();
setRenderingHints(g);
g.drawImage(shadow, 0, 0, 86, 126, null);
g.drawImage(white, 3, 3, 80, 120, null);
g.drawImage(suit, 3, 3, 80, 120, null);
g.drawImage(value, 3, 3, 80, 120, null);
g.dispose();
cardImages[i] = img;
}
private void setRenderingHints(Graphics2D g){
g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}
How should I approach this differently?
Thanks.
Edit:
Without RenderingHints
Setting AA hints made no difference. Also, setting RenderingHints when creating the images makes no difference either. It's only when they are being rotated with AffineTransform and painted using g.drawImage(...) that they seem to blur. The image above shows the difference between default (nearest neighbor) and bilinear interpolation.
Here is how I'm currently painting them (much faster than TexturePaint):
// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
}
// ImageLoader.java
public BufferedImage getCard(int code){
return cardImages[code];
}
All my cards are 80x120 and the shadow .png is 86x126, so as to leave 3px semi-transparent shadow around the card. It's not a realistic shadow I know, but it looks okay.
And so the question becomes... How can you produce sharp paint results when rotating a BufferedImage?
Reference to a previous question also regarding a fanned card hand:
How can you detect a mouse-click event on an Image object in Java?
Bounty-Edit:
Okay so after much discussion I made a few test .svg cards to see how SVG Salamander would go about rendering them. Unfortunately, the performance is terrible. My implementation is clean enough, seeing as with double-buffered BufferedImage's the painting was incredibly fast. Which means I have come full circle and I'm back to my original problem.
I'll give the 50 bounty to whoever can give me a solution to get sharp BufferedImage rotations. Suggestions have been to make the images bigger than they need to be and downscale before painting, and to use bicubic interpolation. If these are the only possible solutions, then I really don't know where to go from here and I may just have to deal with the blurred rotations - because both of those impose performance setbacks.
I can finish my game if I can find a way to do this well.
Thanks to everyone. :)
When you rotate a rasterized image (such as a BufferedImage), you lose data. The best solution is to save your images larger than you'll need them, and downscale on the fly when you paint them. I've found that 1.5x the size you need is a good starting point.
Then, when you're painting the image, resize on the fly:
g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
Rotations using bilinear interpolation is recommended.
Credit for suggestion goes to guido.
This advice is probably a little late in your design, but may be worth mentioning.
Rasterized images is probably the wrong technology to use if a lot of rotations and animations are a part of your UI; especially with complicated images with lots of curves. Just wait until you try and scale your canvass. I might suggest looking at a vector based graphical library. They will render the sorts of effects you want with less potential for artifacts.
http://xmlgraphics.apache.org/batik/using/swing.htm
Setting the interpolation type, as well as anti-aliasing value, in an AffineTransformOp may offer some improvement. Type TYPE_BICUBIC, while slower, is typically the best quality; an example is outlined here. Note that you can supply multiple RenderingHints. Another pitfall arises from failing to apply the hints each time the image is rendered. You may also need to adjust the transparency of the background, as suggested here. Finally, consider creating an sscce that includes one of your actual images.