In my application I have rolled my own SVG converter that translates a raw SVG XML file into a sequence of graphical vector objects (Paint, Path, etc.) which are then applied to Canvas. The SVG data is first converted into a kind of bytecode format together with Lists of Paths, etc. which means that initial XML parsing is only done once, and then thereafter each redraw of the SVG graphics can happen reasonably fast. The fact that SVG conversion is done at run-time enables me to perform some program manipulations on various graphical elements. For example, I can draw a graphical temperature gauge in Inkscape and at runtime the code manipulates the scale of the gauge and moves the pointer around. This is why I 'need' to draw both static background graphics as well as moving graphics using vector operations, as opposed to compiling static bitmaps. Anyway, this is a little bit of background information for what I'm doing; I'll move onto the actual question.
Let's say a typical gauge widget is composed of two overlayed Views. The background View contains static graphics (the circular face of the gauge, numeric legends, tick marks) that were drawn using Paths. The foreground View contains the graphics for the pointer that needs to be redrawn frequently. Each widget shall probably use a RelativeLayout to contain these children views.
I realise that whenever the onDraw() is called for the foreground pointer View, then the onDraw is to be called for the backgound View that sits behind it. Therefore, to optimise the widget redraw time, I've attempted to make use of the Canvas' drawing cache for the background View. What I have done does work and results in a great redraw speed hike, but the problem is that the cache bitmap that I save and then redraw to the Canvas later is never quite as razor-sharp as what was originally drawn using the Paths.
The basic code I've used in my onDraw() method for the background View is:
Bitmap bm = null;
onDraw(Canvas canvas){
if(bm!=null){
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(false);
bitmapPaint.setFilterBitmap(false);
bitmapPaint.setDither(false);
canvas.drawBitmap(bm, 0, 0, bitmapPaint);
return;
}
... Vector drawing to the Canvas using Path objects happens here,
performed by the SVG converter object ...
setDrawingCacheEnabled(true);
setDrawingCacheQuality(DRAWING_CACHE_QUALITY_HIGH);
bm = Bitmap.createBitmap(getDrawingCache());
}
To try to get the quality of the bitmap has high as possible, I have done the following things:
Ensured that the cache drawing quality is high.
Used a Paint object (bitmapPaint) with anti-alias, dither, etc. disabled when drawing the bitmap.
Knowing that any scaling of the bitmap would immediately affect its quality, I've (hopefully) ensured that no scaling of the bitmap should need to be performed. I've set the size of the View to be exactly that of the original SVG image, which means that my SVG converter code applies no scaling to the Canvas. A scale() call is always performed by the SVG converter, but in the present situation it's made with the arguments canvas.scale( 1, 1). If I were to intentionally change the width / height of the View to mismatch that of the original SVG image, Canvas scaling is no longer 1,1 and then further loss of quality to the bitmap is very much apparent.
So my main question is, is there anything wrong with the code I've used above to make use of the drawing cache bitmap? I've got a feeling that there's some kind of density / scale happening to the bitmap that I'm unaware of, even though it's appearing on the screen exactly the same size as the original graphics I had drawn.
Furthermore (possibly going out of the scope of the main question) is there any other technique I could employ to avoid redraw of background vector graphics when there are foreground moving graphics? As far as I can tell, using the bitmap cache is the only way to my knowledge so far.
Thanks in advance; any advice would be much appreciated.
Trev
I'm pleased to say that the problem is now resolved. I realised that the Canvas being passed to onDraw() in the first place already had some scaling applied. The fact that this scaling is there is what's degrading the quality of the bitmap. But I could not understand why the Canvas should already have some scaling.
I posted another question to ask about this here: In the onDraw() method, why does the supplied Canvas already have scale?. The accepted answer there suggests that I may not have specified android:minSdkVersion. Indeed, quite stupidly I hadn't. Once I specified the minSdkVersion, the cache bitmap now draws beautifully. Amazingly I had not realised until now that the images I was drawing were not to the phone's proper screen resolution; they were all being scaled due to lack of minSdkVersion.
Credit due to adamp!