I'm trying to blend 2 images together in the following way:
Image 1 should be drawn as the base image. Image 2 should be drawn overtop of image 1. Anywhere image 2 is non-transparent, it should replace the contents of image 1 (not blend, but overwrite what is there). Wherever image 2 is transparent, image 1 should show through. I've tried to do this with the following code, but I'm obviously doing something incorrectly with the blending.
gl.glEnable(GL.GL_BLEND);
if (iconTexture1 != null)
{
gl.glEnable(GL.GL_TEXTURE_2D);
iconTexture1.bind();
double red = (double) fillColor.getRed() / 255.0;
double green = (double) fillColor.getGreen() / 255.0;
double blue = (double) fillColor.getBlue() / 255.0;
gl.glColor4d(red, green, blue, this.getOpacity());
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
TextureCoords texCoords = iconTexture1.getImageTexCoords();
gl.glScaled(width, height, 1d);
dc.drawUnitQuad(texCoords);
}
if (iconTexture2 != null)
{
gl.glEnable(GL.GL_TEXTURE_2D);
iconTexture2.bind();
// image2 is all white, so color it here
gl.glColor4d(1d, 0d, 0d, 1d);
// TODO: What blend function should I be using here to allow image 2 to overwrite what is already there?
TextureCoords texCoords = iconTexture2.getImageTexCoords();
gl.glScaled(width, height, 1d);
dc.drawUnitQuad(texCoords);
}
Any help to make this work correctly would be appreciated. Thanks.
Jeff
There are a few things that might be issues:
You shouldn't turn the blending function on until after you have drawn image 1. Doing so before will blend image 1 with whatever was there already.
I'm not a big texture user, but I think using texture will override the color of the quad, including any alpha you have specified; so unless the texture has alpha you won't get any blending.
If you have z-buffering enabled, then maybe image 2 is behind image1; that would obscure it, even if image 2 is transparent. Special methods have to be used to draw transparent 3d.
A good way of working with OpenGL things that don't seem to work is to remove all the complexity, and then add it back bit by bit. The texture is your most complex part - leave that 'til last.
Related
Firstly, I am trying to make a simple game in Java. I have a viewport that shows a tile map and I have a tank in the middle that moves by controlling the JScrollBars of the scrollpane in which the viewport resides in. So far everything has been going well, until I needed to rotate an image. Here is a picture of the game: Note: the tank body and tilemap are on seperate panels and do not share the same graphics.
Picture of non rotated tank body:
Essentially, I want to rotate a buffered image around its center (rotating in place) using arrow keys. I already have the code for the keys, and I also have a method to try and rotate the buffered image given a buffered image and angle in degrees (the angle is changed to radians in the method). This method will return a buffered image that is rotated correctly. Here is the code:
public static BufferedImage rotateImage(BufferedImage image, double angle) {
if(angle == 0)
return image;
else {
angle = Math.toRadians(angle);
double x = Math.abs(Math.cos(angle));
double y = Math.abs(Math.sin(angle));
int newWidth = (int) Math.floor(image.getWidth()*x + image.getHeight()*y);
int newHeight = (int) Math.floor(image.getHeight()*x + image.getWidth()*y);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, image.getType());
Graphics2D tool = rotated.createGraphics();
AffineTransform transformer = new AffineTransform();
transformer.rotate(angle, image.getWidth()/2, image.getHeight()/2);
tool.drawImage(image, transformer, null);
tool.dispose();
return rotated;
}
}
However, as the title suggests, the image gets cut off at the top and left sides of the image when rotated as shown:
Picture of rotated tank body:
So I have looked at many different forums but I could not solve my problem. I could add whitespace around the image, but that interferes a lot with collision detection which I plan to do later on. I know that it has to do something with the original display being smaller than the display of the rotated image, and I have tried to translate accordingly in many ways. If I translate with this line of code specifically,
transformer.translate((newWidth - image.getWidth())/2, (newHeight - image.getHeight())/2);
Then the image (tank body) rotates without cutting, but bounces out of place as shown (I drew a rectangle to show where it was):
Picture of rotated tank with translation:
I also have tried negating the translations too but it only avails to funky movements.
So, I really have no clue how to solve this, and I have been spending too much time on this problem. I would really appreciate a helpful answer that directly edits my method if possible.
Answer
So here is the opening idea that I needed to realize to answer this problem.
The method to translate and rotate is meant so that the image is not cut off. However, it won't be around the center as intended as seen in the 3rd picture. But again, the method is not intended to recenter it. The painting code itself needs to account for this shift. I simply added variables to account for this:
xOffset = (newWidth - image.getWidth())/2;
yOffset = (newHeight - image.getHeight())/2
And simply subtracted these from where I was painting the tank's body.
Thanks to #camickr for the solution
When rotating a square sprite around the center point, the target image should be larger than the original image by a factor of the square root of 2 (approx. 1.41). For example, a sprite will not be clipped at a rotation angle of 45 °.
I hope this information helps you to solve your problem.
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 want to draw a bitmap, but with only gray pixel (for example 0x555555 color).
So I should change all the colorful pixel's color to gray.
How to do this or how to draw an image with only grayed pixels?
What you are looking for is called "converting image to grayscale in English". Making a simple search I found the following thread (luckily it targets exactly Android. Hopefully it will help you.
You can read every pixel, convert it to RGB color system.
Then if you want grey, then Red, Green and Blue must have equal value.
I don't know exactly what is best, when you get the good "gray" color.
But maybe if you do this math: (Red + Green + Blue) / 3 = x
Color R = x, G = x, B = x;
Then they all got the same color, but I don't know if yo get a correct gray scale with this.
If you need more information, just say it, I got some code how to read pixels from a bitmap and convert them into RGB color system.