void SkView::inval(SkRect* rect) { if (!this->isVisible()) return; SkRect bounds; this->getLocalBounds(&bounds); if (rect && !bounds.intersect(*rect)) return; rect = &bounds; SkView* view = this; for (;;) { if (view->handleInval(bounds)) break; SkRect parentR; SkView* parent = view->fParent; if (parent == NULL || !parent->isVisible()) break; bounds.offset(view->fLoc.fX, view->fLoc.fY); parent->getLocalBounds(&parentR); if (!bounds.intersect(parentR)) return; view = parent; } }
void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint) { SkRect deviceClipRect; bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect); if (deviceClipRect.isEmpty()) return; SkRect sourceOpaqueRect = layerOpaqueRect; // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible. SkRect destinationOpaqueRect = currentTrackingOpaqueRect(); bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false); if (!outsideSourceOpaqueRectPreservesOpaque) markRectAsNonOpaque(deviceClipRect); if (!deviceClipIsARect) return; if (!sourceOpaqueRect.intersect(deviceClipRect)) return; bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0); bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque); bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque); // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise, // if it preserves opaque then keep the intersection of the two. if (sourceOpaqueRectXfersOpaque) markRectAsOpaque(sourceOpaqueRect); else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect)) markRectAsOpaque(sourceOpaqueRect); }
// Atlased layers must be small enough to fit in the atlas, not have a // paint with an image filter and be neither nested nor nesting. // TODO: allow leaf nested layers to appear in the atlas. void GrLayerHoister::FindLayersToAtlas(GrContext* context, const SkPicture* topLevelPicture, const SkMatrix& initialMat, const SkRect& query, SkTDArray<GrHoistedLayer>* atlased, SkTDArray<GrHoistedLayer>* recycled, int numSamples) { if (0 != numSamples) { // MSAA layers are currently never atlased return; } GrLayerCache* layerCache = context->getLayerCache(); layerCache->processDeletedPictures(); SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); if (!topLevelData) { return; } const SkLayerInfo *topLevelGPUData = static_cast<const SkLayerInfo*>(topLevelData); if (0 == topLevelGPUData->numBlocks()) { return; } atlased->setReserve(atlased->count() + topLevelGPUData->numBlocks()); for (int i = 0; i < topLevelGPUData->numBlocks(); ++i) { const SkLayerInfo::BlockInfo& info = topLevelGPUData->block(i); // TODO: ignore perspective projected layers here? bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested || (info.fPaint && info.fPaint->getImageFilter()); if (disallowAtlasing) { continue; } SkRect layerRect; initialMat.mapRect(&layerRect, info.fBounds); if (!layerRect.intersect(query)) { continue; } const SkIRect dstIR = layerRect.roundOut(); SkIRect srcIR; if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) { continue; } prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, srcIR, dstIR, atlased, recycled, true, 0); } }
void SkPictureImageFilter::drawPictureAtLocalResolution(Proxy* proxy, SkBaseDevice* device, const SkIRect& deviceBounds, const Context& ctx) const { SkMatrix inverseCtm; if (!ctx.ctm().invert(&inverseCtm)) return; SkRect localBounds = SkRect::Make(ctx.clipBounds()); inverseCtm.mapRect(&localBounds); if (!localBounds.intersect(fCropRect)) return; SkIRect localIBounds = localBounds.roundOut(); SkAutoTUnref<SkBaseDevice> localDevice(proxy->createDevice(localIBounds.width(), localIBounds.height())); // Pass explicit surface props, as the simplified canvas constructor discards device properties. // FIXME: switch back to the public constructor (and unfriend) after // https://code.google.com/p/skia/issues/detail?id=3142 is fixed. SkCanvas localCanvas(localDevice, &proxy->surfaceProps(), SkCanvas::kDefault_InitFlags); localCanvas.translate(-SkIntToScalar(localIBounds.fLeft), -SkIntToScalar(localIBounds.fTop)); localCanvas.drawPicture(fPicture); // Pass explicit surface props, as the simplified canvas constructor discards device properties. // FIXME: switch back to the public constructor (and unfriend) after // https://code.google.com/p/skia/issues/detail?id=3142 is fixed. SkCanvas canvas(device, &proxy->surfaceProps(), SkCanvas::kDefault_InitFlags); canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop)); canvas.concat(ctx.ctm()); SkPaint paint; paint.setFilterQuality(fFilterQuality); canvas.drawBitmap(localDevice.get()->accessBitmap(false), SkIntToScalar(localIBounds.fLeft), SkIntToScalar(localIBounds.fTop), &paint); //canvas.drawPicture(fPicture); }
void OpaqueRegionSkia::didDraw(const PlatformContextSkia* context, const AffineTransform& transform, const SkRect& rect, const SkPaint& paint, bool drawsOpaque, bool fillsBounds) { SkRect targetRect = rect; // Apply the transform to device coordinate space. SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); if (!canvasTransform.mapRect(&targetRect)) fillsBounds = false; // Apply the current clip in device coordinate space. if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType) fillsBounds = false; else { SkIRect deviceClip; context->canvas()->getClipDeviceBounds(&deviceClip); if (!targetRect.intersect(SkIntToScalar(deviceClip.fLeft), SkIntToScalar(deviceClip.fTop), SkIntToScalar(deviceClip.fRight), SkIntToScalar(deviceClip.fBottom))) return; } if (!context->clippedToImage().isOpaque()) fillsBounds = false; // Apply the transform to the tracking space. SkMatrix canvasToTargetTransform = transform; if (!canvasToTargetTransform.mapRect(&targetRect)) fillsBounds = false; if (fillsBounds && xfermodeIsOpaque(paint, drawsOpaque)) markRectAsOpaque(targetRect); else if (SkRect::Intersects(targetRect, m_opaqueRect) && !xfermodePreservesOpaque(paint, drawsOpaque)) markRectAsNonOpaque(targetRect); }
SkCanvas* SkDocument::beginPage(SkScalar width, SkScalar height, const SkRect* content) { if (width <= 0 || height <= 0) { return nullptr; } SkRect outer = SkRect::MakeWH(width, height); SkRect inner; if (content) { inner = *content; if (!inner.intersect(outer)) { return nullptr; } } else { inner = outer; } for (;;) { switch (fState) { case kBetweenPages_State: fState = kInPage_State; return this->onBeginPage(width, height, inner); case kInPage_State: this->endPage(); break; case kClosed_State: return nullptr; } } SkDEBUGFAIL("never get here"); return nullptr; }
void SkPictureImageFilter::drawPictureAtLocalResolution(Proxy* proxy, SkBaseDevice* device, const SkIRect& deviceBounds, const Context& ctx) const { SkMatrix inverseCtm; if (!ctx.ctm().invert(&inverseCtm)) { return; } SkRect localBounds = SkRect::Make(ctx.clipBounds()); inverseCtm.mapRect(&localBounds); if (!localBounds.intersect(fCropRect)) { return; } SkIRect localIBounds = localBounds.roundOut(); SkAutoTUnref<SkBaseDevice> localDevice(proxy->createDevice(localIBounds.width(), localIBounds.height())); SkCanvas localCanvas(localDevice); localCanvas.translate(-SkIntToScalar(localIBounds.fLeft), -SkIntToScalar(localIBounds.fTop)); localCanvas.drawPicture(fPicture); SkCanvas canvas(device); canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop)); canvas.concat(ctx.ctm()); SkPaint paint; paint.setFilterQuality(fFilterQuality); canvas.drawBitmap(localDevice.get()->accessBitmap(false), SkIntToScalar(localIBounds.fLeft), SkIntToScalar(localIBounds.fTop), &paint); }
void OpaqueRegionSkia::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType) { SkRect targetRect = rect; // Apply the transform to device coordinate space. SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); if (!canvasTransform.mapRect(&targetRect)) fillsBounds = false; // Apply the current clip. SkRect deviceClipRect; if (!getDeviceClipAsRect(context, deviceClipRect)) fillsBounds = false; else if (!targetRect.intersect(deviceClipRect)) return; bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap); bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque); bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); if (fillsBounds && xfersOpaque) markRectAsOpaque(targetRect); else if (!preservesOpaque) markRectAsNonOpaque(targetRect); }
// Adjust rect for all paints that may affect its geometry, then map it to identity space. Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const { // Inverted rectangles really confuse our BBHs. rect.sort(); // Adjust the rect for its own paint. if (!AdjustForPaint(paint, &rect)) { // The paint could do anything to our bounds. The only safe answer is the current clip. return fCurrentClipBounds; } // Adjust rect for all the paints from the SaveLayers we're inside. if (!this->adjustForSaveLayerPaints(&rect)) { // Same deal as above. return fCurrentClipBounds; } // Map the rect back to identity space. fCTM.mapRect(&rect); // Nothing can draw outside the current clip. if (!rect.intersect(fCurrentClipBounds)) { return Bounds::MakeEmpty(); } return rect; }
bool SkHitTest::draw(SkAnimateMaker& maker) { hits.setCount(bullets.count()); value = false; int bulletCount = bullets.count(); int targetCount = targets.count(); for (int bIndex = 0; bIndex < bulletCount; bIndex++) { SkDisplayable* bullet = bullets[bIndex]; SkRect bBounds; bullet->getBounds(&bBounds); hits[bIndex] = -1; if (bBounds.fLeft == (int16_t)0x8000U) continue; for (int tIndex = 0; tIndex < targetCount; tIndex++) { SkDisplayable* target = targets[tIndex]; SkRect tBounds; target->getBounds(&tBounds); if (bBounds.intersect(tBounds)) { hits[bIndex] = tIndex; value = true; break; } } } return false; }
// 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. void NativeImageSkia::drawResampledBitmap(GraphicsContext* context, SkPaint& paint, const SkRect& srcRect, const SkRect& destRect) const { TRACE_EVENT0("skia", "drawResampledBitmap"); // 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; context->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; if (!context->canvas()->getClipBounds(&destRectVisibleSubset)) return; // 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; SkBitmap scaledImageFragment = extractScaledImageFragment(srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect); context->drawBitmapRect(scaledImageFragment, &scaledSrcRect, destRectVisibleSubset, &paint); }
void SkView::inval(SkRect* rect) { SkView* view = this; SkRect storage; for (;;) { if (!view->isVisible()) { return; } if (view->isClipToBounds()) { SkRect bounds; view->getLocalBounds(&bounds); if (rect && !bounds.intersect(*rect)) { return; } storage = bounds; rect = &storage; } if (view->handleInval(rect)) { return; } SkView* parent = view->fParent; if (parent == NULL) { return; } if (rect) { rect->offset(view->fLoc.fX, view->fLoc.fY); } view = parent; } }
void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no canvas or surface!"); SkRect dirty; mDamageAccumulator.finish(&dirty); // TODO: Re-enable after figuring out cause of b/22592975 // if (dirty.isEmpty() && Properties::skipEmptyFrames) { // mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); // return; // } mCurrentFrameInfo->markIssueDrawCommandsStart(); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } if (!dirty.isEmpty()) { mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { mCanvas->prepare(mOpaque); } Rect outBounds; mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); bool drew = mCanvas->finish(); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); if (drew) { swapBuffers(dirty, width, height); } // TODO: Use a fence for real completion? mCurrentFrameInfo->markFrameCompleted(); mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); }
// 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 SkScan::FillPath(const SkPath& path, const SkRegion& origClip, SkBlitter* blitter) { if (origClip.isEmpty()) { return; } // Our edges are fixed-point, and don't like the bounds of the clip to // exceed that. Here we trim the clip just so we don't overflow later on const SkRegion* clipPtr = &origClip; SkRegion finiteClip; if (clip_to_limit(origClip, &finiteClip)) { if (finiteClip.isEmpty()) { return; } clipPtr = &finiteClip; } // don't reference "origClip" any more, just use clipPtr SkRect bounds = path.getBounds(); bool irPreClipped = false; if (!SkRectPriv::MakeLargeS32().contains(bounds)) { if (!bounds.intersect(SkRectPriv::MakeLargeS32())) { bounds.setEmpty(); } irPreClipped = true; } SkIRect ir = conservative_round_to_int(bounds); if (ir.isEmpty()) { if (path.isInverseFillType()) { blitter->blitRegion(*clipPtr); } return; } SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType(), irPreClipped); blitter = clipper.getBlitter(); if (blitter) { // we have to keep our calls to blitter in sorted order, so we // must blit the above section first, then the middle, then the bottom. if (path.isInverseFillType()) { sk_blit_above(blitter, ir, *clipPtr); } SkASSERT(clipper.getClipRect() == nullptr || *clipper.getClipRect() == clipPtr->getBounds()); sk_fill_path(path, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom, 0, clipper.getClipRect() == nullptr); if (path.isInverseFillType()) { sk_blit_below(blitter, ir, *clipPtr); } } else { // what does it mean to not have a blitter if path.isInverseFillType??? } }
void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer, const SkRect* srcRect, const SkRect* dstRect, SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix, const GrClip& clip, const SkPaint& paint) { // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry. SK_HISTOGRAM_BOOLEAN("DrawTiled", false); // Figure out the actual dst and src rect by clipping the src rect to the bounds of the // adjuster. If the src rect is clipped then the dst rect must be recomputed. Also determine // the matrix that maps the src rect to the dst rect. SkRect clippedSrcRect; SkRect clippedDstRect; const SkRect srcBounds = SkRect::MakeIWH(producer->width(), producer->height()); SkMatrix srcToDstMatrix; if (srcRect) { if (!dstRect) { dstRect = &srcBounds; } if (!srcBounds.contains(*srcRect)) { clippedSrcRect = *srcRect; if (!clippedSrcRect.intersect(srcBounds)) { return; } if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } srcToDstMatrix.mapRect(&clippedDstRect, clippedSrcRect); } else { clippedSrcRect = *srcRect; clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(*srcRect, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } } else { clippedSrcRect = srcBounds; if (dstRect) { clippedDstRect = *dstRect; if (!srcToDstMatrix.setRectToRect(srcBounds, *dstRect, SkMatrix::kFill_ScaleToFit)) { return; } } else { clippedDstRect = srcBounds; srcToDstMatrix.reset(); } } // Now that we have both the view and srcToDst matrices, log our scale factor. LogDrawScaleFactor(SkMatrix::Concat(viewMatrix, srcToDstMatrix), paint.getFilterQuality()); this->drawTextureProducerImpl(producer, clippedSrcRect, clippedDstRect, constraint, viewMatrix, srcToDstMatrix, clip, paint); }
void onDraw(const int loops, SkCanvas* canvas) override { for (int outer = 0; outer < loops; ++outer) { int count = 0; SkRect r; for (size_t i = 0; i < SK_ARRAY_COUNT(fRects); ++i) { count += r.intersect(fRects[0], fRects[i]); } this->virtualCallToFoilOptimizers(count); } }
static SkCanvas* trim(SkCanvas* canvas, SkScalar width, SkScalar height, const SkRect* content) { if (content && canvas) { SkRect inner = *content; if (!inner.intersect({0, 0, width, height})) { return nullptr; } canvas->clipRect(inner); canvas->translate(inner.x(), inner.y()); } return canvas; }
void OpaqueRegionSkia::didDraw(const PlatformContextSkia* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType) { SkRect targetRect = rect; // Apply the transform to device coordinate space. SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); if (!canvasTransform.mapRect(&targetRect)) fillsBounds = false; // Apply the current clip in device coordinate space. if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType) fillsBounds = false; else { SkIRect deviceClip; if (!context->canvas()->getClipDeviceBounds(&deviceClip)) return; if (!targetRect.intersect(SkIntToScalar(deviceClip.fLeft), SkIntToScalar(deviceClip.fTop), SkIntToScalar(deviceClip.fRight), SkIntToScalar(deviceClip.fBottom))) return; } bool checkFillOnly = drawType == FillOnly; bool lastLayerDrawsOpaque = paintIsOpaque(paint, sourceBitmap, checkFillOnly); bool xfersOpaque = xfermodeIsOpaque(paint, lastLayerDrawsOpaque); // Apply the SkCanvas layers we will be drawing through. for (size_t i = m_canvasLayerStack.size(); i > 0; --i) { const CanvasLayerState& canvasLayer = m_canvasLayerStack[i-1]; // FIXME: We could still track the opaque part but it's always empty right now anyways. if (canvasLayer.hasImageMask && !canvasLayer.imageOpaqueRect.contains(targetRect)) fillsBounds = false; bool checkFillOnly = drawType == FillOnly; lastLayerDrawsOpaque = paintIsOpaque(canvasLayer.paint, 0, checkFillOnly); // If any layer doesn't paint opaque, then the result will not be opaque. xfersOpaque &= xfermodeIsOpaque(canvasLayer.paint, lastLayerDrawsOpaque); } // Preserving opaque only matters for the bottom-most layer. Its contents are either opaque or not, and if not // then we care if it preserves the opaqueness of the target device when it is drawn into the device. bool preservesOpaque; if (m_canvasLayerStack.isEmpty()) preservesOpaque = xfermodePreservesOpaque(paint, lastLayerDrawsOpaque); else preservesOpaque = xfermodePreservesOpaque(m_canvasLayerStack[0].paint, lastLayerDrawsOpaque); if (fillsBounds && xfersOpaque) markRectAsOpaque(targetRect); else if (SkRect::Intersects(targetRect, m_opaqueRect) && !preservesOpaque) markRectAsNonOpaque(targetRect); }
void LayerAndroid::clipInner(SkTDArray<SkRect>* region, const SkRect& local) const { SkRect localBounds; bounds(&localBounds); localBounds.intersect(local); if (localBounds.isEmpty()) return; if (m_recordingPicture && boundsIsUnique(*region, localBounds)) *region->append() = localBounds; for (int i = 0; i < countChildren(); i++) getChild(i)->clipInner(region, m_haveClip ? localBounds : local); }
// Atlased layers must be small enough to fit in the atlas, not have a // paint with an image filter and be neither nested nor nesting. // TODO: allow leaf nested layers to appear in the atlas. void GrLayerHoister::FindLayersToAtlas(GrContext* context, const SkPicture* topLevelPicture, const SkRect& query, SkTDArray<GrHoistedLayer>* atlased, SkTDArray<GrHoistedLayer>* recycled) { GrLayerCache* layerCache = context->getLayerCache(); layerCache->processDeletedPictures(); SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); if (!topLevelData) { return; } const GrAccelData *topLevelGPUData = static_cast<const GrAccelData*>(topLevelData); if (0 == topLevelGPUData->numSaveLayers()) { return; } atlased->setReserve(atlased->count() + topLevelGPUData->numSaveLayers()); for (int i = 0; i < topLevelGPUData->numSaveLayers(); ++i) { const GrAccelData::SaveLayerInfo& info = topLevelGPUData->saveLayerInfo(i); // TODO: ignore perspective projected layers here? bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested || (info.fPaint && info.fPaint->getImageFilter()); if (disallowAtlasing) { continue; } SkRect layerRect = info.fBounds; if (!layerRect.intersect(query)) { continue; } SkIRect ir; layerRect.roundOut(&ir); if (!GrLayerCache::PlausiblyAtlasable(ir.width(), ir.height())) { continue; } prepare_for_hoisting(layerCache, topLevelPicture, info, ir, atlased, recycled, true); } }
void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); SkRect dirty; mDamageAccumulator.finish(&dirty); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { //ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", // SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } status_t status; if (!dirty.isEmpty()) { status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { status = mCanvas->prepare(mOpaque); } Rect outBounds; status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); mCanvas->finish(); profiler().markPlaybackEnd(); if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } else { mEglManager.cancelFrame(); } profiler().finishFrame(); }
void GrLayerHoister::FindLayersToHoist(GrContext* context, const SkPicture* topLevelPicture, const SkMatrix& initialMat, const SkRect& query, SkTDArray<GrHoistedLayer>* needRendering, SkTDArray<GrHoistedLayer>* recycled, int numSamples) { GrLayerCache* layerCache = context->getLayerCache(); layerCache->processDeletedPictures(); const SkBigPicture::AccelData* topLevelData = nullptr; if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { topLevelData = bp->accelData(); } if (!topLevelData) { return; } const SkLayerInfo *topLevelGPUData = static_cast<const SkLayerInfo*>(topLevelData); if (0 == topLevelGPUData->numBlocks()) { return; } // Find and prepare for hoisting all the layers that intersect the query rect for (int i = 0; i < topLevelGPUData->numBlocks(); ++i) { const SkLayerInfo::BlockInfo& info = topLevelGPUData->block(i); if (info.fIsNested) { // Parent layers are currently hoisted while nested layers are not. continue; } SkRect layerRect; initialMat.mapRect(&layerRect, info.fBounds); if (!layerRect.intersect(query)) { continue; } const SkIRect dstIR = layerRect.roundOut(); SkIRect srcIR; if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) { continue; } prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, srcIR, dstIR, needRendering, recycled, false, numSamples); } }
void SkPictureImageFilter::drawPictureAtLocalResolution(SkSpecialImage* source, SkCanvas* canvas, const SkIRect& deviceBounds, const Context& ctx) const { SkMatrix inverseCtm; if (!ctx.ctm().invert(&inverseCtm)) { return; } SkRect localBounds = SkRect::Make(ctx.clipBounds()); inverseCtm.mapRect(&localBounds); if (!localBounds.intersect(fCropRect)) { return; } SkIRect localIBounds = localBounds.roundOut(); sk_sp<SkSpecialImage> localImg; { const SkImageInfo info = SkImageInfo::MakeN32(localIBounds.width(), localIBounds.height(), kPremul_SkAlphaType); sk_sp<SkSpecialSurface> localSurface(source->makeSurface(info)); if (!localSurface) { return; } SkCanvas* localCanvas = localSurface->getCanvas(); SkASSERT(localCanvas); localCanvas->translate(-SkIntToScalar(localIBounds.fLeft), -SkIntToScalar(localIBounds.fTop)); localCanvas->drawPicture(fPicture); localImg = localSurface->makeImageSnapshot(); SkASSERT(localImg); } { canvas->translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop)); canvas->concat(ctx.ctm()); SkPaint paint; paint.setFilterQuality(fFilterQuality); localImg->draw(canvas, SkIntToScalar(localIBounds.fLeft), SkIntToScalar(localIBounds.fTop), &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_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); }
SkImageFilter* SkTileImageFilter::Create(const SkRect& srcRect, const SkRect& dstRect, SkImageFilter* input) { if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) { return nullptr; } if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) { SkRect ir = dstRect; if (!ir.intersect(srcRect)) { return SkSafeRef(input); } CropRect cropRect(ir); return SkOffsetImageFilter::Create(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y(), input, &cropRect); } return new SkTileImageFilter(srcRect, dstRect, input); }
sk_sp<SkImageFilter> SkTileImageFilter::Make(const SkRect& srcRect, const SkRect& dstRect, sk_sp<SkImageFilter> input) { if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) { return nullptr; } if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) { SkRect ir = dstRect; if (!ir.intersect(srcRect)) { return input; } CropRect cropRect(ir); return SkOffsetImageFilter::Make(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y(), std::move(input), &cropRect); } return sk_sp<SkImageFilter>(new SkTileImageFilter(srcRect, dstRect, std::move(input))); }
void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context) { ASSERT(!m_canvasLayerStack.isEmpty()); if (m_canvasLayerStack.isEmpty()) return; const CanvasLayerState& canvasLayer = m_canvasLayerStack.last(); SkRect layerOpaqueRect = canvasLayer.opaqueRect; SkPaint layerPaint = canvasLayer.paint; // Apply the image mask. if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect)) layerOpaqueRect.setEmpty(); m_canvasLayerStack.removeLast(); applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint); }
// 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); }
bool Tile::intersectWithRect(int x, int y, int tileWidth, int tileHeight, float scale, const SkRect& dirtyRect, SkRect& realTileRect) { // compute the rect to corresponds to pixels realTileRect.fLeft = x * tileWidth; realTileRect.fTop = y * tileHeight; realTileRect.fRight = realTileRect.fLeft + tileWidth; realTileRect.fBottom = realTileRect.fTop + tileHeight; // scale the dirtyRect for intersect computation. SkRect realDirtyRect = SkRect::MakeWH(dirtyRect.width() * scale, dirtyRect.height() * scale); realDirtyRect.offset(dirtyRect.fLeft * scale, dirtyRect.fTop * scale); if (!realTileRect.intersect(realDirtyRect)) return false; return true; }