Flutter Engine
The Flutter Engine
SurfaceContext.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
14#include "src/core/SkMipmap.h"
15#include "src/core/SkYUVMath.h"
27#include "src/gpu/ganesh/SkGr.h"
32
33#include <memory>
34
35using namespace skia_private;
36
37#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner())
38#define RETURN_FALSE_IF_ABANDONED if (this->fContext->abandoned()) { return false; }
39#define RETURN_NULLPTR_IF_ABANDONED if (this->fContext->abandoned()) { return nullptr; }
40
41namespace skgpu::ganesh {
42
44 GrSurfaceProxyView readView,
45 const GrColorInfo& info)
46 : fContext(context), fReadView(std::move(readView)), fColorInfo(info) {
47 SkASSERT(!context->abandoned());
48}
49
50const GrCaps* SurfaceContext::caps() const { return fContext->priv().caps(); }
51
53 return fContext->priv().drawingManager();
54}
55
57 return fContext->priv().drawingManager();
58}
59
60#ifdef SK_DEBUG
61skgpu::SingleOwner* SurfaceContext::singleOwner() const { return fContext->priv().singleOwner(); }
62#endif
63
64static bool alpha_types_compatible(SkAlphaType srcAlphaType, SkAlphaType dstAlphaType) {
65 // If both alpha types are kUnknown things make sense. If not, it's too underspecified.
66 return (srcAlphaType == kUnknown_SkAlphaType) == (dstAlphaType == kUnknown_SkAlphaType);
67}
68
72 SkDEBUGCODE(this->validate();)
73 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "readPixels", fContext);
74
75 if (!fContext->priv().matches(dContext)) {
76 return false;
77 }
78
79 if (dst.colorType() == GrColorType::kUnknown) {
80 return false;
81 }
82
83 if (dst.rowBytes() % dst.info().bpp()) {
84 return false;
85 }
86
87 dst = dst.clip(this->dimensions(), &pt);
88 if (!dst.hasPixels()) {
89 return false;
90 }
91 if (!alpha_types_compatible(this->colorInfo().alphaType(), dst.alphaType())) {
92 return false;
93 }
94 // We allow unknown alpha types but only if both src and dst are unknown. Otherwise, it's too
95 // weird to reason about what should be expected.
96
98
99 if (srcProxy->framebufferOnly()) {
100 return false;
101 }
102
103 // MDB TODO: delay this instantiation until later in the method
104 if (!srcProxy->instantiate(dContext->priv().resourceProvider())) {
105 return false;
106 }
107
108 GrSurface* srcSurface = srcProxy->peekSurface();
109
111 SkColorSpaceXformSteps{this->colorInfo(), dst.info()}.flags;
112 bool unpremul = flags.unpremul,
113 needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
114 premul = flags.premul;
115
116 const GrCaps* caps = dContext->priv().caps();
117 bool srcIsCompressed = caps->isFormatCompressed(srcSurface->backendFormat());
118 // This is the getImageData equivalent to the canvas2D putImageData fast path. We probably don't
119 // care so much about getImageData performance. However, in order to ensure putImageData/
120 // getImageData in "legacy" mode are round-trippable we use the GPU to do the complementary
121 // unpremul step to writeSurfacePixels's premul step (which is determined empirically in
122 // fContext->vaildaPMUPMConversionExists()).
125 GrColorType srcColorType = this->colorInfo().colorType();
126 bool canvas2DFastPath = unpremul && !needColorConversion &&
127 (GrColorType::kRGBA_8888 == dst.colorType() ||
128 GrColorType::kBGRA_8888 == dst.colorType()) &&
129 SkToBool(srcProxy->asTextureProxy()) &&
130 (srcColorType == GrColorType::kRGBA_8888 ||
131 srcColorType == GrColorType::kBGRA_8888) &&
132 defaultRGBAFormat.isValid() &&
133 dContext->priv().validPMUPMConversionExists();
134
135 // Since the validPMUPMConversionExists function actually submits work to the gpu to do its
136 // tests, it is possible that during that call we have abandoned the context. Thus, we do
137 // another abandoned check here to make sure we are still valid.
139
140 auto readFlag = caps->surfaceSupportsReadPixels(srcSurface);
142 return false;
143 }
144
145 if (readFlag == GrCaps::SurfaceReadPixelsSupport::kCopyToTexture2D || canvas2DFastPath) {
146 std::unique_ptr<SurfaceContext> tempCtx;
147 if (this->asTextureProxy()) {
149 if (canvas2DFastPath || srcIsCompressed) {
151 } else {
152 GrBackendFormat backendFormat =
154 if (!backendFormat.isValid()) {
156 }
157 }
158
159 SkAlphaType alphaType = canvas2DFastPath ? dst.alphaType()
160 : this->colorInfo().alphaType();
161 GrImageInfo tempInfo(colorType,
162 alphaType,
163 this->colorInfo().refColorSpace(),
164 dst.dimensions());
165 auto sfc = dContext->priv().makeSFC(
166 tempInfo, "SurfaceContext_ReadPixels", SkBackingFit::kApprox);
167 if (!sfc) {
168 return false;
169 }
170
171 std::unique_ptr<GrFragmentProcessor> fp;
172 if (canvas2DFastPath) {
174 this->readSurfaceView(), this->colorInfo().alphaType()));
175 if (dst.colorType() == GrColorType::kBGRA_8888) {
177 dst = GrPixmap(dst.info().makeColorType(GrColorType::kRGBA_8888),
178 dst.addr(),
179 dst.rowBytes());
180 }
181 } else {
182 fp = GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType());
183 }
184 if (!fp) {
185 return false;
186 }
187 sfc->fillRectToRectWithFP(SkIRect::MakePtSize(pt, dst.dimensions()),
188 SkIRect::MakeSize(dst.dimensions()),
189 std::move(fp));
190 pt = {0, 0};
191 tempCtx = std::move(sfc);
192 } else {
193 auto restrictions = this->caps()->getDstCopyRestrictions(this->asRenderTargetProxy(),
194 this->colorInfo().colorType());
196 static constexpr auto kFit = SkBackingFit::kExact;
197 static constexpr auto kBudgeted = skgpu::Budgeted::kYes;
198 static constexpr auto kMipMapped = skgpu::Mipmapped::kNo;
199 if (restrictions.fMustCopyWholeSrc) {
201 std::move(srcProxy),
202 this->origin(),
203 kMipMapped,
204 kFit,
205 kBudgeted,
206 /*label=*/"SurfaceContext_ReadPixelsWithCopyWholeSrc");
207 } else {
208 auto srcRect = SkIRect::MakePtSize(pt, dst.dimensions());
210 std::move(srcProxy),
211 this->origin(),
212 kMipMapped,
213 srcRect,
214 kFit,
215 kBudgeted,
216 /*label=*/"SurfaceContext_ReadPixels",
217 restrictions.fRectsMustMatch);
218 pt = {0, 0};
219 }
220 if (!copy) {
221 return false;
222 }
223 GrSurfaceProxyView view{std::move(copy), this->origin(), this->readSwizzle()};
224 tempCtx = dContext->priv().makeSC(std::move(view), this->colorInfo());
225 SkASSERT(tempCtx);
226 }
227 return tempCtx->readPixels(dContext, dst, pt);
228 }
229
230 bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
231
232 auto supportedRead = caps->supportedReadPixelsColorType(
233 this->colorInfo().colorType(), srcProxy->backendFormat(), dst.colorType());
234
235 bool makeTight =
236 !caps->readPixelsRowBytesSupport() && dst.rowBytes() != dst.info().minRowBytes();
237
238 bool convert = unpremul || premul || needColorConversion || flip || makeTight ||
239 (dst.colorType() != supportedRead.fColorType);
240
241 std::unique_ptr<char[]> tmpPixels;
242 GrPixmap tmp;
243 void* readDst = dst.addr();
244 size_t readRB = dst.rowBytes();
245 if (convert) {
246 GrImageInfo tmpInfo(supportedRead.fColorType,
247 this->colorInfo().alphaType(),
248 this->colorInfo().refColorSpace(),
249 dst.dimensions());
250 size_t tmpRB = tmpInfo.minRowBytes();
251 size_t size = tmpRB * tmpInfo.height();
252 // Chrome MSAN bots require the data to be initialized (hence the ()).
253 tmpPixels = std::make_unique<char[]>(size);
254 tmp = {tmpInfo, tmpPixels.get(), tmpRB};
255
256 readDst = tmpPixels.get();
257 readRB = tmpRB;
258 pt.fY = flip ? srcSurface->height() - pt.fY - dst.height() : pt.fY;
259 }
260
261 dContext->priv().flushSurface(srcProxy.get());
262 dContext->submit();
263 if (!dContext->priv().getGpu()->readPixels(srcSurface,
264 SkIRect::MakePtSize(pt, dst.dimensions()),
265 this->colorInfo().colorType(),
266 supportedRead.fColorType,
267 readDst,
268 readRB)) {
269 return false;
270 }
271
272 if (tmp.hasPixels()) {
273 return GrConvertPixels(dst, tmp, flip);
274 }
275 return true;
276}
277
280 SkIPoint dstPt) {
283 SkDEBUGCODE(this->validate();)
284
285 src = src.clip(this->dimensions(), &dstPt);
286 if (!src.hasPixels()) {
287 return false;
288 }
289 if (!src.info().bpp() || src.rowBytes() % src.info().bpp()) {
290 return false;
291 }
292 return this->internalWritePixels(dContext, &src, 1, dstPt);
293}
294
296 const GrCPixmap src[],
297 int numLevels) {
300 SkDEBUGCODE(this->validate();)
301
302 SkASSERT(dContext);
303 SkASSERT(numLevels >= 1);
304 SkASSERT(src);
305
306 if (numLevels == 1) {
307 if (src->dimensions() != this->dimensions()) {
308 return false;
309 }
310 return this->writePixels(dContext, src[0], {0, 0});
311 }
312 if (!this->asTextureProxy() ||
313 this->asTextureProxy()->proxyMipmapped() == skgpu::Mipmapped::kNo) {
314 return false;
315 }
316
317 SkISize dims = this->dimensions();
318 if (numLevels != SkMipmap::ComputeLevelCount(dims) + 1) {
319 return false;
320 }
321 for (int i = 0; i < numLevels; ++i) {
322 if (src[i].colorInfo() != src[0].colorInfo()) {
323 return false;
324 }
325 if (dims != src[i].dimensions()) {
326 return false;
327 }
328 if (!src[i].info().bpp() || src[i].rowBytes() % src[i].info().bpp()) {
329 return false;
330 }
331 dims = {std::max(1, dims.width()/2), std::max(1, dims.height()/2)};
332 }
333 return this->internalWritePixels(dContext, src, numLevels, {0, 0});
334}
335
336bool SurfaceContext::internalWritePixels(GrDirectContext* dContext,
337 const GrCPixmap src[],
338 int numLevels,
339 SkIPoint pt) {
340 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "internalWritePixels", fContext);
341
342 SkASSERT(numLevels >= 1);
343 SkASSERT(src);
344
345 // We can either write to a subset or write MIP levels, but not both.
346 SkASSERT((src[0].dimensions() == this->dimensions() && pt.isZero()) || numLevels == 1);
347 SkASSERT(numLevels == 1 || (this->asTextureProxy() &&
349 // Our public caller should have clipped to the bounds of the surface already.
352
353 if (!dContext) {
354 return false;
355 }
356
357 if (this->asSurfaceProxy()->readOnly()) {
358 return false;
359 }
360
361 if (src[0].colorType() == GrColorType::kUnknown) {
362 return false;
363 }
364
365 if (!alpha_types_compatible(src[0].alphaType(), this->colorInfo().alphaType())) {
366 return false;
367 }
368
369 GrSurfaceProxy* dstProxy = this->asSurfaceProxy();
370
371 if (dstProxy->framebufferOnly()) {
372 return false;
373 }
374
375 if (!dstProxy->instantiate(dContext->priv().resourceProvider())) {
376 return false;
377 }
378
379 GrSurface* dstSurface = dstProxy->peekSurface();
380
382 SkColorSpaceXformSteps{src[0].colorInfo(), this->colorInfo()}.flags;
383 bool unpremul = flags.unpremul,
384 needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
385 premul = flags.premul;
386
387 const GrCaps* caps = dContext->priv().caps();
388
389 auto rgbaDefaultFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
391
392 GrColorType dstColorType = this->colorInfo().colorType();
393 // For canvas2D putImageData performance we have a special code path for unpremul RGBA_8888 srcs
394 // that are premultiplied on the GPU. This is kept as narrow as possible for now.
395 bool canvas2DFastPath = !caps->avoidWritePixelsFastPath() && premul && !needColorConversion &&
396 (src[0].colorType() == GrColorType::kRGBA_8888 ||
397 src[0].colorType() == GrColorType::kBGRA_8888) &&
398 this->asFillContext() &&
399 (dstColorType == GrColorType::kRGBA_8888 ||
400 dstColorType == GrColorType::kBGRA_8888) &&
401 rgbaDefaultFormat.isValid() &&
402 dContext->priv().validPMUPMConversionExists();
403
404 // Since the validPMUPMConversionExists function actually submits work to the gpu to do its
405 // tests, it is possible that during that call we have abanoned the context. Thus we do an
406 // abanoned check here to make sure we are still valid.
408
409 // Drawing code path doesn't support writing to levels and doesn't support inserting layout
410 // transitions.
411 if ((!caps->surfaceSupportsWritePixels(dstSurface) || canvas2DFastPath) && numLevels == 1) {
412 GrColorInfo tempColorInfo;
414 skgpu::Swizzle tempReadSwizzle;
415 if (canvas2DFastPath) {
416 tempColorInfo = {GrColorType::kRGBA_8888,
418 this->colorInfo().refColorSpace()};
419 format = rgbaDefaultFormat;
420 } else {
421 tempColorInfo = this->colorInfo();
422 format = dstProxy->backendFormat().makeTexture2D();
423 if (!format.isValid()) {
424 return false;
425 }
426 tempReadSwizzle = this->readSwizzle();
427 }
428
429 // It is more efficient for us to write pixels into a top left origin so we prefer that.
430 // However, if the final proxy isn't a render target then we must use a copy to move the
431 // data into it which requires the origins to match. If the final proxy is a render target
432 // we can use a draw instead which doesn't have this origin restriction. Thus for render
433 // targets we will use top left and otherwise we will make the origins match.
434 GrSurfaceOrigin tempOrigin =
436 auto tempProxy = dContext->priv().proxyProvider()->createProxy(
437 format,
438 src[0].dimensions(),
440 1,
445 /*label=*/"SurfaceContext_InternalWritePixels");
446 if (!tempProxy) {
447 return false;
448 }
449 GrSurfaceProxyView tempView(tempProxy, tempOrigin, tempReadSwizzle);
450 SurfaceContext tempCtx(dContext, tempView, tempColorInfo);
451
452 // In the fast path we always write the srcData to the temp context as though it were RGBA.
453 // When the data is really BGRA the write will cause the R and B channels to be swapped in
454 // the intermediate surface which gets corrected by a swizzle effect when drawing to the
455 // dst.
456 GrCPixmap origSrcBase = src[0];
457 GrCPixmap srcBase = origSrcBase;
458 if (canvas2DFastPath) {
459 srcBase = GrCPixmap(origSrcBase.info().makeColorType(GrColorType::kRGBA_8888),
460 origSrcBase.addr(),
461 origSrcBase.rowBytes());
462 }
463 if (!tempCtx.writePixels(dContext, srcBase, {0, 0})) {
464 return false;
465 }
466
467 if (this->asFillContext()) {
468 std::unique_ptr<GrFragmentProcessor> fp;
469 if (canvas2DFastPath) {
470 fp = dContext->priv().createUPMToPMEffect(
471 GrTextureEffect::Make(std::move(tempView), tempColorInfo.alphaType()));
472 // Important: check the original src color type here!
473 if (origSrcBase.colorType() == GrColorType::kBGRA_8888) {
475 }
476 } else {
477 fp = GrTextureEffect::Make(std::move(tempView), tempColorInfo.alphaType());
478 }
479 if (!fp) {
480 return false;
481 }
483 SkIRect::MakeSize(srcBase.dimensions()),
484 SkIRect::MakePtSize(pt, srcBase.dimensions()),
485 std::move(fp));
486 } else {
487 SkIRect srcRect = SkIRect::MakeSize(srcBase.dimensions());
488 SkIPoint dstPoint = SkIPoint::Make(pt.fX, pt.fY);
489 if (!this->copy(std::move(tempProxy), srcRect, dstPoint)) {
490 return false;
491 }
492 }
493 return true;
494 }
495
496 GrColorType srcColorType = src[0].colorType();
497 auto [allowedColorType, _] =
499 dstProxy->backendFormat(),
500 srcColorType);
501 bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
502
503 bool convertAll = premul ||
504 unpremul ||
505 needColorConversion ||
506 flip ||
507 (srcColorType != allowedColorType);
508 bool mustBeTight = !caps->writePixelsRowBytesSupport();
509 size_t tmpSize = 0;
510 if (mustBeTight || convertAll) {
511 for (int i = 0; i < numLevels; ++i) {
512 if (convertAll || (mustBeTight && src[i].rowBytes() != src[i].info().minRowBytes())) {
513 tmpSize += src[i].info().makeColorType(allowedColorType).minRowBytes()*
514 src[i].height();
515 }
516 }
517 }
518
519 auto tmpData = tmpSize ? SkData::MakeUninitialized(tmpSize) : nullptr;
520 void* tmp = tmpSize ? tmpData->writable_data() : nullptr;
521 AutoSTArray<15, GrMipLevel> srcLevels(numLevels);
522 bool ownAllStorage = true;
523 for (int i = 0; i < numLevels; ++i) {
524 if (convertAll || (mustBeTight && src[i].rowBytes() != src[i].info().minRowBytes())) {
525 GrImageInfo tmpInfo(allowedColorType,
526 this->colorInfo().alphaType(),
527 this->colorInfo().refColorSpace(),
528 src[i].dimensions());
529 auto tmpRB = tmpInfo.minRowBytes();
530 GrPixmap tmpPM(tmpInfo, tmp, tmpRB);
531 SkAssertResult(GrConvertPixels(tmpPM, src[i], flip));
532 srcLevels[i] = {tmpPM.addr(), tmpPM.rowBytes(), tmpData};
533 tmp = SkTAddOffset<void>(tmp, tmpRB*tmpPM.height());
534 } else {
535 srcLevels[i] = {src[i].addr(), src[i].rowBytes(), src[i].pixelStorage()};
536 ownAllStorage &= src[i].ownsPixels();
537 }
538 }
539 pt.fY = flip ? dstSurface->height() - pt.fY - src[0].height() : pt.fY;
540
541 if (!dContext->priv().drawingManager()->newWritePixelsTask(
542 sk_ref_sp(dstProxy),
544 allowedColorType,
545 this->colorInfo().colorType(),
546 srcLevels.begin(),
547 numLevels)) {
548 return false;
549 }
550 if (numLevels > 1) {
551 dstProxy->asTextureProxy()->markMipmapsClean();
552 }
553 if (!ownAllStorage) {
554 // If any pixmap doesn't own its pixels then we must flush so that the pixels are pushed to
555 // the GPU before we return.
556 dContext->priv().flushSurface(dstProxy);
557 }
558 return true;
559}
560
562 const SkImageInfo& info,
563 const SkIRect& srcRect,
564 RescaleGamma rescaleGamma,
565 RescaleMode rescaleMode,
567 ReadPixelsContext callbackContext) {
568 if (!dContext) {
569 callback(callbackContext, nullptr);
570 return;
571 }
572 auto rt = this->asRenderTargetProxy();
573 if (rt && rt->wrapsVkSecondaryCB()) {
574 callback(callbackContext, nullptr);
575 return;
576 }
577 if (rt && rt->framebufferOnly()) {
578 callback(callbackContext, nullptr);
579 return;
580 }
581 auto dstCT = SkColorTypeToGrColorType(info.colorType());
582 if (dstCT == GrColorType::kUnknown) {
583 callback(callbackContext, nullptr);
584 return;
585 }
586 bool needsRescale = srcRect.size() != info.dimensions() ||
588 this->colorInfo().alphaType() != info.alphaType() ||
589 !SkColorSpace::Equals(this->colorInfo().colorSpace(), info.colorSpace());
590 auto surfaceBackendFormat = this->asSurfaceProxy()->backendFormat();
591 auto readInfo = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(),
592 surfaceBackendFormat,
593 dstCT);
594 // Fail if we can't read from the source surface's color type.
595 if (readInfo.fColorType == GrColorType::kUnknown) {
596 callback(callbackContext, nullptr);
597 return;
598 }
599 // Fail if read color type does not have all of dstCT's color channels and those missing color
600 // channels are in the src.
601 uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
602 uint32_t legalReadChannels = GrColorTypeChannelFlags(readInfo.fColorType);
603 uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
604 if ((~legalReadChannels & dstChannels) & srcChannels) {
605 callback(callbackContext, nullptr);
606 return;
607 }
608
609 std::unique_ptr<SurfaceFillContext> tempFC;
610 int x = srcRect.fLeft;
611 int y = srcRect.fTop;
612 if (needsRescale) {
613 auto tempInfo = GrImageInfo(info).makeColorType(this->colorInfo().colorType());
614 tempFC = this->rescale(tempInfo, kTopLeft_GrSurfaceOrigin, srcRect,
615 rescaleGamma, rescaleMode);
616 if (!tempFC) {
617 callback(callbackContext, nullptr);
618 return;
619 }
620 SkASSERT(SkColorSpace::Equals(tempFC->colorInfo().colorSpace(), info.colorSpace()));
621 SkASSERT(tempFC->origin() == kTopLeft_GrSurfaceOrigin);
622 x = y = 0;
623 }
624 auto srcCtx = tempFC ? tempFC.get() : this;
625 return srcCtx->asyncReadPixels(dContext,
626 SkIRect::MakePtSize({x, y}, info.dimensions()),
627 info.colorType(),
628 callback,
629 callbackContext);
630}
631
633 const SkIRect& rect,
636 ReadPixelsContext callbackContext) {
639
640 SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
641 SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
642
643 if (!dContext || this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
644 callback(callbackContext, nullptr);
645 return;
646 }
647
648 auto mappedBufferManager = dContext->priv().clientMappedBufferManager();
649
650 auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
651
652 if (!transferResult.fTransferBuffer) {
653 auto ii = SkImageInfo::Make(rect.size(), colorType, this->colorInfo().alphaType(),
654 this->colorInfo().refColorSpace());
656 auto result = std::make_unique<AsyncReadResult>(kInvalid);
658 result->addCpuPlane(pm.pixelStorage(), pm.rowBytes());
659
660 SkIPoint pt{rect.fLeft, rect.fTop};
661 if (!this->readPixels(dContext, pm, pt)) {
662 callback(callbackContext, nullptr);
663 return;
664 }
665 callback(callbackContext, std::move(result));
666 return;
667 }
668
669 struct FinishContext {
670 ReadPixelsCallback* fClientCallback;
671 ReadPixelsContext fClientContext;
672 SkISize fSize;
673 GrClientMappedBufferManager* fMappedBufferManager;
674 PixelTransferResult fTransferResult;
675 };
676 // Assumption is that the caller would like to flush. We could take a parameter or require an
677 // explicit flush from the caller. We'd have to have a way to defer attaching the finish
678 // callback to GrGpu until after the next flush that flushes our op list, though.
679 auto* finishContext = new FinishContext{callback,
680 callbackContext,
681 rect.size(),
682 mappedBufferManager,
683 std::move(transferResult)};
684 auto finishCallback = [](GrGpuFinishedContext c) {
685 const auto* context = reinterpret_cast<const FinishContext*>(c);
686 auto manager = context->fMappedBufferManager;
687 auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
688 if (!result->addTransferResult(context->fTransferResult,
689 context->fSize,
690 context->fTransferResult.fRowBytes,
691 manager)) {
692 result.reset();
693 }
694 (*context->fClientCallback)(context->fClientContext, std::move(result));
695 delete context;
696 };
697 GrFlushInfo flushInfo;
698 flushInfo.fFinishedContext = finishContext;
699 flushInfo.fFinishedProc = finishCallback;
700
701 dContext->priv().flushSurface(
703}
704
706 SkYUVColorSpace yuvColorSpace,
707 bool readAlpha,
708 sk_sp<SkColorSpace> dstColorSpace,
709 const SkIRect& srcRect,
710 SkISize dstSize,
711 RescaleGamma rescaleGamma,
712 RescaleMode rescaleMode,
714 ReadPixelsContext callbackContext) {
717
718 SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
719 SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
720 SkASSERT(!dstSize.isZero());
721 SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
722
723 if (!dContext) {
724 callback(callbackContext, nullptr);
725 return;
726 }
727 auto rt = this->asRenderTargetProxy();
728 if (rt && rt->wrapsVkSecondaryCB()) {
729 callback(callbackContext, nullptr);
730 return;
731 }
732 if (rt && rt->framebufferOnly()) {
733 callback(callbackContext, nullptr);
734 return;
735 }
736 if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
737 callback(callbackContext, nullptr);
738 return;
739 }
740 int x = srcRect.fLeft;
741 int y = srcRect.fTop;
742 bool needsRescale = srcRect.size() != dstSize ||
743 !SkColorSpace::Equals(this->colorInfo().colorSpace(), dstColorSpace.get());
744 GrSurfaceProxyView srcView = this->readSurfaceView();
745 if (needsRescale) {
746 auto info = SkImageInfo::Make(dstSize,
748 this->colorInfo().alphaType(),
749 dstColorSpace);
750 // TODO: Incorporate the YUV conversion into last pass of rescaling.
751 auto tempFC = this->rescale(info,
753 srcRect,
754 rescaleGamma,
755 rescaleMode);
756 if (!tempFC) {
757 callback(callbackContext, nullptr);
758 return;
759 }
760 SkASSERT(SkColorSpace::Equals(tempFC->colorInfo().colorSpace(), info.colorSpace()));
761 SkASSERT(tempFC->origin() == kTopLeft_GrSurfaceOrigin);
762 x = y = 0;
763 srcView = tempFC->readSurfaceView();
764 } else if (!srcView.asTextureProxy()) {
765 srcView = GrSurfaceProxyView::Copy(
766 fContext,
767 std::move(srcView),
769 srcRect,
772 /*label=*/"SurfaceContext_AsyncRescaleAndReadPixelsYUV420");
773 if (!srcView) {
774 // If we can't get a texture copy of the contents then give up.
775 callback(callbackContext, nullptr);
776 return;
777 }
778 SkASSERT(srcView.asTextureProxy());
779 x = y = 0;
780 }
781
782 auto yaInfo = SkImageInfo::MakeA8(dstSize);
783 auto yFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox,
784 /* sampleCount= */ 1,
786 std::unique_ptr<SurfaceFillContext> aFC;
787 if (readAlpha) {
788 aFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox,
789 /* sampleCount= */ 1,
791 }
792
793 auto uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2);
794 auto uFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox,
795 /* sampleCount= */ 1,
797 auto vFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox,
798 /* sampleCount= */ 1,
800
801 if (!yFC || !uFC || !vFC || (readAlpha && !aFC)) {
802 callback(callbackContext, nullptr);
803 return;
804 }
805
806 float baseM[20];
807 SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
808
809 // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
810
811 auto texMatrix = SkMatrix::Translate(x, y);
812
813 auto [readCT, offsetAlignment] =
814 this->caps()->supportedReadPixelsColorType(yFC->colorInfo().colorType(),
815 yFC->asSurfaceProxy()->backendFormat(),
817 if (readCT == GrColorType::kUnknown) {
818 callback(callbackContext, nullptr);
819 return;
820 }
821 bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport() ||
822 !offsetAlignment;
823 PixelTransferResult yTransfer, aTransfer, uTransfer, vTransfer;
824
825 // This matrix generates (r,g,b,a) = (0, 0, 0, y)
826 float yM[20];
827 std::fill_n(yM, 15, 0.f);
828 std::copy_n(baseM + 0, 5, yM + 15);
829
830 auto yFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
831 yFP = GrFragmentProcessor::ColorMatrix(std::move(yFP),
832 yM,
833 /*unpremulInput=*/false,
834 /*clampRGBOutput=*/true,
835 /*premulOutput=*/false);
836 yFC->fillWithFP(std::move(yFP));
837 if (!doSynchronousRead) {
838 yTransfer = yFC->transferPixels(GrColorType::kAlpha_8,
839 SkIRect::MakeSize(yFC->dimensions()));
840 if (!yTransfer.fTransferBuffer) {
841 callback(callbackContext, nullptr);
842 return;
843 }
844 }
845
846 if (readAlpha) {
847 auto aFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
848 SkASSERT(baseM[15] == 0 &&
849 baseM[16] == 0 &&
850 baseM[17] == 0 &&
851 baseM[18] == 1 &&
852 baseM[19] == 0);
853 aFC->fillWithFP(std::move(aFP));
854 if (!doSynchronousRead) {
855 aTransfer = aFC->transferPixels(GrColorType::kAlpha_8,
856 SkIRect::MakeSize(aFC->dimensions()));
857 if (!aTransfer.fTransferBuffer) {
858 callback(callbackContext, nullptr);
859 return;
860 }
861 }
862 }
863
864 texMatrix.preScale(2.f, 2.f);
865 // This matrix generates (r,g,b,a) = (0, 0, 0, u)
866 float uM[20];
867 std::fill_n(uM, 15, 0.f);
868 std::copy_n(baseM + 5, 5, uM + 15);
869
870 auto uFP = GrTextureEffect::Make(srcView,
871 this->colorInfo().alphaType(),
872 texMatrix,
874 uFP = GrFragmentProcessor::ColorMatrix(std::move(uFP),
875 uM,
876 /*unpremulInput=*/false,
877 /*clampRGBOutput=*/true,
878 /*premulOutput=*/false);
879 uFC->fillWithFP(std::move(uFP));
880 if (!doSynchronousRead) {
881 uTransfer = uFC->transferPixels(GrColorType::kAlpha_8,
882 SkIRect::MakeSize(uFC->dimensions()));
883 if (!uTransfer.fTransferBuffer) {
884 callback(callbackContext, nullptr);
885 return;
886 }
887 }
888
889 // This matrix generates (r,g,b,a) = (0, 0, 0, v)
890 float vM[20];
891 std::fill_n(vM, 15, 0.f);
892 std::copy_n(baseM + 10, 5, vM + 15);
893 auto vFP = GrTextureEffect::Make(std::move(srcView),
894 this->colorInfo().alphaType(),
895 texMatrix,
897 vFP = GrFragmentProcessor::ColorMatrix(std::move(vFP),
898 vM,
899 /*unpremulInput=*/false,
900 /*clampRGBOutput=*/true,
901 /*premulOutput=*/false);
902 vFC->fillWithFP(std::move(vFP));
903
904 if (!doSynchronousRead) {
905 vTransfer = vFC->transferPixels(GrColorType::kAlpha_8,
906 SkIRect::MakeSize(vFC->dimensions()));
907 if (!vTransfer.fTransferBuffer) {
908 callback(callbackContext, nullptr);
909 return;
910 }
911 }
912
913 if (doSynchronousRead) {
914 GrPixmap yPmp = GrPixmap::Allocate(yaInfo);
915 GrPixmap uPmp = GrPixmap::Allocate(uvInfo);
916 GrPixmap vPmp = GrPixmap::Allocate(uvInfo);
917 GrPixmap aPmp;
918 if (readAlpha) {
919 aPmp = GrPixmap::Allocate(yaInfo);
920 }
921 if (!yFC->readPixels(dContext, yPmp, {0, 0}) ||
922 !uFC->readPixels(dContext, uPmp, {0, 0}) ||
923 !vFC->readPixels(dContext, vPmp, {0, 0}) ||
924 (readAlpha && !aFC->readPixels(dContext, aPmp, {0, 0}))) {
925 callback(callbackContext, nullptr);
926 return;
927 }
928 auto result = std::make_unique<AsyncReadResult>(dContext->directContextID());
929 result->addCpuPlane(yPmp.pixelStorage(), yPmp.rowBytes());
930 result->addCpuPlane(uPmp.pixelStorage(), uPmp.rowBytes());
931 result->addCpuPlane(vPmp.pixelStorage(), vPmp.rowBytes());
932 if (readAlpha) {
933 result->addCpuPlane(aPmp.pixelStorage(), aPmp.rowBytes());
934 }
935 callback(callbackContext, std::move(result));
936 return;
937 }
938
939 struct FinishContext {
940 ReadPixelsCallback* fClientCallback;
941 ReadPixelsContext fClientContext;
942 GrClientMappedBufferManager* fMappedBufferManager;
943 SkISize fSize;
944 PixelTransferResult fYTransfer;
945 PixelTransferResult fUTransfer;
946 PixelTransferResult fVTransfer;
947 PixelTransferResult fATransfer;
948 };
949 // Assumption is that the caller would like to flush. We could take a parameter or require an
950 // explicit flush from the caller. We'd have to have a way to defer attaching the finish
951 // callback to GrGpu until after the next flush that flushes our op list, though.
952 auto* finishContext = new FinishContext{callback,
953 callbackContext,
954 dContext->priv().clientMappedBufferManager(),
955 dstSize,
956 std::move(yTransfer),
957 std::move(uTransfer),
958 std::move(vTransfer),
959 std::move(aTransfer)};
960 auto finishCallback = [](GrGpuFinishedContext c) {
961 const auto* context = reinterpret_cast<const FinishContext*>(c);
962 auto manager = context->fMappedBufferManager;
963 auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
964 if (!result->addTransferResult(context->fYTransfer,
965 context->fSize,
966 context->fYTransfer.fRowBytes,
967 manager)) {
968 (*context->fClientCallback)(context->fClientContext, nullptr);
969 delete context;
970 return;
971 }
972 SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2};
973 if (!result->addTransferResult(context->fUTransfer,
974 uvSize,
975 context->fUTransfer.fRowBytes,
976 manager)) {
977 (*context->fClientCallback)(context->fClientContext, nullptr);
978 delete context;
979 return;
980 }
981 if (!result->addTransferResult(context->fVTransfer,
982 uvSize,
983 context->fVTransfer.fRowBytes,
984 manager)) {
985 (*context->fClientCallback)(context->fClientContext, nullptr);
986 delete context;
987 return;
988 }
989 if (context->fATransfer.fTransferBuffer &&
990 !result->addTransferResult(context->fATransfer,
991 context->fSize,
992 context->fATransfer.fRowBytes,
993 manager)) {
994 (*context->fClientCallback)(context->fClientContext, nullptr);
995 delete context;
996 return;
997 }
998 (*context->fClientCallback)(context->fClientContext, std::move(result));
999 delete context;
1000 };
1001 GrFlushInfo flushInfo;
1002 flushInfo.fFinishedContext = finishContext;
1003 flushInfo.fFinishedProc = finishCallback;
1004 dContext->priv().flushSurface(
1006}
1007
1009 SkIRect srcRect,
1010 SkIPoint dstPoint) {
1011 if (!GrClipSrcRectAndDstPoint(this->dimensions(), &dstPoint,
1012 src->dimensions(), &srcRect)) {
1013 return nullptr;
1014 }
1015
1016 SkIRect dstRect = SkIRect::MakePtSize(dstPoint, srcRect.size());
1017 return this->copyScaled(src, srcRect, dstRect, GrSamplerState::Filter::kNearest);
1018}
1019
1020sk_sp<GrRenderTask> SurfaceContext::copyScaled(sk_sp<GrSurfaceProxy> src,
1021 SkIRect srcRect,
1022 SkIRect dstRect,
1023 GrSamplerState::Filter filter) {
1026 SkDEBUGCODE(this->validate();)
1027 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "copyScaled", fContext);
1028
1029 const GrCaps* caps = fContext->priv().caps();
1030
1031 if (this->asSurfaceProxy()->framebufferOnly()) {
1032 return nullptr;
1033 }
1034
1035 // canCopySurface() verifies that src and dst rects are contained in their surfaces.
1036 if (!caps->canCopySurface(this->asSurfaceProxy(), dstRect, src.get(), srcRect)) {
1037 return nullptr;
1038 }
1039
1040 if (filter == GrSamplerState::Filter::kLinear && !src->isFunctionallyExact()) {
1041 // If we're linear filtering an image that is approx-sized, there are cases where the filter
1042 // could sample outside the logical dimensions. Specifically if we're upscaling along an
1043 // axis where we are copying up to the logical dimension, but that dimension is less than
1044 // the actual backing store dimension, the linear filter will access one texel beyond the
1045 // logical size, potentially incorporating undefined values.
1046 // NOTE: Identity scales that sample along the logical boundary of an approxi-fit texture
1047 // can still technically access a row or column of undefined data (albeit with a weight that
1048 // *should* be zero). We also disallow copying with linear filtering in that scenario,
1049 // just in case.
1050#if defined(SK_USE_SAFE_INSET_FOR_TEXTURE_SAMPLING)
1051 const bool upscalingXAtApproxEdge =
1052 dstRect.width() >= srcRect.width() &&
1053 srcRect.fRight == src->width() &&
1054 srcRect.fRight < src->backingStoreDimensions().width();
1055 const bool upscalingYAtApproxEdge =
1056 dstRect.height() >= srcRect.height() &&
1057 srcRect.fBottom == src->height() &&
1058 srcRect.fBottom < src->backingStoreDimensions().height();
1059#else
1060 // In this mode we allow non-scaling copies through even if the linear filtering would
1061 // access the adjacent undefined row or column.
1062 const bool upscalingXAtApproxEdge =
1063 dstRect.width() > srcRect.width() &&
1064 srcRect.fRight == src->width() &&
1065 srcRect.fRight < src->backingStoreDimensions().width();
1066 const bool upscalingYAtApproxEdge =
1067 dstRect.height() > srcRect.height() &&
1068 srcRect.fBottom == src->height() &&
1069 srcRect.fBottom < src->backingStoreDimensions().height();
1070#endif
1071 if (upscalingXAtApproxEdge || upscalingYAtApproxEdge) {
1072 return nullptr;
1073 }
1074
1075 // NOTE: Any upscaling with the linear filter will include content that's 1px outside the
1076 // src rect, but as long as that's still within the logical dimensions we assume it's okay.
1077 }
1078
1079 SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal);
1080 SkASSERT(src->backendFormat() == this->asSurfaceProxy()->backendFormat());
1081 return this->drawingManager()->newCopyRenderTask(this->asSurfaceProxyRef(),
1082 dstRect,
1083 std::move(src),
1084 srcRect,
1085 filter,
1086 this->origin());
1087}
1088
1089std::unique_ptr<SurfaceFillContext> SurfaceContext::rescale(const GrImageInfo& info,
1090 GrSurfaceOrigin origin,
1091 SkIRect srcRect,
1092 RescaleGamma rescaleGamma,
1093 RescaleMode rescaleMode) {
1094 auto sfc = fContext->priv().makeSFCWithFallback(info,
1096 /* sampleCount= */ 1,
1098 this->asSurfaceProxy()->isProtected(),
1099 origin);
1100 if (!sfc || !this->rescaleInto(sfc.get(),
1101 SkIRect::MakeSize(sfc->dimensions()),
1102 srcRect,
1103 rescaleGamma,
1104 rescaleMode)) {
1105 return nullptr;
1106 }
1107 return sfc;
1108}
1109
1111 SkIRect dstRect,
1112 SkIRect srcRect,
1113 RescaleGamma rescaleGamma,
1114 RescaleMode rescaleMode) {
1115 SkASSERT(dst);
1116 if (!SkIRect::MakeSize(dst->dimensions()).contains((dstRect))) {
1117 return false;
1118 }
1119
1120 auto rtProxy = this->asRenderTargetProxy();
1121 if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
1122 return false;
1123 }
1124
1125 if (this->asSurfaceProxy()->framebufferOnly()) {
1126 return false;
1127 }
1128
1129 GrSurfaceProxyView texView = this->readSurfaceView();
1130 // If we perform scaling as draws, texView must be texturable; if it's not already, we have to
1131 // make a copy. However, if the scaling can use copyScaled(), we can avoid this copy.
1132 auto ensureTexturable = [this](GrSurfaceProxyView texView, SkIRect srcRect) {
1133 if (!texView.asTextureProxy()) {
1134 // TODO: If copying supported specifying a renderable copy then we could return the copy
1135 // when there are no other conversions.
1137 std::move(texView),
1139 srcRect,
1142 "SurfaceContext_RescaleInto");
1143 if (texView) {
1144 SkASSERT(texView.asTextureProxy());
1145 srcRect = SkIRect::MakeSize(srcRect.size());
1146 }
1147 }
1148 return std::make_pair(std::move(texView), srcRect);
1149 };
1150
1151 SkISize finalSize = dstRect.size();
1152 if (finalSize == srcRect.size()) {
1153 rescaleGamma = RescaleGamma::kSrc;
1154 rescaleMode = RescaleMode::kNearest;
1155 }
1156
1157 // Within a rescaling pass A is the input (if not null) and B is the output. At the end of the
1158 // pass B is moved to A. If 'this' is the input on the first pass then tempA is null.
1159 std::unique_ptr<SurfaceFillContext> tempA;
1160 std::unique_ptr<SurfaceFillContext> tempB;
1161
1162 // Assume we should ignore the rescale linear request if the surface has no color space since
1163 // it's unclear how we'd linearize from an unknown color space.
1164 if (rescaleGamma == RescaleGamma::kLinear && this->colorInfo().colorSpace() &&
1165 !this->colorInfo().colorSpace()->gammaIsLinear()) {
1166 // Colorspace transformations are always handled by drawing so we need to be texturable
1167 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1168 if (!texView) {
1169 return false;
1170 }
1171 auto cs = this->colorInfo().colorSpace()->makeLinearGamma();
1172 // We'll fall back to kRGBA_8888 if half float not supported.
1174 dst->colorInfo().alphaType(),
1175 std::move(cs),
1176 srcRect.size());
1177 auto linearRTC = fContext->priv().makeSFCWithFallback(std::move(ii),
1179 /* sampleCount= */ 1,
1181 texView.proxy()->isProtected(),
1182 dst->origin());
1183 if (!linearRTC) {
1184 return false;
1185 }
1186 auto fp = GrTextureEffect::Make(std::move(texView),
1187 this->colorInfo().alphaType(),
1188 SkMatrix::Translate(srcRect.topLeft()),
1191 fp = GrColorSpaceXformEffect::Make(std::move(fp),
1192 this->colorInfo(),
1193 linearRTC->colorInfo());
1194 linearRTC->fillWithFP(std::move(fp));
1195 texView = linearRTC->readSurfaceView();
1196 SkASSERT(texView.asTextureProxy());
1197 tempA = std::move(linearRTC);
1198 srcRect = SkIRect::MakeSize(srcRect.size());
1199 }
1200
1201 do {
1202 SkISize nextDims = finalSize;
1203 if (rescaleMode != RescaleMode::kNearest && rescaleMode != RescaleMode::kLinear) {
1204 if (srcRect.width() > finalSize.width()) {
1205 nextDims.fWidth = std::max((srcRect.width() + 1)/2, finalSize.width());
1206 } else if (srcRect.width() < finalSize.width()) {
1207 nextDims.fWidth = std::min(srcRect.width()*2, finalSize.width());
1208 }
1209 if (srcRect.height() > finalSize.height()) {
1210 nextDims.fHeight = std::max((srcRect.height() + 1)/2, finalSize.height());
1211 } else if (srcRect.height() < finalSize.height()) {
1212 nextDims.fHeight = std::min(srcRect.height()*2, finalSize.height());
1213 }
1214 }
1215 auto input = tempA ? tempA.get() : this;
1217 SurfaceFillContext* stepDst;
1218 SkIRect stepDstRect;
1219 if (nextDims == finalSize) {
1220 stepDst = dst;
1221 stepDstRect = dstRect;
1222 xform = GrColorSpaceXform::Make(input->colorInfo(), dst->colorInfo());
1223 } else {
1224 GrImageInfo nextInfo(input->colorInfo(), nextDims);
1225
1227 /* sampleCount= */ 1,
1229 texView.proxy()->isProtected());
1230 if (!tempB) {
1231 return false;
1232 }
1233 stepDst = tempB.get();
1234 stepDstRect = SkIRect::MakeSize(tempB->dimensions());
1235 }
1236 std::unique_ptr<GrFragmentProcessor> fp;
1237 if (rescaleMode == RescaleMode::kRepeatedCubic) {
1238 // Cubic sampling is always handled by drawing with a shader, so we must be texturable
1239 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1240 if (!texView) {
1241 return false;
1242 }
1244 if (nextDims.width() == srcRect.width()) {
1246 } else if (nextDims.height() == srcRect.height()) {
1248 }
1249 static constexpr auto kWM = GrSamplerState::WrapMode::kClamp;
1250 static constexpr auto kKernel = GrBicubicEffect::gCatmullRom;
1251 fp = GrBicubicEffect::MakeSubset(std::move(texView),
1252 input->colorInfo().alphaType(),
1253 SkMatrix::I(),
1254 kWM,
1255 kWM,
1256 SkRect::Make(srcRect),
1257 kKernel,
1258 dir,
1259 *this->caps());
1260 } else {
1261 auto filter = rescaleMode == RescaleMode::kNearest ? GrSamplerState::Filter::kNearest
1263 if (xform ||
1264 texView.origin() != stepDst->origin() ||
1265 !stepDst->copyScaled(texView.refProxy(), srcRect, stepDstRect, filter)) {
1266 // We could not or were unable to successful perform a scaling blit (which can be
1267 // much faster if texView isn't already texturable). Scale by drawing instead.
1268 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1269 if (!texView) {
1270 return false;
1271 }
1272 auto srcRectF = SkRect::Make(srcRect);
1273 fp = GrTextureEffect::MakeSubset(std::move(texView),
1274 this->colorInfo().alphaType(),
1275 SkMatrix::I(),
1277 srcRectF,
1278 srcRectF,
1279 *this->caps());
1280 }
1281 }
1282 if (xform) {
1283 SkASSERT(SkToBool(fp)); // shouldn't have done a copy if there was a color xform
1284 fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform));
1285 }
1286 if (fp) {
1287 // When fp is not null, we scale by drawing; if it is null, presumably the src has
1288 // already been copied into stepDst.
1289 stepDst->fillRectToRectWithFP(srcRect, stepDstRect, std::move(fp));
1290 }
1291 texView = stepDst->readSurfaceView();
1292 tempA = std::move(tempB);
1293 srcRect = SkIRect::MakeSize(nextDims);
1294 } while (srcRect.size() != finalSize);
1295 return true;
1296}
1297
1299 const SkIRect& rect) {
1300 SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
1301 SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
1302 auto direct = fContext->asDirectContext();
1303 if (!direct) {
1304 return {};
1305 }
1306 auto rtProxy = this->asRenderTargetProxy();
1307 if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
1308 return {};
1309 }
1310
1311 auto proxy = this->asSurfaceProxy();
1312 auto supportedRead = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(),
1313 proxy->backendFormat(), dstCT);
1314 // Fail if read color type does not have all of dstCT's color channels and those missing color
1315 // channels are in the src.
1316 uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
1317 uint32_t legalReadChannels = GrColorTypeChannelFlags(supportedRead.fColorType);
1318 uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
1319 if ((~legalReadChannels & dstChannels) & srcChannels) {
1320 return {};
1321 }
1322
1323 if (!this->caps()->transferFromSurfaceToBufferSupport() ||
1324 !supportedRead.fOffsetAlignmentForTransferBuffer) {
1325 return {};
1326 }
1327
1328 size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width();
1329 rowBytes = SkAlignTo(rowBytes, this->caps()->transferBufferRowBytesAlignment());
1330 size_t size = rowBytes * rect.height();
1331 // By using kStream_GrAccessPattern here, we are not able to cache and reuse the buffer for
1332 // multiple reads. Switching to kDynamic_GrAccessPattern would allow for this, however doing
1333 // so causes a crash in a chromium test. See skbug.com/11297
1334 auto buffer = direct->priv().resourceProvider()->createBuffer(
1335 size,
1339 if (!buffer) {
1340 return {};
1341 }
1342 auto srcRect = rect;
1343 bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
1344 if (flip) {
1345 srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight,
1346 this->height() - rect.fTop);
1347 }
1349 this->colorInfo().colorType(),
1350 supportedRead.fColorType, buffer, 0);
1352 result.fTransferBuffer = std::move(buffer);
1353 auto at = this->colorInfo().alphaType();
1354 if (supportedRead.fColorType != dstCT || flip) {
1355 int w = rect.width(), h = rect.height();
1356 GrImageInfo srcInfo(supportedRead.fColorType, at, nullptr, w, h);
1357 GrImageInfo dstInfo(dstCT, at, nullptr, w, h);
1358 result.fRowBytes = dstInfo.minRowBytes();
1359 result.fPixelConverter = [dstInfo, srcInfo, rowBytes](
1360 void* dst, const void* src) {
1361 GrConvertPixels( GrPixmap(dstInfo, dst, dstInfo.minRowBytes()),
1362 GrCPixmap(srcInfo, src, rowBytes));
1363 };
1364 } else {
1365 result.fRowBytes = rowBytes;
1366 }
1367 return result;
1368}
1369
1370#ifdef SK_DEBUG
1371void SurfaceContext::validate() const {
1373 fReadView.proxy()->validate(fContext);
1374 if (this->colorInfo().colorType() != GrColorType::kUnknown) {
1376 this->colorInfo().colorType(), fReadView.proxy()->backendFormat()));
1377 }
1378 this->onValidate();
1379}
1380#endif
1381
1382} // namespace skgpu::ganesh
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
bool GrConvertPixels(const GrPixmap &dst, const GrCPixmap &src, bool flipY)
static bool GrClipSrcRectAndDstPoint(const SkISize &dstSize, SkIPoint *dstPoint, const SkISize &srcSize, SkIRect *srcRect)
Definition: GrRect.h:48
#define GR_CREATE_TRACE_MARKER_CONTEXT(classname, op, context)
Definition: GrTracing.h:18
static constexpr uint32_t GrColorTypeChannelFlags(GrColorType ct)
Definition: GrTypesPriv.h:661
static constexpr size_t GrColorTypeBytesPerPixel(GrColorType ct)
Definition: GrTypesPriv.h:896
GrColorType
Definition: GrTypesPriv.h:540
@ kStream_GrAccessPattern
Definition: GrTypesPriv.h:430
static constexpr GrColorType SkColorTypeToGrColorType(SkColorType ct)
Definition: GrTypesPriv.h:629
GrSurfaceOrigin
Definition: GrTypes.h:147
@ kBottomLeft_GrSurfaceOrigin
Definition: GrTypes.h:149
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
void * GrGpuFinishedContext
Definition: GrTypes.h:178
kUnpremul_SkAlphaType
static constexpr size_t SkAlignTo(size_t x, size_t alignment)
Definition: SkAlign.h:33
SkAlphaType
Definition: SkAlphaType.h:26
@ kUnknown_SkAlphaType
uninitialized
Definition: SkAlphaType.h:27
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkColorType
Definition: SkColorType.h:19
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
@ kInvalid
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
SkYUVColorSpace
Definition: SkImageInfo.h:68
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
const Context & fContext
SkFilterMode
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:31
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
void SkColorMatrix_RGB2YUV(SkYUVColorSpace cs, float m[20])
Definition: SkYUVMath.cpp:389
#define RETURN_FALSE_IF_ABANDONED
#define RETURN_NULLPTR_IF_ABANDONED
#define ASSERT_SINGLE_OWNER
static uint32_t premul(uint32_t color)
bool isValid() const
GrBackendFormat makeTexture2D() const
bool matches(GrContext_Base *candidate) const
const GrCaps * caps() const
static constexpr SkCubicResampler gCatmullRom
static std::unique_ptr< GrFragmentProcessor > MakeSubset(GrSurfaceProxyView view, SkAlphaType, const SkMatrix &, const GrSamplerState::WrapMode wrapX, const GrSamplerState::WrapMode wrapY, const SkRect &subset, SkCubicResampler, Direction, const GrCaps &)
Definition: GrCaps.h:57
bool avoidWritePixelsFastPath() const
Definition: GrCaps.h:141
virtual SupportedWrite supportedWritePixelsColorType(GrColorType surfaceColorType, const GrBackendFormat &surfaceFormat, GrColorType srcColorType) const =0
bool surfaceSupportsWritePixels(const GrSurface *) const
Definition: GrCaps.cpp:305
virtual SurfaceReadPixelsSupport surfaceSupportsReadPixels(const GrSurface *) const =0
virtual DstCopyRestrictions getDstCopyRestrictions(const GrRenderTargetProxy *src, GrColorType ct) const
Definition: GrCaps.h:461
bool areColorTypeAndFormatCompatible(GrColorType grCT, const GrBackendFormat &format) const
Definition: GrCaps.cpp:428
GrBackendFormat getDefaultBackendFormat(GrColorType, GrRenderable) const
Definition: GrCaps.cpp:400
bool isFormatCompressed(const GrBackendFormat &format) const
Definition: GrCaps.cpp:457
SupportedRead supportedReadPixelsColorType(GrColorType srcColorType, const GrBackendFormat &srcFormat, GrColorType dstColorType) const
Definition: GrCaps.cpp:366
bool canCopySurface(const GrSurfaceProxy *dst, const SkIRect &dstRect, const GrSurfaceProxy *src, const SkIRect &srcRect) const
Definition: GrCaps.cpp:309
bool readPixelsRowBytesSupport() const
Definition: GrCaps.h:365
bool writePixelsRowBytesSupport() const
Definition: GrCaps.h:354
bool transferFromSurfaceToBufferSupport() const
Definition: GrCaps.h:367
sk_sp< SkColorSpace > refColorSpace() const
Definition: GrColorInfo.cpp:49
SkColorSpace * colorSpace() const
Definition: GrColorInfo.cpp:48
GrColorType colorType() const
Definition: GrColorInfo.h:43
SkAlphaType alphaType() const
Definition: GrColorInfo.h:44
static std::unique_ptr< GrFragmentProcessor > Make(std::unique_ptr< GrFragmentProcessor > child, SkColorSpace *src, SkAlphaType srcAT, SkColorSpace *dst, SkAlphaType dstAT)
static sk_sp< GrColorSpaceXform > Make(SkColorSpace *src, SkAlphaType srcAT, SkColorSpace *dst, SkAlphaType dstAT)
virtual GrDirectContext * asDirectContext()
GrSemaphoresSubmitted flushSurface(GrSurfaceProxy *proxy, SkSurfaces::BackendSurfaceAccess access=SkSurfaces::BackendSurfaceAccess::kNoAccess, const GrFlushInfo &info={}, const skgpu::MutableTextureState *newState=nullptr)
GrResourceProvider * resourceProvider()
std::unique_ptr< GrFragmentProcessor > createPMToUPMEffect(std::unique_ptr< GrFragmentProcessor >)
std::unique_ptr< GrFragmentProcessor > createUPMToPMEffect(std::unique_ptr< GrFragmentProcessor >)
GrClientMappedBufferManager * clientMappedBufferManager()
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
DirectContextID directContextID() const
GrDirectContextPriv priv()
void newTransferFromRenderTask(const sk_sp< GrSurfaceProxy > &srcProxy, const SkIRect &srcRect, GrColorType surfaceColorType, GrColorType dstColorType, sk_sp< GrGpuBuffer > dstBuffer, size_t dstOffset)
sk_sp< GrRenderTask > newCopyRenderTask(sk_sp< GrSurfaceProxy > dst, SkIRect dstRect, const sk_sp< GrSurfaceProxy > &src, SkIRect srcRect, GrSamplerState::Filter filter, GrSurfaceOrigin)
bool newWritePixelsTask(sk_sp< GrSurfaceProxy > dst, SkIRect rect, GrColorType srcColorType, GrColorType dstColorType, const GrMipLevel[], int levelCount)
static std::unique_ptr< GrFragmentProcessor > SwizzleOutput(std::unique_ptr< GrFragmentProcessor >, const skgpu::Swizzle &)
static std::unique_ptr< GrFragmentProcessor > ColorMatrix(std::unique_ptr< GrFragmentProcessor > child, const float matrix[20], bool unpremulInput, bool clampRGBOutput, bool premulOutput)
bool readPixels(GrSurface *surface, SkIRect rect, GrColorType surfaceColorType, GrColorType dstColorType, void *buffer, size_t rowBytes)
Definition: GrGpu.cpp:426
GrImageInfo makeColorType(GrColorType ct) const
Definition: GrImageInfo.cpp:38
int height() const
Definition: GrImageInfo.h:56
size_t minRowBytes() const
Definition: GrImageInfo.h:60
sk_sp< SkData > pixelStorage() const
Definition: GrPixmap.h:25
const GrImageInfo & info() const
Definition: GrPixmap.h:17
size_t rowBytes() const
Definition: GrPixmap.h:21
bool hasPixels() const
Definition: GrPixmap.h:23
SkISize dimensions() const
Definition: GrPixmap.h:29
T * addr() const
Definition: GrPixmap.h:20
GrColorType colorType() const
Definition: GrPixmap.h:30
static GrPixmap Allocate(const GrImageInfo &info)
Definition: GrPixmap.h:101
sk_sp< GrTextureProxy > createProxy(const GrBackendFormat &, SkISize dimensions, GrRenderable, int renderTargetSampleCnt, skgpu::Mipmapped, SkBackingFit, skgpu::Budgeted, GrProtected, std::string_view label, GrInternalSurfaceFlags=GrInternalSurfaceFlags::kNone, UseAllocator useAllocator=UseAllocator::kYes)
GrDrawingManager * drawingManager()
GrProxyProvider * proxyProvider()
std::unique_ptr< skgpu::ganesh::SurfaceFillContext > makeSFCWithFallback(GrImageInfo, SkBackingFit, int sampleCount, skgpu::Mipmapped, skgpu::Protected, GrSurfaceOrigin=kTopLeft_GrSurfaceOrigin, skgpu::Budgeted=skgpu::Budgeted::kYes)
std::unique_ptr< skgpu::ganesh::SurfaceContext > makeSC(GrSurfaceProxyView readView, const GrColorInfo &)
std::unique_ptr< skgpu::ganesh::SurfaceFillContext > makeSFC(GrImageInfo, std::string_view label, SkBackingFit=SkBackingFit::kExact, int sampleCount=1, skgpu::Mipmapped=skgpu::Mipmapped::kNo, skgpu::Protected=skgpu::Protected::kNo, GrSurfaceOrigin=kTopLeft_GrSurfaceOrigin, skgpu::Budgeted=skgpu::Budgeted::kYes)
GrRecordingContextPriv priv()
bool abandoned() override
GrTextureProxy * asTextureProxy() const
GrSurfaceOrigin origin() const
GrSurfaceProxy * proxy() const
sk_sp< GrSurfaceProxy > refProxy() const
static GrSurfaceProxyView Copy(GrRecordingContext *context, GrSurfaceProxyView src, skgpu::Mipmapped mipmapped, SkIRect srcRect, SkBackingFit fit, skgpu::Budgeted budgeted, std::string_view label)
GrProtected isProtected() const
const GrBackendFormat & backendFormat() const
virtual bool instantiate(GrResourceProvider *)=0
bool framebufferOnly() const
static sk_sp< GrSurfaceProxy > Copy(GrRecordingContext *, sk_sp< GrSurfaceProxy > src, GrSurfaceOrigin, skgpu::Mipmapped, SkIRect srcRect, SkBackingFit, skgpu::Budgeted, std::string_view label, RectsMustMatch=RectsMustMatch::kNo, sk_sp< GrRenderTask > *outTask=nullptr)
virtual GrTextureProxy * asTextureProxy()
GrSurface * peekSurface() const
virtual GrBackendFormat backendFormat() const =0
int height() const
Definition: GrSurface.h:37
static std::unique_ptr< GrFragmentProcessor > MakeSubset(GrSurfaceProxyView, SkAlphaType, const SkMatrix &, GrSamplerState, const SkRect &subset, const GrCaps &caps, const float border[4]=kDefaultBorder, bool alwaysUseShaderTileMode=false)
static std::unique_ptr< GrFragmentProcessor > Make(GrSurfaceProxyView, SkAlphaType, const SkMatrix &=SkMatrix::I(), GrSamplerState::Filter=GrSamplerState::Filter::kNearest, GrSamplerState::MipmapMode mipmapMode=GrSamplerState::MipmapMode::kNone)
void markMipmapsClean()
static bool Equals(const SkColorSpace *, const SkColorSpace *)
sk_sp< SkColorSpace > makeLinearGamma() const
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
RescaleMode
Definition: SkImage.h:587
RescaleGamma
Definition: SkImage.h:585
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
static int ComputeLevelCount(int baseWidth, int baseHeight)
Definition: SkMipmap.cpp:134
T * get() const
Definition: SkRefCnt.h:303
static constexpr Swizzle BGRA()
Definition: Swizzle.h:67
PixelTransferResult transferPixels(GrColorType colorType, const SkIRect &rect)
GrSurfaceProxy * asSurfaceProxy()
GrDrawingManager * drawingManager()
bool readPixels(GrDirectContext *dContext, GrPixmap dst, SkIPoint srcPt)
SkDEBUGCODE(void validate() const ;) SkDEBUGCODE(skgpu GrRecordingContext * fContext
bool writePixels(GrDirectContext *dContext, GrCPixmap src, SkIPoint dstPt)
GrRenderTargetProxy * asRenderTargetProxy()
bool rescaleInto(SurfaceFillContext *dst, SkIRect dstRect, SkIRect srcRect, SkImage::RescaleGamma, SkImage::RescaleMode)
const GrCaps * caps() const
SkImage::ReadPixelsCallback ReadPixelsCallback
const GrColorInfo & colorInfo() const
GrSurfaceProxyView fReadView
GrTextureProxy * asTextureProxy()
void asyncRescaleAndReadPixels(GrDirectContext *, const SkImageInfo &info, const SkIRect &srcRect, RescaleGamma rescaleGamma, RescaleMode, ReadPixelsCallback callback, ReadPixelsContext callbackContext)
skgpu::Mipmapped mipmapped() const
GrSurfaceProxyView readSurfaceView()
SurfaceContext(GrRecordingContext *, GrSurfaceProxyView readView, const GrColorInfo &)
void asyncRescaleAndReadPixelsYUV420(GrDirectContext *, SkYUVColorSpace yuvColorSpace, bool readAlpha, sk_sp< SkColorSpace > dstColorSpace, const SkIRect &srcRect, SkISize dstSize, RescaleGamma rescaleGamma, RescaleMode, ReadPixelsCallback callback, ReadPixelsContext context)
void asyncReadPixels(GrDirectContext *, const SkIRect &srcRect, SkColorType, ReadPixelsCallback, ReadPixelsContext)
skgpu::Swizzle readSwizzle() const
SkImage::ReadPixelsContext ReadPixelsContext
std::unique_ptr< SurfaceFillContext > rescale(const GrImageInfo &info, GrSurfaceOrigin, SkIRect srcRect, SkImage::RescaleGamma, SkImage::RescaleMode)
virtual SurfaceFillContext * asFillContext()
sk_sp< GrSurfaceProxy > asSurfaceProxyRef()
GrSurfaceOrigin origin() const
void fillRectToRectWithFP(const SkRect &srcRect, const SkIRect &dstRect, std::unique_ptr< GrFragmentProcessor > fp)
static Editor::Movement convert(skui::Key key)
FlutterSemanticsFlag flags
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
uint32_t uint32_t * format
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double y
double x
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
@ kNoAccess
back-end surface will not be used by client
Definition: copy.py:1
const uint32_t fp
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Definition: switches.h:218
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
static bool alpha_types_compatible(SkAlphaType srcAlphaType, SkAlphaType dstAlphaType)
constexpr bool contains(std::string_view str, std::string_view needle)
Definition: SkStringView.h:41
Definition: ref_ptr.h:256
SkScalar w
SkScalar h
GrGpuFinishedContext fFinishedContext
Definition: GrTypes.h:220
GrGpuFinishedProc fFinishedProc
Definition: GrTypes.h:219
bool isZero() const
Definition: SkPoint_impl.h:58
int32_t fX
x-axis value
Definition: SkPoint_impl.h:29
int32_t fY
y-axis value
Definition: SkPoint_impl.h:30
static constexpr SkIPoint Make(int32_t x, int32_t y)
Definition: SkPoint_impl.h:38
Definition: SkRect.h:32
int32_t fBottom
larger y-axis bounds
Definition: SkRect.h:36
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
constexpr SkISize size() const
Definition: SkRect.h:172
constexpr int32_t height() const
Definition: SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
static constexpr SkIRect MakeSize(const SkISize &size)
Definition: SkRect.h:66
constexpr int32_t width() const
Definition: SkRect.h:158
constexpr SkIPoint topLeft() const
Definition: SkRect.h:151
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
static constexpr SkIRect MakePtSize(SkIPoint pt, SkISize size)
Definition: SkRect.h:78
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
int32_t fRight
larger x-axis bounds
Definition: SkRect.h:35
Definition: SkSize.h:16
int32_t fHeight
Definition: SkSize.h:18
int32_t fWidth
Definition: SkSize.h:17
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
bool isZero() const
Definition: SkSize.h:28
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkAlphaType alphaType() const
Definition: SkImageInfo.h:375
static SkImageInfo MakeA8(int width, int height)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669