Search code examples
androidbitmapwallpaper

How do I get a smooth wallpaper?


I'm trying to set a wallpaper but I get banding. I have a large gradient JPG which is saved on my device. I read it from file, scale it so that its height matches the height of the device then I set the wallpaper and the wallpaper hints. The scaling step seems to be converting it to a RGB565 format rather than the original ARGB888 format. Also, I dont seem to have any dither which might help aleviate the banding.

Here is my code:

public class WallpaperSetter {

public static void setWallpaper(String url, Context context) throws IOException {
    FileCache cache = new FileCache(context);
    File f = cache.getFile(url);
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    options.inDither = true;
    Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, options);

    Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

    int targetHeight = display.getHeight() > display.getWidth() ? display.getHeight() : display.getWidth() - 10;
    int targetWidth = (int) ((float) targetHeight / (float) bmp.getHeight() * (float) bmp.getWidth());
    Bitmap resizedBitmap = resize(bmp, targetHeight, targetWidth);

    WallpaperManager manager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
    manager.setBitmap(resizedBitmap);

    int displayHeight = display.getHeight() > display.getWidth() ? display.getHeight() : display.getWidth();
    int displayWidth = display.getHeight() > display.getWidth() ? display.getWidth() : display.getHeight();
    int height = resizedBitmap.getHeight() > displayHeight ? resizedBitmap.getHeight() : displayHeight;
    int width = resizedBitmap.getWidth() < displayWidth ? displayWidth : resizedBitmap.getWidth();
    manager.suggestDesiredDimensions(width, height);
}

private static Bitmap resize(Bitmap bitmap, int targetHeight, int targetWidth) {
    System.out.println("config start: " + bitmap.getConfig().name().toString());
    Bitmap b = Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, false);
    System.out.println("config: " + b.getConfig().name().toString());
    return b;
}

}

I'm developing on a SGS2 with CyanogenMod if that makes a difference.


Solution

  • I found that adding some noise or subtle randomness to a gradient image helps reduce banding, but I suspect using a .png containing at least a pixel of alpha (weird but works) and RGB565 format is the way to go. Also I found that using the 'raw' resources folder instead of 'drawable' folder prevented Android assuming it could compress the image internally. Here's the code I used anyway:

        private void generateBackgroundGraphic() {
            background = BitmapFactory.decodeResource(res, R.raw.gradient);
    
            //create local copy of 'background' bitmap size
            Bitmap tempB = Bitmap.createBitmap(background.getWidth(), background.getHeight(), Bitmap.Config.RGB_565);
            backgroundPaint.setDither(true);
    
            //wrap a canvas around 'background' bitmap
            Canvas tempC = new Canvas (tempB);
            tempC.drawBitmap(background, 0, 0, null);
            background = Bitmap.createScaledBitmap(tempB, globalCanvasSize.x, globalCanvasSize.y, false);
            tempB.recycle();
            tempB = null;
            tempC = null;
        }
    

    I hope this is of some use to you... Android deals with images strangely sometimes :-/

    Chris