Search in sources :

Example 21 with BitmapRegionDecoder

use of android.graphics.BitmapRegionDecoder in project android_frameworks_base by crdroidandroid.

the class WallpaperManagerService method generateCrop.

/**
     * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
     * for display.
     */
private void generateCrop(WallpaperData wallpaper) {
    boolean success = 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() + " 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;
        // 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));
            // If the crop hint was larger than the image we just overshot. Patch things up.
            if (cropHint.left < 0) {
                cropHint.left = 0;
            }
            if (cropHint.top < 0) {
                cropHint.top = 0;
            }
            // Don't bother cropping if what we're left with is identity
            needCrop = (options.outHeight > cropHint.height() || options.outWidth > 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) {
            // the whole thing and just copy the image file directly.
            if (DEBUG) {
                Slog.v(TAG, "Null crop of new wallpaper; copying");
            }
            success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
            if (!success) {
                wallpaper.cropFile.delete();
            // TODO: fall back to default wallpaper in this case
            }
        } else {
            // 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);
                // 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);
                    // adjust by downsampling factor
                    cropHint.right /= scale;
                    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);
                    finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                    // don't rely on the implicit flush-at-close when noting success
                    bos.flush();
                    success = true;
                }
            } catch (Exception e) {
                if (DEBUG) {
                    Slog.e(TAG, "Error decoding crop", e);
                }
            } finally {
                IoUtils.closeQuietly(bos);
                IoUtils.closeQuietly(f);
            }
        }
    }
    if (!success) {
        Slog.e(TAG, "Unable to apply new wallpaper");
        wallpaper.cropFile.delete();
    }
    if (wallpaper.cropFile.exists()) {
        boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
        if (DEBUG) {
            Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
        }
    }
}
Also used : Rect(android.graphics.Rect) Bitmap(android.graphics.Bitmap) FileOutputStream(java.io.FileOutputStream) BitmapRegionDecoder(android.graphics.BitmapRegionDecoder) BitmapFactory(android.graphics.BitmapFactory) BufferedOutputStream(java.io.BufferedOutputStream) Point(android.graphics.Point) ErrnoException(android.system.ErrnoException) FileNotFoundException(java.io.FileNotFoundException) XmlPullParserException(org.xmlpull.v1.XmlPullParserException) NameNotFoundException(android.content.pm.PackageManager.NameNotFoundException) RemoteException(android.os.RemoteException) IOException(java.io.IOException)

Example 22 with BitmapRegionDecoder

use of android.graphics.BitmapRegionDecoder in project android_frameworks_base by crdroidandroid.

the class WallpaperManager method getBuiltInDrawable.

/**
     * Returns a drawable for the built-in static wallpaper of the specified type.  Based on the
     * parameters, the drawable can be cropped and scaled.
     *
     * @param outWidth The width of the returned drawable
     * @param outWidth The height of the returned drawable
     * @param scaleToFit If true, scale the wallpaper down rather than just cropping it
     * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image;
     *        0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned
     * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image;
     *        0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned
     * @param which The {@code FLAG_*} identifier of a valid wallpaper type.  Throws
     *     IllegalArgumentException if an invalid wallpaper is requested.
     * @return A Drawable presenting the built-in default wallpaper image of the given type,
     *        or {@code null} if no default image of that type is defined on this device.
     */
public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which) {
    if (sGlobals.mService == null) {
        Log.w(TAG, "WallpaperService not running");
        throw new RuntimeException(new DeadSystemException());
    }
    if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
        throw new IllegalArgumentException("Must request exactly one kind of wallpaper");
    }
    Resources resources = mContext.getResources();
    horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment));
    verticalAlignment = Math.max(0, Math.min(1, verticalAlignment));
    InputStream wpStream = openDefaultWallpaper(mContext, which);
    if (wpStream == null) {
        if (DEBUG) {
            Log.w(TAG, "default wallpaper stream " + which + " is null");
        }
        return null;
    } else {
        InputStream is = new BufferedInputStream(wpStream);
        if (outWidth <= 0 || outHeight <= 0) {
            Bitmap fullSize = BitmapFactory.decodeStream(is, null, null);
            return new BitmapDrawable(resources, fullSize);
        } else {
            int inWidth;
            int inHeight;
            // Just measure this time through...
            {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(is, null, options);
                if (options.outWidth != 0 && options.outHeight != 0) {
                    inWidth = options.outWidth;
                    inHeight = options.outHeight;
                } else {
                    Log.e(TAG, "default wallpaper dimensions are 0");
                    return null;
                }
            }
            // Reopen the stream to do the full decode.  We know at this point
            // that openDefaultWallpaper() will return non-null.
            is = new BufferedInputStream(openDefaultWallpaper(mContext, which));
            RectF cropRectF;
            outWidth = Math.min(inWidth, outWidth);
            outHeight = Math.min(inHeight, outHeight);
            if (scaleToFit) {
                cropRectF = getMaxCropRect(inWidth, inHeight, outWidth, outHeight, horizontalAlignment, verticalAlignment);
            } else {
                float left = (inWidth - outWidth) * horizontalAlignment;
                float right = left + outWidth;
                float top = (inHeight - outHeight) * verticalAlignment;
                float bottom = top + outHeight;
                cropRectF = new RectF(left, top, right, bottom);
            }
            Rect roundedTrueCrop = new Rect();
            cropRectF.roundOut(roundedTrueCrop);
            if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
                Log.w(TAG, "crop has bad values for full size image");
                return null;
            }
            // See how much we're reducing the size of the image
            int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / outWidth, roundedTrueCrop.height() / outHeight);
            // Attempt to open a region decoder
            BitmapRegionDecoder decoder = null;
            try {
                decoder = BitmapRegionDecoder.newInstance(is, true);
            } catch (IOException e) {
                Log.w(TAG, "cannot open region decoder for default wallpaper");
            }
            Bitmap crop = null;
            if (decoder != null) {
                // Do region decoding to get crop bitmap
                BitmapFactory.Options options = new BitmapFactory.Options();
                if (scaleDownSampleSize > 1) {
                    options.inSampleSize = scaleDownSampleSize;
                }
                crop = decoder.decodeRegion(roundedTrueCrop, options);
                decoder.recycle();
            }
            if (crop == null) {
                // BitmapRegionDecoder has failed, try to crop in-memory. We know at
                // this point that openDefaultWallpaper() will return non-null.
                is = new BufferedInputStream(openDefaultWallpaper(mContext, which));
                Bitmap fullSize = null;
                BitmapFactory.Options options = new BitmapFactory.Options();
                if (scaleDownSampleSize > 1) {
                    options.inSampleSize = scaleDownSampleSize;
                }
                fullSize = BitmapFactory.decodeStream(is, null, options);
                if (fullSize != null) {
                    crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, roundedTrueCrop.top, roundedTrueCrop.width(), roundedTrueCrop.height());
                }
            }
            if (crop == null) {
                Log.w(TAG, "cannot decode default wallpaper");
                return null;
            }
            // Scale down if necessary
            if (outWidth > 0 && outHeight > 0 && (crop.getWidth() != outWidth || crop.getHeight() != outHeight)) {
                Matrix m = new Matrix();
                RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight());
                RectF returnRect = new RectF(0, 0, outWidth, outHeight);
                m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
                Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), (int) returnRect.height(), Bitmap.Config.ARGB_8888);
                if (tmp != null) {
                    Canvas c = new Canvas(tmp);
                    Paint p = new Paint();
                    p.setFilterBitmap(true);
                    c.drawBitmap(crop, m, p);
                    crop = tmp;
                }
            }
            return new BitmapDrawable(resources, crop);
        }
    }
}
Also used : DeadSystemException(android.os.DeadSystemException) Rect(android.graphics.Rect) BufferedInputStream(java.io.BufferedInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) Canvas(android.graphics.Canvas) BitmapRegionDecoder(android.graphics.BitmapRegionDecoder) BitmapDrawable(android.graphics.drawable.BitmapDrawable) IOException(java.io.IOException) Paint(android.graphics.Paint) Paint(android.graphics.Paint) RectF(android.graphics.RectF) Bitmap(android.graphics.Bitmap) Matrix(android.graphics.Matrix) BufferedInputStream(java.io.BufferedInputStream) Resources(android.content.res.Resources) BitmapFactory(android.graphics.BitmapFactory)

Aggregations

BitmapRegionDecoder (android.graphics.BitmapRegionDecoder)22 Rect (android.graphics.Rect)19 Bitmap (android.graphics.Bitmap)18 IOException (java.io.IOException)15 BitmapFactory (android.graphics.BitmapFactory)13 InputStream (java.io.InputStream)12 Matrix (android.graphics.Matrix)10 RectF (android.graphics.RectF)10 Paint (android.graphics.Paint)7 FileInputStream (java.io.FileInputStream)6 Resources (android.content.res.Resources)5 Canvas (android.graphics.Canvas)5 Point (android.graphics.Point)5 BitmapDrawable (android.graphics.drawable.BitmapDrawable)5 DeadSystemException (android.os.DeadSystemException)5 BufferedInputStream (java.io.BufferedInputStream)5 FileNotFoundException (java.io.FileNotFoundException)5 NameNotFoundException (android.content.pm.PackageManager.NameNotFoundException)4 RemoteException (android.os.RemoteException)4 ErrnoException (android.system.ErrnoException)4