Bitmap 分析


Bitmap是位圖,每個(gè)像素點(diǎn)根據(jù)位的深度表示不同種類的顏色,深度越大,代表的顏色值越多,存儲(chǔ)空間越大。


BitmapFactory工廠

從文件讀取。

public static Bitmap decodeFile(String pathName)
public static Bitmap decodeFile(String pathName, Options opts)

根據(jù)路徑,創(chuàng)建一個(gè)FileInputStream,然后,調(diào)用decodeStream方法。
從資源讀取。

public static Bitmap decodeResource(Resources res, int id, Options opts) 
public static Bitmap decodeResource(Resources res, int id)
public static Bitmap decodeResourceStream(Resources res, TypedValue value,InputStream is, Rect pad, Options opts)

前兩個(gè)方法,通過(guò)Resources的openRawResource(id, value)方法,根據(jù)資源id,調(diào)用AssetManager的openNonAsset方法,創(chuàng)建AssetInputStream。然后,他們?cè)偃フ{(diào)用decodeResourceStream方法。最終,這三個(gè)方法全部觸發(fā)decodeStream。
從字節(jié)數(shù)組讀取。

public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeByteArray(byte[] data, int offset, int length)

調(diào)用本地nativeDecodeByteArray方法。
從流讀取。

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeStream(InputStream is)

資源解析id成AssetInputStream流,調(diào)用本地nativeDecodeAsset方法。其他流調(diào)用decodeStreamInternal方法,調(diào)用本地nativeDecodeStream方法。
從文件描述符讀取。

public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd)

調(diào)本地nativeDecodeFileDescriptor方法,或創(chuàng)建FileInputStream流解析,與文件讀取類似,調(diào)用decodeStreamInternal方法。

綜上所述

BitmapFactory提供了解析文件、資源、字節(jié)數(shù)組、文件描述符、或流的方式創(chuàng)建Bitmap,在創(chuàng)建時(shí),Option配置將影響它占用的內(nèi)存。以上就是幾種主要的解析方式,下面我們根據(jù)源碼看一下InputStream是如何解析。


InputStream流解析

看一下BitmapFactory的decodeStream方法。

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    Bitmap bm = null;
    try {
        if (is instanceof AssetManager.AssetInputStream) {
            final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
            bm = nativeDecodeAsset(asset, outPadding, opts);//根據(jù)InputStream不同。
        } else {
            bm = decodeStreamInternal(is, outPadding, opts);
        }
        ...
        setDensityFromOptions(bm, opts);
    } finally {
    }
    return bm;
}

它的三個(gè)參數(shù),InputStream、Rect和Option,Options是工廠的靜態(tài)內(nèi)部類,包含一些解析的配置信息,將這些信息傳到底層。AssetInputStream流調(diào)用nativeDecodeAsset方法,其他的調(diào)用nativeDecodeStream方法。

private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
    byte [] tempStorage = null;
    if (opts != null) tempStorage = opts.inTempStorage;
    if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
    return nativeDecodeStream(is, tempStorage, outPadding, opts);
}

字節(jié)數(shù)組大小16 * 1024。

static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
        jobject padding, jobject options) {
    jobject bitmap = NULL;
    SkAutoTDelete<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage))
    if (stream.get()) {
        SkAutoTDelete<SkStreamRewindable> bufferedStream(
                SkFrontBufferedStream::Create(stream.detach(), BYTES_TO_BUFFER));
        bitmap = doDecode(env, bufferedStream, padding, options);
    }
    return bitmap;
}

該JNI#方法的第一個(gè)參數(shù)是InputStream數(shù)據(jù)輸入流,第二個(gè)參數(shù)是storage字節(jié)數(shù)組,padding是null,第四個(gè)參數(shù)是Bitmap配置Options。然后,將上層傳入的InputStream轉(zhuǎn)換底層SkStreamRewindable,JNI#方法doDecode解析bufferedStream,生成一個(gè)jobject對(duì)象,它就是Java層的Bitmap。看一下JNI#方法doDecode。該方法比較長(zhǎng),分成四個(gè)部分。

第一部分代碼
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
    int sampleSize = 1;
    SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
    SkColorType prefColorType = kN32_SkColorType;

    bool doDither = true;
    bool isMutable = false;
    float scale = 1.0f;
    bool preferQualityOverSpeed = false;
    bool requireUnpremultiplied = false;

    if (options != NULL) {
        //解析Option的inSampleSize,圖片讀取到內(nèi)存中的像素壓縮的比例。
        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
        //解析Option的inJustDecodeBounds,只獲取圖片信息,不讀像素模式。
        if (optionsJustBounds(env, options)) {
            decodeMode = SkImageDecoder::kDecodeBounds_Mode;
        }
        //初始化Option的outWidth與outHeight為-1。outMimeType為0。
        env->SetIntField(options, gOptions_widthFieldID, -1);
        env->SetIntField(options, gOptions_heightFieldID, -1);
        env->SetObjectField(options, gOptions_mimeFieldID, 0);
        //Bitmap.Config配置inPreferredConfig。
        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
        //解析出SkColorType。
        prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
        //Java層值讀取
        isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
        preferQualityOverSpeed = env->GetBooleanField(options,gOptions_preferQualityOverSpeedFieldID);
        requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
        //Java層inBitmap對(duì)象
        javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
        //Option的inScaled,默認(rèn)true
        if (env->GetBooleanField(options, gOptions_scaledFieldID)) { 
            //Option的inDensity
            const int density = env->GetIntField(options, gOptions_densityFieldID);
            //Option的inTargetDensity
            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
            //Option的inScreenDensity
            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
            if (density != 0 && targetDensity != 0 && density != screenDensity) {
                scale = (float) targetDensity / density;
            }
        }
    }
    const bool willScale = scale != 1.0f;
    ...
    //后續(xù)代碼
}

當(dāng)Java層Option參數(shù)不空時(shí)候,讀取參數(shù)信息,主要Option信息包括。
inSampleSize,圖片讀取到內(nèi)存中的像素采樣率,即圖片的壓縮比,inSampleSize是2代表圖像的長(zhǎng)寬都變?yōu)樵嫉?/2,該值根據(jù)圖片的真實(shí)像素寬高和ImageView的寬高共同計(jì)算得出。
inJustDecodeBounds,該參數(shù)表示解析時(shí)只讀取圖片信息,如圖片尺寸,不分配像素內(nèi)存空間,SkImageDecoder模式是kDecodeBounds_Mode。
isMutable,可變的,生成Bitmap可變位圖,可修改像素,默認(rèn)是false。
inPreferredConfig:Bitmap.Config圖片像素類型,基本類型有ALPHA_8、RGB_565、ARGB_4444、ARGB_8888。默認(rèn)是ARGB_8888,它的圖片質(zhì)量較高,每像素4個(gè)字節(jié),內(nèi)存占用最大。可配置成RGB_565類型,2個(gè)字節(jié)。
outHeight/outWidth,圖像寬高,初始值設(shè)置-1。
inBitmap,重用bitmap,如果在Java層設(shè)置了這個(gè)參數(shù),新Bitmap會(huì)使用該參數(shù)的內(nèi)存空間,Option#isMutable必須是可變的。
prefColorType,根據(jù)上層Config的nativeInt變量,解析SkColorType類型,與Config像素類型對(duì)應(yīng),kRGB_565_SkColorType、kIndex_8_SkColorType、kN32_SkColorType等。
inScaled,inDensity,inTargetDensity,inScreenDensity,由它們決定圖片是否縮放,首先是inScaled決定是否縮放,默認(rèn)縮放,然后,如果配置了inTargetDensity和inDensity,并且inScreenDensity和inDensity不相等,按照比例inTargetDensity/inDensity縮放,圖片去適應(yīng)屏幕。最后的結(jié)果scale不是1,將設(shè)置willScale參數(shù)。
inDensity,圖片像素密度。
inTargetDensity,目標(biāo)像素密度。根據(jù)該值與Bitmap的像素密度的比值,確定縮放比例。
inScreenDensity,屏幕像素密度。
當(dāng)我們從資源加載圖片時(shí),設(shè)置inTargetDensity值為densityDpi,因此,不同屏幕像素密度手機(jī)從資源文件加載圖片時(shí)縮放比不同,載入內(nèi)存也是不同的。densityDpi值以160像素為基準(zhǔn),240,320,480,640。

第二部分代碼
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
//decoder解析器是空返回null
//SkImageDecoder設(shè)置sampleSize、doDither、
//preferQualityOverSpeed、requireUnpremultiplied等參數(shù)。
decoder->setSampleSize(sampleSize);
...
android::Bitmap* reuseBitmap = nullptr;
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
    reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
    //這個(gè)bitmap不可變
    if (reuseBitmap->peekAtPixelRef()->isImmutable()) {//不可變的bitmap,無(wú)法重用
        javaBitmap = NULL;
        reuseBitmap = nullptr;
    } else {
        existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
    }
}
...
JavaPixelAllocator javaAllocator(env);
RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
            (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
    if (!willScale) {
        decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator);
        decoder->setAllocator(outputAllocator);
    } else if (javaBitmap != NULL) {
        decoder->setAllocator(&scaleCheckingAllocator);
    }
}

創(chuàng)建解析器SkImageDecoder,設(shè)置從Option讀取到部分配置。獲取上層重用javaBitmap的底層Bitmap對(duì)象,如果不可變,不能重用,置空。如果可重用,解析javaBitmap內(nèi)存字節(jié)大小existingBufferSize。
在非kDecodeBounds_Mode時(shí),讀取像素到內(nèi)存,SKImageDecoder的setAllocator方法,設(shè)置Allocator內(nèi)存分配器。包括三種,JavaPixelAllocator,RecyclingPixelAllocator和ScaleCheckingAllocator。
當(dāng)不需要縮放時(shí),重用Bitmap,使用RecyclingPixelAllocator,不重用Bitmap,使用JavaPixelAllocator。
當(dāng)需要縮放,重用Bitmap,使用ScaleCheckingAllocator。
只有RecyclingPixelAllocator和ScaleCheckingAllocator傳入existingBufferSize大小,與重用Bitmap相關(guān)。

第三部分代碼
SkAutoTDelete<SkImageDecoder> add(decoder);
AutoDecoderCancel adc(options, decoder);

SkBitmap decodingBitmap;
//stream解析到skbitmap
if (decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)
            != SkImageDecoder::kSuccess) {
    return nullObjectReturn("decoder->decode returned false");
}
//拿到寬高
int scaledWidth = decodingBitmap.width();
int scaledHeight = decodingBitmap.height();
//Scale處理后的寬高
if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
    scaledWidth = int(scaledWidth * scale + 0.5f);
    scaledHeight = int(scaledHeight * scale + 0.5f);
}
//得到了寬和高和mimeType,設(shè)置option
if (options != NULL) {
    jstring mimeType = getMimeTypeString(env, decoder->getFormat());
    if (env->ExceptionCheck()) {
        return nullObjectReturn("OOM in getMimeTypeString()");
    }
    env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
    env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
    env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
}
//inJustDecodeBounds為true,直接返回NULL
if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) {
    return NULL;
}
//NinePatchPeeker相關(guān)處理
...

SkImageDecode的decode方法,解碼stream后,得到SkBitmap類型的對(duì)象。
獲取SkBitmap的寬高,如果需要縮放,并且非kDecodeBounds_Mode模式,將重新計(jì)算的scaledWidth、scaledHeight和mimeType信息設(shè)置到Java層Option對(duì)象。
如果是kDecodeBounds模式,表示Java層Option設(shè)置inJustDecodeBounds參數(shù),不需要分配內(nèi)存,返回空,圖片信息已保存在Option對(duì)象。

第四部分代碼
SkBitmap outputBitmap;
if (willScale) {
    //計(jì)算Scale的比例。
    const float sx = scaledWidth / float(decodingBitmap.width());
    const float sy = scaledHeight / float(decodingBitmap.height());

    SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType());  
    outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
            colorType, decodingBitmap.alphaType()));
    if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
        return nullObjectReturn("allocation failed for scaled bitmap");
    }
   
    if (outputAllocator != &javaAllocator) {
        outputBitmap.eraseColor(0);
    }

    SkPaint paint;
    paint.setFilterQuality(kLow_SkFilterQuality);

    SkCanvas canvas(outputBitmap);
    canvas.scale(sx, sy);
    canvas.drawARGB(0x00, 0x00, 0x00, 0x00);
    canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
} else {
    outputBitmap.swap(decodingBitmap);
}

if (outputBitmap.pixelRef() == NULL) {
    return nullObjectReturn("Got null SkPixelRef");
}

if (!isMutable && javaBitmap == NULL) {
    outputBitmap.setImmutable();
}

if (javaBitmap != NULL) {
    bool isPremultiplied = !requireUnpremultiplied;
    GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
    outputBitmap.notifyPixelsChanged();
    return javaBitmap;
}

int bitmapCreateFlags = 0x0;
if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;

// now create the java bitmap
return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
                bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);

SkImageInfo是一個(gè)結(jié)構(gòu)體,包含寬高、顏色類型、透明類型等信息,Make方法,創(chuàng)建該結(jié)構(gòu)體,SKBitmap的setInfo方法設(shè)置內(nèi)部SkImageInfo對(duì)象。
如果不縮放,將前面解析的decodingBitmap交給輸出outputBitmap。
如果重用javaBitmap不空,利用輸出的outputBitmap重新初始化該對(duì)象,通知像素改變,將該Java層Bitmap對(duì)象返回。
如果不重用,GraphicsJNI的createBitmap方法,創(chuàng)建Java層的Bitmap實(shí)例。

jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap,
        int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
        int density) {
    ...
    jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
            reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(),
            bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
            ninePatchChunk, ninePatchInsets);
    return obj;
}

env的NewObject方法,創(chuàng)建Bitmap實(shí)例,入?yún)⑹莕ative層bitmap指針,Java層字節(jié)數(shù)組的強(qiáng)引用,bitmap寬/高,density,isMutable,isPremultiplied,ninePatchChunk,ninePatchInsets。
Java層字節(jié)數(shù)組強(qiáng)引用是mPixelStorage中結(jié)構(gòu)體java的jbyteArray變量。在使用JavaPixelAllocator分配器分配內(nèi)存時(shí),會(huì)介紹如何對(duì)結(jié)構(gòu)體賦值。

// called from JNI
Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density, boolean isMutable, 
                boolean requestPremultiplied, byte[] ninePatchChunk, 
                NinePatch.InsetStruct ninePatchInsets)

最終,Java層Bitmap實(shí)例創(chuàng)建成功,構(gòu)造方法是由底層JNI方法調(diào)用。


了解SKImageDecode解碼

我們前面說(shuō)過(guò),通過(guò)解碼器SkImageDecoder的decode方法,解析stream,得到SkBitmap對(duì)象。

SkImageDecoder::Result SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
                    SkColorType pref,Mode mode) {
    fShouldCancelDecode = false;
    fDefaultPref = pref;
    SkBitmap tmp;
    const Result result = this->onDecode(stream, &tmp, mode);
    if (kFailure != result) {
        bm->swap(tmp);
    }
    return result;
}

調(diào)用的onDecode方法在它的子類里實(shí)現(xiàn),子類包括各種圖片格式png,jpeg及gif的解碼類。比如,SkPNGImageDecoder繼承SkImageDecoder,看一下它的onDecode方法。

SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, 
                    SkBitmap* decodedBitmap,Mode mode) {
    //初始化
    if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
        return kFailure;
    }
    ....
    //該模式直接返回成功
    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
        return kSuccess;
    }
    ....
    //內(nèi)存分配
    //最終觸發(fā)Allocator的allocPixelRef方法,入?yún)ecodedBitmap
    if (!this->allocPixelRef(decodedBitmap,
                kIndex_8_SkColorType == colorType ? colorTable : NULL)) {
        return kFailure;
    }
    png_read_update_info(png_ptr, info_ptr);
    ....
    //像素讀取
    ....
    png_read_end(png_ptr, info_ptr);
    ....
    return kSuccess;
}

調(diào)用它自己的allocPixelRef方法。

bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
                    SkColorTable* ctable) const {
    return bitmap->tryAllocPixels(fAllocator, ctable);
}

該方法調(diào)用入?yún)kBitmap的tryAllocPixels方法,將SkImageDecoder內(nèi)部的分配器傳入。在SkImageDecoder創(chuàng)建后會(huì)setAllocator初始化Allocator,前面代碼都有寫(xiě)。

bool SkBitmap::tryAllocPixels(Allocator* allocator, SkColorTable* ctable) {
    HeapAllocator stdalloc;
    if (NULL == allocator) {
        allocator = &stdalloc;
    }
    return allocator->allocPixelRef(this, ctable);
}

最終,在SkBitmap類中,調(diào)用Allocator的allocPixelRef方法,將SKBitmap作為參數(shù)傳入。以JavaPixelAllocator分配器為例,在Java堆上分配內(nèi)存。

class JavaPixelAllocator : public SkBitmap::Allocator {
public:
    JavaPixelAllocator(JNIEnv* env);
    ~JavaPixelAllocator();

    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;

    android::Bitmap* getStorageObjAndReset() {
        android::Bitmap* result = mStorage;
        mStorage = NULL;
        return result;
    };

private:
    JavaVM* mJavaVM;
    android::Bitmap* mStorage = nullptr;
};

JavaPixelAllocator的allocPixelRef方法。

bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
    JNIEnv* env = vm2env(mJavaVM);
    mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
    return mStorage != nullptr;
}

GraphicsJNI的allocateJavaPixelRef方法。創(chuàng)建底層Bitmap對(duì)象,指針賦值mStorage,在JavaPixelAllocator中保存。

android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, 
                    SkBitmap* bitmap,SkColorTable* ctable) {
    const SkImageInfo& info = bitmap->info();

    size_t size;
    if (!computeAllocationSize(*bitmap, &size)) {
        return NULL;
    }
    const size_t rowBytes = bitmap->rowBytes();
    jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(
                gVMRuntime, gVMRuntime_newNonMovableArray,
                gByte_class, size);//Java堆分配內(nèi)存
    jbyte* addr = (jbyte*) env->CallLongMethod(gVMRuntime, 
                    gVMRuntime_addressOf, arrayObj);//地址
    android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,
            info, rowBytes, ctable);//創(chuàng)建native層Bitmap
    wrapper->getSkBitmap(bitmap);//用native層Bitmap初始化SkBitmap
    bitmap->lockPixels();

    return wrapper;
}

GraphicsJNI的computeAllocationSize方法,根據(jù)SkBitmap的height和width計(jì)算分配空間大小,存儲(chǔ)在size。
VMRuntime的newNonMovableArray方法,在Java堆分配內(nèi)存,返回Java層字節(jié)數(shù)組arrayObj。
VMRuntime的addressOf方法,返回jbyteArray字節(jié)數(shù)組arrayObj的地址。
VMRuntime源碼/libcore/libart/src/main/java/dalvik/system/VMRuntime.java目錄。
創(chuàng)建底層的Bitmap。

Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
        const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
        : mPixelStorageType(PixelStorageType::Java) {
    env->GetJavaVM(&mPixelStorage.java.jvm);
    mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj);
    mPixelStorage.java.jstrongRef = nullptr;//數(shù)組強(qiáng)引用暫時(shí)置空。
    mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
 
    mPixelRef->unref();
}

底層Bitmap的兩個(gè)變量mPixelStorage和mPixelRef。
mPixelStorage,聯(lián)合體,內(nèi)部的java結(jié)構(gòu)體內(nèi)容。jweakRef指向NewWeakGlobalRef方法創(chuàng)建的Java層字節(jié)數(shù)組(Java堆對(duì)象)的弱全局引用。mPixelRef,WrappedPixelRef,繼承SkPixelRef,封裝Bitmap本身,地址addr,像素大小rowBytes,SkImageInfo等。

struct {
    JavaVM* jvm;
    jweak jweakRef;
    jbyteArray jstrongRef;
} java;

底層Bitmap創(chuàng)建后,調(diào)用getSkBitmap方法,初始化SkBitmap輸出。

void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
    outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes());
    outBitmap->setPixelRef(refPixelRefLocked())->unref();//獲取mPixelRef引用
    outBitmap->setHasHardwareMipMap(hasHardwareMipMap());
}

底層Bitmap的refPixelRefLocked方法,獲取mPixelRef引用。SkBitmap內(nèi)部包含mPixelRef引用。

JavaPixelAllocator分配內(nèi)存.jpg

綜上所述

Android6.0的Bitmap源碼,位圖像素存儲(chǔ)在Java堆字節(jié)數(shù)組中,Java層的Bitmap對(duì)象引用底層Bitmap,字節(jié)數(shù)組同時(shí)被Java層Bitmap和底層Bitmap#mPixelStorage引用。


任重而道遠(yuǎn)

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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