Android Canvas DrawBitmap poor quality - android

I'm extending SurfaceView to create a fullscreen horizontally scrollable background.
My issue is with the quality of the bitmap. When used in the draw-call as
canvas.drawBitmap(bitmap, 0, 0, paint);
It's quality is much poorer than its original. I'm not doing any scaling.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setFilterBitmap(true);
Not sure if the images show it clearly, they're screenshots from the tablet. The good one is an ImageView with its src as the drawable.
The canvas one has this horizontally stripy effect, its very significant in reality. This effect is vertical as well. It's whenever the gradient is slightly changing. As if the canvas' bitmap has much less colors..
I'm sure there's a solution to this, but I could not find any.
Poor quality example
Real quality example

Looks like color quantization. The default color format for SurfaceView is 16-bit RGB_565. Change it to 32-bit with something like this on onCreate():
mSurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888);
(Some examples use PixelFormat.TRANSLUCENT.)

Related

Using NinePatch (.9.png) for making scaled Bitmaps

I need to draw a Bitmap for my new Android Application. Since my content view is set to a Game-Panel class I've created. I want to display images through Bitmap. Just because its more convenient, and easier to do then anything else.
From what I've gathered from researching, Nine Patch images (.9.png) are used in android to scale more properly then regular Drawable images. It also says "This can be used for scaling properly in backgrounds. For example a regular button background...". If Nine Patch images are made to scale better. Can You use a Nine Patch for making a Bitmap in a specific width and height because it would scale more properly? For example
Bitmap b = createBitmap(BitmapFactory.decodeResource(NinePatchImage), x, y, width, height);
Is this possible? Should I do it a different way? Should I create a bitmap first and then just scale the bitmap? Is this even necessary? Please help.
Bitmap bitmap = Bitmap.createBitmap(canvasWidth, canvasHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
NinePatchDrawable drawable = (NinePatchDrawable) getResources().getDrawable(R.drawable.myDrawable);
drawable.setBounds(new Rect(x, y, width, height));
drawable.draw(canvas);

Set Picture into RemoteViews

I have some SVGs in my assets folder and I need to dynamically set them in my widget (on an ImageView).
I am using this library: http://code.google.com/p/svg-android/
This library returns a Picture or a PictureDrawable.
The only methods I can see to use on RemoteViews are setImageViewBitmap which obviously takes a bitmap.
I tried looking for code to convert a Drawable to a Bitmap like this:
PictureDrawable pictureDrawable = svg.createPictureDrawable();
Bitmap bitmap = Bitmap.createBitmap(pictureDrawable.getIntrinsicWidth(), pictureDrawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawPicture(pictureDrawable.getPicture());
currentBitmap = bitmap;
But the bitmap is too small. When I create the bitmap in Illustrator I set the artboard size to 65 which is what comes through on the intrinsic width/height.
My widgets can be resized so the ImageView sizes are variable. Even if I set the width and height statically to some large number like this...
Bitmap bitmap = Bitmap.createBitmap(300, 300, Config.ARGB_8888);
then the resulting bitmap just has a bunch of whitespace below and to the right of a tiny image.
I guess I need to somehow draw the picture at a scaled up value as well as creating the Bitmap at size 300. Ideally I could figure out the size of the ImageView at runtime and set the proper sized Bitmap if I knew that. Is this the best approach and how would I do this? Perhaps there is a better approach I don't even know about?
I've not used android-svg but if it's using vanilla PictureDrawables, then it should be just a matter of not using the intrinsic bounds.
Try the following:
PictureDrawable pictureDrawable = svg.createPictureDrawable();
Bitmap bitmap = Bitmap.createBitmap(300, 300, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
pictureDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
pictureDrawable.draw(canvas); // do not access the Picture directly, that defeats the purpose
currentBitmap = bitmap;
In short, use the Drawable, not its Picture and set the Drawable's bounds to be the full canvas.
Have you tried createScaledBitmap?
createScaledBitmap()
I have tried svg-android and it has not worked for me for this very reason. Not to mention its severely limited feature set.
The point of using vector graphics is that I can generate images of any appropriate size to fit the UI View size. Which means the generation method must accept the size requirements at run-time, and not always use width,height declared in <svg> tag.
Hence I used the native implementation: libsvg-android, which exactly does that.
It directly renders to a canvas with given size:
long objId = SvgRaster.svgAndroidCreate();
SvgRaster.svgAndroidParseBuffer(objId, readString(mInputStream, "UTF-8"));
SvgRaster.svgAndroidSetAntialiasing(objId, true);
SvgRaster.svgAndroidRenderToArea(objId, mCanvas, 0, 0, mWidth, mHeight);
I ended up modifying the underlying Artboard size to be 300. None of the scaling up methods worked. So the final code I used was that which I originally posted:
PictureDrawable pictureDrawable = svg.createPictureDrawable();
Bitmap bitmap = Bitmap.createBitmap(pictureDrawable.getIntrinsicWidth(), pictureDrawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawPicture(pictureDrawable.getPicture());
currentBitmap = bitmap;
Once I had a 300x300 Bitmap I was able to set it into the RemoteViews using setImageViewBitmap .
This kind of defeated the purpose of using SVGs in the first place (at least as far as using them in widgets was concerned).
My problem was not the same as User117. Perhaps he did not name the outer layer 'bounds' as was required in the library. Either way, I managed to get the library working, albeit by having to modify the SVG Artboard size.
Hopefully with the increase in screen resolution Android will introduce SVGs as part of the platform soon.

Android gradient graphics quality (scaled bitmap)

I want to display a splash screen with an gradient in my app. But the quality of the gradient in the background is pretty bad. So I create a simple radiant gradient to have a closer look.
Edit: Maybe I should have mentioned that I am using BitmapFactory.createScaledBitmap(); and BitmapFactory.decodeStream() (The graphics are in the assets folder.)
Thats the result:
Original *.jpg (Quality 100%)
Screenshot Xperia X10 (I took the screenshot with Eclipse)
You can do it without any jpg file, but using a shape drawable. Create rectangle and provide necessary parmas for gradient tag.
In order to scale a bitmap and keep ARGB_8888 I created a new bitmap in the desired size. Then I use canvas to draw the intrinsic bitmap on the new bitmap with drawBitmap. This method allows me to scale the bitmap before I draw it on the new bitmap.
inputStream = assetManager.open(path);
originalBitmap = BitmapFactory.decodeStream(inputStream, null, opts);
Bitmap resizedBitmap = Bitmap.createBitmap(desiredX, desiredY, Config.ARGB_8888);
Canvas canvas = new Canvas(resizedBitmap);
canvas.drawBitmap(originalBitmap, null, new Rect(0, 0, desiredX, desiredY), null);
inputStream.close();
return resizedBitmap;

Android, rotate 18x18 pixel circle icon, result: icons of different sizes?

I'm drawing a bunch of icons on a map. Actually the icons come from the same image rotated. But on the map the images take on two different sizes, I don't know why. This is what the result looks like: http://orangesoftware.net/iconmap.png
The image file looks like this: http://orangesoftware.net/arrow18.png
The code to rotate the icon:
Matrix mtx = new Matrix();
mtx.postRotate(unit.heading);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.arrow18);
Bitmap rotatedBMP = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(),
bmp.getHeight(), mtx, true);
BitmapDrawable bmd = new BitmapDrawable(rotatedBMP);
Any magically insights appreciated, thanks
The cause of the variation in sizes is when a rotation is not a multiple of 90 degrees. The bmp becomes a diamond who's corners stick out beyond the ImageView holding it, thus it gets resized to fit the ImageView.
The easiest way to take care of this discrepancy is to set the ImageView's scaleType to CENTER. This will simply center the image inside without scaling it to fit.

Drawing scaled bitmaps on a SurfaceView -- no antialiasing

I'm sorry if this topic has been brought before, but all my searches on the web and google groups did not help me.
I'm currently developing a little game with the Android SDK, and use hi-res bitmaps that I resize accordingly to match the device's resolution (letting the system do it for me is
not "crisp" enough).
I use a SurfaceView, on which I paint in one pass a canvas filling the whole surface. The paint uses setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) to allow masking.
Beforehand, I retrieve various bitmaps -- which are resized at initialization with createScaledBitmap() and put in a cache -- and I apply the bitmaps with a paint on this canvas, before drawing this canvas on the SurfaceView.
My problem is, whatever I try, whatever paint settings I use (dithering, antialias, etc..), the resized bitmaps are not antialiased and the drawing present jagged edges. I tried everything.
The only little success I had was using inSampleSize to approach the desired scaled size and force a first pass of antialiasing, before invoking createScaledBitmap on the retrieved
hi-res bitmap, but it is not beautiful enough. I just can't allow to create multitudes of pre-sized bitmaps for every combination of resolution. What did I miss ?
Thanks a lot in advance
First when you load your bitmap you make sure that you don't lose any image quality by settings options to argb_8888:
Options options = new Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.id.pic, options);
When you scale the bitmap turn on the filter:
pic = Bitmap.createScaledBitmap(pic, screenW, screenH, true);
However if one streaches the image too much inevitably it degrades in quality.
When you use paint you can improve quality but lose on speed with turning on ditherig and filtering:
Paint paint = new Paint();
paint.setFlags(Paint.DITHER_FLAG);
paint.setFilterBitmap(true);
Finally the entire activity window could be set on argb_4444 instead on argb_8888 (OS < 2.3). You can chage this if you instert this line before setContentView:
getWindow().setFormat(PixelFormat.RGBA_8888);
If it comes down to it, you can manually antialias without too much trouble. Just apply a simple lowpass filter (something like an NxN average) to the pixel data before asking the bitmap object to rescale itself.
you may clear canvas buffer by youself! such as follows:
canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);

Categories

Resources