I'm trying to make a game where the player sometimes have to rotate, but I can't figure out a way to make it rotate more each time, here's what I've got:
Graphics2D g2d = (Graphics2D) g;
AffineTransform at = new AffineTransform();
at.translate(300, Main.height - 115);
if(rotateright) {
at.rotate(Math.PI / 15);
} else if(rotateleft) {
at.rotate(Math.PI / 15);
}
at.scale(0.125, 0.125);
at.translate(-texture.getWidth() / 2, -texture.getHeight() / 2);
g2d.drawImage(texture, at, null);
This method makes it rotate just fine, but I can't find a way to make it rotate more each time, so this method pretty much just rotates it, but only once, and then it just stops. Is there anyway to add to the rotation or something?
you're always starting of with the image in the normal rotation (0 deg/RAD rotated), and then radiating it by PI/15 RAD. if you want it to rotate over time, you will need to multiply PI/15 with a value that will be changing over the duration of your animation.
Related
I am drawing a radial gradient circle on an image like this
I have java code for this
private void drawRadialGradientCircleJava(String imagePath, double posX, double posY, float radius, String outputPath) throws IOException{
BufferedImage city = ImageIO.read(new File(imagePath));
BufferedImage mask = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
Color transparent = new Color(255, 0, 0, 0);
Color fill = Color.RED;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point2D.Double(posX, posY),
radius,
new float[]{0f, 0.75f, 1f},
new Color[]{transparent, transparent, fill});
g2d.setPaint(rgp);
g2d.fill(new Rectangle(0, 0, mask.getWidth(), mask.getHeight()));
g2d.dispose();
BufferedImage masked = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = masked.createGraphics();
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, masked.getWidth(), masked.getHeight());
g2d.drawImage(city, 0, 0, null);
g2d.setComposite(AlphaComposite.DstAtop);
g2d.drawImage(mask, 0, 0, null);
g2d.dispose();
ImageIO.write(masked,"png", new File(outputPath));
}
I want to do same thing in Android, I have an image view in which I have an image, now I want to touch a point in image and draw this transparent circle around that point
I have following Android code as well but id doesn't draw anything on the image
private void drawRadialGradientCircleAndroid(ImageView imageView, float posX,
float posY, float radius) throws IOException {
RadialGradient gradient = new RadialGradient(posX, posY, radius, Color.TRANSPARENT,
Color.TRANSPARENT, android.graphics.Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setDither(true);
p.setShader(gradient);
Bitmap bm = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap bmOverlay = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bm, new Matrix(), null);
canvas.drawCircle(posY, posX, radius, p);
imageView.setImageBitmap(bmOverlay);
}
Please help how can I achieve this in Android.
We should migrate this to the answer boxes.
OP has basically got it here- and in fact the OP's revised gist is brilliant.
Some general tips regarding the first attempt in the question:
1) In protected void onSizeChanged(int w, int h, int oldw, int oldh):
width = w; there is no reason why you can't call getWidth() when you require this. The reason it's advisable is because the View's internal width is set quite late after onMeasure. Consequently, onDraw may be the next time you want a most up to date version, so use the getter there.
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);. Creating a bitmap is an expensive and memory intensive operation. Unless you want to write a bitmap to a file, or send it to a BitmapDrawable for an ImageView or something, you don't need to do this. Especially with effects drawn onto the UI with android's graphics library.
mCanvas = new Canvas(mBitmap); followed by a draw operation onto the new canvas. This is never needed. And yet I've seen it (not work) in many code bases and attempts. I think it's the fault of an old stack overflow post that got people doing this so that they could transform a canvas on a custom view without effecting the drawing onto the rest of the canvas. Incidentally, if you need this, use .restore() and .save() instead. If you see new Canvas, be suspicious.
2) onDraw(...):
Yes, you need to avoid doing things in onDraw, like, creating objects, or any heavy processing. But you still need to do the things in onDraw you need to do in onDraw!
So here you simply need to call : canvas.drawCircle(float cx, float cy, float radius, Paint paint) with arguments as per the docs.
This really isn't that sinful for onDraw. If you're worried about calling this too much, as might be the case if your entire button is animating across the screen, you need to use hardware acceleration available in later API versions, as will be detailed in an article called Optimizing the View; very helpful reading if you're using lots of custom drawn views.
3) That pesky radial gradient. The next issue you had is that you quite rightly created your paint in an initmethod so that the object creation was off the draw. But then quite rightly it will have IllegalArgumentExceptioned (I think) on you because at that stage the getHeight() of the view was 0. You tried passing in small pixel values- that won't work unless you know some magic about screen sizes.
This isn't your issue as much as the annoying view cycle at the heart of Android's design patterns. The fix though is easy enough: simply use a later part of the view's drawing process after the onMeasure call to set the paint filter.
But there are some issues with getting this right, namely that sometimes, annoyingly, onDraw gets called before the point at which you'd expect it. The result would be your paint is null and you wouldn't get the desired behavior.
I have found a more robust solution is simply to do a cheeky and naughty little null check in the onDraw and then once only construct the paint object there. It's not strictly speaking optimal, but given the complex way in which the Paint objects hook up with Android's graphics native layer better than trying to straddle the paint configuration and construction in many frequently called places. And it makes for darn clearer code.
This would look like (amending your gist):
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setShader(new RadialGradient(getWidth() / 2, getHeight() / 2,
getHeight() / 3, Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));
}
width = getWidth();
height = getHeight();
canvas.drawCircle(width / 2, height / 2, height / 3, mPaint);
}
So note a few changes- I think from your description you want the two colours swapped round in the arguments, also don't forget to center the center of your gradient in your view: width/2 and height/2 arguments.
Best of luck!
I would like to rotate, scale, and translate a section of an image. For example, I have a sprite-sheet with columns and rows of sprites. I can draw the section I want onto a temporary BufferedImage, then transform that temporary image onto the main graphics, but this is a very slow operation.
How can I make this much much faster? It needs to occur more than 100 * 60 times per second.
public void Draw_WorldSpace(Graphics2D g, double x, double y, double angle, double deltaTime) {
// setup portion of image to transform
BufferedImage tempImage = new BufferedImage(sourceRectangle.width, sourceRectangle.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = tempImage.createGraphics();
g2.drawImage(image, 0, 0, sourceRectangle.width, sourceRectangle.height, sourceRectangle.x, sourceRectangle.y, sourceRectangle.width, sourceRectangle.height, null);
g2.dispose();
// setup a transformation object
AffineTransform transform = new AffineTransform();
// match view rotation
transform.translate(GameLogic.viewPort.GetCanvasCenter().x, GameLogic.viewPort.GetCanvasCenter().y);
transform.rotate(Math.toRadians(GameLogic.viewPort.GetAngle()));
transform.translate(-GameLogic.viewPort.GetCanvasCenter().x, -GameLogic.viewPort.GetCanvasCenter().y);
// set to position
transform.translate(x - GameLogic.viewPort.GetViewPortCenter().x + GameLogic.viewPort.GetCanvasCenter().x, y - GameLogic.viewPort.GetViewPortCenter().y + GameLogic.viewPort.GetCanvasCenter().y);
// rotate
transform.rotate(Math.toRadians(angle));
// center on sprite
transform.translate(-sourceRectangle.width / 2, -sourceRectangle.height() / 2);
// draw the sprite
g.drawImage(tempImage, transform, null);
}
Ultimately I did what Hovercraft Full Of Eels suggested. I wasn't the biggest fan of the solution simply because it requires a lot of memory overhead. But ultimately, it worked like a charm and even game me more streamlined control over the graphics, so that's really cool.
I want to roatate an Image with Affinetransform
I did it like this:
AffineTransform at = AffineTransform.getTranslateInstance(image.getWidth(null), image.getHeight(null));
at.rotate(Math.toRadians(getRadian()),image.getWidth(null)/2, image.getHeight(null)/2);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, at, null);
But now I want to set the Position of the Image,how do I do this?
Thanks for help
First of all it's unclear which position of image you want to translate to necessary place. Let me propose general rule. Suppose that you want to be able to rotate image around its internal coordinates (ix,iy) and then place this internal position to new global place with coordinates (gx, gy). And rotation angle is "angleInRadians". Then your transformation will be:
AffineTransform at = AffineTransform.getTranslateInstance(gx, gy);
at.rotate(angleInRadians);
at.translate(-ix, -iy);
Do you see back order of events here? It's because of how transformation matrices are applied to each other and to position vector. If you read this from the end to beginning then what we do is we move internal position to coordinate start, then we rotate around this start and then we move this start to necessary global place.
If you need to move center of image then:
double ix = imageWidth / 2;
double iy = imageHeight / 2;
If you want to move left upper corner then ix = 0 and iy = 0, so you can skip at.translate(-ix, -iy) in this case.
First happy new year to all here!
I have a question about drawing background in code. I have a code for simple Android game and all assests is in png format, expect background. I`m not a programmer (but newbie in this and I learn with live examples).
I think this code draw background clouds on the screen:
//draw cloud layer 1
background_shader.setColor(getResources().getColor(R.color.blue_dark));
int radius = DrawBackgroundCloud(canvas, (ScreenHeight() / 2), 7);
canvas.drawRect(0, (float) ((ScreenHeight() / 2.2) + radius * 1.5), ScreenWidth(), ScreenHeight(), background_shader);
//draw cloud layer 2
background_shader.setColor(getResources().getColor(R.color.blue_darkest));
radius = DrawBackgroundCloud(canvas, (int) (ScreenHeight() / 1.5), 4);
canvas.drawRect(0, (float) ((ScreenHeight() / 1.7) + radius * 1.5), ScreenWidth(), ScreenHeight(), background_shader);
This draw some random circles as clouds but I want to change this to draw something like hills or mountains. Here is a picture of current background and what I`m looking for.
http://prntscr.com/5nqa25
Can anyone help me with this? I will be really thankfuly
Responding to the further question in the comment:
you cant really do that with canvas.drawColor, but you can use a proper Paint object and use canvas.drawPaint (or other canvas method that uses Paint object - if you want to for example draw shape with gradient).
The key part of creating your gradient Paint object is calling its setShader(...) method. For example like so:
mGradientPaint = new Paint();
mGradientPaint.setStyle(Paint.Style.FILL);
mGradientPaint.setShader(new LinearGradient(0, 0, 0, getHeight(), Color.TRANSPARENT, Color.GREEN, Shader.TileMode.MIRROR));
I have a problem with rotating image in a fixed position with Graphcis2D and AffineTransform.
The idea is to rotate an image according to body's rotation.
The rotation is happening correctly as the rotation angle of the image matches the angle of the body's rotation. However, as the rotation takes place, the image is not drawn to the same position as the body should be drawn. The code of the method painting the picture is the following:
public void paintPicture(Graphics g, Body body) {
Graphics2D g2 = (Graphics2D) g;
Vector2f[] vertices = ((Box) body.getShape()).getPoints(body.getPosition(), body.getRotation());
Vector2f topLeftCorner = vertices[0];
AffineTransform oldTransform = g2.getTransform();
AffineTransform at = new AffineTransform();
at.rotate(body.getRotation());
g2.setTransform(at);
g2.drawImage(this.img, (int) topLeftCorner.x, (int) topLeftCorner.y, null);
g2.setTransform(oldTransform);
}
Any ideas what might cause the movement of the image instead of drawing it according to the coordinates (topLeftCorner.x, topLeftCorner.y)?
You need to first translate you object so the anchor point (the point around which you want it to rotate) is at the origin, perform your rotation, then translate it back. So if you want to rotate around the point (50, 75), you'd do the following:
at.translate (-50, -75);
at.rotate (body.getRotation());
at.translate (50, 75);
I'm assuming that your AffineTransform class can accumulate transformations. If not, you'll need 3 different transforms.