clipping semi-circle from bitmap - android

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).

Related

How to draw segmented circle like this with certain requirements

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.

How to close a complicated path in canvas?

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.

Vignette in Android

So this is what I have for a vignette style effect in Android (image is a Bitmap):
public void vignette() {
float radius = (float) (image.getWidth()/1.5);
RadialGradient gradient = new RadialGradient(image.getWidth()/2, image.getHeight()/2, radius, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
Canvas canvas = new Canvas(image);
canvas.drawARGB(1, 0, 0, 0);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setShader(gradient);
final Rect rect = new Rect(0, 0, image.getWidth(), image.getHeight());
final RectF rectf = new RectF(rect);
canvas.drawRect(rectf, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(image, rect, rect, paint);
}
This "works" but there are a couple of problems. First of all, this is not really a vignette, it's just a gradient so you can see bits of the black going nearly all the way to the center rather than feathering out closer to the edges.
The RadialGradient used also only allows for setting the radius of a circle rather than an ellipse. An ellipse would be able to more effectively match the dimensions of a non-square image than a circle.
The quality of the gradient is also not superb.
I'm trying to replicate the vignetteImage method from ImageMagick (I'm referring specifically to the php version). I have this code in PHP that produces the style of image that I want:
$im = new IMagick('city.png');
$im->vignetteImage($width/1.5, 350, 20, 20);
I've tried building ImageMagick with the NDK but have been unsuccessful in properly linking the various image libraries (I've only successfully built with gif support but no png, jpeg or tiff).
I've also attached an image comparing the two methods shown above. The image on the left was generated with ImageMagick through php and the image on the right was generated using the method shown above for Android.
If you look carefully at the image on left, tf uses exponential increase in Alpha (transparency) vs. image on right which is very linear.
Clearly Shader.TitleMode.CLAMP is a linear function. What you should do instead is use RadialGradient(float x, float y, float radius, int[] colors, float[] positions, Shader.TileMode tile) to define 10 or more points on the image with exponentially decreasing color values (black to transparent).
Alternatively, you can refer gallery 3d source for ICS gallery http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.0.1_r1/com/android/gallery3d/photoeditor/filters/VignetteFilter.java?av=h
I know this is an old discussion but it may help someone.
You can use AccelerateInterpolator to generate the points that Taranfx had mentioned and it will lock awesome.

Using method -canvas.drawBitmap(bitmap, src, dst, paint)

Everytime I use this code nothing is drawn. I need to draw a bitmap inside of a specified rectangle.
canvas.drawBitmap(MyBitmap, null, rectangle, null)
I've looked online but can't find much help.
EDIT
The original answer is incorrect.
You can use the sourceRect to specify a part of a Bitmap to draw.
It may be null, in which case the whole image will be used.
As per the fryer comment he was drawing beneath something, I'll add a note on that.
drawBitmap(bitmap, srcRect, destRect, paint)
does not handle Z ordering (depth) and the order of calling draw on object matters.
If you have 3 shapes to be drawn, square, triangle and circle. If you want the square to be on top then it must be drawn last.
You're not specified any source, so its not drawn anything.
Example:
You have a Bitmap 100x100 pixels. You want to draw the whole Bitmap.
canvas.drawBitmap(MyBitmap, new Rect(0,0,100,100), rectangle, null);
You want to draw only the left half of the bitmap.
canvas.drawBitmap(MyBitmap, new Rect(0,0,50,100), rectangle, null);
You need to specify the source rect, the source rect can be a rectangle anywhere from 0,0 to the width,height of the bitmap.
The main item to remember when defining the Rect is:
left < right and top < bottom
The rect is in screen coordinates (positive Y downward) ...
I find it helpful to think of the Rect arguments
(left, top, right, bottom)
as
(X, Y, X + Width, Y + Height)
where X,Y is the top left corner of the sprite image.
NOTE: If want to center the image on a particular location, remember to offset those values by half the sprite width & height. For example:
int halfWidth = Width/2;
int halfHeight = Height/2
Rect dstRectForRender = new Rect( X - halfWidth, Y - halfHeight, X + halfWidth, Y + halfHeight );
canvas.drawBitmap ( someBitmap, null, dstRectForRender, null );
This uses the whole original image (since src rect is null) and scales it to fit the size and position from dstRectForRender ... and using the default Paint.
I dont know why but this worked for me!
Rect rectangle = new Rect(0,0,100,100);
canvas.drawBitmap(bitmap, null, rectangle, null);
Thanks:)

How to clear only a part of the screen?

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.

Categories

Resources