• [译]Android调整图像大小的一些方法



    翻译自 [某大神在Stack Overflow里的自问自答](http://stackoverflow.com/questions/32121058/most-memory-efficient-way-to-resize-bitmaps-on-android) (一般我们将Bitmap翻译为位图,但为了更好理解,在本文中我将它翻译成图像);
    我们在开发的时候,经常需要从服务器中加载图像到客户端中,但有时手机屏幕较小(服务器传来的图像是大图)导致我们需要重新调整图像的大小以适应手机的屏幕。我们可以使用createScaledBitmap方法来调整图像的大小,可当我们使用createScaledBitmap来得到大量的缩略图后(图像数量较大),会导致许多的内存溢出错误(out-of-memory errors)。那么问题来了,在Android中哪种方式是调整图像大小的最有效的内存的利用方式呢?
    
    文章Loading large bitmaps Efficiently(现在打不开,你懂的) 介绍了怎样利用isSampleSize去加载一个图像的缩略图,这里只是对它进行一个总结;文章Pre-scaling bitmaps(现在打不开,你懂的) 详细介绍了调整图像大小的各种方法,并且怎样去混合使用这些方法得到一个最好的内存利用方式;
    

    在Android中有三种主要的方式来调整图像的大小,并且每种方法会有不同的内存性能:

    1. createScaledBitmap API

    这个API会加载一个已经存在的图像,并用你希望得到的图像尺寸来创建一个新的图像。一方面,你可以得到你想得到的确切尺寸的图像。但这个API可以正常工作的前提是已经有一个图像(大图)存在了。这意味着在创建新尺寸的图像前,原来的图像会先经历加载,解码,创建的过程(在内存中创建这个大图)。这是理想的得到确切图像尺寸的方式,但这是以额外的内存开销为代价的。

    2. inSampleSize 属性

    BitmapFactory.Options的属性inSampleSize在解码时就会重新调整图像的大小,避免为临时的图像进行解码操作。在加载图像时,会使用一个整形值x来加载原图1/x的图像。例如,设置inSampleSize的值为2,则会返回一个1/2原图大小的图像,设置inSampleSize的值为4,则会返回一个1/4原图大小的图像。一般来说,图像的大小会比原图尺寸小2的某次方;
    

    从内存性能的角度,使用inSampleSize是最快的一种方式,因为它只解码原图的1/x像素到最终的图像里。inSampleSize也有两个主要的问题:

    • 它不会给你一个确切的分辨率,它只会减小原图的2的某次方大小;
    • 它不会产生重新调整后的最好的图像质量,大部分的调整过滤器都会通过读取像素块,并根据权重来得到调整后的像素。但inSampleSize仅仅是每隔几个像素读取一个像素来保证高性能,低内存,但图像的质量可能就没有那么好。

    如果你只是想得到原图的某个比例的图像,但对图像的质量没什么要求,这种方法的最高效的内存利用方式;

    3. inScaled, inDensity, inTargetDensity 属性

    如果你想得到的图像的尺寸并不是原图的2的某次方之一($1/2^x$),那么你就需要BitmapFactory.Options的这些属性inScaled, inDensity, inTargetDensity。如果设置了inScaled属性,系统就会通过inDensity的值和inTargetDensity的值来得到新图的尺寸并用这个尺寸来创建新图。如:
    
    mBitmapOptions.inScaled = true;
    mBitmapOptions.inDensity = srcWidth;
    mBitmapOptions.inTargetDensity =  dstWidth;
    
    // will load & resize the image to be 1/inSampleSize dimensions
    mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
          mImageIDs, mBitmapOptions);
    
    使用这个方法可以得到较好图像质量的缩略图,因为在调整的过程中,会运用图像过滤器(也就是某些数字方法来补偿)来让图像看起来更好。但需要注意的是:额外的过滤补偿,会带来额外的处理时间,这个时间在处理大图像时会快速增强,会导致调整的时间变慢,并且过滤器本身也需要额外的内存分配。
    

    因此,如果原图比你希望得到的图像的大小大太多的话,这个方法并不会是比较好的选择,因为它需要额外的过滤补偿过程;

    4. 混合使用这些方法

    从内存和性能的角度考虑,我们可以考虑混合使用这些方法来得到一个最好的结果(设置inSampleSize,inScaled, inDensity, inTargetDensity 属性)。
    
    首先设置inSampleSize比希望得到的图像尺寸的2的某次方大(如:希望得到一个原图1/4大小的图像,则设置inSampleSize的值为2,这些就会先得到原图1/2大小的图像)。然后通过设置inDensity, inTargetDensity属性来精确需要得到图像的尺寸,并使用过滤器来处理图像(让图像变得更好看)。
    

    混合使用这两个方法是比较快速的操作,因为inSampleSize操作会减小后面操作的像素。如:

    mBitmapOptions.inScaled = true;
    mBitmapOptions.inSampleSize = 4
    mBitmapOptions.inDensity = srcWidth;
    mBitmapOptions.inTargetDensity =  dstWidth * mBitmapOptions.inSampleSize;
    
    // will load & resize the image to be 1/inSampleSize dimensions
    mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);
    

    因此如果你需要得到一个精确尺寸,并且图像质量还可以的图像,这个方法是一个不错的选择。

    5. 得到图像的尺寸

    为了调整图像的大小在不解码原图的情况下得到原图的尺寸。通过设置inJustDecodeBounds属性来帮助你得到原图的尺寸;如:

    // Decode just the boundries
    mBitmapOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(fileName, mBitmapOptions);
    srcWidth = mBitmapOptions.outWidth;
    srcHeight = mBitmapOptions.outHeight;
    
    
    //now go resize the image to the size you want
    

    你可以使用这个属性来先得到原图的尺寸,然后计算得到目标图像的具体值;

  • 相关阅读:
    C++的一道变态题
    关于"短信处理"模式的一段讨论
    Fire Balls 10——音效的添加
    Stealth——01场景的基本搭建以及基础逻辑
    Fire Balls 09——砖块的淡出,消失以及砖塔的下落
    Fire Balls 08——子弹的消失,当子弹击中自身时不可发射子弹
    Unity进阶:PlayMaker
    Fire Balls 07——子弹的命中及后续效果
    Fire Balls 06——坦克和子弹的制作以及炮台发射子弹
    Fire Balls 05——砖塔的创建,动态上升以及旋转
  • 原文地址:https://www.cnblogs.com/WoodJim/p/4759674.html
Copyright © 2020-2023  润新知