使用正确的大小解码Android中的位图

时间:2010-04-14 23:22:43

标签: android memory bitmap sd-card

我使用BitmapFactory.decodeFile解码SD卡中的位图。有时位图大于应用程序需要或堆允许的位图,因此我使用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);
}

11 个答案:

答案 0 :(得分:44)

你走在正确的轨道上,但是你想要同时做两件事:阅读文件并将其缩放到适当的大小。

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

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

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

答案 1 :(得分:15)

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

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

现在将 inJustDecodeBounds 设置为 FALSE ,然后致电BitmapFactory.decodeFile()加载上面计算的确切尺寸的图片。< / p>

答案 2 :(得分: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); 

答案 3 :(得分:1)

由于API已经声明样本大小可能会或可能不会受到尊重。 所以我猜你在使用BitmapFactory时运气不好。

但是出路将使用您自己的位图阅读器。但我建议您坚持使用BitmapFactory作为其经过充分测试和标准化的测试。

我会尝试而不是太担心额外的内存消耗,但试着找出为什么不尊重这些值。可悲的是,我对此并不了解:(

答案 4 :(得分:1)

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

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

答案 5 :(得分: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;
}

答案 6 :(得分:0)

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

答案 7 :(得分:0)

正如100rabh所说,如果你使用BitmapFactory,你真的没有多少选择。然而,位图解码非常简单,并且根据您想要使用的缩放算法,通常可以非常简单地在不必将原始位图的大部分内容读入内存的情况下进行扩展(通常,您将会只需要原始位图的1-2行所需的内存加倍。

答案 8 :(得分:0)

当设置inScaled标志时(默认情况下为TRUE),如果inDensity和inTargetDensity不为0,则位图将在加载时缩放以匹配inTargetDensity,而不是每次绘制时都依赖于图形系统缩放它一幅画布。所以只需将inScaled设置为false即可。

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

答案 9 :(得分: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);
}                

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

答案 10 :(得分:0)

Documentation非常不解。首先,我想从中取出一个小片段,但发现全读更有助于理解,并且对于不使内存出现某些故障是必需的。最后,它很小。