Custom font rendering on android 4.4 has hollow transparent text - android

Something weird is happening on android 4.4 with one of my apps. I am using a custom view in which I set a custom typeface with Typeface.createFromAsset. The rendering works fine on the device/emulator as you can see in the image above. The problem appears when I save the content of the custom View as a bitmap:
Bitmap currentBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(currentBitmap);
view.draw(canvas);
Everything works fine on android < 4.4. The saved image looks the same as the one visible on the screen. However, on 4.4 I only see the font borders and the inside of text is transparent.
How can I fix this ?

When drawing text on a canvas in API 19 and above, you have to be mindful of the stroke and fill for the Paint object that you're using. In prior versions of Android, text was always drawn filled, even when using the stroke style. In KitKat, setting the style to stroke will do exactly that; stroke the text rather than fill it. You will need to manually set the paint style to fill when rendering text and then re-set it to stroke for drawing other things (if that's what you're trying to do).
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTextPaint.setStyle(Style.STROKE);
// do other setup on Paint object
// Draw non-text stuff
mTextPaint.setStyle(Style.FILL);
// do other setup on Paint object
// Draw all the text stuff
}

Related

Clip content of ActivityView

i am implementing a custom launcher in Android, displaying 3rd party apps using ActivityViews.
I want to clip the content of these ActivityViews to a custom shape (other than Circle, Rectangle, RoundedRectangle, Ring...).
I already tried to call clipPath/drawPath on the dispatchDraw canvas of a parent viewgroup element which works fine for all children except the ActivityView. Even the ActivityView itself and its referenced SurfaceView seem to be clipped according to my given path (if i add a solid color for testing). But the rendered content remains unchanged. Manipulating the SurfaceView canvas (which you receive by calling getHolder().lockCanvas()) doesnt have any effect, too.
I think this has something to do with the virtualdisplay and/or various SurfaceControls which are used by the ActivityView, but i dont have any clue how to set clipping areas/paths for those classes.
Does anyone have an idea how to solve this?
Hint: i cannot paint over the ActivityViews content as i want to display the system wallpaper in the transparent areas.
finally setting up the PorterDuffXfermode correctly solved the issue. Just overwrite the dispatchDraw method of the parent viewgroup and erase the required areas using PorterDuff.
Sometimes it looks like the direction does matter how the path for drawPath is created (CW or CCW). So maybe try both.
Unfortunately this approach does not work if the hosted activity uses a SurfaceView on its own.
protected void dispatchDraw(Canvas canvas) {
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(getContext().getColor(android.R.color.white));
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
int save = canvas.save();
super.dispatchDraw(canvas);
canvas.drawPath(path, p);
canvas.restoreToCount(save);
}

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 view clipping/subtracting

Is there any way in Android (from API 15) to clip/subtract views like masking in photoshop?
See the example below:
https://s31.postimg.org/d18lktjq3/index.jpg
The Red view is just a bold funny V shape, while the blue one is something more complex.
Note that in the red view, the striped part is transparent.
The result view i would like to obtain, is something like the blue big view, with the V shape of the second view, and anything above cut away.
Note that, in the final result, the space "inside" the V shape must be transparent.
Currently i achieve this effect using the two views one on top of the other (filling the gap in the V shape view), but this is not optimal because i have to know exactly what the other view is, and the summed up view is bigger than the source.
Thank you
In Android, this is done using Porter-Duff Transfer Modes.
Your best best for this is to have two overlay bitmaps: One that has the red V-shape, and a complementary bitmap that represents everything you want to cut out of the lower layer.
With a custom view, you override onDraw() to do these steps:
Draw the base (blue) bitmap
Draw the red stripe using Porter-Duff mode SRC_OVER
Draw the top V cutout using Porter-Duff mode CLEAR
Code would look something like this (assuming you have created the bitmaps and computed the x,y coordinates where you want to draw them):
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(blue_base, blueX, blueY, paint);
// draw the red v on top of the blue part
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
canvas.drawBitmap(red_v, redX, redY, paint);
// erase the unwanted pixels from the blue part
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(cut_out, redX, redY, paint);
Here's an interesting tutorial to get you started: Punch a hole in a bitmap by using Android's porter-duff Xfer - TechRepublic

Expanding a ViewGroup's surface to include text shadows

In an Android project, I am using FrameLayout to contain a TextView. This TextView has a large shadow on it (to provide a glow). However, Android is detecting the TextView's bounding rect to be just that of the contained text, without the shadow, and as a result the compositor gets confused when objects animate around it.
As a workaround I tried forcing the TextView to have its own hardware surface (with setLayerType), but this too is closely cropped to the text and doesn't account for the shadow, so the shadow gets cut off.
I have tried adding padding to the FrameLayout, but that doesn't expand the surface - it only moves the TextView down and right by the padding amount.
If I set a background color on the FrameLayout, the surface does expand to cover its entire size, but unfortunately this background is visible, even if I set it to 0x01000000. If there were some way to force it to contain the entire background even if the background color is 0, that would be a suitable solution.
What is the easiest way to expand the hardware surface to include the text glow, ideally without affecting the position of the text itself?
The trick is to get Canvas to believe that every pixel has been touched, without actually having anything to paint. Simply drawing a ColorDrawable does not work, because every step along the way seems to detect that a fully-transparent color doesn't need to be rendered. And, apparently the render for the text shadow is also only rendering where the canvas thinks it's been invalidated – so, for example, only drawing a couple of background pixels will expand the surface but still does not cause the actual text shadow to be rendered.
However, there is an operation that will cause Canvas's clipping mask to get updated even with transparent pixels – a transparent Bitmap!
So, adding a background Drawable that renders out a transparent Bitmap has the desired effect. You can add a transparent bitmap as a resource (drawn using a BitmapDrawable), or you can do this:
final Bitmap bmp = Bitmap.createBitmap(2, 2, Bitmap.Config.ALPHA_8);
final Paint bmpPaint = new Paint();
wrappingFrameLayout.setBackground(new Drawable() {
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public void draw(final Canvas c) {
c.drawBitmap(bmp, null, getBounds(), bmpPaint);
}
public void setAlpha(final int a) {}
public void setColorFilter(final ColorFilter cf) {}
});

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());
}

Categories

Resources