diff options
| author | 2015-10-27 10:33:20 -0400 | |
|---|---|---|
| committer | 2015-11-19 15:39:34 +0000 | |
| commit | 1f979639c168ebdf77ad8d7771786fc321ce8234 (patch) | |
| tree | 98d9747e1cba1af52d9931d5d78bde4559bec958 | |
| parent | 4550388a3f718fb4f94972e8ad8c19601fa8d8e3 (diff) | |
Merge new implementation of BitmapRegionDecoder.
This is a combination of the following 4 commits.
=====================================================================
Make SkBitmapRegionDecoder use SkAndroidCodec
The current implementation of SkBitmapRegionDecoder relies
on SkImageDecoder::decodeSubset() which itself relies on
forked copies of libjpeg and libpng.
This implementation has caused numerous correctness and memory
bugs, and also prevented us from updating to the latest
optimized versions of libjpeg-turbo and libpng.
https://docs.google.com/a/google.com/document/d/1w0vdC9sPPquwgJLY4wjBvzwm8QZIqIgg1q3tDEvOoUU/edit?usp=sharing
The SkAndroidCodec implementation fixes known correctness and
memory bugs, at least matches the performance of the
the old implementation (and in many cases improves upon it),
and uses standard copies of libjpeg-turbo and libpng.
In addition to improving region decodes, switching to new
copies of libjpeg-turbo and libpng will improve performance
of full image decodes significantly. Jpeg, in particular,
will be about 2x faster.
Change-Id: Ia51645009b243607d3022d49e8e0c82ec4e959bc
=====================================================================
Make JavaPixelAllocator and RCPAllocator implement SkBRDAllocator
This will allow us to optimize decodes when destination memory
is zero initialized.
Change-Id: I1e56cd5410d1e9b6544b0e47aac8da740bca5252
=====================================================================
Fix build by using SkBRDAllocator
Change-Id: Icf031409f0e58496d80b9bdc91def8ff97f7d0d2
=====================================================================
Fix bug in RecyclingPixelAllocator::copyIfNecessary()
This was exposed by a new test that I hope to add to
the BitmapRegionDecoderTests.
Change-Id: Ic5a32e095ff3ce457abab7216a8da1acf17db27b
=====================================================================
Depends on adding libjpeg-turbo to the manifest.
Change-Id: Ic8ffa339722bfa9f40f44f68d03ce9a3faef1ee2
Depends on update to Skia.
Change-Id: I4bdaacb8a04e2dee5e3eccc58033601384c77b7d
BUG:25424175
BUG:25344771
http://skbug.com/1243
http://skbug.com/4475
https://code.google.com/p/android/issues/detail?id=77195
http://skbug.com/4417
https://code.google.com/p/android/issues/detail?id=162760
http://skbug.com/4264
https://code.google.com/p/android/issues/detail?id=76976
http://skbug.com/4418
http://skbug.com/1282
https://code.google.com/p/android/issues/detail?id=189248
https://code.google.com/p/android/issues/detail?id=80316
https://buganizer.corp.google.com/u/0/issues/20224409
http://skbug.com/4319
http://skbug.com/4361
https://code.google.com/p/android/issues/detail?id=165546
https://code.google.com/p/android/issues/detail?id=81068
https://buganizer.corp.google.com/u/0/issues/22527238
https://buganizer.corp.google.com/u/0/issues/23731509
https://code.google.com/p/skia/issues/detail?id=4469
http://skbug.com/4360
http://skbug.com/4489
http://skbug.com/4490
| -rw-r--r-- | core/jni/Android.mk | 3 | ||||
| -rw-r--r-- | core/jni/android/graphics/BitmapRegionDecoder.cpp | 217 | ||||
| -rw-r--r-- | core/jni/android/graphics/Graphics.cpp | 87 | ||||
| -rw-r--r-- | core/jni/android/graphics/GraphicsJNI.h | 75 |
4 files changed, 268 insertions, 114 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 4d648cea4675..476d13c5f024 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -198,7 +198,6 @@ LOCAL_C_INCLUDES += \ external/sqlite/android \ external/expat/lib \ external/tremor/Tremor \ - external/jpeg \ external/harfbuzz_ng/src \ libcore/include \ $(call include-path-for, audio-utils) \ @@ -237,7 +236,7 @@ LOCAL_SHARED_LIBRARIES := \ libicuuc \ libicui18n \ libmedia \ - libjpeg \ + libjpeg-turbo \ libusbhost \ libharfbuzz_ng \ libz \ diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index 3cdf640ae941..f10f4bda3226 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -16,17 +16,20 @@ #define LOG_TAG "BitmapRegionDecoder" -#include "AutoDecodeCancel.h" #include "BitmapFactory.h" #include "CreateJavaOutputStreamAdaptor.h" +#include "GraphicsJNI.h" +#include "Utils.h" + #include "SkBitmap.h" +#include "SkBitmapRegionDecoder.h" +#include "SkCodec.h" #include "SkData.h" -#include "GraphicsJNI.h" -#include "SkImageEncoder.h" +#include "SkEncodedFormat.h" #include "SkUtils.h" #include "SkPixelRef.h" #include "SkStream.h" -#include "Utils.h" + #include "android_nio_utils.h" #include "android_util_Binder.h" #include "core_jni_helpers.h" @@ -39,60 +42,54 @@ using namespace android; -class BitmapRegionDecoder { -public: - BitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) { - fDecoder = decoder; - fWidth = width; - fHeight = height; - } - ~BitmapRegionDecoder() { - delete fDecoder; +// This is very similar to, and based on, getMimeTypeString() in BitmapFactory. +jstring encodedFormatToString(JNIEnv* env, SkEncodedFormat format) { + const char* mimeType; + switch (format) { + case SkEncodedFormat::kBMP_SkEncodedFormat: + mimeType = "image/bmp"; + break; + case SkEncodedFormat::kGIF_SkEncodedFormat: + mimeType = "image/gif"; + break; + case SkEncodedFormat::kICO_SkEncodedFormat: + mimeType = "image/x-ico"; + break; + case SkEncodedFormat::kJPEG_SkEncodedFormat: + mimeType = "image/jpeg"; + break; + case SkEncodedFormat::kPNG_SkEncodedFormat: + mimeType = "image/png"; + break; + case SkEncodedFormat::kWEBP_SkEncodedFormat: + mimeType = "image/webp"; + break; + case SkEncodedFormat::kWBMP_SkEncodedFormat: + mimeType = "image/vnd.wap.wbmp"; + break; + default: + mimeType = nullptr; + break; } - bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect, - SkColorType pref, int sampleSize) { - fDecoder->setSampleSize(sampleSize); - return fDecoder->decodeSubset(bitmap, rect, pref); + jstring jstr = nullptr; + if (mimeType != nullptr) { + jstr = env->NewStringUTF(mimeType); } - - SkImageDecoder* getDecoder() const { return fDecoder; } - int getWidth() const { return fWidth; } - int getHeight() const { return fHeight; } - -private: - SkImageDecoder* fDecoder; - int fWidth; - int fHeight; -}; + return jstr; +} // Takes ownership of the SkStreamRewindable. For consistency, deletes stream even // when returning null. static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) { - SkImageDecoder* decoder = SkImageDecoder::Factory(stream); - int width, height; - if (NULL == decoder) { - delete stream; + SkAutoTDelete<SkBitmapRegionDecoder> brd( + SkBitmapRegionDecoder::Create(stream, SkBitmapRegionDecoder::kAndroidCodec_Strategy)); + if (NULL == brd) { doThrowIOE(env, "Image format not supported"); - return nullObjectReturn("SkImageDecoder::Factory returned null"); - } - - JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env); - decoder->setAllocator(javaAllocator); - javaAllocator->unref(); - - // This call passes ownership of stream to the decoder, or deletes on failure. - if (!decoder->buildTileIndex(stream, &width, &height)) { - char msg[100]; - snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder", - decoder->getFormatName()); - doThrowIOE(env, msg); - delete decoder; - return nullObjectReturn("decoder->buildTileIndex returned false"); + return nullObjectReturn("CreateBitmapRegionDecoder returned null"); } - BitmapRegionDecoder *bm = new BitmapRegionDecoder(decoder, width, height); - return GraphicsJNI::createBitmapRegionDecoder(env, bm); + return GraphicsJNI::createBitmapRegionDecoder(env, brd.detach()); } static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, @@ -160,102 +157,106 @@ static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, /* * nine patch not supported - * * purgeable not supported * reportSizeToVM not supported */ -static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, - jint start_x, jint start_y, jint width, jint height, jobject options) { - BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); - jobject tileBitmap = NULL; - SkImageDecoder *decoder = brd->getDecoder(); +static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, + jint inputY, jint inputWidth, jint inputHeight, jobject options) { + + // Set default options. int sampleSize = 1; - SkColorType prefColorType = kUnknown_SkColorType; - bool doDither = true; - bool preferQualityOverSpeed = false; - bool requireUnpremultiplied = false; + SkColorType colorType = kN32_SkColorType; + bool requireUnpremul = false; + jobject javaBitmap = NULL; + // Update the default options with any options supplied by the client. if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); - // initialize these, in case we fail later on + jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); + colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); + if (kAlpha_8_SkColorType == colorType) { + colorType = kGray_8_SkColorType; + } + requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); + javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); + // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will + // ignore the values of these fields. + + // Initialize these fields to indicate a failure. If the decode succeeds, we + // will update them later on. env->SetIntField(options, gOptions_widthFieldID, -1); env->SetIntField(options, gOptions_heightFieldID, -1); env->SetObjectField(options, gOptions_mimeFieldID, 0); - - jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); - prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); - doDither = env->GetBooleanField(options, gOptions_ditherFieldID); - preferQualityOverSpeed = env->GetBooleanField(options, - gOptions_preferQualityOverSpeedFieldID); - // Get the bitmap for re-use if it exists. - tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); - requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); } - decoder->setDitherImage(doDither); - decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); - decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); - AutoDecoderCancel adc(options, decoder); - - // To fix the race condition in case "requestCancelDecode" - // happens earlier than AutoDecoderCancel object is added - // to the gAutoDecoderCancelMutex linked list. - if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { - return nullObjectReturn("gOptions_mCancelID");; + // Recycle a bitmap if possible. + android::Bitmap* recycledBitmap = nullptr; + size_t recycledBytes = 0; + if (javaBitmap) { + recycledBitmap = GraphicsJNI::getBitmap(env, javaBitmap); + if (recycledBitmap->peekAtPixelRef()->isImmutable()) { + ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); + } + recycledBytes = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); } - SkIRect region; - region.fLeft = start_x; - region.fTop = start_y; - region.fRight = start_x + width; - region.fBottom = start_y + height; - SkBitmap bitmap; - - if (tileBitmap != NULL) { - // Re-use bitmap. - GraphicsJNI::getSkBitmap(env, tileBitmap, &bitmap); + // Set up the pixel allocator + SkBRDAllocator* allocator = nullptr; + RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); + JavaPixelAllocator javaAlloc(env); + if (javaBitmap) { + allocator = &recycleAlloc; + // We are required to match the color type of the recycled bitmap. + colorType = recycledBitmap->info().colorType(); + } else { + allocator = &javaAlloc; } - if (!brd->decodeRegion(&bitmap, region, prefColorType, sampleSize)) { - return nullObjectReturn("decoder->decodeRegion returned false"); + // Decode the region. + SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + SkBitmap bitmap; + if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, colorType, requireUnpremul)) { + return nullObjectReturn("Failed to decode region."); } - // update options (if any) + // If the client provided options, indicate that the decode was successful. if (NULL != options) { env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); - // TODO: set the mimeType field with the data from the codec. - // but how to reuse a set of strings, rather than allocating new one - // each time? env->SetObjectField(options, gOptions_mimeFieldID, - getMimeTypeString(env, decoder->getFormat())); + encodedFormatToString(env, brd->getEncodedFormat())); } - if (tileBitmap != NULL) { - bitmap.notifyPixelsChanged(); - return tileBitmap; + // If we may have reused a bitmap, we need to indicate that the pixels have changed. + if (javaBitmap) { + recycleAlloc.copyIfNecessary(); + return javaBitmap; } - JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); - int bitmapCreateFlags = 0; - if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; - return GraphicsJNI::createBitmap(env, allocator->getStorageObjAndReset(), - bitmapCreateFlags); + if (!requireUnpremul) { + bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; + } + return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags); } static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { - BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); - return static_cast<jint>(brd->getHeight()); + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + return static_cast<jint>(brd->height()); } static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { - BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); - return static_cast<jint>(brd->getWidth()); + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + return static_cast<jint>(brd->width()); } static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { - BitmapRegionDecoder *brd = reinterpret_cast<BitmapRegionDecoder*>(brdHandle); + SkBitmapRegionDecoder* brd = + reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); delete brd; } diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index ed4401978f7b..3d5091a78687 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -439,7 +439,7 @@ int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap) return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID); } -jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap) +jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) { SkASSERT(bitmap != NULL); @@ -677,6 +677,91 @@ bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { //////////////////////////////////////////////////////////////////////////////// +RecyclingClippingPixelAllocator::RecyclingClippingPixelAllocator( + android::Bitmap* recycledBitmap, size_t recycledBytes) + : mRecycledBitmap(recycledBitmap) + , mRecycledBytes(recycledBytes) + , mSkiaBitmap(nullptr) + , mNeedsCopy(false) +{} + +RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {} + +bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { + // Ensure that the caller did not pass in a NULL bitmap to the constructor or this + // function. + LOG_ALWAYS_FATAL_IF(!mRecycledBitmap); + LOG_ALWAYS_FATAL_IF(!bitmap); + mSkiaBitmap = bitmap; + + // This behaves differently than the RecyclingPixelAllocator. For backwards + // compatibility, the original color type of the recycled bitmap must be maintained. + if (mRecycledBitmap->info().colorType() != bitmap->colorType()) { + return false; + } + + // The Skia bitmap specifies the width and height needed by the decoder. + // mRecycledBitmap specifies the width and height of the bitmap that we + // want to reuse. Neither can be changed. We will try to find a way + // to reuse the memory. + const int maxWidth = SkTMax(bitmap->width(), mRecycledBitmap->info().width()); + const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height()); + const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight); + const size_t rowBytes = maxInfo.minRowBytes(); + const size_t bytesNeeded = maxInfo.getSafeSize(rowBytes); + if (bytesNeeded <= mRecycledBytes) { + // Here we take advantage of reconfigure() to reset the rowBytes and ctable + // of mRecycledBitmap. It is very important that we pass in + // mRecycledBitmap->info() for the SkImageInfo. According to the + // specification for BitmapRegionDecoder, we are not allowed to change + // the SkImageInfo. + mRecycledBitmap->reconfigure(mRecycledBitmap->info(), rowBytes, ctable); + + // This call will give the bitmap the same pixelRef as mRecycledBitmap. + bitmap->setPixelRef(mRecycledBitmap->refPixelRef())->unref(); + + // Make sure that the recycled bitmap has the correct alpha type. + mRecycledBitmap->setAlphaType(bitmap->alphaType()); + + bitmap->lockPixels(); + mNeedsCopy = false; + + // TODO: If the dimensions of the SkBitmap are smaller than those of + // mRecycledBitmap, should we zero the memory in mRecycledBitmap? + return true; + } + + // In the event that mRecycledBitmap is not large enough, allocate new memory + // on the heap. + SkBitmap::HeapAllocator heapAllocator; + + // We will need to copy from heap memory to mRecycledBitmap's memory after the + // decode is complete. + mNeedsCopy = true; + + return heapAllocator.allocPixelRef(bitmap, ctable); +} + +void RecyclingClippingPixelAllocator::copyIfNecessary() { + if (mNeedsCopy) { + SkPixelRef* recycledPixels = mRecycledBitmap->refPixelRef(); + void* dst = recycledPixels->pixels(); + size_t dstRowBytes = mRecycledBitmap->rowBytes(); + size_t bytesToCopy = SkTMin(mRecycledBitmap->info().minRowBytes(), + mSkiaBitmap->info().minRowBytes()); + for (int y = 0; y < mRecycledBitmap->info().height(); y++) { + memcpy(dst, mSkiaBitmap->getAddr(0, y), bytesToCopy); + dst = SkTAddOffset<void>(dst, dstRowBytes); + } + recycledPixels->notifyPixelsChanged(); + recycledPixels->unref(); + } + mRecycledBitmap = nullptr; + mSkiaBitmap = nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// + AshmemPixelAllocator::AshmemPixelAllocator(JNIEnv *env) { LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK, "env->GetJavaVM failed"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 90f8291fe846..e99a3ffa2a47 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -3,6 +3,8 @@ #include "Bitmap.h" #include "SkBitmap.h" +#include "SkBRDAllocator.h" +#include "SkCodec.h" #include "SkDevice.h" #include "SkPixelRef.h" #include "SkMallocPixelRef.h" @@ -12,7 +14,7 @@ #include <Canvas.h> #include <jni.h> -class BitmapRegionDecoder; +class SkBitmapRegionDecoder; class SkCanvas; namespace android { @@ -90,7 +92,7 @@ public: static jobject createRegion(JNIEnv* env, SkRegion* region); - static jobject createBitmapRegionDecoder(JNIEnv* env, BitmapRegionDecoder* bitmap); + static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable); @@ -123,7 +125,7 @@ public: * ensure that the allocated buffer is properly accounted for with a * reference in the heap (or a JNI global reference). */ -class JavaPixelAllocator : public SkBitmap::Allocator { +class JavaPixelAllocator : public SkBRDAllocator { public: JavaPixelAllocator(JNIEnv* env); ~JavaPixelAllocator(); @@ -139,11 +141,78 @@ public: return result; }; + /** + * Indicates that this allocator allocates zero initialized + * memory. + */ + SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; } + private: JavaVM* mJavaVM; android::Bitmap* mStorage = nullptr; }; +/** + * Allocator to handle reusing bitmaps for BitmapRegionDecoder. + * + * The BitmapRegionDecoder documentation states that, if it is + * provided, the recycled bitmap will always be reused, clipping + * the decoded output to fit in the recycled bitmap if necessary. + * This allocator implements that behavior. + * + * Skia's SkBitmapRegionDecoder expects the memory that + * is allocated to be large enough to decode the entire region + * that is requested. It will decode directly into the memory + * that is provided. + * + * FIXME: BUG:25465958 + * If the recycled bitmap is not large enough for the decode + * requested, meaning that a clip is required, we will allocate + * enough memory for Skia to perform the decode, and then copy + * from the decoded output into the recycled bitmap. + * + * If the recycled bitmap is large enough for the decode requested, + * we will provide that memory for Skia to decode directly into. + * + * This allocator should only be used for a single allocation. + * After we reuse the recycledBitmap once, it is dangerous to + * reuse it again, given that it still may be in use from our + * first allocation. + */ +class RecyclingClippingPixelAllocator : public SkBRDAllocator { +public: + + RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, + size_t recycledBytes); + + ~RecyclingClippingPixelAllocator(); + + virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override; + + /** + * Must be called! + * + * In the event that the recycled bitmap is not large enough for + * the allocation requested, we will allocate memory on the heap + * instead. As a final step, once we are done using this memory, + * we will copy the contents of the heap memory into the recycled + * bitmap's memory, clipping as necessary. + */ + void copyIfNecessary(); + + /** + * Indicates that this allocator does not allocate zero initialized + * memory. + */ + SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kNo_ZeroInitialized; } + +private: + android::Bitmap* mRecycledBitmap; + const size_t mRecycledBytes; + SkBitmap* mSkiaBitmap; + bool mNeedsCopy; +}; + class AshmemPixelAllocator : public SkBitmap::Allocator { public: AshmemPixelAllocator(JNIEnv* env); |