virtual void onDrawContent(SkCanvas* canvas) { SkRect srcR; srcR.set(fSrcPts[0], fSrcPts[1]); srcR = SkRect::MakeXYWH(fSrcPts[0].fX, fSrcPts[0].fY, 32, 32); srcR.offset(-srcR.width()/2, -srcR.height()/2); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setColor(SK_ColorYELLOW); SkBitmap bitmap; make_bitmap(&bitmap); canvas->translate(20, 20); canvas->drawBitmap(bitmap, 0, 0, &paint); canvas->drawRect(srcR, paint); for (int i = 0; i < 2; ++i) { paint.setFilterLevel(1 == i ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel); canvas->drawBitmapRectToRect(bitmap, &srcR, fDstR[i], &paint); canvas->drawRect(fDstR[i], paint); } this->bounce(); this->inval(NULL); }
virtual void onDraw(SkCanvas* canvas) { SkBitmap bm; SkIRect center; make_bitmap(&bm, ¢er); // amount of bm that should not be stretched (unless we have to) const SkScalar fixed = SkIntToScalar(bm.width() - center.width()); const SkTSize<SkScalar> size[] = { { fixed * 4 / 5, fixed * 4 / 5 }, // shrink in both axes { fixed * 4 / 5, fixed * 4 }, // shrink in X { fixed * 4, fixed * 4 / 5 }, // shrink in Y { fixed * 4, fixed * 4 } }; canvas->drawBitmap(bm, SkIntToScalar(10), SkIntToScalar(10), NULL); SkScalar x = SkIntToScalar(100); SkScalar y = SkIntToScalar(100); SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); for (int iy = 0; iy < 2; ++iy) { for (int ix = 0; ix < 2; ++ix) { int i = ix * 2 + iy; SkRect r = SkRect::MakeXYWH(x + ix * fixed, y + iy * fixed, size[i].width(), size[i].height()); canvas->drawBitmapNine(bm, center, r, &paint); } } }
virtual void onDrawContent(SkCanvas* canvas) { canvas->translate(this->width()/2, this->height()/2); Sk3DView view; view.rotateX(fRX); view.rotateY(fRY); view.applyToCanvas(canvas); SkPaint paint; if (fShaders.count() > 0) { bool frontFace = view.dotWithNormal(0, 0, SK_Scalar1) < 0; if (frontFace != fFrontFace) { fFrontFace = frontFace; fShaderIndex = (fShaderIndex + 1) % fShaders.count(); } paint.setAntiAlias(true); paint.setShader(fShaders[fShaderIndex]); paint.setFilterLevel(SkPaint::kLow_FilterLevel); SkRect r = { -150, -150, 150, 150 }; canvas->drawRoundRect(r, 30, 30, paint); } fRY += SampleCode::GetAnimSecondsDelta() * 90; if (fRY >= SkIntToScalar(360)) { fRY = 0; } this->inval(NULL); }
virtual void onDraw(SkCanvas* canvas) { SkMatrix m; m.reset(); m.setRotate(33 * SK_Scalar1); m.postScale(3000 * SK_Scalar1, 3000 * SK_Scalar1); m.postTranslate(6000 * SK_Scalar1, -5000 * SK_Scalar1); canvas->concat(m); SkPaint paint; paint.setColor(SK_ColorRED); paint.setAntiAlias(true); bool success = m.invert(&m); SkASSERT(success); (void) success; // silence compiler :( SkPath path; SkPoint pt = {10 * SK_Scalar1, 10 * SK_Scalar1}; SkScalar small = 1 / (500 * SK_Scalar1); m.mapPoints(&pt, 1); path.addCircle(pt.fX, pt.fY, small); canvas->drawPath(path, paint); pt.set(30 * SK_Scalar1, 10 * SK_Scalar1); m.mapPoints(&pt, 1); SkRect rect = {pt.fX - small, pt.fY - small, pt.fX + small, pt.fY + small}; canvas->drawRect(rect, paint); SkBitmap bmp; bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); bmp.allocPixels(); bmp.lockPixels(); uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels()); pixels[0] = SkPackARGB32(0xFF, 0xFF, 0x00, 0x00); pixels[1] = SkPackARGB32(0xFF, 0x00, 0xFF, 0x00); pixels[2] = SkPackARGB32(0x80, 0x00, 0x00, 0x00); pixels[3] = SkPackARGB32(0xFF, 0x00, 0x00, 0xFF); bmp.unlockPixels(); pt.set(30 * SK_Scalar1, 30 * SK_Scalar1); m.mapPoints(&pt, 1); SkShader* shader = SkShader::CreateBitmapShader( bmp, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); SkMatrix s; s.reset(); s.setScale(SK_Scalar1 / 1000, SK_Scalar1 / 1000); shader->setLocalMatrix(s); paint.setShader(shader)->unref(); paint.setAntiAlias(false); paint.setFilterLevel(SkPaint::kLow_FilterLevel); rect.setLTRB(pt.fX - small, pt.fY - small, pt.fX + small, pt.fY + small); canvas->drawRect(rect, paint); }
virtual void onDrawContent(SkCanvas* canvas) { SkIRect srcRect; SkRect dstRect; SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); // Test that bitmap draws from malloc-backed bitmaps respect // the constrained texture domain. srcRect.setXYWH(1, 1, 3, 3); dstRect.setXYWH(5.0f, 5.0f, 305.0f, 305.0f); canvas->drawBitmapRect(fBM, &srcRect, dstRect, &paint); // Test that bitmap draws across separate devices also respect // the constrainted texture domain. // Note: GPU-backed bitmaps follow a different rendering path // when copying from one GPU device to another. SkAutoTUnref<SkBaseDevice> secondDevice(canvas->createCompatibleDevice( SkBitmap::kARGB_8888_Config, 5, 5, true)); SkCanvas secondCanvas(secondDevice.get()); srcRect.setXYWH(1, 1, 3, 3); dstRect.setXYWH(1.0f, 1.0f, 3.0f, 3.0f); secondCanvas.drawBitmapRect(fBM, &srcRect, dstRect, &paint); SkBitmap deviceBitmap = secondDevice->accessBitmap(false); srcRect.setXYWH(1, 1, 3, 3); dstRect.setXYWH(405.0f, 5.0f, 305.0f, 305.0f); canvas->drawBitmapRect(deviceBitmap, &srcRect, dstRect, &paint); // Test that bitmap blurring using a subrect // renders correctly srcRect.setXYWH(1, 1, 3, 3); dstRect.setXYWH(5.0f, 405.0f, 305.0f, 305.0f); SkMaskFilter* mf = SkBlurMaskFilter::Create( SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), SkBlurMaskFilter::kHighQuality_BlurFlag | SkBlurMaskFilter::kIgnoreTransform_BlurFlag); paint.setMaskFilter(mf)->unref(); canvas->drawBitmapRect(deviceBitmap, &srcRect, dstRect, &paint); // Blur and a rotation + NULL src rect // This should not trigger the texture domain code // but it will test a code path in SkGpuDevice::drawBitmap // that handles blurs with rects transformed to non- // orthogonal rects. It also tests the NULL src rect handling mf = SkBlurMaskFilter::Create( SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), SkBlurMaskFilter::kHighQuality_BlurFlag); paint.setMaskFilter(mf)->unref(); dstRect.setXYWH(-150.0f, -150.0f, 300.0f, 300.0f); canvas->translate(550, 550); canvas->rotate(45); canvas->drawBitmapRect(fBM, NULL, dstRect, &paint); }
static void draw_row(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& mat, SkScalar dx) { SkPaint paint; SkAutoCanvasRestore acr(canvas, true); canvas->drawBitmapMatrix(bm, mat, &paint); paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas->translate(dx, 0); canvas->drawBitmapMatrix(bm, mat, &paint); paint.setFilterLevel(SkPaint::kMedium_FilterLevel); canvas->translate(dx, 0); canvas->drawBitmapMatrix(bm, mat, &paint); paint.setFilterLevel(SkPaint::kHigh_FilterLevel); canvas->translate(dx, 0); canvas->drawBitmapMatrix(bm, mat, &paint); }
void Panel::OnDraw(SkCanvas* canvas, const SkRect& clip_rect) { SkPaint paint; paint.setFilterLevel(SkPaint::kMedium_FilterLevel); SkRect rect = SkRect::MakeXYWH( SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(Width()), SkIntToScalar(Height())); auto bitmaps = Bitmap(); !bitmaps.empty() ? GetRenderTactics()->Draw(canvas, bitmaps[0], rect, paint) : canvas->clear(SK_AlphaTRANSPARENT); }
virtual void onDrawContent(SkCanvas* canvas) { SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setColor(SK_ColorYELLOW); for (int i = 0; i < 2; ++i) { paint.setFilterLevel(1 == i ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel); canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR[i], &paint); canvas->drawRect(fDstR[i], paint); } this->bounceMe(); this->inval(NULL); }
bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const Context&, SkBitmap* result, SkIPoint*) const { SkScalar scale = fScale; if (scale > SK_Scalar1 || scale <= 0) { return false; } int dstW = SkScalarRoundToInt(src.width() * scale); int dstH = SkScalarRoundToInt(src.height() * scale); if (dstW < 1) { dstW = 1; } if (dstH < 1) { dstH = 1; } SkBitmap tmp; // downsample { SkBaseDevice* dev = proxy->createDevice(dstW, dstH); if (NULL == dev) { return false; } OwnDeviceCanvas canvas(dev); SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas.scale(scale, scale); canvas.drawBitmap(src, 0, 0, &paint); tmp = dev->accessBitmap(false); } // upscale { SkBaseDevice* dev = proxy->createDevice(src.width(), src.height()); if (NULL == dev) { return false; } OwnDeviceCanvas canvas(dev); SkRect r = SkRect::MakeWH(SkIntToScalar(src.width()), SkIntToScalar(src.height())); canvas.drawBitmapRect(tmp, NULL, r, NULL); *result = dev->accessBitmap(false); } return true; }
// Draw ~1/4 of the large bitmap void drawCase3(SkCanvas* canvas, int transX, int transY, SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) { SkRect src = SkRect::MakeXYWH(2, 2, SkIntToScalar(fBitmapBig.width()/2-2), SkIntToScalar(fBitmapBig.height()/2-2)); SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); SkPaint paint; paint.setFilterLevel(filter); canvas->save(); canvas->translate(SkIntToScalar(transX), SkIntToScalar(transY)); canvas->drawBitmapRectToRect(fBitmapBig, &src, dst, &paint, flags); canvas->restore(); }
// Draw only the center of the small bitmap void drawCase1(SkCanvas* canvas, int transX, int transY, SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) { SkRect src = SkRect::MakeXYWH(2, 2, SkIntToScalar(kSmallTextureSize-4), SkIntToScalar(kSmallTextureSize-4)); SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); SkPaint paint; paint.setFilterLevel(filter); canvas->save(); canvas->translate(SkIntToScalar(transX), SkIntToScalar(transY)); canvas->drawBitmapRectToRect(fBitmapSmall, &src, dst, &paint, flags); canvas->restore(); }
virtual void onDrawContent(SkCanvas* canvas) { canvas->translate(SkIntToScalar(10), SkIntToScalar(50)); const SkScalar W = SkIntToScalar(fBitmaps[0].width() + 1); const SkScalar H = SkIntToScalar(fBitmaps[0].height() + 1); SkPaint paint; const SkScalar scale = 0.897917f; canvas->scale(SK_Scalar1, scale); for (int k = 0; k < 2; k++) { paint.setFilterLevel(k == 1 ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel); for (int j = 0; j < 2; j++) { paint.setDither(j == 1); for (int i = 0; i < fBitmapCount; i++) { SkScalar x = (k * fBitmapCount + j) * W; SkScalar y = i * H; x = SkScalarRoundToScalar(x); y = SkScalarRoundToScalar(y); canvas->drawBitmap(fBitmaps[i], x, y, &paint); if (i == 0) { SkPaint p; p.setAntiAlias(true); p.setTextAlign(SkPaint::kCenter_Align); p.setTextSize(SkIntToScalar(18)); SkString s("dither="); s.appendS32(paint.isDither()); s.append(" filter="); s.appendS32(paint.getFilterLevel() != SkPaint::kNone_FilterLevel); canvas->drawText(s.c_str(), s.size(), x + W/2, y - p.getTextSize(), p); } if (k+j == 2) { SkPaint p; p.setAntiAlias(true); p.setTextSize(SkIntToScalar(18)); SkString s; s.append(" depth="); s.appendS32(fBitmaps[i].colorType() == kRGB_565_SkColorType ? 16 : 32); canvas->drawText(s.c_str(), s.size(), x + W + SkIntToScalar(4), y + H/2, p); } } } } }
// Draw the center of the small bitmap with a mask filter void drawCase4(SkCanvas* canvas, int transX, int transY, SkCanvas::DrawBitmapRectFlags flags, SkPaint::FilterLevel filter) { SkRect src = SkRect::MakeXYWH(2, 2, SkIntToScalar(kSmallTextureSize-4), SkIntToScalar(kSmallTextureSize-4)); SkRect dst = SkRect::MakeXYWH(0, 0, SkIntToScalar(kBlockSize), SkIntToScalar(kBlockSize)); SkPaint paint; paint.setFilterLevel(filter); SkMaskFilter* mf = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3))); paint.setMaskFilter(mf)->unref(); canvas->save(); canvas->translate(SkIntToScalar(transX), SkIntToScalar(transY)); canvas->drawBitmapRectToRect(fBitmapSmall, &src, dst, &paint, flags); canvas->restore(); }
static void mesh_slide(SkCanvas* canvas) { Rec fRecs[3]; SkIPoint size; SkShader* fShader0 = make_shader0(&size); SkShader* fShader1 = make_shader1(size); SkAutoUnref aur0(fShader0); SkAutoUnref aur1(fShader1); make_strip(&fRecs[0], size.fX, size.fY); make_fan(&fRecs[1], size.fX, size.fY); make_tris(&fRecs[2]); SkPaint paint; paint.setDither(true); paint.setFilterLevel(SkPaint::kLow_FilterLevel); for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) { canvas->save(); paint.setShader(NULL); canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, fRecs[i].fVerts, fRecs[i].fTexs, NULL, NULL, NULL, 0, paint); canvas->translate(SkIntToScalar(210), 0); paint.setShader(fShader0); canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, fRecs[i].fVerts, fRecs[i].fTexs, NULL, NULL, NULL, 0, paint); canvas->translate(SkIntToScalar(210), 0); paint.setShader(fShader1); canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount, fRecs[i].fVerts, fRecs[i].fTexs, NULL, NULL, NULL, 0, paint); canvas->restore(); canvas->translate(0, SkIntToScalar(250)); } }
bool SkMatrixImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkRect dstRect; SkIRect srcBounds, dstBounds; src.getBounds(&srcBounds); srcBounds.offset(srcOffset); SkRect srcRect = SkRect::Make(srcBounds); SkMatrix matrix; if (!ctx.ctm().invert(&matrix)) { return false; } matrix.postConcat(fTransform); matrix.postConcat(ctx.ctm()); matrix.mapRect(&dstRect, srcRect); dstRect.roundOut(&dstBounds); SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height())); if (NULL == device.get()) { return false; } SkCanvas canvas(device.get()); canvas.translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y())); canvas.concat(matrix); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setFilterLevel(fFilterLevel); canvas.drawBitmap(src, srcRect.x(), srcRect.y(), &paint); *result = device.get()->accessBitmap(false); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; return true; }
static void Transform(DataSourceSurface* aDest, DataSourceSurface* aSource, const gfx3DMatrix& aTransform, const Point& aDestOffset) { if (aTransform.IsSingular()) { return; } IntSize destSize = aDest->GetSize(); SkImageInfo destInfo = SkImageInfo::Make(destSize.width, destSize.height, kBGRA_8888_SkColorType, kPremul_SkAlphaType); SkBitmap destBitmap; destBitmap.setInfo(destInfo, aDest->Stride()); destBitmap.setPixels((uint32_t*)aDest->GetData()); SkCanvas destCanvas(destBitmap); IntSize srcSize = aSource->GetSize(); SkImageInfo srcInfo = SkImageInfo::Make(srcSize.width, srcSize.height, kBGRA_8888_SkColorType, kPremul_SkAlphaType); SkBitmap src; src.setInfo(srcInfo, aSource->Stride()); src.setPixels((uint32_t*)aSource->GetData()); gfx3DMatrix transform = aTransform; transform.TranslatePost(Point3D(-aDestOffset.x, -aDestOffset.y, 0)); destCanvas.setMatrix(Matrix3DToSkia(transform)); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setAntiAlias(true); paint.setFilterLevel(SkPaint::kLow_FilterLevel); SkRect destRect = SkRect::MakeXYWH(0, 0, srcSize.width, srcSize.height); destCanvas.drawBitmapRectToRect(src, nullptr, destRect, &paint); }
static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap, Float aAlpha = 1.0) { switch (aPattern.GetType()) { case PatternType::COLOR: { Color color = static_cast<const ColorPattern&>(aPattern).mColor; aPaint.setColor(ColorToSkColor(color, aAlpha)); break; } case PatternType::LINEAR_GRADIENT: { const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); if (stops->mCount >= 2) { SkPoint points[2]; points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), SkFloatToScalar(pat.mBegin.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), SkFloatToScalar(pat.mEnd.y)); SkShader* shader = SkGradientShader::CreateLinear(points, &stops->mColors.front(), &stops->mPositions.front(), stops->mCount, mode); if (shader) { SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); shader->setLocalMatrix(mat); SkSafeUnref(aPaint.setShader(shader)); } } else { aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); } break; } case PatternType::RADIAL_GRADIENT: { const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern); GradientStopsSkia *stops = static_cast<GradientStopsSkia*>(pat.mStops.get()); SkShader::TileMode mode = ExtendModeToTileMode(stops->mExtendMode); if (stops->mCount >= 2) { SkPoint points[2]; points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), SkFloatToScalar(pat.mCenter1.y)); points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), SkFloatToScalar(pat.mCenter2.y)); SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], SkFloatToScalar(pat.mRadius1), points[1], SkFloatToScalar(pat.mRadius2), &stops->mColors.front(), &stops->mPositions.front(), stops->mCount, mode); if (shader) { SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); shader->setLocalMatrix(mat); SkSafeUnref(aPaint.setShader(shader)); } } else { aPaint.setColor(SkColorSetARGB(0, 0, 0, 0)); } break; } case PatternType::SURFACE: { const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); aTmpBitmap = GetBitmapForSurface(pat.mSurface); const SkBitmap& bitmap = aTmpBitmap.mBitmap; SkShader::TileMode mode = ExtendModeToTileMode(pat.mExtendMode); SkShader* shader = SkShader::CreateBitmapShader(bitmap, mode, mode); SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); shader->setLocalMatrix(mat); SkSafeUnref(aPaint.setShader(shader)); if (pat.mFilter == Filter::POINT) { aPaint.setFilterLevel(SkPaint::kNone_FilterLevel); } break; } } }
void NativeImageSkia::drawPattern( GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect, blink::WebBlendMode blendMode, const IntSize& repeatSpacing) const { FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix totalMatrix = context->getTotalMatrix(); SkScalar ctmScaleX = totalMatrix.getScaleX(); SkScalar ctmScaleY = totalMatrix.getScaleY(); totalMatrix.preScale(scale.width(), scale.height()); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. SkRect destRectTarget; totalMatrix.mapRect(&destRectTarget, normSrcRect); float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); // Compute the resampling mode. ResamplingMode resampling; if (context->isAccelerated() || context->printing()) resampling = LinearResampling; else resampling = computeResamplingMode(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight); resampling = limitResamplingMode(context, resampling); SkMatrix shaderTransform; RefPtr<SkShader> shader; bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); // Bicubic filter is only applied to defer-decoded images, see // NativeImageSkia::draw for details. bool useBicubicFilter = resampling == AwesomeResampling && isLazyDecoded; if (resampling == AwesomeResampling && !useBicubicFilter) { // Do nice resampling. float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; // The image fragment generated here is not exactly what is // requested. The scale factor used is approximated and image // fragment is slightly larger to align to integer // boundaries. SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect); if (repeatSpacing.isZero()) { shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); } else { shader = adoptRef(SkShader::CreateBitmapShader( createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); } // Since we just resized the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * shaderTransform to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set shaderTransform's scale to the inverse of // CTM scale. shaderTransform.setScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1); } else { // No need to resample before drawing. SkBitmap srcSubset; bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); if (repeatSpacing.isZero()) { shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); } else { shader = adoptRef(SkShader::CreateBitmapShader( createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); } // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. shaderTransform.setScale(scale.width(), scale.height()); } // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the shaderTransform. float adjustedX = phase.x() + normSrcRect.x() * scale.width(); float adjustedY = phase.y() + normSrcRect.y() * scale.height(); shaderTransform.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); shader->setLocalMatrix(shaderTransform); SkPaint paint; paint.setShader(shader.get()); paint.setXfermode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode).get()); paint.setColorFilter(context->colorFilter()); paint.setFilterBitmap(resampling == LinearResampling); if (useBicubicFilter) paint.setFilterLevel(SkPaint::kHigh_FilterLevel); if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()); context->drawRect(destRect, paint); }
virtual void onDraw(SkCanvas* canvas) { static const int kBmpSize = 2048; if (fLargeBitmap.isNull()) { makebm(&fLargeBitmap, kBmpSize, kBmpSize); } SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)}; static const int kMaxSrcRectSize = 1 << (SkNextLog2(kBmpSize) + 2); static const int kPadX = 30; static const int kPadY = 40; SkPaint paint; paint.setAlpha(0x20); canvas->drawBitmapRect(fLargeBitmap, NULL, SkRect::MakeWH(gSize * SK_Scalar1, gSize * SK_Scalar1), &paint); canvas->translate(SK_Scalar1 * kPadX / 2, SK_Scalar1 * kPadY / 2); SkPaint blackPaint; SkScalar titleHeight = SK_Scalar1 * 24; blackPaint.setColor(SK_ColorBLACK); blackPaint.setTextSize(titleHeight); blackPaint.setAntiAlias(true); SkString title; title.printf("Bitmap size: %d x %d", kBmpSize, kBmpSize); canvas->drawText(title.c_str(), title.size(), 0, titleHeight, blackPaint); canvas->translate(0, SK_Scalar1 * kPadY / 2 + titleHeight); int rowCount = 0; canvas->save(); for (int w = 1; w <= kMaxSrcRectSize; w *= 4) { for (int h = 1; h <= kMaxSrcRectSize; h *= 4) { SkIRect srcRect = SkIRect::MakeXYWH((kBmpSize - w) / 2, (kBmpSize - h) / 2, w, h); canvas->drawBitmapRect(fLargeBitmap, &srcRect, dstRect); SkString label; label.appendf("%d x %d", w, h); blackPaint.setAntiAlias(true); blackPaint.setStyle(SkPaint::kFill_Style); blackPaint.setTextSize(SK_Scalar1 * 10); SkScalar baseline = dstRect.height() + blackPaint.getTextSize() + SK_Scalar1 * 3; canvas->drawText(label.c_str(), label.size(), 0, baseline, blackPaint); blackPaint.setStyle(SkPaint::kStroke_Style); blackPaint.setStrokeWidth(SK_Scalar1); blackPaint.setAntiAlias(false); canvas->drawRect(dstRect, blackPaint); canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0); ++rowCount; if ((dstRect.width() + kPadX) * rowCount > gSize) { canvas->restore(); canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY); canvas->save(); rowCount = 0; } } } { // test the following code path: // SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter() SkIRect srcRect; SkPaint paint; SkBitmap bm; bm = make_chessbm(5, 5); paint.setFilterLevel(SkPaint::kLow_FilterLevel); srcRect.setXYWH(1, 1, 3, 3); SkMaskFilter* mf = SkBlurMaskFilter::Create( kNormal_SkBlurStyle, SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(5)), SkBlurMaskFilter::kHighQuality_BlurFlag | SkBlurMaskFilter::kIgnoreTransform_BlurFlag); paint.setMaskFilter(mf)->unref(); canvas->drawBitmapRect(bm, &srcRect, dstRect, &paint); } }
void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, MediaButton buttonType, bool translucent, bool drawBackground, const IntRect& thumb)//4.2 Merge { if (!gDecoded) { Decode(); } if (!canvas) return; // If we failed to decode, do nothing. This way the browser still works, // and webkit will still draw the label and layout space for us. if (gDecodingFailed) return; bool drawsNinePatch = false; bool drawsImage = true; int ninePatchIndex = 0; int imageIndex = 0; SkRect bounds(r); SkScalar imageMargin = 8; SkPaint paint; int alpha = 255; if (translucent) alpha = 190; SkColor backgroundColor = SkColorSetARGB(alpha, 34, 34, 34); SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100); paint.setColor(backgroundColor); //Android KITKAT Merge - START // paint.setFlags(SkPaint::kFilterBitmap_Flag); //P140210-04273 : // In D2 Device if we pass kMedium_FilterLevel image gets corrupted while drawing the image into the canvas. So loading icons was corrupting // kLow_FilterLevel works for other kitkat devices. //WAS paint.setFilterLevel(SkPaint::kMedium_FilterLevel); paint.setFilterLevel(SkPaint::kLow_FilterLevel); //Android KITKAT Merge - END switch (buttonType) { case PAUSE: case PLAY: case MUTE: case REWIND: case FORWARD: case FULLSCREEN: { imageIndex = buttonType + 1; paint.setColor(backgroundColor); break; } case SPINNER_OUTER: case SPINNER_INNER: case VIDEO: { imageIndex = buttonType + 1; break; } case BACKGROUND_SLIDER: { drawsImage = false; break; } case SLIDER_TRACK: { drawsNinePatch = true; drawsImage = false; ninePatchIndex = buttonType + 1; break; } case SLIDER_THUMB: { imageMargin = 0; imageIndex = buttonType + 1; break; } default: return; } if (drawBackground) { canvas->drawRect(r, paint); } if (drawsNinePatch) { const PatchData& pd = gFiles[ninePatchIndex]; int marginValue = pd.margin + pd.outset; SkIRect margin; margin.set(marginValue, marginValue, marginValue, marginValue); if (buttonType == SLIDER_TRACK) { // Cut the height in half (with some extra slop determined by trial // and error to get the placement just right. SkScalar quarterHeight = SkScalarHalf(SkScalarHalf(bounds.height())); bounds.fTop += quarterHeight + SkScalarHalf(3); bounds.fBottom += -quarterHeight + SK_ScalarHalf; if (!thumb.isEmpty()) {//4.2 Merge // Inset the track by half the width of the thumb, so the track // does not appear to go beyond the space where the thumb can // be. SkScalar thumbHalfWidth = SkIntToScalar(thumb.width()/2); bounds.fLeft += thumbHalfWidth; bounds.fRight -= thumbHalfWidth; if (thumb.x() > 0) { // The video is past the starting point. Show the area to // left of the thumb as having been played. SkScalar alreadyPlayed = SkIntToScalar(thumb.center().x() + r.x()); SkRect playedRect(bounds); playedRect.fRight = alreadyPlayed; SkNinePatch::DrawNine(canvas, playedRect, gButton[0], margin); bounds.fLeft = alreadyPlayed; } } } SkNinePatch::DrawNine(canvas, bounds, gButton[ninePatchIndex], margin); } if (drawsImage) { SkScalar SIZE = gButton[imageIndex].width(); SkScalar width = r.width(); SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE); int saveScaleCount = canvas->save(); canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin); canvas->scale(scale, scale); canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint); canvas->restoreToCount(saveScaleCount); } }
void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, const SkRect& destRect, PassRefPtr<SkXfermode> compOp) const { TRACE_EVENT0("skia", "NativeImageSkia::draw"); SkPaint paint; paint.setXfermode(compOp.get()); paint.setColorFilter(context->colorFilter()); paint.setAlpha(context->getNormalizedAlpha()); paint.setLooper(context->drawLooper()); // only antialias if we're rotated or skewed paint.setAntiAlias(hasNon90rotation(context)); ResamplingMode resampling; if (context->isAccelerated()) { resampling = LinearResampling; } else if (context->printing()) { resampling = NoResampling; } else { // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). SkRect destRectTarget = destRect; SkMatrix totalMatrix = context->getTotalMatrix(); if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) totalMatrix.mapRect(&destRectTarget, destRect); resampling = computeResamplingMode(totalMatrix, SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height())); } if (resampling == NoResampling) { // FIXME: This is to not break tests (it results in the filter bitmap flag // being set to true). We need to decide if we respect NoResampling // being returned from computeResamplingMode. resampling = LinearResampling; } resampling = limitResamplingMode(context, resampling); paint.setFilterBitmap(resampling == LinearResampling); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); // FIXME: Bicubic filtering in Skia is only applied to defer-decoded images // as an experiment. Once this filtering code path becomes stable we should // turn this on for all cases, including non-defer-decoded images. bool useBicubicFilter = resampling == AwesomeResampling && isLazyDecoded; if (useBicubicFilter) paint.setFilterLevel(SkPaint::kHigh_FilterLevel); if (resampling == AwesomeResampling && !useBicubicFilter) { // Resample the image and then draw the result to canvas with bilinear // filtering. drawResampledBitmap(context, paint, srcRect, destRect); } else { // We want to filter it if we decided to do interpolation above, or if // there is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so we // don't send extra pixels. context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()); context->didDrawRect(destRect, paint, &bitmap()); }
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); switch ( face->glyph->format ) { case FT_GLYPH_FORMAT_OUTLINE: { FT_Outline* outline = &face->glyph->outline; FT_BBox bbox; FT_Bitmap target; if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) { emboldenOutline(face, outline); } int dx = 0, dy = 0; if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { dx = SkFixedToFDot6(glyph.getSubXFixed()); dy = SkFixedToFDot6(glyph.getSubYFixed()); // negate dy since freetype-y-goes-up and skia-y-goes-down dy = -dy; } FT_Outline_Get_CBox(outline, &bbox); /* what we really want to do for subpixel is offset(dx, dy) compute_bounds offset(bbox & !63) but that is two calls to offset, so we do the following, which achieves the same thing with only one offset call. */ FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), dy - ((bbox.yMin + dy) & ~63)); if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); SkMask mask; glyph.toMask(&mask); if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } else { target.width = glyph.fWidth; target.rows = glyph.fHeight; target.pitch = glyph.rowBytes(); target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); target.num_grays = 256; memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); } } break; case FT_GLYPH_FORMAT_BITMAP: { FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); // Assume that the other formats do not exist. SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || FT_PIXEL_MODE_GRAY == pixel_mode || FT_PIXEL_MODE_BGRA == pixel_mode); // These are the only formats this ScalerContext should request. SkASSERT(SkMask::kBW_Format == maskFormat || SkMask::kA8_Format == maskFormat || SkMask::kARGB32_Format == maskFormat || SkMask::kLCD16_Format == maskFormat); if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) { FT_GlyphSlot_Own_Bitmap(face->glyph); FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); } // If no scaling needed, directly copy glyph bitmap. if (glyph.fWidth == face->glyph->bitmap.width && glyph.fHeight == face->glyph->bitmap.rows && glyph.fTop == -face->glyph->bitmap_top && glyph.fLeft == face->glyph->bitmap_left) { SkMask dstMask; glyph.toMask(&dstMask); copyFTBitmap(face->glyph->bitmap, dstMask); break; } // Otherwise, scale the bitmap. // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) SkBitmap unscaledBitmap; unscaledBitmap.setConfig(SkBitmapConfig_for_FTPixelMode(pixel_mode), face->glyph->bitmap.width, face->glyph->bitmap.rows); unscaledBitmap.allocPixels(); SkMask unscaledBitmapAlias; unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height()); unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkBitmapConfig(unscaledBitmap.config()); copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. // BW requires an A8 target for resizing, which can then be down sampled. // LCD should use a 4x A8 target, which will then be down sampled. // For simplicity, LCD uses A8 and is replicated. int bitmapRowBytes = 0; if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { bitmapRowBytes = glyph.rowBytes(); } SkBitmap dstBitmap; dstBitmap.setConfig(SkBitmapConfig_for_SkMaskFormat(maskFormat), glyph.fWidth, glyph.fHeight, bitmapRowBytes); if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { dstBitmap.allocPixels(); } else { dstBitmap.setPixels(glyph.fImage); } // Scale unscaledBitmap into dstBitmap. SkCanvas canvas(dstBitmap); canvas.clear(SK_ColorTRANSPARENT); canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width), SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows)); SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); // If the destination is BW or LCD, convert from A8. if (SkMask::kBW_Format == maskFormat) { // Copy the A8 dstBitmap into the A1 glyph.fImage. SkMask dstMask; glyph.toMask(&dstMask); packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); } else if (SkMask::kLCD16_Format == maskFormat) { // Copy the A8 dstBitmap into the LCD16 glyph.fImage. uint8_t* src = dstBitmap.getAddr8(0, 0); uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); for (int y = dstBitmap.height(); y --> 0;) { for (int x = 0; x < dstBitmap.width(); ++x) { dst[x] = grayToRGB16(src[x]); } dst = (uint16_t*)((char*)dst + glyph.rowBytes()); src += dstBitmap.rowBytes(); } } } break; default: SkDEBUGFAIL("unknown glyph format"); memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); return; } // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, // it is optional #if defined(SK_GAMMA_APPLY_TO_A8) if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) { uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; unsigned rowBytes = glyph.rowBytes(); for (int y = glyph.fHeight - 1; y >= 0; --y) { for (int x = glyph.fWidth - 1; x >= 0; --x) { dst[x] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { int sampleSize = 1; 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); 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); 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) { jstring mimeType = getMimeTypeString(env, decoder->getFormat()); if (env->ExceptionCheck()) { return nullObjectReturn("OOM in getMimeTypeString()"); } env->SetIntField(options, gOptions_widthFieldID, scaledWidth); env->SetIntField(options, gOptions_heightFieldID, scaledHeight); env->SetObjectField(options, gOptions_mimeFieldID, mimeType); } // 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); }