Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
55
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()).
124 GrRenderable::kYes);
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()) {
148 GrColorType colorType = (canvas2DFastPath || srcIsCompressed)
150 : this->colorInfo().colorType();
151 SkAlphaType alphaType = canvas2DFastPath ? dst.alphaType()
152 : this->colorInfo().alphaType();
153 GrImageInfo tempInfo(colorType,
154 alphaType,
155 this->colorInfo().refColorSpace(),
156 dst.dimensions());
157 auto sfc = dContext->priv().makeSFC(
158 tempInfo, "SurfaceContext_ReadPixels", SkBackingFit::kApprox);
159 if (!sfc) {
160 return false;
161 }
162
163 std::unique_ptr<GrFragmentProcessor> fp;
164 if (canvas2DFastPath) {
166 this->readSurfaceView(), this->colorInfo().alphaType()));
167 if (dst.colorType() == GrColorType::kBGRA_8888) {
169 dst = GrPixmap(dst.info().makeColorType(GrColorType::kRGBA_8888),
170 dst.addr(),
171 dst.rowBytes());
172 }
173 } else {
174 fp = GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType());
175 }
176 if (!fp) {
177 return false;
178 }
179 sfc->fillRectToRectWithFP(SkIRect::MakePtSize(pt, dst.dimensions()),
180 SkIRect::MakeSize(dst.dimensions()),
181 std::move(fp));
182 pt = {0, 0};
183 tempCtx = std::move(sfc);
184 } else {
185 auto restrictions = this->caps()->getDstCopyRestrictions(this->asRenderTargetProxy(),
186 this->colorInfo().colorType());
188 static constexpr auto kFit = SkBackingFit::kExact;
189 static constexpr auto kBudgeted = skgpu::Budgeted::kYes;
190 static constexpr auto kMipMapped = skgpu::Mipmapped::kNo;
191 if (restrictions.fMustCopyWholeSrc) {
193 std::move(srcProxy),
194 this->origin(),
195 kMipMapped,
196 kFit,
197 kBudgeted,
198 /*label=*/"SurfaceContext_ReadPixelsWithCopyWholeSrc");
199 } else {
200 auto srcRect = SkIRect::MakePtSize(pt, dst.dimensions());
202 std::move(srcProxy),
203 this->origin(),
204 kMipMapped,
205 srcRect,
206 kFit,
207 kBudgeted,
208 /*label=*/"SurfaceContext_ReadPixels",
209 restrictions.fRectsMustMatch);
210 pt = {0, 0};
211 }
212 if (!copy) {
213 return false;
214 }
215 GrSurfaceProxyView view{std::move(copy), this->origin(), this->readSwizzle()};
216 tempCtx = dContext->priv().makeSC(std::move(view), this->colorInfo());
217 SkASSERT(tempCtx);
218 }
219 return tempCtx->readPixels(dContext, dst, pt);
220 }
221
222 bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
223
224 auto supportedRead = caps->supportedReadPixelsColorType(
225 this->colorInfo().colorType(), srcProxy->backendFormat(), dst.colorType());
226
227 bool makeTight =
228 !caps->readPixelsRowBytesSupport() && dst.rowBytes() != dst.info().minRowBytes();
229
230 bool convert = unpremul || premul || needColorConversion || flip || makeTight ||
231 (dst.colorType() != supportedRead.fColorType);
232
233 std::unique_ptr<char[]> tmpPixels;
234 GrPixmap tmp;
235 void* readDst = dst.addr();
236 size_t readRB = dst.rowBytes();
237 if (convert) {
238 GrImageInfo tmpInfo(supportedRead.fColorType,
239 this->colorInfo().alphaType(),
240 this->colorInfo().refColorSpace(),
241 dst.dimensions());
242 size_t tmpRB = tmpInfo.minRowBytes();
243 size_t size = tmpRB * tmpInfo.height();
244 // Chrome MSAN bots require the data to be initialized (hence the ()).
245 tmpPixels = std::make_unique<char[]>(size);
246 tmp = {tmpInfo, tmpPixels.get(), tmpRB};
247
248 readDst = tmpPixels.get();
249 readRB = tmpRB;
250 pt.fY = flip ? srcSurface->height() - pt.fY - dst.height() : pt.fY;
251 }
252
253 dContext->priv().flushSurface(srcProxy.get());
254 dContext->submit();
255 if (!dContext->priv().getGpu()->readPixels(srcSurface,
256 SkIRect::MakePtSize(pt, dst.dimensions()),
257 this->colorInfo().colorType(),
258 supportedRead.fColorType,
259 readDst,
260 readRB)) {
261 return false;
262 }
263
264 if (tmp.hasPixels()) {
265 return GrConvertPixels(dst, tmp, flip);
266 }
267 return true;
268}
269
271 GrCPixmap src,
272 SkIPoint dstPt) {
275 SkDEBUGCODE(this->validate();)
276
277 src = src.clip(this->dimensions(), &dstPt);
278 if (!src.hasPixels()) {
279 return false;
280 }
281 if (!src.info().bpp() || src.rowBytes() % src.info().bpp()) {
282 return false;
283 }
284 return this->internalWritePixels(dContext, &src, 1, dstPt);
285}
286
288 const GrCPixmap src[],
289 int numLevels) {
292 SkDEBUGCODE(this->validate();)
293
294 SkASSERT(dContext);
295 SkASSERT(numLevels >= 1);
296 SkASSERT(src);
297
298 if (numLevels == 1) {
299 if (src->dimensions() != this->dimensions()) {
300 return false;
301 }
302 return this->writePixels(dContext, src[0], {0, 0});
303 }
304 if (!this->asTextureProxy() ||
305 this->asTextureProxy()->proxyMipmapped() == skgpu::Mipmapped::kNo) {
306 return false;
307 }
308
309 SkISize dims = this->dimensions();
310 if (numLevels != SkMipmap::ComputeLevelCount(dims) + 1) {
311 return false;
312 }
313 for (int i = 0; i < numLevels; ++i) {
314 if (src[i].colorInfo() != src[0].colorInfo()) {
315 return false;
316 }
317 if (dims != src[i].dimensions()) {
318 return false;
319 }
320 if (!src[i].info().bpp() || src[i].rowBytes() % src[i].info().bpp()) {
321 return false;
322 }
323 dims = {std::max(1, dims.width()/2), std::max(1, dims.height()/2)};
324 }
325 return this->internalWritePixels(dContext, src, numLevels, {0, 0});
326}
327
328bool SurfaceContext::internalWritePixels(GrDirectContext* dContext,
329 const GrCPixmap src[],
330 int numLevels,
331 SkIPoint pt) {
332 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "internalWritePixels", fContext);
333
334 SkASSERT(numLevels >= 1);
335 SkASSERT(src);
336
337 // We can either write to a subset or write MIP levels, but not both.
338 SkASSERT((src[0].dimensions() == this->dimensions() && pt.isZero()) || numLevels == 1);
339 SkASSERT(numLevels == 1 || (this->asTextureProxy() &&
340 this->asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes));
341 // Our public caller should have clipped to the bounds of the surface already.
343 SkIRect::MakePtSize(pt, src[0].dimensions())));
344
345 if (!dContext) {
346 return false;
347 }
348
349 if (this->asSurfaceProxy()->readOnly()) {
350 return false;
351 }
352
353 if (src[0].colorType() == GrColorType::kUnknown) {
354 return false;
355 }
356
357 if (!alpha_types_compatible(src[0].alphaType(), this->colorInfo().alphaType())) {
358 return false;
359 }
360
361 GrSurfaceProxy* dstProxy = this->asSurfaceProxy();
362
363 if (dstProxy->framebufferOnly()) {
364 return false;
365 }
366
367 if (!dstProxy->instantiate(dContext->priv().resourceProvider())) {
368 return false;
369 }
370
371 GrSurface* dstSurface = dstProxy->peekSurface();
372
374 SkColorSpaceXformSteps{src[0].colorInfo(), this->colorInfo()}.flags;
375 bool unpremul = flags.unpremul,
376 needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
377 premul = flags.premul;
378
379 const GrCaps* caps = dContext->priv().caps();
380
381 auto rgbaDefaultFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
382 GrRenderable::kNo);
383
384 GrColorType dstColorType = this->colorInfo().colorType();
385 // For canvas2D putImageData performance we have a special code path for unpremul RGBA_8888 srcs
386 // that are premultiplied on the GPU. This is kept as narrow as possible for now.
387 bool canvas2DFastPath = !caps->avoidWritePixelsFastPath() && premul && !needColorConversion &&
388 (src[0].colorType() == GrColorType::kRGBA_8888 ||
389 src[0].colorType() == GrColorType::kBGRA_8888) &&
390 this->asFillContext() &&
391 (dstColorType == GrColorType::kRGBA_8888 ||
392 dstColorType == GrColorType::kBGRA_8888) &&
393 rgbaDefaultFormat.isValid() &&
394 dContext->priv().validPMUPMConversionExists();
395
396 // Since the validPMUPMConversionExists function actually submits work to the gpu to do its
397 // tests, it is possible that during that call we have abanoned the context. Thus we do an
398 // abanoned check here to make sure we are still valid.
400
401 // Drawing code path doesn't support writing to levels and doesn't support inserting layout
402 // transitions.
403 if ((!caps->surfaceSupportsWritePixels(dstSurface) || canvas2DFastPath) && numLevels == 1) {
404 GrColorInfo tempColorInfo;
406 skgpu::Swizzle tempReadSwizzle;
407 if (canvas2DFastPath) {
408 tempColorInfo = {GrColorType::kRGBA_8888,
410 this->colorInfo().refColorSpace()};
411 format = rgbaDefaultFormat;
412 } else {
413 tempColorInfo = this->colorInfo();
414 format = dstProxy->backendFormat().makeTexture2D();
415 if (!format.isValid()) {
416 return false;
417 }
418 tempReadSwizzle = this->readSwizzle();
419 }
420
421 // It is more efficient for us to write pixels into a top left origin so we prefer that.
422 // However, if the final proxy isn't a render target then we must use a copy to move the
423 // data into it which requires the origins to match. If the final proxy is a render target
424 // we can use a draw instead which doesn't have this origin restriction. Thus for render
425 // targets we will use top left and otherwise we will make the origins match.
426 GrSurfaceOrigin tempOrigin =
428 auto tempProxy = dContext->priv().proxyProvider()->createProxy(
429 format,
430 src[0].dimensions(),
431 GrRenderable::kNo,
432 1,
433 skgpu::Mipmapped::kNo,
436 GrProtected::kNo,
437 /*label=*/"SurfaceContext_InternalWritePixels");
438 if (!tempProxy) {
439 return false;
440 }
441 GrSurfaceProxyView tempView(tempProxy, tempOrigin, tempReadSwizzle);
442 SurfaceContext tempCtx(dContext, tempView, tempColorInfo);
443
444 // In the fast path we always write the srcData to the temp context as though it were RGBA.
445 // When the data is really BGRA the write will cause the R and B channels to be swapped in
446 // the intermediate surface which gets corrected by a swizzle effect when drawing to the
447 // dst.
448 GrCPixmap origSrcBase = src[0];
449 GrCPixmap srcBase = origSrcBase;
450 if (canvas2DFastPath) {
451 srcBase = GrCPixmap(origSrcBase.info().makeColorType(GrColorType::kRGBA_8888),
452 origSrcBase.addr(),
453 origSrcBase.rowBytes());
454 }
455 if (!tempCtx.writePixels(dContext, srcBase, {0, 0})) {
456 return false;
457 }
458
459 if (this->asFillContext()) {
460 std::unique_ptr<GrFragmentProcessor> fp;
461 if (canvas2DFastPath) {
462 fp = dContext->priv().createUPMToPMEffect(
463 GrTextureEffect::Make(std::move(tempView), tempColorInfo.alphaType()));
464 // Important: check the original src color type here!
465 if (origSrcBase.colorType() == GrColorType::kBGRA_8888) {
467 }
468 } else {
469 fp = GrTextureEffect::Make(std::move(tempView), tempColorInfo.alphaType());
470 }
471 if (!fp) {
472 return false;
473 }
475 SkIRect::MakeSize(srcBase.dimensions()),
476 SkIRect::MakePtSize(pt, srcBase.dimensions()),
477 std::move(fp));
478 } else {
479 SkIRect srcRect = SkIRect::MakeSize(srcBase.dimensions());
480 SkIPoint dstPoint = SkIPoint::Make(pt.fX, pt.fY);
481 if (!this->copy(std::move(tempProxy), srcRect, dstPoint)) {
482 return false;
483 }
484 }
485 return true;
486 }
487
488 GrColorType srcColorType = src[0].colorType();
489 auto [allowedColorType, _] =
491 dstProxy->backendFormat(),
492 srcColorType);
493 bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
494
495 bool convertAll = premul ||
496 unpremul ||
497 needColorConversion ||
498 flip ||
499 (srcColorType != allowedColorType);
500 bool mustBeTight = !caps->writePixelsRowBytesSupport();
501 size_t tmpSize = 0;
502 if (mustBeTight || convertAll) {
503 for (int i = 0; i < numLevels; ++i) {
504 if (convertAll || (mustBeTight && src[i].rowBytes() != src[i].info().minRowBytes())) {
505 tmpSize += src[i].info().makeColorType(allowedColorType).minRowBytes()*
506 src[i].height();
507 }
508 }
509 }
510
511 auto tmpData = tmpSize ? SkData::MakeUninitialized(tmpSize) : nullptr;
512 void* tmp = tmpSize ? tmpData->writable_data() : nullptr;
513 AutoSTArray<15, GrMipLevel> srcLevels(numLevels);
514 bool ownAllStorage = true;
515 for (int i = 0; i < numLevels; ++i) {
516 if (convertAll || (mustBeTight && src[i].rowBytes() != src[i].info().minRowBytes())) {
517 GrImageInfo tmpInfo(allowedColorType,
518 this->colorInfo().alphaType(),
519 this->colorInfo().refColorSpace(),
520 src[i].dimensions());
521 auto tmpRB = tmpInfo.minRowBytes();
522 GrPixmap tmpPM(tmpInfo, tmp, tmpRB);
523 SkAssertResult(GrConvertPixels(tmpPM, src[i], flip));
524 srcLevels[i] = {tmpPM.addr(), tmpPM.rowBytes(), tmpData};
525 tmp = SkTAddOffset<void>(tmp, tmpRB*tmpPM.height());
526 } else {
527 srcLevels[i] = {src[i].addr(), src[i].rowBytes(), src[i].pixelStorage()};
528 ownAllStorage &= src[i].ownsPixels();
529 }
530 }
531 pt.fY = flip ? dstSurface->height() - pt.fY - src[0].height() : pt.fY;
532
533 if (!dContext->priv().drawingManager()->newWritePixelsTask(
534 sk_ref_sp(dstProxy),
535 SkIRect::MakePtSize(pt, src[0].dimensions()),
536 allowedColorType,
537 this->colorInfo().colorType(),
538 srcLevels.begin(),
539 numLevels)) {
540 return false;
541 }
542 if (numLevels > 1) {
543 dstProxy->asTextureProxy()->markMipmapsClean();
544 }
545 if (!ownAllStorage) {
546 // If any pixmap doesn't own its pixels then we must flush so that the pixels are pushed to
547 // the GPU before we return.
548 dContext->priv().flushSurface(dstProxy);
549 }
550 return true;
551}
552
554 const SkImageInfo& info,
555 const SkIRect& srcRect,
556 RescaleGamma rescaleGamma,
557 RescaleMode rescaleMode,
559 ReadPixelsContext callbackContext) {
560 if (!dContext) {
561 callback(callbackContext, nullptr);
562 return;
563 }
564 auto rt = this->asRenderTargetProxy();
565 if (rt && rt->wrapsVkSecondaryCB()) {
566 callback(callbackContext, nullptr);
567 return;
568 }
569 if (rt && rt->framebufferOnly()) {
570 callback(callbackContext, nullptr);
571 return;
572 }
573 auto dstCT = SkColorTypeToGrColorType(info.colorType());
574 if (dstCT == GrColorType::kUnknown) {
575 callback(callbackContext, nullptr);
576 return;
577 }
578 bool needsRescale = srcRect.size() != info.dimensions() ||
580 this->colorInfo().alphaType() != info.alphaType() ||
581 !SkColorSpace::Equals(this->colorInfo().colorSpace(), info.colorSpace());
582 auto surfaceBackendFormat = this->asSurfaceProxy()->backendFormat();
583 auto readInfo = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(),
584 surfaceBackendFormat,
585 dstCT);
586 // Fail if we can't read from the source surface's color type.
587 if (readInfo.fColorType == GrColorType::kUnknown) {
588 callback(callbackContext, nullptr);
589 return;
590 }
591 // Fail if read color type does not have all of dstCT's color channels and those missing color
592 // channels are in the src.
593 uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
594 uint32_t legalReadChannels = GrColorTypeChannelFlags(readInfo.fColorType);
595 uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
596 if ((~legalReadChannels & dstChannels) & srcChannels) {
597 callback(callbackContext, nullptr);
598 return;
599 }
600
601 std::unique_ptr<SurfaceFillContext> tempFC;
602 int x = srcRect.fLeft;
603 int y = srcRect.fTop;
604 if (needsRescale) {
605 auto tempInfo = GrImageInfo(info).makeColorType(this->colorInfo().colorType());
606 tempFC = this->rescale(tempInfo, kTopLeft_GrSurfaceOrigin, srcRect,
607 rescaleGamma, rescaleMode);
608 if (!tempFC) {
609 callback(callbackContext, nullptr);
610 return;
611 }
612 SkASSERT(SkColorSpace::Equals(tempFC->colorInfo().colorSpace(), info.colorSpace()));
613 SkASSERT(tempFC->origin() == kTopLeft_GrSurfaceOrigin);
614 x = y = 0;
615 }
616 auto srcCtx = tempFC ? tempFC.get() : this;
617 return srcCtx->asyncReadPixels(dContext,
618 SkIRect::MakePtSize({x, y}, info.dimensions()),
619 info.colorType(),
620 callback,
621 callbackContext);
622}
623
625 const SkIRect& rect,
628 ReadPixelsContext callbackContext) {
631
632 SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
633 SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
634
635 if (!dContext || this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
636 callback(callbackContext, nullptr);
637 return;
638 }
639
640 auto mappedBufferManager = dContext->priv().clientMappedBufferManager();
641
642 auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
643
644 if (!transferResult.fTransferBuffer) {
645 auto ii = SkImageInfo::Make(rect.size(), colorType, this->colorInfo().alphaType(),
646 this->colorInfo().refColorSpace());
648 auto result = std::make_unique<AsyncReadResult>(kInvalid);
650 result->addCpuPlane(pm.pixelStorage(), pm.rowBytes());
651
652 SkIPoint pt{rect.fLeft, rect.fTop};
653 if (!this->readPixels(dContext, pm, pt)) {
654 callback(callbackContext, nullptr);
655 return;
656 }
657 callback(callbackContext, std::move(result));
658 return;
659 }
660
661 struct FinishContext {
662 ReadPixelsCallback* fClientCallback;
663 ReadPixelsContext fClientContext;
664 SkISize fSize;
665 GrClientMappedBufferManager* fMappedBufferManager;
666 PixelTransferResult fTransferResult;
667 };
668 // Assumption is that the caller would like to flush. We could take a parameter or require an
669 // explicit flush from the caller. We'd have to have a way to defer attaching the finish
670 // callback to GrGpu until after the next flush that flushes our op list, though.
671 auto* finishContext = new FinishContext{callback,
672 callbackContext,
673 rect.size(),
674 mappedBufferManager,
675 std::move(transferResult)};
676 auto finishCallback = [](GrGpuFinishedContext c) {
677 const auto* context = reinterpret_cast<const FinishContext*>(c);
678 auto manager = context->fMappedBufferManager;
679 auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
680 if (!result->addTransferResult(context->fTransferResult,
681 context->fSize,
682 context->fTransferResult.fRowBytes,
683 manager)) {
684 result.reset();
685 }
686 (*context->fClientCallback)(context->fClientContext, std::move(result));
687 delete context;
688 };
689 GrFlushInfo flushInfo;
690 flushInfo.fFinishedContext = finishContext;
691 flushInfo.fFinishedProc = finishCallback;
692
693 dContext->priv().flushSurface(
695}
696
698 SkYUVColorSpace yuvColorSpace,
699 bool readAlpha,
700 sk_sp<SkColorSpace> dstColorSpace,
701 const SkIRect& srcRect,
702 SkISize dstSize,
703 RescaleGamma rescaleGamma,
704 RescaleMode rescaleMode,
706 ReadPixelsContext callbackContext) {
709
710 SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
711 SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
712 SkASSERT(!dstSize.isZero());
713 SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
714
715 if (!dContext) {
716 callback(callbackContext, nullptr);
717 return;
718 }
719 auto rt = this->asRenderTargetProxy();
720 if (rt && rt->wrapsVkSecondaryCB()) {
721 callback(callbackContext, nullptr);
722 return;
723 }
724 if (rt && rt->framebufferOnly()) {
725 callback(callbackContext, nullptr);
726 return;
727 }
728 if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
729 callback(callbackContext, nullptr);
730 return;
731 }
732 int x = srcRect.fLeft;
733 int y = srcRect.fTop;
734 bool needsRescale = srcRect.size() != dstSize ||
735 !SkColorSpace::Equals(this->colorInfo().colorSpace(), dstColorSpace.get());
736 GrSurfaceProxyView srcView = this->readSurfaceView();
737 if (needsRescale) {
738 auto info = SkImageInfo::Make(dstSize,
740 this->colorInfo().alphaType(),
741 dstColorSpace);
742 // TODO: Incorporate the YUV conversion into last pass of rescaling.
743 auto tempFC = this->rescale(info,
745 srcRect,
746 rescaleGamma,
747 rescaleMode);
748 if (!tempFC) {
749 callback(callbackContext, nullptr);
750 return;
751 }
752 SkASSERT(SkColorSpace::Equals(tempFC->colorInfo().colorSpace(), info.colorSpace()));
753 SkASSERT(tempFC->origin() == kTopLeft_GrSurfaceOrigin);
754 x = y = 0;
755 srcView = tempFC->readSurfaceView();
756 } else if (!srcView.asTextureProxy()) {
757 srcView = GrSurfaceProxyView::Copy(
758 fContext,
759 std::move(srcView),
760 skgpu::Mipmapped::kNo,
761 srcRect,
764 /*label=*/"SurfaceContext_AsyncRescaleAndReadPixelsYUV420");
765 if (!srcView) {
766 // If we can't get a texture copy of the contents then give up.
767 callback(callbackContext, nullptr);
768 return;
769 }
770 SkASSERT(srcView.asTextureProxy());
771 x = y = 0;
772 }
773
774 auto yaInfo = SkImageInfo::MakeA8(dstSize);
775 auto yFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox,
776 /* sampleCount= */ 1,
777 skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
778 std::unique_ptr<SurfaceFillContext> aFC;
779 if (readAlpha) {
780 aFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox,
781 /* sampleCount= */ 1,
782 skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
783 }
784
785 auto uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2);
786 auto uFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox,
787 /* sampleCount= */ 1,
788 skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
789 auto vFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox,
790 /* sampleCount= */ 1,
791 skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
792
793 if (!yFC || !uFC || !vFC || (readAlpha && !aFC)) {
794 callback(callbackContext, nullptr);
795 return;
796 }
797
798 float baseM[20];
799 SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
800
801 // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
802
803 auto texMatrix = SkMatrix::Translate(x, y);
804
805 auto [readCT, offsetAlignment] =
806 this->caps()->supportedReadPixelsColorType(yFC->colorInfo().colorType(),
807 yFC->asSurfaceProxy()->backendFormat(),
809 if (readCT == GrColorType::kUnknown) {
810 callback(callbackContext, nullptr);
811 return;
812 }
813 bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport() ||
814 !offsetAlignment;
815 PixelTransferResult yTransfer, aTransfer, uTransfer, vTransfer;
816
817 // This matrix generates (r,g,b,a) = (0, 0, 0, y)
818 float yM[20];
819 std::fill_n(yM, 15, 0.f);
820 std::copy_n(baseM + 0, 5, yM + 15);
821
822 auto yFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
823 yFP = GrFragmentProcessor::ColorMatrix(std::move(yFP),
824 yM,
825 /*unpremulInput=*/false,
826 /*clampRGBOutput=*/true,
827 /*premulOutput=*/false);
828 yFC->fillWithFP(std::move(yFP));
829 if (!doSynchronousRead) {
830 yTransfer = yFC->transferPixels(GrColorType::kAlpha_8,
831 SkIRect::MakeSize(yFC->dimensions()));
832 if (!yTransfer.fTransferBuffer) {
833 callback(callbackContext, nullptr);
834 return;
835 }
836 }
837
838 if (readAlpha) {
839 auto aFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
840 SkASSERT(baseM[15] == 0 &&
841 baseM[16] == 0 &&
842 baseM[17] == 0 &&
843 baseM[18] == 1 &&
844 baseM[19] == 0);
845 aFC->fillWithFP(std::move(aFP));
846 if (!doSynchronousRead) {
847 aTransfer = aFC->transferPixels(GrColorType::kAlpha_8,
848 SkIRect::MakeSize(aFC->dimensions()));
849 if (!aTransfer.fTransferBuffer) {
850 callback(callbackContext, nullptr);
851 return;
852 }
853 }
854 }
855
856 texMatrix.preScale(2.f, 2.f);
857 // This matrix generates (r,g,b,a) = (0, 0, 0, u)
858 float uM[20];
859 std::fill_n(uM, 15, 0.f);
860 std::copy_n(baseM + 5, 5, uM + 15);
861
862 auto uFP = GrTextureEffect::Make(srcView,
863 this->colorInfo().alphaType(),
864 texMatrix,
865 GrSamplerState::Filter::kLinear);
866 uFP = GrFragmentProcessor::ColorMatrix(std::move(uFP),
867 uM,
868 /*unpremulInput=*/false,
869 /*clampRGBOutput=*/true,
870 /*premulOutput=*/false);
871 uFC->fillWithFP(std::move(uFP));
872 if (!doSynchronousRead) {
873 uTransfer = uFC->transferPixels(GrColorType::kAlpha_8,
874 SkIRect::MakeSize(uFC->dimensions()));
875 if (!uTransfer.fTransferBuffer) {
876 callback(callbackContext, nullptr);
877 return;
878 }
879 }
880
881 // This matrix generates (r,g,b,a) = (0, 0, 0, v)
882 float vM[20];
883 std::fill_n(vM, 15, 0.f);
884 std::copy_n(baseM + 10, 5, vM + 15);
885 auto vFP = GrTextureEffect::Make(std::move(srcView),
886 this->colorInfo().alphaType(),
887 texMatrix,
888 GrSamplerState::Filter::kLinear);
889 vFP = GrFragmentProcessor::ColorMatrix(std::move(vFP),
890 vM,
891 /*unpremulInput=*/false,
892 /*clampRGBOutput=*/true,
893 /*premulOutput=*/false);
894 vFC->fillWithFP(std::move(vFP));
895
896 if (!doSynchronousRead) {
897 vTransfer = vFC->transferPixels(GrColorType::kAlpha_8,
898 SkIRect::MakeSize(vFC->dimensions()));
899 if (!vTransfer.fTransferBuffer) {
900 callback(callbackContext, nullptr);
901 return;
902 }
903 }
904
905 if (doSynchronousRead) {
906 GrPixmap yPmp = GrPixmap::Allocate(yaInfo);
907 GrPixmap uPmp = GrPixmap::Allocate(uvInfo);
908 GrPixmap vPmp = GrPixmap::Allocate(uvInfo);
909 GrPixmap aPmp;
910 if (readAlpha) {
911 aPmp = GrPixmap::Allocate(yaInfo);
912 }
913 if (!yFC->readPixels(dContext, yPmp, {0, 0}) ||
914 !uFC->readPixels(dContext, uPmp, {0, 0}) ||
915 !vFC->readPixels(dContext, vPmp, {0, 0}) ||
916 (readAlpha && !aFC->readPixels(dContext, aPmp, {0, 0}))) {
917 callback(callbackContext, nullptr);
918 return;
919 }
920 auto result = std::make_unique<AsyncReadResult>(dContext->directContextID());
921 result->addCpuPlane(yPmp.pixelStorage(), yPmp.rowBytes());
922 result->addCpuPlane(uPmp.pixelStorage(), uPmp.rowBytes());
923 result->addCpuPlane(vPmp.pixelStorage(), vPmp.rowBytes());
924 if (readAlpha) {
925 result->addCpuPlane(aPmp.pixelStorage(), aPmp.rowBytes());
926 }
927 callback(callbackContext, std::move(result));
928 return;
929 }
930
931 struct FinishContext {
932 ReadPixelsCallback* fClientCallback;
933 ReadPixelsContext fClientContext;
934 GrClientMappedBufferManager* fMappedBufferManager;
935 SkISize fSize;
936 PixelTransferResult fYTransfer;
937 PixelTransferResult fUTransfer;
938 PixelTransferResult fVTransfer;
939 PixelTransferResult fATransfer;
940 };
941 // Assumption is that the caller would like to flush. We could take a parameter or require an
942 // explicit flush from the caller. We'd have to have a way to defer attaching the finish
943 // callback to GrGpu until after the next flush that flushes our op list, though.
944 auto* finishContext = new FinishContext{callback,
945 callbackContext,
946 dContext->priv().clientMappedBufferManager(),
947 dstSize,
948 std::move(yTransfer),
949 std::move(uTransfer),
950 std::move(vTransfer),
951 std::move(aTransfer)};
952 auto finishCallback = [](GrGpuFinishedContext c) {
953 const auto* context = reinterpret_cast<const FinishContext*>(c);
954 auto manager = context->fMappedBufferManager;
955 auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
956 if (!result->addTransferResult(context->fYTransfer,
957 context->fSize,
958 context->fYTransfer.fRowBytes,
959 manager)) {
960 (*context->fClientCallback)(context->fClientContext, nullptr);
961 delete context;
962 return;
963 }
964 SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2};
965 if (!result->addTransferResult(context->fUTransfer,
966 uvSize,
967 context->fUTransfer.fRowBytes,
968 manager)) {
969 (*context->fClientCallback)(context->fClientContext, nullptr);
970 delete context;
971 return;
972 }
973 if (!result->addTransferResult(context->fVTransfer,
974 uvSize,
975 context->fVTransfer.fRowBytes,
976 manager)) {
977 (*context->fClientCallback)(context->fClientContext, nullptr);
978 delete context;
979 return;
980 }
981 if (context->fATransfer.fTransferBuffer &&
982 !result->addTransferResult(context->fATransfer,
983 context->fSize,
984 context->fATransfer.fRowBytes,
985 manager)) {
986 (*context->fClientCallback)(context->fClientContext, nullptr);
987 delete context;
988 return;
989 }
990 (*context->fClientCallback)(context->fClientContext, std::move(result));
991 delete context;
992 };
993 GrFlushInfo flushInfo;
994 flushInfo.fFinishedContext = finishContext;
995 flushInfo.fFinishedProc = finishCallback;
996 dContext->priv().flushSurface(
998}
999
1000sk_sp<GrRenderTask> SurfaceContext::copy(sk_sp<GrSurfaceProxy> src,
1001 SkIRect srcRect,
1002 SkIPoint dstPoint) {
1003 if (!GrClipSrcRectAndDstPoint(this->dimensions(), &dstPoint,
1004 src->dimensions(), &srcRect)) {
1005 return nullptr;
1006 }
1007
1008 SkIRect dstRect = SkIRect::MakePtSize(dstPoint, srcRect.size());
1009 return this->copyScaled(src, srcRect, dstRect, GrSamplerState::Filter::kNearest);
1010}
1011
1012sk_sp<GrRenderTask> SurfaceContext::copyScaled(sk_sp<GrSurfaceProxy> src,
1013 SkIRect srcRect,
1014 SkIRect dstRect,
1015 GrSamplerState::Filter filter) {
1018 SkDEBUGCODE(this->validate();)
1019 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "copyScaled", fContext);
1020
1021 const GrCaps* caps = fContext->priv().caps();
1022
1023 if (this->asSurfaceProxy()->framebufferOnly()) {
1024 return nullptr;
1025 }
1026
1027 // canCopySurface() verifies that src and dst rects are contained in their surfaces.
1028 if (!caps->canCopySurface(this->asSurfaceProxy(), dstRect, src.get(), srcRect)) {
1029 return nullptr;
1030 }
1031
1032 if (filter == GrSamplerState::Filter::kLinear && !src->isFunctionallyExact()) {
1033 // If we're linear filtering an image that is approx-sized, there are cases where the filter
1034 // could sample outside the logical dimensions. Specifically if we're upscaling along an
1035 // axis where we are copying up to the logical dimension, but that dimension is less than
1036 // the actual backing store dimension, the linear filter will access one texel beyond the
1037 // logical size, potentially incorporating undefined values.
1038 // NOTE: Identity scales that sample along the logical boundary of an approxi-fit texture
1039 // can still technically access a row or column of undefined data (albeit with a weight that
1040 // *should* be zero). We also disallow copying with linear filtering in that scenario,
1041 // just in case.
1042#if defined(SK_USE_SAFE_INSET_FOR_TEXTURE_SAMPLING)
1043 const bool upscalingXAtApproxEdge =
1044 dstRect.width() >= srcRect.width() &&
1045 srcRect.fRight == src->width() &&
1046 srcRect.fRight < src->backingStoreDimensions().width();
1047 const bool upscalingYAtApproxEdge =
1048 dstRect.height() >= srcRect.height() &&
1049 srcRect.fBottom == src->height() &&
1050 srcRect.fBottom < src->backingStoreDimensions().height();
1051#else
1052 // In this mode we allow non-scaling copies through even if the linear filtering would
1053 // access the adjacent undefined row or column.
1054 const bool upscalingXAtApproxEdge =
1055 dstRect.width() > srcRect.width() &&
1056 srcRect.fRight == src->width() &&
1057 srcRect.fRight < src->backingStoreDimensions().width();
1058 const bool upscalingYAtApproxEdge =
1059 dstRect.height() > srcRect.height() &&
1060 srcRect.fBottom == src->height() &&
1061 srcRect.fBottom < src->backingStoreDimensions().height();
1062#endif
1063 if (upscalingXAtApproxEdge || upscalingYAtApproxEdge) {
1064 return nullptr;
1065 }
1066
1067 // NOTE: Any upscaling with the linear filter will include content that's 1px outside the
1068 // src rect, but as long as that's still within the logical dimensions we assume it's okay.
1069 }
1070
1071 SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal);
1072 SkASSERT(src->backendFormat() == this->asSurfaceProxy()->backendFormat());
1073 return this->drawingManager()->newCopyRenderTask(this->asSurfaceProxyRef(),
1074 dstRect,
1075 std::move(src),
1076 srcRect,
1077 filter,
1078 this->origin());
1079}
1080
1081std::unique_ptr<SurfaceFillContext> SurfaceContext::rescale(const GrImageInfo& info,
1082 GrSurfaceOrigin origin,
1083 SkIRect srcRect,
1084 RescaleGamma rescaleGamma,
1085 RescaleMode rescaleMode) {
1086 auto sfc = fContext->priv().makeSFCWithFallback(info,
1088 /* sampleCount= */ 1,
1089 skgpu::Mipmapped::kNo,
1090 this->asSurfaceProxy()->isProtected(),
1091 origin);
1092 if (!sfc || !this->rescaleInto(sfc.get(),
1093 SkIRect::MakeSize(sfc->dimensions()),
1094 srcRect,
1095 rescaleGamma,
1096 rescaleMode)) {
1097 return nullptr;
1098 }
1099 return sfc;
1100}
1101
1103 SkIRect dstRect,
1104 SkIRect srcRect,
1105 RescaleGamma rescaleGamma,
1106 RescaleMode rescaleMode) {
1107 SkASSERT(dst);
1108 if (!SkIRect::MakeSize(dst->dimensions()).contains((dstRect))) {
1109 return false;
1110 }
1111
1112 auto rtProxy = this->asRenderTargetProxy();
1113 if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
1114 return false;
1115 }
1116
1117 if (this->asSurfaceProxy()->framebufferOnly()) {
1118 return false;
1119 }
1120
1121 GrSurfaceProxyView texView = this->readSurfaceView();
1122 // If we perform scaling as draws, texView must be texturable; if it's not already, we have to
1123 // make a copy. However, if the scaling can use copyScaled(), we can avoid this copy.
1124 auto ensureTexturable = [this](GrSurfaceProxyView texView, SkIRect srcRect) {
1125 if (!texView.asTextureProxy()) {
1126 // TODO: If copying supported specifying a renderable copy then we could return the copy
1127 // when there are no other conversions.
1129 std::move(texView),
1130 skgpu::Mipmapped::kNo,
1131 srcRect,
1134 "SurfaceContext_RescaleInto");
1135 if (texView) {
1136 SkASSERT(texView.asTextureProxy());
1137 srcRect = SkIRect::MakeSize(srcRect.size());
1138 }
1139 }
1140 return std::make_pair(std::move(texView), srcRect);
1141 };
1142
1143 SkISize finalSize = dstRect.size();
1144 if (finalSize == srcRect.size()) {
1145 rescaleGamma = RescaleGamma::kSrc;
1146 rescaleMode = RescaleMode::kNearest;
1147 }
1148
1149 // Within a rescaling pass A is the input (if not null) and B is the output. At the end of the
1150 // pass B is moved to A. If 'this' is the input on the first pass then tempA is null.
1151 std::unique_ptr<SurfaceFillContext> tempA;
1152 std::unique_ptr<SurfaceFillContext> tempB;
1153
1154 // Assume we should ignore the rescale linear request if the surface has no color space since
1155 // it's unclear how we'd linearize from an unknown color space.
1156 if (rescaleGamma == RescaleGamma::kLinear && this->colorInfo().colorSpace() &&
1157 !this->colorInfo().colorSpace()->gammaIsLinear()) {
1158 // Colorspace transformations are always handled by drawing so we need to be texturable
1159 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1160 if (!texView) {
1161 return false;
1162 }
1163 auto cs = this->colorInfo().colorSpace()->makeLinearGamma();
1164 // We'll fall back to kRGBA_8888 if half float not supported.
1166 dst->colorInfo().alphaType(),
1167 std::move(cs),
1168 srcRect.size());
1169 auto linearRTC = fContext->priv().makeSFCWithFallback(std::move(ii),
1171 /* sampleCount= */ 1,
1172 skgpu::Mipmapped::kNo,
1173 texView.proxy()->isProtected(),
1174 dst->origin());
1175 if (!linearRTC) {
1176 return false;
1177 }
1178 auto fp = GrTextureEffect::Make(std::move(texView),
1179 this->colorInfo().alphaType(),
1180 SkMatrix::Translate(srcRect.topLeft()),
1181 GrSamplerState::Filter::kNearest,
1182 GrSamplerState::MipmapMode::kNone);
1183 fp = GrColorSpaceXformEffect::Make(std::move(fp),
1184 this->colorInfo(),
1185 linearRTC->colorInfo());
1186 linearRTC->fillWithFP(std::move(fp));
1187 texView = linearRTC->readSurfaceView();
1188 SkASSERT(texView.asTextureProxy());
1189 tempA = std::move(linearRTC);
1190 srcRect = SkIRect::MakeSize(srcRect.size());
1191 }
1192
1193 do {
1194 SkISize nextDims = finalSize;
1195 if (rescaleMode != RescaleMode::kNearest && rescaleMode != RescaleMode::kLinear) {
1196 if (srcRect.width() > finalSize.width()) {
1197 nextDims.fWidth = std::max((srcRect.width() + 1)/2, finalSize.width());
1198 } else if (srcRect.width() < finalSize.width()) {
1199 nextDims.fWidth = std::min(srcRect.width()*2, finalSize.width());
1200 }
1201 if (srcRect.height() > finalSize.height()) {
1202 nextDims.fHeight = std::max((srcRect.height() + 1)/2, finalSize.height());
1203 } else if (srcRect.height() < finalSize.height()) {
1204 nextDims.fHeight = std::min(srcRect.height()*2, finalSize.height());
1205 }
1206 }
1207 auto input = tempA ? tempA.get() : this;
1209 SurfaceFillContext* stepDst;
1210 SkIRect stepDstRect;
1211 if (nextDims == finalSize) {
1212 stepDst = dst;
1213 stepDstRect = dstRect;
1214 xform = GrColorSpaceXform::Make(input->colorInfo(), dst->colorInfo());
1215 } else {
1216 GrImageInfo nextInfo(input->colorInfo(), nextDims);
1217
1219 /* sampleCount= */ 1,
1220 skgpu::Mipmapped::kNo,
1221 texView.proxy()->isProtected());
1222 if (!tempB) {
1223 return false;
1224 }
1225 stepDst = tempB.get();
1226 stepDstRect = SkIRect::MakeSize(tempB->dimensions());
1227 }
1228 std::unique_ptr<GrFragmentProcessor> fp;
1229 if (rescaleMode == RescaleMode::kRepeatedCubic) {
1230 // Cubic sampling is always handled by drawing with a shader, so we must be texturable
1231 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1232 if (!texView) {
1233 return false;
1234 }
1236 if (nextDims.width() == srcRect.width()) {
1238 } else if (nextDims.height() == srcRect.height()) {
1240 }
1241 static constexpr auto kWM = GrSamplerState::WrapMode::kClamp;
1242 static constexpr auto kKernel = GrBicubicEffect::gCatmullRom;
1243 fp = GrBicubicEffect::MakeSubset(std::move(texView),
1244 input->colorInfo().alphaType(),
1245 SkMatrix::I(),
1246 kWM,
1247 kWM,
1248 SkRect::Make(srcRect),
1249 kKernel,
1250 dir,
1251 *this->caps());
1252 } else {
1253 auto filter = rescaleMode == RescaleMode::kNearest ? GrSamplerState::Filter::kNearest
1254 : GrSamplerState::Filter::kLinear;
1255 if (xform ||
1256 texView.origin() != stepDst->origin() ||
1257 !stepDst->copyScaled(texView.refProxy(), srcRect, stepDstRect, filter)) {
1258 // We could not or were unable to successful perform a scaling blit (which can be
1259 // much faster if texView isn't already texturable). Scale by drawing instead.
1260 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1261 if (!texView) {
1262 return false;
1263 }
1264 auto srcRectF = SkRect::Make(srcRect);
1265 fp = GrTextureEffect::MakeSubset(std::move(texView),
1266 this->colorInfo().alphaType(),
1267 SkMatrix::I(),
1268 {filter, GrSamplerState::MipmapMode::kNone},
1269 srcRectF,
1270 srcRectF,
1271 *this->caps());
1272 }
1273 }
1274 if (xform) {
1275 SkASSERT(SkToBool(fp)); // shouldn't have done a copy if there was a color xform
1276 fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform));
1277 }
1278 if (fp) {
1279 // When fp is not null, we scale by drawing; if it is null, presumably the src has
1280 // already been copied into stepDst.
1281 stepDst->fillRectToRectWithFP(srcRect, stepDstRect, std::move(fp));
1282 }
1283 texView = stepDst->readSurfaceView();
1284 tempA = std::move(tempB);
1285 srcRect = SkIRect::MakeSize(nextDims);
1286 } while (srcRect.size() != finalSize);
1287 return true;
1288}
1289
1291 const SkIRect& rect) {
1292 SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
1293 SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
1294 auto direct = fContext->asDirectContext();
1295 if (!direct) {
1296 return {};
1297 }
1298 auto rtProxy = this->asRenderTargetProxy();
1299 if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
1300 return {};
1301 }
1302
1303 auto proxy = this->asSurfaceProxy();
1304 auto supportedRead = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(),
1305 proxy->backendFormat(), dstCT);
1306 // Fail if read color type does not have all of dstCT's color channels and those missing color
1307 // channels are in the src.
1308 uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
1309 uint32_t legalReadChannels = GrColorTypeChannelFlags(supportedRead.fColorType);
1310 uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
1311 if ((~legalReadChannels & dstChannels) & srcChannels) {
1312 return {};
1313 }
1314
1315 if (!this->caps()->transferFromSurfaceToBufferSupport() ||
1316 !supportedRead.fOffsetAlignmentForTransferBuffer) {
1317 return {};
1318 }
1319
1320 size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width();
1321 rowBytes = SkAlignTo(rowBytes, this->caps()->transferBufferRowBytesAlignment());
1322 size_t size = rowBytes * rect.height();
1323 // By using kStream_GrAccessPattern here, we are not able to cache and reuse the buffer for
1324 // multiple reads. Switching to kDynamic_GrAccessPattern would allow for this, however doing
1325 // so causes a crash in a chromium test. See skbug.com/11297
1326 auto buffer = direct->priv().resourceProvider()->createBuffer(
1327 size,
1331 if (!buffer) {
1332 return {};
1333 }
1334 auto srcRect = rect;
1335 bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
1336 if (flip) {
1337 srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight,
1338 this->height() - rect.fTop);
1339 }
1341 this->colorInfo().colorType(),
1342 supportedRead.fColorType, buffer, 0);
1344 result.fTransferBuffer = std::move(buffer);
1345 auto at = this->colorInfo().alphaType();
1346 if (supportedRead.fColorType != dstCT || flip) {
1347 int w = rect.width(), h = rect.height();
1348 GrImageInfo srcInfo(supportedRead.fColorType, at, nullptr, w, h);
1349 GrImageInfo dstInfo(dstCT, at, nullptr, w, h);
1350 result.fRowBytes = dstInfo.minRowBytes();
1351 result.fPixelConverter = [dstInfo, srcInfo, rowBytes](
1352 void* dst, const void* src) {
1353 GrConvertPixels( GrPixmap(dstInfo, dst, dstInfo.minRowBytes()),
1354 GrCPixmap(srcInfo, src, rowBytes));
1355 };
1356 } else {
1357 result.fRowBytes = rowBytes;
1358 }
1359 return result;
1360}
1361
1362#ifdef SK_DEBUG
1363void SurfaceContext::validate() const {
1365 fReadView.proxy()->validate(fContext);
1366 if (this->colorInfo().colorType() != GrColorType::kUnknown) {
1368 this->colorInfo().colorType(), fReadView.proxy()->backendFormat()));
1369 }
1370 this->onValidate();
1371}
1372#endif
1373
1374} // namespace skgpu::ganesh
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
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)
static constexpr size_t GrColorTypeBytesPerPixel(GrColorType ct)
GrColorType
@ kStream_GrAccessPattern
static constexpr GrColorType SkColorTypeToGrColorType(SkColorType ct)
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 SkAssertResult(cond)
Definition SkAssert.h:123
#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
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
@ kInvalid
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
SkYUVColorSpace
Definition SkImageInfo.h:68
static bool contains(const SkRect &r, SkPoint p)
sk_sp< T > sk_ref_sp(T *obj)
Definition SkRefCnt.h:381
const Context & fContext
SkFilterMode
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
void SkColorMatrix_RGB2YUV(SkYUVColorSpace cs, float m[20])
#define RETURN_FALSE_IF_ABANDONED
#define RETURN_NULLPTR_IF_ABANDONED
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 &)
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
SkColorSpace * colorSpace() const
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
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()
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
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
static const uint8_t buffer[]
GAsyncResult * result
uint32_t uint32_t * format
#define ASSERT_SINGLE_OWNER
Definition Device.cpp:120
double y
double x
@ kNoAccess
back-end surface will not be used by client
Definition copy.py:1
const uint32_t fp
static bool alpha_types_compatible(SkAlphaType srcAlphaType, SkAlphaType dstAlphaType)
Definition ref_ptr.h:256
SkScalar w
SkScalar h
GrGpuFinishedContext fFinishedContext
Definition GrTypes.h:220
GrGpuFinishedProc fFinishedProc
Definition GrTypes.h:219
bool isZero() const
int32_t fX
x-axis value
int32_t fY
y-axis value
static constexpr SkIPoint Make(int32_t x, int32_t y)
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
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
static SkImageInfo MakeA8(int width, int height)
static SkRect Make(const SkISize &size)
Definition SkRect.h:669