bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { SkASSERT(&gImageRefMutex == this->mutex()); if (fErrorInDecoding) { return false; } /* As soon as we really know our config, we record it, so that on subsequent calls to the codec, we are sure we will always get the same result. */ if (SkBitmap::kNo_Config != fBitmap.config()) { fConfig = fBitmap.config(); } if (NULL != fBitmap.getPixels() || (SkBitmap::kNo_Config != fBitmap.config() && SkImageDecoder::kDecodeBounds_Mode == mode)) { return true; } SkASSERT(fBitmap.getPixels() == NULL); fStream->rewind(); SkImageDecoder* codec; if (fFactory) { codec = fFactory->newDecoder(fStream); } else { codec = SkImageDecoder::Factory(fStream); } if (codec) { SkAutoTDelete<SkImageDecoder> ad(codec); codec->setSampleSize(fSampleSize); codec->setDitherImage(fDoDither); if (this->onDecode(codec, fStream, &fBitmap, fConfig, mode)) { return true; } } #ifdef DUMP_IMAGEREF_LIFECYCLE if (NULL == codec) { SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI()); } else { SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n", this->getURI(), mode); } #endif fErrorInDecoding = true; fBitmap.reset(); return false; }
bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { if (fErrorInDecoding) { return false; } if (NULL != fBitmap.getPixels() || (SkBitmap::kNo_Config != fBitmap.config() && SkImageDecoder::kDecodeBounds_Mode == mode)) { return true; } SkASSERT(fBitmap.getPixels() == NULL); if (!fStream->rewind()) { SkDEBUGF(("Failed to rewind SkImageRef stream!")); return false; } SkImageDecoder* codec; if (fFactory) { codec = fFactory->newDecoder(fStream); } else { codec = SkImageDecoder::Factory(fStream); } if (codec) { SkAutoTDelete<SkImageDecoder> ad(codec); codec->setSampleSize(fSampleSize); codec->setDitherImage(fDoDither); codec->setRequireUnpremultipliedColors(this->info().fAlphaType == kUnpremul_SkAlphaType); if (this->onDecode(codec, fStream, &fBitmap, fBitmap.config(), mode)) { if (kOpaque_SkAlphaType == fBitmap.alphaType()) { this->changeAlphaType(kOpaque_SkAlphaType); } SkASSERT(this->info() == fBitmap.info()); return true; } } #ifdef DUMP_IMAGEREF_LIFECYCLE if (NULL == codec) { SkDebugf("--- ImageRef: <%s> failed to find codec\n", this->getURI()); } else { SkDebugf("--- ImageRef: <%s> failed in codec for %d mode\n", this->getURI(), mode); } #endif fErrorInDecoding = true; fBitmap.reset(); return false; }
bool MpoDecoder::decodeBounds() { long bufferSize = mMP_Images[0]->getImageSize() + 2 + 2; //SOI + JPEG + EOI mMpoFileStream->rewind(); if (false == mMpoFileStream->skip(mMP_Images[0]->getOffsetInFile()) ) { XLOGE("failed to jump to image data"); return false; } char* jpegBuffer = (char*)malloc((unsigned int)bufferSize); if (NULL == jpegBuffer) { XLOGE("can not allocate memory to hold JPEG data"); return false; } if (bufferSize != mMpoFileStream->read(jpegBuffer, bufferSize)) { free(jpegBuffer); XLOGE("read jpeg data failed"); return false; } SkMemoryStream memStream(jpegBuffer, bufferSize); int sampleSize = 1; int preferSize = 0; bool doDither = true; SkBitmap* bitmap = new SkBitmap; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodeBounds_Mode; SkImageDecoder* decoder = SkImageDecoder::Factory(&memStream); if (NULL == decoder) { XLOGE("SkImageDecoder-Factory() returned false"); free(jpegBuffer); return false; } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); //decoder->setPreferSize(preferSize); if (!decoder->decode(&memStream, bitmap, prefConfig, decodeMode)) { XLOGE("SkImageDecoder-decode() returned false"); free(jpegBuffer); return false; } //free memory buffer free(jpegBuffer); mWidth = bitmap->width(); mHeight = bitmap->height(); //delete created SkBitmap delete bitmap; return true; }
DecodeView() { SkFILEStream stream("/skimages/index.png"); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (codec) { for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { stream.rewind(); codec->setDitherImage(gRec[i].fDither); codec->decode(&stream, &fBitmap[i], gRec[i].fPrefConfig, SkImageDecoder::kDecodePixels_Mode); } } }
bool RenderSkinNinePatch::decodeAsset(AssetManager* am, const char* filename, NinePatch* ninepatch) { Asset* asset = am->open(filename, android::Asset::ACCESS_BUFFER); if (!asset) { asset = am->openNonAsset(filename, android::Asset::ACCESS_BUFFER); if (!asset) { return false; } } SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; SkMemoryStream stream(asset->getBuffer(false), asset->getLength()); SkImageDecoder* decoder = SkImageDecoder::Factory(&stream); if (!decoder) { asset->close(); ALOGE("RenderSkinNinePatch::Failed to create an image decoder"); return false; } decoder->setSampleSize(1); decoder->setDitherImage(true); decoder->setPreferQualityOverSpeed(false); NinePatchPeeker peeker(decoder); SkAutoTDelete<SkImageDecoder> add(decoder); decoder->setPeeker(&peeker); if (!decoder->decode(&stream, &ninepatch->m_bitmap, prefConfig, mode, true)) { asset->close(); ALOGE("RenderSkinNinePatch::Failed to decode nine patch asset"); return false; } asset->close(); if (!peeker.fPatch) { ALOGE("RenderSkinNinePatch::Patch data not valid"); return false; } void** data = &ninepatch->m_serializedPatchData; *data = malloc(peeker.fPatch->serializedSize()); peeker.fPatch->serialize(*data); return true; }
BitmapGlue* BitmapFactoryGlue::doDecode(SkStream* stream, Options& options, bool allowPurgeable, bool forcePurgeable) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; bool doDither = true; bool isMutable = false; bool isPurgeable = forcePurgeable || (allowPurgeable && options.isPurgeable); BitmapGlue* javaBitmap = NULL; sampleSize = options.sampleSize; if (options.justDecodeBounds) mode = SkImageDecoder::kDecodeBounds_Mode; // initialize these, in case we fail later on options.width = -1; options.height = -1; prefConfig = options.config; isMutable = options.isMutable; doDither = options.doDither; javaBitmap = options.bitmap; SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (NULL == decoder) return NULL; decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); BitmapGlue* bitmap; if (javaBitmap == NULL) { bitmap = new BitmapGlue; } else { if (sampleSize != 1) { return NULL; } bitmap = javaBitmap; // config of supplied bitmap overrules config set in options prefConfig = bitmap->getConfig(); } SkAutoTDelete<SkImageDecoder> add(decoder); SkAutoTDelete<SkBitmap> adb(bitmap, (javaBitmap == NULL)); SkImageDecoder::Mode decodeMode = mode; if (isPurgeable) { decodeMode = SkImageDecoder::kDecodeBounds_Mode; } if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) return NULL; // update options (if any) options.width = bitmap->width(); options.height = bitmap->height(); // if we're in justBounds mode, return now (skip the java bitmap) if (SkImageDecoder::kDecodeBounds_Mode == mode) { return NULL; } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); SkPixelRef* pr; if (isPurgeable) { pr = installPixelRef(bitmap, stream, sampleSize, doDither); } else { // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. pr = bitmap->pixelRef(); } if (!isMutable) { // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); } if (javaBitmap != NULL) { // If a java bitmap was passed in for reuse, pass it back return javaBitmap; } // now create the java bitmap return bitmap; }
status_t BootAnimation::initTexture(const Animation::Frame& frame) { //StopWatch watch("blah"); SkBitmap bitmap; SkMemoryStream stream(frame.map->getDataPtr(), frame.map->getDataLength()); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (codec) { codec->setDitherImage(false); codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode); delete codec; } // FileMap memory is never released until application exit. // Release it now as the texture is already loaded and the memory used for // the packed resource can be released. frame.map->release(); // ensure we can call getPixels(). No need to call unlock, since the // bitmap will go out of scope when we return from this method. bitmap.lockPixels(); const int w = bitmap.width(); const int h = bitmap.height(); const void* p = bitmap.getPixels(); GLint crop[4] = { 0, h, w, -h }; int tw = 1 << (31 - __builtin_clz(w)); int th = 1 << (31 - __builtin_clz(h)); if (tw < w) tw <<= 1; if (th < h) th <<= 1; switch (bitmap.getConfig()) { case SkBitmap::kARGB_8888_Config: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, p); } break; case SkBitmap::kRGB_565_Config: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } break; default: break; } glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); return NO_ERROR; }
/* * nine patch not supported * * purgeable not supported * reportSizeToVM not supported */ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd, int start_x, int start_y, int width, int height, jobject options) { jobject tileBitmap = NULL; SkImageDecoder *decoder = brd->getDecoder(); int sampleSize = 1; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; bool preferQualityOverSpeed = false; if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); // initialize these, in case we fail 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); prefConfig = GraphicsJNI::getNativeBitmapConfig(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); } decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 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");; } SkIRect region; region.fLeft = start_x; region.fTop = start_y; region.fRight = start_x + width; region.fBottom = start_y + height; SkBitmap* bitmap = NULL; SkTScopedPtr<SkBitmap> adb; if (tileBitmap != NULL) { // Re-use bitmap. bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap); } if (bitmap == NULL) { bitmap = new SkBitmap; adb.reset(bitmap); } if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) { return nullObjectReturn("decoder->decodeRegion returned false"); } // update options (if any) 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())); } SkPixelRef* pr = bitmap->pixelRef(); // promise we will never change our pixels (great for sharing and pictures) // pr->setImmutable(); if(tileBitmap != NULL) { return tileBitmap; } // detach bitmap from its autodeleter, since we want to own it now adb.release(); JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); jbyteArray buff = allocator->getStorageObjAndReset(); return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1); }
SkBitmap* MpoDecoder::decodeBuffer(JNIEnv* env, jobject options, SkStream* stream) { int sampleSize = 1; int preferSize = 0; int postproc = 0; int postprocflag = 0; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; bool isPurgeable = options != NULL && env->GetBooleanField(options, options_purgeableFieldID); if (NULL != options) { sampleSize = env->GetIntField(options, options_sampleSizeFieldID); //preferSize = env->GetIntField(options, options_preferSizeFieldID); //postproc = env->GetBooleanField(options, options_postprocFieldID); //postprocflag = env->GetIntField(options, options_postprocflagFieldID); if (env->GetBooleanField(options, options_justBoundsFieldID)) { mode = SkImageDecoder::kDecodeBounds_Mode; } // initialize these, in case we fail later on env->SetIntField(options, options_widthFieldID, -1); env->SetIntField(options, options_heightFieldID, -1); env->SetObjectField(options, options_mimeFieldID, 0); jobject jconfig = env->GetObjectField(options, options_configFieldID); prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); doDither = env->GetBooleanField(options, options_ditherFieldID); } SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (NULL == decoder) { XLOGE("SkImageDecoder-Factory() returned false"); return NULL; } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); //decoder->setPreferSize(preferSize); //decoder->setPostProcFlag((postproc | (postprocflag << 4))); // 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, options_mCancelID)) { XLOGE("Decoding is cancelled by requestCancelDecode"); return NULL; } SkImageDecoder::Mode decodeMode = mode; if (isPurgeable) { decodeMode = SkImageDecoder::kDecodeBounds_Mode; } SkBitmap* bitmap = new SkBitmap; if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) { XLOGE("SkImageDecoder-decode() returned false"); return NULL; } // update options (if any) if (NULL != options) { env->SetIntField(options, options_widthFieldID, bitmap->width()); env->SetIntField(options, options_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, options_mimeFieldID,env->NewStringUTF("image/mpo")); } // if we're in justBounds mode, return now (skip the java bitmap) if (SkImageDecoder::kDecodeBounds_Mode == mode) { delete bitmap; return NULL; } else { return bitmap; } }
status_t BootAnimation::initTexture(void* buffer, size_t len) { //StopWatch watch("blah"); SkBitmap bitmap; SkMemoryStream stream(buffer, len); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); codec->setDitherImage(false); if (codec) { codec->decode(&stream, &bitmap, SkBitmap::kRGB_565_Config, SkImageDecoder::kDecodePixels_Mode); delete codec; } // ensure we can call getPixels(). No need to call unlock, since the // bitmap will go out of scope when we return from this method. bitmap.lockPixels(); const int w = bitmap.width(); const int h = bitmap.height(); const void* p = bitmap.getPixels(); GLint crop[4] = { 0, h, w, -h }; int tw = 1 << (31 - __builtin_clz(w)); int th = 1 << (31 - __builtin_clz(h)); if (tw < w) tw <<= 1; if (th < h) th <<= 1; switch (bitmap.getConfig()) { case SkBitmap::kARGB_8888_Config: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, p); } break; case SkBitmap::kRGB_565_Config: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } break; default: break; } glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); return NO_ERROR; }
// since we "may" create a purgeable imageref, we require the stream be ref'able // i.e. dynamically allocated, since its lifetime may exceed the current stack // frame. static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options, bool allowPurgeable, bool forcePurgeable = false) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; bool doDither = true; bool isMutable = false; float scale = 1.0f; bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)); bool preferQualityOverSpeed = false; bool requireUnpremultiplied = false; jobject javaBitmap = NULL; if (options != NULL) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); if (optionsJustBounds(env, options)) { mode = SkImageDecoder::kDecodeBounds_Mode; } // initialize these, in case we fail 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); prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); doDither = env->GetBooleanField(options, gOptions_ditherFieldID); preferQualityOverSpeed = env->GetBooleanField(options, gOptions_preferQualityOverSpeedFieldID); requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 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); if (density != 0 && targetDensity != 0 && density != screenDensity) { scale = (float) targetDensity / density; } } } const bool willScale = scale != 1.0f; isPurgeable &= !willScale; SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (decoder == NULL) { return nullObjectReturn("SkImageDecoder::Factory returned null"); } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); SkBitmap* outputBitmap = NULL; unsigned int existingBufferSize = 0; if (javaBitmap != NULL) { outputBitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); if (outputBitmap->isImmutable()) { ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); javaBitmap = NULL; outputBitmap = NULL; } else { existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); } } SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL); if (outputBitmap == NULL) outputBitmap = adb.get(); NinePatchPeeker peeker(decoder); decoder->setPeeker(&peeker); SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode; JavaPixelAllocator javaAllocator(env); RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize); ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ? (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator; if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) { if (!willScale) { // If the java allocator is being used to allocate the pixel memory, the decoder // need not write zeroes, since the memory is initialized to 0. decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator); decoder->setAllocator(outputAllocator); } else if (javaBitmap != NULL) { // check for eventual scaled bounds at allocation time, so we don't decode the bitmap // only to find the scaled result too large to fit in the allocation decoder->setAllocator(&scaleCheckingAllocator); } } // Only setup the decoder to be deleted after its stack-based, refcounted // components (allocators, peekers, etc) are declared. This prevents RefCnt // asserts from firing due to the order objects are deleted from the stack. SkAutoTDelete<SkImageDecoder> add(decoder); 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 (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) { return nullObjectReturn("gOptions_mCancelID"); } SkBitmap decodingBitmap; if (!decoder->decode(stream, &decodingBitmap, prefConfig, decodeMode)) { return nullObjectReturn("decoder->decode returned false"); } int scaledWidth = decodingBitmap.width(); int scaledHeight = decodingBitmap.height(); 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())); } // if we're in justBounds mode, return now (skip the java bitmap) if (mode == SkImageDecoder::kDecodeBounds_Mode) { return NULL; } jbyteArray ninePatchChunk = NULL; if (peeker.fPatch != NULL) { if (willScale) { scaleNinePatchChunk(peeker.fPatch, scale); } size_t ninePatchArraySize = peeker.fPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == NULL) { return nullObjectReturn("ninePatchChunk == null"); } jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); if (array == NULL) { return nullObjectReturn("primitive array == null"); } peeker.fPatch->serialize(array); env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); } jintArray layoutBounds = NULL; if (peeker.fLayoutBounds != NULL) { layoutBounds = env->NewIntArray(4); if (layoutBounds == NULL) { return nullObjectReturn("layoutBounds == null"); } jint scaledBounds[4]; if (willScale) { for (int i=0; i<4; i++) { scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f); } } else { memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds)); } env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds); if (javaBitmap != NULL) { env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds); } } if (willScale) { // This is weird so let me explain: we could use the scale parameter // directly, but for historical reasons this is how the corresponding // Dalvik code has always behaved. We simply recreate the behavior here. // The result is slightly different from simply using scale because of // the 0.5f rounding bias applied when computing the target image size const float sx = scaledWidth / float(decodingBitmap.width()); const float sy = scaledHeight / float(decodingBitmap.height()); // TODO: avoid copying when scaled size equals decodingBitmap size SkBitmap::Config config = configForScaledOutput(decodingBitmap.config()); outputBitmap->setConfig(config, scaledWidth, scaledHeight); outputBitmap->setIsOpaque(decodingBitmap.isOpaque()); if (!outputBitmap->allocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } // If outputBitmap's pixels are newly allocated by Java, there is no need // to erase to 0, since the pixels were initialized to 0. if (outputAllocator != &javaAllocator) { outputBitmap->eraseColor(0); } SkPaint paint; paint.setFilterBitmap(true); SkCanvas canvas(*outputBitmap); canvas.scale(sx, sy); canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); } else { outputBitmap->swap(decodingBitmap); } if (padding) { if (peeker.fPatch != NULL) { GraphicsJNI::set_jrect(env, padding, peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop, peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom); } else { GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } SkPixelRef* pr; if (isPurgeable) { pr = installPixelRef(outputBitmap, stream, sampleSize, doDither); } else { // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. pr = outputBitmap->pixelRef(); } if (pr == NULL) { return nullObjectReturn("Got null SkPixelRef"); } if (!isMutable && javaBitmap == NULL) { // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); if (javaBitmap != NULL) { bool isPremultiplied = !requireUnpremultiplied; GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied); outputBitmap->notifyPixelsChanged(); // If a java bitmap was passed in for reuse, pass it back 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, outputBitmap, javaAllocator.getStorageObj(), bitmapCreateFlags, ninePatchChunk, layoutBounds, -1); }
/* * 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) { SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); jobject tileBitmap = NULL; SkImageDecoder *decoder = brd->getDecoder(); int sampleSize = 1; SkColorType prefColorType = kUnknown_SkColorType; bool doDither = true; bool preferQualityOverSpeed = false; bool requireUnpremultiplied = false; if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); // initialize these, in case we fail 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");; } SkIRect region; region.fLeft = start_x; region.fTop = start_y; region.fRight = start_x + width; region.fBottom = start_y + height; SkBitmap* bitmap = NULL; SkAutoTDelete<SkBitmap> adb; if (tileBitmap != NULL) { // Re-use bitmap. bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap); } if (bitmap == NULL) { bitmap = new SkBitmap; adb.reset(bitmap); } if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) { return nullObjectReturn("decoder->decodeRegion returned false"); } // update options (if any) 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())); } if (tileBitmap != NULL) { return tileBitmap; } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); jbyteArray buff = allocator->getStorageObjAndReset(); int bitmapCreateFlags = 0; if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1); }
// since we "may" create a purgeable imageref, we require the stream be ref'able // i.e. dynamically allocated, since its lifetime may exceed the current stack // frame. static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, jobject options, bool allowPurgeable, bool forcePurgeable = false, bool applyScale = false, float scale = 1.0f) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; bool doDither = true; bool isMutable = false; bool willScale = applyScale && scale != 1.0f; bool isPurgeable = !willScale && (forcePurgeable || (allowPurgeable && optionsPurgeable(env, options))); bool preferQualityOverSpeed = false; jobject javaBitmap = NULL; if (options != NULL) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); if (optionsJustBounds(env, options)) { mode = SkImageDecoder::kDecodeBounds_Mode; } // initialize these, in case we fail 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); prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); doDither = env->GetBooleanField(options, gOptions_ditherFieldID); preferQualityOverSpeed = env->GetBooleanField(options, gOptions_preferQualityOverSpeedFieldID); javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); } if (willScale && javaBitmap != NULL) { return nullObjectReturn("Cannot pre-scale a reused bitmap"); } SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (decoder == NULL) { return nullObjectReturn("SkImageDecoder::Factory returned null"); } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); NinePatchPeeker peeker(decoder); JavaPixelAllocator javaAllocator(env); SkBitmap* bitmap; if (javaBitmap == NULL) { bitmap = new SkBitmap; } else { if (sampleSize != 1) { return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1"); } bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID); // config of supplied bitmap overrules config set in options prefConfig = bitmap->getConfig(); } SkAutoTDelete<SkImageDecoder> add(decoder); SkAutoTDelete<SkBitmap> adb(bitmap, javaBitmap == NULL); decoder->setPeeker(&peeker); if (!isPurgeable) { decoder->setAllocator(&javaAllocator); } 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 (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) { return nullObjectReturn("gOptions_mCancelID"); } SkImageDecoder::Mode decodeMode = mode; if (isPurgeable) { decodeMode = SkImageDecoder::kDecodeBounds_Mode; } SkBitmap* decoded; if (willScale) { decoded = new SkBitmap; } else { decoded = bitmap; } SkAutoTDelete<SkBitmap> adb2(willScale ? decoded : NULL); if (!decoder->decode(stream, decoded, prefConfig, decodeMode, javaBitmap != NULL)) { return nullObjectReturn("decoder->decode returned false"); } int scaledWidth = decoded->width(); int scaledHeight = decoded->height(); 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())); } // if we're in justBounds mode, return now (skip the java bitmap) if (mode == SkImageDecoder::kDecodeBounds_Mode) { return NULL; } jbyteArray ninePatchChunk = NULL; if (peeker.fPatch != NULL) { if (willScale) { scaleNinePatchChunk(peeker.fPatch, scale); } size_t ninePatchArraySize = peeker.fPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == NULL) { return nullObjectReturn("ninePatchChunk == null"); } jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); if (array == NULL) { return nullObjectReturn("primitive array == null"); } peeker.fPatch->serialize(array); env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); } jintArray layoutBounds = NULL; if (peeker.fLayoutBounds != NULL) { layoutBounds = env->NewIntArray(4); if (layoutBounds == NULL) { return nullObjectReturn("layoutBounds == null"); } jint scaledBounds[4]; if (willScale) { for (int i=0; i<4; i++) { scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f); } } else { memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds)); } env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds); if (javaBitmap != NULL) { env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds); } } if (willScale) { // This is weird so let me explain: we could use the scale parameter // directly, but for historical reasons this is how the corresponding // Dalvik code has always behaved. We simply recreate the behavior here. // The result is slightly different from simply using scale because of // the 0.5f rounding bias applied when computing the target image size const float sx = scaledWidth / float(decoded->width()); const float sy = scaledHeight / float(decoded->height()); SkBitmap::Config config = decoded->config(); switch (config) { case SkBitmap::kNo_Config: case SkBitmap::kIndex8_Config: case SkBitmap::kRLE_Index8_Config: config = SkBitmap::kARGB_8888_Config; break; default: break; } bitmap->setConfig(config, scaledWidth, scaledHeight); bitmap->setIsOpaque(decoded->isOpaque()); if (!bitmap->allocPixels(&javaAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } bitmap->eraseColor(0); SkPaint paint; paint.setFilterBitmap(true); SkCanvas canvas(*bitmap); canvas.scale(sx, sy); canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint); // Save off the unscaled version of bitmap to be used in later // transformations if it would reduce memory pressure. Only do // so if it is being upscaled more than 50%, is bigger than // 256x256, and not too big to be keeping a copy of (<1MB). const int numUnscaledPixels = decoded->width() * decoded->height(); if (sx > 1.5 && numUnscaledPixels > 65536 && numUnscaledPixels < 262144) { bitmap->setUnscaledBitmap(decoded); adb2.detach(); //responsibility for freeing decoded's memory is //transferred to bitmap's destructor } } if (padding) { if (peeker.fPatch != NULL) { GraphicsJNI::set_jrect(env, padding, peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop, peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom); } else { GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } SkPixelRef* pr; if (isPurgeable) { pr = installPixelRef(bitmap, stream, sampleSize, doDither); } else { // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. pr = bitmap->pixelRef(); } if (pr == NULL) { return nullObjectReturn("Got null SkPixelRef"); } if (!isMutable) { // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); if (javaBitmap != NULL) { // If a java bitmap was passed in for reuse, pass it back return javaBitmap; } // now create the java bitmap return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), isMutable, ninePatchChunk, layoutBounds, -1); }
// since we "may" create a purgeable imageref, we require the stream be ref'able // i.e. dynamically allocated, since its lifetime may exceed the current stack // frame. static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, jobject options, bool allowPurgeable, bool forcePurgeable = false) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)); bool reportSizeToVM = optionsReportSizeToVM(env, options); bool preferQualityOverSpeed = false; if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) { mode = SkImageDecoder::kDecodeBounds_Mode; } // initialize these, in case we fail 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); prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); doDither = env->GetBooleanField(options, gOptions_ditherFieldID); preferQualityOverSpeed = env->GetBooleanField(options, gOptions_preferQualityOverSpeedFieldID); } SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (NULL == decoder) { return nullObjectReturn("SkImageDecoder::Factory returned null"); } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); NinePatchPeeker peeker(decoder); JavaPixelAllocator javaAllocator(env, reportSizeToVM); SkBitmap* bitmap = new SkBitmap; Res_png_9patch dummy9Patch; SkAutoTDelete<SkImageDecoder> add(decoder); SkAutoTDelete<SkBitmap> adb(bitmap); decoder->setPeeker(&peeker); if (!isPurgeable) { decoder->setAllocator(&javaAllocator); } 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");; } SkImageDecoder::Mode decodeMode = mode; if (isPurgeable) { decodeMode = SkImageDecoder::kDecodeBounds_Mode; } if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) { return nullObjectReturn("decoder->decode returned false"); } // update options (if any) 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())); } // if we're in justBounds mode, return now (skip the java bitmap) if (SkImageDecoder::kDecodeBounds_Mode == mode) { return NULL; } jbyteArray ninePatchChunk = NULL; if (peeker.fPatchIsValid) { size_t ninePatchArraySize = peeker.fPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (NULL == ninePatchChunk) { return nullObjectReturn("ninePatchChunk == null"); } jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); if (NULL == array) { return nullObjectReturn("primitive array == null"); } peeker.fPatch->serialize(array); env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); } // detach bitmap from its autotdeleter, since we want to own it now adb.detach(); if (padding) { if (peeker.fPatchIsValid) { GraphicsJNI::set_jrect(env, padding, peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop, peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom); } else { GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } SkPixelRef* pr; if (isPurgeable) { pr = installPixelRef(bitmap, stream, sampleSize, doDither); } else { // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. pr = bitmap->pixelRef(); } // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); // now create the java bitmap return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); }
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { int sampleSize = 1; int preferSize = 0; int postproc = 0; int postprocflag = 0; #ifdef MTK_IMAGE_DC_SUPPORT void* dc; bool dcflag = false; jint* pdynamicCon = NULL; jintArray dynamicCon; jsize size = 0; #endif 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; jobject javaBitmap = NULL; if (options != NULL) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); if (optionsJustBounds(env, options)) { decodeMode = SkImageDecoder::kDecodeBounds_Mode; } // initialize these, in case we fail 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); isMutable = env->GetBooleanField(options, gOptions_mutableFieldID); doDither = env->GetBooleanField(options, gOptions_ditherFieldID); preferQualityOverSpeed = env->GetBooleanField(options, gOptions_preferQualityOverSpeedFieldID); requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); postproc = env->GetBooleanField(options, gOptions_postprocFieldID); postprocflag = env->GetIntField(options, gOptions_postprocflagFieldID); #ifdef MTK_IMAGE_DC_SUPPORT dcflag = env->GetBooleanField(options, gOptions_dynamicConflagFieldID); dynamicCon = (jintArray)env->GetObjectField(options, gOptions_dynamicConFieldID); //pdynamicCon = (unsigned int*)env->GetIntArrayElements(dynamicCon, 0); pdynamicCon = env->GetIntArrayElements(dynamicCon, NULL); size = env->GetArrayLength(dynamicCon); //for (int i=0; i<size; i++) //{ //ALOGD("pdynamicCon[%d]=%d", i, pdynamicCon[i]); //} //ALOGD("BitmapFactory.cpp postproc=%d, postprocflag=%d", postproc, postprocflag); //ALOGD("BitmapFactory.cpp dcflag=%d", dcflag); //ALOGD("BitmapFactory.cpp dynamicCon=%p", dynamicCon); //ALOGD("BitmapFactory.cpp size=%d", size); #endif 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); if (density != 0 && targetDensity != 0 && density != screenDensity) { scale = (float) targetDensity / density; } } } const bool willScale = scale != 1.0f; SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (decoder == NULL) { return nullObjectReturn("SkImageDecoder::Factory returned null"); } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); decoder->setPreferSize(preferSize); decoder->setPostProcFlag((postproc | (postprocflag << 4))); #ifdef MTK_IMAGE_DC_SUPPORT if (dcflag == true) { dc= (void*)pdynamicCon; int len = (int)size; decoder->setDynamicCon(dc, len); } else { dc = NULL; decoder->setDynamicCon(dc, 0); } // (env)->ReleaseIntArrayElements(dynamicCon, pdynamicCon, 0); #endif SkBitmap* outputBitmap = NULL; unsigned int existingBufferSize = 0; if (javaBitmap != NULL) { outputBitmap = (SkBitmap*) env->GetLongField(javaBitmap, gBitmap_nativeBitmapFieldID); if (outputBitmap->isImmutable()) { ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); javaBitmap = NULL; outputBitmap = NULL; } else { existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); } } SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL); if (outputBitmap == NULL) outputBitmap = adb.get(); NinePatchPeeker peeker(decoder); decoder->setPeeker(&peeker); JavaPixelAllocator javaAllocator(env); RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize); ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize); SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ? (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator; if (decodeMode != SkImageDecoder::kDecodeBounds_Mode) { if (!willScale) { // If the java allocator is being used to allocate the pixel memory, the decoder // need not write zeroes, since the memory is initialized to 0. decoder->setSkipWritingZeroes(outputAllocator == &javaAllocator); decoder->setAllocator(outputAllocator); } else if (javaBitmap != NULL) { // check for eventual scaled bounds at allocation time, so we don't decode the bitmap // only to find the scaled result too large to fit in the allocation decoder->setAllocator(&scaleCheckingAllocator); } } // Only setup the decoder to be deleted after its stack-based, refcounted // components (allocators, peekers, etc) are declared. This prevents RefCnt // asserts from firing due to the order objects are deleted from the stack. SkAutoTDelete<SkImageDecoder> add(decoder); 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 (options != NULL && env->GetBooleanField(options, gOptions_mCancelID)) { return nullObjectReturn("gOptions_mCancelID"); } SkBitmap decodingBitmap; if (!decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode)) { return nullObjectReturn("decoder->decode returned false"); } int scaledWidth = decodingBitmap.width(); int scaledHeight = decodingBitmap.height(); if (willScale && decodeMode != 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())); } // if we're in justBounds mode, return now (skip the java bitmap) if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) { return NULL; } jbyteArray ninePatchChunk = NULL; if (peeker.mPatch != NULL) { if (willScale) { scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight); } size_t ninePatchArraySize = peeker.mPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == NULL) { return nullObjectReturn("ninePatchChunk == null"); } jbyte* array = (jbyte*) env->GetPrimitiveArrayCritical(ninePatchChunk, NULL); if (array == NULL) { return nullObjectReturn("primitive array == null"); } memcpy(array, peeker.mPatch, peeker.mPatchSize); env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0); } jobject ninePatchInsets = NULL; if (peeker.mHasInsets) { ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID, peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3], peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3], peeker.mOutlineRadius, peeker.mOutlineAlpha, scale); if (ninePatchInsets == NULL) { return nullObjectReturn("nine patch insets == null"); } if (javaBitmap != NULL) { env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets); } } if (willScale) { // This is weird so let me explain: we could use the scale parameter // directly, but for historical reasons this is how the corresponding // Dalvik code has always behaved. We simply recreate the behavior here. // The result is slightly different from simply using scale because of // the 0.5f rounding bias applied when computing the target image size const float sx = scaledWidth / float(decodingBitmap.width()); const float sy = scaledHeight / float(decodingBitmap.height()); // TODO: avoid copying when scaled size equals decodingBitmap size SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType()); // FIXME: If the alphaType is kUnpremul and the image has alpha, the // colors may not be correct, since Skia does not yet support drawing // to/from unpremultiplied bitmaps. outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType())); if (!outputBitmap->allocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } // If outputBitmap's pixels are newly allocated by Java, there is no need // to erase to 0, since the pixels were initialized to 0. if (outputAllocator != &javaAllocator) { outputBitmap->eraseColor(0); } SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); SkCanvas canvas(*outputBitmap); canvas.scale(sx, sy); canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); } else { outputBitmap->swap(decodingBitmap); } if (padding) { if (peeker.mPatch != NULL) { GraphicsJNI::set_jrect(env, padding, peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop, peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom); } else { GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1); } } // if we get here, we're in kDecodePixels_Mode and will therefore // already have a pixelref installed. if (outputBitmap->pixelRef() == NULL) { return nullObjectReturn("Got null SkPixelRef"); } if (!isMutable && javaBitmap == NULL) { // promise we will never change our pixels (great for sharing and pictures) outputBitmap->setImmutable(); } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); if (javaBitmap != NULL) { bool isPremultiplied = !requireUnpremultiplied; GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied); outputBitmap->notifyPixelsChanged(); // If a java bitmap was passed in for reuse, pass it back 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, outputBitmap, javaAllocator.getStorageObj(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1); }
ECode CLargeBitmap::NativeDecodeRegion( /* [in] */ SkBitmapRegionDecoder* lbm, /* [in] */ Int32 startX, /* [in] */ Int32 startY, /* [in] */ Int32 width, /* [in] */ Int32 height, /* [in] */ IBitmapFactoryOptions* options, /* [out] */ IBitmap** bitmap) { SkImageDecoder* decoder = lbm->getDecoder(); Int32 sampleSize = 1; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; bool preferQualityOverSpeed = false; CBitmapFactoryOptions* optObj = (CBitmapFactoryOptions*)options; if (NULL != optObj) { sampleSize = optObj->mInSampleSize; // initialize these, in case we fail later on optObj->mOutWidth = -1; optObj->mOutHeight = -1; optObj->mOutMimeType = NULL; prefConfig = Graphics::GetNativeBitmapConfig(optObj->mInPreferredConfig); doDither = (bool)optObj->mInDither; preferQualityOverSpeed = (bool)optObj->mInPreferQualityOverSpeed; } decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); SkBitmap* nativeBitmap = new SkBitmap; SkAutoTDelete<SkBitmap> adb(nativeBitmap); 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 != optObj && optObj->mCancel) { *bitmap = NULL; return NOERROR; } SkIRect region; region.fLeft = startX; region.fTop = startY; region.fRight = startX + width; region.fBottom = startY + height; if (!lbm->decodeRegion(nativeBitmap, region, prefConfig, sampleSize)) { *bitmap = NULL; return NOERROR; } // update options (if any) if (NULL != optObj) { optObj->mOutWidth = nativeBitmap->width(); optObj->mOutHeight = nativeBitmap->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? optObj->mOutMimeType = CBitmapFactory::GetMimeTypeString(decoder->getFormat()); } // detach bitmap from its autotdeleter, since we want to own it now adb.detach(); SkPixelRef* pr; pr = nativeBitmap->pixelRef(); // promise we will never change our pixels (great for sharing and pictures) pr->setImmutable(); // now create the java bitmap return Graphics::CreateBitmap(nativeBitmap, FALSE, NULL, -1, bitmap); }