【问题标题】:Trouble understanding Android's doc about downscaling large bitmaps无法理解 Android 关于缩小大位图的文档
【发布时间】:2016-02-11 13:14:58
【问题描述】:

我的问题是:为什么这个文档会存在并且经常被发布和重新发布,而没有人遇到过它的问题。我的结论是我误解了代码或整个主题的逻辑,但是有人可以解释一下吗?故事是这样的:

我正在尝试缩小大型位图,遵循 android 的文档: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap

更具体地说,使用此代码 sn-p:

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

我正在尝试使 100x600 图像适合 400x400 ImageView。所以我的应用进入了第一个 if() 语句,因为高度太大了,但是没有做任何事情,因为它没有进入 while() 语句——一半的高度不算太大!

我真的不明白检查 halfWidth 和 halfHeight 的意义,因为它必须适合整个图像......

我会这样写:

public static int calculateInSampleSize(
                BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((height / inSampleSize) > reqHeight
                    || (width / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }


        return inSampleSize;
    }

我错过了什么吗?谢谢!

【问题讨论】:

    标签: android bitmap scale


    【解决方案1】:
    import android.content.Context;
    import android.database.Cursor;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.media.ExifInterface;
    import android.net.Uri;
    import android.provider.MediaStore;
    
    import com.serviceledger.corporatefloor.modals.ImageData;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * Created by rohit.h on 1/29/2016.
     */
    public class ImageCompresser {
    
        private static ImageCompresser instance;
    
        private ImageCompresser() {
        }
    
        public static ImageCompresser getInstance() {
            if (instance == null) {
                instance = new ImageCompresser();
            }
    
            return instance;
        }
    
    
    
        public ImageData compressImage(Context mContext,String imageUri) {
    
            String filePath = imageUri;
            // String filePath = getRealPathFromURI(imageUri);
            Bitmap scaledBitmap = null;
    
            BitmapFactory.Options options = new BitmapFactory.Options();
    
            // by setting this field as true, the actual bitmap pixels are not
            // loaded in the memory. Just the bounds are loaded. If
            // you try the use the bitmap here, you will get null.
            options.inJustDecodeBounds = true;
            Bitmap bmp = BitmapFactory.decodeFile(filePath, options);
    
            int actualHeight = options.outHeight;
            int actualWidth = options.outWidth;
    
            // max Height and width values of the compressed image is taken as
            // 816x612
    
            /*
             * float maxHeight = 816.0f; float maxWidth = 612.0f;
             */
            float maxHeight = 1080.0f;
            float maxWidth = 800.0f;
    
            float imgRatio = actualWidth / (float) actualHeight;
            float maxRatio = maxWidth / (float) maxHeight;
    
            // width and height values are set maintaining the aspect ratio of the
            // image
    
            if (actualHeight > maxHeight || actualWidth > maxWidth) {
                if (imgRatio < maxRatio) {
                    imgRatio = maxHeight / actualHeight;
                    actualWidth = (int) (imgRatio * actualWidth);
                    actualHeight = (int) maxHeight;
                } else if (imgRatio > maxRatio) {
                    imgRatio = maxWidth / actualWidth;
                    actualHeight = (int) (imgRatio * actualHeight);
                    actualWidth = (int) maxWidth;
                } else {
                    actualHeight = (int) maxHeight;
                    actualWidth = (int) maxWidth;
    
                }
            }
    
            // setting inSampleSize value allows to load a scaled down version of
            // the original image
    
            options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);
    
            // inJustDecodeBounds set to false to load the actual bitmap
            options.inJustDecodeBounds = false;
    
            // this options allow android to claim the bitmap memory if it runs low
            // on memory
            options.inPurgeable = true;
            options.inInputShareable = true;
            options.inTempStorage = new byte[16 * 1024];
    
            try {
                // load the bitmap from its path
                bmp = BitmapFactory.decodeFile(filePath, options);
            } catch (OutOfMemoryError exception) {
                exception.printStackTrace();
    
            }
            try {
                scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
            } catch (OutOfMemoryError exception) {
                exception.printStackTrace();
            }
    
            float ratioX = actualWidth / (float) options.outWidth;
            float ratioY = actualHeight / (float) options.outHeight;
            float middleX = actualWidth / 2.0f;
            float middleY = actualHeight / 2.0f;
    
            Matrix scaleMatrix = new Matrix();
            scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
    
            Canvas canvas = new Canvas(scaledBitmap);
            canvas.setMatrix(scaleMatrix);
            canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
    
            // check the rotation of the image and display it properly
            ExifInterface exif;
            try {
                exif = new ExifInterface(filePath);
    
                int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
            //    displayLogs("EXIF", "Exif: " + orientation);
                Matrix matrix = new Matrix();
                if (orientation == 6) {
                    matrix.postRotate(90);
            //        displayLogs("EXIF", "Exif: " + orientation);
                } else if (orientation == 3) {
                    matrix.postRotate(180);
             //       displayLogs("EXIF", "Exif: " + orientation);
                } else if (orientation == 8) {
                    matrix.postRotate(270);
              //      displayLogs("EXIF", "Exif: " + orientation);
                }
    
                scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            FileOutputStream out = null;
            String filename = getFilename(mContext,filePath);
            // File image = new File(getFilename(), System.currentTimeMillis() +
            // ".jpg");
            try {
                out = new FileOutputStream(filename);
    
                // write the compressed bitmap at the destination specified by
                // filename.
                boolean didWrite = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
    
                out.close();
    
                System.out.println(didWrite);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
    
                e.printStackTrace();
            }
            ImageData data = new ImageData();
            data.setBitmap(scaledBitmap);
            data.setUrl(filename);
            return data;
    
        }
    
        private String getRealPathFromURI(Context mContext,String contentURI) {
            Uri contentUri = Uri.parse(contentURI);
            Cursor cursor = mContext.getContentResolver().query(contentUri, null, null, null, null);
            if (cursor == null) {
                return contentUri.getPath();
            } else {
                cursor.moveToFirst();
                int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                return cursor.getString(index);
            }
        }
    
        public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
    
            if (height > reqHeight || width > reqWidth) {
                final int heightRatio = Math.round((float) height / (float) reqHeight);
                final int widthRatio = Math.round((float) width / (float) reqWidth);
                inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            }
            final float totalPixels = width * height;
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;
            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
    
            return inSampleSize;
        }
    
        private String getFilename(Context mContext,String originalname) {
    
            ImageCache imageCache = new ImageCache(mContext);
            File file = imageCache.getCacheDirectory();
    
            // return file;
            String uriSting = (file.getAbsolutePath() + "/" + "F_" + System.currentTimeMillis() + ".png");
            return uriSting;
    
        }
    
    }
    
    Second class:
    
    public class ImageCache {
    
        private File cacheDir;
    
        public ImageCache(Context context){
            //Find the dir to save cached images
            if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
                cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"yourpackagename");
            }
            else{
                cacheDir=context.getCacheDir();     
            }
    
            if(!cacheDir.exists()){
                cacheDir.mkdirs();
            }
    
        }
    
        public File getCacheDirectory(){
            return cacheDir;
        }
    
    
        public File getFile(String url){
            //I identify images by hashcode. Not a perfect solution, good for the demo.
            String filename=String.valueOf(url.hashCode());
            //Another possible solution (thanks to grantland)
            //String filename = URLEncoder.encode(url);
            File f = new File(cacheDir, filename);
            return f;
    
        }
    
        public void clear(){
            File[] files=cacheDir.listFiles();
            if(files==null)
                return;
            for(File f:files)
                f.delete();
        }
    ModalClass:-
    
    public class ImageData {
        private String Url;
        private Bitmap bitmap;
    
        public boolean isImageAvailable() {
            return isImageAvailable;
        }
    
        public void setIsImageAvailable(boolean isImageAvailable) {
            this.isImageAvailable = isImageAvailable;
        }
    
        private boolean isImageAvailable;
    
        public boolean isImageDeleteable() {
            return isImageDeleteable;
        }
    
        public void setIsImageDeleteable(boolean isImageDeleteable) {
            this.isImageDeleteable = isImageDeleteable;
        }
    
        private boolean isImageDeleteable;
        public Bitmap getBitmap() {
            return bitmap;
        }
    
        public void setBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
        }
    
        public String getUrl() {
            return Url;
        }
    
        public void setUrl(String url) {
            Url = url;
        }
    
    }
    

    【讨论】:

    • 感谢您的解决方案,但我实际上并不是在寻找缩小图像的解决方案(我猜这就是您的建议?)。只是想知道我是否在滥用官方文档(也许它只是为了缩放比目标大两倍以上的图像?)
    【解决方案2】:

    我认为由于质量损失,您的代码不是那么完美。

    在你的情况下,图片的宽度是100,但你需要将它缩放到400。为了图形质量,最好不压缩加载原始图像。文档中的代码将返回 1,因此不会压缩图片。这可能是最好的解决方案,并且与您的结果相同。

    假设原始图像为 402x402,比视图大一点。您的方法将返回 2,导致您的应用加载尺寸为 201x201 的子采样版本,然后将其缩放到 400x400。显然,让较大的图像适合较小的视图比放大它更好。总而言之,文档认为如果图像没有比需要的大两倍,则应直接加载并缩小到视图大小。否则,加载下采样版本以节省内存空间并使下采样版本缩小到所需的大小。

    由于方法由您定义,您可以根据自己的情况细化代码。即如果你需要控制内存空间并且不需要太关注图像质量,请忽略文档的想法。

    就是这样:)

    【讨论】:

      猜你喜欢
      • 2015-09-12
      • 2023-01-24
      • 1970-01-01
      • 1970-01-01
      • 2017-08-27
      • 1970-01-01
      • 2015-10-01
      • 2020-04-04
      • 1970-01-01
      相关资源
      最近更新 更多