ArcsView() { //testparse(); fSweep = SkIntToScalar(100); this->setBGColor(0xFFDDDDDD); SkFILEStream stream("c:/test_ba.png"); SkImageDecoder* dec = sk_libpng_dfactory(&stream); //delete dec; SkImageDecoder *coder = SkImageDecoder::Factory(&stream); coder->decode(&stream, &fbmp, SkColorType::kRGBA_8888_SkColorType, SkImageDecoder::kDecodePixels_Mode); delete dec; }
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 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; }
bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref, Mode mode) { SkASSERT(stream); SkASSERT(bm); bool success = false; SkImageDecoder* codec = SkImageDecoder::Factory(stream); if (NULL != codec) { success = codec->decode(stream, bm, pref, mode); delete codec; } return success; }
void onOnceBeforeDraw() override { SkImageDecoder* codec = nullptr; SkString resourcePath = GetResourcePath("mandrill_512.png"); SkFILEStream stream(resourcePath.c_str()); if (stream.isValid()) { codec = SkImageDecoder::Factory(&stream); } if (codec) { stream.rewind(); codec->decode(&stream, &fBM, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); delete codec; } else { fBM.allocN32Pixels(1, 1); fBM.eraseARGB(255, 255, 0 , 0); // red == bad } }
void makeBitmap() { SkImageDecoder* codec = NULL; SkString resourcePath = GetResourcePath(fFilename.c_str()); SkFILEStream stream(resourcePath.c_str()); if (stream.isValid()) { codec = SkImageDecoder::Factory(&stream); } if (codec) { stream.rewind(); codec->decode(&stream, &fBM, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); SkDELETE(codec); } else { fBM.allocN32Pixels(1, 1); *(fBM.getAddr32(0,0)) = 0xFF0000FF; // red == bad } fSize = fBM.height(); }
int ResSkiaImage::ImageDataToSkBitmap(NVGcontext* ctx, const CStdString& src, CStdValVector* data) { int bitmap = 0; bool bOpaque = false; // if (!data->m_data) // return 0; // // SkMemoryStream* stream = data->m_data; // return nvgCreateImageMem(ctx, 0, (unsigned char *)stream->getMemoryBase(), stream->getLength()); return nvgCreateImageMem(ctx, 0, (unsigned char *)data->GetBuffer(), data->GetSize()); #if 0 if (-1 != src.Find(L".jpg") || -1 != src.Find(L".png")) { // 这里改为不需要后缀,直接通过文件头判断是何图片类型 bOpaque = true; GDIPlusDecoder(data); } SkMemoryStream* stream = data->m_data; // GDIPlusDecoder可能会更改m_data if (!stream) return NULL; bitmap = new SkBitmap(); if (BMPImageReader::decode(false, stream, bitmap)) return bitmap; delete bitmap; return 0; #endif #if 0 // 直接用skia的BMP解压器,会发现png转换而成的BMP32,会丢失alpha通道 SkImageDecoder* coder = SkImageDecoder::Factory(stream); if (!coder) return NULL; bitmap = new SkBitmap(); if (!coder->decode(stream, bitmap, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode)) { delete bitmap; return NULL; } bitmap->setIsOpaque(bOpaque); #endif return bitmap; }
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; }
SkImageGenerator* SkImageGenerator::NewFromData(SkData* data) { if (NULL == data) { return NULL; } SkMemoryStream stream(data->data(), data->size(), false); SkImageDecoder* decoder = SkImageDecoder::Factory(&stream); if (NULL == decoder) { return NULL; } SkBitmap bm; stream.rewind(); if (!decoder->decode(&stream, &bm, kUnknown_SkColorType, SkImageDecoder::kDecodeBounds_Mode)) { SkDELETE(decoder); return NULL; } return SkNEW_ARGS(SkImageDecoderGenerator, (bm.info(), decoder, data)); }
bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref, Mode mode, Format* format) { SkASSERT(stream); SkASSERT(bm); bool success = false; SkImageDecoder* codec = SkImageDecoder::Factory(stream); if (codec) { success = codec->decode(stream, bm, pref, mode) != kFailure; if (success && format) { *format = codec->getFormat(); if (kUnknown_Format == *format) { if (stream->rewind()) { *format = GetStreamFormat(stream); } } } delete codec; } return success; }
SubpixelTranslateView(const char imageFilename[], float horizontalVelocity, float verticalVelocity) : fHorizontalVelocity(horizontalVelocity), fVerticalVelocity(verticalVelocity) { SkString resourcePath = GetResourcePath(imageFilename); SkImageDecoder* codec = NULL; SkFILEStream stream(resourcePath.c_str()); if (stream.isValid()) { codec = SkImageDecoder::Factory(&stream); } if (codec) { stream.rewind(); codec->decode(&stream, &fBM, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); delete codec; } else { fBM.allocN32Pixels(1, 1); *(fBM.getAddr32(0,0)) = 0xFF0000FF; // red == bad } fCurPos = SkPoint::Make(0,0); fSize = 200; }
static void decodeFileAndWrite(const char srcPath[]) { SkBitmap bitmap; SkFILEStream stream(srcPath); if (!stream.isValid()) { return; } SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (NULL == codec) { return; } SkAutoTDelete<SkImageDecoder> ad(codec); stream.rewind(); if (!codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode)) { return; } write_bitmap(srcPath, bitmap); }
static bool decodeFile(SkBitmap* bitmap, const char srcPath[]) { SkFILEStream stream(srcPath); if (!stream.isValid()) { SkDebugf("ERROR: bad filename <%s>\n", srcPath); return false; } SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (NULL == codec) { SkDebugf("ERROR: no codec found for <%s>\n", srcPath); return false; } SkAutoTDelete<SkImageDecoder> ad(codec); stream.rewind(); if (!codec->decode(&stream, bitmap, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode)) { SkDebugf("ERROR: codec failed for <%s>\n", srcPath); return false; } return true; }
bool get_bitmap(SkData* fileBits, DiffResource& resource, SkImageDecoder::Mode mode) { SkMemoryStream stream(fileBits->data(), fileBits->size()); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (NULL == codec) { SkDebugf("ERROR: no codec found for <%s>\n", resource.fFullPath.c_str()); resource.fStatus = DiffResource::kCouldNotDecode_Status; return false; } // In debug, the DLL will automatically be unloaded when this is deleted, // but that shouldn't be a problem in release mode. SkAutoTDelete<SkImageDecoder> ad(codec); stream.rewind(); if (!codec->decode(&stream, &resource.fBitmap, SkBitmap::kARGB_8888_Config, mode)) { SkDebugf("ERROR: codec failed for basePath <%s>\n", resource.fFullPath.c_str()); resource.fStatus = DiffResource::kCouldNotDecode_Status; return false; } resource.fStatus = DiffResource::kDecoded_Status; 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; }
void ImageSource::setData(SharedBuffer* data, bool allDataReceived) { #ifdef ANDROID_ANIMATED_GIF // This is only necessary if we allow ourselves to partially decode GIF bool disabledAnimatedGif = false; if (m_decoder.m_gifDecoder && !m_decoder.m_gifDecoder->failed()) { m_decoder.m_gifDecoder->setData(data, allDataReceived); if (!allDataReceived || m_decoder.m_gifDecoder->frameCount() != 1) return; disabledAnimatedGif = true; delete m_decoder.m_gifDecoder; m_decoder.m_gifDecoder = 0; } #endif if (NULL == m_decoder.m_image #ifdef ANDROID_ANIMATED_GIF && !m_decoder.m_gifDecoder #endif ) { SkBitmap tmp; SkMemoryStream stream(data->data(), data->size(), false); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (!codec) return; SkAutoTDelete<SkImageDecoder> ad(codec); // FOR KITKAT MR2 INTEGRATION //codec->setPrefConfigTable(gPrefConfigTable); SkImageDecoder::PrefConfigTable configTable; configTable.fPrefFor_8Index_NoAlpha_src = gPrefConfigTable[0]; configTable.fPrefFor_8Index_YesAlpha_src = gPrefConfigTable[1]; configTable.fPrefFor_8Gray_src = SkBitmap::kNo_Config; configTable.fPrefFor_8bpc_NoAlpha_src = gPrefConfigTable[4]; configTable.fPrefFor_8bpc_YesAlpha_src = gPrefConfigTable[5]; codec->setPrefConfigTable(configTable); // FOR KITKAT MR2 INTEGRATION if (!codec->decode(&stream, &tmp, SkImageDecoder::kDecodeBounds_Mode)) return; int origW = tmp.width(); int origH = tmp.height(); #ifdef ANDROID_ANIMATED_GIF // First, check to see if this is an animated GIF const char* contents = data->data(); if (data->size() > 3 && strncmp(contents, "GIF8", 4) == 0 && should_use_animated_gif(origW, origH) && !disabledAnimatedGif) { // This means we are looking at a GIF, so create special // GIF Decoder // Need to wait for all data received if we are assigning an // allocator (which we are not at the moment). if (!m_decoder.m_gifDecoder /*&& allDataReceived*/) m_decoder.m_gifDecoder = new GIFImageDecoder(m_alphaOption, m_gammaAndColorProfileOption); int frameCount = 0; if (!m_decoder.m_gifDecoder->failed()) { m_decoder.m_gifDecoder->setData(data, allDataReceived); if (!allDataReceived) return; frameCount = m_decoder.m_gifDecoder->frameCount(); } if (frameCount != 1) return; delete m_decoder.m_gifDecoder; m_decoder.m_gifDecoder = 0; } #endif int sampleSize = computeSampleSize(tmp); if (sampleSize > 1) { codec->setSampleSize(sampleSize); stream.rewind(); if (!codec->decode(&stream, &tmp, SkImageDecoder::kDecodeBounds_Mode)) { return; } } m_decoder.m_image = new PrivateAndroidImageSourceRec(tmp, origW, origH, sampleSize); // SkDebugf("----- started: [%d %d] %s\n", origW, origH, m_decoder.m_url.c_str()); } PrivateAndroidImageSourceRec* decoder = m_decoder.m_image; if (allDataReceived && decoder && !decoder->fAllDataReceived) { decoder->fAllDataReceived = true; SkBitmap* bm = &decoder->bitmap(); // 4.2 Merge BEGIN << //Following code removed in 4.2 // SkPixelRef* ref = convertToRLE(bm, data->data(), data->size()); //4.2 Merge : removed in 4.2 // if (ref) { // bm->setPixelRef(ref)->unref(); // } else { // 4.2 Merge END >> BitmapAllocatorAndroid alloc(data, decoder->fSampleSize); if (!alloc.allocPixelRef(bm, NULL)) { return; } SkPixelRef* ref = bm->pixelRef();//4.2 Merge // }//4.3 Merge : removed in 4.2 // we promise to never change the pixels (makes picture recording fast) ref->setImmutable(); // give it the URL if we have one ref->setURI(m_decoder.m_url); } }
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); }
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; } }
void ImageSource::setData(SharedBuffer* data, bool allDataReceived) { #ifdef ANDROID_ANIMATED_GIF // This is only necessary if we allow ourselves to partially decode GIF if (m_decoder.m_gifDecoder && !m_decoder.m_gifDecoder->failed()) { m_decoder.m_gifDecoder->setData(data, allDataReceived); return; } #endif if (NULL == m_decoder.m_image #ifdef ANDROID_ANIMATED_GIF && !m_decoder.m_gifDecoder #endif ) { SkBitmap tmp; SkMemoryStream stream(data->data(), data->size(), false); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); SkAutoTDelete<SkImageDecoder> ad(codec); if (!codec || !codec->decode(&stream, &tmp, SkBitmap::kNo_Config, SkImageDecoder::kDecodeBounds_Mode)) { return; } int origW = tmp.width(); int origH = tmp.height(); #ifdef ANDROID_ANIMATED_GIF // First, check to see if this is an animated GIF const Vector<char>& buffer = data->buffer(); const char* contents = buffer.data(); if (buffer.size() > 3 && strncmp(contents, "GIF8", 4) == 0 && should_use_animated_gif(origW, origH)) { // This means we are looking at a GIF, so create special // GIF Decoder // Need to wait for all data received if we are assigning an // allocator (which we are not at the moment). if (!m_decoder.m_gifDecoder /*&& allDataReceived*/) m_decoder.m_gifDecoder = new GIFImageDecoder(); if (!m_decoder.m_gifDecoder->failed()) m_decoder.m_gifDecoder->setData(data, allDataReceived); return; } #endif int sampleSize = computeSampleSize(tmp); if (sampleSize > 1) { codec->setSampleSize(sampleSize); stream.rewind(); if (!codec->decode(&stream, &tmp, SkBitmap::kNo_Config, SkImageDecoder::kDecodeBounds_Mode)) { return; } } m_decoder.m_image = new PrivateAndroidImageSourceRec(tmp, origW, origH, sampleSize); // SkDebugf("----- started: [%d %d] %s\n", origW, origH, m_decoder.m_url.c_str()); } PrivateAndroidImageSourceRec* decoder = m_decoder.m_image; if (allDataReceived && !decoder->fAllDataReceived) { decoder->fAllDataReceived = true; SkBitmap* bm = &decoder->bitmap(); SkPixelRef* ref = convertToRLE(bm, data->data(), data->size()); if (ref) { bm->setPixelRef(ref)->unref(); } else { BitmapAllocatorAndroid alloc(data, decoder->fSampleSize); if (!alloc.allocPixelRef(bm, NULL)) { return; } ref = bm->pixelRef(); } // we promise to never change the pixels (makes picture recording fast) ref->setImmutable(); // give it the URL if we have one ref->setURI(m_decoder.m_url); } }
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, 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); }
// 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); }
// 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); }