void SourceSurfaceSkia::DrawTargetWillChange() { if (mDrawTarget) { MaybeUnlock(); mDrawTarget = nullptr; SkBitmap temp = mBitmap; mBitmap.reset(); temp.copyTo(&mBitmap, temp.getConfig()); } }
bool SkShader::setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) { SkASSERT(!this->setContextHasBeenCalled()); const SkMatrix* m = &matrix; SkMatrix total; fDeviceConfig = SkToU8(device.getConfig()); fPaintAlpha = paint.getAlpha(); if (this->hasLocalMatrix()) { total.setConcat(matrix, this->getLocalMatrix()); m = &total; } if (m->invert(&fTotalInverse)) { fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse); SkDEBUGCODE(fInSetContext = true;) return true;
bool SkShader::setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) { const SkMatrix* m = &matrix; SkMatrix total; fDeviceConfig = SkToU8(device.getConfig()); fPaintAlpha = paint.getAlpha(); if (fLocalMatrix) { total.setConcat(matrix, *fLocalMatrix); m = &total; } if (m->invert(&fTotalInverse)) { fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse); return true; } return false; }
bool SourceSurfaceSkia::InitWithBitmap(const SkBitmap& aBitmap, SurfaceFormat aFormat, DrawTargetSkia* aOwner) { mFormat = aFormat; mSize = IntSize(aBitmap.width(), aBitmap.height()); if (aOwner) { mBitmap = aBitmap; mStride = aBitmap.rowBytes(); mDrawTarget = aOwner; return true; } else if (aBitmap.copyTo(&mBitmap, aBitmap.getConfig())) { mStride = mBitmap.rowBytes(); return true; } return false; }
/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes to our SkWStream. Since we don't reference/own the SkWStream, our consumer must only live for the duration of the onEncode() method. */ bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm, int quality) { // Used for converting a bitmap to 8888. const SkBitmap* bmPtr = &bm; SkBitmap bitmap8888; CFStringRef type; switch (fType) { case kJPEG_Type: type = kUTTypeJPEG; break; case kPNG_Type: // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: // <Error>: CGImageDestinationAddImage image could not be converted to destination // format. // <Error>: CGImageDestinationFinalize image destination does not have enough images // So instead we copy to 8888. if (bm.getConfig() == SkBitmap::kARGB_4444_Config) { bm.copyTo(&bitmap8888, SkBitmap::kARGB_8888_Config); bmPtr = &bitmap8888; } type = kUTTypePNG; break; default: return false; } CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); if (NULL == dst) { return false; } SkAutoTCallVProc<const void, CFRelease> ardst(dst); CGImageRef image = SkCreateCGImageRef(*bmPtr); if (NULL == image) { return false; } SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image); CGImageDestinationAddImage(dst, image, NULL); return CGImageDestinationFinalize(dst); }
void GLUtils::updateTextureWithBitmap(GLuint texture, int x, int y, const SkBitmap& bitmap, GLint filter) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, texture); GLUtils::checkGlError("glBindTexture"); SkBitmap::Config config = bitmap.getConfig(); int internalformat = getInternalFormat(config); int type = getType(config); bitmap.lockPixels(); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, bitmap.width(), bitmap.height(), internalformat, type, bitmap.getPixels()); bitmap.unlockPixels(); if (GLUtils::checkGlError("glTexSubImage2D")) { XLOG("GL ERROR: glTexSubImage2D parameters are : bitmap.width() %d, bitmap.height() %d," " x %d, y %d, internalformat 0x%x, type 0x%x, bitmap.getPixels() %p", bitmap.width(), bitmap.height(), x, y, internalformat, type, bitmap.getPixels()); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); }
static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) { switch (bitmap.getConfig()) { case SkBitmap::kARGB_8888_Config: *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y)); break; case SkBitmap::kRGB_565_Config: *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y)); break; case SkBitmap::kARGB_4444_Config: *c = SkUnPreMultiply::PMColorToColor( SkPixel4444ToPixel32(*bitmap.getAddr16(x, y))); break; case SkBitmap::kIndex8_Config: { SkColorTable* ctable = bitmap.getColorTable(); *c = SkUnPreMultiply::PMColorToColor( (*ctable)[*bitmap.getAddr8(x, y)]); break; } default: return false; } return true; }
bool GLUtils::createTextureWithBitmapFailSafe(GLuint texture, const SkBitmap& bitmap, GLint filter) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, texture); bool val = GLUtils::checkGlError("glBindTexture", false); if(val) return false; SkBitmap::Config config = bitmap.getConfig(); int internalformat = getInternalFormat(config); int type = getType(config); bitmap.lockPixels(); glTexImage2D(GL_TEXTURE_2D, 0, internalformat, bitmap.width(), bitmap.height(), 0, internalformat, type, bitmap.getPixels()); bitmap.unlockPixels(); if (GLUtils::checkGlError("glTexImage2D", false)) { return false; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); // The following is a workaround -- remove when EGLImage texture upload is fixed. GLuint fboID; glGenFramebuffers(1, &fboID); glBindFramebuffer(GL_FRAMEBUFFER, fboID); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glCheckFramebufferStatus(GL_FRAMEBUFFER); // should return GL_FRAMEBUFFER_COMPLETE glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO glDeleteFramebuffers(1, &fboID); return true; }
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; }
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; }
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, const char* name) { Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); if (!asset) return NO_INIT; SkBitmap bitmap; #ifdef SLIDE_HORIZONTAL SkBitmap origbitmap; SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), &origbitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); #else SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); #endif asset->close(); delete asset; #ifdef SLIDE_HORIZONTAL // create a bitmap with rotated dimensions and draw the rotated original bitmap bitmap.setConfig(origbitmap.config(), origbitmap.height(), origbitmap.width()); bitmap.allocPixels(); SkCanvas canvas(bitmap); canvas.translate(SkIntToScalar(bitmap.width()), 0); canvas.rotate(SkIntToScalar(90)); canvas.drawBitmap(origbitmap, 0, 0, NULL); #endif // 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 }; texture->w = w; texture->h = h; glGenTextures(1, &texture->name); glBindTexture(GL_TEXTURE_2D, texture->name); switch (bitmap.getConfig()) { case SkBitmap::kA8_Config: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p); break; case SkBitmap::kARGB_4444_Config: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, p); break; case SkBitmap::kARGB_8888_Config: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, p); break; case SkBitmap::kRGB_565_Config: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); break; default: break; } glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 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, 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; int preferSize = 0; int postproc = 0; int postprocflag = 0; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config; bool doDither = true; bool isMutable = false; bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options)); bool preferQualityOverSpeed = false; jobject javaBitmap = NULL; if (NULL != options) { 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); postproc = env->GetBooleanField(options, gOptions_postprocFieldID); postprocflag = env->GetIntField(options, gOptions_postprocflagFieldID); javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); } SkImageDecoder* decoder = SkImageDecoder::Factory(stream); if (NULL == decoder) { return nullObjectReturn("SkImageDecoder::Factory returned null"); } decoder->setSampleSize(sampleSize); decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); decoder->setPreferSize(preferSize); decoder->setPostProcFlag((postproc | (postprocflag << 4))); 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(); } Res_png_9patch dummy9Patch; 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 (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, javaBitmap != NULL)) { 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 autodeleter, 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(); } 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 GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), isMutable, ninePatchChunk); }
SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) { SkBitmap::Config config = bitmap.getConfig(); // TODO(vandebo) Handle alpha and alpha only images correctly. SkASSERT(config == SkBitmap::kRGB_565_Config || config == SkBitmap::kARGB_4444_Config || config == SkBitmap::kARGB_8888_Config || config == SkBitmap::kIndex8_Config || config == SkBitmap::kRLE_Index8_Config); SkMemoryStream* image_data = extractImageData(bitmap); SkAutoUnref image_data_unref(image_data); fStream = new SkPDFStream(image_data); fStream->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject"); typeValue->unref(); // SkRefPtr and new both took a reference. insert("Type", typeValue.get()); SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image"); subTypeValue->unref(); // SkRefPtr and new both took a reference. insert("Subtype", subTypeValue.get()); SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width()); widthValue->unref(); // SkRefPtr and new both took a reference. insert("Width", widthValue.get()); SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height()); heightValue->unref(); // SkRefPtr and new both took a reference. insert("Height", heightValue.get()); // if (!image mask) { SkRefPtr<SkPDFObject> colorSpaceValue; if (config == SkBitmap::kIndex8_Config || config == SkBitmap::kRLE_Index8_Config) { colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable()); } else { colorSpaceValue = new SkPDFName("DeviceRGB"); } colorSpaceValue->unref(); // SkRefPtr and new both took a reference. insert("ColorSpace", colorSpaceValue.get()); // } int bitsPerComp = bitmap.bytesPerPixel() * 2; if (bitsPerComp == 0) { SkASSERT(config == SkBitmap::kA1_Config); bitsPerComp = 1; } else if (bitsPerComp == 2 || (bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) { bitsPerComp = 8; } SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp); bitsPerCompValue->unref(); // SkRefPtr and new both took a reference. insert("BitsPerComponent", bitsPerCompValue.get()); if (config == SkBitmap::kRGB_565_Config) { SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0); zeroVal->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1 scale5Val->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1 scale6Val->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray(); decodeValue->unref(); // SkRefPtr and new both took a reference. decodeValue->reserve(6); decodeValue->append(zeroVal.get()); decodeValue->append(scale5Val.get()); decodeValue->append(zeroVal.get()); decodeValue->append(scale6Val.get()); decodeValue->append(zeroVal.get()); decodeValue->append(scale5Val.get()); insert("Decode", decodeValue.get()); } }