[Android]修改Bitmap的Config格式设置及其Config参数源码阅读

关于修改Bitmap的Config设置


原创博客,转载请注明出处:
http://blog.csdn.net/u011035622/article/details/50277565


Bitmap.Config介绍

  首先先介绍一下Config设置里面的参数吧,一方面让读者清楚,另一方面也是给自己加深印象。
  Bitmap.Config一共有四个参数如下:
(这些参数决定了Bitmap位图的配置,会影响到bitmap的像素如何、色彩、以及是否有透明度的能力)

  1. Bitmap.Config ALPHA_8
    这个参数每个像素占用1字节的空间。
    它代表每个像素点被存储为单个透明度的通道,这对于设置遮罩的图片用例十分有用,它不存储颜色信息。

  2. Bitmap.Config RGB_565
    这个参数每个像素占用2字节的空间。
    它代表只有RGB通道的编码,其中红色占用5位地址,绿色占用6位地址,蓝色占用5位地址。没有透明度的通道。
    使用不透明的位图时,不要求高的色彩保真度使用此配置是不错的选择。

  3. Bitmap.Config ARGB_4444
    这个参数每个像素占用2字节的空间。
    它一共有四个通道,顾名思义,分别是透明度、红、绿、蓝。每个通道分别占用四位地址,所以一共2字节。
    当应用需要节省内存(对色彩质量要求低),同时又需要存储透明度信息,这个配置可以作为选择,但官方比较推荐用ARGB_8888的设置,因为这个的色彩质量差。

  4. Bitmap.Config ARGB_8888
    这个参数每个像素占用4字节的空间。
    这也是一共4个通道,但不一样的是每个通道站8位地址,因而色彩质量比上一个设置高了特别特别多(16倍)。
    能够满足最好的位图质量,在内存充足的情况下,十分推荐使用这个。

    以下附参考的官方源码解释:

    /**
     * Possible bitmap configurations. A bitmap configuration describes
     * how pixels are stored. This affects the quality (color depth) as
     * well as the ability to display transparent/translucent colors.
     */
    public enum Config {
        // these native values must match up with the enum in SkBitmap.h

        /**
         * Each pixel is stored as a single translucency (alpha) channel.
         * This is very useful to efficiently store masks for instance.
         * No color information is stored.
         * With this configuration, each pixel requires 1 byte of memory.
         */
        ALPHA_8     (1),

        /**
         * Each pixel is stored on 2 bytes and only the RGB channels are
         * encoded: red is stored with 5 bits of precision (32 possible
         * values), green is stored with 6 bits of precision (64 possible
         * values) and blue is stored with 5 bits of precision.
         * 
         * This configuration can produce slight visual artifacts depending
         * on the configuration of the source. For instance, without
         * dithering, the result might show a greenish tint. To get better
         * results dithering should be applied.
         * 
         * This configuration may be useful when using opaque bitmaps
         * that do not require high color fidelity.
         */
        RGB_565     (3),

        /**
         * Each pixel is stored on 2 bytes. The three RGB color channels
         * and the alpha channel (translucency) are stored with a 4 bits
         * precision (16 possible values.)
         * 
         * This configuration is mostly useful if the application needs
         * to store translucency information but also needs to save
         * memory.
         * 
         * It is recommended to use {@link #ARGB_8888} instead of this
         * configuration.
         *
         * Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},
         * any bitmap created with this configuration will be created
         * using {@link #ARGB_8888} instead.
         * 
         * @deprecated Because of the poor quality of this configuration,
         *             it is advised to use {@link #ARGB_8888} instead.
         */
        @Deprecated
        ARGB_4444   (4),

        /**
         * Each pixel is stored on 4 bytes. Each channel (RGB and alpha
         * for translucency) is stored with 8 bits of precision (256
         * possible values.)
         * 
         * This configuration is very flexible and offers the best
         * quality. It should be used whenever possible.
         */
        ARGB_8888   (5);

        final int nativeInt;

        @SuppressWarnings({"deprecation"})
        private static Config sConfigs[] = {
            null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
        };

        Config(int ni) {
            this.nativeInt = ni;
        }

        static Config nativeToConfig(int ni) {
            return sConfigs[ni];
        }
    }

以上第一部分只是介绍,而接下来的内容才是我想记录的内容。

修改Bitmap的Config设置


修改时遇到的问题

  如果是本地图片路径的话,我们可以直接通过这样的方法得到一张bitmap并通过Options.inPreferredConfig来设置他的Config参数(如果你看一下BitmapFactory的源码,就会发现默认的就是Config.ARGB_8888格式)。

     Bitmap bitmap = null;
     File file = new File(path);
     if(file.exists()) {
         BitmapFactory.Options opts = new BitmapFactory.Options();
         opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
         try {
             bitmap = BitmapFactory.decodeFile(path, opts);
         } catch (OutOfMemoryError error) {
             bitmap = null;
         }
     }

  这并没有什么问题。
  但是当我们已经有一张Bitmap了,如何修改他的Config设置呢,大家去看Bitmap里面的方法会发现有类似于setConfig或这reconfigure的方法(事实上前者调用的是后者的方法,后者调用的是native方法-nativeReconfigure)。
  当看到这个方法时我直接的调用了它。

bitmap.setConfig(Bitmap.Config.ARGB_8888);

  当然跪了。。
***AndroidRuntime: FATAL EXCEPTION:
main java.lang.NoSuchMethodError:android.graphics.Bitmap.reconfigure***


问题解决方法

折腾好一会不知道错哪里,点看源码看下才知道:
  

setConfig、reconfigure方法不能用于目前使用的位图!!!=。=
  (最后贴官方源码读者可以参考看看)

  思考了一下,我们就只能先将当前的bitmap转位字节流,然后通过Bitmapfactory中的方法将这个字节流根据设置重新生成bitmap。
  果然,问题解决:

      /*传入bitmap参数,返回bitmap。*/
      ByteArrayOutputStream dataByte = new ByteArrayOutputStream();
      bitmap.compress(Bitmap.CompressFormat.PNG, 100, dataByte);
      BitmapFactory.Options opts = new BitmapFactory.Options();
      opts.inSampleSize = OPT_INSAMPLE_SIZE;
      opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
      bitmap = BitmapFactory.decodeByteArray(dataByte.toByteArray(), 0, dataByte.size(), opts);

  多次经历告诉我,学习时常常点进去看看源码和介绍真的很重要。决定找个时间把Bitmap和BitmapFactory的源码走一遍!


最后贴上setConfig的官方源码部分:

    /**
     * 

Convenience method for calling {@link #reconfigure(int, int, Config)} with the current height and width.

*(警告不能用于当前bitmap=。=) *

WARNING: this method should not be used on bitmaps currently used by the view system, see {@link #reconfigure(int, int, Config)} for more details. */ public void setConfig(Config config) { reconfigure(getWidth(), getHeight(), config); }

reconfigure:

这里还讲到:
1、修改Bitmap可能会影响到hasalpha,即格式转变可能有或没有透明通道。
2、如果位图的配置不够大,支持新的配置,IllegalArgumentException将抛出,位图将不被修改。

    /**
     * 

Modifies the bitmap to have a specified width, height, and {@link * Config}, without affecting the underlying allocation backing the bitmap. * Bitmap pixel data is not re-initialized for the new configuration.

* *

This method can be used to avoid allocating a new bitmap, instead * reusing an existing bitmap's allocation for a new configuration of equal * or lesser size. If the Bitmap's allocation isn't large enough to support * the new configuration, an IllegalArgumentException will be thrown and the * bitmap will not be modified.

* *

The result of {@link #getByteCount()} will reflect the new configuration, * while {@link #getAllocationByteCount()} will reflect that of the initial * configuration.

* *

Note: This may change this result of hasAlpha(). When converting to 565, * the new bitmap will always be considered opaque. When converting from 565, * the new bitmap will be considered non-opaque, and will respect the value * set by setPremultiplied().

* *

WARNING: This method should NOT be called on a bitmap currently used * by the view system. It does not make guarantees about how the underlying * pixel buffer is remapped to the new config, just that the allocation is * reused. Additionally, the view system does not account for bitmap * properties being modifying during use, e.g. while attached to * drawables.

* * @see #setWidth(int) * @see #setHeight(int) * @see #setConfig(Config) */ public void reconfigure(int width, int height, Config config) { checkRecycled("Can't call reconfigure() on a recycled bitmap"); if (width <= 0 || height <= 0) { throw new IllegalArgumentException("width and height must be > 0"); } if (!isMutable()) { throw new IllegalStateException("only mutable bitmaps may be reconfigured"); } if (mBuffer == null) { throw new IllegalStateException("native-backed bitmaps may not be reconfigured"); } nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length, mRequestPremultiplied); mWidth = width; mHeight = height; }

你可能感兴趣的:(android)