How to draw a circle on a bitmap without filling it? - java

I am ddrawing a circle on the bitmap and setting this bitmap to ImageView. the circle is drawn correctly but i dont want it to be a filled circle is there any way to make the filled area transparent?
i am using the following code
Bitmap bmp = RasterImageConverter.convertToBitmap(_loadedImage, ConvertToImageOptions.NONE.getValue());
Canvas c = new Canvas(bmp);
c = new Canvas(bmp);
myimgview.draw(c);
Paint p = new Paint();
p.setColor(Color.RED);
float x=(float) circleX;
float y=(float) circleY;
float Tx=(float) textX;
float Ty=(float)textY;
// c.drawLine(x, y, xend, yend, p);
c.drawCircle(300, 300, 200, p);
c.drawText(myText, Tx, Ty, p);
myimgview.setImageBitmap(bmp);

You need to inform the paint that you dont' use a fill style, but a stroke style.
The default is FILL
The Style specifies if the primitive being drawn is filled, stroked, or both (in the same color). The default is FILL.
So your code must be:
Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);
Here are explained the differences between the different styles.
STROKE
Geometry and text drawn with this style will be stroked, respecting the stroke-related fields on the paint.
FILL
Geometry and text drawn with this style will be filled, ignoring all stroke-related settings in the paint.
FILL_AND_STROKE
Geometry and text drawn with this style will be both filled and stroked at the same time, respecting the stroke-related fields on the paint.

You need to change the Paint style to stroke:
Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);

Use the setStyle(Paint.Style.STROKE) method on your paint p instance.

You are almost there... you need to set the style of the Paint object...
by invoking the method
p.setStyle(Paint.Style.STROKE);
The options are:
Example:
Bitmap bmp = RasterImageConverter.convertToBitmap(_loadedImage, ConvertToImageOptions.NONE.getValue());
Canvas c = new Canvas(bmp);
c = new Canvas(bmp);
myimgview.draw(c);
Paint p = new Paint();
p.setColor(Color.RED);
p.setStyle(Paint.Style.STROKE);

Related

customize the stroke to have a border and a fill

How can I draw a stroke with a fill color and a (different color) border?
e.g. I want something like this:
I tried creating 2 paints - one with a Stroke style and one with a Fill style, but calling
strokePaint = new Paint();
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setColor(Color.parseColor("#A3A3A3"));
fillPaint = new Paint();
fillPaint.setStyle(Paint.Style.FILL);
fillPaint.setColor(Color.WHITE);
canvas.drawPath(totalPath, strokePaint);
canvas.drawPath(totalPath, fillPaint);
doesn't create the intended effect and looks quite bad.
Is it even possible?
Figured it out. The trick is to draw it twice, once as a background layer that is 1-2 pixels thicker, and then the foreground layer.
i.e. :
strokePaintBackground = new Paint(Paint.ANTI_ALIAS_FLAG);
strokePaintBackground.setStyle(Paint.Style.STROKE);
strokePaintBackground.setColor(Color.BLACK);
strokePaintBackground.setStrokeWidth(8);
strokePaintBackground.setPathEffect(new DashPathEffect(new float[]{30, 15}, 0));
strokePaintBackground.setStrokeCap(Paint.Cap.ROUND);
strokePaintBackground.setStrokeJoin(Paint.Join.ROUND);
strokePaintForground = new Paint(Paint.ANTI_ALIAS_FLAG);
strokePaintForground.setStyle(Paint.Style.STROKE);
strokePaintForground.setColor(Color.WHITE);
strokePaintForground.setStrokeWidth(6);
strokePaintForground.setPathEffect(new DashPathEffect(new float[]{30, 15}, 0));
strokePaintForground.setStrokeCap(Paint.Cap.ROUND);
strokePaintForground.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(totalPath, strokePaintBackground);
canvas.drawPath(totalPath, strokePaintForground);

Unable to modify Bitmap to be transparent in Android

I am trying to draw a transparent circle on a Bitmap in android. I have three primary variables:
mask = Bitmap.createBitmap(this.getWidth(),this.getHeight(), Bitmap.Config.ARGB_8888);
Canvas can = new Canvas(mask);
Paint clear = new Paint();
If I do the following, I get my expected results:
clear.setColor(Color.TRANSPARENT);
can.drawRect(new Rect(0,0,this.getWidth(),this.getHeight()),clear);
However, if I draw something else on the canvas first, then try to clear it out with transparency, the old data remains. For example:
clear.setColor(Color.argb(255,255,0,0));
can.drawRect(new Rect(0,0,this.getWidth(),this.getHeight()),clear);
clear.setColor(Color.TRANSPARENT);
can.drawRect(new Rect(0,0,this.getWidth(),this.getHeight()),clear);
I only see a giant red square. The bottom two lines are supposed to "erase" the filled red to make it transparent again. Ultimately the mask is drawn on another canvas like this:
#Override
public void onDraw(Canvas c)
{
c.drawBitmap(mask,0,0,null);
super.onDraw(c);
}
As it turns out it does have to do with the Paint object and setting the Xfermode...
mask = Bitmap.createBitmap(this.getWidth(),this.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas can = new Canvas(mask);
Paint clear = new Paint();
clear.setColor(Color.argb(255,255,0,0));
can.drawRect(new Rect(0,0,this.getWidth(),this.getHeight()),clear);
PorterDuffXfermode xfer = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
clear.setXfermode(xfer);
clear.setColor(Color.TRANSPARENT);
can.drawCircle(this.getWidth()/2, this.getHeight()/2, this.getHeight()/2, clear);

RadialGradientPaint Equivalent in Android

I am drawing a radial gradient circle on an image like this
I have java code for this
private void drawRadialGradientCircleJava(String imagePath, double posX, double posY, float radius, String outputPath) throws IOException{
BufferedImage city = ImageIO.read(new File(imagePath));
BufferedImage mask = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
Color transparent = new Color(255, 0, 0, 0);
Color fill = Color.RED;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point2D.Double(posX, posY),
radius,
new float[]{0f, 0.75f, 1f},
new Color[]{transparent, transparent, fill});
g2d.setPaint(rgp);
g2d.fill(new Rectangle(0, 0, mask.getWidth(), mask.getHeight()));
g2d.dispose();
BufferedImage masked = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = masked.createGraphics();
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, masked.getWidth(), masked.getHeight());
g2d.drawImage(city, 0, 0, null);
g2d.setComposite(AlphaComposite.DstAtop);
g2d.drawImage(mask, 0, 0, null);
g2d.dispose();
ImageIO.write(masked,"png", new File(outputPath));
}
I want to do same thing in Android, I have an image view in which I have an image, now I want to touch a point in image and draw this transparent circle around that point
I have following Android code as well but id doesn't draw anything on the image
private void drawRadialGradientCircleAndroid(ImageView imageView, float posX,
float posY, float radius) throws IOException {
RadialGradient gradient = new RadialGradient(posX, posY, radius, Color.TRANSPARENT,
Color.TRANSPARENT, android.graphics.Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setDither(true);
p.setShader(gradient);
Bitmap bm = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap bmOverlay = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bm, new Matrix(), null);
canvas.drawCircle(posY, posX, radius, p);
imageView.setImageBitmap(bmOverlay);
}
Please help how can I achieve this in Android.
We should migrate this to the answer boxes.
OP has basically got it here- and in fact the OP's revised gist is brilliant.
Some general tips regarding the first attempt in the question:
1) In protected void onSizeChanged(int w, int h, int oldw, int oldh):
width = w; there is no reason why you can't call getWidth() when you require this. The reason it's advisable is because the View's internal width is set quite late after onMeasure. Consequently, onDraw may be the next time you want a most up to date version, so use the getter there.
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);. Creating a bitmap is an expensive and memory intensive operation. Unless you want to write a bitmap to a file, or send it to a BitmapDrawable for an ImageView or something, you don't need to do this. Especially with effects drawn onto the UI with android's graphics library.
mCanvas = new Canvas(mBitmap); followed by a draw operation onto the new canvas. This is never needed. And yet I've seen it (not work) in many code bases and attempts. I think it's the fault of an old stack overflow post that got people doing this so that they could transform a canvas on a custom view without effecting the drawing onto the rest of the canvas. Incidentally, if you need this, use .restore() and .save() instead. If you see new Canvas, be suspicious.
2) onDraw(...):
Yes, you need to avoid doing things in onDraw, like, creating objects, or any heavy processing. But you still need to do the things in onDraw you need to do in onDraw!
So here you simply need to call : canvas.drawCircle(float cx, float cy, float radius, Paint paint) with arguments as per the docs.
This really isn't that sinful for onDraw. If you're worried about calling this too much, as might be the case if your entire button is animating across the screen, you need to use hardware acceleration available in later API versions, as will be detailed in an article called Optimizing the View; very helpful reading if you're using lots of custom drawn views.
3) That pesky radial gradient. The next issue you had is that you quite rightly created your paint in an initmethod so that the object creation was off the draw. But then quite rightly it will have IllegalArgumentExceptioned (I think) on you because at that stage the getHeight() of the view was 0. You tried passing in small pixel values- that won't work unless you know some magic about screen sizes.
This isn't your issue as much as the annoying view cycle at the heart of Android's design patterns. The fix though is easy enough: simply use a later part of the view's drawing process after the onMeasure call to set the paint filter.
But there are some issues with getting this right, namely that sometimes, annoyingly, onDraw gets called before the point at which you'd expect it. The result would be your paint is null and you wouldn't get the desired behavior.
I have found a more robust solution is simply to do a cheeky and naughty little null check in the onDraw and then once only construct the paint object there. It's not strictly speaking optimal, but given the complex way in which the Paint objects hook up with Android's graphics native layer better than trying to straddle the paint configuration and construction in many frequently called places. And it makes for darn clearer code.
This would look like (amending your gist):
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setShader(new RadialGradient(getWidth() / 2, getHeight() / 2,
getHeight() / 3, Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));
}
width = getWidth();
height = getHeight();
canvas.drawCircle(width / 2, height / 2, height / 3, mPaint);
}
So note a few changes- I think from your description you want the two colours swapped round in the arguments, also don't forget to center the center of your gradient in your view: width/2 and height/2 arguments.
Best of luck!

Android Canvas drawRect function doesn't show paint shadow

I am trying to use Android's onDraw function to draw rectangles and lines with shadows around them so they can be seen on a white backgrounds. I have my Paint set up to have a shadowlayer but there is no shadow when the lines are drawn.
Here is my code for the Paint:
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(m_textSize);
paint.setAntiAlias(true);
Typeface font = Typeface.create("Times New Roman", Typeface.NORMAL);
paint.setTypeface(font);
paint.setShadowLayer(5, 0, 0, Color.BLACK);
this.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
And here is my drawing code:
private void drawMark(Canvas c, float y, float size)
{
float x = (float) (getWidth()-5.0-size);
c.drawRect(x, y, x + size, y + markHeight, paint);
}
Is there something I am missing to make the shadow work for drawRect?
Please note that I am also using the canvas to draw text and the text does get the shadow effect, but shapes and lines do not.
Thanks
The shadows will only appear when you're drawing in software mode:
this.setLayerType(View.LAYER_TYPE_SOFTWARE, paint);

Add text with grey background to pictures like Snapchat does? Android/Java

Bitmap newBm = ...
Canvas canvas = new Canvas(newBm);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setTextSize((int) (44 * scale));
Rect bounds = new Rect();
paint.getTextBounds(gText, 0, gText.length(), bounds);
canvas.drawText(gText, x, y, paint);
I drew text on the Bitmap like so. How could I get a grey background that is the same height as the text but covers the whole screen??
You could use a Rect. Before drawing the text draw the Rect to the screen:
int screenWidth = getApplicationContext().getResources().getDisplayMetrics().widthPixels;
Rect greyBack = new Rect(0,top,screenWidth,bottom);
Paint paint = new Paint();
paint.setARGB(128, 100, 100, 100); //added alpha because Snapchat has translucent //grey background
canvas.drawRect(greyBack, paint);
top and bottom need to be coordinates above and below the text. You could use y's value and take away a bit for top and add a bit for bottom. How much you add/subtract is up to you and changes the height of the greyBack background.
The best way to see and learn how these sort of things are done with well written code is to look at the android source code itself. For example here is the onDraw method for a TextView it includes additional stuff you won't probably need like compoundPadding, but you can follow it through and get the basic concept of how it's done.

Categories