在 Android 中解码大小合适的位图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2641726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:44:06  来源:igfitidea点击:

Decoding bitmaps in Android with the right size

androidmemorybitmapsd-card

提问by hpique

I decode bitmaps from the SD card using BitmapFactory.decodeFile. Sometimes the bitmaps are bigger than what the application needs or that the heap allows, so I use BitmapFactory.Options.inSampleSizeto request a subsampled (smaller) bitmap.

我使用BitmapFactory.decodeFile. 有时位图比应用程序需要的或堆允许的大,所以我BitmapFactory.Options.inSampleSize用来请求子采样(较小的)位图。

The problem is that the platform does not enforce the exact value of inSampleSize, and I sometimes end up with a bitmap either too small, or still too big for the available memory.

问题是平台没有强制执行 inSampleSize 的确切值,有时我最终得到的位图要么太小,要么对于可用内存来说仍然太大。

From http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize:

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize

Note: the decoder will try to fulfill this request, but the resulting bitmap may have different dimensions that precisely what has been requested. Also, powers of 2 are often faster/easier for the decoder to honor.

注意:解码器将尝试满足此请求,但生成的位图可能具有与所请求的完全不同的维度。此外,对于解码器而言,2 的幂通常更快/更容易实现。

How should I decode bitmaps from the SD card to get a bitmap of the exact size I need while consuming as little memory as possible to decode it?

我应该如何从 SD 卡解码位图以获得所需大小的位图,同时消耗尽可能少的内存来解码?

Edit:

编辑:

Current source code:

当前源代码:

BitmapFactory.Options bounds = new BitmapFactory.Options();
this.bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, bounds);
if (bounds.outWidth == -1) { // TODO: Error }
int width = bounds.outWidth;
int height = bounds.outHeight;
boolean withinBounds = width <= maxWidth && height <= maxHeight;
if (!withinBounds) {
    int newWidth = calculateNewWidth(int width, int height);
    float sampleSizeF = (float) width / (float) newWidth;
    int sampleSize = Math.round(sampleSizeF);
    BitmapFactory.Options resample = new BitmapFactory.Options();
    resample.inSampleSize = sampleSize;
    bitmap = BitmapFactory.decodeFile(filePath, resample);
}

采纳答案by sirdodger

You are on the right track, however you are trying to do two things at once: read the file in and scale it to the appropriate size.

您走在正确的轨道上,但是您正在尝试同时做两件事:读入文件并将其缩放到适当的大小。

The first step is to read the file to a Bitmap slightly bigger than you require, using BitmapFactory.Options.inSampleSize to ensure that you do not consume excessive memory reading a large bitmap when all you want is a smaller thumbnail or screen resolution image.

第一步是将文件读取到比您需要的稍大的位图,使用 BitmapFactory.Options.inSampleSize 确保当您只需要较小的缩略图或屏幕分辨率图像时,不会消耗过多的内存来读取大位图。

The second step is to call Bitmap.createScaledBitmap() to create a new bitmap to the exact resolution you require.

第二步是调用 Bitmap.createScaledBitmap() 以创建一个新的位图到您需要的精确分辨率。

Make sure you clean up after the temporary bitmap to reclaim its memory. (Either let the variable go out of scope and let the GC deal with it, or call .recycle() on it if you are loading lots of images and are running tight on memory.)

确保在临时位图之后进行清理以回收其内存。(要么让变量超出范围并让 GC 处理它,要么在加载大量图像并且内存紧张时调用 .recycle() 。)

回答by TheCodeArtist

You may want to use inJustDecodeBounds. Set it to TRUEand load the file as it is.

您可能想要使用inJustDecodeBounds. 将其设置为TRUE并按原样加载文件。

The image won't be loaded into memory. But the outheightand outwidthproperties of BitmapFactory.Optionswill contain the actual size params of the image specified. Calculate how much u want to subsample it. i.e. 1/2 or 1/4 or 1/8 etc. and assign 2/4/8 etc. accordingly to the inSampleSize.

图像不会加载到内存中。但outheightoutwidth的性质BitmapFactory.Options将包含指定图像的实际尺寸PARAMS。计算你想要对它进行多少次采样。即 1/2 或 1/4 或 1/8 等,并相应地将 2/4/8 等分配给inSampleSize

Now set inJustDecodeBoundsto FALSEand call BitmapFactory.decodeFile()to load the image of the exact size as calculated above.

现在设置inJustDecodeBoundsFALSE并调用BitmapFactory.decodeFile()以加载如上计算出的精确尺寸的图像。

回答by Ravi Bhojani

First you need to sample the image to nearest sampling value so that bitmap become memory efficient, which you have described perfectly. Once it's done you can scale it up to fit your screen.

首先,您需要将图像采样到最接近的采样值,以便位图变得高效,您已经完美地描述了这一点。完成后,您可以将其放大以适合您的屏幕。

// This function taking care of sampling
backImage =decodeSampledBitmapFromResource(getResources(),R.drawable.back, width, height);

// This will scale it to your screen width and height. you need to pass screen width and height.
backImage = Bitmap.createScaledBitmap(backImage, width, height, false); 

回答by Ankur Gautam

ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))

You can directly set the THUMBSIZE depending upon your requirement,however i am not sure about the lower level implementation details,output image quality and its performance but i usually prefer it when i need to show thumbnail.

您可以根据您的要求直接设置 THUMBSIZE,但是我不确定较低级别的实现细节、输出图像质量及其性能,但当我需要显示缩略图时,我通常更喜欢它。

回答by Amit Padekar

For those who are looking for exact size image from resources.

对于那些正在从资源中寻找精确尺寸图像的人。

For exact width pass reqHight=0 and for exact height pass reqWidth=0

对于精确宽度传递 reqHight=0 和精确高度传递 reqWidth=0

public static Bitmap decodeBitmapResource(Context context, int resId, int reqWidth, int reqHeight)
{
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(context.getResources(), resId, options);
    float scale = 1;
    if (reqWidth != 0 && reqHeight == 0)
    {
        options.inSampleSize = (int) Math.ceil(options.outWidth / (float) reqWidth);
        scale = (float) options.outWidth / (float) reqWidth;
    }
    else if (reqHeight != 0 && reqWidth == 0)
    {
        options.inSampleSize = (int) Math.ceil(options.outHeight / (float) reqHeight);
        scale = (float) options.outHeight / (float) reqHeight;
    }
    else if (reqHeight != 0 && reqWidth != 0)
    {
        float x = (float) options.outWidth / (float) reqWidth;
        float y = (float) options.outHeight / (float) reqHeight;
        scale = x > y ? x : y;
    }
    reqWidth = (int) ((float) options.outWidth / scale);
    reqHeight = (int) ((float) options.outHeight / scale);
    options.inJustDecodeBounds = false;
    Bitmap tmp = BitmapFactory.decodeResource(context.getResources(), resId, options);
    Bitmap out = Bitmap.createScaledBitmap(tmp, reqWidth, reqHeight, false);
    tmp.recycle();
    tmp = null;
    return out;
}

回答by the100rabh

Since the API already states that the sample size may or may not be honored. So I guess ur out of luck while using BitmapFactory.

由于 API 已经声明样本量可能会或可能不会受到尊重。所以我猜你在使用BitmapFactory.

But the way out would be using your own bitmap reader. But I would suggest that you stick with BitmapFactory as its fairly well tested and standardized.

但出路是使用您自己的位图阅读器。但我建议你坚持使用 BitmapFactory,因为它经过了很好的测试和标准化。

I would try and not worry too much about little extra memory consumption, but try and find out why its not honoring the values. Sadly I have not idea about that :(

我会尽量不要太担心额外的内存消耗,而是尝试找出为什么它不尊重这些值。可悲的是,我对此一无所知:(

回答by Fedor

Since I use inSampleSize I never face out of memory issues any more. So inSampleSize works quite stable for me. I would recommend you to double check the issue. The size may be not exacly the same as you requested. But it should be reduced anyway and there should be no more "Out of memory". I would also recommend to post your code snippet here, maybe there's something wrong with it.

自从我使用 inSampleSize 以来,我再也不会遇到内存不足的问题了。所以 inSampleSize 对我来说非常稳定。我建议你仔细检查这个问题。尺寸可能与您要求的不完全相同。但无论如何都应该减少它,并且应该不再有“内存不足”。我还建议在此处发布您的代码片段,也许它有问题。

回答by Tal Pressman

As the100rabh said, if you use BitmapFactory you don't really have much choice. However, bitmap decoding is really simple, and depending on the scaling algorithm you want to use, it's usually fairly simple to scale it on-the-fly without having to read large parts of the original bitmap into memory (in general, you'll only need double the memory required for 1-2 lines of the original bitmap).

正如 the100rabh 所说,如果你使用 BitmapFactory,你真的没有太多选择。但是,位图解码非常简单,根据您要使用的缩放算法,即时缩放它通常相当简单,而无需将原始位图的大部分读入内存(通常,您将只需要原始位图 1-2 行所需内存的两倍)。

回答by ucMedia

When inScaled flag is set (by default it is TRUE), if inDensity and inTargetDensity are not 0, the bitmap will be scaled to match inTargetDensity when loaded, rather than relying on the graphics system scaling it each time it is drawn to a Canvas. So simply set the inScaled to false will do.

当设置 inScaled 标志时(默认情况下为 TRUE),如果 inDensity 和 inTargetDensity 不为 0,则位图将在加载时缩放以匹配 inTargetDensity,而不是在每次将其绘制到 Canvas 时依赖图形系统对其进行缩放。所以只需将 inScaled 设置为 false 就可以了。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;

回答by masarapmabuhay

I was able to approximately get the "right size" by rescaling the decoded image file to 70% its bitmap size.

通过将解码的图像文件重新缩放到其位图大小的 70%,我能够大致获得“正确的大小”。

Bitmap myBitmap = BitmapFactory.decodeFile(path);
if(myBitmap != null)
{
  //reduce to 70% size; bitmaps produce larger than actual image size
  Bitmap rescaledMyBitmap = Bitmap.createScaledBitmap(
                                                  myBitmap, 
                                                  myBitmap.getWidth()/10*7, 
                                                  myBitmap.getHeight()/10*7, 
                                                  false);

  image.setImageBitmap(rescaledMyBitmap);
}                

I hope this helps you out somehow! Peace!

我希望这能以某种方式帮助你!和平!