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.
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'm not very skilled in android application developing, and I'm working on a test app. I detected the face and the eyes, now I'm going to draw something like acne or scar on the face (e.g below the eyes on the cheek) based on the coordinates of the eyes. Later, I'm going to put eye-glass or hat on the appropriate locations.
I know the coordinates of the left and the right eye (leftEyePosx... [and so on] in the code). For example 136x168 [left] and 216x168 [right] (mirrored in picture). Now, I'm calculating the scale: glass bitmap should be scaled around 80 pixels (216-136) or bigger for the width, and 80 pixels multiplied with the original image's aspect ratio for the height (e.g. 80 * 0.7). I have the bitmap with the code:
glassBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.glass_01);
Now, how can I re-scale the eye-glass and use the method canvas.drawBitmap() and use eye coordinates and draw the glass on the face? Or should I use another way?
Thanks in advance, and sorry for my bad English :)
First you scale the bitmaps:
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, targetWidth, targetHeight, false);
Then you draw one bitmap on top of the other, at xPos and yPos:
Canvas canvas = new Canvas(baseBitmap);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(overlayBitmap, xPos, yPos, paint);
xPos and yPos are the upper left corner of the overlay bitmap. For example, if the base image eye is supposed to show in the middle of the overlay bitmap, you have to adjust for this by deducting half the width and height to find the desired xPos and yPos.
I tried to post a different topic for this but people didn't really seem to understand what I was trying to do, so, now I've closed that one and opened this one to give more detail and rephrase the question as a whole.
Ok.
So basically, I have an application which draws an Ellipse. Now, I have a certain number of points (that can be random) in which I have to rotate an image and draw at.
Using Maths I know that to get a point on an Ellipse based by using an angle I use the following equation;
final int radiusW = (width / 2);
final int radiusH = (height / 2);
final int angle = 120;
int pointX = (int) (radiusW + (radiusW * Math.cos(Math.toRadians(angle))));
int pointY = (int) (radiusH + (radiusH * Math.sin(Math.toRadians(angle))));
And that works fine, I can locate an absolute point around the perimeter of the Ellipse.
However, now I'm trying to draw an image on this point so that the image is rotated facing the center of ellipse and is centered on the point.
So, to get the image rotated to the center of the point I do the following;
final AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(angle - 90), image.getWidth() / 2, image.getHeight() / 2);
final AffineTransformOp ato = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
And then I get my new rotated BufferedImage using;
BufferedImage rotated = ato.filter(image, null);
However, I can't seem to be able to get the central point of the image anymore.
If the angle was 0 so that the Image was the original direction then I would simply do;
graphics.drawImage(rotated, pointX - rotated.getWidth() / 2, pointY - rotated.getHeight() / 2, this);
However I'm not sure how to find the central point and draw it based upon that on a rotated image.
I know it involves using cos and sin to multiply the original pointX and pointY by the rotation matrix but everytime I try and work out a solution it always draws completely wrong.
Any help would be very much appreciated as I've spent the best part of a day trying to resolve this.
Thank you.
The thing is that if you just use sin and cos to rotate the corner of the image you will end up with the new rotated position of that corner - when actually what you want to find is the new width and height.
The center is width/2, height/2
Use this to calculate your new width and height:
Calculate Bounding box coordinates from a rotated rectangle
I have done this with OpenCV, and there the image have been rotated but the resulted image was having the same width and height as the initial on. The image was cropped if it was getting out of the initial dimentions and there were black pixels if no information (the rotated image has no pixels in that place).
If you think that rotated has more pixels than the initial image you can verify with size() or length(). Or you can play with the diagonals of the initial rectangle (image size): compute the projection of the diagonals and thake the greatest or what you think. But I am sure that it is similar to the OpenCV case.
I don't know if this can help you, but I hope so.
Your question is not entirely clear but it seems to me that you have a misunderstanding of the nature of the angle used to parametrise your ellipse. The angle as you have it is merely used to parametrise the form of an ellipse equation. It is not the same as the polar angle (except at particular angles). That is to say if you evaluate a point on your ellipse using an angle of (pi/4) radians (45 degrees), then measured the angle that the line from the ellipse centre to your point makes with the axis, it will not measure 45 degrees (except for the case where the ellipse is actually a circle).
That is to say that
int pointX = (int) (radiusW + (radiusW * Math.cos(Math.toRadians(angle))));
int pointY = (int) (radiusH + (radiusH * Math.sin(Math.toRadians(angle))));
is just a parametrisation of an ellipse and that angle is not a polar angle and treating this angle as a rotation angle will not give accurate results (except at integer multiples of (pi/2) radians)
It seems to me that you require the polar form of an ellipse relative to its centre in order for your code to make sense in the context of using this angle for rotation.
It is also possible that I have misunderstood your question though, in which case this answer will be downvoted on a grand scale and I will delete it.
Ok I've been trying to rotate a Rectangle that sits on top of an image. I have a Scrollable class that displays the Image. I can draw Rectangles on top of the Image. My problem is trying to rotate the Rectangles when the Image is rotated. The Rectangle gets lost and placed in the wrong location.
I've already tried suing Graphics2D, AffineTransform, createTransformedShape() but no luck.
What I'm trying to do now is to rotate the Rectangle manually. I'm trying to get the Rectangle Point(x,y) using below formula:
double rectX = (Math.cos(Math.toRadians(90)) * (x - anchorX) - Math.sin(Math.toRadians(90)) * (y - anchorY)) + anchorY;
double rectY = (Math.sin(Math.toRadians(90)) * (x - anchorX) - Math.cos(Math.toRadians(90)) * (y - anchorY)) + anchorY;
How can I find the anchorX and anchorY values using Java? I've tried diving the Image height by 2 but doesn't work for all rotation angles. Do I have to get the JPanel width and height or something? Is there a formula that Java uses to find anchor points?
I'm not sure of the goal, but I see two possible alternative approaches:
Use an inverse transform, as shown here.
Render the upright image and rectangle(s) into a BufferedImage and rotate the composite image, as shown here.
If you're trying to make it like a a box rolling across the top, you have to find the bottom corner in direction of travel. If you're rolling to the right, you need the bottom right corner.
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.