What is the size of the bitmap?

On devices of API level 19+, we have getByteCount() and getAllocationByteCount() , each of which returns the size of the Bitmap in bytes. The latter takes into account the fact that a Bitmap can actually represent a smaller image than its number of bytes (for example, Bitmap initially contained a larger image, but was then used with BitmapFactory.Options and inBitmap to store a smaller image).

In most IPC scenarios for Android, especially those related to Parcelable , we have a 1MB "transaction transaction limit".

In order to determine if this Bitmap is small enough for IPC, do we getByteCount() or getAllocationByteCount() ?

My intuitive instinct says that we use getByteCount() , since this should be the number of bytes that the current image in Bitmap occupies, but I was hoping someone had a more authoritative answer.

+6
source share
1 answer

The size of the image data recorded on the plot is getByteCount() plus the size of the Bitmap color table, if any. About 48 bytes of Bitmap attributes are also written to the parcel. The following code analysis and tests provide the basis for these statements.

The native function for writing Bitmap to Parcel starts at line 620 of this file . The function is included here with an explanation added:

 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jboolean isMutable, jint density, jobject parcel) { const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); if (parcel == NULL) { SkDebugf("------- writeToParcel null parcel\n"); return JNI_FALSE; } android::Parcel* p = android::parcelForJavaObject(env, parcel); 

The next seven int are the first data written to the package. In Test No. 2, described below, these values โ€‹โ€‹are read from the sample to confirm the size of the data recorded for Bitmap .

 p->writeInt32(isMutable); p->writeInt32(bitmap->colorType()); p->writeInt32(bitmap->alphaType()); p->writeInt32(bitmap->width()); p->writeInt32(bitmap->height()); p->writeInt32(bitmap->rowBytes()); p->writeInt32(density); 

If the bitmap has a color map, it is written to the package. The exact definition of the size of the sending bitmap should also take this into account.

 if (bitmap->colorType() == kIndex_8_SkColorType) { SkColorTable* ctable = bitmap->getColorTable(); if (ctable != NULL) { int count = ctable->count(); p->writeInt32(count); memcpy(p->writeInplace(count * sizeof(SkPMColor)), ctable->lockColors(), count * sizeof(SkPMColor)); ctable->unlockColors(); } else { p->writeInt32(0); // indicate no ctable } } 

Now we get to the bottom of the question: how much data is written for the bitmap? The amount is determined by this call to bitmap->getSize() . This feature is analyzed below. Note that the value is saved in size , which is used in the code that follows to write the blob for image data, and copies the data to the memory pointed to by blob.

 size_t size = bitmap->getSize(); 

A variable-sized data block is written to the parcel using blob. If the block is less than 40K, it is written "in place" in the package. Blocks larger than 40 K are written to shared memory using ashmem , and the attributes of the ashmem area ashmem written in the ashmem . The blob itself is just a small descriptor containing several elements that contain a pointer to a block, its length and a flag indicating whether the block is in place or in shared memory. The class definition for WriteableBlob is located on line 262 of this file . The definition of writeBlob( ) is on line 747 of this file . writeBlob() determines whether the data block is small enough to be written in place. If so, it expands the send buffer to free up space. If not, the ashmem area is created and configured. In both cases, the blob members (pointer, size, flag) are defined for later use when copying a block. Note that for both cases, size determines the size of the data that will be copied, both in place and in shared memory. When writeBlob() , the target data buffer is defined in blob , and the values โ€‹โ€‹are written to a batch that describes how the block of image data is stored (in place or in shared memory), and for shared memory - attributes ashmem region.

 android::Parcel::WritableBlob blob; android::status_t status = p->writeBlob(size, &blob); if (status) { doThrowRE(env, "Could not write bitmap to parcel blob."); return JNI_FALSE; } 

With the target buffer for the now configured block, data can be copied using the pointer in the blob. Note that size determines the amount of data to be copied. Also note that there is only one size . The same value is used for on-site purposes and for shared memory.

 bitmap->lockPixels(); const void* pSrc = bitmap->getPixels(); if (pSrc == NULL) { memset(blob.data(), 0, size); } else { memcpy(blob.data(), pSrc, size); } bitmap->unlockPixels(); blob.release(); return JNI_TRUE; } 

This completes the analysis of Bitmap_writeToParcel . It is now clear that although small (<40K) images are recorded in place and large images are recorded in shared memory, the size of the recorded data is the same for both cases. The easiest and most direct way to see what size it is is to create a test case using an image <40K so that it is written in place. Then the size of the received package shows the size of the image data.

The second method for determining that size requires an understanding of SkBitmap::getSize() . This is the function used in the code analyzed above to get the image block size.

SkBitmap::getSize() defines this file on line 130. It:

 size_t getSize() const { return fHeight * fRowBytes; } 

Two other functions in the same file related to this explanation are height() , defined on line 98:

 int height() const { return fHeight; } 

and rowBytes() defined on line 101:

 int rowBytes() const { return fRowBytes; } 

We saw these functions used in Bitmap_writeToParcel when bitmap attributes are written to the package:

 p->writeInt32(bitmap->height()); p->writeInt32(bitmap->rowBytes()); 

With this understanding of these functions, we now see that by unloading the first few ints in the package, we can see the values fHeight and fRowBytes , from which we can derive the value returned by getSize() .

The second code snippet below does this and provides additional confirmation that the size of the data written to Parcel matches the value returned by getByteCount() .

Test # 1

This test creates a bitmap of less than 40 KB in size to create an in-place data warehouse. Then, the size of the sending data is evaluated to show that getByteCount() determines the size of the image data stored in the packet.

The first few instructions in the code below should create a Bitmap less than 40K.

logcat output confirms that the size of the data written to Parcel matches the value returned by getByteCount() .

 byteCount=38400 allocatedByteCount=38400 parcelDataSize=38428 byteCount=7680 allocatedByteCount=38400 parcelDataSize=7708 

Code that showed the result:

  // Setup to get a mutable bitmap less than 40 Kbytes String path = "someSmallImage.jpg"; Bitmap bm0 = BitmapFactory.decodeFile(path); // Need it mutable to change height Bitmap bm1 = bm0.copy(bm0.getConfig(), true); // Chop it to get a size less than 40K bm1.setHeight(bm1.getHeight() / 32); // Now we have a BitMap with size < 40K for the test Bitmap bm2 = bm1.copy(bm0.getConfig(), true); // What the parcel size? Parcel p1 = Parcel.obtain(); bm2.writeToParcel(p1, 0); // Expect byteCount and allocatedByteCount to be the same Log.i("Demo", String.format("byteCount=%d allocatedByteCount=%d parcelDataSize=%d", bm2.getByteCount(), bm2.getAllocationByteCount(), p1.dataSize())); // Resize to make byteCount and allocatedByteCount different bm2.setHeight(bm2.getHeight() / 4); // What the parcel size? Parcel p2 = Parcel.obtain(); bm2.writeToParcel(p2, 0); // Show that byteCount determines size of data written to parcel Log.i("Demo", String.format("byteCount=%d allocatedByteCount=%d parcelDataSize=%d", bm2.getByteCount(), bm2.getAllocationByteCount(), p2.dataSize())); p1.recycle(); p2.recycle(); 

Test number 2

This test stores the bitmap on a parcel, then flushes the first few ints to get values โ€‹โ€‹from which you can determine the size of the image data.

logcat output with added comments:

// Raster Attributes

 bc=12000000 abc=12000000 hgt=1500 wid=2000 rbyt=8000 dens=213 

// Attributes after changing the height. byteCount changed, allocated ByteCount not.

 bc=744000 abc=12000000 hgt=93 wid=2000 rbyt=8000 dens=213 

// Dump the data of the package. The parcel data size is 48. The image is too large for the place.

 pds=48 mut=1 ctyp=4 atyp=1 hgt=93 wid=2000 rbyt=8000 dens=213 

// Show the value of getSize () obtained from the data of the package. It is equal to getByteCount ().

 bitmap->getSize()= 744000 getByteCount()=744000 

The code that generated this output is:

 String path = "someImage.jpg"; Bitmap bm0 = BitmapFactory.decodeFile(path); // Need it mutable to change height Bitmap bm = bm0.copy(bm0.getConfig(), true); // For reference, and to provide confidence that the parcel data dump is // correct, log the bitmap attributes. Log.i("Demo", String.format("bc=%d abc=%d hgt=%d wid=%d rbyt=%d dens=%d", bm.getByteCount(), bm.getAllocationByteCount(), bm.getHeight(), bm.getWidth(), bm.getRowBytes(), bm.getDensity())); // Change size bm.setHeight(bm.getHeight() / 16); Log.i("Demo", String.format("bc=%d abc=%d hgt=%d wid=%d rbyt=%d dens=%d", bm.getByteCount(), bm.getAllocationByteCount(), bm.getHeight(), bm.getWidth(), bm.getRowBytes(), bm.getDensity())); // Get a parcel and write the bitmap to it. Parcel p = Parcel.obtain(); bm.writeToParcel(p, 0); // When the image is too large to be written in-place, // the parcel data size will be ~48 bytes (when there is no color map). int parcelSize = p.dataSize(); // What are the first few ints in the parcel? p.setDataPosition(0); int mutable = p.readInt(); //1 int colorType = p.readInt(); //2 int alphaType = p.readInt(); //3 int width = p.readInt(); //4 int height = p.readInt(); //5 bitmap->height() int rowBytes = p.readInt(); //6 bitmap->rowBytes() int density = p.readInt(); //7 Log.i("Demo", String.format("pds=%d mut=%d ctyp=%d atyp=%d hgt=%d wid=%d rbyt=%d dens=%d", parcelSize, mutable, colorType, alphaType, height, width, rowBytes, density)); // From code analysis, we know that the value returned // by SkBitmap::getSize() is the size of the image data written. // We also know that the value of getSize() is height()*rowBytes(). // These are the values in ints 5 and 6. int imageSize = height * rowBytes; // Show that the size of image data stored is Bitmap.getByteCount() Log.i("Demo", String.format("bitmap->getSize()= %d getByteCount()=%d", imageSize, bm.getByteCount())); p.recycle(); 
+5
source

All Articles