I'm trying to use canvas.clipPath() to draw a bitmap image inside a constraining circle similar to to Clipping activity in the APP demos. The problem is that my code only renders properly on the emulator, when run on an actual Samsung Galaxy Nexus 4.2 it appears as if the clipPath works more like a rectangular clipping. I'm totally stumped! I create a new Path() and decode a bitmap in my view ctor. Any suggestions?
#Override
protected void onDraw(Canvas canvas) {
Point point = getPoint();
path.reset();
canvas.clipPath(path); // makes the clip empty
path.addCircle(point.x, point.y, getScale() * 140, Path.Direction.CCW);
canvas.clipPath(path, Region.Op.REPLACE);
Point scaledSize = new Point((int) (bitmapSize.x * getScale()), (int) (bitmapSize.y * getScale()));
Point topLeft = new Point((point.x - (scaledSize.x / 2)), (point.y - (scaledSize.y / 2)));
canvas.drawBitmap(bitmap, null, new Rect(topLeft.x, topLeft.y, topLeft.x + scaledSize.x, topLeft.y + scaledSize.y), paint);
}
Galaxy Nexus
Emulator
ClipPath is not supported for hardware acceleration
You can close hardware acceleration use this:
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
clipPath is not supported for hardware acceleration.
You can create a clipped bitmap using something like this:
Bitmap clippedBitmap = ... // Create same size bitmap as source
Paint paint = new Paint();
Canvas canvas = new Canvas(clippedBitmap);
paint.setColor(Color.RED);
paint.setStyle(PAint.Style.FILL);
paint.setFilterBitmap(true);
canvas.drawCircle(cx, cy, radius, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(sourceBitmap, 0, 0, paint);
Do this once, and then draw the clipped bitmap instead of the source one
ClipPath is not supported for hardware acceleration. Check the link below under the topic Unsupported Drawing Operations.
http://developer.android.com/guide/topics/graphics/hardware-accel.html#drawing-support
You can use the below for reference and modify the paramters of draw circle to suit your needs.
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
DrawingView dv = new DrawingView(this);
dv.setBackgroundColor(Color.RED);
setContentView(dv);
}
class DrawingView extends View{
Bitmap bitmap;
public DrawingView(Context context)
{
super(context);
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.sqaure);
}
#Override
public void onDraw(Canvas canvas)
{
Paint paint = new Paint();
//paint.setStyle(Paint.Style.FILL);
// paint.setColor(Color.CYAN);
canvas.drawBitmap(getclip(), 0, 0, paint);//originally x and y is o and o .
}
public Bitmap getclip()
{
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(),
bitmap.getHeight());
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
//paint.setColor(color);
canvas.drawCircle(bitmap.getWidth() / 2-40,
bitmap.getHeight() / 2, bitmap.getWidth() / 2-40, paint);
// change the parameters accordin to your needs.
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
}
}
Related
I want to make a hole in a solid colored view but I can't get what I want. I use the following code:
my customView extends View{
...
protected void PunchTheView() {
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
RectF outerRectangle = new RectF(0, 0, getWidth(), getHeight());
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(getResources().getColor(R.color.black);
canvas.drawRect(outerRectangle, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
float centerX = getWidth()/2;
float centerY = (getHeight()/2)-120 ;
float radius = (getWidth()-(getWidth()/6))/2;
canvas.drawCircle(centerX, centerY, radius, paint);
}
}
and this is the result when I use it; the hole still has some opacity. (note that the root holder has white background)
"I also tried PorterDuff.Mode.SRC_OUT and PorterDuff.Mode.DST_OUT
and they didn't help neither
)
I want to draw a circle and that activity must receive values (in degrees). I would draw lines on this circle and will have the opportunity to draw points on it.
If you can help me it would be great :)
I tried This :
public class DiagObstActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.diagobstacles_main);
int radius;
radius = 200;
final ImageView imgCircle = (ImageView) findViewById(R.id.imgCircle);
Paint paint = new Paint();
paint.setColor(getResources().getColor((R.color.blue)));
paint.setStyle(Paint.Style.STROKE);
Bitmap bmp = Bitmap.createBitmap(500,500,Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bmp);
canvas.drawCircle(bmp.getWidth() / 2, bmp.getHeight() / 2, radius, paint);
RectF rectF = new RectF(50, 20, 100, 80);
canvas.drawOval(rectF, paint);
paint.setColor(Color.BLACK);
canvas.drawArc(rectF, 180, 0, true, paint);
imgCircle.setImageBitmap(bmp);
}
}
but the result is 2 circles screenshot
i am new thanks for ur help :)
Say I've got a fully rectangle image:
Now when I show it in an ImageView, I want one corner to be cut off, like this:
How can I achieve this on runtime?
I've solved it using this code:
public static Bitmap maskImage(Context context, Bitmap original) {
if (original == null)
return null;
Bitmap result = Bitmap.createBitmap(original.getWidth(), original.getHeight(), Config.ARGB_8888);
Canvas c = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(android.graphics.Color.WHITE);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
Path path = new Path();
path.moveTo(result.getWidth(), result.getHeight());
path.lineTo(result.getWidth() - dpToPx(context, CORNERWIDTHDP), result.getHeight());
path.lineTo(result.getWidth(), result.getHeight() - dpToPx(context, CORNERHEIGHTDP));
path.close();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
c.drawBitmap(original, 0, 0, null);
c.drawPath(path, paint);
paint.setXfermode(null);
return result;
}
I am trying to cut a circle from a square bitmap using following code
Canvas canvas=new Canvas(bitmapimg );
int circleXCoord = bitmapimg .getWidth() / 2;
int circleYCoord = bitmapimg .getHeight() / 2;
int circleRadius = bitmapimg .getWidth() / 2;
Rect rect = new Rect(circleXCoord - circleRadius, circleYCoord - circleRadius, circleXCoord + circleRadius, circleYCoord + circleRadius);
int width = rect.width();
int height = rect.height();
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);
canvas.drawBitmap(bitmapimg , rect, rect, paint);
Path p = new Path();
p.addCircle(circleXCoord, circleYCoord, width / 2F, Path.Direction.CW);
canvas.clipPath(p, Region.Op.DIFFERENCE);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
The idea is to attach a square (rectangular) bitmap to canvas and then clip a circular path. Clear out the difference between the rectangle and circle (make it transparent).
The code works fine for Android 4, but on Android 2.3.3 device, the difference area is appearing black rather that transparent.
Am I missing something here or PorterDuff.Mode.CLEAR is not supported in gingerbread? Is there a better way to cut a circle from a square in Android?
Seems like PorterDuff.Mode.Clear did not work for gingerbread
Solved the problem (cut circle from square using this code)
public Bitmap BitmapCircularCroper(Bitmap bitmapimg){
Bitmap output = Bitmap.createBitmap(bitmapimg.getWidth(),
bitmapimg.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmapimg.getWidth(),
bitmapimg.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(bitmapimg.getWidth() / 2,
bitmapimg.getHeight() / 2, bitmapimg.getWidth() / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmapimg, rect, rect, paint);
return output;
}
import android.graphics.PorterDuff.Mode;
import android.graphics.Bitmap.Config;
public static Bitmap getCircularBitmap(Bitmap bitmap)
{
Bitmap output;
if (bitmap.getWidth() > bitmap.getHeight()) {
output = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getHeight(), Config.ARGB_8888);
} else {
output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getWidth(), Config.ARGB_8888);
}
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
float r = 0;
if (bitmap.getWidth() > bitmap.getHeight()) {
r = bitmap.getHeight() / 2;
} else {
r = bitmap.getWidth() / 2;
}
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(r, r, r, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
For anyone that's still looking at this, this answer will cause a few issues.
1.) The canvas that you are creating in this instance will not have hardware acceleration. Even though your Paint object has anti-aliasing, the canvas will not. This will cause artifacting when you decide to paint this back to your original canvas in your onDraw() call.
2.) This takes a lot more resources. You have to create a second Bitmap (which can cause OOM), and a secondary Canvas as well as all of the different alterations you have to do.
Please check out Romain Guy's answer. You create a BitmapShader and then create a RoundRect that gives you a Circle. You just need to know the dimensions of your RectF so that it can determine the circle properly.
This means that if you know the center point (x, y) and radius, you can easily determine the RectF.
left = x - radius;
top = y - radius;
right = x + radius;
bottom = y + radius;
This also means that with this solution posted below you only have to draw to the screen once, everything else is done in the off-screen buffer.
http://www.curious-creature.com/2012/12/11/android-recipe-1-image-with-rounded-corners/
The best solution is found here:
BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rect = new RectF(0.0f, 0.0f, width, height);
// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);
I used the below to make a bitmap with rounded corners. Now I want to draw a line around the bitmap.
private BitmapDrawable roundCornered(BitmapDrawable scaledBitmap, int i) {
Bitmap bitmap = scaledBitmap.getBitmap();
result = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
canvas = new Canvas(result);
color = 0xff424242;
paint = new Paint();
rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
rectF = new RectF(rect);
roundPx = i;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.BLUE);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
BitmapDrawable finalresult = new BitmapDrawable(result);
return finalresult;
}
I got the image below, but my actual need is that I must draw a border around the image.
I put the following together for myself.
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int color, int cornerDips, int borderDips, Context context) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int borderSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) borderDips,
context.getResources().getDisplayMetrics());
final int cornerSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) cornerDips,
context.getResources().getDisplayMetrics());
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
// prepare canvas for transfer
paint.setAntiAlias(true);
paint.setColor(0xFFFFFFFF);
paint.setStyle(Paint.Style.FILL);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawRoundRect(rectF, cornerSizePx, cornerSizePx, paint);
// draw bitmap
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
// draw border
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth((float) borderSizePx);
canvas.drawRoundRect(rectF, cornerSizePx, cornerSizePx, paint);
return output;
}
Credits, of course, to http://ruibm.com/?p=184
How about to prepare 9-patch image like below and set it as a background by using android:background
I use BitmapShader and drawRoundRect do it and it work for me, look at the screenshot
RectF roundRect; // the Rect you have to draw into
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// draw the border at bottom
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mBorderColor);
canvas.drawRoundRect(roundRect, mRadius, mRadius, mPaint);
// ------------------ draw scheme bitmap
roundRect.set(itemRect.left + mBorderSize, itemRect.top + mBorderSize, itemRect.right - mBorderSize, itemRect.bottom - mBorderSize);
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
canvas.drawRoundRect(roundRect, mRadius, mRadius, mPaint);
mPaint.setShader(null);
I did many search to implement that effect by code, before I found another way but it's not perfect enough, you can see the jaggy on each corner, I set Paint.setAntiAlias(true), Paint.setDither(true), Paint.setFilterBitmap(true), but It doesn't work, so I hope somebody can help me.
RectF roundRect = new RectF(itemRect);
Bitmap bitmap = scheme.getSchemeBitmap(getResources());
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mSchemeSelectedColor);
canvas.drawRoundRect(roundRect, mSchemeCornerRadius, mSchemeCornerRadius, mPaint);
roundRect.set(itemRect.left + mBorderSize, itemRect.top + mBorderSize, itemRect.right - mBorderSize, itemRect.bottom - mBorderSize);
Path clipPath = new Path();
clipPath.addRoundRect(roundRect, mSchemeCornerRadius, mSchemeCornerRadius, Path.Direction.CW);
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipPath(clipPath);
canvas.drawBitmap(bitmap, null, roundRect, mPaint);
canvas.restore();
Unfortunately there's no nice and neat "border" parameter in Android, but the simplest way to do it is to encase that within another layout, set the background of the parent layout to your border color/drawable and then set a padding on it. The padding will appear around your BitmapDrawable.
my way:
public static Bitmap getRoundedCornerBitmap1(Bitmap bitmap, int color, int cornerDips, int borderDips) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth()+2*borderDips,
bitmap.getHeight()+2*borderDips,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawColor(Color.TRANSPARENT);
final RectF rectF = new RectF(0, 0, output.getWidth(), output.getHeight());
final Paint paint = new Paint();
// prepare canvas for transfer
paint.setAntiAlias(true);
paint.setStrokeWidth((float) borderDips);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(rectF, borderDips, borderDips, paint);
canvas.drawBitmap(bitmap, borderDips, borderDips, null);
bitmap.recycle();
return output;
}
Result