| /* |
| * 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/v1/Device_v1.h" |
| |
| #include "include/core/SkImageFilter.h" |
| #include "include/core/SkMaskFilter.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 "include/private/chromium/GrSlug.h" |
| #include "src/core/SkCanvasPriv.h" |
| #include "src/core/SkClipStack.h" |
| #include "src/core/SkCustomMeshPriv.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/GrDisableColorXP.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 SKGPU_ASSERT_SINGLE_OWNER(fContext->priv().singleOwner()) |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| namespace { |
| |
| bool force_aa_clip(const skgpu::v1::SurfaceDrawContext* sdc) { |
| return sdc->numSamples() > 1 || sdc->alwaysAntialias(); |
| } |
| |
| 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"); |
| } |
| |
| std::unique_ptr<GrFragmentProcessor> make_inverse_rrect_fp(const SkMatrix& viewMatrix, |
| const SkRRect& rrect, GrAA aa, |
| const GrShaderCaps& shaderCaps) { |
| SkTCopyOnFirstWrite<SkRRect> 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; |
| } |
| |
| bool init_vertices_paint(GrRecordingContext* rContext, |
| const GrColorInfo& colorInfo, |
| const SkPaint& skPaint, |
| const SkMatrixProvider& matrixProvider, |
| sk_sp<SkBlender> blender, |
| bool hasColors, |
| GrPaint* grPaint) { |
| if (hasColors) { |
| return SkPaintToGrPaintWithBlend(rContext, |
| colorInfo, |
| skPaint, |
| matrixProvider, |
| blender.get(), |
| grPaint); |
| } else { |
| return SkPaintToGrPaint(rContext, colorInfo, skPaint, matrixProvider, grPaint); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| namespace skgpu::v1 { |
| |
| sk_sp<BaseDevice> Device::Make(GrRecordingContext* rContext, |
| GrColorType colorType, |
| sk_sp<GrSurfaceProxy> proxy, |
| sk_sp<SkColorSpace> colorSpace, |
| GrSurfaceOrigin origin, |
| const SkSurfaceProps& surfaceProps, |
| InitContents init) { |
| auto sdc = SurfaceDrawContext::Make(rContext, |
| colorType, |
| std::move(proxy), |
| std::move(colorSpace), |
| origin, |
| surfaceProps); |
| |
| return Device::Make(std::move(sdc), kPremul_SkAlphaType, init); |
| } |
| |
| sk_sp<BaseDevice> Device::Make(std::unique_ptr<SurfaceDrawContext> sdc, |
| SkAlphaType alphaType, |
| InitContents init) { |
| if (!sdc) { |
| return nullptr; |
| } |
| |
| GrRecordingContext* rContext = sdc->recordingContext(); |
| if (rContext->abandoned()) { |
| return nullptr; |
| } |
| |
| SkColorType ct = GrColorTypeToSkColorType(sdc->colorInfo().colorType()); |
| |
| DeviceFlags flags; |
| if (!rContext->colorTypeSupportedAsSurface(ct) || |
| !CheckAlphaTypeAndGetFlags(alphaType, init, &flags)) { |
| return nullptr; |
| } |
| return sk_sp<Device>(new Device(std::move(sdc), flags)); |
| } |
| |
| sk_sp<BaseDevice> Device::Make(GrRecordingContext* rContext, |
| SkBudgeted budgeted, |
| const SkImageInfo& ii, |
| SkBackingFit fit, |
| int sampleCount, |
| GrMipmapped mipMapped, |
| GrProtected isProtected, |
| GrSurfaceOrigin origin, |
| const SkSurfaceProps& props, |
| InitContents init) { |
| if (!rContext) { |
| return nullptr; |
| } |
| |
| auto sdc = SurfaceDrawContext::Make(rContext, |
| SkColorTypeToGrColorType(ii.colorType()), |
| ii.refColorSpace(), |
| fit, |
| ii.dimensions(), |
| props, |
| sampleCount, |
| mipMapped, |
| isProtected, |
| origin, |
| budgeted); |
| |
| return Device::Make(std::move(sdc), ii.alphaType(), init); |
| } |
| |
| Device::Device(std::unique_ptr<SurfaceDrawContext> sdc, DeviceFlags flags) |
| : INHERITED(sk_ref_sp(sdc->recordingContext()), |
| MakeInfo(sdc.get(), flags), |
| sdc->surfaceProps()) |
| , fSurfaceDrawContext(std::move(sdc)) |
| , fClip(SkIRect::MakeSize(fSurfaceDrawContext->dimensions()), |
| &this->asMatrixProvider(), |
| force_aa_clip(fSurfaceDrawContext.get())) { |
| if (flags & DeviceFlags::kNeedClear) { |
| this->clearAll(); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| bool Device::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 Device::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 Device::onAccessPixels(SkPixmap* pmap) { |
| ASSERT_SINGLE_OWNER |
| return false; |
| } |
| |
| SurfaceDrawContext* Device::surfaceDrawContext() { |
| ASSERT_SINGLE_OWNER |
| return fSurfaceDrawContext.get(); |
| } |
| |
| const SurfaceDrawContext* Device::surfaceDrawContext() const { |
| ASSERT_SINGLE_OWNER |
| return fSurfaceDrawContext.get(); |
| } |
| |
| skgpu::SurfaceFillContext* Device::surfaceFillContext() { |
| ASSERT_SINGLE_OWNER |
| return fSurfaceDrawContext.get(); |
| } |
| |
| void Device::clearAll() { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "clearAll", fContext.get()); |
| |
| SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); |
| fSurfaceDrawContext->clearAtLeast(rect, SK_PMColor4fTRANSPARENT); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::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 Device::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().asM33(), SkRect::Make(globalRgn.getBounds()), aa, op); |
| } else { |
| SkPath path; |
| globalRgn.getBoundaryPath(&path); |
| fClip.clipPath(this->globalToDevice().asM33(), path, aa, op); |
| } |
| } |
| |
| void Device::onAsRgnClip(SkRegion* region) const { |
| SkIRect bounds = fClip.getConservativeBounds(); |
| // Assume wide open and then perform intersect/difference operations reducing the region |
| region->setRect(bounds); |
| const SkRegion deviceBounds(bounds); |
| for (const ClipStack::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 Device::onClipIsAA() const { |
| for (const ClipStack::Element& e : fClip) { |
| if (e.fAA == GrAA::kYes) { |
| return true; |
| } |
| SkASSERT(!fSurfaceDrawContext->alwaysAntialias()); |
| } |
| return false; |
| } |
| |
| SkBaseDevice::ClipType Device::onGetClipType() const { |
| ClipStack::ClipState state = fClip.clipState(); |
| if (state == ClipStack::ClipState::kEmpty) { |
| return ClipType::kEmpty; |
| } else if (state == ClipStack::ClipState::kDeviceRect || |
| state == ClipStack::ClipState::kWideOpen) { |
| return ClipType::kRect; |
| } else { |
| return ClipType::kComplex; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::drawPaint(const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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()); |
| } |
| |
| void Device::drawPoints(SkCanvas::PointMode mode, |
| size_t count, |
| const SkPoint pts[], |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawPoints", fContext.get()); |
| SkScalar width = paint.getStrokeWidth(); |
| if (width < 0) { |
| return; |
| } |
| |
| GrAA aa = fSurfaceDrawContext->chooseAA(paint); |
| |
| if (count == 2 && mode == SkCanvas::kLines_PointMode) { |
| if (paint.getPathEffect()) { |
| // Probably a dashed line. Draw as a path. |
| GrPaint grPaint; |
| if (SkPaintToGrPaint(this->recordingContext(), |
| fSurfaceDrawContext->colorInfo(), |
| paint, |
| this->asMatrixProvider(), |
| &grPaint)) { |
| SkPath path; |
| path.setIsVolatile(true); |
| path.moveTo(pts[0]); |
| path.lineTo(pts[1]); |
| fSurfaceDrawContext->drawPath(this->clip(), |
| std::move(grPaint), |
| aa, |
| this->localToDevice(), |
| path, |
| GrStyle(paint, SkPaint::kStroke_Style)); |
| } |
| return; |
| } |
| if (!paint.getMaskFilter() && |
| paint.getStrokeWidth() > 0 && // drawStrokedLine doesn't support hairlines. |
| paint.getStrokeCap() != SkPaint::kRound_Cap) { // drawStrokedLine doesn't do round caps. |
| // Simple stroked line. Bypass path rendering. |
| GrPaint grPaint; |
| if (SkPaintToGrPaint(this->recordingContext(), |
| fSurfaceDrawContext->colorInfo(), |
| paint, |
| this->asMatrixProvider(), |
| &grPaint)) { |
| fSurfaceDrawContext->drawStrokedLine(this->clip(), |
| std::move(grPaint), |
| aa, |
| this->localToDevice(), |
| pts, |
| SkStrokeRec(paint, SkPaint::kStroke_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-coverage-aa hairlines and paints without path effects or mask filters, |
| // else we let the SkDraw call our drawPath() |
| if (!isHairline || |
| paint.getPathEffect() || |
| paint.getMaskFilter() || |
| fSurfaceDrawContext->chooseAAType(aa) == GrAAType::kCoverage) { |
| 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; |
| |
| GrPaint grPaint; |
| if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), paint, |
| *matrixProvider, &grPaint)) { |
| return; |
| } |
| |
| static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode; |
| sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, SkToS32(count), pts, nullptr, |
| nullptr); |
| |
| fSurfaceDrawContext->drawVertices(this->clip(), std::move(grPaint), *matrixProvider, |
| std::move(vertices), &primitiveType); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::drawRect(const SkRect& rect, const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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 Device::drawEdgeAAQuad(const SkRect& rect, |
| const SkPoint clip[4], |
| SkCanvas::QuadAAFlags aaFlags, |
| const SkColor4f& color, |
| SkBlendMode mode) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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 Device::drawRRect(const SkRRect& rrect, const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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); |
| } |
| |
| void Device::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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 Device::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 Device::drawOval(const SkRect& oval, const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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 Device::drawArc(const SkRect& oval, |
| SkScalar startAngle, |
| SkScalar sweepAngle, |
| bool useCenter, |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "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)); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::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("skgpu::v1::Device", "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<SkSpecialImage> Device::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<SkSpecialImage> Device::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<SkSpecialImage> Device::snapSpecial(const SkIRect& subset, bool forceCopy) { |
| ASSERT_SINGLE_OWNER |
| |
| auto 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 Device::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("skgpu::v1::Device", "drawDevice", fContext.get()); |
| this->INHERITED::drawDevice(device, sampling, paint); |
| } |
| |
| void Device::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 Device::drawViewLattice(GrSurfaceProxyView view, |
| const GrColorInfo& info, |
| std::unique_ptr<SkLatticeIter> iter, |
| const SkRect& dst, |
| SkFilterMode filter, |
| const SkPaint& origPaint) { |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawViewLattice", fContext.get()); |
| SkASSERT(view); |
| |
| SkTCopyOnFirstWrite<SkPaint> paint(&origPaint); |
| |
| if (!info.isAlphaOnly() && (paint->getColor() & 0x00FFFFFF) != 0x00FFFFFF) { |
| paint.writable()->setColor(SkColorSetARGB(origPaint.getAlpha(), 0xFF, 0xFF, 0xFF)); |
| } |
| GrPaint grPaint; |
| // Passing null as shaderFP indicates that the GP will provide the shader. |
| if (!SkPaintToGrPaintReplaceShader(this->recordingContext(), |
| fSurfaceDrawContext->colorInfo(), |
| *paint, |
| this->asMatrixProvider(), |
| /*shaderFP=*/nullptr, |
| &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(skgpu::Swizzle("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 Device::drawImageLattice(const SkImage* image, |
| const SkCanvas::Lattice& lattice, |
| const SkRect& dst, |
| SkFilterMode filter, |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| auto iter = std::make_unique<SkLatticeIter>(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); |
| } |
| } |
| |
| void Device::drawVertices(const SkVertices* vertices, |
| sk_sp<SkBlender> blender, |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawVertices", fContext.get()); |
| SkASSERT(vertices); |
| |
| #ifdef SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER |
| if (!paint.getShader()) { |
| blender = SkBlender::Mode(SkBlendMode::kDst); |
| } |
| #endif |
| |
| SkVerticesPriv info(vertices->priv()); |
| |
| GrPaint grPaint; |
| if (!init_vertices_paint(fContext.get(), |
| fSurfaceDrawContext->colorInfo(), |
| paint, |
| this->asMatrixProvider(), |
| std::move(blender), |
| info.hasColors(), |
| &grPaint)) { |
| return; |
| } |
| fSurfaceDrawContext->drawVertices(this->clip(), |
| std::move(grPaint), |
| this->asMatrixProvider(), |
| sk_ref_sp(const_cast<SkVertices*>(vertices)), |
| nullptr); |
| } |
| |
| void Device::drawCustomMesh(SkCustomMesh customMesh, |
| sk_sp<SkBlender> blender, |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawCustomMesh", fContext.get()); |
| SkASSERT(customMesh.vb); |
| |
| GrPaint grPaint; |
| if (!init_vertices_paint(fContext.get(), |
| fSurfaceDrawContext->colorInfo(), |
| paint, |
| this->asMatrixProvider(), |
| std::move(blender), |
| SkCustomMeshSpecificationPriv::HasColors(*customMesh.spec), |
| &grPaint)) { |
| return; |
| } |
| fSurfaceDrawContext->drawCustomMesh(this->clip(), |
| std::move(grPaint), |
| this->asMatrixProvider(), |
| std::move(customMesh)); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::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("skgpu::v1::Device", "drawShadow", fContext.get()); |
| |
| if (!fSurfaceDrawContext->drawFastShadow(this->clip(), this->localToDevice(), path, rec)) { |
| // failed to find an accelerated case |
| this->INHERITED::drawShadow(path, rec); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::drawAtlas(const SkRSXform xform[], |
| const SkRect texRect[], |
| const SkColor colors[], |
| int count, |
| sk_sp<SkBlender> blender, |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawAtlas", fContext.get()); |
| |
| GrPaint grPaint; |
| if (colors) { |
| if (!SkPaintToGrPaintWithBlend(this->recordingContext(), |
| fSurfaceDrawContext->colorInfo(), |
| paint, |
| this->asMatrixProvider(), |
| blender.get(), |
| &grPaint)) { |
| return; |
| } |
| } else { |
| if (!SkPaintToGrPaint(this->recordingContext(), fSurfaceDrawContext->colorInfo(), |
| paint, this->asMatrixProvider(), &grPaint)) { |
| return; |
| } |
| } |
| |
| fSurfaceDrawContext->drawAtlas(this->clip(), std::move(grPaint), this->localToDevice(), count, |
| xform, texRect, colors); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::onDrawGlyphRunList(SkCanvas* canvas, |
| const SkGlyphRunList& glyphRunList, |
| const SkPaint& paint) { |
| ASSERT_SINGLE_OWNER |
| GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::v1::Device", "drawGlyphRunList", fContext.get()); |
| SkASSERT(!glyphRunList.hasRSXForm()); |
| |
| #if defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG) |
| auto slug = this->convertGlyphRunListToSlug(glyphRunList, paint); |
| if (slug != nullptr) { |
| this->drawSlug(slug.get()); |
| } |
| return; |
| #endif |
| |
| fSurfaceDrawContext->drawGlyphRunList( |
| canvas, this->clip(), this->asMatrixProvider(), glyphRunList, paint); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void Device::drawDrawable(SkCanvas* canvas, SkDrawable* drawable, const SkMatrix* matrix) { |
| ASSERT_SINGLE_OWNER |
| |
| GrBackendApi api = this->recordingContext()->backend(); |
| if (GrBackendApi::kVulkan == api) { |
| const SkMatrix& ctm = this->localToDevice(); |
| const SkMatrix& combinedMatrix = matrix ? SkMatrix::Concat(ctm, *matrix) : ctm; |
| std::unique_ptr<SkDrawable::GpuDrawHandler> gpuDraw = |
| drawable->snapGpuDrawHandler(api, combinedMatrix, this->devClipBounds(), |
| this->imageInfo()); |
| if (gpuDraw) { |
| fSurfaceDrawContext->drawDrawable( |
| std::move(gpuDraw), combinedMatrix.mapRect(drawable->getBounds())); |
| return; |
| } |
| } |
| this->INHERITED::drawDrawable(canvas, drawable, matrix); |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| bool Device::wait(int numSemaphores, |
| const GrBackendSemaphore* waitSemaphores, |
| bool deleteSemaphoresAfterWait) { |
| ASSERT_SINGLE_OWNER |
| |
| return fSurfaceDrawContext->waitOnSemaphores(numSemaphores, waitSemaphores, |
| deleteSemaphoresAfterWait); |
| } |
| |
| bool Device::replaceBackingProxy(SkSurface::ContentChangeMode mode, |
| sk_sp<GrRenderTargetProxy> newRTP, |
| GrColorType grColorType, |
| sk_sp<SkColorSpace> colorSpace, |
| GrSurfaceOrigin origin, |
| const SkSurfaceProps& props) { |
| auto sdc = SurfaceDrawContext::Make(fContext.get(), grColorType, std::move(newRTP), |
| std::move(colorSpace), origin, props); |
| if (!sdc) { |
| return false; |
| } |
| |
| SkASSERT(sdc->dimensions() == fSurfaceDrawContext->dimensions()); |
| SkASSERT(sdc->numSamples() == fSurfaceDrawContext->numSamples()); |
| SkASSERT(sdc->asSurfaceProxy()->priv().isExact()); |
| if (mode == SkSurface::kRetain_ContentChangeMode) { |
| if (fContext->abandoned()) { |
| return false; |
| } |
| |
| SkASSERT(fSurfaceDrawContext->asTextureProxy()); |
| SkAssertResult(sdc->blitTexture(fSurfaceDrawContext->readSurfaceView(), |
| SkIRect::MakeWH(this->width(), this->height()), |
| SkIPoint::Make(0, 0))); |
| } |
| |
| fSurfaceDrawContext = std::move(sdc); |
| return true; |
| } |
| |
| void Device::asyncRescaleAndReadPixels(const SkImageInfo& info, |
| const SkIRect& srcRect, |
| RescaleGamma rescaleGamma, |
| RescaleMode rescaleMode, |
| ReadPixelsCallback callback, |
| ReadPixelsContext context) { |
| auto* sdc = fSurfaceDrawContext.get(); |
| // Context TODO: Elevate direct context requirement to public API. |
| auto dContext = sdc->recordingContext()->asDirectContext(); |
| if (!dContext) { |
| return; |
| } |
| sdc->asyncRescaleAndReadPixels(dContext, info, srcRect, rescaleGamma, rescaleMode, callback, |
| context); |
| } |
| |
| void Device::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace, |
| sk_sp<SkColorSpace> dstColorSpace, |
| const SkIRect& srcRect, |
| SkISize dstSize, |
| RescaleGamma rescaleGamma, |
| RescaleMode rescaleMode, |
| ReadPixelsCallback callback, |
| ReadPixelsContext context) { |
| auto* sdc = fSurfaceDrawContext.get(); |
| // Context TODO: Elevate direct context requirement to public API. |
| auto dContext = sdc->recordingContext()->asDirectContext(); |
| if (!dContext) { |
| return; |
| } |
| sdc->asyncRescaleAndReadPixelsYUV420(dContext, |
| yuvColorSpace, |
| std::move(dstColorSpace), |
| srcRect, |
| dstSize, |
| rescaleGamma, |
| rescaleMode, |
| callback, |
| context); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| SkBaseDevice* Device::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 = SurfaceDrawContext::MakeWithFallback( |
| fContext.get(), SkColorTypeToGrColorType(cinfo.fInfo.colorType()), |
| fSurfaceDrawContext->colorInfo().refColorSpace(), fit, cinfo.fInfo.dimensions(), props, |
| fSurfaceDrawContext->numSamples(), GrMipmapped::kNo, |
| fSurfaceDrawContext->asSurfaceProxy()->isProtected(), |
| fSurfaceDrawContext->origin(), |
| 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() ? InitContents::kUninit : InitContents::kClear; |
| |
| return Device::Make(std::move(sdc), cinfo.fInfo.alphaType(), init).release(); |
| } |
| |
| sk_sp<SkSurface> Device::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* Device::getImageFilterCache() { |
| ASSERT_SINGLE_OWNER |
| // We always return a transient cache, so it is freed after each |
| // filter traversal. |
| return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| |
| bool Device::android_utils_clipWithStencil() { |
| SkRegion clipRegion; |
| this->onAsRgnClip(&clipRegion); |
| if (clipRegion.isEmpty()) { |
| return false; |
| } |
| auto 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; |
| } |
| |
| } // namespace skgpu::v1 |