Bitmap 在內(nèi)存中的實(shí)際大小

占了多大內(nèi)存?

首先這里要明確的是

  • 占用內(nèi)存

  • 圖片大小

以上兩者是不同的,占用內(nèi)存表示圖片被加載進(jìn)來(lái)以后占用的內(nèi)存空間大小,圖片大小則是圖片在磁盤存儲(chǔ)時(shí)占用的大小。

兩者之間有什么關(guān)系么?下面再講。

獲取一個(gè)biemap占用多大內(nèi)存空間的方法如下:

       int sizeOf = bitmap.getRowBytes() * bitmap.getHeight();
       //getRowBytes 表示一行的字節(jié)數(shù),getHeight可以認(rèn)為總共有多少行。

舉個(gè)例子

現(xiàn)在有一個(gè)1080*1920大小26.6K的圖片放在xxhdpi文件夾中加載到內(nèi)存中,獲取到的大小為:

8294400 B = 7.9 MB

為什么會(huì)這么大?

實(shí)際上是因?yàn)? 1080 * 1920 * 4

為什么要乘以四,因?yàn)閳D片的大小不但和像素有關(guān)還和色彩格式有關(guān)ARGB_8888一個(gè)像素占用四個(gè)字節(jié)。

同時(shí)還與圖片所在的資源目錄有關(guān)

測(cè)試手機(jī)為 1+3T 1920 * 1080 5.5寸 像素密度為 400 ==> xxhdpi,

而如果放在xhdpi下 則為 1080 * 1.5 * 1920 * 1.5 *4 = 18662400 至于為什么這么算請(qǐng)看下面。

怎么計(jì)算的

先說(shuō)結(jié)論Bitmap在內(nèi)存當(dāng)中占用的大小其實(shí)取決于以下三點(diǎn):

  • 色彩格式
  • 原始文件存放的資源目錄
  • 目標(biāo)屏幕的密度

Bitmap 的生成

關(guān)于 bitmap 的原理網(wǎng)上有很多文章這里就不在講了,可以參考這里,其中在生成過(guò)程中有下面的關(guān)鍵代碼。

outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
                colorType, decodingBitmap.alphaType()));

可以看到最終輸出的 outputBitmap 的寬高分別為是 scaledWidth、scaledHeight,把這兩個(gè)變量計(jì)算的片段如下:

if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
     scaledWidth = int(scaledWidth * scale + 0.5f);
     scaledHeight = int(scaledHeight * scale + 0.5f);
}

其中 scale 的大小是這樣得來(lái)的:

if (density != 0 && targetDensity != 0 && density != screenDensity) {
                scale = (float) targetDensity / density;
    }

scale 是由 density 和 targetDensity 得來(lái)的,density 是 decodingBitmap 的 density,這個(gè)值跟這張圖片的放置的目錄有關(guān),targetDensity 是目標(biāo)的 density,它的值和當(dāng)前設(shè)備的屏幕密度有關(guān),比如例子中的1+3T就屬于 xxhdpi 則值為480。

到這里我們就知道一個(gè)圖片的寬高值了,那圖片的大小是不是就是 width * height 呢?顯然不是,接著往下看。

獲取大小

獲取大小的方法在最上面我們就講了,獲取一個(gè)biemap占用多大內(nèi)存空間的方法如下:

       int sizeOf = bitmap.getRowBytes() * bitmap.getHeight();
       //getRowBytes 表示一行的字節(jié)數(shù),getHeight可以認(rèn)為總共有多少行。

getHeight 就是獲取高度單位是(px)高度的計(jì)算方法上面已經(jīng)講了,getRowBytes 又是什么?

public final int getRowBytes() {
    if (mRecycled) {
        Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
    }
    return nativeRowBytes(mFinalizer.mNativeBitmap);
}

可以看到它最終到 jni 里面了。

{   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes }

static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
    return static_cast<jint>(bitmap->rowBytes());
}

可以看到 bitmap 的實(shí)際類型是一個(gè) SkBitmap,

    /** Return the number of bytes between subsequent rows of the bitmap. */
   size_t rowBytes() const { return fRowBytes; }

   /** SKbitmap.cpp */

   size_t SkBitmap::ComputeRowBytes(Config c, int width) {
        return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
    }

SkImageInfo.h

// 不同色彩格式所占的字節(jié)數(shù)
static int SkColorTypeBytesPerPixel(SkColorType ct) {
    static const uint8_t gSize[] = {
        0,  // Unknown
        1,  // Alpha_8
        2,  // RGB_565
        2,  // ARGB_4444
        4,  // RGBA_8888
        4,  // BGRA_8888
        1,  // kIndex_8
    };
    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
                      size_mismatch_with_SkColorType_enum);

    SkASSERT((size_t)ct < SK_ARRAY_COUNT(gSize));
    return gSize[ct];
}

// getRowBytes 的返回值
static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) {
    return width * SkColorTypeBytesPerPixel(ct);
}

最終可以追蹤到 getRowBytes 的值為 寬度 * 字節(jié)數(shù)。

所以呢大小怎么算?

所以可以得出一個(gè)公式,一個(gè)本地資源的圖片放在內(nèi)存中的大小為:

int(width * targetDensity/density + 0.5f) * int(height * targetDensity/density + 0.5) * gSize

targetDensity 為當(dāng)前設(shè)備的 dpi,density 為圖片原始 dpi,gSize 的值就和圖片的色彩格式有關(guān),具體對(duì)應(yīng)關(guān)系如下:

色彩格式 字節(jié)數(shù)
Alpha_8 1
RGB_565 2
ARGB_4444 2
RGBA_8888 4
BGRA_8888 4
kIndex_8 1
Unknown 0

屏幕密度對(duì)應(yīng)關(guān)系:

dpi 比例
ldpi 120 0.75
mdpi 160 1
hdpi 240 1.5
xhdpi 320 2.0
xxhdpi 480 3.0
xxxhdpi 640 4.0

上面的公式很好理解,如果要要在一個(gè) xxxhdpi 的設(shè)備上加載一個(gè)放在 xxhdpi 中的大小為 768 * 960 的 png 圖片時(shí)內(nèi)存大小就是

int(768 * 640/480 + 0.5f)* int(960 * 640/480 + 0.5f) * 4

網(wǎng)絡(luò)圖片大小

上面講了本地圖片加載到內(nèi)存中以后所占空間的計(jì)算方法,那如果是網(wǎng)絡(luò)圖片呢,由于它沒(méi)有原始 dpi,所以默認(rèn)的和當(dāng)前設(shè)備 dpi 一致,因此如果未做處理的情況下一個(gè)圖片的大小應(yīng)該為:

width * height * gsize

小結(jié)

由上面一通分析可以得知一個(gè) Bitmap 在內(nèi)存中的大小主要由以下決定:

  • 圖片的顏色格式
  • 圖片本身的大小
  • 本地圖片所在的目錄
  • 目標(biāo)設(shè)備的屏幕像素密度

參考文章
理解 Bitmap

?著作權(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)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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