I would like to achieve on a bitmap an effect equivalent to what
paint.setStyle(Paint.Style.STROKE)
canvas.drawText(string, x, y, paint);
does for text.
Something akin to BlurMaskFilter, but for stroke, not glow, nor shadow.
Or if there isn't a built-in way, perhaps someone can suggest an algorithm to achieve this?
private Bitmap multiplyAlpha(final Bitmap bitmap, Paint paint,
final boolean color, final float x) {
paint = new Paint(paint.getFlags());
Bitmap result = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), color ? Config.ARGB_8888 : Config.ALPHA_8);
// ColorMatrixColorFilter requires ARGB.
Bitmap auxiliary = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.ARGB_8888);
new Canvas(auxiliary).drawBitmap(bitmap, 0, 0, paint);
// #formatter:off
paint.setColorFilter(new ColorMatrixColorFilter(new float[] {
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, x, 0
}));
// #formatter:on
new Canvas(result).drawBitmap(auxiliary, 0, 0, paint);
return result;
}
private Bitmap opaque(final Bitmap bitmap, Paint paint, final boolean color) {
return multiplyAlpha(bitmap, paint, color, 255);
}
private RectF inset(final RectF rectF, final float dx, final float dy) {
RectF result = new RectF(rectF);
result.inset(dx, dy);
return result;
}
private RectF offset(final RectF rectF, final float dx, final float dy) {
RectF result = new RectF(rectF);
result.offset(dx, dy);
return result;
}
private Bitmap antialias(final Bitmap bitmap, Paint paint,
final float radius) {
Bitmap result = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.ALPHA_8);
if (radius > 0) {
paint = new Paint(paint.getFlags());
Canvas canvas = new Canvas(result);
Bitmap opaque = opaque(bitmap, paint, false);
canvas.drawBitmap(opaque, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setMaskFilter(new BlurMaskFilter(radius, Blur.INNER));
canvas.drawBitmap(opaque, 0, 0, paint);
}
return result;
}
private Bitmap stroke(final Bitmap bitmap, Paint paint, final float radius,
final RectF rectF, final float dx, final float dy) {
paint = new Paint(paint.getFlags());
Bitmap result = Bitmap.createBitmap(
(int) Math.ceil(rectF.width() + 2 * radius),
(int) Math.ceil(rectF.height() + 2 * radius), Config.ALPHA_8);
if (radius > 0) {
paint.setMaskFilter(new BlurMaskFilter(radius, Blur.NORMAL));
new Canvas(result).drawBitmap(opaque(bitmap, paint, false), null,
offset(rectF, -dx, -dy), paint);
}
return result;
}
private Bitmap stroke(final Bitmap bitmap, Paint paint, final float radius,
final RectF rectF, final int color, final float antialias,
final float factor, final boolean fill, final float dx,
final float dy) {
paint = new Paint(paint.getFlags());
Canvas canvas = new Canvas();
paint.setColor(color);
Bitmap stroke = stroke(bitmap, paint, radius, rectF, dx, dy);
Bitmap auxiliary = Bitmap.createBitmap(stroke.getWidth(),
stroke.getHeight(), Config.ALPHA_8);
canvas.setBitmap(auxiliary);
// Paint [opaque] stroke.
canvas.drawBitmap(opaque(stroke, paint, false), 0, 0, paint);
// Antialias stroke with outside.
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
Bitmap outer = multiplyAlpha(antialias(stroke, paint, antialias),
paint, false, factor);
canvas.drawBitmap(outer, 0, 0, paint);
paint.setXfermode(null);
// If FILL, leave the inside filled with color (this is the way e.g.
// Photoshop strokes); otherwise, the stroke will be only on the outside
// of the bitmap; the more transparent the bitmap, the more noticeable
// the effect.
if (!fill) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(opaque(bitmap, paint, false), null,
offset(rectF, -dx, -dy), paint);
paint.setXfermode(null);
}
Bitmap result = Bitmap.createBitmap(auxiliary.getWidth(),
auxiliary.getHeight(), bitmap.getConfig());
canvas.setBitmap(result);
RectF output = offset(rectF, -dx, -dy);
canvas.drawBitmap(auxiliary, null, inset(output, -radius, -radius),
paint);
// Paint bitmap.
canvas.drawBitmap(bitmap, null, output, paint);
// Antialias stroke with bitmap.
Bitmap inner = multiplyAlpha(antialias(bitmap, paint, antialias),
paint, false, factor);
canvas.drawBitmap(inner, null, output, paint);
return result;
}
private void stroke(final Bitmap bitmap, Paint paint, final float radius,
final RectF rectF, final int color, final float antialias,
final float factor, final boolean fill, Canvas canvas) {
float dx = rectF.left - radius;
float dy = rectF.top - radius;
Bitmap stroke = stroke(bitmap, paint, radius, rectF, color, antialias,
factor, fill, dx, dy);
canvas.drawBitmap(stroke, dx, dy, paint);
}
where:
antialias is the width of the antialias effect
factor modifies the antialias strength
fill indicates whether to false paint only the stroke outside, or true also fill the inside (this is the way e.g. Photoshop strokes); the more transparent the bitmap, the more noticeable the effect
rectF indicates the RectF to draw in
usage (e.g.):
stroke(bmp, paint, radius, rectF, 0xffff0000, antialias, factor, fill, canvas);
Related
in my app, i want to change text color in below image, when user clicked each word , i have position of each words in data base and draw a rectangle with canvas.drawRect but i want to change exactly color of texts in my ImageView:
my code in Imageview is :
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.parseColor("#5DFFF700"));
paint.setStyle(Paint.Style.FILL_AND_STROKE);
if (highlight) {
float rightx = thisL.maxX/scale;
float leftx = thisL.minX/scale;
float bottomy = thisL.maxY/scale;
float topy= thisL.minY/scale;
canvas.drawRect(leftx, topy, rightx, bottomy, paint);
}
//slctd word
if (slctdWord!=null) {
paint.setStyle(Paint.Style.FILL_AND_STROKE);
float wrightx = slctdWord.max_x / scale;
float wleftx = slctdWord.min_x / scale;
float wbottomy = slctdWord.max_y / scale;
float wtopy = slctdWord.min_y / scale;
canvas.drawRect(wleftx, wtopy, wrightx, wbottomy, paint);
}
}
at last i crop a part of my imageview,change color it and draw this with canvas:
(onDraw)
Bitmap bm=((BitmapDrawable)getDrawable()).getBitmap();
Bitmap crdb = changeBitmapColor(Bitmap.createBitmap(bm,
Math.round(leftx), Math.round(topy), Math.round(rightx-leftx),Math.round(bottomy-topy)));
canvas.drawBitmap(crdb,leftx,topy,null);
method changeBitmapColor
public Bitmap changeBitmapColor(Bitmap sourceBitmap)
{
Bitmap resultBitmap = sourceBitmap.copy(sourceBitmap.getConfig(),true);
Paint paint = new Paint();
ColorFilter filter = new LightingColorFilter(Color.WHITE, Color.RED);
paint.setColorFilter(filter);
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(resultBitmap, 0, 0, paint);
return resultBitmap;
}
I am using Picasso to transform an image into a rounded square image.
It also has the border. But when I put this image to Imageview, it's edge is cut. Any solution for this? Android version is 4.4.2.
My Imageview looks like this.
<ImageView
android:id="#+id/imageview_picture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/edge_margin"
android:layout_marginRight="#dimen/edge_margin"
android:adjustViewBounds="true" />
Below is code.
#Override public Bitmap transform(Bitmap source) {
int ratio = source.getWidth() / mDisplayWidth;
float strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, Constants.SHAPE_STROKE_WIDTH, mDisplayMetrics) * ratio;
float radius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, Constants.SQUARE_RADIUS, mDisplayMetrics) * ratio;
int x = (int) (mRectF.left * ratio);
int y = (int) (mRectF.top * ratio);
int width = (int) ((mRectF.right - mRectF.left) * ratio);
int height = (int) ((mRectF.bottom - mRectF.top) * ratio);
Bitmap bitmap = Bitmap.createBitmap(source, x, y, width, height);
source.recycle();
return getRoundedCornerBitmap(bitmap, radius, strokeWidth);
}
#Override public String key() {
return "card_square";
}
private Bitmap getRoundedCornerBitmap(Bitmap bitmap, int color, float cornerSizePx, float borderSizePx) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
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);
bitmap.recycle();
// draw border
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(borderSizePx);
canvas.drawRoundRect(rectF, cornerSizePx, cornerSizePx, paint);
return output;
}`
This is what I used in one of my previous project to transform an image into a rounded image:
public Bitmap getRoundedBitmap(Bitmap bitmap) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.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, bitmap.getWidth(), bitmap.getHeight());
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
bitmap.getWidth() / 2, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
I am create a custom ImageView with Pinch IN-OUT zoom and circular crop image.The Pinch in-out is wroking fine but when i trying to cropping image, can't get the particulr circle image. I'm using Pinch in-out working based on onTuchListener and Circular Cropping based on canvas class.
I have used below mentioned code for Pinch in-out and Circle Crop image:
#Override
protected void onDraw(Canvas canvas) {
onDrawReady = true;
imageRenderedAtLeastOnce = true;
if (delayedZoomVariables != null) {
setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType);
delayedZoomVariables = null;
}
super.onDraw(canvas);
if (bitmap == null) {
circleWindowFrame(); //Creating circle view
}
canvas.drawBitmap(bitmap, 0, 0, null);
}
protected void circleWindowFrame() {
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas osCanvas = new Canvas(bitmap);
RectF outerRectangle = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(getResources().getColor(R.color.overlay));
paint.setAlpha(99);
osCanvas.drawRect(outerRectangle, paint);
paint.setColor(Color.TRANSPARENT);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
float centerX = getWidth() / 2;
float centerY = getHeight() / 2;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
float radius = width / 2;
osCanvas.drawCircle(centerX, centerY, radius, paint);
}
This code for Cropping:
public static Bitmap getCrop() {
Bitmap circleBitmap;
circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setShader(shader);
paint.setAntiAlias(true);
Canvas c = new Canvas(circleBitmap);
c.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
return bitmap;
}
Thanks for Advance...
try this
public static Bitmap toOvalBitmap(#NonNull Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
int color = 0xff424242;
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
RectF rect = new RectF(0, 0, width, height);
canvas.drawOval(rect, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
bitmap.recycle();
return output;
}
I need to create shadow effect for an image .
private static Bitmap getDropShadow3(Bitmap bitmap) {
if (bitmap==null) return null;
int think = 6;
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int newW = w - (think);
int newH = h - (think);
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmp = Bitmap.createBitmap(w, h, conf);
Bitmap sbmp = Bitmap.createScaledBitmap(bitmap, newW, newH, false);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas c = new Canvas(bmp);
// Right
Shader rshader = new LinearGradient(newW, 0, w, 0, Color.GRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
paint.setShader(rshader);
c.drawRect(newW, think, w, newH, paint);
// Bottom
Shader bshader = new LinearGradient(0, newH, 0, h, Color.GRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
paint.setShader(bshader);
c.drawRect(think, newH, newW , h, paint);
//Corner
Shader cchader = new LinearGradient(0, newH, 0, h, Color.LTGRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
paint.setShader(cchader);
c.drawRect(newW, newH, w , h, paint);
c.drawBitmap(sbmp, 0, 0, null);
return bmp;
}
i used the above code, and i get two sides (Right, bottom) shadow effect . How can i make the effect in all sides including (top,Left)?
the method that you are using; may cause some problems with a certain type of images (irregular ones)
try this method instead (much much easier to understand and more flexible):
public Bitmap addShadowToBitmap(final Bitmap bm, final int dstHeight, final int dstWidth, int color, int size, float dx, float dy) {
final Bitmap mask = Bitmap.createBitmap(dstWidth, dstHeight, Config.ALPHA_8);
final Matrix scaleToFit = new Matrix();
final RectF src = new RectF(0, 0, bm.getWidth(), bm.getHeight());
final RectF dst = new RectF(0, 0, dstWidth - dx, dstHeight - dy);
scaleToFit.setRectToRect(src, dst, ScaleToFit.CENTER);
final Matrix dropShadow = new Matrix(scaleToFit);
dropShadow.postTranslate(dx, dy);
final Canvas maskCanvas = new Canvas(mask);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
maskCanvas.drawBitmap(bm, scaleToFit, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
maskCanvas.drawBitmap(bm, dropShadow, paint);
final BlurMaskFilter filter = new BlurMaskFilter(size, Blur.NORMAL);
paint.reset();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setMaskFilter(filter);
paint.setFilterBitmap(true);
final Bitmap ret = Bitmap.createBitmap(dstWidth, dstHeight, Config.ARGB_8888);
final Canvas retCanvas = new Canvas(ret);
retCanvas.drawBitmap(mask, 0, 0, paint);
retCanvas.drawBitmap(bm, scaleToFit, null);
mask.recycle();
return ret;
}
call it through:
addShadowToBitmap(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
and it will return you a bitmap.
hope this will help.
Ahmad's code was not quite right for me. I augmented his code to determine w/h of returned bitmap based on the dx/dy and shadow size. This code worked for me with a bitmap that was a signature of a user.
public Bitmap addShadowToBitmap(final Bitmap bm, int color, int size, int dx, int dy) {
int dstWidth = bm.getWidth() + dx + size/2;
int dstHeight = bm.getHeight() + dy + size/2;
final Bitmap mask = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ALPHA_8);
final Canvas maskCanvas = new Canvas(mask);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
maskCanvas.drawBitmap(bm, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
maskCanvas.drawBitmap(bm, dx, dy, paint);
final BlurMaskFilter filter = new BlurMaskFilter(size, BlurMaskFilter.Blur.NORMAL);
paint.reset();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setMaskFilter(filter);
paint.setFilterBitmap(true);
final Bitmap ret = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ARGB_8888);
final Canvas retCanvas = new Canvas(ret);
retCanvas.drawBitmap(mask, 0, 0, paint);
retCanvas.drawBitmap(bm, 0, 0, null);
mask.recycle();
return ret;
}
I need to round the corners of a Bitmap. After that, I need to add a border for it. I have done the source below to round the corners but I don't know how to draw a border in the Bitmap using Canvas. Is there any way to do it? Thanks
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawRoundRect(rectF, 20.0f, 20.0f, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
I have added the solution:
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
Paint paintStroke = new Paint();
paintStroke.setStrokeWidth(2);
paintStroke.setStyle(Paint.Style.STROKE);
paintStroke.setColor(Color.RED);
paintStroke.setAntiAlias(true);
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawRoundRect(rectF, 20.0f, 20.0f, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
canvas.drawRoundRect(rectF, round, round, paintStroke);
return output;
try this Method and pass bitmap it will return bitmap with border with angle :
Bitmap rotateAndFrame(Bitmap bitmap,float angle) {
// final boolean positive = sRandom.nextFloat() >= 0.5f;
// final float angle = (ROTATION_ANGLE_MIN + sRandom.nextFloat() *
// ROTATION_ANGLE_EXTRA) * (positive ? 1.0f : -1.0f);
final double radAngle = Math.toRadians(angle);
final int bitmapWidth = bitmap.getWidth();
final int bitmapHeight = bitmap.getHeight();
final double cosAngle = Math.abs(Math.cos(radAngle));
final double sinAngle = Math.abs(Math.sin(radAngle));
final int strokedWidth = (int) (bitmapWidth + 2 * PHOTO_BORDER_WIDTH);
final int strokedHeight = (int) (bitmapHeight + 2 * PHOTO_BORDER_WIDTH);
final int width = (int) (strokedHeight * sinAngle + strokedWidth * cosAngle);
final int height = (int) (strokedWidth * sinAngle + strokedHeight * cosAngle);
final float x = (width - bitmapWidth) / 2.0f;
final float y = (height - bitmapHeight) / 2.0f;
final Bitmap decored = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(decored);
canvas.rotate(angle, width / 2.0f, height / 2.0f);
canvas.drawBitmap(bitmap, x, y, sPaint);
canvas.drawRect(x, y, x + bitmapWidth, y + bitmapHeight, sStrokePaint);
canvas.drawText(String.valueOf(k_value),(one_piecewidth*0.84f),
(one_pieceheight*0.35f),sNumPaint);
k++;
return decored;
}
hope it helps...
edited:
private static final float PHOTO_BORDER_WIDTH2 = 3.0f;
private static final float PHOTO_BORDER_WIDTH = 1.0f;
private static final int PHOTO_BORDER_COLOR2=0x00000000;
private static final int PHOTO_BORDER_COLOR = 0xffffffff;
private static final Paint sPaint =
new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private static final Paint sStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static final Paint sNumPaint = new Paint(Paint.LINEAR_TEXT_FLAG);
static {
sNumPaint.setColor(Color.BLACK);
sNumPaint.setTextSize(25);
sNumPaint.setTextAlign(Align.RIGHT);
}
static {
sStrokePaint.setStrokeWidth(PHOTO_BORDER_WIDTH);
sStrokePaint.setStyle(Paint.Style.STROKE);
// sStrokePaint.measureText("hello");
sStrokePaint.setColor(PHOTO_BORDER_COLOR);
}