I am trying to draw an oval in canvas. Basically, I combine four bezier curves to an oval.
I successfully draw an oval but I don't know how to close it.
Codes:
//Paints for bazier curve
Paint bezierPaint = new Paint();
bezierPaint.setColor(Color.RED);
bezierPaint.setStyle(Paint.Style.STROKE);
bezierPaint.setStrokeCap(Paint.Cap.ROUND);
bezierPaint.setStrokeWidth(3.0f);
bezierPaint.setAntiAlias(true);
//Oval Path
Path ovalPath = new Path();
//Draw the first curve. from top point to right point
ovalPath.moveTo(160,0);
ovalPath.cubicTo(210, 0, 260, 100, 260, 150);
//Draw the second curve. from right point to bottom point
ovalPath.moveTo(260, 150);
ovalPath.cubicTo(260, 200, 210, 300, 160, 300);
//Draw the thrid curve. from bottom point to left point
ovalPath.moveTo(160, 300);
ovalPath.cubicTo(110, 300, 60, 200, 60, 150);
//Draw the fourth curve. from left point to top point
ovalPath.moveTo(60, 150);
ovalPath.cubicTo(60, 100, 110, 0, 160, 0);
**//I expect this oval close correctly. But in actually, the fourth curve was closed.
//Please see the image in attachment.How should I close this path as my expectation?**
ovalPath.close()
canvas.drawPath(ovalPath, bezierPaint);
Just drop the call to path.close(). This function adds a line segment from the current draw point to the first draw point. I don't think there's any harm to skipping this call.
Alternatively, pay more attention to the orientation of your segments so that each one starts where the last ended, and more importantly, so that the last one ends where the first one started.
Or do what marnaish said, and use drawOval() instead.
I think it is quite easier to use following function to draw an oval:
canvas.drawOval(ovalRect, paint);
The ovalRect is a RectF Object in which the oval will find its place.
Related
I am trying to draw 3 rects on canvas.
First one is green and is going on the back. Second is red and is over the green one. Third is going over both of them, and it should "cut through" first two rects. Something like this:
I get that I should do something like this:
canvas.drawColor(red);
canvas.drawRect(greenRect, paintGreen);
canvas.drawRect(smallRect, paintWithSomePorterduff);
canvas.drawRect(redRect, paintRed);
canvas.drawRect(smallRect, paintWithSomePorterduff);
But what PorterDuffXfermode to use, and how to clip only one rect and not make a hole through all of them and get this:
P.S. I can't make different bitmaps and then draw them because it will redraw it at every 1 or 2 seconds.
I had to do it by creating a bitmap and redrawing it.
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawColor(colorBottom);
timeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
timeCanvas = new Canvas(timeBitmap);
timeCanvas.drawArc(oval, 270, secRot, true, secondHandPaint);
timePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
timeCanvas.drawText(time, xOffset, yOffset, timePaint);
canvas.drawBitmap(timeBitmap, 0, 0, new Paint());
I'm trying to clip a semi-circular chunk out of the bitmap so that it looks like this
The problem I'm having is correctly scaling the arc (so that its smaller than bitmap) and positioning it on the left edge. If I try to draw the arc in the other quadrant path.arcTo(rectF, 180-30, 60), then the concavity is pointing the wrong way.
Canvas c = new Canvas(sshotBitmap);
Path path = new Path();
RectF rectF = new
RectF(0, 0, (int)((float)social.getWidth()), social.getHeight());
path.reset();
path.arcTo(rectF, -30, 60);
path.close();
c.clipPath(path, Region.Op.DIFFERENCE);
social.draw(c);
Using arcs can be somewhat cumbersome. Since you just need a semicircular clip, an easier, and possibly more intuitive, solution would be to use the Path#addCircle() method instead, and center it on the middle of the left side of the Canvas. That is, center it at (0, c.getHeight() / 2).
I can't just seem to figure it out. I am trying to draw a segmented circle (what looks like circle inside a circle). However I want the segments to have specific colors and to be transparent inside the smaller circle. Preferably , I would like to make the color of the segmented lines different than the circle
Here are the solutions I had in mind:
1- Draw arc with fill color for the bigger circle and draw a circle for the small circle. 2 problems with this. First one is that the inner circle area is no longer transparent as it takes the color from the bigger one. Second problem is that the segmentation lines of the outer circle is going all the way to the center (not only to the inner circle perimeter)
2) Draw arcs for the bigger outer circle and draw circle for the inner circle. Set it to be color filled but don't show strokes. Then draw another outer circle on top with no fill just to show strokes. And then draw lines between the inner and outer circle using the calculations ( angle and radius) to determine where the lines are... Very convoluted solution, there has to be another way. Even with this solution, still have problem with the color showing in the center but maybe playing with gradient can help.
I read so much on SO but I couldn't figure the right answer as many answers would remove the control of circle parameters
HEELP!!!
#Override
public void draw(Canvas canvas) {
float size = Math.min(getWidth(),getHeight());
paint.setStrokeWidth(size/4);
paint.setStyle(Paint.Style.STROKE);
final RectF oval = new RectF(0, 0, getWidth(), getHeight());
oval.inset(size/8,size/8);
paint.setColor(Color.RED);
Path redPath = new Path();
redPath.arcTo(oval, 0, 120, true);
canvas.drawPath(redPath, paint);
paint.setColor(Color.GREEN);
Path greenPath = new Path();
greenPath.arcTo(oval, 120, 120, true);
canvas.drawPath(greenPath, paint);
paint.setColor(Color.BLUE);
Path bluePath = new Path();
bluePath.arcTo(oval, 240, 120, true);
canvas.drawPath(bluePath, paint);
paint.setStrokeWidth(2);
paint.setColor(0xff000000);
canvas.save();
for(int i=0;i<360;i+=40){
canvas.rotate(40,size/2,size/2);
canvas.drawLine(size*3/4,size/2,size,size/2,paint);
}
canvas.restore();
final RectF ovalOuter = new RectF(0, 0, getWidth(), getHeight());
ovalOuter.inset(1,1);
canvas.drawOval(ovalOuter,paint);
final RectF ovalInner = new RectF(size/4, size/4, size*3/4,size*3/4);
canvas.drawOval(ovalInner,paint);
}
I'm drawing arcs using the Path class and strokes. Style.STROKE gives arcs without filling. Stroke width is set to size/4 which is a quarter of the view. Half of that stroke width goes outside and the second half goes inside, like this:
xxxxxxxx outer border of the arc of width 5
xxxxxxxx
------------ stroke
xxxxxxxx
xxxxxxxx inner border of the arc
That's why I'm using insets - I need to offset the stroke a bit in order to fit it in the view. Without insets the arcs are cut by all four sides of the view.
And why canvas rotation? Because it's easier to rotate the canvas with built-in methods than calculate lines manually. Rotation uses trigonometric functions and quickly becomes quite complex, hard to read and error prone. Basically I'm rotating the paper and drawing straight lines.
I'm trying to learn how to make an animated sprite in android and couldn't figure out how to go about organising my bitmaps. I have a sprite sheet of my character walking to the Right: a bitmap of five copies of a character, equally spaced (every 45px), in a walk cycle.
I planned to draw each frame by drawing a tiny section of my sprite sheet bitmap at a time by going:
Rect sourceRect = new Rect(0, 0, 45, 75);
canvas.drawBitmap(spriteSheetBitmap, sourceRect, new Rect(0, 0, 45, 75), null);
Then for the next frames, increment "sourceRect.x" by 45, then redraw and so forth.
However, I'm now not sure how to go about making my sprite walk to the Left. I had initially thought I could just mirror my rectangle that I am drawing from to get a flipped picture. Something like:
sourceRect = new Rect(45, 0, 0, 75);
which doesn't seem to work (not sure what actually happens here, but nothing gets drawn to my surface).
Searching online, it seems I should make a copy of my original bitmap, mirror it with a transform matrix, then use that bitmap for drawing when walking to the left. However I've also found implementations where many smaller bitmap objects get created out of the original sprite sheet, stored (and transformed for the mirrored motion), then used as needed.
So I'm wondering what would be the best in this case or if there is really any difference (performance/memory):
Method 1: Load in my original sprite sheet, make a new bitmap instance, mirror it,, then calculate all the rectangles and use those + two entire sheets to draw from (admittedly there is some extra bitmap space where the sprite sheet is unused).
Method 2: Load in my original sprite sheet, for every frame create a new two bitmap objects (1 mirrored, 1 normal) and store those to draw from.
Method 3: Other better ways?
Method 2 would be way too expensive, and you don't need a canvas to flip a bitmap. Simply create another bitmap with a Matrix applied, like so:
BitmapDrawable flip(BitmapDrawable d)
{
Matrix m = new Matrix();
m.preScale(-1, 1);
Bitmap src = d.getBitmap();
Bitmap dst = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), m, false);
dst.setDensity(DisplayMetrics.DENSITY_DEFAULT);
return new BitmapDrawable(dst);
}
To mirror your sprite simply apply the following transform on the Canvas: scale(-1, 1). You will have to offset the sprite by its width too.
To draw a vertical mirrored bitmap bmp on a canvas:
Matrix m = new Matrix();
// Mirror is basically a rotation
m.setScale( -1 , 1 );
// so you got to move your bitmap back to it's place. otherwise you will not see it
m.postTranslate(canvas.getWidth(), 0);
canvas.drawBitmap(bmp, m, p);
I am using SurfaceHolder to draw in the canvas. I want to know how can I clear only part of the screen, or how can I draw animation only part of the screen? I don't want to draw in all the screean, can I do it only part of the screen?
If you want to draw to only a part of the screen, first specify two rects. Like this:
Rect src = new Rect(0, 0, 50, 50);
Rect dst = new Rect(0, 0, 50, 50);
Then you just draw using the drawBitmap method that takes two rects as parameters.
canvas.drawBitmap(bitmap, src, dst, null);
Say that you have a background image thats 100*100 px. Then the code above would redraw only the top left corner of that bitmap.