62

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

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

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

注意:解码器将尝试满足此请求,但生成的位图可能具有与所请求的尺寸不同的尺寸。此外,2 的幂通常更快/更容易让解码器兑现。

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

编辑:

当前源代码:

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);
}
4

11 回答 11

47

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

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

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

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

于 2010-07-29T16:57:02.577 回答
17

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

图像不会加载到内存中。但是BitmapFactory.Options的outheightoutwidth属性将包含指定图像的实际大小参数。计算你想对它进行多少次采样。即 1/2 或 1/4 或 1/8 等,并根据inSampleSize分配 2/4/8 等。

现在设置并调用inJustDecodeBounds以加载上面计算的精确大小的图像。FALSEBitmapFactory.decodeFile()

于 2010-04-17T16:56:31.660 回答
7

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

// 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); 
于 2012-06-03T08:22:34.700 回答
1
ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filesPath), THUMBSIZE, THUMBSIZE))

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

于 2014-01-25T06:49:04.353 回答
1

由于 API 已经声明样本大小可能会或可能不会被兑现。所以我猜你在使用BitmapFactory.

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

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

于 2010-04-18T14:20:40.490 回答
1

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

对于精确宽度传递 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;
}
于 2016-08-02T07:32:12.543 回答
0

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

于 2010-04-22T09:41:32.607 回答
0

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

于 2010-04-21T15:09:14.940 回答
0

通过将解码的图像文件重新缩放到其位图大小的 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);
}                

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

于 2017-04-04T03:14:26.490 回答
0

文档非常不言自明。首先,我想从那里制作一个小片段,但发现完整阅读更有助于理解,并且不会因记忆而失败。最后,它很小。

于 2019-12-05T19:29:26.970 回答
0

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

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
于 2016-12-25T16:40:11.950 回答