Drawing drawable using canvas - Clear destination - android

I have a custom view. I create a fullscreen transparent overlay using full screen rectangle by calling this method in onDraw:
private void drawOverlay(Canvas canvas) {
canvas.drawRect(0, 0, this.getWidth(), this.getMeasuredHeight(), backgroundPaint);
}
My screen is now overlayed with transparent background I defined earlier.
Now, I want to draw a Drawable on the screen. A drawable can be have a transparent parts on it. It can be, for example a shape , defined like this:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#color/transparent"></solid>
<stroke android:width="10dp" android:color="#ffff0716"/>
</shape>
Now I want to draw that drawable on the screen. I draw it using this method:
private void drawDrawableOverTargetView(Canvas canvas) {
Rect rect = new Rect(200, 200, 450, 400); //TODO
Drawable drawable = tutorial.getHighlightDrawable();
drawable.setBounds(rect);
drawable.draw(canvas);
}
The inner part of my oval is defined transparent in xml. When displayed, the inner part of the oval will be the overlay color, because the overlay rectangle is below it.
The question is: Is it possible to define that drawable should override the color of background rectangle, meaning that the inner part of it becomes transparent ? Is it possible to remove the part of overlay rectangle which is below my drawable?
I tried using
drawable.setColorFilter(Color.Transparent, PorterDuff.Mode.Clear);
But it doesn't seem to work :/

You're making it way harder than you need to. Canvases maintain what is on them in pixels not objects. When a part of that canvas is invalidated it calls all the draw code relevant there and asks for the new pixels. Just code it up so that it draws the way you'd like it to, and invalidate the area. It'll call up the draw routine again and put them in whatever order you draw them in.
The canvas doesn't recall the background color under objects because there's just pixels not objects. The only sense wherein there are objects is where you draw objections, which causes a rendering of that on the canvas. But you can't remove it. You can however declare the area where that object was drawn to be invalid and upon refresh have it not draw that object the next time draw gets called.
Just make sure you call up the stuff in the order you'd like, as what you'd like changes, have the code draw it in that order.
You can't change the stuff already on the screen after the fact except by one of the blending operations, the Porter-Duff operations simply deal with all the logical ways you can manipulate pixels on a screen. CLEAR for example will restore that area to pure transparent. Which is basically just clearing that section of the screen. There's no circle on the screen. You are writing transparent pixels to a set of pixels that looks like a circle. Don't do that. When you don't want that circle thing, declare the picture invalid and when it calls the draw routine again, do not draw that circle.
After they are drawn, you're modifying pixels not things.

Related

Android Dynamic Gradient Drawing

Please see the image attached.
I would like to draw a gradient on the edges of the blue force field around the car, as seen in the image below.
The problem is that the gradient can appear on any edge of the blue square (depending on user input) and has to be oriented towards the center. In addition, it is also possible only small sections on the edge will have a gradient.
I have tried several different approaches and cannot get a decent solution.
Currently I am using a gradient defined in xml like this:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:type="radial" android:gradientRadius="75"
android:startColor="#color/warningColor"
android:endColor="#00d8345a"/>
</shape>
and then using it in java and drawing it using a bitmap on a SurfaceView canvas:
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.circle_gradient, null);
mGradientBmp = Bitmap.createBitmap(250, 250, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mGradientBmp);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
The input to the application is currently received through touch input where the user can touch anywhere on the screen. I am keeping a list of all touch points while event.getAction == MotionEvent.ACTION_MOVE and draw the bitmap above in each of those locations, like so:
public void drawScreen(Canvas canvas) {
long c = System.currentTimeMillis();
canvas.drawColor(Color.WHITE);
canvas.setMatrix(mPerspectiveMatrix);
canvas.drawBitmap(mRoadBmp, 0,0, null);
canvas.drawBitmap(mForceFieldBmp, null, mForceFieldRect, null);
synchronized (mTouchPointsList) {
for (PointF p : mTouchPointsList) {
canvas.drawBitmap(mGradientBmp, p.x, p.y, null);
}
}
}
I have a couple of questions.
How would you suggest drawing the gradient (only around the edge of the blue force field) and keeping the gradient oriented towards the center?
How do I keep track of the touch points efficiently? I am currently using a CopyOnWriteArrayList because the input and drawing are on separate threads.
My current solution almost works (besides for being inefficient with the list so it slows down after a while) but the drawing needs to be limited to inside the blue force field.
Any suggestions on how to approach this would be greatly appreciated.
Thanks

create shadow for bitmap in custom view

I have a custom view wich contain some bitmaps and I want to set shadows for them, for that, I use this code:
shadowPaints=new Paint(Paint.ANTI_ALIAS_FLAG);
shadowPaints.setShadowLayer(10.0f, 3.0f, 2.0f, Color.BLACK);
canvas.drawBitmap(bmp, matrix, shadowPaints);
setLayerType(LAYER_TYPE_SOFTWARE, shadowPaints);
and my result is
as you can see my shadow actually is another bitmap with different x and y position but what I want is my shadow be a solid color
bitmap.
can anyone help me about this?
setShadowLayer is actually meant for putting shadows on text.
If you already know the bitmap you want to draw, you can just add a shadow in PhotoShop and draw the bitmap and shadow all at once.
If you don't want to do that, you could make a shadow by making a copy of the image, using a PorterDuff filter to make it all grey, use Renderscript to blur the image, and draw it on the canvas at an x,y offset before drawing the actual image on top of it.
Personally, I think PhotoShop is a lot easier.

Android canvas draw path with multiple "drawing tools" and eraser vector (avoid bmps and use transparent bg)

I am working on a program that lets you draw highlights, pen and text comment mark-up. I have a working version where I recycle a bmp 3 times then draw it to the canvas.
The issue is my app has 3 of these views in memory, so that is a lot of bmps being created.
In my current solution, I am trying to avoid bmps. I have extended an ImageView and added the following to the Draw event:
`
public override void Draw (Canvas canvas)
{
base.Draw (canvas);
HighlightLayer.Draw(canvas, Width, Height);
PenLayer.Draw(canvas, Width, Height);
TextLayer.Draw(canvas, Width, Height);
}
`
Note that I am using monodroid so it's C# (but nearly the same as Java) and all the "Layer" objects are a subclass of Drawable. Just assume they have draw path commands etc.
Anyway, it sort of works except all the highlights / pen have black boxes around them.
When I draw a line (that's not an eraser), I use PorterDuff.Mode.Src.
I tried to combine the 3 into one bmp instead of recycling it but the issue was that if I had an eraser vector it would erase anything that is already drawn. So an erased pen line would also erase the highlights ...
Is it possible to "freeze" a drawing so that as you continue to draw paths on the canvas and change the PorterDuff mode to clear it will not effect anything marked as freeze?
I know enough to know that the black box I am seeing around my lines on my Drawables is due to the background bmp not being set. The problem is I want them to be layers on top of the ImageView. If they each retained a background then you could not see the Drawables underneath. How can I use a drawable with the background of the "parent ImageView" and avoid the black background it assumes to be there.
Somehow I need the background of the "parent" ImageView to be applied to the Drawables when it's choosing the transparent fill to avoid the default black.
SOLUTION:
Using the suggestion below I created a LayerDrawable and added Drawables for each mark-up tool. In the Drawable, before drawing the lines I added the following code:
public override void Draw (Canvas canvas)
{
canvas.SaveLayer(new RectF(0,0,Bounds.Width(),Bounds.Height()), null, SaveFlags.All);
canvas.DrawColor(Color.Transparent, PorterDuff.Mode.Clear);
GraphicsUtil.DrawVector(canvas, Lines, ToolID, Bounds.Width(), Bounds.Height());
}

Creating a "cut out" layout

Right now, I have it so that my layout consists of a Fragment that takes up the entire screen and displays an image. I want to make it so that an additional View exists on top of it, also taking up the entire screen. On that top layer, I want to be able to color it all black initially, and then create certain spots that are transparent (alpha?) and reveal the image displayed on the fragment behind it. So basically the screen will be all black except for a few spots where the image behind is showing through, which I would determine programmatically. I've looked into a bunch of the graphics and views that Android provides, but have no clue where to start. Is this suited for a SurfaceView if I just want it to be all black with some spots of alpha?
Once I select the correct view to use, I'm assuming that I just override the onDraw() method and then do something like canvas.setBody(black) and then add shapes of alpha to it? Will the shapes correctly affect the background color?
For your masking View, you can make a custom view that can keep track of which areas to unmask (as Rects or something similar) and then draw them in onDraw(Canvas) like this:
public class MaskView extends View {
private Set<Rect> mRects = new HashSet<Rect>();
private Paint mUnmaskPaint = new Paint();
{
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
/**
* Add an unmasking rectangle to this view's background.
*
* #param rect
* a rectangle used to unmask the background
*/
public void addUnmaskRect(Rect rect) {
mRects.add(rect);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (Rect r : mRects) {
canvas.drawRect(r, mUnmaskPaint);
}
}
}
Your Fragment (or whatever is keeping track of the unmask areas) passes Rects to MaskView via addUnmaskRect(Rect). Whenever the view is redrawn (remember to call invalidate() each time you are done passing Rects) it is first filled with black, and then has any rectangles unmask the black background. The coordinates for your rectangles must be set to the coordinate space of the view, but if the underlying image occupies the exact same area it should be relatively simple (you can also look at the View.getLocationInWindow(int[]) method to help you with this as well).

How to draw alpha on an image / setting pixels invisible - on fingertouch

I want to create a little scratch game. The problem is, that I can't figure out how to erase pixels from an image in android (like the eraser in gimp / photoshop).
The image is an .png with alpha channel.
AIUI, drawing operations on a canvas blend a transparent pixel with the prior value of the pixel. This is by default, and you can see it by setting a canvas to black and drawing a fully transparent shape onto it, and then drawing the underlying bitmap over an image of another canvas (result: a fully black canvas), or by setting a canvas to a partially transparent color and, drawing a shape of another partially transparent color, and then drawing this over an image (result: the original image is tinted by the first color, outside the shape; within the shape, it's tinted by both transparent colors). I don't know the blending method used by default, and looking through the docs just makes me wish I knew what book to buy so I can understand how to use what's available.
So I would set pixels to transparent by setting them 'directly', with Bitmap methods, rather than with canvas operations. Although if you need to punch a transparent shape into an overlay, you can draw the shape with a solid non-transparent color, and then manipulate the bitmap directly, mapping this color to the transparent color.
Bitmap docs. Prefer getPixels() and setPixels() to a method-call per pixel.
EDIT: ...er, did I misunderstand? You want to 'erase' pixels as in a paint program? Then just draw whatever the background color is. There's no erasure involved.

Categories

Resources