* 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/gpu/GrContext.h"
#include "include/private/SkImageInfoPriv.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/SkMakeUnique.h"
#include "src/core/SkPictureData.h"
#include "src/core/SkRRectPriv.h"
#include "src/core/SkRasterClip.h"
#include "src/core/SkRecord.h"
#include "src/core/SkSpecialImage.h"
#include "src/core/SkStroke.h"
#include "src/core/SkTLazy.h"
#include "src/core/SkVertState.h"
#include "src/core/SkWritePixelsRec.h"
#include "src/gpu/GrBitmapTextureMaker.h"
#include "src/gpu/GrBlurUtils.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrImageTextureMaker.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/GrStyle.h"
#include "src/gpu/GrSurfaceProxyPriv.h"
#include "src/gpu/GrTextureAdjuster.h"
#include "src/gpu/GrTracing.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/effects/GrBicubicEffect.h"
#include "src/gpu/effects/GrTextureDomain.h"
#include "src/gpu/geometry/GrShape.h"
#include "src/gpu/text/GrTextTarget.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 \
SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fContext->priv().singleOwner());)
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:
return false;
}
}
if (kClear_InitContents == init) {
*flags |= kNeedClear_Flag;
}
return true;
}
sk_sp<SkGpuDevice> SkGpuDevice::Make(GrContext* context,
sk_sp<GrRenderTargetContext> renderTargetContext,
int width, int height,
InitContents init) {
if (!renderTargetContext || context->priv().abandoned()) {
return nullptr;
}
unsigned flags;
if (!CheckAlphaTypeAndGetFlags(nullptr, init, &flags)) {
return nullptr;
}
return sk_sp<SkGpuDevice>(new SkGpuDevice(context, std::move(renderTargetContext),
width, height, flags));
}
sk_sp<SkGpuDevice> SkGpuDevice::Make(GrContext* context, SkBudgeted budgeted,
const SkImageInfo& info, int sampleCount,
GrSurfaceOrigin origin, const SkSurfaceProps* props,
GrMipMapped mipMapped, InitContents init) {
unsigned flags;
if (!CheckAlphaTypeAndGetFlags(&info, init, &flags)) {
return nullptr;
}
sk_sp<GrRenderTargetContext> renderTargetContext(MakeRenderTargetContext(context, budgeted,
info, sampleCount,
origin, props,
mipMapped));
if (!renderTargetContext) {
return nullptr;
}
return sk_sp<SkGpuDevice>(new SkGpuDevice(context, std::move(renderTargetContext),
info.width(), info.height(), flags));
}
static SkImageInfo make_info(GrRenderTargetContext* context, int w, int h, bool opaque) {
SkColorType colorType = GrColorTypeToSkColorType(context->colorSpaceInfo().colorType());
return SkImageInfo::Make(w, h, colorType, opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
context->colorSpaceInfo().refColorSpace());
}
SkGpuDevice::SkGpuDevice(GrContext* context, sk_sp<GrRenderTargetContext> renderTargetContext,
int width, int height, unsigned flags)
: INHERITED(make_info(renderTargetContext.get(), width, height,
SkToBool(flags & kIsOpaque_Flag)), renderTargetContext->surfaceProps())
, fContext(SkRef(context))
, fRenderTargetContext(std::move(renderTargetContext))
{
fSize.set(width, height);
if (flags & kNeedClear_Flag) {
this->clearAll();
}
}
sk_sp<GrRenderTargetContext> SkGpuDevice::MakeRenderTargetContext(
GrContext* context,
SkBudgeted budgeted,
const SkImageInfo& origInfo,
int sampleCount,
GrSurfaceOrigin origin,
const SkSurfaceProps* surfaceProps,
GrMipMapped mipMapped) {
if (kUnknown_SkColorType == origInfo.colorType() ||
origInfo.width() < 0 || origInfo.height() < 0) {
return nullptr;
}
if (!context) {
return nullptr;
}
return context->priv().makeDeferredRenderTargetContext(
SkBackingFit::kExact, origInfo.width(), origInfo.height(),
SkColorTypeToGrColorType(origInfo.colorType()), origInfo.refColorSpace(), sampleCount,
mipMapped, origin, surfaceProps, budgeted);
}
sk_sp<SkSpecialImage> SkGpuDevice::filterTexture(SkSpecialImage* srcImg,
int left, int top,
SkIPoint* offset,
const SkImageFilter* filter) {
SkASSERT(srcImg->isTextureBacked());
SkASSERT(filter);
SkMatrix matrix = this->ctm();
matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
const SkIRect clipBounds = this->devClipBounds().makeOffset(-left, -top);
sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
SkColorType colorType =
GrColorTypeToSkColorType(fRenderTargetContext->colorSpaceInfo().colorType());
if (colorType == kUnknown_SkColorType) {
colorType = kRGBA_8888_SkColorType;
}
SkImageFilter_Base::Context ctx(matrix, clipBounds, cache.get(), colorType,
fRenderTargetContext->colorSpaceInfo().colorSpace(), srcImg);
return as_IFB(filter)->filterImage(ctx, offset);
}
bool SkGpuDevice::onReadPixels(const SkPixmap& pm, int x, int y) {
ASSERT_SINGLE_OWNER
if (!SkImageInfoValidConversion(pm.info(), this->imageInfo())) {
return false;
}
return fRenderTargetContext->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), {x, y});
}
bool SkGpuDevice::onWritePixels(const SkPixmap& pm, int x, int y) {
ASSERT_SINGLE_OWNER
if (!SkImageInfoValidConversion(this->imageInfo(), pm.info())) {
return false;
}
return fRenderTargetContext->writePixels(pm.info(), pm.addr(), pm.rowBytes(), {x, y});
}
bool SkGpuDevice::onAccessPixels(SkPixmap* pmap) {
ASSERT_SINGLE_OWNER
return false;
}
GrRenderTargetContext* SkGpuDevice::accessRenderTargetContext() {
ASSERT_SINGLE_OWNER
return fRenderTargetContext.get();
}
void SkGpuDevice::clearAll() {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "clearAll", fContext.get());
SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
fRenderTargetContext->clear(&rect, SK_PMColor4fTRANSPARENT,
GrRenderTargetContext::CanClearFullscreen::kYes);
}
void SkGpuDevice::replaceRenderTargetContext(sk_sp<GrRenderTargetContext> rtc,
bool shouldRetainContent) {
SkASSERT(rtc->width() == this->width());
SkASSERT(rtc->height() == this->height());
SkASSERT(rtc->numSamples() == fRenderTargetContext->numSamples());
SkASSERT(rtc->asSurfaceProxy()->priv().isExact());
if (shouldRetainContent) {
if (this->context()->abandoned()) {
return;
}
SkASSERT(fRenderTargetContext->asTextureProxy());
SkAssertResult(rtc->blitTexture(fRenderTargetContext->asTextureProxy(),
SkIRect::MakeWH(this->width(), this->height()),
SkIPoint::Make(0,0)));
}
fRenderTargetContext = std::move(rtc);
}
void SkGpuDevice::replaceRenderTargetContext(bool shouldRetainContent) {
ASSERT_SINGLE_OWNER
SkBudgeted budgeted = fRenderTargetContext->priv().isBudgeted();
sk_sp<GrRenderTargetContext> newRTC(MakeRenderTargetContext(
this->context(),
budgeted,
this->imageInfo(),
fRenderTargetContext->numSamples(),
fRenderTargetContext->origin(),
&this->surfaceProps(),
fRenderTargetContext->mipMapped()));
if (!newRTC) {
return;
}
this->replaceRenderTargetContext(std::move(newRTC), shouldRetainContent);
}
void SkGpuDevice::drawPaint(const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPaint", fContext.get());
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawPaint(this->clip(), std::move(grPaint), this->ctm());
}
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;
}
if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) {
GrStyle style(paint, SkPaint::kStroke_Style);
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
SkPath path;
path.setIsVolatile(true);
path.moveTo(pts[0]);
path.lineTo(pts[1]);
fRenderTargetContext->drawPath(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), path, style);
return;
}
SkScalar scales[2];
bool isHairline = (0 == width) || (1 == width && this->ctm().getMinMaxScales(scales) &&
SkScalarNearlyEqual(scales[0], 1.f) &&
SkScalarNearlyEqual(scales[1], 1.f));
if (!isHairline || paint.getPathEffect() || paint.getMaskFilter() || paint.isAntiAlias()) {
SkRasterClip rc(this->devClipBounds());
SkDraw draw;
draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(this->width(), this->height()), nullptr, 0);
draw.fMatrix = &this->ctm();
draw.fRC = &rc;
draw.drawPoints(mode, count, pts, paint, this);
return;
}
GrPrimitiveType primitiveType = point_mode_to_primitive_type(mode);
const SkMatrix* viewMatrix = &this->ctm();
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
SkMatrix tempMatrix;
if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) {
tempMatrix = *viewMatrix;
static const SkScalar kOffset = 0.063f;
tempMatrix.postTranslate(kOffset, kOffset);
viewMatrix = &tempMatrix;
}
#endif
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
*viewMatrix, &grPaint)) {
return;
}
static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, SkToS32(count), pts, nullptr,
nullptr);
fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), *viewMatrix,
std::move(vertices), nullptr, 0, &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);
if (paint.getMaskFilter() || paint.getPathEffect()) {
GrShape shape(rect, style);
GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
this->clip(), paint, this->ctm(), shape);
return;
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), rect, &style);
}
void SkGpuDevice::drawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4],
SkCanvas::QuadAAFlags aaFlags, SkColor color, SkBlendMode mode) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawEdgeAAQuad", fContext.get());
SkPMColor4f dstColor = SkColor4fPrepForDst(SkColor4f::FromColor(color),
fRenderTargetContext->colorSpaceInfo())
.premul();
GrPaint grPaint;
grPaint.setColor4f(dstColor);
if (mode != SkBlendMode::kSrcOver) {
grPaint.setXPFactory(SkBlendMode_AsXPFactory(mode));
}
GrQuadAAFlags grAA = SkToGrQuadAAFlags(aaFlags);
if (clip) {
fRenderTargetContext->fillQuadWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA,
this->ctm(), clip, nullptr);
} else {
fRenderTargetContext->fillRectWithEdgeAA(this->clip(), std::move(grPaint), GrAA::kYes, grAA,
this->ctm(), 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;
}
}
GrStyle style(paint);
if (mf || style.pathEffect()) {
GrShape shape(rrect, style);
GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(),
this->clip(), paint, this->ctm(), shape);
return;
}
SkASSERT(!style.pathEffect());
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawRRect(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), rrect, style);
}
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()) {
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawDRRect(this->clip(), std::move(grPaint),
GrAA(paint.isAntiAlias()), this->ctm(), outer, inner);
return;
}
SkPath path;
path.setIsVolatile(true);
path.addRRect(outer);
path.addRRect(inner);
path.setFillType(SkPath::kEvenOdd_FillType);
GrShape shape(path, paint);
GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
paint, this->ctm(), shape);
}
void SkGpuDevice::drawRegion(const SkRegion& region, const SkPaint& paint) {
if (paint.getMaskFilter()) {
SkPath path;
region.getBoundaryPath(&path);
path.setIsVolatile(true);
return this->drawPath(path, paint, true);
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawRegion(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), 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()) {
SkRRect rr = SkRRect::MakeOval(oval);
return this->drawRRect(rr, paint);
}
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawOval(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), 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->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawArc(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), oval, startAngle, sweepAngle, useCenter,
GrStyle(paint));
}
#include "include/core/SkMaskFilter.h"
void SkGpuDevice::drawStrokedLine(const SkPoint points[2],
const SkPaint& origPaint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawStrokedLine", fContext.get());
SkASSERT(SkPaint::kRound_Cap != origPaint.getStrokeCap());
SkASSERT(SkPaint::kStroke_Style == origPaint.getStyle());
SkASSERT(!origPaint.getPathEffect());
SkASSERT(!origPaint.getMaskFilter());
const SkScalar halfWidth = 0.5f * origPaint.getStrokeWidth();
SkASSERT(halfWidth > 0);
SkVector v = points[1] - points[0];
SkScalar length = SkPoint::Normalize(&v);
if (!length) {
v.fX = 1.0f;
v.fY = 0.0f;
}
SkPaint newPaint(origPaint);
newPaint.setStyle(SkPaint::kFill_Style);
SkScalar xtraLength = 0.0f;
if (SkPaint::kButt_Cap != origPaint.getStrokeCap()) {
xtraLength = halfWidth;
}
SkPoint mid = points[0] + points[1];
mid.scale(0.5f);
SkRect rect = SkRect::MakeLTRB(mid.fX-halfWidth, mid.fY - 0.5f*length - xtraLength,
mid.fX+halfWidth, mid.fY + 0.5f*length + xtraLength);
SkMatrix m;
m.setSinCos(v.fX, -v.fY, mid.fX, mid.fY);
SkMatrix local = m;
m.postConcat(this->ctm());
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), newPaint, m,
&grPaint)) {
return;
}
fRenderTargetContext->fillRectWithLocalMatrix(
this->clip(), std::move(grPaint), GrAA(newPaint.isAntiAlias()), m, rect, local);
}
void SkGpuDevice::drawPath(const SkPath& origSrcPath, const SkPaint& paint, bool pathIsMutable) {
ASSERT_SINGLE_OWNER
if (!origSrcPath.isInverseFillType() && !paint.getPathEffect()) {
SkPoint points[2];
if (SkPaint::kStroke_Style == paint.getStyle() && paint.getStrokeWidth() > 0 &&
!paint.getMaskFilter() && SkPaint::kRound_Cap != paint.getStrokeCap() &&
this->ctm().preservesRightAngles() && origSrcPath.isLine(points)) {
SkScalar strokeWidth = this->ctm().getMaxScale() * paint.getStrokeWidth();
if (strokeWidth >= 1.0f) {
this->drawStrokedLine(points, paint);
return;
}
}
}
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPath", fContext.get());
if (!paint.getMaskFilter()) {
GrPaint grPaint;
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), &grPaint)) {
return;
}
fRenderTargetContext->drawPath(this->clip(), std::move(grPaint), GrAA(paint.isAntiAlias()),
this->ctm(), origSrcPath, GrStyle(paint));
return;
}
GrShape shape(origSrcPath, paint);
GrBlurUtils::drawShapeWithMaskFilter(fContext.get(), fRenderTargetContext.get(), this->clip(),
paint, this->ctm(), shape);
}
static const int kBmpSmallTileSize = 1 << 10;
static inline int get_tile_count(const SkIRect& srcRect, int tileSize) {
int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
return tilesX * tilesY;
}
static int determine_tile_size(const SkIRect& src, int maxTileSize) {
if (maxTileSize <= kBmpSmallTileSize) {
return maxTileSize;
}
size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
maxTileTotalTileSize *= maxTileSize * maxTileSize;
smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
return kBmpSmallTileSize;
} else {
return maxTileSize;
}
}
static void determine_clipped_src_rect(int width, int height,
const GrClip& clip,
const SkMatrix& viewMatrix,
const SkMatrix& srcToDstRect,
const SkISize& imageSize,
const SkRect* srcRectPtr,
SkIRect* clippedSrcIRect) {
clip.getConservativeBounds(width, height, clippedSrcIRect, nullptr);
SkMatrix inv = SkMatrix::Concat(viewMatrix, srcToDstRect);
if (!inv.invert(&inv)) {
clippedSrcIRect->setEmpty();
return;
}
SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
inv.mapRect(&clippedSrcRect);
if (srcRectPtr) {
if (!clippedSrcRect.intersect(*srcRectPtr)) {
clippedSrcIRect->setEmpty();
return;
}
}
clippedSrcRect.roundOut(clippedSrcIRect);
SkIRect bmpBounds = SkIRect::MakeSize(imageSize);
if (!clippedSrcIRect->intersect(bmpBounds)) {
clippedSrcIRect->setEmpty();
}
}
const GrCaps* SkGpuDevice::caps() const {
return fContext->priv().caps();
}
bool SkGpuDevice::shouldTileImageID(uint32_t imageID,
const SkIRect& imageRect,
const SkMatrix& viewMatrix,
const SkMatrix& srcToDstRect,
const GrSamplerState& params,
const SkRect* srcRectPtr,
int maxTileSize,
int* tileSize,
SkIRect* clippedSubset) const {
ASSERT_SINGLE_OWNER
if (imageRect.width() > maxTileSize || imageRect.height() > maxTileSize) {
determine_clipped_src_rect(fRenderTargetContext->width(), fRenderTargetContext->height(),
this->clip(), viewMatrix, srcToDstRect, imageRect.size(),
srcRectPtr, clippedSubset);
*tileSize = determine_tile_size(*clippedSubset, maxTileSize);
return true;
}
const size_t area = imageRect.width() * imageRect.height();
if (area < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
return false;
}
size_t bmpSize = area * sizeof(SkPMColor);
size_t cacheSize;
fContext->getResourceCacheLimits(nullptr, &cacheSize);
if (bmpSize < cacheSize / 2) {
return false;
}
determine_clipped_src_rect(fRenderTargetContext->width(), fRenderTargetContext->height(),
this->clip(), viewMatrix, srcToDstRect, imageRect.size(), srcRectPtr,
clippedSubset);
*tileSize = kBmpSmallTileSize;
size_t usedTileBytes = get_tile_count(*clippedSubset, kBmpSmallTileSize) *
kBmpSmallTileSize * kBmpSmallTileSize *
sizeof(SkPMColor);
return usedTileBytes * 2 < bmpSize;
}
bool SkGpuDevice::shouldTileImage(const SkImage* image, const SkRect* srcRectPtr,
SkCanvas::SrcRectConstraint constraint, SkFilterQuality quality,
const SkMatrix& viewMatrix,
const SkMatrix& srcToDstRect) const {
ASSERT_SINGLE_OWNER
SkASSERT(!image->isTextureBacked());
GrSamplerState samplerState;
bool doBicubic;
GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
image->width(), image->height(), quality, viewMatrix, srcToDstRect,
fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
int tileFilterPad;
if (doBicubic) {
tileFilterPad = GrBicubicEffect::kFilterTexelPad;
} else if (GrSamplerState::Filter::kNearest == textureFilterMode) {
tileFilterPad = 0;
} else {
tileFilterPad = 1;
}
samplerState.setFilterMode(textureFilterMode);
int maxTileSize = this->caps()->maxTileSize() - 2 * tileFilterPad;
int outTileSize;
SkIRect outClippedSrcRect;
return this->shouldTileImageID(image->unique(), image->bounds(), viewMatrix, srcToDstRect,
samplerState, srcRectPtr, maxTileSize, &outTileSize,
&outClippedSrcRect);
}
static inline void clamped_outset_with_offset(SkIRect* iRect,
int outset,
SkPoint* offset,
const SkIRect& clamp) {
iRect->outset(outset, outset);
int leftClampDelta = clamp.fLeft - iRect->fLeft;
if (leftClampDelta > 0) {
offset->fX -= outset - leftClampDelta;
iRect->fLeft = clamp.fLeft;
} else {
offset->fX -= outset;
}
int topClampDelta = clamp.fTop - iRect->fTop;
if (topClampDelta > 0) {
offset->fY -= outset - topClampDelta;
iRect->fTop = clamp.fTop;
} else {
offset->fY -= outset;
}
if (iRect->fRight > clamp.fRight) {
iRect->fRight = clamp.fRight;
}
if (iRect->fBottom > clamp.fBottom) {
iRect->fBottom = clamp.fBottom;
}
}
void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
const SkMatrix& viewMatrix,
const SkMatrix& dstMatrix,
const SkRect& srcRect,
const SkIRect& clippedSrcIRect,
const GrSamplerState& params,
const SkPaint& origPaint,
SkCanvas::SrcRectConstraint constraint,
int tileSize,
bool bicubic) {
ASSERT_SINGLE_OWNER
SK_HISTOGRAM_BOOLEAN("DrawTiled", true);
LogDrawScaleFactor(viewMatrix, SkMatrix::I(), origPaint.getFilterQuality());
const SkPaint* paint = &origPaint;
SkPaint tempPaint;
if (origPaint.isAntiAlias() && fRenderTargetContext->numSamples() <= 1) {
tempPaint = origPaint;
tempPaint.setAntiAlias(false);
paint = &tempPaint;
}
SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
int nx = bitmap.width() / tileSize;
int ny = bitmap.height() / tileSize;
for (int x = 0; x <= nx; x++) {
for (int y = 0; y <= ny; y++) {
SkRect tileR;
tileR.set(SkIntToScalar(x * tileSize),
SkIntToScalar(y * tileSize),
SkIntToScalar((x + 1) * tileSize),
SkIntToScalar((y + 1) * tileSize));
if (!SkRect::Intersects(tileR, clippedSrcRect)) {
continue;
}
if (!tileR.intersect(srcRect)) {
continue;
}
SkIRect iTileR;
tileR.roundOut(&iTileR);
SkVector offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
SkIntToScalar(iTileR.fTop));
SkRect rectToDraw = tileR;
dstMatrix.mapRect(&rectToDraw);
if (GrSamplerState::Filter::kNearest != params.filter() || bicubic) {
SkIRect iClampRect;
if (SkCanvas::kFast_SrcRectConstraint == constraint) {
iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
} else {
srcRect.roundOut(&iClampRect);
}
int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1;
clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect);
}
SkBitmap tmpB;
if (bitmap.extractSubset(&tmpB, iTileR)) {
tileR.offset(-offset.fX, -offset.fY);
bool needsTextureDomain = true;
this->drawBitmapTile(tmpB,
viewMatrix,
rectToDraw,
tileR,
params,
*paint,
constraint,
bicubic,
needsTextureDomain);
}
}
}
}
void SkGpuDevice::drawBitmapTile(const SkBitmap& bitmap,
const SkMatrix& viewMatrix,
const SkRect& dstRect,
const SkRect& srcRect,
const GrSamplerState& samplerState,
const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint,
bool bicubic,
bool needsTextureDomain) {
SkASSERT(bitmap.width() <= this->caps()->maxTextureSize() &&
bitmap.height() <= this->caps()->maxTextureSize());
SkASSERT(bitmap.width() <= this->caps()->maxTileSize() &&
bitmap.height() <= this->caps()->maxTileSize());
SkASSERT(!samplerState.isRepeated());
SkScalar scales[2] = {1.f, 1.f};
sk_sp<GrTextureProxy> proxy =
GrRefCachedBitmapTextureProxy(fContext.get(), bitmap, samplerState, scales);
if (!proxy) {
return;
}
SkMatrix texMatrix = SkMatrix::MakeRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
texMatrix.postScale(scales[0], scales[1]);
std::unique_ptr<GrFragmentProcessor> fp;
if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
SkRect domain;
if (srcRect.width() > SK_Scalar1) {
domain.fLeft = srcRect.fLeft + 0.5f;
domain.fRight = srcRect.fRight - 0.5f;
} else {
domain.fLeft = domain.fRight = srcRect.centerX();
}
if (srcRect.height() > SK_Scalar1) {
domain.fTop = srcRect.fTop + 0.5f;
domain.fBottom = srcRect.fBottom - 0.5f;
} else {
domain.fTop = domain.fBottom = srcRect.centerY();
}
if (bicubic) {
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain, kDir,
bitmap.alphaType());
} else {
fp = GrTextureDomainEffect::Make(std::move(proxy), texMatrix, domain,
GrTextureDomain::kClamp_Mode, samplerState.filter());
}
} else if (bicubic) {
SkASSERT(GrSamplerState::Filter::kNearest == samplerState.filter());
GrSamplerState::WrapMode wrapMode[2] = {samplerState.wrapModeX(), samplerState.wrapModeY()};
static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, wrapMode, kDir, bitmap.alphaType());
} else {
fp = GrSimpleTextureEffect::Make(std::move(proxy), texMatrix, samplerState);
}
fp = GrColorSpaceXformEffect::Make(std::move(fp), bitmap.colorSpace(), bitmap.alphaType(),
fRenderTargetContext->colorSpaceInfo().colorSpace());
GrPaint grPaint;
if (!SkPaintToGrPaintWithTexture(this->context(), fRenderTargetContext->colorSpaceInfo(), paint,
viewMatrix, std::move(fp),
kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) {
return;
}
GrAA aa = GrAA(paint.isAntiAlias() && fRenderTargetContext->numSamples() > 1);
fRenderTargetContext->drawRect(this->clip(), std::move(grPaint), aa, viewMatrix, dstRect);
}
void SkGpuDevice::drawSprite(const SkBitmap& bitmap,
int left, int top, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSprite", fContext.get());
if (fContext->priv().abandoned()) {
return;
}
sk_sp<SkSpecialImage> srcImg = this->makeSpecial(bitmap);
if (!srcImg) {
return;
}
this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
}
void SkGpuDevice::drawSpecial(SkSpecialImage* special, int left, int top, const SkPaint& paint,
SkImage* clipImage, const SkMatrix& clipMatrix) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext.get());
sk_sp<SkSpecialImage> result;
if (paint.getImageFilter()) {
SkIPoint offset = { 0, 0 };
result = this->filterTexture(special, left, top, &offset, paint.getImageFilter());
if (!result) {
return;
}
left += offset.fX;
top += offset.fY;
} else {
result = sk_ref_sp(special);
}
SkASSERT(result->isTextureBacked());
sk_sp<GrTextureProxy> proxy = result->asTextureProxyRef(this->context());
if (!proxy) {
return;
}
const GrPixelConfig config = proxy->config();
SkMatrix ctm = this->ctm();
ctm.postTranslate(-SkIntToScalar(left), -SkIntToScalar(top));
SkPaint tmpUnfiltered(paint);
if (tmpUnfiltered.getMaskFilter()) {
tmpUnfiltered.setMaskFilter(tmpUnfiltered.getMaskFilter()->makeWithMatrix(ctm));
}
tmpUnfiltered.setImageFilter(nullptr);
auto fp = GrSimpleTextureEffect::Make(std::move(proxy), SkMatrix::I());
fp = GrColorSpaceXformEffect::Make(std::move(fp), result->getColorSpace(), result->alphaType(),
fRenderTargetContext->colorSpaceInfo().colorSpace());
if (GrPixelConfigIsAlphaOnly(config)) {
fp = GrFragmentProcessor::MakeInputPremulAndMulByOutput(std::move(fp));
} else {
if (paint.getColor4f().isOpaque()) {
fp = GrFragmentProcessor::OverrideInput(std::move(fp), SK_PMColor4fWHITE, false);
} else {
fp = GrFragmentProcessor::MulChildByInputAlpha(std::move(fp));
}
}
GrPaint grPaint;
if (!SkPaintToGrPaintReplaceShader(this->context(), fRenderTargetContext->colorSpaceInfo(),
tmpUnfiltered, std::move(fp), &grPaint)) {
return;
}
const SkIRect& subset = result->subset();
SkRect dstRect = SkRect::Make(SkIRect::MakeXYWH(left, top, subset.width(), subset.height()));
SkRect srcRect = SkRect::Make(subset);
if (clipImage) {
GrSamplerState sampler = paint.getFilterQuality() > kNone_SkFilterQuality ?
GrSamplerState::ClampBilerp() : GrSamplerState::ClampNearest();
sk_sp<GrTextureProxy> clipProxy = as_IB(clipImage)->asTextureProxyRef(this->context(),
sampler, nullptr);
ctm.preConcat(clipMatrix);
SkMatrix inverseClipMatrix;
std::unique_ptr<GrFragmentProcessor> cfp;
if (clipProxy && ctm.invert(&inverseClipMatrix)) {
cfp = GrSimpleTextureEffect::Make(std::move(clipProxy), inverseClipMatrix, sampler);
if (clipImage->colorType() != kAlpha_8_SkColorType) {
cfp = GrFragmentProcessor::SwizzleOutput(std::move(cfp), GrSwizzle::AAAA());
}
}
if (cfp) {
grPaint.addCoverageFragmentProcessor(std::move(cfp));
ctm.postTranslate(SkIntToScalar(left), SkIntToScalar(top));
inverseClipMatrix.preTranslate(-SkIntToScalar(left), -SkIntToScalar(top));
SkRect clipGeometry = SkRect::MakeWH(clipImage->width(), clipImage->height());
if (!clipGeometry.contains(inverseClipMatrix.mapRect(dstRect))) {
SkClipStack clip(this->cs());
clip.clipDevRect(SkIRect::MakeXYWH(left, top, subset.width(), subset.height()),
SkClipOp::kIntersect);
SkMatrix local = SkMatrix::Concat(SkMatrix::MakeRectToRect(
dstRect, srcRect, SkMatrix::kFill_ScaleToFit), ctm);
fRenderTargetContext->fillRectWithLocalMatrix(GrClipStackClip(&clip),
std::move(grPaint), GrAA(paint.isAntiAlias()), ctm, clipGeometry, local);
return;
}
}
}
fRenderTargetContext->fillRectToRect(this->clip(), std::move(grPaint),
GrAA(paint.isAntiAlias()), SkMatrix::I(), dstRect, srcRect);
}
void SkGpuDevice::drawBitmapRect(const SkBitmap& bitmap,
const SkRect* src, const SkRect& origDst,
const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
ASSERT_SINGLE_OWNER
const SkRect* dst = &origDst;
const SkRect bmpBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
if (!src) {
src = &bmpBounds;
}
SkMatrix srcToDstMatrix;
if (!srcToDstMatrix.setRectToRect(*src, *dst, SkMatrix::kFill_ScaleToFit)) {
return;
}
SkRect tmpSrc, tmpDst;
if (src != &bmpBounds) {
if (!bmpBounds.contains(*src)) {
tmpSrc = *src;
if (!tmpSrc.intersect(bmpBounds)) {
return;
}
src = &tmpSrc;
srcToDstMatrix.mapRect(&tmpDst, *src);
dst = &tmpDst;
}
}
int maxTileSize = this->caps()->maxTileSize();
bool useCoverageAA = fRenderTargetContext->numSamples() <= 1 &&
paint.isAntiAlias() && bitmap.width() <= maxTileSize &&
bitmap.height() <= maxTileSize;
bool skipTileCheck = useCoverageAA || paint.getMaskFilter();
if (!skipTileCheck) {
int tileSize;
SkIRect clippedSrcRect;
GrSamplerState sampleState;
bool doBicubic;
GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
bitmap.width(), bitmap.height(), paint.getFilterQuality(), this->ctm(),
srcToDstMatrix, fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
int tileFilterPad;
if (doBicubic) {
tileFilterPad = GrBicubicEffect::kFilterTexelPad;
} else if (GrSamplerState::Filter::kNearest == textureFilterMode) {
tileFilterPad = 0;
} else {
tileFilterPad = 1;
}
sampleState.setFilterMode(textureFilterMode);
int maxTileSizeForFilter = this->caps()->maxTileSize() - 2 * tileFilterPad;
if (this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(), this->ctm(),
srcToDstMatrix, sampleState, src, maxTileSizeForFilter,
&tileSize, &clippedSrcRect)) {
this->drawTiledBitmap(bitmap, this->ctm(), srcToDstMatrix, *src, clippedSrcRect,
sampleState, paint, constraint, tileSize, doBicubic);
return;
}
}
GrBitmapTextureMaker maker(fContext.get(), bitmap);
this->drawTextureProducer(&maker, src, dst, constraint, this->ctm(), paint, true);
}
sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(const SkBitmap& bitmap) {
sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(fContext->priv().proxyProvider(),
bitmap);
if (!proxy) {
return nullptr;
}
const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
rect,
bitmap.getGenerationID(),
std::move(proxy),
bitmap.refColorSpace(),
&this->surfaceProps());
}
sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(const SkImage* image) {
SkPixmap pm;
if (image->isTextureBacked()) {
sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef(this->context());
return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
SkIRect::MakeWH(image->width(), image->height()),
image->uniqueID(),
std::move(proxy),
image->refColorSpace(),
&this->surfaceProps());
} else if (image->peekPixels(&pm)) {
SkBitmap bm;
bm.installPixels(pm);
return this->makeSpecial(bm);
} else {
return nullptr;
}
}
sk_sp<SkSpecialImage> SkGpuDevice::snapSpecial() {
if (this->accessRenderTargetContext()->wrapsVkSecondaryCB()) {
return nullptr;
}
sk_sp<GrTextureProxy> proxy(this->accessRenderTargetContext()->asTextureProxyRef());
if (!proxy) {
proxy = GrSurfaceProxy::Copy(fContext.get(),
this->accessRenderTargetContext()->asSurfaceProxy(),
GrMipMapped::kNo,
SkBackingFit::kApprox,
SkBudgeted::kYes);
if (!proxy) {
return nullptr;
}
}
const SkImageInfo ii = this->imageInfo();
const SkIRect srcRect = SkIRect::MakeWH(ii.width(), ii.height());
return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
srcRect,
kNeedNewImageUniqueID_SpecialImage,
std::move(proxy),
ii.refColorSpace(),
&this->surfaceProps());
}
sk_sp<SkSpecialImage> SkGpuDevice::snapBackImage(const SkIRect& subset) {
GrRenderTargetContext* rtc = this->accessRenderTargetContext();
if (rtc->wrapsVkSecondaryCB()) {
return nullptr;
}
GrContext* ctx = this->context();
SkASSERT(rtc->asSurfaceProxy());
auto srcProxy =
GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(), subset,
SkBackingFit::kApprox, rtc->asSurfaceProxy()->isBudgeted());
if (!srcProxy) {
return nullptr;
}
return SkSpecialImage::MakeDeferredFromGpu(fContext.get(),
SkIRect::MakeSize(srcProxy->isize()),
kNeedNewImageUniqueID_SpecialImage,
srcProxy,
this->imageInfo().refColorSpace(),
&this->surfaceProps());
}
void SkGpuDevice::drawDevice(SkBaseDevice* device,
int left, int top, const SkPaint& paint) {
SkASSERT(!paint.getImageFilter());
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDevice", fContext.get());
SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
sk_sp<SkSpecialImage> srcImg(dev->snapSpecial());
if (!srcImg) {
return;
}
this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
}
void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
ASSERT_SINGLE_OWNER
GrQuadAAFlags aaFlags = paint.isAntiAlias() ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
this->drawImageQuad(image, src, &dst, nullptr, GrAA(paint.isAntiAlias()), aaFlags, nullptr,
paint, constraint);
}
static GrSamplerState::Filter compute_lattice_filter_mode(const SkPaint& paint) {
if (paint.getFilterQuality() == kNone_SkFilterQuality) {
return GrSamplerState::Filter::kNearest;
}
return GrSamplerState::Filter::kBilerp;
}
void SkGpuDevice::drawImageNine(const SkImage* image,
const SkIRect& center, const SkRect& dst, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
uint32_t pinnedUniqueID;
auto iter = skstd::make_unique<SkLatticeIter>(image->width(), image->height(), center, dst);
if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(this->context(),
&pinnedUniqueID)) {
GrTextureAdjuster adjuster(this->context(), std::move(proxy),
SkColorTypeToGrColorType(image->colorType()), image->alphaType(),
pinnedUniqueID, image->colorSpace());
this->drawProducerLattice(&adjuster, std::move(iter), dst, paint);
} else {
SkBitmap bm;
if (image->isLazyGenerated()) {
GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
} else if (as_IB(image)->getROPixels(&bm)) {
GrBitmapTextureMaker maker(fContext.get(), bm);
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
}
}
}
void SkGpuDevice::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
const SkRect& dst, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
auto iter = skstd::make_unique<SkLatticeIter>(bitmap.width(), bitmap.height(), center, dst);
GrBitmapTextureMaker maker(fContext.get(), bitmap);
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
}
void SkGpuDevice::drawProducerLattice(GrTextureProducer* producer,
std::unique_ptr<SkLatticeIter> iter, const SkRect& dst,
const SkPaint& origPaint) {
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawProducerLattice", fContext.get());
SkTCopyOnFirstWrite<SkPaint> paint(&origPaint);
if (!producer->isAlphaOnly() && (paint->getColor() & 0x00FFFFFF) != 0x00FFFFFF) {
paint.writable()->setColor(SkColorSetARGB(origPaint.getAlpha(), 0xFF, 0xFF, 0xFF));
}
GrPaint grPaint;
if (!SkPaintToGrPaintWithPrimitiveColor(this->context(), fRenderTargetContext->colorSpaceInfo(),
*paint, &grPaint)) {
return;
}
auto dstColorSpace = fRenderTargetContext->colorSpaceInfo().colorSpace();
const GrSamplerState::Filter filter = compute_lattice_filter_mode(*paint);
auto proxy = producer->refTextureProxyForParams(&filter, nullptr);
if (!proxy) {
return;
}
auto csxf = GrColorSpaceXform::Make(producer->colorSpace(), producer->alphaType(),
dstColorSpace, kPremul_SkAlphaType);
fRenderTargetContext->drawImageLattice(this->clip(), std::move(grPaint), this->ctm(),
std::move(proxy), std::move(csxf), filter,
std::move(iter), dst);
}
void SkGpuDevice::drawImageLattice(const SkImage* image,
const SkCanvas::Lattice& lattice, const SkRect& dst,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
uint32_t pinnedUniqueID;
auto iter = skstd::make_unique<SkLatticeIter>(lattice, dst);
if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(this->context(),
&pinnedUniqueID)) {
GrTextureAdjuster adjuster(this->context(), std::move(proxy),
SkColorTypeToGrColorType(image->colorType()), image->alphaType(),
pinnedUniqueID, image->colorSpace());
this->drawProducerLattice(&adjuster, std::move(iter), dst, paint);
} else {
SkBitmap bm;
if (image->isLazyGenerated()) {
GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
} else if (as_IB(image)->getROPixels(&bm)) {
GrBitmapTextureMaker maker(fContext.get(), bm);
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
}
}
}
void SkGpuDevice::drawBitmapLattice(const SkBitmap& bitmap,
const SkCanvas::Lattice& lattice, const SkRect& dst,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
auto iter = skstd::make_unique<SkLatticeIter>(lattice, dst);
GrBitmapTextureMaker maker(fContext.get(), bitmap);
this->drawProducerLattice(&maker, std::move(iter), dst, paint);
}
static bool init_vertices_paint(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo,
const SkPaint& skPaint, const SkMatrix& matrix, SkBlendMode bmode,
bool hasTexs, bool hasColors, GrPaint* grPaint) {
if (hasTexs && skPaint.getShader()) {
if (hasColors) {
return SkPaintToGrPaintWithXfermode(context, colorSpaceInfo, skPaint, matrix, bmode,
grPaint);
} else {
return SkPaintToGrPaint(context, colorSpaceInfo, skPaint, matrix, grPaint);
}
} else {
if (hasColors) {
return SkPaintToGrPaintWithPrimitiveColor(context, colorSpaceInfo, skPaint, grPaint);
} else {
return SkPaintToGrPaintNoShader(context, colorSpaceInfo, skPaint, grPaint);
}
}
}
void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCount,
const SkPoint vertices[],
const SkVertices::Bone bones[], int boneCount,
SkBlendMode bmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "wireframeVertices", fContext.get());
SkPaint copy(paint);
copy.setStyle(SkPaint::kStroke_Style);
copy.setStrokeWidth(0);
GrPaint grPaint;
if (!SkPaintToGrPaintNoShader(this->context(), fRenderTargetContext->colorSpaceInfo(), copy,
&grPaint)) {
return;
}
int triangleCount = 0;
int n = (nullptr == indices) ? vertexCount : indexCount;
switch (vmode) {
case SkVertices::kTriangles_VertexMode:
triangleCount = n / 3;
break;
case SkVertices::kTriangleStrip_VertexMode:
triangleCount = n - 2;
break;
case SkVertices::kTriangleFan_VertexMode:
SK_ABORT("Unexpected triangle fan.");
break;
}
VertState state(vertexCount, indices, indexCount);
VertState::Proc vertProc = state.chooseProc(vmode);
indexCount = triangleCount * 6;
static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
SkVertices::Builder builder(kIgnoredMode, vertexCount, indexCount, 0);
memcpy(builder.positions(), vertices, vertexCount * sizeof(SkPoint));
uint16_t* lineIndices = builder.indices();
int i = 0;
while (vertProc(&state)) {
lineIndices[i] = state.f0;
lineIndices[i + 1] = state.f1;
lineIndices[i + 2] = state.f1;
lineIndices[i + 3] = state.f2;
lineIndices[i + 4] = state.f2;
lineIndices[i + 5] = state.f0;
i += 6;
}
GrPrimitiveType primitiveType = GrPrimitiveType::kLines;
fRenderTargetContext->drawVertices(this->clip(),
std::move(grPaint),
this->ctm(),
builder.detach(),
bones,
boneCount,
&primitiveType);
}
void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
int boneCount, SkBlendMode mode, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext.get());
SkASSERT(vertices);
GrPaint grPaint;
bool hasColors = vertices->hasColors();
bool hasTexs = vertices->hasTexCoords();
if ((!hasTexs || !paint.getShader()) && !hasColors) {
this->wireframeVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
bones, boneCount, mode, vertices->indices(), vertices->indexCount(),
paint);
return;
}
if (!init_vertices_paint(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint,
this->ctm(), mode, hasTexs, hasColors, &grPaint)) {
return;
}
fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), this->ctm(),
sk_ref_sp(const_cast<SkVertices*>(vertices)),
bones, boneCount);
}
void SkGpuDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawShadow", fContext.get());
if (!fRenderTargetContext->drawFastShadow(this->clip(), this->ctm(), path, rec)) {
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 SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawAtlas", fContext.get());
SkPaint p(paint);
p.setShader(atlas->makeShader());
GrPaint grPaint;
if (colors) {
if (!SkPaintToGrPaintWithXfermode(this->context(), fRenderTargetContext->colorSpaceInfo(),
p, this->ctm(), (SkBlendMode)mode, &grPaint)) {
return;
}
} else {
if (!SkPaintToGrPaint(this->context(), fRenderTargetContext->colorSpaceInfo(), p,
this->ctm(), &grPaint)) {
return;
}
}
fRenderTargetContext->drawAtlas(
this->clip(), std::move(grPaint), this->ctm(), count, xform, texRect, colors);
}
void SkGpuDevice::drawGlyphRunList(const SkGlyphRunList& glyphRunList) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawGlyphRunList", fContext.get());
const SkMatrix& ctm = this->ctm();
if (!ctm.isFinite() || !glyphRunList.allFontsFinite()) {
return;
}
fRenderTargetContext->drawGlyphRunList(this->clip(), ctm, glyphRunList);
}
void SkGpuDevice::drawDrawable(SkDrawable* drawable, const SkMatrix* matrix, SkCanvas* canvas) {
GrBackendApi api = this->context()->backend();
if (GrBackendApi::kVulkan == api) {
const SkMatrix& ctm = canvas->getTotalMatrix();
const SkMatrix& combinedMatrix = matrix ? SkMatrix::Concat(ctm, *matrix) : ctm;
std::unique_ptr<SkDrawable::GpuDrawHandler> gpuDraw =
drawable->snapGpuDrawHandler(api, combinedMatrix, canvas->getDeviceClipBounds(),
this->imageInfo());
if (gpuDraw) {
fRenderTargetContext->drawDrawable(std::move(gpuDraw), drawable->getBounds());
return;
}
}
this->INHERITED::drawDrawable(drawable, matrix, canvas);
}
void SkGpuDevice::flush() {
this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, GrFlushInfo());
}
GrSemaphoresSubmitted SkGpuDevice::flush(SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info) {
ASSERT_SINGLE_OWNER
return fRenderTargetContext->flush(access, info);
}
bool SkGpuDevice::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {
ASSERT_SINGLE_OWNER
return fRenderTargetContext->waitOnSemaphores(numSemaphores, waitSemaphores);
}
SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
ASSERT_SINGLE_OWNER
SkSurfaceProps props(this->surfaceProps().flags(), cinfo.fPixelGeometry);
SkBackingFit fit = kNever_TileUsage == cinfo.fTileUsage ? SkBackingFit::kApprox
: SkBackingFit::kExact;
GrColorType colorType = fRenderTargetContext->colorSpaceInfo().colorType();
if (colorType == GrColorType::kRGBA_1010102) {
colorType = GrColorType::kRGBA_8888;
}
sk_sp<GrRenderTargetContext> rtc(fContext->priv().makeDeferredRenderTargetContext(
fit,
cinfo.fInfo.width(),
cinfo.fInfo.height(),
colorType,
fRenderTargetContext->colorSpaceInfo().refColorSpace(),
fRenderTargetContext->numSamples(),
GrMipMapped::kNo,
kBottomLeft_GrSurfaceOrigin,
&props,
SkBudgeted::kYes,
fRenderTargetContext->asSurfaceProxy()->isProtected() ? GrProtected::kYes
: GrProtected::kNo));
if (!rtc) {
return nullptr;
}
InitContents init = cinfo.fInfo.isOpaque() ? kUninit_InitContents : kClear_InitContents;
return SkGpuDevice::Make(fContext.get(), std::move(rtc),
cinfo.fInfo.width(), cinfo.fInfo.height(), init).release();
}
sk_sp<SkSurface> SkGpuDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
ASSERT_SINGLE_OWNER
static const SkBudgeted kBudgeted = SkBudgeted::kNo;
return SkSurface::MakeRenderTarget(fContext.get(), kBudgeted, info,
fRenderTargetContext->numSamples(),
fRenderTargetContext->origin(), &props);
}
SkImageFilterCache* SkGpuDevice::getImageFilterCache() {
ASSERT_SINGLE_OWNER
return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize);
}