BitmapFactory.Options中的inDensity,inTargetDensity,inScreenDensity詳解

我們來(lái)看下這三個(gè)值的初始值分別是什么

inTargetDensity

通過(guò)搜索,這個(gè)值只在decodeResourceStream方法中進(jìn)行了賦值操作

......
  if (opts.inTargetDensity == 0 && res != null) {
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }
......

從代碼中可以看出,inTargetDensity的取值是獲取當(dāng)前手機(jī)的densityDpi,即當(dāng)前的手機(jī)運(yùn)行環(huán)境的手機(jī)密度

inScreenDensity

搜索整個(gè)BitmapFactory的所有代碼,發(fā)現(xiàn)這個(gè)變量并沒(méi)有賦值的地方,只有一個(gè)使用的地方setDensityFromOptions

 private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
        if (outputBitmap == null || opts == null) return;

        final int density = opts.inDensity;
        if (density != 0) {
            outputBitmap.setDensity(density);
            final int targetDensity = opts.inTargetDensity;
           //對(duì)desity和targetDensity進(jìn)行比較
            if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
                return;
            }

            byte[] np = outputBitmap.getNinePatchChunk();
            final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
            if (opts.inScaled || isNinePatch) {
                outputBitmap.setDensity(targetDensity);
            }
        } else if (opts.inBitmap != null) {
            // bitmap was reused, ensure density is reset
            outputBitmap.setDensity(Bitmap.getDefaultDensity());
        }
    }

從而,我們可以知道,這個(gè)值默認(rèn)的情況下一直是0。

inDensity

同樣的,搜索BitmapFactory的源碼,可以搜索到賦值的地方是:

 public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) {
      ......
        if (opts.inDensity == 0 && value != null) {
            final int density = value.density;
            if (density == TypedValue.DENSITY_DEFAULT) {
                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            } else if (density != TypedValue.DENSITY_NONE) {
                opts.inDensity = density;
            }
        }
        ......
        return decodeStream(is, pad, opts);
    }

這個(gè)方法是在decodeResource中調(diào)用

    public static Bitmap decodeResource(Resources res, int id, Options opts) {
            ......
            final TypedValue value = new TypedValue();
            //通過(guò)資源的id去加載圖片資源的信息,并保存在TypedValue中
            is = res.openRawResource(id, value);

            bm = decodeResourceStream(res, value, is, null, opts);
            .....
    }

這里代碼其實(shí)就是分為兩步:

  1. 通過(guò)資源的id去加載合適的資源,并把一些參數(shù)保存在TypedValue對(duì)象中,如圖片的對(duì)應(yīng)文件夾的density
  2. 通過(guò)第1步拿到資源流,去讀取我們需要的圖片

從上面的代碼可以看到density的值是把從資源中讀取的density賦值給它,其實(shí)就是我們的圖片資源所在drawable對(duì)應(yīng)的density,也就是下面表格的對(duì)應(yīng)的值:


image.png

三個(gè)值的作用

從上面的賦值中,我們知道了這三個(gè)變量表示的意思,那么我們來(lái)看看這個(gè)三個(gè)變量是怎么使用的。

public final int getByteCount() {
    return getRowBytes() * getHeight();
}
public final int getRowBytes() {
    ......
    return nativeRowBytes(mNativePtr);
}

翻閱nativeRowBytes這個(gè)native的源碼,可以知道一個(gè)bitmap對(duì)象在運(yùn)行時(shí)所占內(nèi)存大小是:bitmap占用的寬 * bitmap占用的高*每個(gè)像素占用的字節(jié),那么這里的寬高又是什么呢?圖片的寬高嗎?我們來(lái)看下decodeResourceStream方法的源碼:

  public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) {
        validate(opts);
        if (opts == null) {
            opts = new Options();
        }

        if (opts.inDensity == 0 && value != null) {
            final int density = value.density;
            if (density == TypedValue.DENSITY_DEFAULT) {
                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            } else if (density != TypedValue.DENSITY_NONE) {
                opts.inDensity = density;
            }
        }
        
        if (opts.inTargetDensity == 0 && res != null) {
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }
        
        return decodeStream(is, pad, opts);
    }
    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
        ......
        Bitmap bm = null;

        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
        try {
            if (is instanceof AssetManager.AssetInputStream) {
                final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
                bm = nativeDecodeAsset(asset, outPadding, opts);
            } else {
                bm = decodeStreamInternal(is, outPadding, opts);
            }
         ......
        return bm;
    }

調(diào)用nativeDecodeAsset或者decodeStreamInternal方法,這兩個(gè)方法最終都會(huì)調(diào)用到BitmapFactory.cpp中的doDecode方法

static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
        jobject options, bool allowPurgeable, bool forcePurgeable = false) {


    if (options != NULL) {

        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
            const int density = env->GetIntField(options, gOptions_densityFieldID);
            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
             //代碼1
            if (density != 0 && targetDensity != 0 && density != screenDensity) {
                scale = (float) targetDensity / density;
            }
        }
    }
    //代碼2
    int scaledWidth = decodingBitmap.width();
    int scaledHeight = decodingBitmap.height();

    //代碼3
    if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
        scaledWidth = int(scaledWidth * scale + 0.5f);
        scaledHeight = int(scaledHeight * scale + 0.5f);
    }

    // update options (if any)
    if (options != NULL) {
        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
        env->SetObjectField(options, gOptions_mimeFieldID,
                getMimeTypeString(env, decoder->getFormat()));
    }
}

從上面的代碼中:
代碼1處的代碼:
根據(jù)density,targetDensity,screenDensity來(lái)計(jì)算縮放的比例scale,scale = (float) targetDensity / density
標(biāo)志2處的代碼:獲取原始圖片的原始寬高
標(biāo)志3處的代碼:根據(jù)上面計(jì)算的縮放比例scale,重新計(jì)算實(shí)際的寬高。那這個(gè)有什么作用呢?

總結(jié):
這三個(gè)參數(shù)使用來(lái)計(jì)算bitmap對(duì)象的寬高的,總結(jié)來(lái)說(shuō)一個(gè)bitmap的占用內(nèi)存為:

bitmap的寬=原圖片的寬 / inSampleSize * (inTargetDensity / inDensity+0.5f)
bitmap的高=原圖片的高 / inSampleSize * (inTargetDensity / inDensity+0.5f)
bitmap所占內(nèi)存的大小=bitmap的寬*bitmap的高*每個(gè)像素所占內(nèi)存
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容