Loading services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +111 −46 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; Loading Loading @@ -265,36 +266,57 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { */ private void generateCrop(WallpaperData wallpaper) { boolean success = false; boolean needCrop = false; boolean needScale = false; Rect cropHint = new Rect(wallpaper.cropHint); if (DEBUG) { Slog.v(TAG, "Generating crop for new wallpaper(s): 0x" + Integer.toHexString(wallpaper.whichPending) + " to " + wallpaper.cropFile.getName()); + " to " + wallpaper.cropFile.getName() + " crop=(" + cropHint.width() + 'x' + cropHint.height() + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')'); } // Analyse the source; needed in multiple cases BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options); if (options.outWidth <= 0 || options.outHeight <= 0) { Slog.e(TAG, "Invalid wallpaper data"); success = false; } else { boolean needCrop = false; boolean needScale = false; // We'll need to scale if the crop is sufficiently bigger than the display // Empty crop means use the full image if (cropHint.isEmpty()) { cropHint.left = cropHint.top = 0; cropHint.right = options.outWidth; cropHint.bottom = options.outHeight; } else { // force the crop rect to lie within the measured bounds cropHint.offset( (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0), (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0)); // Don't bother cropping if what we're left with is identity needCrop = (options.outHeight >= cropHint.height() && options.outWidth >= cropHint.width()); } // Legacy case uses an empty crop rect here, so we just preserve the // source image verbatim if (!wallpaper.cropHint.isEmpty()) { // ...clamp the crop rect to the measured bounds... wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth); wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight); // ...and don't bother cropping if what we're left with is identity needCrop = (options.outHeight >= wallpaper.cropHint.height() && options.outWidth >= wallpaper.cropHint.width()); // scale if the crop height winds up not matching the recommended metrics needScale = (wallpaper.height != cropHint.height()); if (DEBUG) { Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height()); Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height); Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight); Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale); } if (!needCrop && !needScale) { // Simple case: the nominal crop is at least as big as the source image, // so we take the whole thing and just copy the image file directly. // Simple case: the nominal crop fits what we want, so we take // the whole thing and just copy the image file directly. if (DEBUG) { Slog.v(TAG, "Null crop of new wallpaper; copying"); } Loading @@ -304,33 +326,76 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { // TODO: fall back to default wallpaper in this case } } else { // Fancy case: crop and/or scale // Fancy case: crop and scale. First, we decode and scale down if appropriate. FileOutputStream f = null; BufferedOutputStream bos = null; try { BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance( wallpaper.wallpaperFile.getAbsolutePath(), false); Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null); // This actually downsamples only by powers of two, but that's okay; we do // a proper scaling blit later. This is to minimize transient RAM use. // We calculate the largest power-of-two under the actual ratio rather than // just let the decode take care of it because we also want to remap where the // cropHint rectangle lies in the decoded [super]rect. final BitmapFactory.Options scaler; final int actualScale = cropHint.height() / wallpaper.height; int scale = 1; while (2*scale < actualScale) { scale *= 2; } if (scale > 1) { scaler = new BitmapFactory.Options(); scaler.inSampleSize = scale; if (DEBUG) { Slog.v(TAG, "Downsampling cropped rect with scale " + scale); } } else { scaler = null; } Bitmap cropped = decoder.decodeRegion(cropHint, scaler); decoder.recycle(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); } else { // We've got the extracted crop; now we want to scale it properly to // the desired rectangle. That's a height-biased operation: make it // fit the hinted height, and accept whatever width we end up with. cropHint.offsetTo(0, 0); cropHint.right /= scale; // adjust by downsampling factor cropHint.bottom /= scale; final float heightR = ((float)wallpaper.height) / ((float)cropHint.height()); if (DEBUG) { Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint); } final int destWidth = (int)(cropHint.width() * heightR); final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped, destWidth, wallpaper.height, true); if (DEBUG) { Slog.v(TAG, "Final extract:"); Slog.v(TAG, " dims: w=" + wallpaper.width + " h=" + wallpaper.height); Slog.v(TAG, " out: w=" + finalCrop.getWidth() + " h=" + finalCrop.getHeight()); } f = new FileOutputStream(wallpaper.cropFile); bos = new BufferedOutputStream(f, 32*1024); cropped.compress(Bitmap.CompressFormat.PNG, 90, bos); finalCrop.compress(Bitmap.CompressFormat.PNG, 90, bos); bos.flush(); // don't rely on the implicit flush-at-close when noting success success = true; } } catch (IOException e) { } catch (Exception e) { if (DEBUG) { Slog.e(TAG, "I/O error decoding crop: " + e.getMessage()); Slog.e(TAG, "Error decoding crop", e); } } finally { IoUtils.closeQuietly(bos); IoUtils.closeQuietly(f); } } } if (!success) { Slog.e(TAG, "Unable to apply new wallpaper"); Loading Loading
services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +111 −46 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; Loading Loading @@ -265,36 +266,57 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { */ private void generateCrop(WallpaperData wallpaper) { boolean success = false; boolean needCrop = false; boolean needScale = false; Rect cropHint = new Rect(wallpaper.cropHint); if (DEBUG) { Slog.v(TAG, "Generating crop for new wallpaper(s): 0x" + Integer.toHexString(wallpaper.whichPending) + " to " + wallpaper.cropFile.getName()); + " to " + wallpaper.cropFile.getName() + " crop=(" + cropHint.width() + 'x' + cropHint.height() + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')'); } // Analyse the source; needed in multiple cases BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options); if (options.outWidth <= 0 || options.outHeight <= 0) { Slog.e(TAG, "Invalid wallpaper data"); success = false; } else { boolean needCrop = false; boolean needScale = false; // We'll need to scale if the crop is sufficiently bigger than the display // Empty crop means use the full image if (cropHint.isEmpty()) { cropHint.left = cropHint.top = 0; cropHint.right = options.outWidth; cropHint.bottom = options.outHeight; } else { // force the crop rect to lie within the measured bounds cropHint.offset( (cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0), (cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0)); // Don't bother cropping if what we're left with is identity needCrop = (options.outHeight >= cropHint.height() && options.outWidth >= cropHint.width()); } // Legacy case uses an empty crop rect here, so we just preserve the // source image verbatim if (!wallpaper.cropHint.isEmpty()) { // ...clamp the crop rect to the measured bounds... wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth); wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight); // ...and don't bother cropping if what we're left with is identity needCrop = (options.outHeight >= wallpaper.cropHint.height() && options.outWidth >= wallpaper.cropHint.width()); // scale if the crop height winds up not matching the recommended metrics needScale = (wallpaper.height != cropHint.height()); if (DEBUG) { Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height()); Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height); Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight); Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale); } if (!needCrop && !needScale) { // Simple case: the nominal crop is at least as big as the source image, // so we take the whole thing and just copy the image file directly. // Simple case: the nominal crop fits what we want, so we take // the whole thing and just copy the image file directly. if (DEBUG) { Slog.v(TAG, "Null crop of new wallpaper; copying"); } Loading @@ -304,33 +326,76 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { // TODO: fall back to default wallpaper in this case } } else { // Fancy case: crop and/or scale // Fancy case: crop and scale. First, we decode and scale down if appropriate. FileOutputStream f = null; BufferedOutputStream bos = null; try { BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance( wallpaper.wallpaperFile.getAbsolutePath(), false); Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null); // This actually downsamples only by powers of two, but that's okay; we do // a proper scaling blit later. This is to minimize transient RAM use. // We calculate the largest power-of-two under the actual ratio rather than // just let the decode take care of it because we also want to remap where the // cropHint rectangle lies in the decoded [super]rect. final BitmapFactory.Options scaler; final int actualScale = cropHint.height() / wallpaper.height; int scale = 1; while (2*scale < actualScale) { scale *= 2; } if (scale > 1) { scaler = new BitmapFactory.Options(); scaler.inSampleSize = scale; if (DEBUG) { Slog.v(TAG, "Downsampling cropped rect with scale " + scale); } } else { scaler = null; } Bitmap cropped = decoder.decodeRegion(cropHint, scaler); decoder.recycle(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); } else { // We've got the extracted crop; now we want to scale it properly to // the desired rectangle. That's a height-biased operation: make it // fit the hinted height, and accept whatever width we end up with. cropHint.offsetTo(0, 0); cropHint.right /= scale; // adjust by downsampling factor cropHint.bottom /= scale; final float heightR = ((float)wallpaper.height) / ((float)cropHint.height()); if (DEBUG) { Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint); } final int destWidth = (int)(cropHint.width() * heightR); final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped, destWidth, wallpaper.height, true); if (DEBUG) { Slog.v(TAG, "Final extract:"); Slog.v(TAG, " dims: w=" + wallpaper.width + " h=" + wallpaper.height); Slog.v(TAG, " out: w=" + finalCrop.getWidth() + " h=" + finalCrop.getHeight()); } f = new FileOutputStream(wallpaper.cropFile); bos = new BufferedOutputStream(f, 32*1024); cropped.compress(Bitmap.CompressFormat.PNG, 90, bos); finalCrop.compress(Bitmap.CompressFormat.PNG, 90, bos); bos.flush(); // don't rely on the implicit flush-at-close when noting success success = true; } } catch (IOException e) { } catch (Exception e) { if (DEBUG) { Slog.e(TAG, "I/O error decoding crop: " + e.getMessage()); Slog.e(TAG, "Error decoding crop", e); } } finally { IoUtils.closeQuietly(bos); IoUtils.closeQuietly(f); } } } if (!success) { Slog.e(TAG, "Unable to apply new wallpaper"); Loading