Rounded corners with Picasso - android

Is there a reasonable way to do rounded corners with Picasso that
Doesn't significantly slow down drawing
Works with hardware layers
Doesn't create an extra bitmap for each image
Allows resizing the downloaded bitmap into the size of the destination imageview
Most of the picasso advice on rounded corners suggests that a transformation be used, but I haven't seen an example that doesn't create an extra bitmap as part of the transformation.
This seems to be because Picasso only uses bitmaps, while the tricks to do rounded corners use the fact that you can dynamically draw the rounded corners on reasonably efficiently (most solutions use something along the lines of http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/).
Doing this with Volley was a bit hacky but possible, by just changing the type of ImageView to something that took a custom drawable, which drew rounded corners. Since Picasso needs bitmaps (at least, there's only a bitmap -> bitmap transformation), this is out, since the conversion of the drawable to bitmap creates a bitmap in the process.
One solution would be to do the work to modify picasso in a branch on my own that added a bitmap -> drawable transform, but I'd imagine there's a better way to go about this.
I do not want to draw a 9-patch on top of a view to give the appearance of rounded corners.

This code works fine for me
Picasso.with(getApplicationContext())
.load(sp.getString("image_url", ""))
.transform(new RoundedTransformation(100, 0))
.fit()
.into(userProfileImg);
// here is the class for make
public class RoundedTransformation implements
com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(),
source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth()
- margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
#Override
public String key() {
return "rounded";
}
}

I also needed something like this, but with a border. I've searched the internet and I've found one version (without rounded corners) that looked good, but the border was over the image and I didn't like that. So I made my own version with the border outside the image.
public class BitmapBorderTransformation implements Transformation {
private int mBorderSize;
private int mCornerRadius = 0;
private int mColor;
public BitmapBorderTransformation(int borderSize, int color) {
this.mBorderSize = borderSize;
this.mColor = color;
}
public BitmapBorderTransformation(int borderSize, int cornerRadius, int color) {
this.mBorderSize = borderSize;
this.mCornerRadius = cornerRadius;
this.mColor = color;
}
#Override
public Bitmap transform(Bitmap source) {
int width = source.getWidth();
int height = source.getHeight();
Bitmap image = Bitmap.createBitmap(width, height, source.getConfig());
Canvas canvas = new Canvas(image);
canvas.drawARGB(0, 0, 0, 0);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect rect = new Rect(0, 0, width, height);
if(this.mCornerRadius == 0) {
canvas.drawRect(rect, paint);
}
else {
canvas.drawRoundRect(new RectF(rect),
this.mCornerRadius, this.mCornerRadius, paint);
}
paint.setXfermode(new PorterDuffXfermode((PorterDuff.Mode.SRC_IN)));
canvas.drawBitmap(source, rect, rect, paint);
Bitmap output;
if(this.mBorderSize == 0) {
output = image;
}
else {
width = width + this.mBorderSize * 2;
height = height + this.mBorderSize * 2;
output = Bitmap.createBitmap(width, height, source.getConfig());
canvas.setBitmap(output);
canvas.drawARGB(0, 0, 0, 0);
rect = new Rect(0, 0, width, height);
paint.setXfermode(null);
paint.setColor(this.mColor);
paint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(new RectF(rect), this.mCornerRadius, this.mCornerRadius, paint);
canvas.drawBitmap(image, this.mBorderSize, this.mBorderSize, null);
}
if(source != output){
source.recycle();
}
return output;
}
#Override
public String key() {
return "bitmapBorder(" +
"borderSize=" + this.mBorderSize + ", " +
"cornerRadius=" + this.mCornerRadius + ", " +
"color=" + this.mColor +")";
}
}
Here are some samples:
Border with rounded corners:
new BitmapBorderTransformation(3, 15, Color.WHITE);
http://postimg.org/image/68fz5md39/
Rounded corners without border:
new BitmapBorderTransformation(0, 15, Color.WHITE);
http://postimg.org/image/he4681rsv/
Also you can do border without rounded corners:
new BitmapBorderTransformation(3, Color.WHITE);

this will work for any image of any size--
1) first create an empty image container for different resolution
2) then on runtime get its height and width by this-------
BitmapFactory.Options dimensions = new BitmapFactory.Options();
dimensions.inJustDecodeBounds = true;
Bitmap mBitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.icon, dimensions);
int height = dimensions.outHeight;
int width = dimensions.outWidth;
3)
Picasso.with(getActivity())
.load(url)
.error(R.drawable.image2)
.placeholder(R.drawable.ic_drawer)
.resize(width, height )
.transform(new ImageTrans_roundedcorner())
.into(imageView1);
4) now transformation class----
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Bitmap.Config;
import android.graphics.Rect;
import android.graphics.RectF;
import com.squareup.picasso.Transformation;
public class ImageTrans_roundedcorner implements Transformation{
private int mBorderSize=10;
private int mCornerRadius = 20;
private int mColor=Color.BLACK;
#Override
public Bitmap transform(Bitmap source) {
// TODO Auto-generated method stub
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.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, source.getWidth(), source.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = mCornerRadius;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(source, rect, rect, paint);
// draw border
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth((float) mBorderSize);
canvas.drawRoundRect(rectF, mCornerRadius, mCornerRadius, paint);
//-------------------
if(source != output) source.recycle();
return output;
}
#Override
public String key() {
// TODO Auto-generated method stub
return "grayscaleTransformation()";
}
}

EDIT: the answer I would suggest would be to wait for Picasso 2.3, or fork their github now, where you can actually get at a BitmapDrawable.
One approach I've found so far is that you can load images into a Target object, create a custom drawable from the bitmap that way, then set the drawable into the ImageView, where it'll draw without creating a new bitmap.
This approach sort of sucks for a few reasons though:
1) You have to manage Target objects. These are weak-referenced (thankfully), so you have to keep track of them yourself. Ick. Memory leaks ahoy.
2) When you get the callback, you had better check to make sure the state of the world is still relevant to the picture, which is part of what you want to avoid by using picasso.
In short, there are a few things that seem to prevent a better solution.
1) Picasso wraps bitmaps in PicassoDrawables. This means you you have to handle arbitrary drawables in your custom imageView (if you go that route), or special case for this class.
2) PicassoDrawable doesn't expose the source bitmap, so you have to convert the drawable to a bitmap (requires creating a new bitmap, afaict).
3) There's no bitmap -> drawable transform function (see #1 for why, most likely).
Would love to hear if there's something I'm missing, or someone has come up with a better solution. Right now my best plan is to either do the Target management proposed above, or fork the picasso repo, change PicassoDrawable to have a public accessor for the underlying bitmap, and do the conversion into a custom drawable that way in my imageView.

Related

Canvas draw color inside drawable. Dont touch transparent part of image

I have a drawable with transparent left and right parts. I need to draw some progress inside it.
startDrawable.setBounds(0, 0, startDrawable.getIntrinsicWidth(), canvas.getHeight());
startDrawable.draw(canvas);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(startDrawable.getIntrinsicWidth() / 2, 0);
path.lineTo(startDrawable.getIntrinsicWidth() / 2, canvas.getHeight());
canvas.drawPath(path, paint);
But unfortunately this code fill transparent parts too and it looks like black rectangle.
I want to see something like this. Does it possible to draw black color only inside not trasparent part of image ??
You can create two different Bitmaps:
one that includes your normal background, lines, shapes and any draw you want (be sure to stay INSIDE your choosed bounds)
one that is the shape used to CUT the previous one via "Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.xxxxxxx))" method (do a "PorterDuffXfermode" search on Google Images to see all mixes possibilities)
In this way you are free to draw what you want in a normal rectangular/squared Bitmap, and at the end you apply a Mask (over the whole Bitmap) using a free shape (triangular, circular, etc..). Same tecnique is used to create a circular picture starting from the original rectangular version.
For example this code creates a rounded version of a rectangular Bitmap:
#Nullable
public static Bitmap getRoundedBitmap(#Nullable final Bitmap bmp, final int radius) {
if ((bmp == null) || (radius < 1)) return null;
Bitmap cBitmap;
if (bmp.getWidth() != radius || bmp.getHeight() != radius) {
float cSmallest = Math.min(bmp.getWidth(), bmp.getHeight());
float cFactor = cSmallest / radius;
try {
cBitmap = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth() / cFactor), (int)(bmp.getHeight() / cFactor), true);
} catch (Exception e) {
cBitmap = null;
}
} else cBitmap = bmp;
if (cBitmap == null) return null;
final Bitmap cOutput = Bitmap.createBitmap(radius, radius, Bitmap.Config.ARGB_8888);
final Canvas cCanvas = new Canvas(cOutput);
final Paint cPaint = new Paint();
final Rect cRect = new Rect(0, 0, radius, radius);
cPaint.setAntiAlias(true);
cPaint.setFilterBitmap(true);
cPaint.setDither(true);
cPaint.setStyle(Paint.Style.FILL);
cCanvas.drawARGB(0, 0, 0, 0);
cPaint.setColor(Color.BLACK);
cCanvas.drawCircle(radius / 2f, radius / 2f, radius / 2f, cPaint);
cPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//draws the rectangular Bitmap and use the special Paint object that has the circular "mask" set
cCanvas.drawBitmap(cBitmap, cRect, cRect, cPaint);
return cOutput;
}

Out of memory error while loading image in CircularImageView

I am trying to load an image in CircularImageView but it is giving me outOfMemory error.
I am using following code for CircularImageView:
CircularImageView.java
public class CircularImageView extends ImageView {
public CircularImageView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
int w = getWidth(), h = getHeight();
Bitmap roundBitmap = getRoundedCroppedBitmap(bitmap, w);
canvas.drawBitmap(roundBitmap, 0, 0, null);
}
public static Bitmap getRoundedCroppedBitmap(Bitmap bitmap, int radius) {
Bitmap
finalBitmap;
if (bitmap.getWidth() != radius || bitmap.getHeight() != radius)
finalBitmap = Bitmap.createScaledBitmap(bitmap, radius, radius,
false);
else
finalBitmap = bitmap;
Bitmap output = Bitmap.createBitmap(finalBitmap.getWidth(),
finalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, finalBitmap.getWidth(),
finalBitmap.getHeight());
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawCircle(finalBitmap.getWidth() / 2 + 0.7f,
finalBitmap.getHeight() / 2 + 0.7f,
finalBitmap.getWidth() / 2 + 0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(finalBitmap, rect, rect, paint);
return output;
}
}
I using it in the design as given below:
<com.almabay.almachat.circularImageView.CircularImageView
android:id="#+id/img_group"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_alignParentLeft="true" />
I am getting image from server and trying to display it in the list view using Adapter.Following code is being used here:
Picasso.with(context).load(image_url).error(R.drawable.default_avatar).into(viewHolder.imgGroup);
Here image_url is the URL of the image to be loaded in circular image view.ViewHolder.imgGroup is the circularImageView.
Please help me to fix the issue.
I see you are using Picasso for image loading why not use picasso image transformations. it's very simple to implement :-
ImageView im = (ImageView) findViewById(R.id.img1);
Picasso.with(MainActivity2.this).load(R.drawable.kitten)
.transform(new CropCircleTransformation()).into(im);
and it looks like this
You just need to add transformation class
import com.squareup.picasso.Transformation;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
public class CropCircleTransformation implements Transformation {
#Override public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
int width = (source.getWidth() - size) / 2;
int height = (source.getHeight() - size) / 2;
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader =
new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
if (width != 0 || height != 0) {
// source isn't square, move viewport to center
Matrix matrix = new Matrix();
matrix.setTranslate(-width, -height);
shader.setLocalMatrix(matrix);
}
paint.setShader(shader);
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
source.recycle();
return bitmap;
}
#Override public String key() {
return "CropCircleTransformation()";
}
}
More transformations can we find here https://github.com/wasabeef/picasso-transformations
Normally I'd tell you that the error could be anywhere and you're probably leaking. But you're making 2 new bitmaps in onDraw? And all 3 are in memory? Not only is that hideously wasteful, but your performance will suck- you should even avoid using new in onDraw, much less allocating bitmaps.
Throw this out and rewrite it completely. You should avoid working on a drawable here (especially since you're assuming its a bitmap anyway which is a bad idea), you should take in a filename or resource id of the bitmap and read it in/scale it with 1 createBitmap command by using BitmapOptions. You should do this when the bitmap is set and save the result, do NOT scale in onDraw which is called all the time. You can avoid the 3rd bitmap by using a circular clipping path instead of using porter duff modes and a second canvas. You can do all this with just 1 bitmap, and should.

How to create a partially-rounded-corners-rectangular drawable with center-crop and without creating new bitmap?

Background
I've already seen how to create a drawable that's circular out of a bitmap, and also how to add an outline (AKA stroke) around it, here.
The problem
I can't find out how to do a similar task for rounding only some of the corners of the bitmap, inside the drawable, without creating a new bitmap, and still do it for a center-crop ImageView.
What I've found
This is what I've found, but it does create a new bitmap, and when using it in an imageView with center-crop (source here):
/**
* Create rounded corner bitmap from original bitmap.
*
* #param input Original bitmap.
* #param cornerRadius Corner radius in pixel.
* #param squareTL,squareTR,squareBL,squareBR where to use square corners instead of rounded ones.
*/
public static Bitmap getRoundedCornerBitmap(final Bitmap input, final float cornerRadius, final int w, final int h,
final boolean squareTL, final boolean squareTR, final boolean squareBL, final boolean squareBR) {
final Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Rect rect = new Rect(0, 0, w, h);
final RectF rectF = new RectF(rect);
// make sure that our rounded corner is scaled appropriately
Paint paint = new Paint();
paint.setXfermode(null);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
// draw rectangles over the corners we want to be square
if (squareTL)
canvas.drawRect(0, 0, w / 2, h / 2, paint);
if (squareTR)
canvas.drawRect(w / 2, 0, w, h / 2, paint);
if (squareBL)
canvas.drawRect(0, h / 2, w / 2, h, paint);
if (squareBR)
canvas.drawRect(w / 2, h / 2, w, h, paint);
paint.setXfermode(PORTER_DUFF_XFERMODE_SRC_IN);
canvas.drawBitmap(input, 0, 0, paint);
return output;
}
And, this is what I've found for creating a rounded corners drawable that acts on all corners:
public static class RoundedCornersDrawable extends Drawable {
private final float mCornerRadius;
private final RectF mRect = new RectF();
private final BitmapShader mBitmapShader;
private final Paint mPaint;
public RoundedCornersDrawable(final Bitmap bitmap, final float cornerRadius) {
mCornerRadius = cornerRadius;
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(false);
mPaint.setShader(mBitmapShader);
mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(0, 0, bounds.width(), bounds.height());
}
#Override
public void draw(final Canvas canvas) {
canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}
But this solution only works well if the imageView shows the content while maintaining the same aspect ratio as the bitmap, and also has its size pre-determined.
The question
How to create a center-cropped drawable, that shows a bitmap, has rounded corners for specific corners, and also be able to show an outline/stroke around it?
I want to do it without creating a new bitmap or extending ImageView. Only use a drawable that has the bitmap as the input.
The SMART way is to use the PorterDuff blending mode. It's really simple and slick to create any fancy shading, blending, "crop" effect. you can find a lot of good tutorial about PorterDuff. here a good one.
I always use this library to achieve what you are looking for. you can round any corner you want and also add stroke.
https://github.com/vinc3m1/RoundedImageView
You can use it or see it's source codes just for inspiration.
EDIT
there is no need to use Image View and make bitmap or drawable yourself and show it in Image View.
Just replace Image View with Rounded Image View and it will handle everything for you without any extra work in code !
here is sample :
<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageView1"
android:scaleType="centerCrop"
app:riv_corner_radius="8dp"
app:riv_border_width="2dp"
app:riv_border_color="#333333"
app:riv_oval="false" />
In code, just pass any image resource to it or use any Image Loader with it.
RoundedImageView myRoundedImage=(RoundedImageView)findViewById(R.id.imageView1);
myRoundedImage.setImageResource(R.drawable.MY_DRAWABLE);
// OR
ImageLoader.getInstance().displayImage(YOUR_IMAGE_URL, myRoundedImage);
if you want to just make specific corners rounded try this:
<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageView1"
android:scaleType="centerCrop"
app:riv_corner_radius_top_right="8dp"
app:riv_corner_radius_bottom_right="8dp"
app:riv_border_width="2dp"
app:riv_border_color="#333333"
app:riv_oval="false" />
Well, you can create a new .xml drawable named my_background and paste this code below:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<solid android:color="#00000000"/>
<corners
android:bottomLeftRadius="12dp"
android:bottomRightRadius="12dp"
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
<stroke
android:width="1dp"
android:color="#000000"
/>
</shape>
Then, you set your ImageButton's, ImageView's background to your new drawable, like this:
android:background="#drawable/my_background"
android:scaleType="centerCrop"
or programatically:
myView.setBackground(R.drawable.my_background);
Hope it helps!
EDIT:
To programmatically create a similar drawable, you can use it:
GradientDrawable shape = new GradientDrawable();
shape.setShape(GradientDrawable.RECTANGLE);
shape.setCornerRadii(new float[] { 8, 8, 8, 8, 0, 0, 0, 0 });
shape.setColor(Color.TRANSPARENT);
shape.setStroke(3, Color.BLACK);
v.setBackgroundDrawable(shape);
Ok, here's my try. The only gotcha is that "int corners" is meant to be a set of flags. Such as 0b1111 where each 1 represents a corner to be rounded, and 0 is the opposite. The order is TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT.
example usage first, formatted for readability:
((ImageView) findViewById(R.id.imageView)).setImageDrawable(
new RoundedRectDrawable(
getResources(),
bitmap,
(float) .15,
0b1101,
8,
Color.YELLOW
)
);
code:
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.SystemClock;
/**
* Created by mliu on 4/15/16.
*/
public class RoundedRectDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
private final float outlineBorderRadius;
private final int w;
private final int h;
private final int corners;
private final int border;
private final int bordercolor;
public RoundedRectDrawable(final Resources resources, final Bitmap bitmap, float borderRadiusSeed, int corners, int borderPX, int borderColor) {
super(resources, bitmap);
bitmapShader = new BitmapShader(getBitmap(),
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
final Bitmap b = getBitmap();
p = getPaint();
p.setAntiAlias(true);
p.setShader(bitmapShader);
w = b.getWidth();
h = b.getHeight();
rect = new RectF(0,0,w,h);
borderRadius = borderRadiusSeed * Math.min(w, h);
border = borderPX;
this.corners = corners;
this.bordercolor = borderColor;
outlineBorderRadius = borderRadiusSeed * Math.min(w+border,h+border);
}
#Override
public void draw(final Canvas canvas) {
if ((corners&0b1111)==0){
if (border>0) {
Paint border = new Paint();
border.setColor(bordercolor);
canvas.drawRect(rect, border);
}
canvas.drawRect(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border, p);
} else {
if (border >0) {
Paint border = new Paint();
border.setColor(bordercolor);
canvas.drawRoundRect(rect, outlineBorderRadius, outlineBorderRadius, border);
if ((corners & 0b1000) == 0) {
//top left
canvas.drawRect(rect.left, rect.top, rect.width() / 2, rect.height() / 2, border);
}
if ((corners & 0b0100) == 0) {
//top right
canvas.drawRect(rect.width() / 2, rect.top, rect.width(), rect.height() / 2, border);
}
if ((corners & 0b0010) == 0) {
//bottom left
canvas.drawRect(rect.left, rect.height() / 2, rect.width() / 2, rect.height(), border);
}
if ((corners & 0b0001) == 0) {
//bottom right
canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width(), rect.height(), border);
}
}
canvas.drawRoundRect(new RectF(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border), borderRadius, borderRadius, p);
if ((corners & 0b1000) == 0) {
//top left
canvas.drawRect(rect.left + border, rect.top + border, rect.width() / 2, rect.height() / 2, p);
}
if ((corners & 0b0100) == 0) {
//top right
canvas.drawRect(rect.width() / 2, rect.top + border, rect.width() - border, rect.height() / 2, p);
}
if ((corners & 0b0010) == 0) {
//bottom left
canvas.drawRect(rect.left + border, rect.height() / 2, rect.width() / 2, rect.height() - border, p);
}
if ((corners & 0b0001) == 0) {
//bottom right
canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width() - border, rect.height() - border, p);
}
}
}
}
So, this handles the outline first, if needed, then the bitmap. It marks the canvas up with a rounded rect first, then "squares out" each corner you don't want to round. Seems highly inefficient, and probably is, but average case run time before minimal optimizations (corners = 0b0000, 10 canvas.draw calls) takes ~ 200us on a S7. And, that time is SUPER inconsistent based on phone usage. I've gotten as low as 80us and as high as 1.5ms.
NOTES/WARNING: I am BAD at this. This is not optimal. There's probably better answers already here on SO. The subject matter is just a bit uncommon and difficult to search up. I was originally not going to post this, but at time of writing this is still not marked answered, and the library OP did not wish to use due to problems with their Drawable actually use a very similar approach as my terrible solution. So, now I'm less embarrassed to share this. Additionally, though what I posted today was 95% written yesterday, I know I got some of this code from a tutorial or a SO post, but I can't remember who to attribute cause I didn't end up using it in my project. Apologies whoever you are.

How to set a unique radius for Picasso RoundedCornerTransofrmation()

I have a Recycler view and the view containing n number of url images. The url images are different resolution. I am using Picasso image loader to load the images and set a RoundedCornerTransofrmation() with fixed radius. But different images are setting with different radius.
you can add this class in your package :
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
// enables hardware accelerated rounded corners
// original idea here : http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
#Override
public String key() {
return "rounded";
}
you can use above class like this :
Picasso.with(c).load(s[0]).placeholder(R.drawable.placeholder).transform(new RoundedTransformation(40,4)).error(c.getResources().getDrawable(R.drawable.placeholder)).into(holder.im);
I have found the solution to set fixed radius for different resolution images in Picasso roundedCornerTransformation()
Implement the custom class with implements com.squareup.picasso.Transformation
and modify the transform method as like below
#Override
public Bitmap transform(final Bitmap source) {
//Get the Image Width Form incoming image
int radius = source.getWidth();
//Set the percentage value for radius
radius = (radius*10)/100;
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
Based on the percentage value, we can set image curve radius as fixed.
Thanks.

Antialiased rounded corners on Android ImageView [duplicate]

This question already has answers here:
How to make an ImageView with rounded corners?
(58 answers)
Closed 9 years ago.
I am new to android dev, and I have been trying for a few hours now to add nice and smooth rounded corners to an ImageView, without success.
First thing I tried is simply to round corners of my images directly, but this implies changing the bitmap, and since I need to keep the raw ones, and those are pretty big, this is not really memory friendly. This would also cause other difficulties since my ImageView is fluid.
Second thing I tried to use is the clipPath method after subclassing my view. This works, but corners are aliased. I then tried adding a PaintFlagsDrawFilter to implement the aliasing, but this didn't worked. I'm using monodroid, and I was wondering this was supposed to work in Java.
Here is my code (C#):
public class MyImageView : ImageView
{
private float[] roundedCorner;
/**
* Contains the rounded corners for the view.
* You can define one, four or height values.
* This behaves as the css border-radius property
*
* #see http://developer.android.com/reference/android/graphics/Path.html#addRoundRect(android.graphics.RectF, float[], android.graphics.Path.Direction)
*/
public float[] RoundedCorners{
get{
return roundedCorner;
}
set{
float[] finalValue = new float[8];
int i=0;
if(value.Length == 1){
for(i=0; i<8;i++){
finalValue[i] = value[0];
}
}else if(value.Length == 4){
for(i=0; i<4;i++){
finalValue[2*i] = value[i];
finalValue[2*i+1] = value[i];
}
}
roundedCorner = finalValue;
}
}
public SquareImageView (Context context) :
base (context)
{
Initialize ();
}
public SquareImageView (Context context, IAttributeSet attrs) :
base (context, attrs)
{
Initialize ();
}
private void Initialize ()
{
RoundedCorners = new float[]{0,0,0,0};
}
public override void Draw (Android.Graphics.Canvas canvas)
{
Path path = new Path();
path.AddRoundRect(new RectF(0,0, Width,Height),RoundedCorners, Path.Direction.Cw);
canvas.ClipPath(path);
base.Draw (canvas);
}
/**
* try to add antialiasing.
*/
protected override void DispatchDraw (Canvas canvas)
{
canvas.DrawFilter = new PaintFlagsDrawFilter((PaintFlags)1, PaintFlags.AntiAlias);
base.DispatchDraw (canvas);
}
}
Thanks for your help!
I've created a RoundedImageView based off Romain Guy's example code that wraps this logic into an ImageView that you should be able to just use. It supports borders and antialiasing out of the box.
It's more efficient than other rounded corner examples because it doesn't create another copy of the bitmap, nor does it use clipPath which draws twice to the canvas.
use below code
public Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels)
{
Bitmap output = null;
if(bitmap != null)
{
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());
final RectF rectF = new RectF(rect);
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
}
return output;
}
and call this method like
imageView.setImageBitmap(getRoundedCornerBitmap(bitmap, 10));

Categories

Resources