void NinePatchGlue::drawStretchyPatch(SkCanvas& canvas, SkIRect& src, const SkRect& dst, const BitmapGlue& bitmap, const PaintGlue& paint, SkColor initColor, uint32_t colorHint, bool hasXfer) { if (colorHint != Res_png_9patch::NO_COLOR) { ((PaintGlue*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha())); canvas.drawRect(dst, paint); ((PaintGlue*)&paint)->setColor(initColor); } else if (src.width() == 1 && src.height() == 1) { SkColor c; if (!getColor(bitmap, src.fLeft, src.fTop, &c)) { goto SLOW_CASE; } if (0 != c || hasXfer) { SkColor prev = paint.getColor(); ((PaintGlue*)&paint)->setColor(c); canvas.drawRect(dst, paint); ((PaintGlue*)&paint)->setColor(prev); } } else { SLOW_CASE: canvas.drawBitmapRect(bitmap, &src, dst, &paint); } }
void paint(GraphicsContext* ctxt, const IntRect& r) { if (ctxt->paintingDisabled()) return; if (!m_isVisible) return; if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef())) return; SkCanvas* canvas = ctxt->platformContext()->getCanvas(); if (!canvas) return; // We paint with the following rules in mind: // - only downscale the poster, never upscale // - maintain the natural aspect ratio of the poster // - the poster should be centered in the target rect float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height()); int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width(); int posterHeight = posterWidth / originalRatio; int posterX = ((r.width() - posterWidth) / 2) + r.x(); int posterY = ((r.height() - posterHeight) / 2) + r.y(); IntRect targetRect(posterX, posterY, posterWidth, posterHeight); canvas->drawBitmapRect(*m_poster, 0, targetRect, 0); }
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) { SkPaint paint; paint.setXfermodeMode(compOp); paint.setFilterBitmap(true); paint.setAlpha(platformContext->getNormalizedAlpha()); paint.setLooper(platformContext->getDrawLooper()); // only antialias if we're rotated or skewed paint.setAntiAlias(hasNon90rotation(platformContext)); SkCanvas* canvas = platformContext->canvas(); ResamplingMode resampling; if (platformContext->isAccelerated()) resampling = RESAMPLE_LINEAR; else resampling = platformContext->printing() ? RESAMPLE_NONE : computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(), SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height())); if (resampling == RESAMPLE_AWESOME) { drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); } else { // No resampling necessary, we can just draw the bitmap. We want to // filter it if we decided to do linear 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. canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint); } }
ImageTexture::ImageTexture(SkBitmapRef* img) : m_imageRef(img) , m_image(0) , m_textureId(0) , m_refCount(0) { #ifdef DEBUG_COUNT ClassTracker::instance()->increment("ImageTexture"); #endif if (!m_imageRef) return; SkBitmap* bitmap = &m_imageRef->bitmap(); m_image = new SkBitmap(); int w = bitmap->width(); int h = bitmap->height(); m_image->setConfig(SkBitmap::kARGB_8888_Config, w, h); m_image->allocPixels(); SkDevice* device = new SkDevice(NULL, *m_image, false); SkCanvas canvas; canvas.setDevice(device); device->unref(); SkRect dest; dest.set(0, 0, w, h); m_image->setIsOpaque(false); m_image->eraseARGB(0, 0, 0, 0); canvas.drawBitmapRect(*bitmap, 0, dest); }
void TransparencyWin::compositeTextComposite() { if (!m_validLayer) return; const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopDevice()->accessBitmap(true); SkColor textColor = m_textCompositeColor.rgb(); for (int y = 0; y < m_layerSize.height(); y++) { uint32_t* row = bitmap.getAddr32(0, y); for (int x = 0; x < m_layerSize.width(); x++) { // The alpha is the average of the R, G, and B channels. int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3; // Apply that alpha to the text color and write the result. row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); } } // Now the layer has text with the proper color and opacity. SkCanvas* destCanvas = canvasForContext(*m_destContext); destCanvas->save(); // We want to use Untransformed space (see above) SkMatrix identity; identity.reset(); destCanvas->setMatrix(identity); SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() }; // Note that we need to specify the source layer subset, since the bitmap // may have been cached and it could be larger than what we're using. SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0); destCanvas->restore(); }
// This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "drawResampledBitmap"); #endif // We want to scale |destRect| with transformation in the canvas to obtain // the final scale. The final scale is a combination of scale transform // in canvas and explicit scaling (srcRect and destRect). SkRect screenRect; canvas.getTotalMatrix().mapRect(&screenRect, destRect); float realScaleX = screenRect.width() / srcRect.width(); float realScaleY = screenRect.height() / srcRect.height(); // This part of code limits scaling only to visible portion in the SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Find the corresponding rect in the source image. SkMatrix destToSrcTransform; SkRect srcRectVisibleSubset; destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToFit); destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset); SkRect scaledSrcRect; SkIRect enclosingScaledSrcRect; SkBitmap scaledImageFragment = extractScaledImageFragment(bitmap, srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect, &enclosingScaledSrcRect); // Expand the destination rectangle because the source rectangle was // expanded to fit to integer boundaries. SkMatrix scaledSrcToDestTransform; scaledSrcToDestTransform.setRectToRect(scaledSrcRect, destRectVisibleSubset, SkMatrix::kFill_ScaleToFit); SkRect enclosingDestRect; enclosingDestRect.set(enclosingScaledSrcRect); scaledSrcToDestTransform.mapRect(&enclosingDestRect); // The reason we do clipping is because Skia doesn't support SkRect as // source rect. See http://crbug.com/145540. // When Skia supports then use this as the source rect to replace 0. // // scaledSrcRect.offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y()); canvas.save(); canvas.clipRect(destRectVisibleSubset); // Because the image fragment is generated with an approxmiated scaling // factor. This draw will perform a close to 1 scaling. // // NOTE: For future optimization. If the difference in scale is so small // that Skia doesn't produce a difference then we can just blit it directly // to enhance performance. canvas.drawBitmapRect(scaledImageFragment, 0, enclosingDestRect, &paint); canvas.restore(); }
void TransparencyWin::compositeOpaqueComposite() { if (!m_validLayer) return; SkCanvas* destCanvas = canvasForContext(*m_destContext); destCanvas->save(); SkBitmap* bitmap = const_cast<SkBitmap*>( &bitmapForContext(*m_layerBuffer->context())); // This function will be called for WhiteLayer as well, which we don't want // to change. if (m_layerMode == OpaqueCompositeLayer) { // Fix up our bitmap, making it contain only the pixels which changed // and transparent everywhere else. SkAutoLockPixels sourceLock(*m_referenceBitmap); SkAutoLockPixels lock(*bitmap); for (int y = 0; y < bitmap->height(); y++) { uint32_t* source = m_referenceBitmap->getAddr32(0, y); uint32_t* dest = bitmap->getAddr32(0, y); for (int x = 0; x < bitmap->width(); x++) { // Clear out any pixels that were untouched. if (dest[x] == source[x]) dest[x] = 0; else dest[x] |= (0xFF << SK_A32_SHIFT); } } } else makeLayerOpaque(); SkRect destRect; if (m_transformMode != Untransform) { // We want to use Untransformed space. // // Note that we DON'T call m_layerBuffer->image() here. This actually // makes a copy of the image, which is unnecessary and slow. Instead, we // just draw the image from inside the destination context. SkMatrix identity; identity.reset(); destCanvas->setMatrix(identity); destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY()); } else destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY()); SkPaint paint; paint.setFilterBitmap(true); paint.setAntiAlias(true); // Note that we need to specify the source layer subset, since the bitmap // may have been cached and it could be larger than what we're using. SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); destCanvas->restore(); }
DEF_TEST(IndexedPngOverflow, reporter) { SkBitmap image; bool success = decode_memory(gPng, sizeof(gPng), &image); REPORTER_ASSERT(reporter, success); SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(SkImageInfo::MakeN32Premul(20, 1))); SkCanvas* canvas = surface->getCanvas(); SkRect destRect = SkRect::MakeXYWH(0, 0, 20, 1); canvas->drawBitmapRect(image, destRect, nullptr); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) { startAnimation(); SkBitmapRef* image = this->nativeImageForCurrentFrame(); if (!image) { // If it's too early we won't have an image yet. return; } // in case we get called with an incomplete bitmap const SkBitmap& bitmap = image->bitmap(); if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { #ifdef TRACE_SKIPPED_BITMAPS SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n", bitmap.width(), bitmap.height(), bitmap.getPixels(), bitmap.pixelRef()); #endif return; } SkIRect srcR; SkRect dstR; float invScaleX = (float)bitmap.width() / image->origWidth(); float invScaleY = (float)bitmap.height() / image->origHeight(); android_setrect(&dstR, dstRect); android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); if (srcR.isEmpty() || dstR.isEmpty()) { #ifdef TRACE_SKIPPED_BITMAPS SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n", bitmap.width(), bitmap.height(), srcR.isEmpty(), dstR.isEmpty()); #endif return; } SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; paint.setFilterBitmap(true); paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || bitmap.height() != image->origHeight()) { SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n", bitmap.width(), bitmap.height(), image->origWidth(), image->origHeight()); } #endif }
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "paintSkBitmap"); #endif SkPaint paint; paint.setXfermodeMode(compOp); paint.setAlpha(platformContext->getNormalizedAlpha()); paint.setLooper(platformContext->getDrawLooper()); // only antialias if we're rotated or skewed paint.setAntiAlias(hasNon90rotation(platformContext)); SkCanvas* canvas = platformContext->canvas(); ResamplingMode resampling; if (platformContext->isAccelerated()) resampling = RESAMPLE_LINEAR; else if (platformContext->printing()) resampling = RESAMPLE_NONE; else { // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). SkRect destRectTarget = destRect; if (!(canvas->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) canvas->getTotalMatrix().mapRect(&destRectTarget, destRect); resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap, srcRect.width(), srcRect.height(), SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height())); } if (resampling == RESAMPLE_NONE) { // 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 RESAMPLE_NONE // being returned from computeResamplingMode. resampling = RESAMPLE_LINEAR; } resampling = limitResamplingMode(platformContext, resampling); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); if (resampling == RESAMPLE_AWESOME) drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); else { // No resampling necessary, we can just draw the bitmap. We want to // filter it if we decided to do linear 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. canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint); } platformContext->didDrawRect(destRect, paint, &bitmap.bitmap()); }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "drawResampledBitmap"); #endif // Apply forward transform to destRect to estimate required size of // re-sampled bitmap, and use only in calls required to resize, or that // check for the required size. SkRect destRectTransformed; canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); SkIRect destRectTransformedRounded; destRectTransformed.round(&destRectTransformedRounded); // Compute the visible portion of our rect. SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Compute the image-relative (bitmap space) subset. SkRect destBitmapSubset = destRectVisibleSubset; destBitmapSubset.offset(-destRect.x(), -destRect.y()); // Scale the subset to the requested size. The canvas scale can be negative, // but the resampling code is only interested in positive scaling in its normal space. SkMatrix subsetTransform; subsetTransform.setScale(SkScalarAbs(canvas.getTotalMatrix().getScaleX()), SkScalarAbs(canvas.getTotalMatrix().getScaleY())); SkRect destBitmapSubsetTransformed; subsetTransform.mapRect(&destBitmapSubsetTransformed, destBitmapSubset); SkIRect destBitmapSubsetTransformedRounded; destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded // to go outside the image, so need to clip to avoid problems. if (!destBitmapSubsetTransformedRounded.intersect( 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height())) return; // Image is not visible. SkBitmap resampled = bitmap.resizedBitmap(srcIRect, destRectTransformedRounded.width(), destRectTransformedRounded.height(), destBitmapSubsetTransformedRounded); canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint); }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT("drawResampledBitmap", &canvas, 0); #endif // Apply forward transform to destRect to estimate required size of // re-sampled bitmap, and use only in calls required to resize, or that // check for the required size. SkRect destRectTransformed; canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); SkIRect destRectTransformedRounded; destRectTransformed.round(&destRectTransformedRounded); // Compute the visible portion of our rect. SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Compute the transformed (screen space) portion of the visible portion for // use below. SkRect destRectVisibleSubsetTransformed; canvas.getTotalMatrix().mapRect(&destRectVisibleSubsetTransformed, destRectVisibleSubset); SkRect destBitmapSubsetTransformed = destRectVisibleSubsetTransformed; destBitmapSubsetTransformed.offset(-destRectTransformed.fLeft, -destRectTransformed.fTop); SkIRect destBitmapSubsetTransformedRounded; destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded // to go outside the image, so need to clip to avoid problems. if (!destBitmapSubsetTransformedRounded.intersect( 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height())) return; // Image is not visible. SkBitmap resampled = bitmap.resizedBitmap(srcIRect, destRectTransformedRounded.width(), destRectTransformedRounded.height(), destBitmapSubsetTransformedRounded); canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint); }
void renderer_item_image::draw_graphics_item (SkCanvas &canvas, const renderer_state &/*state*/, const renderer_config *config) const { SkBitmap bitmap = qt2skia::image (m_image_data); SkPaint paint; paint.setAlpha ((int) (m_opacity * 255)); if (config->render_for_selection ()) { if (!configure_painter_for_selection (paint)) return; canvas.drawRect (qt2skia::rect (m_dst_rect), paint); } else { if (!bitmap.isNull ()) { SkIRect irect = qt2skia::Irect (m_src_rect); canvas.drawBitmapRect (bitmap, &irect, qt2skia::rect (m_dst_rect), &paint); } } }
// Test out SkPictureRecorder::partialReplay DEF_TEST(PictureRecorder_replay, reporter) { // check save/saveLayer state { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); canvas->saveLayer(nullptr, nullptr); sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); // The extra save and restore comes from the Copy process. check_save_state(reporter, copy.get(), 2, 1, 3); canvas->saveLayer(nullptr, nullptr); sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); check_save_state(reporter, final.get(), 1, 2, 3); // The copy shouldn't pick up any operations added after it was made check_save_state(reporter, copy.get(), 2, 1, 3); } // (partially) check leakage of draw ops { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(10, 10); SkRect r = SkRect::MakeWH(5, 5); SkPaint p; canvas->drawRect(r, p); sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); SkBitmap bm; make_bm(&bm, 10, 10, SK_ColorRED, true); r.offset(5.0f, 5.0f); canvas->drawBitmapRect(bm, r, nullptr); sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); REPORTER_ASSERT(reporter, final->willPlayBackBitmaps()); REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID()); // The snapshot shouldn't pick up any operations added after it was made REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps()); } // Recreate the Android partialReplay test case { SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(4, 3, nullptr, 0); create_imbalance(canvas); int expectedSaveCount = canvas->getSaveCount(); sk_sp<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder)); check_balance(reporter, copy.get()); REPORTER_ASSERT(reporter, expectedSaveCount = canvas->getSaveCount()); // End the recording of source to test the picture finalization // process isn't complicated by the partialReplay step sk_sp<SkPicture> final(recorder.finishRecordingAsPicture()); } }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { // First get the subset we need. This is efficient and does not copy pixels. SkBitmap subset; bitmap.extractSubset(&subset, srcIRect); SkRect srcRect; srcRect.set(srcIRect); // Whether we're doing a subset or using the full source image. bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 && srcIRect.width() == bitmap.width() && srcIRect.height() == bitmap.height(); // We will always draw in integer sizes, so round the destination rect. SkIRect destRectRounded; destRect.round(&destRectRounded); SkIRect resizedImageRect = // Represents the size of the resized image. { 0, 0, destRectRounded.width(), destRectRounded.height() }; if (srcIsFull && bitmap.hasResizedBitmap(destRectRounded.width(), destRectRounded.height())) { // Yay, this bitmap frame already has a resized version. SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); canvas.drawBitmapRect(resampled, 0, destRect, &paint); return; } // Compute the visible portion of our rect. SkRect destBitmapSubsetSk; ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); // The matrix inverting, etc. could have introduced rounding error which // causes the bounds to be outside of the resized bitmap. We round outward // so we always lean toward it being larger rather than smaller than we // need, and then clamp to the bitmap bounds so we don't get any invalid // data. SkIRect destBitmapSubsetSkI; destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); if (!destBitmapSubsetSkI.intersect(resizedImageRect)) return; // Resized image does not intersect. if (srcIsFull && bitmap.shouldCacheResampling( resizedImageRect.width(), resizedImageRect.height(), destBitmapSubsetSkI.width(), destBitmapSubsetSkI.height())) { // We're supposed to resize the entire image and cache it, even though // we don't need all of it. SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); canvas.drawBitmapRect(resampled, 0, destRect, &paint); } else { // We should only resize the exposed part of the bitmap to do the // minimal possible work. // Resample the needed part of the image. SkBitmap resampled = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destRectRounded.width(), destRectRounded.height(), destBitmapSubsetSkI); // Compute where the new bitmap should be drawn. Since our new bitmap // may be smaller than the original, we have to shift it over by the // same amount that we cut off the top and left. destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop); SkRect offsetDestRect; offsetDestRect.set(destBitmapSubsetSkI); canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); } }