2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkGpuDevice.h"
10 #include "GrBlurUtils.h"
11 #include "GrContext.h"
14 #include "GrGpuResourcePriv.h"
15 #include "GrImageIDTextureAdjuster.h"
17 #include "GrTracing.h"
18 #include "SkCanvasPriv.h"
19 #include "SkErrorInternals.h"
20 #include "SkGlyphCache.h"
22 #include "SkGrPixelRef.h"
24 #include "SkImage_Base.h"
25 #include "SkImageCacherator.h"
26 #include "SkImageFilter.h"
27 #include "SkImageFilterCache.h"
28 #include "SkMaskFilter.h"
29 #include "SkNinePatchIter.h"
30 #include "SkPathEffect.h"
31 #include "SkPicture.h"
32 #include "SkPictureData.h"
33 #include "SkRasterClip.h"
36 #include "SkSpecialImage.h"
38 #include "SkSurface.h"
39 #include "SkSurface_Gpu.h"
42 #include "SkVertState.h"
43 #include "SkXfermode.h"
44 #include "batches/GrRectBatchFactory.h"
45 #include "effects/GrBicubicEffect.h"
46 #include "effects/GrDashingEffect.h"
47 #include "effects/GrSimpleTextureEffect.h"
48 #include "effects/GrTextureDomain.h"
49 #include "text/GrTextUtils.h"
53 #define ASSERT_SINGLE_OWNER \
54 SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fContext->debugSingleOwner());)
56 enum { kDefaultImageFilterCacheSize = 32 * 1024 * 1024 };
59 extern bool (*gShouldDrawProc)();
60 #define CHECK_SHOULD_DRAW(draw) \
62 if (gShouldDrawProc && !gShouldDrawProc()) return; \
63 this->prepareDraw(draw); \
66 #define CHECK_SHOULD_DRAW(draw) this->prepareDraw(draw)
69 ///////////////////////////////////////////////////////////////////////////////
71 // Helper for turning a bitmap into a texture. If the bitmap is GrTexture backed this
72 // just accesses the backing GrTexture. Otherwise, it creates a cached texture
73 // representation and releases it in the destructor.
74 class AutoBitmapTexture : public SkNoncopyable {
76 AutoBitmapTexture() {}
78 AutoBitmapTexture(GrContext* context,
79 const SkBitmap& bitmap,
80 const GrTextureParams& params,
81 SkSourceGammaTreatment gammaTreatment,
82 GrTexture** texture) {
84 *texture = this->set(context, bitmap, params, gammaTreatment);
87 GrTexture* set(GrContext* context,
88 const SkBitmap& bitmap,
89 const GrTextureParams& params,
90 SkSourceGammaTreatment gammaTreatment) {
91 // Either get the texture directly from the bitmap, or else use the cache and
92 // remember to unref it.
93 if (GrTexture* bmpTexture = bitmap.getTexture()) {
94 fTexture.reset(nullptr);
97 fTexture.reset(GrRefCachedBitmapTexture(context, bitmap, params, gammaTreatment));
98 return fTexture.get();
103 SkAutoTUnref<GrTexture> fTexture;
106 ///////////////////////////////////////////////////////////////////////////////
108 /** Checks that the alpha type is legal and gets constructor flags. Returns false if device creation
110 bool SkGpuDevice::CheckAlphaTypeAndGetFlags(
111 const SkImageInfo* info, SkGpuDevice::InitContents init, unsigned* flags) {
114 switch (info->alphaType()) {
115 case kPremul_SkAlphaType:
117 case kOpaque_SkAlphaType:
118 *flags |= SkGpuDevice::kIsOpaque_Flag;
120 default: // If it is unpremul or unknown don't try to render
124 if (kClear_InitContents == init) {
125 *flags |= kNeedClear_Flag;
130 sk_sp<SkGpuDevice> SkGpuDevice::Make(sk_sp<GrRenderTarget> rt, const SkSurfaceProps* props,
132 if (!rt || rt->wasDestroyed() || !rt->getContext()) {
136 if (!CheckAlphaTypeAndGetFlags(nullptr, init, &flags)) {
140 const int width = rt->width();
141 const int height = rt->height();
143 GrContext* context = rt->getContext();
145 sk_sp<GrDrawContext> drawContext(context->drawContext(std::move(rt), props));
146 return sk_sp<SkGpuDevice>(new SkGpuDevice(std::move(drawContext), width, height, flags));
149 sk_sp<SkBaseDevice> SkGpuDevice::Make(sk_sp<GrDrawContext> drawContext,
150 int width, int height,
152 if (!drawContext || drawContext->wasAbandoned()) {
156 if (!CheckAlphaTypeAndGetFlags(nullptr, init, &flags)) {
159 return sk_sp<SkBaseDevice>(new SkGpuDevice(std::move(drawContext), width, height, flags));
162 sk_sp<SkGpuDevice> SkGpuDevice::Make(GrContext* context, SkBudgeted budgeted,
163 const SkImageInfo& info, int sampleCount,
164 const SkSurfaceProps* props, InitContents init) {
166 if (!CheckAlphaTypeAndGetFlags(&info, init, &flags)) {
170 sk_sp<GrDrawContext> drawContext(CreateDrawContext(context, budgeted, info,
171 sampleCount, props));
176 return sk_sp<SkGpuDevice>(new SkGpuDevice(std::move(drawContext),
177 info.width(), info.height(), flags));
180 SkGpuDevice::SkGpuDevice(sk_sp<GrDrawContext> drawContext, int width, int height, unsigned flags)
181 : INHERITED(drawContext->surfaceProps())
182 , fContext(SkRef(drawContext->accessRenderTarget()->getContext()))
183 , fRenderTarget(drawContext->renderTarget())
184 , fDrawContext(std::move(drawContext)) {
185 fOpaque = SkToBool(flags & kIsOpaque_Flag);
187 SkAlphaType at = fOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
188 SkImageInfo info = fRenderTarget->surfacePriv().info(at).makeWH(width, height);
189 SkPixelRef* pr = new SkGrPixelRef(info, fRenderTarget.get());
190 fLegacyBitmap.setInfo(info);
191 fLegacyBitmap.setPixelRef(pr)->unref();
193 if (flags & kNeedClear_Flag) {
198 sk_sp<GrDrawContext> SkGpuDevice::CreateDrawContext(GrContext* context,
200 const SkImageInfo& origInfo,
202 const SkSurfaceProps* surfaceProps) {
203 if (kUnknown_SkColorType == origInfo.colorType() ||
204 origInfo.width() < 0 || origInfo.height() < 0) {
212 SkColorType ct = origInfo.colorType();
213 SkAlphaType at = origInfo.alphaType();
214 SkColorSpace* cs = origInfo.colorSpace();
215 if (kRGB_565_SkColorType == ct || kGray_8_SkColorType == ct) {
216 at = kOpaque_SkAlphaType; // force this setting
218 if (kOpaque_SkAlphaType != at) {
219 at = kPremul_SkAlphaType; // force this setting
222 GrPixelConfig origConfig = SkImageInfo2GrPixelConfig(ct, at, cs, *context->caps());
223 if (!context->caps()->isConfigRenderable(origConfig, sampleCount > 0)) {
224 // Fall back from whatever ct was to default of kRGBA or kBGRA which is aliased as kN32
225 ct = kN32_SkColorType;
228 GrPixelConfig config = SkImageInfo2GrPixelConfig(ct, at, cs, *context->caps());
230 return context->newDrawContext(SkBackingFit::kExact, // Why exact?
231 origInfo.width(), origInfo.height(),
233 kDefault_GrSurfaceOrigin, surfaceProps, budgeted);
236 sk_sp<SkSpecialImage> SkGpuDevice::filterTexture(const SkDraw& draw,
237 SkSpecialImage* srcImg,
240 const SkImageFilter* filter) {
241 SkASSERT(srcImg->isTextureBacked());
244 SkMatrix matrix = *draw.fMatrix;
245 matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
246 const SkIRect clipBounds = draw.fRC->getBounds().makeOffset(-left, -top);
247 SkAutoTUnref<SkImageFilterCache> cache(this->getImageFilterCache());
248 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
250 return filter->filterImage(srcImg, ctx, offset);
254 void SkGpuDevice::drawSpriteWithFilter(const SkDraw& draw, const SkBitmap& bitmap,
255 int left, int top, const SkPaint& paint) {
257 CHECK_SHOULD_DRAW(draw);
258 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpriteWithFilter", fContext);
260 SkASSERT(paint.getImageFilter());
261 this->drawSprite(draw, bitmap, left, top, paint);
264 ///////////////////////////////////////////////////////////////////////////////
266 bool SkGpuDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
270 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels
271 GrPixelConfig config = SkImageInfo2GrPixelConfig(dstInfo, *fContext->caps());
272 if (kUnknown_GrPixelConfig == config) {
277 if (kUnpremul_SkAlphaType == dstInfo.alphaType()) {
278 flags = GrContext::kUnpremul_PixelOpsFlag;
280 return fRenderTarget->readPixels(x, y, dstInfo.width(), dstInfo.height(), config, dstPixels,
284 bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
287 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels
288 GrPixelConfig config = SkImageInfo2GrPixelConfig(info, *fContext->caps());
289 if (kUnknown_GrPixelConfig == config) {
293 if (kUnpremul_SkAlphaType == info.alphaType()) {
294 flags = GrContext::kUnpremul_PixelOpsFlag;
296 fRenderTarget->writePixels(x, y, info.width(), info.height(), config, pixels, rowBytes, flags);
298 // need to bump our genID for compatibility with clients that "know" we have a bitmap
299 fLegacyBitmap.notifyPixelsChanged();
304 const SkBitmap& SkGpuDevice::onAccessBitmap() {
306 return fLegacyBitmap;
309 bool SkGpuDevice::onAccessPixels(SkPixmap* pmap) {
311 // For compatibility with clients the know we're backed w/ a bitmap, and want to inspect its
312 // genID. When we can hide/remove that fact, we can eliminate this call to notify.
314 fLegacyBitmap.notifyPixelsChanged();
318 void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
320 INHERITED::onAttachToCanvas(canvas);
322 // Canvas promises that this ptr is valid until onDetachFromCanvas is called
323 fClipStack.reset(SkRef(canvas->getClipStack()));
326 void SkGpuDevice::onDetachFromCanvas() {
328 INHERITED::onDetachFromCanvas();
330 fClipStack.reset(nullptr);
333 // call this every draw call, to ensure that the context reflects our state,
334 // and not the state from some other canvas/device
335 void SkGpuDevice::prepareDraw(const SkDraw& draw) {
337 SkASSERT(fClipStack.get());
339 SkASSERT(draw.fClipStack && draw.fClipStack == fClipStack);
341 fClip.reset(fClipStack, &this->getOrigin());
344 GrRenderTarget* SkGpuDevice::accessRenderTarget() {
346 return fRenderTarget.get();
349 GrDrawContext* SkGpuDevice::accessDrawContext() {
351 return fDrawContext.get();
354 void SkGpuDevice::clearAll() {
357 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "clearAll", fContext);
358 SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
359 fDrawContext->clear(&rect, color, true);
362 void SkGpuDevice::replaceDrawContext(bool shouldRetainContent) {
365 SkBudgeted budgeted = fRenderTarget->resourcePriv().isBudgeted();
367 sk_sp<GrDrawContext> newDC(CreateDrawContext(this->context(),
370 fDrawContext->numColorSamples(),
371 &this->surfaceProps()));
376 if (shouldRetainContent) {
377 if (fRenderTarget->wasDestroyed()) {
380 newDC->copySurface(fDrawContext->asTexture().get(),
381 SkIRect::MakeWH(this->width(), this->height()),
382 SkIPoint::Make(0, 0));
385 SkASSERT(fDrawContext->accessRenderTarget() != newDC->accessRenderTarget());
387 fRenderTarget = newDC->renderTarget();
390 SkImageInfo info = fRenderTarget->surfacePriv().info(fOpaque ? kOpaque_SkAlphaType :
391 kPremul_SkAlphaType);
392 SkASSERT(info == fLegacyBitmap.info());
394 SkPixelRef* pr = new SkGrPixelRef(fLegacyBitmap.info(), fRenderTarget.get());
395 fLegacyBitmap.setPixelRef(pr)->unref();
397 fDrawContext = newDC;
400 ///////////////////////////////////////////////////////////////////////////////
402 void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
404 CHECK_SHOULD_DRAW(draw);
405 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPaint", fContext);
408 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
409 this->surfaceProps().isGammaCorrect(), &grPaint)) {
413 fDrawContext->drawPaint(fClip, grPaint, *draw.fMatrix);
416 // must be in SkCanvas::PointMode order
417 static const GrPrimitiveType gPointMode2PrimtiveType[] = {
418 kPoints_GrPrimitiveType,
419 kLines_GrPrimitiveType,
420 kLineStrip_GrPrimitiveType
423 // suppress antialiasing on axis-aligned integer-coordinate lines
424 static bool needs_antialiasing(SkCanvas::PointMode mode, size_t count, const SkPoint pts[]) {
425 if (mode == SkCanvas::PointMode::kPoints_PointMode) {
429 // We do not antialias as long as the primary axis of the line is integer-aligned, even if
430 // the other coordinates are not. This does mean the two end pixels of the line will be
431 // sharp even when they shouldn't be, but turning antialiasing on (as things stand
432 // currently) means that the line will turn into a two-pixel-wide blur. While obviously a
433 // more complete fix is possible down the road, for the time being we accept the error on
434 // the two end pixels as being the lesser of two evils.
435 if (pts[0].fX == pts[1].fX) {
436 return ((int) pts[0].fX) != pts[0].fX;
438 if (pts[0].fY == pts[1].fY) {
439 return ((int) pts[0].fY) != pts[0].fY;
445 void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
446 size_t count, const SkPoint pts[], const SkPaint& paint) {
448 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPoints", fContext);
449 CHECK_SHOULD_DRAW(draw);
451 SkScalar width = paint.getStrokeWidth();
456 if (paint.getPathEffect() && 2 == count && SkCanvas::kLines_PointMode == mode) {
457 GrStyle style(paint, SkPaint::kStroke_Style);
459 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
460 this->surfaceProps().isGammaCorrect(), &grPaint)) {
464 path.setIsVolatile(true);
467 fDrawContext->drawPath(fClip, grPaint, *draw.fMatrix, path, style);
471 // we only handle non-antialiased hairlines and paints without path effects or mask filters,
472 // else we let the SkDraw call our drawPath()
473 if (width > 0 || paint.getPathEffect() || paint.getMaskFilter() ||
474 (paint.isAntiAlias() && needs_antialiasing(mode, count, pts))) {
475 draw.drawPoints(mode, count, pts, paint, true);
480 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
481 this->surfaceProps().isGammaCorrect(), &grPaint)) {
485 fDrawContext->drawVertices(fClip,
488 gPointMode2PrimtiveType[mode],
497 ///////////////////////////////////////////////////////////////////////////////
499 void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) {
501 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRect", fContext);
502 CHECK_SHOULD_DRAW(draw);
505 // A couple reasons we might need to call drawPath.
506 if (paint.getMaskFilter() || paint.getPathEffect()) {
508 path.setIsVolatile(true);
510 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext.get(),
512 *draw.fMatrix, nullptr,
513 draw.fRC->getBounds(), true);
518 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
519 this->surfaceProps().isGammaCorrect(), &grPaint)) {
523 GrStyle style(paint);
524 fDrawContext->drawRect(fClip, grPaint, *draw.fMatrix, rect, &style);
527 ///////////////////////////////////////////////////////////////////////////////
529 void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
530 const SkPaint& paint) {
532 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRRect", fContext);
533 CHECK_SHOULD_DRAW(draw);
536 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
537 this->surfaceProps().isGammaCorrect(), &grPaint)) {
541 GrStyle style(paint);
542 if (paint.getMaskFilter()) {
543 // try to hit the fast path for drawing filtered round rects
546 if (rect.transform(*draw.fMatrix, &devRRect)) {
547 if (devRRect.allCornersCircular()) {
549 if (paint.getMaskFilter()->canFilterMaskGPU(devRRect,
550 draw.fRC->getBounds(),
554 maskRect.roundOut(&finalIRect);
555 if (draw.fRC->quickReject(finalIRect)) {
559 if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext->textureProvider(),
574 if (paint.getMaskFilter() || style.pathEffect()) {
575 // The only mask filter the native rrect drawing code could've handle was taken
577 // A path effect will presumably transform this rrect into something else.
579 path.setIsVolatile(true);
581 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext.get(),
583 *draw.fMatrix, nullptr,
584 draw.fRC->getBounds(), true);
588 SkASSERT(!style.pathEffect());
590 fDrawContext->drawRRect(fClip, grPaint, *draw.fMatrix, rect, style);
594 void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
595 const SkRRect& inner, const SkPaint& paint) {
597 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDRRect", fContext);
598 CHECK_SHOULD_DRAW(draw);
600 if (outer.isEmpty()) {
604 if (inner.isEmpty()) {
605 return this->drawRRect(draw, outer, paint);
608 SkStrokeRec stroke(paint);
610 if (stroke.isFillStyle() && !paint.getMaskFilter() && !paint.getPathEffect()) {
612 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
613 this->surfaceProps().isGammaCorrect(), &grPaint)) {
617 fDrawContext->drawDRRect(fClip, grPaint, *draw.fMatrix, outer, inner);
622 path.setIsVolatile(true);
623 path.addRRect(outer);
624 path.addRRect(inner);
625 path.setFillType(SkPath::kEvenOdd_FillType);
627 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext.get(),
629 *draw.fMatrix, nullptr,
630 draw.fRC->getBounds(), true);
634 /////////////////////////////////////////////////////////////////////////////
636 void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
638 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawOval", fContext);
639 CHECK_SHOULD_DRAW(draw);
641 // Presumably the path effect warps this to something other than an oval
642 if (paint.getPathEffect()) {
644 path.setIsVolatile(true);
646 this->drawPath(draw, path, paint, nullptr, true);
650 if (paint.getMaskFilter()) {
651 // The RRect path can handle special case blurring
652 SkRRect rr = SkRRect::MakeOval(oval);
653 return this->drawRRect(draw, rr, paint);
657 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
658 this->surfaceProps().isGammaCorrect(), &grPaint)) {
662 fDrawContext->drawOval(fClip, grPaint, *draw.fMatrix, oval, GrStyle(paint));
665 #include "SkMaskFilter.h"
667 ///////////////////////////////////////////////////////////////////////////////
668 void SkGpuDevice::drawStrokedLine(const SkPoint points[2],
670 const SkPaint& origPaint) {
672 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawStrokedLine", fContext);
673 CHECK_SHOULD_DRAW(draw);
675 // Adding support for round capping would require a GrDrawContext::fillRRectWithLocalMatrix
677 SkASSERT(SkPaint::kRound_Cap != origPaint.getStrokeCap());
678 SkASSERT(SkPaint::kStroke_Style == origPaint.getStyle());
679 SkASSERT(!origPaint.getPathEffect());
680 SkASSERT(!origPaint.getMaskFilter());
682 const SkScalar halfWidth = 0.5f * origPaint.getStrokeWidth();
683 SkASSERT(halfWidth > 0);
685 SkVector v = points[1] - points[0];
687 SkScalar length = SkPoint::Normalize(&v);
693 SkPaint newPaint(origPaint);
694 newPaint.setStyle(SkPaint::kFill_Style);
696 SkScalar xtraLength = 0.0f;
697 if (SkPaint::kButt_Cap != origPaint.getStrokeCap()) {
698 xtraLength = halfWidth;
701 SkPoint mid = points[0] + points[1];
704 SkRect rect = SkRect::MakeLTRB(mid.fX-halfWidth, mid.fY - 0.5f*length - xtraLength,
705 mid.fX+halfWidth, mid.fY + 0.5f*length + xtraLength);
707 m.setSinCos(v.fX, -v.fY, mid.fX, mid.fY);
711 m.postConcat(*draw.fMatrix);
714 if (!SkPaintToGrPaint(this->context(), newPaint, m,
715 this->surfaceProps().isGammaCorrect(), &grPaint)) {
719 fDrawContext->fillRectWithLocalMatrix(fClip, grPaint, m, rect, local);
722 void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
723 const SkPaint& paint, const SkMatrix* prePathMatrix,
724 bool pathIsMutable) {
726 if (!origSrcPath.isInverseFillType() && !paint.getPathEffect() && !prePathMatrix) {
728 if (SkPaint::kStroke_Style == paint.getStyle() && paint.getStrokeWidth() > 0 &&
729 !paint.getMaskFilter() && SkPaint::kRound_Cap != paint.getStrokeCap() &&
730 draw.fMatrix->preservesRightAngles() && origSrcPath.isLine(points)) {
731 // Path-based stroking looks better for thin rects
732 SkScalar strokeWidth = draw.fMatrix->getMaxScale() * paint.getStrokeWidth();
733 if (strokeWidth >= 1.0f) {
734 // Round capping support is currently disabled b.c. it would require
735 // a RRect batch that takes a localMatrix.
736 this->drawStrokedLine(points, draw, paint);
742 if (origSrcPath.isRect(&rect, &isClosed) && isClosed) {
743 this->drawRect(draw, rect, paint);
746 if (origSrcPath.isOval(&rect)) {
747 this->drawOval(draw, rect, paint);
751 if (origSrcPath.isRRect(&rrect)) {
752 this->drawRRect(draw, rrect, paint);
757 CHECK_SHOULD_DRAW(draw);
758 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPath", fContext);
760 GrBlurUtils::drawPathWithMaskFilter(fContext, fDrawContext.get(),
761 fClip, origSrcPath, paint,
762 *draw.fMatrix, prePathMatrix,
763 draw.fRC->getBounds(), pathIsMutable);
766 static const int kBmpSmallTileSize = 1 << 10;
768 static inline int get_tile_count(const SkIRect& srcRect, int tileSize) {
769 int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
770 int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
771 return tilesX * tilesY;
774 static int determine_tile_size(const SkIRect& src, int maxTileSize) {
775 if (maxTileSize <= kBmpSmallTileSize) {
779 size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
780 size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
782 maxTileTotalTileSize *= maxTileSize * maxTileSize;
783 smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
785 if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
786 return kBmpSmallTileSize;
792 // Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
793 // pixels from the bitmap are necessary.
794 static void determine_clipped_src_rect(int width, int height,
796 const SkMatrix& viewMatrix,
797 const SkISize& imageSize,
798 const SkRect* srcRectPtr,
799 SkIRect* clippedSrcIRect) {
800 clip.getConservativeBounds(width, height, clippedSrcIRect, nullptr);
802 if (!viewMatrix.invert(&inv)) {
803 clippedSrcIRect->setEmpty();
806 SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect);
807 inv.mapRect(&clippedSrcRect);
809 // we've setup src space 0,0 to map to the top left of the src rect.
810 clippedSrcRect.offset(srcRectPtr->fLeft, srcRectPtr->fTop);
811 if (!clippedSrcRect.intersect(*srcRectPtr)) {
812 clippedSrcIRect->setEmpty();
816 clippedSrcRect.roundOut(clippedSrcIRect);
817 SkIRect bmpBounds = SkIRect::MakeSize(imageSize);
818 if (!clippedSrcIRect->intersect(bmpBounds)) {
819 clippedSrcIRect->setEmpty();
823 bool SkGpuDevice::shouldTileImageID(uint32_t imageID, const SkIRect& imageRect,
824 const SkMatrix& viewMatrix,
825 const GrTextureParams& params,
826 const SkRect* srcRectPtr,
829 SkIRect* clippedSubset) const {
831 // if it's larger than the max tile size, then we have no choice but tiling.
832 if (imageRect.width() > maxTileSize || imageRect.height() > maxTileSize) {
833 determine_clipped_src_rect(fDrawContext->width(), fDrawContext->height(),
834 fClip, viewMatrix, imageRect.size(),
835 srcRectPtr, clippedSubset);
836 *tileSize = determine_tile_size(*clippedSubset, maxTileSize);
840 // If the image would only produce 4 tiles of the smaller size, don't bother tiling it.
841 const size_t area = imageRect.width() * imageRect.height();
842 if (area < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
846 // At this point we know we could do the draw by uploading the entire bitmap
847 // as a texture. However, if the texture would be large compared to the
848 // cache size and we don't require most of it for this draw then tile to
849 // reduce the amount of upload and cache spill.
851 // assumption here is that sw bitmap size is a good proxy for its size as
853 size_t bmpSize = area * sizeof(SkPMColor); // assume 32bit pixels
855 fContext->getResourceCacheLimits(nullptr, &cacheSize);
856 if (bmpSize < cacheSize / 2) {
860 // Figure out how much of the src we will need based on the src rect and clipping. Reject if
861 // tiling memory savings would be < 50%.
862 determine_clipped_src_rect(fDrawContext->width(), fDrawContext->height(),
863 fClip, viewMatrix, imageRect.size(), srcRectPtr,
865 *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
866 size_t usedTileBytes = get_tile_count(*clippedSubset, kBmpSmallTileSize) *
867 kBmpSmallTileSize * kBmpSmallTileSize;
869 return usedTileBytes < 2 * bmpSize;
872 bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
873 const SkMatrix& viewMatrix,
874 const GrTextureParams& params,
875 const SkRect* srcRectPtr,
878 SkIRect* clippedSrcRect) const {
880 // if bitmap is explictly texture backed then just use the texture
881 if (bitmap.getTexture()) {
885 return this->shouldTileImageID(bitmap.getGenerationID(), bitmap.getSubset(), viewMatrix, params,
886 srcRectPtr, maxTileSize, tileSize, clippedSrcRect);
889 bool SkGpuDevice::shouldTileImage(const SkImage* image, const SkRect* srcRectPtr,
890 SkCanvas::SrcRectConstraint constraint, SkFilterQuality quality,
891 const SkMatrix& viewMatrix) const {
893 // if image is explictly texture backed then just use the texture
894 if (as_IB(image)->peekTexture()) {
898 GrTextureParams params;
900 GrTextureParams::FilterMode textureFilterMode =
901 GrSkFilterQualityToGrFilterMode(quality, viewMatrix, SkMatrix::I(), &doBicubic);
905 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
906 } else if (GrTextureParams::kNone_FilterMode == textureFilterMode) {
911 params.setFilterMode(textureFilterMode);
913 int maxTileSize = fContext->caps()->maxTileSize() - 2 * tileFilterPad;
915 // these are output, which we safely ignore, as we just want to know the predicate
917 SkIRect outClippedSrcRect;
919 return this->shouldTileImageID(image->unique(), image->bounds(), viewMatrix, params, srcRectPtr,
920 maxTileSize, &outTileSize, &outClippedSrcRect);
923 void SkGpuDevice::drawBitmap(const SkDraw& origDraw,
924 const SkBitmap& bitmap,
926 const SkPaint& paint) {
928 CHECK_SHOULD_DRAW(origDraw);
930 viewMatrix.setConcat(*origDraw.fMatrix, m);
931 if (bitmap.getTexture()) {
932 GrBitmapTextureAdjuster adjuster(&bitmap);
933 // We can use kFast here because we know texture-backed bitmaps don't support extractSubset.
934 this->drawTextureProducer(&adjuster, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
935 viewMatrix, fClip, paint);
938 int maxTileSize = fContext->caps()->maxTileSize();
940 // The tile code path doesn't currently support AA, so if the paint asked for aa and we could
941 // draw untiled, then we bypass checking for tiling purely for optimization reasons.
942 bool drawAA = !fDrawContext->isUnifiedMultisampled() &&
943 paint.isAntiAlias() &&
944 bitmap.width() <= maxTileSize &&
945 bitmap.height() <= maxTileSize;
947 bool skipTileCheck = drawAA || paint.getMaskFilter();
949 if (!skipTileCheck) {
950 SkRect srcRect = SkRect::MakeIWH(bitmap.width(), bitmap.height());
952 SkIRect clippedSrcRect;
954 GrTextureParams params;
956 GrTextureParams::FilterMode textureFilterMode =
957 GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), viewMatrix, SkMatrix::I(),
963 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
964 } else if (GrTextureParams::kNone_FilterMode == textureFilterMode) {
969 params.setFilterMode(textureFilterMode);
971 int maxTileSizeForFilter = fContext->caps()->maxTileSize() - 2 * tileFilterPad;
972 if (this->shouldTileBitmap(bitmap, viewMatrix, params, &srcRect,
973 maxTileSizeForFilter, &tileSize, &clippedSrcRect)) {
974 this->drawTiledBitmap(bitmap, viewMatrix, srcRect, clippedSrcRect, params, paint,
975 SkCanvas::kStrict_SrcRectConstraint, tileSize, doBicubic);
979 GrBitmapTextureMaker maker(fContext, bitmap);
980 this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kStrict_SrcRectConstraint,
981 viewMatrix, fClip, paint);
984 // This method outsets 'iRect' by 'outset' all around and then clamps its extents to
985 // 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner
986 // of 'iRect' for all possible outsets/clamps.
987 static inline void clamped_outset_with_offset(SkIRect* iRect,
990 const SkIRect& clamp) {
991 iRect->outset(outset, outset);
993 int leftClampDelta = clamp.fLeft - iRect->fLeft;
994 if (leftClampDelta > 0) {
995 offset->fX -= outset - leftClampDelta;
996 iRect->fLeft = clamp.fLeft;
998 offset->fX -= outset;
1001 int topClampDelta = clamp.fTop - iRect->fTop;
1002 if (topClampDelta > 0) {
1003 offset->fY -= outset - topClampDelta;
1004 iRect->fTop = clamp.fTop;
1006 offset->fY -= outset;
1009 if (iRect->fRight > clamp.fRight) {
1010 iRect->fRight = clamp.fRight;
1012 if (iRect->fBottom > clamp.fBottom) {
1013 iRect->fBottom = clamp.fBottom;
1017 // Break 'bitmap' into several tiles to draw it since it has already
1018 // been determined to be too large to fit in VRAM
1019 void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
1020 const SkMatrix& viewMatrix,
1021 const SkRect& srcRect,
1022 const SkIRect& clippedSrcIRect,
1023 const GrTextureParams& params,
1024 const SkPaint& origPaint,
1025 SkCanvas::SrcRectConstraint constraint,
1030 // This is the funnel for all paths that draw tiled bitmaps/images. Log histogram entries.
1031 SK_HISTOGRAM_BOOLEAN("DrawTiled", true);
1032 LogDrawScaleFactor(viewMatrix, origPaint.getFilterQuality());
1034 // The following pixel lock is technically redundant, but it is desirable
1035 // to lock outside of the tile loop to prevent redecoding the whole image
1036 // at each tile in cases where 'bitmap' holds an SkDiscardablePixelRef that
1037 // is larger than the limit of the discardable memory pool.
1038 SkAutoLockPixels alp(bitmap);
1040 const SkPaint* paint = &origPaint;
1042 if (origPaint.isAntiAlias() && !fDrawContext->isUnifiedMultisampled()) {
1043 // Drop antialiasing to avoid seams at tile boundaries.
1044 tempPaint = origPaint;
1045 tempPaint.setAntiAlias(false);
1048 SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect);
1050 int nx = bitmap.width() / tileSize;
1051 int ny = bitmap.height() / tileSize;
1052 for (int x = 0; x <= nx; x++) {
1053 for (int y = 0; y <= ny; y++) {
1055 tileR.set(SkIntToScalar(x * tileSize),
1056 SkIntToScalar(y * tileSize),
1057 SkIntToScalar((x + 1) * tileSize),
1058 SkIntToScalar((y + 1) * tileSize));
1060 if (!SkRect::Intersects(tileR, clippedSrcRect)) {
1064 if (!tileR.intersect(srcRect)) {
1070 tileR.roundOut(&iTileR);
1071 SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft),
1072 SkIntToScalar(iTileR.fTop));
1074 // Adjust the context matrix to draw at the right x,y in device space
1075 SkMatrix viewM = viewMatrix;
1077 tmpM.setTranslate(offset.fX - srcRect.fLeft, offset.fY - srcRect.fTop);
1078 viewM.preConcat(tmpM);
1080 if (GrTextureParams::kNone_FilterMode != params.filterMode() || bicubic) {
1083 if (SkCanvas::kFast_SrcRectConstraint == constraint) {
1084 // In bleed mode we want to always expand the tile on all edges
1085 // but stay within the bitmap bounds
1086 iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1088 // In texture-domain/clamp mode we only want to expand the
1089 // tile on edges interior to "srcRect" (i.e., we want to
1090 // not bleed across the original clamped edges)
1091 srcRect.roundOut(&iClampRect);
1093 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1;
1094 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect);
1097 if (bitmap.extractSubset(&tmpB, iTileR)) {
1098 // now offset it to make it "local" to our tmp bitmap
1099 tileR.offset(-offset.fX, -offset.fY);
1100 GrTextureParams paramsTemp = params;
1101 // de-optimized this determination
1102 bool needsTextureDomain = true;
1103 this->internalDrawBitmap(tmpB,
1110 needsTextureDomain);
1117 * This is called by drawBitmap(), which has to handle images that may be too
1118 * large to be represented by a single texture.
1120 * internalDrawBitmap assumes that the specified bitmap will fit in a texture
1121 * and that non-texture portion of the GrPaint has already been setup.
1123 void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
1124 const SkMatrix& viewMatrix,
1125 const SkRect& srcRect,
1126 const GrTextureParams& params,
1127 const SkPaint& paint,
1128 SkCanvas::SrcRectConstraint constraint,
1130 bool needsTextureDomain) {
1131 // We should have already handled bitmaps larger than the max texture size.
1132 SkASSERT(bitmap.width() <= fContext->caps()->maxTextureSize() &&
1133 bitmap.height() <= fContext->caps()->maxTextureSize());
1134 // Unless the bitmap is inherently texture-backed, we should be respecting the max tile size
1135 // by the time we get here.
1136 SkASSERT(bitmap.getTexture() ||
1137 (bitmap.width() <= fContext->caps()->maxTileSize() &&
1138 bitmap.height() <= fContext->caps()->maxTileSize()));
1141 SkSourceGammaTreatment gammaTreatment = this->surfaceProps().isGammaCorrect()
1142 ? SkSourceGammaTreatment::kRespect : SkSourceGammaTreatment::kIgnore;
1143 AutoBitmapTexture abt(fContext, bitmap, params, gammaTreatment, &texture);
1144 if (nullptr == texture) {
1148 SkColorSpace* dstColorSpace = nullptr; // XFORMTODO
1149 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(bitmap.colorSpace(),
1151 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() };
1153 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width()));
1154 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height()));
1155 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv),
1156 SkScalarMul(srcRect.fTop, hInv),
1157 SkScalarMul(srcRect.fRight, wInv),
1158 SkScalarMul(srcRect.fBottom, hInv));
1162 if (kAlpha_8_SkColorType == bitmap.colorType() && paint.getShader()) {
1163 // In cases where we are doing an A8 bitmap draw with a shader installed, we cannot use
1164 // local coords with the bitmap draw since it may mess up texture look ups for the shader.
1165 // Thus we need to pass in the transform matrix directly to the texture processor used for
1167 texMatrix.setScale(wInv, hInv);
1170 SkRect textureDomain = SkRect::MakeEmpty();
1172 // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
1173 // the rest from the SkPaint.
1174 sk_sp<GrFragmentProcessor> fp;
1176 if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
1177 // Use a constrained texture domain to avoid color bleeding
1178 SkScalar left, top, right, bottom;
1179 if (srcRect.width() > SK_Scalar1) {
1180 SkScalar border = SK_ScalarHalf / texture->width();
1181 left = paintRect.left() + border;
1182 right = paintRect.right() - border;
1184 left = right = SkScalarHalf(paintRect.left() + paintRect.right());
1186 if (srcRect.height() > SK_Scalar1) {
1187 SkScalar border = SK_ScalarHalf / texture->height();
1188 top = paintRect.top() + border;
1189 bottom = paintRect.bottom() - border;
1191 top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
1193 textureDomain.setLTRB(left, top, right, bottom);
1195 fp = GrBicubicEffect::Make(texture, std::move(colorSpaceXform), texMatrix,
1198 fp = GrTextureDomainEffect::Make(texture, std::move(colorSpaceXform), texMatrix,
1199 textureDomain, GrTextureDomain::kClamp_Mode,
1200 params.filterMode());
1202 } else if (bicubic) {
1203 SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode());
1204 SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() };
1205 fp = GrBicubicEffect::Make(texture, std::move(colorSpaceXform), texMatrix, tileModes);
1207 fp = GrSimpleTextureEffect::Make(texture, std::move(colorSpaceXform), texMatrix, params);
1211 if (!SkPaintToGrPaintWithTexture(this->context(), paint, viewMatrix, std::move(fp),
1212 kAlpha_8_SkColorType == bitmap.colorType(),
1213 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1217 if (kAlpha_8_SkColorType == bitmap.colorType() && paint.getShader()) {
1218 // We don't have local coords in this case and have previously set the transform
1219 // matrices directly on the texture processor.
1220 fDrawContext->drawRect(fClip, grPaint, viewMatrix, dstRect);
1222 fDrawContext->fillRectToRect(fClip, grPaint, viewMatrix, dstRect, paintRect);
1226 void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
1227 int left, int top, const SkPaint& paint) {
1229 CHECK_SHOULD_DRAW(draw);
1230 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSprite", fContext);
1232 if (fContext->abandoned()) {
1236 sk_sp<GrTexture> texture = sk_ref_sp(bitmap.getTexture());
1238 SkAutoLockPixels alp(bitmap, true);
1239 if (!bitmap.readyToDraw()) {
1243 // draw sprite neither filters nor tiles.
1244 texture.reset(GrRefCachedBitmapTexture(fContext, bitmap,
1245 GrTextureParams::ClampNoFilter(),
1246 SkSourceGammaTreatment::kRespect));
1252 SkIRect srcRect = SkIRect::MakeXYWH(bitmap.pixelRefOrigin().fX,
1253 bitmap.pixelRefOrigin().fY,
1257 sk_sp<SkSpecialImage> srcImg(SkSpecialImage::MakeFromGpu(srcRect,
1258 bitmap.getGenerationID(),
1260 &this->surfaceProps()));
1262 this->drawSpecial(draw, srcImg.get(), left, top, paint);
1266 void SkGpuDevice::drawSpecial(const SkDraw& draw,
1267 SkSpecialImage* special1,
1269 const SkPaint& paint) {
1271 CHECK_SHOULD_DRAW(draw);
1272 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext);
1274 SkIPoint offset = { 0, 0 };
1276 sk_sp<SkSpecialImage> result;
1277 if (paint.getImageFilter()) {
1278 result = this->filterTexture(draw, special1, left, top,
1280 paint.getImageFilter());
1285 result = sk_ref_sp(special1);
1288 SkASSERT(result->isTextureBacked());
1289 sk_sp<GrTexture> texture = result->asTextureRef(fContext);
1291 SkPaint tmpUnfiltered(paint);
1292 tmpUnfiltered.setImageFilter(nullptr);
1295 sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture.get(), nullptr,
1297 if (GrPixelConfigIsAlphaOnly(texture->config())) {
1298 fp = GrFragmentProcessor::MulOutputByInputUnpremulColor(std::move(fp));
1300 fp = GrFragmentProcessor::MulOutputByInputAlpha(std::move(fp));
1302 if (!SkPaintToGrPaintReplaceShader(this->context(), tmpUnfiltered, std::move(fp),
1303 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1307 const SkIRect& subset = result->subset();
1309 fDrawContext->fillRectToRect(fClip,
1312 SkRect::Make(SkIRect::MakeXYWH(left + offset.fX, top + offset.fY,
1313 subset.width(), subset.height())),
1314 SkRect::MakeXYWH(SkIntToScalar(subset.fLeft) / texture->width(),
1315 SkIntToScalar(subset.fTop) / texture->height(),
1316 SkIntToScalar(subset.width()) / texture->width(),
1317 SkIntToScalar(subset.height()) / texture->height()));
1320 void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
1321 const SkRect* src, const SkRect& origDst,
1322 const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
1324 CHECK_SHOULD_DRAW(draw);
1325 if (bitmap.getTexture()) {
1326 GrBitmapTextureAdjuster adjuster(&bitmap);
1327 this->drawTextureProducer(&adjuster, src, &origDst, constraint, *draw.fMatrix, fClip,
1331 // The src rect is inferred to be the bmp bounds if not provided. Otherwise, the src rect must
1332 // be clipped to the bmp bounds. To determine tiling parameters we need the filter mode which
1333 // in turn requires knowing the src-to-dst mapping. If the src was clipped to the bmp bounds
1334 // then we use the src-to-dst mapping to compute a new clipped dst rect.
1335 const SkRect* dst = &origDst;
1336 const SkRect bmpBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
1337 // Compute matrix from the two rectangles
1342 SkMatrix srcToDstMatrix;
1343 if (!srcToDstMatrix.setRectToRect(*src, *dst, SkMatrix::kFill_ScaleToFit)) {
1346 SkRect tmpSrc, tmpDst;
1347 if (src != &bmpBounds) {
1348 if (!bmpBounds.contains(*src)) {
1350 if (!tmpSrc.intersect(bmpBounds)) {
1351 return; // nothing to draw
1354 srcToDstMatrix.mapRect(&tmpDst, *src);
1359 int maxTileSize = fContext->caps()->maxTileSize();
1361 // The tile code path doesn't currently support AA, so if the paint asked for aa and we could
1362 // draw untiled, then we bypass checking for tiling purely for optimization reasons.
1363 bool drawAA = !fDrawContext->isUnifiedMultisampled() &&
1364 paint.isAntiAlias() &&
1365 bitmap.width() <= maxTileSize &&
1366 bitmap.height() <= maxTileSize;
1368 bool skipTileCheck = drawAA || paint.getMaskFilter();
1370 if (!skipTileCheck) {
1372 SkIRect clippedSrcRect;
1374 GrTextureParams params;
1376 GrTextureParams::FilterMode textureFilterMode =
1377 GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, srcToDstMatrix,
1383 tileFilterPad = GrBicubicEffect::kFilterTexelPad;
1384 } else if (GrTextureParams::kNone_FilterMode == textureFilterMode) {
1389 params.setFilterMode(textureFilterMode);
1391 int maxTileSizeForFilter = fContext->caps()->maxTileSize() - 2 * tileFilterPad;
1392 // Fold the dst rect into the view matrix. This is only OK because we don't get here if
1393 // we have a mask filter.
1394 SkMatrix viewMatrix = *draw.fMatrix;
1395 viewMatrix.preTranslate(dst->fLeft, dst->fTop);
1396 viewMatrix.preScale(dst->width()/src->width(), dst->height()/src->height());
1397 if (this->shouldTileBitmap(bitmap, viewMatrix, params, src,
1398 maxTileSizeForFilter, &tileSize, &clippedSrcRect)) {
1399 this->drawTiledBitmap(bitmap, viewMatrix, *src, clippedSrcRect, params, paint,
1400 constraint, tileSize, doBicubic);
1404 GrBitmapTextureMaker maker(fContext, bitmap);
1405 this->drawTextureProducer(&maker, src, dst, constraint, *draw.fMatrix, fClip, paint);
1408 sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(const SkBitmap& bitmap) {
1409 SkASSERT(!bitmap.getTexture());
1411 SkAutoLockPixels alp(bitmap, true);
1412 if (!bitmap.readyToDraw()) {
1417 AutoBitmapTexture abt(fContext, bitmap, GrTextureParams::ClampNoFilter(),
1418 SkSourceGammaTreatment::kRespect, &texture);
1423 return SkSpecialImage::MakeFromGpu(bitmap.bounds(),
1424 bitmap.getGenerationID(),
1426 &this->surfaceProps());
1429 sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(SkImage* image) {
1431 if (image->isTextureBacked()) {
1432 GrTexture* texture = as_IB(image)->peekTexture();
1434 return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(image->width(), image->height()),
1437 &this->surfaceProps());
1438 } else if (image->peekPixels(&pm)) {
1441 bm.installPixels(pm);
1442 return this->makeSpecial(bm);
1448 sk_sp<SkSpecialImage> SkGpuDevice::snapSpecial() {
1449 sk_sp<GrTexture> texture(this->accessDrawContext()->asTexture());
1451 // When the device doesn't have a texture, we create a temporary texture.
1452 // TODO: we should actually only copy the portion of the source needed to apply the image
1454 texture.reset(fContext->textureProvider()->createTexture(this->accessDrawContext()->desc(),
1460 if (!fContext->copySurface(this->accessDrawContext()->accessRenderTarget(),
1466 const SkImageInfo ii = this->imageInfo();
1467 const SkIRect srcRect = SkIRect::MakeWH(ii.width(), ii.height());
1469 return SkSpecialImage::MakeFromGpu(srcRect,
1470 kNeedNewImageUniqueID_SpecialImage,
1472 &this->surfaceProps());
1475 void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
1476 int left, int top, const SkPaint& paint) {
1478 // clear of the source device must occur before CHECK_SHOULD_DRAW
1479 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawDevice", fContext);
1481 // drawDevice is defined to be in device coords.
1482 CHECK_SHOULD_DRAW(draw);
1484 SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
1485 sk_sp<SkSpecialImage> srcImg(dev->snapSpecial());
1490 this->drawSpecial(draw, srcImg.get(), left, top, paint);
1493 void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
1494 const SkPaint& paint) {
1496 SkMatrix viewMatrix = *draw.fMatrix;
1497 viewMatrix.preTranslate(x, y);
1498 if (as_IB(image)->peekTexture()) {
1499 CHECK_SHOULD_DRAW(draw);
1500 GrImageTextureAdjuster adjuster(as_IB(image));
1501 this->drawTextureProducer(&adjuster, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
1502 viewMatrix, fClip, paint);
1506 if (this->shouldTileImage(image, nullptr, SkCanvas::kFast_SrcRectConstraint,
1507 paint.getFilterQuality(), *draw.fMatrix)) {
1508 // only support tiling as bitmap at the moment, so force raster-version
1509 if (!as_IB(image)->getROPixels(&bm)) {
1512 this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
1513 } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
1514 CHECK_SHOULD_DRAW(draw);
1515 GrImageTextureMaker maker(fContext, cacher, image, SkImage::kAllow_CachingHint);
1516 this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
1517 viewMatrix, fClip, paint);
1518 } else if (as_IB(image)->getROPixels(&bm)) {
1519 this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
1524 void SkGpuDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
1525 const SkRect& dst, const SkPaint& paint,
1526 SkCanvas::SrcRectConstraint constraint) {
1528 if (as_IB(image)->peekTexture()) {
1529 CHECK_SHOULD_DRAW(draw);
1530 GrImageTextureAdjuster adjuster(as_IB(image));
1531 this->drawTextureProducer(&adjuster, src, &dst, constraint, *draw.fMatrix, fClip, paint);
1535 SkMatrix totalMatrix = *draw.fMatrix;
1536 totalMatrix.preScale(dst.width() / (src ? src->width() : image->width()),
1537 dst.height() / (src ? src->height() : image->height()));
1538 if (this->shouldTileImage(image, src, constraint, paint.getFilterQuality(), totalMatrix)) {
1539 // only support tiling as bitmap at the moment, so force raster-version
1540 if (!as_IB(image)->getROPixels(&bm)) {
1543 this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
1544 } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
1545 CHECK_SHOULD_DRAW(draw);
1546 GrImageTextureMaker maker(fContext, cacher, image, SkImage::kAllow_CachingHint);
1547 this->drawTextureProducer(&maker, src, &dst, constraint, *draw.fMatrix, fClip, paint);
1548 } else if (as_IB(image)->getROPixels(&bm)) {
1549 this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
1553 void SkGpuDevice::drawProducerNine(const SkDraw& draw, GrTextureProducer* producer,
1554 const SkIRect& center, const SkRect& dst, const SkPaint& paint) {
1555 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawProducerNine", fContext);
1557 CHECK_SHOULD_DRAW(draw);
1559 bool useFallback = paint.getMaskFilter() || paint.isAntiAlias() ||
1560 fDrawContext->isUnifiedMultisampled();
1562 GrTextureParams::FilterMode textureFilterMode =
1563 GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I(),
1565 if (useFallback || doBicubic || GrTextureParams::kNone_FilterMode != textureFilterMode) {
1566 SkNinePatchIter iter(producer->width(), producer->height(), center, dst);
1569 while (iter.next(&srcR, &dstR)) {
1570 this->drawTextureProducer(producer, &srcR, &dstR, SkCanvas::kStrict_SrcRectConstraint,
1571 *draw.fMatrix, fClip, paint);
1576 static const GrTextureParams::FilterMode kMode = GrTextureParams::kNone_FilterMode;
1577 bool gammaCorrect = this->surfaceProps().isGammaCorrect();
1578 SkSourceGammaTreatment gammaTreatment = gammaCorrect
1579 ? SkSourceGammaTreatment::kRespect : SkSourceGammaTreatment::kIgnore;
1580 sk_sp<GrFragmentProcessor> fp(
1581 producer->createFragmentProcessor(SkMatrix::I(),
1582 SkRect::MakeIWH(producer->width(), producer->height()),
1583 GrTextureProducer::kNo_FilterConstraint, true,
1584 &kMode, gammaTreatment));
1586 if (!SkPaintToGrPaintWithTexture(this->context(), paint, *draw.fMatrix, std::move(fp),
1587 producer->isAlphaOnly(), gammaCorrect, &grPaint)) {
1591 fDrawContext->drawImageNine(fClip, grPaint, *draw.fMatrix, producer->width(),
1592 producer->height(), center, dst);
1595 void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image,
1596 const SkIRect& center, const SkRect& dst, const SkPaint& paint) {
1598 if (as_IB(image)->peekTexture()) {
1599 GrImageTextureAdjuster adjuster(as_IB(image));
1600 this->drawProducerNine(draw, &adjuster, center, dst, paint);
1603 if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
1604 GrImageTextureMaker maker(fContext, cacher, image, SkImage::kAllow_CachingHint);
1605 this->drawProducerNine(draw, &maker, center, dst, paint);
1606 } else if (as_IB(image)->getROPixels(&bm)) {
1607 this->drawBitmapNine(draw, bm, center, dst, paint);
1612 void SkGpuDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center,
1613 const SkRect& dst, const SkPaint& paint) {
1615 if (bitmap.getTexture()) {
1616 GrBitmapTextureAdjuster adjuster(&bitmap);
1617 this->drawProducerNine(draw, &adjuster, center, dst, paint);
1619 GrBitmapTextureMaker maker(fContext, bitmap);
1620 this->drawProducerNine(draw, &maker, center, dst, paint);
1624 ///////////////////////////////////////////////////////////////////////////////
1626 // must be in SkCanvas::VertexMode order
1627 static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
1628 kTriangles_GrPrimitiveType,
1629 kTriangleStrip_GrPrimitiveType,
1630 kTriangleFan_GrPrimitiveType,
1633 void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
1634 int vertexCount, const SkPoint vertices[],
1635 const SkPoint texs[], const SkColor colors[],
1637 const uint16_t indices[], int indexCount,
1638 const SkPaint& paint) {
1640 CHECK_SHOULD_DRAW(draw);
1641 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext);
1643 // If both textures and vertex-colors are nullptr, strokes hairlines with the paint's color.
1644 if ((nullptr == texs || nullptr == paint.getShader()) && nullptr == colors) {
1648 SkPaint copy(paint);
1649 copy.setStyle(SkPaint::kStroke_Style);
1650 copy.setStrokeWidth(0);
1653 // we ignore the shader if texs is null.
1654 if (!SkPaintToGrPaintNoShader(this->context(), copy,
1655 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1659 int triangleCount = 0;
1660 int n = (nullptr == indices) ? vertexCount : indexCount;
1662 case SkCanvas::kTriangles_VertexMode:
1663 triangleCount = n / 3;
1665 case SkCanvas::kTriangleStrip_VertexMode:
1666 case SkCanvas::kTriangleFan_VertexMode:
1667 triangleCount = n - 2;
1671 VertState state(vertexCount, indices, indexCount);
1672 VertState::Proc vertProc = state.chooseProc(vmode);
1674 //number of indices for lines per triangle with kLines
1675 indexCount = triangleCount * 6;
1677 SkAutoTDeleteArray<uint16_t> lineIndices(new uint16_t[indexCount]);
1679 while (vertProc(&state)) {
1680 lineIndices[i] = state.f0;
1681 lineIndices[i + 1] = state.f1;
1682 lineIndices[i + 2] = state.f1;
1683 lineIndices[i + 3] = state.f2;
1684 lineIndices[i + 4] = state.f2;
1685 lineIndices[i + 5] = state.f0;
1688 fDrawContext->drawVertices(fClip,
1691 kLines_GrPrimitiveType,
1701 GrPrimitiveType primType = gVertexMode2PrimitiveType[vmode];
1703 SkAutoSTMalloc<128, GrColor> convertedColors(0);
1705 // need to convert byte order and from non-PM to PM. TODO: Keep unpremul until after
1707 convertedColors.reset(vertexCount);
1708 for (int i = 0; i < vertexCount; ++i) {
1709 convertedColors[i] = SkColorToPremulGrColor(colors[i]);
1711 colors = convertedColors.get();
1714 if (texs && paint.getShader()) {
1716 // When there are texs and colors the shader and colors are combined using xmode. A null
1717 // xmode is defined to mean modulate.
1718 SkXfermode::Mode colorMode;
1720 if (!xmode->asMode(&colorMode)) {
1724 colorMode = SkXfermode::kModulate_Mode;
1726 if (!SkPaintToGrPaintWithXfermode(this->context(), paint, *draw.fMatrix, colorMode,
1727 false, this->surfaceProps().isGammaCorrect(),
1732 // We have a shader, but no colors to blend it against.
1733 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
1734 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1740 // We have colors, but either have no shader or no texture coords (which implies that
1741 // we should ignore the shader).
1742 if (!SkPaintToGrPaintWithPrimitiveColor(this->context(), paint,
1743 this->surfaceProps().isGammaCorrect(),
1748 // No colors and no shaders. Just draw with the paint color.
1749 if (!SkPaintToGrPaintNoShader(this->context(), paint,
1750 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1756 fDrawContext->drawVertices(fClip,
1768 ///////////////////////////////////////////////////////////////////////////////
1770 void SkGpuDevice::drawAtlas(const SkDraw& draw, const SkImage* atlas, const SkRSXform xform[],
1771 const SkRect texRect[], const SkColor colors[], int count,
1772 SkXfermode::Mode mode, const SkPaint& paint) {
1774 if (paint.isAntiAlias()) {
1775 this->INHERITED::drawAtlas(draw, atlas, xform, texRect, colors, count, mode, paint);
1779 CHECK_SHOULD_DRAW(draw);
1780 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawText", fContext);
1783 p.setShader(atlas->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode));
1787 if (!SkPaintToGrPaintWithXfermode(this->context(), p, *draw.fMatrix, mode, true,
1788 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1792 if (!SkPaintToGrPaint(this->context(), p, *draw.fMatrix,
1793 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1798 SkDEBUGCODE(this->validate();)
1799 fDrawContext->drawAtlas(fClip, grPaint, *draw.fMatrix, count, xform, texRect, colors);
1802 ///////////////////////////////////////////////////////////////////////////////
1804 void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
1805 size_t byteLength, SkScalar x, SkScalar y,
1806 const SkPaint& paint) {
1808 CHECK_SHOULD_DRAW(draw);
1809 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawText", fContext);
1812 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
1813 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1817 SkDEBUGCODE(this->validate();)
1819 fDrawContext->drawText(fClip, grPaint, paint, *draw.fMatrix,
1820 (const char *)text, byteLength, x, y, draw.fRC->getBounds());
1823 void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, size_t byteLength,
1824 const SkScalar pos[], int scalarsPerPos,
1825 const SkPoint& offset, const SkPaint& paint) {
1827 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPosText", fContext);
1828 CHECK_SHOULD_DRAW(draw);
1831 if (!SkPaintToGrPaint(this->context(), paint, *draw.fMatrix,
1832 this->surfaceProps().isGammaCorrect(), &grPaint)) {
1836 SkDEBUGCODE(this->validate();)
1838 fDrawContext->drawPosText(fClip, grPaint, paint, *draw.fMatrix,
1839 (const char *)text, byteLength, pos, scalarsPerPos, offset,
1840 draw.fRC->getBounds());
1843 void SkGpuDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y,
1844 const SkPaint& paint, SkDrawFilter* drawFilter) {
1846 GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawTextBlob", fContext);
1847 CHECK_SHOULD_DRAW(draw);
1849 SkDEBUGCODE(this->validate();)
1851 fDrawContext->drawTextBlob(fClip, paint, *draw.fMatrix,
1852 blob, x, y, drawFilter, draw.fRC->getBounds());
1855 ///////////////////////////////////////////////////////////////////////////////
1857 bool SkGpuDevice::onShouldDisableLCD(const SkPaint& paint) const {
1858 return GrTextUtils::ShouldDisableLCD(paint);
1861 void SkGpuDevice::flush() {
1864 fRenderTarget->prepareForExternalIO();
1867 ///////////////////////////////////////////////////////////////////////////////
1869 SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
1872 SkSurfaceProps props(this->surfaceProps().flags(), cinfo.fPixelGeometry);
1874 // layers are never drawn in repeat modes, so we can request an approx
1875 // match and ignore any padding.
1876 SkBackingFit fit = kNever_TileUsage == cinfo.fTileUsage ? SkBackingFit::kApprox
1877 : SkBackingFit::kExact;
1879 sk_sp<GrDrawContext> dc(fContext->newDrawContext(fit,
1880 cinfo.fInfo.width(), cinfo.fInfo.height(),
1881 fDrawContext->config(),
1882 fDrawContext->desc().fSampleCnt,
1883 kDefault_GrSurfaceOrigin,
1886 SkErrorInternals::SetError( kInternalError_SkError,
1887 "---- failed to create gpu device texture [%d %d]\n",
1888 cinfo.fInfo.width(), cinfo.fInfo.height());
1892 // Skia's convention is to only clear a device if it is non-opaque.
1893 InitContents init = cinfo.fInfo.isOpaque() ? kUninit_InitContents : kClear_InitContents;
1895 return SkGpuDevice::Make(std::move(dc),
1896 cinfo.fInfo.width(), cinfo.fInfo.height(),
1900 sk_sp<SkSurface> SkGpuDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1902 // TODO: Change the signature of newSurface to take a budgeted parameter.
1903 static const SkBudgeted kBudgeted = SkBudgeted::kNo;
1904 return SkSurface::MakeRenderTarget(fContext, kBudgeted, info, fDrawContext->desc().fSampleCnt,
1908 SkImageFilterCache* SkGpuDevice::getImageFilterCache() {
1910 // We always return a transient cache, so it is freed after each
1911 // filter traversal.
1912 return SkImageFilterCache::Create(kDefaultImageFilterCacheSize);