Canvas antialiasing with hardware acceleration (An

2019-02-10 13:53发布

问题:

I have a simple bitmap that I draw within a canvas & that I rotate using a matrix.

The problem I bump into is that using hardware acceleration the edges are not anti-aliased when rotated (this is working perfectly with Hardware acceleration turning off). And of course things like “setDrawFilter” are useless as they are ignored when hardware acceleration is turned on!

canvas.setDrawFilter(new PaintFlagsDrawFilter(1, Paint.ANTI_ALIAS_FLAG));

Am I missing something or is it just a limitation of the hardware rendering method ? Is there any alternative?

回答1:

Setting the antialias flag on the paint would not help anyway. To get antialiased borders on bitmap when rotating them you should add a 1px transparent border around them.



回答2:

Adding this 1px transparent border is not as easy as I thought. Adding it at runtime is really messy. My app loads a lot of bitmap and to add this border I need to allocate even more bitmap to redraw them. This really bust the heap but as I took good care with avoiding any memory leak, it´s working as intended. What’s really killing me is the “lags” that this has introduced. I tracked down this fluidity lost to the GC. This last one seems to be slowing down the UI Thread when he claim back the recycled bitmaps. Using a cache is not the solution either. This is the best way towards performance but storing 500x500 PNGs (alpha obliged) is way out of proportion. So, here I am, hitting the wall. I haven’t yet tried a “draw time” way but my guess is that drawing to an off-screen bitmap buffer that I don’t think will be HW acc. (correct me if I’m wrong) won’t help my cause.

The Idea of having an “AA shader” is really seducing and I see here several advantage. It’s well supported by the hardware acceleration, will of course be a marvel for the memory (no more savage bitmap allocation) and could be totally independent from the bitmap scaling. I made a quick test and this is by far the best AA (and here I mean in quality) I ever manage to get and … it’s fast… This quick wrap-up do an upper edge AA using shaders.

        BitmapShader bitmapShader = new BitmapShader(mBitmap,
                TileMode.CLAMP, TileMode.CLAMP);
        Matrix m = new Matrix(); 
        m.postTranslate(0, 1); 
        bitmapShader.setLocalMatrix(m); 

        final LinearGradient AAshader = new LinearGradient(0, 0, 0, 1, 0x00000000, 0xffffffff, TileMode.CLAMP);

        ComposeShader compositor = new ComposeShader(bitmapShader,
                AAshader, PorterDuff.Mode.DST_IN);

        mPaint.setShader(compositor);
        canvas.drawRect(this.getBounds(), mPaint);

Now the big drawback ! How to get a 4 edge AA :) ??? We can probably manage an upper/lower edge AA using a multi steps Gradient but this is still dirty. A nice way will be to combine the source bitmap with some kind of 9-patch to get the transparent border, but we cannot compose 2 bitmapShader… So, here I am once again. How to get this mask Shader! Damn wall ;)



回答3:

Fix that worked in my case:

private void fixAntiAlias(View viewFromThe80s) {
    if (Build.VERSION.SDK_INT > 10) {
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
        viewFromThe80s.setLayerType(View.LAYER_TYPE_SOFTWARE, p);
        ((View) viewFromThe80s.getParent()).setLayerType(View.LAYER_TYPE_SOFTWARE, p);
    }
}

Details:

My textview used a 9-patch as a background so I couldn't add a transparent 1px border without impacting the 9-patch pixels. So, instead, I was able to get this working by disabling hardware acceleration for the given view and its parent:

I'm calling this right after view inflation. Specifically in onCreateViewHolder in my case (in the viewholder constructor to be exact). This fixes my rotated TextView which uses a NinePatchDrawable background, similar to the following:

<TextView
    android:id="@+id/text_new_feature"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:rotation="-30"
    android:background="@drawable/background_new_feature"/>