/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/SkGpuDevice.h" #include "include/core/SkImageFilter.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPicture.h" #include "include/core/SkSurface.h" #include "include/core/SkVertices.h" #include "include/effects/SkRuntimeEffect.h" #include "include/gpu/GrDirectContext.h" #include "include/gpu/GrRecordingContext.h" #include "include/private/SkShadowFlags.h" #include "include/private/SkTo.h" #include "src/core/SkCanvasPriv.h" #include "src/core/SkClipStack.h" #include "src/core/SkDraw.h" #include "src/core/SkImageFilterCache.h" #include "src/core/SkImageFilter_Base.h" #include "src/core/SkLatticeIter.h" #include "src/core/SkPictureData.h" #include "src/core/SkRRectPriv.h" #include "src/core/SkRasterClip.h" #include "src/core/SkRecord.h" #include "src/core/SkStroke.h" #include "src/core/SkTLazy.h" #include "src/core/SkVerticesPriv.h" #include "src/gpu/GrBlurUtils.h" #include "src/gpu/GrDirectContextPriv.h" #include "src/gpu/GrGpu.h" #include "src/gpu/GrRecordingContextPriv.h" #include "src/gpu/GrStyle.h" #include "src/gpu/GrSurfaceProxyPriv.h" #include "src/gpu/GrTracing.h" #include "src/gpu/SkGr.h" #include "src/gpu/effects/GrRRectEffect.h" #include "src/gpu/geometry/GrStyledShape.h" #include "src/image/SkImage_Base.h" #include "src/image/SkReadPixelsRec.h" #include "src/image/SkSurface_Gpu.h" #include "src/utils/SkUTF.h" #define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(fContext->priv().singleOwner()) /////////////////////////////////////////////////////////////////////////////// /** Checks that the alpha type is legal and gets constructor flags. Returns false if device creation should fail. */ bool SkGpuDevice::CheckAlphaTypeAndGetFlags( const SkImageInfo* info, SkGpuDevice::InitContents init, unsigned* flags) { *flags = 0; if (info) { switch (info->alphaType()) { case kPremul_SkAlphaType: break; case kOpaque_SkAlphaType: *flags |= SkGpuDevice::kIsOpaque_Flag; break; default: // If it is unpremul or unknown don't try to render return false; } } if (kClear_InitContents == init) { *flags |= kNeedClear_Flag; } return true; } sk_sp SkGpuDevice::Make(std::unique_ptr surfaceDrawContext, InitContents init) { if (!surfaceDrawContext) { return nullptr; } GrRecordingContext* rContext = surfaceDrawContext->recordingContext(); if (rContext->abandoned()) { return nullptr; } SkColorType ct = GrColorTypeToSkColorType(surfaceDrawContext->colorInfo().colorType()); unsigned flags; if (!rContext->colorTypeSupportedAsSurface(ct) || !CheckAlphaTypeAndGetFlags(nullptr, init, &flags)) { return nullptr; } return sk_sp(new SkGpuDevice(std::move(surfaceDrawContext), flags)); } sk_sp SkGpuDevice::Make(GrRecordingContext* context, SkBudgeted budgeted, const SkImageInfo& info, int sampleCount, GrSurfaceOrigin origin, const SkSurfaceProps* props, GrMipmapped mipMapped, InitContents init) { unsigned flags; if (!context->colorTypeSupportedAsSurface(info.colorType()) || !CheckAlphaTypeAndGetFlags(&info, init, &flags)) { return nullptr; } auto surfaceDrawContext = MakeSurfaceDrawContext(context, budgeted, info, sampleCount, origin, props, mipMapped); if (!surfaceDrawContext) { return nullptr; } return sk_sp(new SkGpuDevice(std::move(surfaceDrawContext), flags)); } static SkImageInfo make_info(GrSurfaceDrawContext* context, bool opaque) { SkColorType colorType = GrColorTypeToSkColorType(context->colorInfo().colorType()); return SkImageInfo::Make(context->width(), context->height(), colorType, opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType, context->colorInfo().refColorSpace()); } #if !defined(SK_DISABLE_NEW_GR_CLIP_STACK) static bool force_aa_clip(const GrSurfaceDrawContext* sdc) { return (sdc->numSamples() > 1 && !sdc->caps()->multisampleDisableSupport()) || sdc->alwaysAntialias(); } #endif SkGpuDevice::SkGpuDevice(std::unique_ptr surfaceDrawContext, unsigned flags) : INHERITED(make_info(surfaceDrawContext.get(), SkToBool(flags & kIsOpaque_Flag)), surfaceDrawContext->surfaceProps()) , fContext(sk_ref_sp(surfaceDrawContext->recordingContext())) , fSurfaceDrawContext(std::move(surfaceDrawContext)) #if !defined(SK_DISABLE_NEW_GR_CLIP_STACK) , fClip(SkIRect::MakeSize(fSurfaceDrawContext->dimensions()), &this->asMatrixProvider(), force_aa_clip(fSurfaceDrawContext.get())) { #else , fClip(fSurfaceDrawContext->dimensions(), &this->cs(), &this->asMatrixProvider()) { #endif if (flags & kNeedClear_Flag) { this->clearAll(); } } std::unique_ptr SkGpuDevice::MakeSurfaceDrawContext( GrRecordingContext* context, SkBudgeted budgeted, const SkImageInfo& origInfo, int sampleCount, GrSurfaceOrigin origin, const SkSurfaceProps* surfaceProps, GrMipmapped mipmapped) { if (!context) { return nullptr; } // This method is used to create SkGpuDevice's for SkSurface_Gpus. In this case // they need to be exact. return GrSurfaceDrawContext::Make( context, SkColorTypeToGrColorType(origInfo.colorType()), origInfo.refColorSpace(), SkBackingFit::kExact, origInfo.dimensions(), SkSurfacePropsCopyOrDefault(surfaceProps), sampleCount, mipmapped, GrProtected::kNo, origin, budgeted); } /////////////////////////////////////////////////////////////////////////////// bool SkGpuDevice::onReadPixels(const SkPixmap& pm, int x, int y) { ASSERT_SINGLE_OWNER // Context TODO: Elevate direct context requirement to public API auto dContext = fContext->asDirectContext(); if (!dContext || !SkImageInfoValidConversion(pm.info(), this->imageInfo())) { return false; } return fSurfaceDrawContext->readPixels(dContext, pm, {x, y}); } bool SkGpuDevice::onWritePixels(const SkPixmap& pm, int x, int y) { ASSERT_SINGLE_OWNER // Context TODO: Elevate direct context requirement to public API auto dContext = fContext->asDirectContext(); if (!dContext || !SkImageInfoValidConversion(this->imageInfo(), pm.info())) { return false; } return fSurfaceDrawContext->writePixels(dContext, pm, {x, y}); } bool SkGpuDevice::onAccessPixels(SkPixmap* pmap) { ASSERT_SINGLE_OWNER return false; } GrSurfaceDrawContext* SkGpuDevice::surfaceDrawContext() { ASSERT_SINGLE_OWNER return fSurfaceDrawContext.get(); } void SkGpuDevice::clearAll() { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "clearAll", fContext.get()); SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); fSurfaceDrawContext->clearAtLeast(rect, SK_PMColor4fTRANSPARENT); } void SkGpuDevice::replaceSurfaceDrawContext(std::unique_ptr sdc, SkSurface::ContentChangeMode mode) { SkASSERT(sdc->dimensions() == fSurfaceDrawContext->dimensions()); SkASSERT(sdc->numSamples() == fSurfaceDrawContext->numSamples()); SkASSERT(sdc->asSurfaceProxy()->priv().isExact()); if (mode == SkSurface::kRetain_ContentChangeMode) { if (this->recordingContext()->abandoned()) { return; } SkASSERT(fSurfaceDrawContext->asTextureProxy()); SkAssertResult(sdc->blitTexture(fSurfaceDrawContext->readSurfaceView(), SkIRect::MakeWH(this->width(), this->height()), SkIPoint::Make(0, 0))); } fSurfaceDrawContext = std::move(sdc); } void SkGpuDevice::replaceSurfaceDrawContext(SkSurface::ContentChangeMode mode) { ASSERT_SINGLE_OWNER SkBudgeted budgeted = fSurfaceDrawContext->isBudgeted(); // This entry point is used by SkSurface_Gpu::onCopyOnWrite so it must create a // kExact-backed surface draw context auto newSDC = MakeSurfaceDrawContext(this->recordingContext(), budgeted, this->imageInfo(), fSurfaceDrawContext->numSamples(), fSurfaceDrawContext->origin(), &this->surfaceProps(), fSurfaceDrawContext->mipmapped()); if (!newSDC) { return; } this->replaceSurfaceDrawContext(std::move(newSDC), mode); } /////////////////////////////////////////////////////////////////////////////// #if !defined(SK_DISABLE_NEW_GR_CLIP_STACK) void SkGpuDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) { #if GR_TEST_UTILS if (fContext->priv().options().fAllPathsVolatile && !path.isVolatile()) { this->onClipPath(SkPath(path).setIsVolatile(true), op, aa); return; } #endif SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference); fClip.clipPath(this->localToDevice(), path, GrAA(aa), op); } void SkGpuDevice::onClipRegion(const SkRegion& globalRgn, SkClipOp op) { SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference); // Regions don't actually need AA, but in DMSAA mode every clip element is antialiased. GrAA aa = GrAA(fSurfaceDrawContext->alwaysAntialias()); if (globalRgn.isEmpty()) { fClip.clipRect(SkMatrix::I(), SkRect::MakeEmpty(), aa, op); } else if (globalRgn.isRect()) { fClip.clipRect(this->globalToDevice(), SkRect::Make(globalRgn.getBounds()), aa, op); } else { SkPath path; globalRgn.getBoundaryPath(&path); fClip.clipPath(this->globalToDevice(), path, aa, op); } } void SkGpuDevice::onAsRgnClip(SkRegion* region) const { SkRegion deviceBounds(fClip.getConservativeBounds()); for (const GrClipStack::Element& e : fClip) { SkRegion tmp; if (e.fShape.isRect() && e.fLocalToDevice.isIdentity()) { tmp.setRect(e.fShape.rect().roundOut()); } else { SkPath tmpPath; e.fShape.asPath(&tmpPath); tmpPath.transform(e.fLocalToDevice); tmp.setPath(tmpPath, deviceBounds); } region->op(tmp, (SkRegion::Op) e.fOp); } } bool SkGpuDevice::onClipIsAA() const { for (const GrClipStack::Element& e : fClip) { if (e.fAA == GrAA::kYes) { return true; } SkASSERT(!fSurfaceDrawContext->alwaysAntialias()); } return false; } SkBaseDevice::ClipType SkGpuDevice::onGetClipType() const { GrClipStack::ClipState state = fClip.clipState(); if (state == GrClipStack::ClipState::kEmpty) { return ClipType::kEmpty; } else if (state == GrClipStack::ClipState::kDeviceRect || state == GrClipStack::ClipState::kWideOpen) { return ClipType::kRect; } else { return ClipType::kComplex; } } #endif /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawPaint(const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPaint", fContext.get()); GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawPaint(this->clip(), std::move(grPaint), this->localToDevice()); } static inline GrPrimitiveType point_mode_to_primitive_type(SkCanvas::PointMode mode) { switch (mode) { case SkCanvas::kPoints_PointMode: return GrPrimitiveType::kPoints; case SkCanvas::kLines_PointMode: return GrPrimitiveType::kLines; case SkCanvas::kPolygon_PointMode: return GrPrimitiveType::kLineStrip; } SK_ABORT("Unexpected mode"); } void SkGpuDevice::drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPoints", fContext.get()); SkScalar width = paint.getStrokeWidth(); if (width < 0) { return; } GrAA aa = fSurfaceDrawContext->chooseAA(paint); if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) { GrStyle style(paint, SkPaint::kStroke_Style); GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } SkPath path; path.setIsVolatile(true); path.moveTo(pts[0]); path.lineTo(pts[1]); fSurfaceDrawContext->drawPath(this->clip(), std::move(grPaint), aa, this->localToDevice(), path, style); return; } SkScalar scales[2]; bool isHairline = (0 == width) || (1 == width && this->localToDevice().getMinMaxScales(scales) && SkScalarNearlyEqual(scales[0], 1.f) && SkScalarNearlyEqual(scales[1], 1.f)); // we only handle non-antialiased hairlines and paints without path effects or mask filters, // else we let the SkDraw call our drawPath() if (!isHairline || paint.getPathEffect() || paint.getMaskFilter() || aa == GrAA::kYes) { SkRasterClip rc(this->devClipBounds()); SkDraw draw; draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(this->width(), this->height()), nullptr, 0); draw.fMatrixProvider = this; draw.fRC = &rc; draw.drawPoints(mode, count, pts, paint, this); return; } GrPrimitiveType primitiveType = point_mode_to_primitive_type(mode); const SkMatrixProvider* matrixProvider = this; #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK SkTLazy postTranslateMatrixProvider; // This offsetting in device space matches the expectations of the Android framework for non-AA // points and lines. if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) { static const SkScalar kOffset = 0.063f; // Just greater than 1/16. matrixProvider = postTranslateMatrixProvider.init(*matrixProvider, kOffset, kOffset); } #endif GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, *matrixProvider, &grPaint)) { return; } static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode; sk_sp vertices = SkVertices::MakeCopy(kIgnoredMode, SkToS32(count), pts, nullptr, nullptr); fSurfaceDrawContext->drawVertices(this->clip(), std::move(grPaint), *matrixProvider, std::move(vertices), &primitiveType); } /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawRect(const SkRect& rect, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRect", fContext.get()); GrStyle style(paint); // A couple reasons we might need to call drawPath. if (paint.getMaskFilter() || paint.getPathEffect()) { GrStyledShape shape(rect, style); GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(), paint, this->asMatrixProvider(), shape); return; } GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawRect(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), rect, &style); } void SkGpuDevice::drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], SkCanvas::QuadAAFlags aaFlags, const SkColor4f& color, SkBlendMode mode) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawEdgeAAQuad", fContext.get()); SkPMColor4f dstColor = SkColor4fPrepForDst(color, fSurfaceDrawContext->colorInfo()).premul(); GrPaint grPaint; grPaint.setColor4f(dstColor); if (mode != SkBlendMode::kSrcOver) { grPaint.setXPFactory(SkBlendMode_AsXPFactory(mode)); } // This is exclusively meant for tiling operations, so keep AA enabled to handle MSAA seaming GrQuadAAFlags grAA = SkToGrQuadAAFlags(aaFlags); if (clip) { // Use fillQuadWithEdgeAA fSurfaceDrawContext->fillQuadWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA, this->localToDevice(), clip, nullptr); } else { // Use fillRectWithEdgeAA to preserve mathematical properties of dst being rectangular fSurfaceDrawContext->fillRectWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA, this->localToDevice(), rect); } } /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRRect", fContext.get()); SkMaskFilterBase* mf = as_MFB(paint.getMaskFilter()); if (mf) { if (mf->hasFragmentProcessor()) { mf = nullptr; // already handled in SkPaintToGrPaint } } GrStyle style(paint); if (mf || style.pathEffect()) { // A path effect will presumably transform this rrect into something else. GrStyledShape shape(rrect, style); GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(), paint, this->asMatrixProvider(), shape); return; } SkASSERT(!style.pathEffect()); GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawRRect(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), rrect, style); } static std::unique_ptr make_inverse_rrect_fp(const SkMatrix& viewMatrix, const SkRRect& rrect, GrAA aa, const GrShaderCaps& shaderCaps) { SkTCopyOnFirstWrite devRRect(rrect); if (viewMatrix.isIdentity() || rrect.transform(viewMatrix, devRRect.writable())) { auto edgeType = (aa == GrAA::kYes) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW; auto [success, fp] = GrRRectEffect::Make(/*inputFP=*/nullptr, edgeType, *devRRect, shaderCaps); return (success) ? std::move(fp) : nullptr; } return nullptr; } void SkGpuDevice::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDRRect", fContext.get()); if (outer.isEmpty()) { return; } if (inner.isEmpty()) { return this->drawRRect(outer, paint); } SkStrokeRec stroke(paint); if (stroke.isFillStyle() && !paint.getMaskFilter() && !paint.getPathEffect()) { // For axis-aligned filled DRRects, just draw a regular rrect with inner clipped out using a // coverage FP instead of using path rendering. if (auto fp = make_inverse_rrect_fp(this->localToDevice(), inner, fSurfaceDrawContext->chooseAA(paint), *fSurfaceDrawContext->caps()->shaderCaps())) { GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } SkASSERT(!grPaint.hasCoverageFragmentProcessor()); grPaint.setCoverageFragmentProcessor(std::move(fp)); fSurfaceDrawContext->drawRRect(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), outer, GrStyle()); return; } } SkPath path; path.setIsVolatile(true); path.addRRect(outer); path.addRRect(inner); path.setFillType(SkPathFillType::kEvenOdd); // TODO: We are losing the possible mutability of the path here but this should probably be // fixed by upgrading GrStyledShape to handle DRRects. GrStyledShape shape(path, paint); GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(), paint, this->asMatrixProvider(), shape); } ///////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawRegion(const SkRegion& region, const SkPaint& paint) { ASSERT_SINGLE_OWNER if (paint.getMaskFilter()) { SkPath path; region.getBoundaryPath(&path); path.setIsVolatile(true); return this->drawPath(path, paint, true); } GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawRegion(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), region, GrStyle(paint)); } void SkGpuDevice::drawOval(const SkRect& oval, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawOval", fContext.get()); if (paint.getMaskFilter()) { // The RRect path can handle special case blurring SkRRect rr = SkRRect::MakeOval(oval); return this->drawRRect(rr, paint); } GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawOval(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), oval, GrStyle(paint)); } void SkGpuDevice::drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawArc", fContext.get()); if (paint.getMaskFilter()) { this->INHERITED::drawArc(oval, startAngle, sweepAngle, useCenter, paint); return; } GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawArc(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), oval, startAngle, sweepAngle, useCenter, GrStyle(paint)); } #include "include/core/SkMaskFilter.h" /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawPath(const SkPath& origSrcPath, const SkPaint& paint, bool pathIsMutable) { #if GR_TEST_UTILS if (fContext->priv().options().fAllPathsVolatile && !origSrcPath.isVolatile()) { this->drawPath(SkPath(origSrcPath).setIsVolatile(true), paint, true); return; } #endif ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPath", fContext.get()); if (!paint.getMaskFilter()) { GrPaint grPaint; if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), &grPaint)) { return; } fSurfaceDrawContext->drawPath(this->clip(), std::move(grPaint), fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), origSrcPath, GrStyle(paint)); return; } // TODO: losing possible mutability of 'origSrcPath' here GrStyledShape shape(origSrcPath, paint); GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(), paint, this->asMatrixProvider(), shape); } sk_sp SkGpuDevice::makeSpecial(const SkBitmap& bitmap) { ASSERT_SINGLE_OWNER // TODO: this makes a tight copy of 'bitmap' but it doesn't have to be (given SkSpecialImage's // semantics). Since this is cached we would have to bake the fit into the cache key though. auto view = std::get<0>(GrMakeCachedBitmapProxyView(fContext.get(), bitmap)); if (!view) { return nullptr; } const SkIRect rect = SkIRect::MakeSize(view.proxy()->dimensions()); // GrMakeCachedBitmapProxyView creates a tight copy of 'bitmap' so we don't have to subset // the special image return SkSpecialImage::MakeDeferredFromGpu(fContext.get(), rect, bitmap.getGenerationID(), std::move(view), SkColorTypeToGrColorType(bitmap.colorType()), bitmap.refColorSpace(), this->surfaceProps()); } sk_sp SkGpuDevice::makeSpecial(const SkImage* image) { ASSERT_SINGLE_OWNER SkPixmap pm; if (image->isTextureBacked()) { auto [view, ct] = as_IB(image)->asView(this->recordingContext(), GrMipmapped::kNo); SkASSERT(view); return SkSpecialImage::MakeDeferredFromGpu(fContext.get(), SkIRect::MakeWH(image->width(), image->height()), image->uniqueID(), std::move(view), ct, image->refColorSpace(), this->surfaceProps()); } else if (image->peekPixels(&pm)) { SkBitmap bm; bm.installPixels(pm); return this->makeSpecial(bm); } else { return nullptr; } } sk_sp SkGpuDevice::snapSpecial(const SkIRect& subset, bool forceCopy) { ASSERT_SINGLE_OWNER GrSurfaceDrawContext* sdc = fSurfaceDrawContext.get(); // If we are wrapping a vulkan secondary command buffer, then we can't snap off a special image // since it would require us to make a copy of the underlying VkImage which we don't have access // to. Additionaly we can't stop and start the render pass that is used with the secondary // command buffer. if (sdc->wrapsVkSecondaryCB()) { return nullptr; } SkASSERT(sdc->asSurfaceProxy()); SkIRect finalSubset = subset; GrSurfaceProxyView view = sdc->readSurfaceView(); if (forceCopy || !view.asTextureProxy()) { // When the device doesn't have a texture, or a copy is requested, we create a temporary // texture that matches the device contents view = GrSurfaceProxyView::Copy(fContext.get(), std::move(view), GrMipmapped::kNo, // Don't auto generate mips subset, SkBackingFit::kApprox, SkBudgeted::kYes); // Always budgeted if (!view) { return nullptr; } // Since this copied only the requested subset, the special image wrapping the proxy no // longer needs the original subset. finalSubset = SkIRect::MakeSize(view.dimensions()); } GrColorType ct = SkColorTypeToGrColorType(this->imageInfo().colorType()); return SkSpecialImage::MakeDeferredFromGpu(fContext.get(), finalSubset, kNeedNewImageUniqueID_SpecialImage, std::move(view), ct, this->imageInfo().refColorSpace(), this->surfaceProps()); } void SkGpuDevice::drawDevice(SkBaseDevice* device, const SkSamplingOptions& sampling, const SkPaint& paint) { ASSERT_SINGLE_OWNER // clear of the source device must occur before CHECK_SHOULD_DRAW GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDevice", fContext.get()); this->INHERITED::drawDevice(device, sampling, paint); } void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { ASSERT_SINGLE_OWNER GrAA aa = fSurfaceDrawContext->chooseAA(paint); GrQuadAAFlags aaFlags = (aa == GrAA::kYes) ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; this->drawImageQuad(image, src, &dst, nullptr, aa, aaFlags, nullptr, sampling, paint, constraint); } void SkGpuDevice::drawViewLattice(GrSurfaceProxyView view, const GrColorInfo& info, std::unique_ptr iter, const SkRect& dst, SkFilterMode filter, const SkPaint& origPaint) { GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawViewLattice", fContext.get()); SkASSERT(view); SkTCopyOnFirstWrite paint(&origPaint); if (!info.isAlphaOnly() && (paint->getColor() & 0x00FFFFFF) != 0x00FFFFFF) { paint.writable()->setColor(SkColorSetARGB(origPaint.getAlpha(), 0xFF, 0xFF, 0xFF)); } GrPaint grPaint; if (!SkPaintToGrPaintWithPrimitiveColor(this->recordingContext(), fSurfaceDrawContext->colorInfo(), *paint, this->asMatrixProvider(), &grPaint)) { return; } if (info.isAlphaOnly()) { // If we were doing this with an FP graph we'd use a kDstIn blend between the texture and // the paint color. view.concatSwizzle(GrSwizzle("aaaa")); } auto csxf = GrColorSpaceXform::Make(info, fSurfaceDrawContext->colorInfo()); fSurfaceDrawContext->drawImageLattice(this->clip(), std::move(grPaint), this->localToDevice(), std::move(view), info.alphaType(), std::move(csxf), filter, std::move(iter), dst); } void SkGpuDevice::drawImageLattice(const SkImage* image, const SkCanvas::Lattice& lattice, const SkRect& dst, SkFilterMode filter, const SkPaint& paint) { ASSERT_SINGLE_OWNER auto iter = std::make_unique(lattice, dst); if (auto [view, ct] = as_IB(image)->asView(this->recordingContext(), GrMipmapped::kNo); view) { GrColorInfo colorInfo(ct, image->alphaType(), image->refColorSpace()); this->drawViewLattice(std::move(view), std::move(colorInfo), std::move(iter), dst, filter, paint); } } static bool init_vertices_paint(GrRecordingContext* context, const GrColorInfo& colorInfo, const SkPaint& skPaint, const SkMatrixProvider& matrixProvider, SkBlendMode bmode, bool hasColors, GrPaint* grPaint) { if (skPaint.getShader()) { if (hasColors) { // When there are colors and a shader, the shader and colors are combined using bmode. return SkPaintToGrPaintWithBlend(context, colorInfo, skPaint, matrixProvider, bmode, grPaint); } else { // We have a shader, but no colors to blend it against. return SkPaintToGrPaint(context, colorInfo, skPaint, matrixProvider, grPaint); } } else { if (hasColors) { // We have colors, but no shader. return SkPaintToGrPaintWithPrimitiveColor(context, colorInfo, skPaint, matrixProvider, grPaint); } else { // No colors and no shader. Just draw with the paint color. return SkPaintToGrPaintNoShader(context, colorInfo, skPaint, matrixProvider, grPaint); } } } void SkGpuDevice::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext.get()); SkASSERT(vertices); SkVerticesPriv info(vertices->priv()); const SkRuntimeEffect* effect = paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr; GrPaint grPaint; if (!init_vertices_paint(fContext.get(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), mode, info.hasColors(), &grPaint)) { return; } fSurfaceDrawContext->drawVertices(this->clip(), std::move(grPaint), this->asMatrixProvider(), sk_ref_sp(const_cast(vertices)), nullptr, effect); } /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { #if GR_TEST_UTILS if (fContext->priv().options().fAllPathsVolatile && !path.isVolatile()) { this->drawShadow(SkPath(path).setIsVolatile(true), rec); return; } #endif ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawShadow", fContext.get()); if (!fSurfaceDrawContext->drawFastShadow(this->clip(), this->localToDevice(), path, rec)) { // failed to find an accelerated case this->INHERITED::drawShadow(path, rec); } } /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect texRect[], const SkColor colors[], int count, SkBlendMode mode, const SkSamplingOptions& sampling, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawAtlas", fContext.get()); // Convert atlas to an image shader. sk_sp shader = atlas->makeShader(sampling); if (!shader) { return; } // Create a fragment processor for atlas image. GrFPArgs fpArgs(fContext.get(), this->asMatrixProvider(), &fSurfaceDrawContext->colorInfo()); std::unique_ptr shaderFP = as_SB(shader)->asFragmentProcessor(fpArgs); if (shaderFP == nullptr) { return; } GrPaint grPaint; if (colors) { if (!SkPaintToGrPaintWithBlendReplaceShader( this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), std::move(shaderFP), mode, &grPaint)) { return; } } else { if (!SkPaintToGrPaintReplaceShader( this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, this->asMatrixProvider(), std::move(shaderFP), &grPaint)) { return; } } fSurfaceDrawContext->drawAtlas(this->clip(), std::move(grPaint), this->localToDevice(), count, xform, texRect, colors); } /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::onDrawGlyphRunList(const SkGlyphRunList& glyphRunList, const SkPaint& paint) { ASSERT_SINGLE_OWNER GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawGlyphRunList", fContext.get()); SkASSERT(!glyphRunList.hasRSXForm()); fSurfaceDrawContext->drawGlyphRunList( this->clip(), this->asMatrixProvider(), glyphRunList, paint); } /////////////////////////////////////////////////////////////////////////////// void SkGpuDevice::drawDrawable(SkDrawable* drawable, const SkMatrix* matrix, SkCanvas* canvas) { ASSERT_SINGLE_OWNER GrBackendApi api = this->recordingContext()->backend(); if (GrBackendApi::kVulkan == api) { const SkMatrix& ctm = canvas->getLocalToDeviceAs3x3(); const SkMatrix& combinedMatrix = matrix ? SkMatrix::Concat(ctm, *matrix) : ctm; std::unique_ptr gpuDraw = drawable->snapGpuDrawHandler(api, combinedMatrix, canvas->getDeviceClipBounds(), this->imageInfo()); if (gpuDraw) { fSurfaceDrawContext->drawDrawable(std::move(gpuDraw), drawable->getBounds()); return; } } this->INHERITED::drawDrawable(drawable, matrix, canvas); } /////////////////////////////////////////////////////////////////////////////// bool SkGpuDevice::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores, bool deleteSemaphoresAfterWait) { ASSERT_SINGLE_OWNER return fSurfaceDrawContext->waitOnSemaphores(numSemaphores, waitSemaphores, deleteSemaphoresAfterWait); } /////////////////////////////////////////////////////////////////////////////// SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) { ASSERT_SINGLE_OWNER SkSurfaceProps props(this->surfaceProps().flags(), cinfo.fPixelGeometry); // layers are never drawn in repeat modes, so we can request an approx // match and ignore any padding. SkBackingFit fit = kNever_TileUsage == cinfo.fTileUsage ? SkBackingFit::kApprox : SkBackingFit::kExact; SkASSERT(cinfo.fInfo.colorType() != kRGBA_1010102_SkColorType); auto sdc = GrSurfaceDrawContext::MakeWithFallback( fContext.get(), SkColorTypeToGrColorType(cinfo.fInfo.colorType()), fSurfaceDrawContext->colorInfo().refColorSpace(), fit, cinfo.fInfo.dimensions(), props, fSurfaceDrawContext->numSamples(), GrMipmapped::kNo, fSurfaceDrawContext->asSurfaceProxy()->isProtected(), kBottomLeft_GrSurfaceOrigin, SkBudgeted::kYes); if (!sdc) { return nullptr; } // Skia's convention is to only clear a device if it is non-opaque. InitContents init = cinfo.fInfo.isOpaque() ? kUninit_InitContents : kClear_InitContents; return SkGpuDevice::Make(std::move(sdc), init).release(); } sk_sp SkGpuDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { ASSERT_SINGLE_OWNER // TODO: Change the signature of newSurface to take a budgeted parameter. static const SkBudgeted kBudgeted = SkBudgeted::kNo; return SkSurface::MakeRenderTarget(fContext.get(), kBudgeted, info, fSurfaceDrawContext->numSamples(), fSurfaceDrawContext->origin(), &props); } SkImageFilterCache* SkGpuDevice::getImageFilterCache() { ASSERT_SINGLE_OWNER // We always return a transient cache, so it is freed after each // filter traversal. return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize); } //////////////////////////////////////////////////////////////////////////////////// bool SkGpuDevice::android_utils_clipWithStencil() { SkRegion clipRegion; this->onAsRgnClip(&clipRegion); if (clipRegion.isEmpty()) { return false; } GrSurfaceDrawContext* sdc = fSurfaceDrawContext.get(); SkASSERT(sdc); GrPaint grPaint; grPaint.setXPFactory(GrDisableColorXPFactory::Get()); static constexpr GrUserStencilSettings kDrawToStencil( GrUserStencilSettings::StaticInit< 0x1, GrUserStencilTest::kAlways, 0x1, GrUserStencilOp::kReplace, GrUserStencilOp::kReplace, 0x1>() ); // Regions don't actually need AA, but in DMSAA mode everything is antialiased. GrAA aa = GrAA(fSurfaceDrawContext->alwaysAntialias()); sdc->drawRegion(nullptr, std::move(grPaint), aa, SkMatrix::I(), clipRegion, GrStyle::SimpleFill(), &kDrawToStencil); return true; }