I have an ImageView subclass that I use to draw images with rounded corners. The code is based on this answer, and is as follows:
public class ImageViewRoundedCorners extends ImageView {
...
#Override
protected void onDraw(Canvas canvas) {
Bitmap scaledBitmap = Bitmap.createBitmap(getMeasuredWidth(),
getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas scaledCanvas = new Canvas(scaledBitmap);
super.onDraw(scaledCanvas);
drawRoundedCornerBitmap(canvas, scaledBitmap,
getMeasuredWidth(), getMeasuredHeight());
scaledBitmap.recycle();
}
protected void drawRoundedCornerBitmap(Canvas outputCanvas, Bitmap input, int w, int h) {
Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
mPaint.reset();
mPaint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(mClipPath, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(input, 0, 0, mPaint);
outputCanvas.drawBitmap(output, 0, 0, null);
}
}
With this code, the image is drawn with properly rounded corners. To avoid the allocations on the first two lines of drawRoundedCornerBitmap, I want to draw directly to outputCanvas, which is the canvas originally passed to onDraw. The new implementation looks like this:
protected void drawRoundedCornerBitmap(...) {
mPaint.reset();
mPaint.setAntiAlias(true);
outputCanvas.drawARGB(0, 0, 0, 0);
mPaint.setStyle(Paint.Style.FILL);
outputCanvas.drawPath(mClipPath, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
outputCanvas.drawBitmap(input, 0, 0, mPaint);
}
For some reason, this code seems to ignore the Porter-Duff mode, and instead just draws the image with normal (non-rounded) corners. Why is this the case? What is it about drawing to an intermediate Bitmap that makes the original code work?
Create a drawable Romain Guy has done this for you. We are not a link factory but his blog post explains it quite extensively and provides an efficient way of doing this. Rounded Corners
The real basic principle, is create a BitmapShader and attach it to a Paint object which draws in a custom Drawable that way you just apply that Drawable to the ImageView.
Using a drawable means that the Image is only painted to a canvas once, meaning that drawing the image is only done once, then all the ImageView does is just scale the drawable.
Related
I'm trying to put text over map marker but it always appears under it.
First I convert drawable to bitmap and then draw text on it. drawable to bitmap conversion works fine, I only have a problem with text overlay.
I have already tried these:
Adding text to a bitmap in memory in Android
https://stackoverflow.com/a/7328777/3423468
https://stackoverflow.com/a/8831182/3423468
and many more with no luck.
This is my current method:
Bitmap drawableToBitmap(Drawable drawable)
{
var bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
var canvas = new Canvas(bitmap);
if (shouldDrawText)
{
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStrokeWidth(40);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); // Text Overlapping Pattern
//canvas.drawBitmap(bitmap, 0, 0, paint);
canvas.drawText("Testing...", 10, 10, paint);
}
drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
drawable.Draw(canvas);
return bitmap;
}
Any ideas what I'm doing wrong?
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); // Text Overlapping Pattern
Here you use SRC_OVER, which means the source will be over the DST will be under. The DST is the new pixels to be drawn.
You should use DST_OVER to draw the new pixels on top of the old pixels.
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); // Text Overlapping Pattern
See here an overview of how porterduff works
I am drawing text on a canvas. I would like to draw a solid circle of color over the text, and only have the circle be painted where it intersects the text. Example:
and what I want to do:
I'm not sure if this is possible, my draw code is simply:
public void onDraw(Canvas canvas) {
canvas.drawText("Hello", x, y, paint);
paint.setColor(orange);
canvas.drawOval(...);
}
I suppose I would need to apply some masking, but not sure how to get started.
follow this tutorial from a googler...
android-shaders-filters
BitmapShader may help you
You can use PorterDuffXfermode in Android to achieve this.
If you use below code it will work fine:
Bitmap original = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(),
Bitmap.Config.ARGB_8888); // Created from Canvas
Bitmap mask =
Bitmap.createBitmap(getContext().getResources(),R.drawable.mask_image);
Bitmap result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(),
Config.ARGB_8888);
Canvas tempCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
tempCanvas.drawBitmap(original, 0, 0, null);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
tempCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
canvas.drawBitmap(result, 0, 0, new Paint());
What does PorterDuff.Mode mean in android graphics.What does it do?
I'm using the mask a bitmap with another. The operation succeeds well, unfortunately the result of masking seen a slight black border, as you can see in the image:
How do I remove this border? in the source image is not there.
I'll post the code I'm using:
public Bitmap mask(Bitmap source) {
Bitmap targetBitmap = Bitmap.createBitmap(getWidth(),getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(targetBitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
paint.setAntiAlias(true);
paint.setDither(true);
canvas.drawBitmap(source, 0, 0, null);
canvas.drawBitmap(getMask(), 0, 0, paint);
paint.setXfermode(null);
return targetBitmap;
}
where getMask () returns the Bitmap that represents the figure of the Puzzle.
I hope to receive your help, thank you all
Sorry for my english :-)
UPDATE:
the black border is what I point out in this picture:
UPDATE:
place the sequence of transformation. The third image would be identical to the first but without color. The problem is the black edge of the puzzle.
I hope to be more clear:
The way I draw images with mask is kind of the other way around from what you do.
public Bitmap mask(Bitmap source) {
Bitmap targetBitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(targetBitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// paint.setAntiAlias(true); // you've already set this in the constructor
paint.setDither(true);
canvas.drawBitmap(getMask(), 0, 0, null);
canvas.drawBitmap(source, 0, 0, paint);
// paint.setXfermode(null); // no need for this
return targetBitmap;
}
Note that PorterDuff.Mode is set to SRC_IN (not DST_in) and that the mask is drawn first and then the image on top of that mask. With this approach you can also draw the previous source as the base mask, add the new (puzzle) mask and then draw the final source/image on top of that with SRC_IN paint to add new puzzle pieces each time.
If that doesn't solve the black border, check that your mask doesn't have feathered (transparent) edges that might be causing these problems.
Also, ANTI_ALIAS_FLAG doesn't do anything on textures. If you want smoothly scaled textures use paint.setFilterBitmap(true);
I need to overlay two images in live wallpaper. The overlay images is the jpg which needs to be set to "additive" overlay. it adds the pixel value rather than calculating the transparency. how can i achieve this in android ?
You can make use of Android's Bitmap and Drawable classes mixed with Canvas, and try something like in this snippet:
public static Drawable mergeImage(Drawable orig, Drawable over, int left, int top) {
Bitmap original = ((BitmapDrawable)orig).getBitmap();
Bitmap overlay = ((BitmapDrawable)over).getBitmap();
Bitmap result = Bitmap.createBitmap(original.getWidth(), original.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(original, 0, 0, paint);
canvas.drawBitmap(overlay, left, top, paint);
return new BitmapDrawable(result);
}
I've coded a photo image gridview overlayered with "online status" using the above lines. Hope that it works for you too.
A more general approach may be to create a PorterDuffXfermode with your wanted PorterDuffMode and then set it on the Paint object that you use with your canvas, as referenced in mthama's answer but substituting some lines. This allows you to use other Porter-Duff modes as wanted/needed.
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(original, 0, 0, paint);
paint.setXferMode(new PorterDuffXferMode(PorterDuff.Mode.OVERLAY));
canvas.drawBitmap(overlay, left, top, paint);
Mind you, I haven't tried this, so go with mthama's answer. :)
Is there a way to set a background image for a rectangle drawn in a canvas ?
For exemple i have the following onDraw method :
protected void onDraw(Canvas canvas) {
this.setBackgroundGradient();
RectF rect = new RectF();
rect.set(0, 0, canvas.getWidth(), 50);
canvas.drawRoundRect(rect, 0, 0, this.paint);
}
private void setBackgroundGradient()
{
this.paint.setShader(new LinearGradient(0, 0,0, getHeight(), 0xff919191, 0xff424242, Shader.TileMode.MIRROR));
}
I would like to change my gradient by a background image (repeatable if possible).
Note : i would rather to keep rectangle and not use drawBitmap.
A Rect is not a drawable, it is a convenience class and only holds the four values that define the rect. Canvas knows how to draw a rect with the Paint object you give it.
If you want to have a background (image) instead of a rect, then you either use drawBitmap on the canvas or have a (bitmap)drawable that you pass the canvas to when drawing.