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 | |
8 | #include "src/gpu/GrSurfaceContext.h" |
9 | |
10 | #include <memory> |
11 | |
12 | #include "include/gpu/GrDirectContext.h" |
13 | #include "include/gpu/GrRecordingContext.h" |
14 | #include "src/core/SkAutoPixmapStorage.h" |
15 | #include "src/core/SkYUVMath.h" |
16 | #include "src/gpu/GrAuditTrail.h" |
17 | #include "src/gpu/GrContextPriv.h" |
18 | #include "src/gpu/GrDataUtils.h" |
19 | #include "src/gpu/GrDrawingManager.h" |
20 | #include "src/gpu/GrGpu.h" |
21 | #include "src/gpu/GrImageInfo.h" |
22 | #include "src/gpu/GrProxyProvider.h" |
23 | #include "src/gpu/GrRecordingContextPriv.h" |
24 | #include "src/gpu/GrRenderTargetContext.h" |
25 | #include "src/gpu/GrSurfaceContextPriv.h" |
26 | #include "src/gpu/SkGr.h" |
27 | #include "src/gpu/effects/GrBicubicEffect.h" |
28 | #include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h" |
29 | |
30 | #define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(this->singleOwner()) |
31 | #define RETURN_FALSE_IF_ABANDONED if (this->fContext->abandoned()) { return false; } |
32 | |
33 | std::unique_ptr<GrSurfaceContext> GrSurfaceContext::Make(GrRecordingContext* context, |
34 | GrSurfaceProxyView readView, |
35 | GrColorType colorType, |
36 | SkAlphaType alphaType, |
37 | sk_sp<SkColorSpace> colorSpace) { |
38 | // It is probably not necessary to check if the context is abandoned here since uses of the |
39 | // GrSurfaceContext which need the context will mostly likely fail later on without an issue. |
40 | // However having this hear adds some reassurance in case there is a path doesn't handle an |
41 | // abandoned context correctly. It also lets us early out of some extra work. |
42 | if (context->abandoned()) { |
43 | return nullptr; |
44 | } |
45 | GrSurfaceProxy* proxy = readView.proxy(); |
46 | SkASSERT(proxy && proxy->asTextureProxy()); |
47 | |
48 | std::unique_ptr<GrSurfaceContext> surfaceContext; |
49 | if (proxy->asRenderTargetProxy()) { |
50 | SkASSERT(kPremul_SkAlphaType == alphaType || kOpaque_SkAlphaType == alphaType); |
51 | // Will we ever want a swizzle that is not the default write swizzle for the format and |
52 | // colorType here? If so we will need to manually pass that in. |
53 | GrSwizzle writeSwizzle; |
54 | if (colorType != GrColorType::kUnknown) { |
55 | writeSwizzle = |
56 | context->priv().caps()->getWriteSwizzle(proxy->backendFormat(), colorType); |
57 | } |
58 | GrSurfaceProxyView writeView(readView.refProxy(), readView.origin(), writeSwizzle); |
59 | surfaceContext = std::make_unique<GrRenderTargetContext>(context, std::move(readView), |
60 | std::move(writeView), colorType, |
61 | std::move(colorSpace), nullptr); |
62 | } else { |
63 | surfaceContext = std::make_unique<GrSurfaceContext>(context, std::move(readView), colorType, |
64 | alphaType, std::move(colorSpace)); |
65 | } |
66 | SkDEBUGCODE(surfaceContext->validate();) |
67 | return surfaceContext; |
68 | } |
69 | |
70 | std::unique_ptr<GrSurfaceContext> GrSurfaceContext::Make(GrRecordingContext* context, |
71 | SkISize dimensions, |
72 | const GrBackendFormat& format, |
73 | GrRenderable renderable, |
74 | int renderTargetSampleCnt, |
75 | GrMipmapped mipMapped, |
76 | GrProtected isProtected, |
77 | GrSurfaceOrigin origin, |
78 | GrColorType colorType, |
79 | SkAlphaType alphaType, |
80 | sk_sp<SkColorSpace> colorSpace, |
81 | SkBackingFit fit, |
82 | SkBudgeted budgeted) { |
83 | GrSwizzle swizzle; |
84 | if (colorType != GrColorType::kUnknown && !context->priv().caps()->isFormatCompressed(format)) { |
85 | swizzle = context->priv().caps()->getReadSwizzle(format, colorType); |
86 | } |
87 | |
88 | sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy( |
89 | format, dimensions, renderable, renderTargetSampleCnt, mipMapped, fit, budgeted, |
90 | isProtected); |
91 | if (!proxy) { |
92 | return nullptr; |
93 | } |
94 | |
95 | GrSurfaceProxyView view(std::move(proxy), origin, swizzle); |
96 | return GrSurfaceContext::Make(context, std::move(view), colorType, alphaType, |
97 | std::move(colorSpace)); |
98 | } |
99 | |
100 | // In MDB mode the reffing of the 'getLastOpsTask' call's result allows in-progress |
101 | // GrOpsTasks to be picked up and added to by renderTargetContexts lower in the call |
102 | // stack. When this occurs with a closed GrOpsTask, a new one will be allocated |
103 | // when the renderTargetContext attempts to use it (via getOpsTask). |
104 | GrSurfaceContext::GrSurfaceContext(GrRecordingContext* context, |
105 | GrSurfaceProxyView readView, |
106 | GrColorType colorType, |
107 | SkAlphaType alphaType, |
108 | sk_sp<SkColorSpace> colorSpace) |
109 | : fContext(context) |
110 | , fReadView(std::move(readView)) |
111 | , fColorInfo(colorType, alphaType, std::move(colorSpace)) { |
112 | SkASSERT(!context->abandoned()); |
113 | } |
114 | |
115 | const GrCaps* GrSurfaceContext::caps() const { return fContext->priv().caps(); } |
116 | |
117 | GrAuditTrail* GrSurfaceContext::auditTrail() { |
118 | return fContext->priv().auditTrail(); |
119 | } |
120 | |
121 | GrDrawingManager* GrSurfaceContext::drawingManager() { |
122 | return fContext->priv().drawingManager(); |
123 | } |
124 | |
125 | const GrDrawingManager* GrSurfaceContext::drawingManager() const { |
126 | return fContext->priv().drawingManager(); |
127 | } |
128 | |
129 | #ifdef SK_DEBUG |
130 | GrSingleOwner* GrSurfaceContext::singleOwner() { |
131 | return fContext->priv().singleOwner(); |
132 | } |
133 | #endif |
134 | |
135 | bool GrSurfaceContext::readPixels(GrDirectContext* dContext, const GrImageInfo& origDstInfo, |
136 | void* dst, size_t rowBytes, SkIPoint pt) { |
137 | ASSERT_SINGLE_OWNER |
138 | RETURN_FALSE_IF_ABANDONED |
139 | SkDEBUGCODE(this->validate();) |
140 | GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContext::readPixels" ); |
141 | |
142 | if (!dContext) { |
143 | return false; |
144 | } |
145 | |
146 | if (!dst) { |
147 | return false; |
148 | } |
149 | |
150 | size_t tightRowBytes = origDstInfo.minRowBytes(); |
151 | if (!rowBytes) { |
152 | rowBytes = tightRowBytes; |
153 | } else if (rowBytes < tightRowBytes) { |
154 | return false; |
155 | } |
156 | |
157 | if (!origDstInfo.isValid()) { |
158 | return false; |
159 | } |
160 | |
161 | GrSurfaceProxy* srcProxy = this->asSurfaceProxy(); |
162 | |
163 | if (srcProxy->framebufferOnly()) { |
164 | return false; |
165 | } |
166 | |
167 | // MDB TODO: delay this instantiation until later in the method |
168 | if (!srcProxy->instantiate(dContext->priv().resourceProvider())) { |
169 | return false; |
170 | } |
171 | |
172 | GrSurface* srcSurface = srcProxy->peekSurface(); |
173 | |
174 | auto dstInfo = origDstInfo; |
175 | if (!dstInfo.clip(this->width(), this->height(), &pt, &dst, rowBytes)) { |
176 | return false; |
177 | } |
178 | // Our tight row bytes may have been changed by clipping. |
179 | tightRowBytes = dstInfo.minRowBytes(); |
180 | |
181 | SkColorSpaceXformSteps::Flags flags = SkColorSpaceXformSteps{this->colorInfo(), dstInfo}.flags; |
182 | bool unpremul = flags.unpremul, |
183 | needColorConversion = flags.linearize || flags.gamut_transform || flags.encode, |
184 | premul = flags.premul; |
185 | |
186 | const GrCaps* caps = dContext->priv().caps(); |
187 | bool srcIsCompressed = caps->isFormatCompressed(srcSurface->backendFormat()); |
188 | // This is the getImageData equivalent to the canvas2D putImageData fast path. We probably don't |
189 | // care so much about getImageData performance. However, in order to ensure putImageData/ |
190 | // getImageData in "legacy" mode are round-trippable we use the GPU to do the complementary |
191 | // unpremul step to writeSurfacePixels's premul step (which is determined empirically in |
192 | // fContext->vaildaPMUPMConversionExists()). |
193 | GrBackendFormat defaultRGBAFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, |
194 | GrRenderable::kYes); |
195 | GrColorType srcColorType = this->colorInfo().colorType(); |
196 | bool canvas2DFastPath = unpremul && !needColorConversion && |
197 | (GrColorType::kRGBA_8888 == dstInfo.colorType() || |
198 | GrColorType::kBGRA_8888 == dstInfo.colorType()) && |
199 | SkToBool(srcProxy->asTextureProxy()) && |
200 | (srcColorType == GrColorType::kRGBA_8888 || |
201 | srcColorType == GrColorType::kBGRA_8888) && |
202 | defaultRGBAFormat.isValid() && |
203 | dContext->priv().validPMUPMConversionExists(); |
204 | |
205 | auto readFlag = caps->surfaceSupportsReadPixels(srcSurface); |
206 | if (readFlag == GrCaps::SurfaceReadPixelsSupport::kUnsupported) { |
207 | return false; |
208 | } |
209 | |
210 | if (readFlag == GrCaps::SurfaceReadPixelsSupport::kCopyToTexture2D || canvas2DFastPath) { |
211 | GrColorType colorType = (canvas2DFastPath || srcIsCompressed) |
212 | ? GrColorType::kRGBA_8888 : this->colorInfo().colorType(); |
213 | sk_sp<SkColorSpace> cs = canvas2DFastPath ? nullptr : this->colorInfo().refColorSpace(); |
214 | |
215 | auto tempCtx = GrRenderTargetContext::Make( |
216 | dContext, colorType, std::move(cs), SkBackingFit::kApprox, dstInfo.dimensions(), |
217 | 1, GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
218 | if (!tempCtx) { |
219 | return false; |
220 | } |
221 | |
222 | std::unique_ptr<GrFragmentProcessor> fp; |
223 | if (canvas2DFastPath) { |
224 | fp = dContext->priv().createPMToUPMEffect( |
225 | GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType())); |
226 | if (dstInfo.colorType() == GrColorType::kBGRA_8888) { |
227 | fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::BGRA()); |
228 | dstInfo = dstInfo.makeColorType(GrColorType::kRGBA_8888); |
229 | } |
230 | // The render target context is incorrectly tagged as kPremul even though we're writing |
231 | // unpremul data thanks to the PMToUPM effect. Fake out the dst alpha type so we don't |
232 | // double unpremul. |
233 | dstInfo = dstInfo.makeAlphaType(kPremul_SkAlphaType); |
234 | } else { |
235 | fp = GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType()); |
236 | } |
237 | if (!fp) { |
238 | return false; |
239 | } |
240 | GrPaint paint; |
241 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
242 | paint.setColorFragmentProcessor(std::move(fp)); |
243 | |
244 | tempCtx->asRenderTargetContext()->fillRectToRect( |
245 | nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), |
246 | SkRect::MakeWH(dstInfo.width(), dstInfo.height()), |
247 | SkRect::MakeXYWH(pt.fX, pt.fY, dstInfo.width(), dstInfo.height())); |
248 | |
249 | return tempCtx->readPixels(dContext, dstInfo, dst, rowBytes, {0, 0}); |
250 | } |
251 | |
252 | bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin; |
253 | |
254 | auto supportedRead = caps->supportedReadPixelsColorType( |
255 | this->colorInfo().colorType(), srcProxy->backendFormat(), dstInfo.colorType()); |
256 | |
257 | bool makeTight = !caps->readPixelsRowBytesSupport() && tightRowBytes != rowBytes; |
258 | |
259 | bool convert = unpremul || premul || needColorConversion || flip || makeTight || |
260 | (dstInfo.colorType() != supportedRead.fColorType); |
261 | |
262 | std::unique_ptr<char[]> tmpPixels; |
263 | GrImageInfo tmpInfo; |
264 | void* readDst = dst; |
265 | size_t readRB = rowBytes; |
266 | if (convert) { |
267 | tmpInfo = {supportedRead.fColorType, this->colorInfo().alphaType(), |
268 | this->colorInfo().refColorSpace(), dstInfo.width(), dstInfo.height()}; |
269 | size_t tmpRB = tmpInfo.minRowBytes(); |
270 | size_t size = tmpRB * tmpInfo.height(); |
271 | // Chrome MSAN bots require the data to be initialized (hence the ()). |
272 | tmpPixels = std::make_unique<char[]>(size); |
273 | |
274 | readDst = tmpPixels.get(); |
275 | readRB = tmpRB; |
276 | pt.fY = flip ? srcSurface->height() - pt.fY - dstInfo.height() : pt.fY; |
277 | } |
278 | |
279 | dContext->priv().flushSurface(srcProxy); |
280 | dContext->submit(); |
281 | if (!dContext->priv().getGpu()->readPixels(srcSurface, pt.fX, pt.fY, dstInfo.width(), |
282 | dstInfo.height(), this->colorInfo().colorType(), |
283 | supportedRead.fColorType, readDst, readRB)) { |
284 | return false; |
285 | } |
286 | |
287 | if (convert) { |
288 | return GrConvertPixels(dstInfo, dst, rowBytes, tmpInfo, readDst, readRB, flip); |
289 | } |
290 | return true; |
291 | } |
292 | |
293 | bool GrSurfaceContext::writePixels(GrDirectContext* dContext, const GrImageInfo& origSrcInfo, |
294 | const void* src, size_t rowBytes, SkIPoint pt) { |
295 | ASSERT_SINGLE_OWNER |
296 | RETURN_FALSE_IF_ABANDONED |
297 | SkDEBUGCODE(this->validate();) |
298 | GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContext::writePixels" ); |
299 | |
300 | if (!dContext) { |
301 | return false; |
302 | } |
303 | |
304 | if (this->asSurfaceProxy()->readOnly()) { |
305 | return false; |
306 | } |
307 | |
308 | if (!src) { |
309 | return false; |
310 | } |
311 | |
312 | size_t tightRowBytes = origSrcInfo.minRowBytes(); |
313 | if (!rowBytes) { |
314 | rowBytes = tightRowBytes; |
315 | } else if (rowBytes < tightRowBytes) { |
316 | return false; |
317 | } |
318 | |
319 | if (!origSrcInfo.isValid()) { |
320 | return false; |
321 | } |
322 | |
323 | GrSurfaceProxy* dstProxy = this->asSurfaceProxy(); |
324 | |
325 | if (dstProxy->framebufferOnly()) { |
326 | return false; |
327 | } |
328 | |
329 | if (!dstProxy->instantiate(dContext->priv().resourceProvider())) { |
330 | return false; |
331 | } |
332 | |
333 | GrSurface* dstSurface = dstProxy->peekSurface(); |
334 | |
335 | auto srcInfo = origSrcInfo; |
336 | if (!srcInfo.clip(this->width(), this->height(), &pt, &src, rowBytes)) { |
337 | return false; |
338 | } |
339 | // Our tight row bytes may have been changed by clipping. |
340 | tightRowBytes = srcInfo.minRowBytes(); |
341 | |
342 | SkColorSpaceXformSteps::Flags flags = SkColorSpaceXformSteps{srcInfo, this->colorInfo()}.flags; |
343 | bool unpremul = flags.unpremul, |
344 | needColorConversion = flags.linearize || flags.gamut_transform || flags.encode, |
345 | premul = flags.premul; |
346 | |
347 | const GrCaps* caps = dContext->priv().caps(); |
348 | |
349 | auto rgbaDefaultFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, |
350 | GrRenderable::kNo); |
351 | |
352 | GrColorType dstColorType = this->colorInfo().colorType(); |
353 | // For canvas2D putImageData performance we have a special code path for unpremul RGBA_8888 srcs |
354 | // that are premultiplied on the GPU. This is kept as narrow as possible for now. |
355 | bool canvas2DFastPath = !caps->avoidWritePixelsFastPath() && premul && !needColorConversion && |
356 | (srcInfo.colorType() == GrColorType::kRGBA_8888 || |
357 | srcInfo.colorType() == GrColorType::kBGRA_8888) && |
358 | SkToBool(this->asRenderTargetContext()) && |
359 | (dstColorType == GrColorType::kRGBA_8888 || |
360 | dstColorType == GrColorType::kBGRA_8888) && |
361 | rgbaDefaultFormat.isValid() && |
362 | dContext->priv().validPMUPMConversionExists(); |
363 | |
364 | if (!caps->surfaceSupportsWritePixels(dstSurface) || canvas2DFastPath) { |
365 | GrColorType colorType; |
366 | |
367 | GrBackendFormat format; |
368 | SkAlphaType alphaType; |
369 | GrSwizzle tempReadSwizzle; |
370 | if (canvas2DFastPath) { |
371 | colorType = GrColorType::kRGBA_8888; |
372 | format = rgbaDefaultFormat; |
373 | alphaType = kUnpremul_SkAlphaType; |
374 | } else { |
375 | colorType = this->colorInfo().colorType(); |
376 | format = dstProxy->backendFormat().makeTexture2D(); |
377 | if (!format.isValid()) { |
378 | return false; |
379 | } |
380 | alphaType = this->colorInfo().alphaType(); |
381 | tempReadSwizzle = this->readSwizzle(); |
382 | } |
383 | |
384 | // It is more efficient for us to write pixels into a top left origin so we prefer that. |
385 | // However, if the final proxy isn't a render target then we must use a copy to move the |
386 | // data into it which requires the origins to match. If the final proxy is a render target |
387 | // we can use a draw instead which doesn't have this origin restriction. Thus for render |
388 | // targets we will use top left and otherwise we will make the origins match. |
389 | GrSurfaceOrigin tempOrigin = |
390 | this->asRenderTargetContext() ? kTopLeft_GrSurfaceOrigin : this->origin(); |
391 | auto tempProxy = dContext->priv().proxyProvider()->createProxy( |
392 | format, srcInfo.dimensions(), GrRenderable::kNo, 1, GrMipmapped::kNo, |
393 | SkBackingFit::kApprox, SkBudgeted::kYes, GrProtected::kNo); |
394 | if (!tempProxy) { |
395 | return false; |
396 | } |
397 | GrSurfaceProxyView tempView(tempProxy, tempOrigin, tempReadSwizzle); |
398 | GrSurfaceContext tempCtx(dContext, tempView, colorType, alphaType, |
399 | this->colorInfo().refColorSpace()); |
400 | |
401 | // In the fast path we always write the srcData to the temp context as though it were RGBA. |
402 | // When the data is really BGRA the write will cause the R and B channels to be swapped in |
403 | // the intermediate surface which gets corrected by a swizzle effect when drawing to the |
404 | // dst. |
405 | if (canvas2DFastPath) { |
406 | srcInfo = srcInfo.makeColorType(GrColorType::kRGBA_8888); |
407 | } |
408 | if (!tempCtx.writePixels(dContext, srcInfo, src, rowBytes, {0, 0})) { |
409 | return false; |
410 | } |
411 | |
412 | if (this->asRenderTargetContext()) { |
413 | std::unique_ptr<GrFragmentProcessor> fp; |
414 | if (canvas2DFastPath) { |
415 | fp = dContext->priv().createUPMToPMEffect( |
416 | GrTextureEffect::Make(std::move(tempView), alphaType)); |
417 | // Important: check the original src color type here! |
418 | if (origSrcInfo.colorType() == GrColorType::kBGRA_8888) { |
419 | fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::BGRA()); |
420 | } |
421 | } else { |
422 | fp = GrTextureEffect::Make(std::move(tempView), alphaType); |
423 | } |
424 | if (!fp) { |
425 | return false; |
426 | } |
427 | GrPaint paint; |
428 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
429 | paint.setColorFragmentProcessor(std::move(fp)); |
430 | this->asRenderTargetContext()->fillRectToRect( |
431 | nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), |
432 | SkRect::MakeXYWH(pt.fX, pt.fY, srcInfo.width(), srcInfo.height()), |
433 | SkRect::MakeWH(srcInfo.width(), srcInfo.height())); |
434 | } else { |
435 | SkIRect srcRect = SkIRect::MakeWH(srcInfo.width(), srcInfo.height()); |
436 | SkIPoint dstPoint = SkIPoint::Make(pt.fX, pt.fY); |
437 | if (!this->copy(tempProxy.get(), srcRect, dstPoint)) { |
438 | return false; |
439 | } |
440 | } |
441 | return true; |
442 | } |
443 | |
444 | GrColorType allowedColorType = |
445 | caps->supportedWritePixelsColorType(this->colorInfo().colorType(), |
446 | dstProxy->backendFormat(), |
447 | srcInfo.colorType()).fColorType; |
448 | bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin; |
449 | bool makeTight = !caps->writePixelsRowBytesSupport() && rowBytes != tightRowBytes; |
450 | bool convert = premul || unpremul || needColorConversion || makeTight || |
451 | (srcInfo.colorType() != allowedColorType) || flip; |
452 | |
453 | std::unique_ptr<char[]> tmpPixels; |
454 | GrColorType srcColorType = srcInfo.colorType(); |
455 | if (convert) { |
456 | GrImageInfo tmpInfo(allowedColorType, this->colorInfo().alphaType(), |
457 | this->colorInfo().refColorSpace(), srcInfo.width(), srcInfo.height()); |
458 | auto tmpRB = tmpInfo.minRowBytes(); |
459 | tmpPixels.reset(new char[tmpRB * tmpInfo.height()]); |
460 | |
461 | GrConvertPixels(tmpInfo, tmpPixels.get(), tmpRB, srcInfo, src, rowBytes, flip); |
462 | |
463 | srcColorType = tmpInfo.colorType(); |
464 | rowBytes = tmpRB; |
465 | src = tmpPixels.get(); |
466 | pt.fY = flip ? dstSurface->height() - pt.fY - tmpInfo.height() : pt.fY; |
467 | } |
468 | |
469 | // On platforms that prefer flushes over VRAM use (i.e., ANGLE) we're better off forcing a |
470 | // complete flush here. On platforms that prefer VRAM use over flushes we're better off |
471 | // giving the drawing manager the chance of skipping the flush (i.e., by passing in the |
472 | // destination proxy) |
473 | // TODO: should this policy decision just be moved into the drawing manager? |
474 | dContext->priv().flushSurface(caps->preferVRAMUseOverFlushes() ? dstProxy : nullptr); |
475 | |
476 | return dContext->priv().getGpu()->writePixels(dstSurface, pt.fX, pt.fY, srcInfo.width(), |
477 | srcInfo.height(), this->colorInfo().colorType(), |
478 | srcColorType, src, rowBytes); |
479 | } |
480 | |
481 | void GrSurfaceContext::asyncRescaleAndReadPixels(GrDirectContext* dContext, |
482 | const SkImageInfo& info, |
483 | const SkIRect& srcRect, |
484 | RescaleGamma rescaleGamma, |
485 | SkFilterQuality rescaleQuality, |
486 | ReadPixelsCallback callback, |
487 | ReadPixelsContext callbackContext) { |
488 | // We implement this by rendering and we don't currently support rendering kUnpremul. |
489 | if (info.alphaType() == kUnpremul_SkAlphaType) { |
490 | callback(callbackContext, nullptr); |
491 | return; |
492 | } |
493 | if (!dContext) { |
494 | callback(callbackContext, nullptr); |
495 | return; |
496 | } |
497 | auto rt = this->asRenderTargetProxy(); |
498 | if (rt && rt->wrapsVkSecondaryCB()) { |
499 | callback(callbackContext, nullptr); |
500 | return; |
501 | } |
502 | if (rt && rt->framebufferOnly()) { |
503 | callback(callbackContext, nullptr); |
504 | return; |
505 | } |
506 | auto dstCT = SkColorTypeToGrColorType(info.colorType()); |
507 | if (dstCT == GrColorType::kUnknown) { |
508 | callback(callbackContext, nullptr); |
509 | return; |
510 | } |
511 | bool needsRescale = srcRect.width() != info.width() || srcRect.height() != info.height(); |
512 | auto colorTypeOfFinalContext = this->colorInfo().colorType(); |
513 | auto backendFormatOfFinalContext = this->asSurfaceProxy()->backendFormat(); |
514 | if (needsRescale) { |
515 | colorTypeOfFinalContext = dstCT; |
516 | backendFormatOfFinalContext = |
517 | this->caps()->getDefaultBackendFormat(dstCT, GrRenderable::kYes); |
518 | } |
519 | auto readInfo = this->caps()->supportedReadPixelsColorType(colorTypeOfFinalContext, |
520 | backendFormatOfFinalContext, dstCT); |
521 | // Fail if we can't read from the source surface's color type. |
522 | if (readInfo.fColorType == GrColorType::kUnknown) { |
523 | callback(callbackContext, nullptr); |
524 | return; |
525 | } |
526 | // Fail if read color type does not have all of dstCT's color channels and those missing color |
527 | // channels are in the src. |
528 | uint32_t dstChannels = GrColorTypeChannelFlags(dstCT); |
529 | uint32_t legalReadChannels = GrColorTypeChannelFlags(readInfo.fColorType); |
530 | uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType()); |
531 | if ((~legalReadChannels & dstChannels) & srcChannels) { |
532 | callback(callbackContext, nullptr); |
533 | return; |
534 | } |
535 | |
536 | std::unique_ptr<GrRenderTargetContext> tempRTC; |
537 | int x = srcRect.fLeft; |
538 | int y = srcRect.fTop; |
539 | if (needsRescale) { |
540 | tempRTC = this->rescale(info, kTopLeft_GrSurfaceOrigin, srcRect, rescaleGamma, |
541 | rescaleQuality); |
542 | if (!tempRTC) { |
543 | callback(callbackContext, nullptr); |
544 | return; |
545 | } |
546 | SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace())); |
547 | SkASSERT(tempRTC->origin() == kTopLeft_GrSurfaceOrigin); |
548 | x = y = 0; |
549 | } else { |
550 | sk_sp<GrColorSpaceXform> xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(), |
551 | this->colorInfo().alphaType(), |
552 | info.colorSpace(), |
553 | info.alphaType()); |
554 | // Insert a draw to a temporary surface if we need to do a y-flip or color space conversion. |
555 | if (this->origin() == kBottomLeft_GrSurfaceOrigin || xform) { |
556 | GrSurfaceProxyView texProxyView = this->readSurfaceView(); |
557 | SkRect srcRectToDraw = SkRect::Make(srcRect); |
558 | // If the src is not texturable first try to make a copy to a texture. |
559 | if (!texProxyView.asTextureProxy()) { |
560 | texProxyView = |
561 | GrSurfaceProxyView::Copy(fContext, texProxyView, GrMipmapped::kNo, srcRect, |
562 | SkBackingFit::kApprox, SkBudgeted::kNo); |
563 | if (!texProxyView) { |
564 | callback(callbackContext, nullptr); |
565 | return; |
566 | } |
567 | SkASSERT(texProxyView.asTextureProxy()); |
568 | srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height()); |
569 | } |
570 | tempRTC = GrRenderTargetContext::Make(dContext, this->colorInfo().colorType(), |
571 | info.refColorSpace(), SkBackingFit::kApprox, |
572 | srcRect.size(), 1, GrMipmapped::kNo, |
573 | GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
574 | if (!tempRTC) { |
575 | callback(callbackContext, nullptr); |
576 | return; |
577 | } |
578 | tempRTC->drawTexture(nullptr, |
579 | std::move(texProxyView), |
580 | this->colorInfo().alphaType(), |
581 | GrSamplerState::Filter::kNearest, |
582 | GrSamplerState::MipmapMode::kNone, |
583 | SkBlendMode::kSrc, |
584 | SK_PMColor4fWHITE, |
585 | srcRectToDraw, |
586 | SkRect::MakeWH(srcRect.width(), srcRect.height()), |
587 | GrAA::kNo, |
588 | GrQuadAAFlags::kNone, |
589 | SkCanvas::kFast_SrcRectConstraint, |
590 | SkMatrix::I(), |
591 | std::move(xform)); |
592 | x = y = 0; |
593 | } |
594 | } |
595 | auto rtc = tempRTC ? tempRTC.get() : this; |
596 | return rtc->asyncReadPixels(dContext, SkIRect::MakeXYWH(x, y, info.width(), info.height()), |
597 | info.colorType(), callback, callbackContext); |
598 | } |
599 | |
600 | class GrSurfaceContext::AsyncReadResult : public SkImage::AsyncReadResult { |
601 | public: |
602 | AsyncReadResult(uint32_t inboxID) : fInboxID(inboxID) {} |
603 | ~AsyncReadResult() override { |
604 | for (int i = 0; i < fPlanes.count(); ++i) { |
605 | if (!fPlanes[i].fMappedBuffer) { |
606 | delete[] static_cast<const char*>(fPlanes[i].fData); |
607 | } else { |
608 | GrClientMappedBufferManager::BufferFinishedMessageBus::Post( |
609 | {std::move(fPlanes[i].fMappedBuffer), fInboxID}); |
610 | } |
611 | } |
612 | } |
613 | |
614 | int count() const override { return fPlanes.count(); } |
615 | const void* data(int i) const override { return fPlanes[i].fData; } |
616 | size_t rowBytes(int i) const override { return fPlanes[i].fRowBytes; } |
617 | |
618 | bool addTransferResult(const PixelTransferResult& result, |
619 | SkISize dimensions, |
620 | size_t rowBytes, |
621 | GrClientMappedBufferManager* manager) { |
622 | SkASSERT(!result.fTransferBuffer->isMapped()); |
623 | const void* mappedData = result.fTransferBuffer->map(); |
624 | if (!mappedData) { |
625 | return false; |
626 | } |
627 | if (result.fPixelConverter) { |
628 | std::unique_ptr<char[]> convertedData(new char[rowBytes * dimensions.height()]); |
629 | result.fPixelConverter(convertedData.get(), mappedData); |
630 | this->addCpuPlane(std::move(convertedData), rowBytes); |
631 | result.fTransferBuffer->unmap(); |
632 | } else { |
633 | manager->insert(result.fTransferBuffer); |
634 | this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer)); |
635 | } |
636 | return true; |
637 | } |
638 | |
639 | void addCpuPlane(std::unique_ptr<const char[]> data, size_t rowBytes) { |
640 | SkASSERT(data); |
641 | SkASSERT(rowBytes > 0); |
642 | fPlanes.emplace_back(data.release(), rowBytes, nullptr); |
643 | } |
644 | |
645 | private: |
646 | void addMappedPlane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> mappedBuffer) { |
647 | SkASSERT(data); |
648 | SkASSERT(rowBytes > 0); |
649 | SkASSERT(mappedBuffer); |
650 | SkASSERT(mappedBuffer->isMapped()); |
651 | fPlanes.emplace_back(data, rowBytes, std::move(mappedBuffer)); |
652 | } |
653 | |
654 | struct Plane { |
655 | Plane(const void* data, size_t rowBytes, sk_sp<GrGpuBuffer> buffer) |
656 | : fData(data), fRowBytes(rowBytes), fMappedBuffer(std::move(buffer)) {} |
657 | const void* fData; |
658 | size_t fRowBytes; |
659 | // If this is null then fData is heap alloc and must be delete[]ed as const char[]. |
660 | sk_sp<GrGpuBuffer> fMappedBuffer; |
661 | }; |
662 | SkSTArray<3, Plane> fPlanes; |
663 | uint32_t fInboxID; |
664 | }; |
665 | |
666 | void GrSurfaceContext::asyncReadPixels(GrDirectContext* dContext, |
667 | const SkIRect& rect, |
668 | SkColorType colorType, |
669 | ReadPixelsCallback callback, |
670 | ReadPixelsContext callbackContext) { |
671 | SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width()); |
672 | SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height()); |
673 | |
674 | if (!dContext || this->asSurfaceProxy()->isProtected() == GrProtected::kYes) { |
675 | callback(callbackContext, nullptr); |
676 | return; |
677 | } |
678 | |
679 | auto mappedBufferManager = dContext->priv().clientMappedBufferManager(); |
680 | |
681 | auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect); |
682 | |
683 | if (!transferResult.fTransferBuffer) { |
684 | auto ii = SkImageInfo::Make(rect.size(), colorType, this->colorInfo().alphaType(), |
685 | this->colorInfo().refColorSpace()); |
686 | auto result = std::make_unique<AsyncReadResult>(0); |
687 | std::unique_ptr<char[]> data(new char[ii.computeMinByteSize()]); |
688 | SkPixmap pm(ii, data.get(), ii.minRowBytes()); |
689 | result->addCpuPlane(std::move(data), pm.rowBytes()); |
690 | |
691 | SkIPoint pt{rect.fLeft, rect.fTop}; |
692 | if (!this->readPixels(dContext, ii, pm.writable_addr(), pm.rowBytes(), pt)) { |
693 | callback(callbackContext, nullptr); |
694 | return; |
695 | } |
696 | callback(callbackContext, std::move(result)); |
697 | return; |
698 | } |
699 | |
700 | struct FinishContext { |
701 | ReadPixelsCallback* fClientCallback; |
702 | ReadPixelsContext fClientContext; |
703 | SkISize fSize; |
704 | SkColorType fColorType; |
705 | GrClientMappedBufferManager* fMappedBufferManager; |
706 | PixelTransferResult fTransferResult; |
707 | }; |
708 | // Assumption is that the caller would like to flush. We could take a parameter or require an |
709 | // explicit flush from the caller. We'd have to have a way to defer attaching the finish |
710 | // callback to GrGpu until after the next flush that flushes our op list, though. |
711 | auto* finishContext = new FinishContext{callback, |
712 | callbackContext, |
713 | rect.size(), |
714 | colorType, |
715 | mappedBufferManager, |
716 | std::move(transferResult)}; |
717 | auto finishCallback = [](GrGpuFinishedContext c) { |
718 | const auto* context = reinterpret_cast<const FinishContext*>(c); |
719 | auto result = std::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID()); |
720 | size_t rowBytes = context->fSize.width() * SkColorTypeBytesPerPixel(context->fColorType); |
721 | if (!result->addTransferResult(context->fTransferResult, context->fSize, rowBytes, |
722 | context->fMappedBufferManager)) { |
723 | result.reset(); |
724 | } |
725 | (*context->fClientCallback)(context->fClientContext, std::move(result)); |
726 | delete context; |
727 | }; |
728 | GrFlushInfo flushInfo; |
729 | flushInfo.fFinishedContext = finishContext; |
730 | flushInfo.fFinishedProc = finishCallback; |
731 | this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo, nullptr); |
732 | } |
733 | |
734 | void GrSurfaceContext::asyncRescaleAndReadPixelsYUV420(GrDirectContext* dContext, |
735 | SkYUVColorSpace yuvColorSpace, |
736 | sk_sp<SkColorSpace> dstColorSpace, |
737 | const SkIRect& srcRect, |
738 | SkISize dstSize, |
739 | RescaleGamma rescaleGamma, |
740 | SkFilterQuality rescaleQuality, |
741 | ReadPixelsCallback callback, |
742 | ReadPixelsContext callbackContext) { |
743 | SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width()); |
744 | SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height()); |
745 | SkASSERT(!dstSize.isZero()); |
746 | SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0)); |
747 | |
748 | if (!dContext) { |
749 | callback(callbackContext, nullptr); |
750 | return; |
751 | } |
752 | auto rt = this->asRenderTargetProxy(); |
753 | if (rt && rt->wrapsVkSecondaryCB()) { |
754 | callback(callbackContext, nullptr); |
755 | return; |
756 | } |
757 | if (rt && rt->framebufferOnly()) { |
758 | callback(callbackContext, nullptr); |
759 | return; |
760 | } |
761 | if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) { |
762 | callback(callbackContext, nullptr); |
763 | return; |
764 | } |
765 | int x = srcRect.fLeft; |
766 | int y = srcRect.fTop; |
767 | bool needsRescale = srcRect.size() != dstSize; |
768 | GrSurfaceProxyView srcView; |
769 | if (needsRescale) { |
770 | // We assume the caller wants kPremul. There is no way to indicate a preference. |
771 | auto info = SkImageInfo::Make(dstSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType, |
772 | dstColorSpace); |
773 | // TODO: Incorporate the YUV conversion into last pass of rescaling. |
774 | auto tempRTC = this->rescale(info, kTopLeft_GrSurfaceOrigin, srcRect, rescaleGamma, |
775 | rescaleQuality); |
776 | if (!tempRTC) { |
777 | callback(callbackContext, nullptr); |
778 | return; |
779 | } |
780 | SkASSERT(SkColorSpace::Equals(tempRTC->colorInfo().colorSpace(), info.colorSpace())); |
781 | SkASSERT(tempRTC->origin() == kTopLeft_GrSurfaceOrigin); |
782 | x = y = 0; |
783 | srcView = tempRTC->readSurfaceView(); |
784 | } else { |
785 | srcView = this->readSurfaceView(); |
786 | if (!srcView.asTextureProxy()) { |
787 | srcView = GrSurfaceProxyView::Copy(fContext, std::move(srcView), GrMipmapped::kNo, |
788 | srcRect, SkBackingFit::kApprox, SkBudgeted::kYes); |
789 | if (!srcView) { |
790 | // If we can't get a texture copy of the contents then give up. |
791 | callback(callbackContext, nullptr); |
792 | return; |
793 | } |
794 | SkASSERT(srcView.asTextureProxy()); |
795 | x = y = 0; |
796 | } |
797 | // We assume the caller wants kPremul. There is no way to indicate a preference. |
798 | sk_sp<GrColorSpaceXform> xform = GrColorSpaceXform::Make( |
799 | this->colorInfo().colorSpace(), this->colorInfo().alphaType(), dstColorSpace.get(), |
800 | kPremul_SkAlphaType); |
801 | if (xform) { |
802 | SkRect srcRectToDraw = SkRect::MakeXYWH(x, y, srcRect.width(), srcRect.height()); |
803 | auto tempRTC = GrRenderTargetContext::Make( |
804 | dContext, this->colorInfo().colorType(), dstColorSpace, SkBackingFit::kApprox, |
805 | dstSize, 1, GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
806 | if (!tempRTC) { |
807 | callback(callbackContext, nullptr); |
808 | return; |
809 | } |
810 | tempRTC->drawTexture(nullptr, |
811 | std::move(srcView), |
812 | this->colorInfo().alphaType(), |
813 | GrSamplerState::Filter::kNearest, |
814 | GrSamplerState::MipmapMode::kNone, |
815 | SkBlendMode::kSrc, |
816 | SK_PMColor4fWHITE, |
817 | srcRectToDraw, |
818 | SkRect::Make(srcRect.size()), |
819 | GrAA::kNo, |
820 | GrQuadAAFlags::kNone, |
821 | SkCanvas::kFast_SrcRectConstraint, |
822 | SkMatrix::I(), |
823 | std::move(xform)); |
824 | srcView = tempRTC->readSurfaceView(); |
825 | SkASSERT(srcView.asTextureProxy()); |
826 | x = y = 0; |
827 | } |
828 | } |
829 | |
830 | auto yRTC = GrRenderTargetContext::MakeWithFallback( |
831 | dContext, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, dstSize, 1, |
832 | GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
833 | int halfW = dstSize.width() /2; |
834 | int halfH = dstSize.height()/2; |
835 | auto uRTC = GrRenderTargetContext::MakeWithFallback( |
836 | dContext, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, {halfW, halfH}, |
837 | 1, GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
838 | auto vRTC = GrRenderTargetContext::MakeWithFallback( |
839 | dContext, GrColorType::kAlpha_8, dstColorSpace, SkBackingFit::kApprox, {halfW, halfH}, |
840 | 1, GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
841 | if (!yRTC || !uRTC || !vRTC) { |
842 | callback(callbackContext, nullptr); |
843 | return; |
844 | } |
845 | |
846 | float baseM[20]; |
847 | SkColorMatrix_RGB2YUV(yuvColorSpace, baseM); |
848 | |
849 | // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost? |
850 | |
851 | auto texMatrix = SkMatrix::Translate(x, y); |
852 | |
853 | SkRect dstRectY = SkRect::Make(dstSize); |
854 | SkRect dstRectUV = SkRect::MakeWH(halfW, halfH); |
855 | |
856 | bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport(); |
857 | PixelTransferResult yTransfer, uTransfer, vTransfer; |
858 | |
859 | // This matrix generates (r,g,b,a) = (0, 0, 0, y) |
860 | float yM[20]; |
861 | std::fill_n(yM, 15, 0.f); |
862 | std::copy_n(baseM + 0, 5, yM + 15); |
863 | GrPaint yPaint; |
864 | auto yTexFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix); |
865 | auto yColFP = GrColorMatrixFragmentProcessor::Make(std::move(yTexFP), yM, |
866 | /*unpremulInput=*/false, |
867 | /*clampRGBOutput=*/true, |
868 | /*premulOutput=*/false); |
869 | yPaint.setColorFragmentProcessor(std::move(yColFP)); |
870 | yPaint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
871 | yRTC->fillRectToRect(nullptr, std::move(yPaint), GrAA::kNo, SkMatrix::I(), dstRectY, dstRectY); |
872 | if (!doSynchronousRead) { |
873 | yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8, |
874 | SkIRect::MakeWH(yRTC->width(), yRTC->height())); |
875 | if (!yTransfer.fTransferBuffer) { |
876 | callback(callbackContext, nullptr); |
877 | return; |
878 | } |
879 | } |
880 | |
881 | texMatrix.preScale(2.f, 2.f); |
882 | // This matrix generates (r,g,b,a) = (0, 0, 0, u) |
883 | float uM[20]; |
884 | std::fill_n(uM, 15, 0.f); |
885 | std::copy_n(baseM + 5, 5, uM + 15); |
886 | GrPaint uPaint; |
887 | auto uTexFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix, |
888 | GrSamplerState::Filter::kLinear); |
889 | auto uColFP = GrColorMatrixFragmentProcessor::Make(std::move(uTexFP), uM, |
890 | /*unpremulInput=*/false, |
891 | /*clampRGBOutput=*/true, |
892 | /*premulOutput=*/false); |
893 | uPaint.setColorFragmentProcessor(std::move(uColFP)); |
894 | uPaint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
895 | uRTC->fillRectToRect(nullptr, std::move(uPaint), GrAA::kNo, SkMatrix::I(), dstRectUV, |
896 | dstRectUV); |
897 | if (!doSynchronousRead) { |
898 | uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8, |
899 | SkIRect::MakeWH(uRTC->width(), uRTC->height())); |
900 | if (!uTransfer.fTransferBuffer) { |
901 | callback(callbackContext, nullptr); |
902 | return; |
903 | } |
904 | } |
905 | |
906 | // This matrix generates (r,g,b,a) = (0, 0, 0, v) |
907 | float vM[20]; |
908 | std::fill_n(vM, 15, 0.f); |
909 | std::copy_n(baseM + 10, 5, vM + 15); |
910 | GrPaint vPaint; |
911 | auto vTexFP = GrTextureEffect::Make(std::move(srcView), this->colorInfo().alphaType(), |
912 | texMatrix, GrSamplerState::Filter::kLinear); |
913 | auto vColFP = GrColorMatrixFragmentProcessor::Make(std::move(vTexFP), vM, |
914 | /*unpremulInput=*/false, |
915 | /*clampRGBOutput=*/true, |
916 | /*premulOutput=*/false); |
917 | vPaint.setColorFragmentProcessor(std::move(vColFP)); |
918 | vPaint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
919 | vRTC->fillRectToRect(nullptr, std::move(vPaint), GrAA::kNo, SkMatrix::I(), dstRectUV, |
920 | dstRectUV); |
921 | if (!doSynchronousRead) { |
922 | vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8, |
923 | SkIRect::MakeWH(vRTC->width(), vRTC->height())); |
924 | if (!vTransfer.fTransferBuffer) { |
925 | callback(callbackContext, nullptr); |
926 | return; |
927 | } |
928 | } |
929 | |
930 | if (doSynchronousRead) { |
931 | GrImageInfo yInfo(GrColorType::kAlpha_8, kPremul_SkAlphaType, nullptr, dstSize); |
932 | GrImageInfo uvInfo = yInfo.makeWH(halfW, halfH); |
933 | size_t yRB = yInfo.minRowBytes(); |
934 | size_t uvRB = uvInfo.minRowBytes(); |
935 | std::unique_ptr<char[]> y(new char[yRB * yInfo.height()]); |
936 | std::unique_ptr<char[]> u(new char[uvRB*uvInfo.height()]); |
937 | std::unique_ptr<char[]> v(new char[uvRB*uvInfo.height()]); |
938 | if (!yRTC->readPixels(dContext, yInfo, y.get(), yRB, {0, 0}) || |
939 | !uRTC->readPixels(dContext, uvInfo, u.get(), uvRB, {0, 0}) || |
940 | !vRTC->readPixels(dContext, uvInfo, v.get(), uvRB, {0, 0})) { |
941 | callback(callbackContext, nullptr); |
942 | return; |
943 | } |
944 | auto result = std::make_unique<AsyncReadResult>(dContext->priv().contextID()); |
945 | result->addCpuPlane(std::move(y), yRB ); |
946 | result->addCpuPlane(std::move(u), uvRB); |
947 | result->addCpuPlane(std::move(v), uvRB); |
948 | callback(callbackContext, std::move(result)); |
949 | return; |
950 | } |
951 | |
952 | struct FinishContext { |
953 | ReadPixelsCallback* fClientCallback; |
954 | ReadPixelsContext fClientContext; |
955 | GrClientMappedBufferManager* fMappedBufferManager; |
956 | SkISize fSize; |
957 | PixelTransferResult fYTransfer; |
958 | PixelTransferResult fUTransfer; |
959 | PixelTransferResult fVTransfer; |
960 | }; |
961 | // Assumption is that the caller would like to flush. We could take a parameter or require an |
962 | // explicit flush from the caller. We'd have to have a way to defer attaching the finish |
963 | // callback to GrGpu until after the next flush that flushes our op list, though. |
964 | auto* finishContext = new FinishContext{callback, |
965 | callbackContext, |
966 | dContext->priv().clientMappedBufferManager(), |
967 | dstSize, |
968 | std::move(yTransfer), |
969 | std::move(uTransfer), |
970 | std::move(vTransfer)}; |
971 | auto finishCallback = [](GrGpuFinishedContext c) { |
972 | const auto* context = reinterpret_cast<const FinishContext*>(c); |
973 | auto result = std::make_unique<AsyncReadResult>(context->fMappedBufferManager->inboxID()); |
974 | auto manager = context->fMappedBufferManager; |
975 | size_t rowBytes = SkToSizeT(context->fSize.width()); |
976 | if (!result->addTransferResult(context->fYTransfer, context->fSize, rowBytes, manager)) { |
977 | (*context->fClientCallback)(context->fClientContext, nullptr); |
978 | delete context; |
979 | return; |
980 | } |
981 | rowBytes /= 2; |
982 | SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2}; |
983 | if (!result->addTransferResult(context->fUTransfer, uvSize, rowBytes, manager)) { |
984 | (*context->fClientCallback)(context->fClientContext, nullptr); |
985 | delete context; |
986 | return; |
987 | } |
988 | if (!result->addTransferResult(context->fVTransfer, uvSize, rowBytes, manager)) { |
989 | (*context->fClientCallback)(context->fClientContext, nullptr); |
990 | delete context; |
991 | return; |
992 | } |
993 | (*context->fClientCallback)(context->fClientContext, std::move(result)); |
994 | delete context; |
995 | }; |
996 | GrFlushInfo flushInfo; |
997 | flushInfo.fFinishedContext = finishContext; |
998 | flushInfo.fFinishedProc = finishCallback; |
999 | this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo, nullptr); |
1000 | } |
1001 | |
1002 | bool GrSurfaceContext::copy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { |
1003 | ASSERT_SINGLE_OWNER |
1004 | RETURN_FALSE_IF_ABANDONED |
1005 | SkDEBUGCODE(this->validate();) |
1006 | GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContextPriv::copy" ); |
1007 | |
1008 | const GrCaps* caps = fContext->priv().caps(); |
1009 | |
1010 | SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal); |
1011 | SkASSERT(src->backendFormat() == this->asSurfaceProxy()->backendFormat()); |
1012 | |
1013 | if (this->asSurfaceProxy()->framebufferOnly()) { |
1014 | return false; |
1015 | } |
1016 | |
1017 | if (!caps->canCopySurface(this->asSurfaceProxy(), src, srcRect, dstPoint)) { |
1018 | return false; |
1019 | } |
1020 | |
1021 | // The swizzle doesn't matter for copies and it is not used. |
1022 | return this->drawingManager()->newCopyRenderTask( |
1023 | GrSurfaceProxyView(sk_ref_sp(src), this->origin(), GrSwizzle("rgba" )), srcRect, |
1024 | this->readSurfaceView(), dstPoint); |
1025 | } |
1026 | |
1027 | std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale(const GrImageInfo& info, |
1028 | GrSurfaceOrigin origin, |
1029 | SkIRect srcRect, |
1030 | RescaleGamma rescaleGamma, |
1031 | SkFilterQuality rescaleQuality) { |
1032 | auto rtProxy = this->asRenderTargetProxy(); |
1033 | if (rtProxy && rtProxy->wrapsVkSecondaryCB()) { |
1034 | return nullptr; |
1035 | } |
1036 | |
1037 | if (this->asSurfaceProxy()->framebufferOnly()) { |
1038 | return nullptr; |
1039 | } |
1040 | |
1041 | // We rescale by drawing and don't currently support drawing to a kUnpremul destination. |
1042 | if (info.alphaType() == kUnpremul_SkAlphaType) { |
1043 | return nullptr; |
1044 | } |
1045 | |
1046 | GrSurfaceProxyView texView = this->readSurfaceView(); |
1047 | SkAlphaType srcAlphaType = this->colorInfo().alphaType(); |
1048 | if (!texView.asTextureProxy()) { |
1049 | texView = GrSurfaceProxyView::Copy(fContext, std::move(texView), GrMipmapped::kNo, srcRect, |
1050 | SkBackingFit::kApprox, SkBudgeted::kNo); |
1051 | if (!texView) { |
1052 | return nullptr; |
1053 | } |
1054 | SkASSERT(texView.asTextureProxy()); |
1055 | srcRect = SkIRect::MakeSize(srcRect.size()); |
1056 | } |
1057 | |
1058 | // Within a rescaling pass A is the input (if not null) and B is the output. At the end of the |
1059 | // pass B is moved to A. If 'this' is the input on the first pass then tempA is null. |
1060 | std::unique_ptr<GrRenderTargetContext> tempA; |
1061 | std::unique_ptr<GrRenderTargetContext> tempB; |
1062 | |
1063 | // Assume we should ignore the rescale linear request if the surface has no color space since |
1064 | // it's unclear how we'd linearize from an unknown color space. |
1065 | if (rescaleGamma == RescaleGamma::kLinear && this->colorInfo().colorSpace() && |
1066 | !this->colorInfo().colorSpace()->gammaIsLinear()) { |
1067 | auto cs = this->colorInfo().colorSpace()->makeLinearGamma(); |
1068 | auto xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(), srcAlphaType, cs.get(), |
1069 | kPremul_SkAlphaType); |
1070 | // We'll fall back to kRGBA_8888 if half float not supported. |
1071 | auto linearRTC = GrRenderTargetContext::MakeWithFallback( |
1072 | fContext, GrColorType::kRGBA_F16, cs, SkBackingFit::kApprox, srcRect.size(), 1, |
1073 | GrMipmapped::kNo, GrProtected::kNo, origin); |
1074 | if (!linearRTC) { |
1075 | return nullptr; |
1076 | } |
1077 | // 1-to-1 draw can always be kFast. |
1078 | linearRTC->drawTexture(nullptr, |
1079 | std::move(texView), |
1080 | srcAlphaType, |
1081 | GrSamplerState::Filter::kNearest, |
1082 | GrSamplerState::MipmapMode::kNone, |
1083 | SkBlendMode::kSrc, |
1084 | SK_PMColor4fWHITE, |
1085 | SkRect::Make(srcRect), |
1086 | SkRect::Make(srcRect.size()), |
1087 | GrAA::kNo, |
1088 | GrQuadAAFlags::kNone, |
1089 | SkCanvas::kFast_SrcRectConstraint, |
1090 | SkMatrix::I(), |
1091 | std::move(xform)); |
1092 | texView = linearRTC->readSurfaceView(); |
1093 | SkASSERT(texView.asTextureProxy()); |
1094 | tempA = std::move(linearRTC); |
1095 | srcRect = SkIRect::MakeSize(srcRect.size()); |
1096 | } |
1097 | |
1098 | while (srcRect.size() != info.dimensions()) { |
1099 | SkISize nextDims = info.dimensions(); |
1100 | if (rescaleQuality != kNone_SkFilterQuality) { |
1101 | if (srcRect.width() > info.width()) { |
1102 | nextDims.fWidth = std::max((srcRect.width() + 1)/2, info.width()); |
1103 | } else if (srcRect.width() < info.width()) { |
1104 | nextDims.fWidth = std::min(srcRect.width()*2, info.width()); |
1105 | } |
1106 | if (srcRect.height() > info.height()) { |
1107 | nextDims.fHeight = std::max((srcRect.height() + 1)/2, info.height()); |
1108 | } else if (srcRect.height() < info.height()) { |
1109 | nextDims.fHeight = std::min(srcRect.height()*2, info.height()); |
1110 | } |
1111 | } |
1112 | auto input = tempA ? tempA.get() : this; |
1113 | GrColorType colorType = input->colorInfo().colorType(); |
1114 | auto cs = input->colorInfo().refColorSpace(); |
1115 | sk_sp<GrColorSpaceXform> xform; |
1116 | auto prevAlphaType = input->colorInfo().alphaType(); |
1117 | if (nextDims == info.dimensions()) { |
1118 | // Might as well fold conversion to final info in the last step. |
1119 | cs = info.refColorSpace(); |
1120 | xform = GrColorSpaceXform::Make(input->colorInfo().colorSpace(), |
1121 | input->colorInfo().alphaType(), cs.get(), |
1122 | info.alphaType()); |
1123 | } |
1124 | tempB = GrRenderTargetContext::MakeWithFallback(fContext, colorType, std::move(cs), |
1125 | SkBackingFit::kApprox, nextDims, 1, |
1126 | GrMipmapped::kNo, GrProtected::kNo, origin); |
1127 | if (!tempB) { |
1128 | return nullptr; |
1129 | } |
1130 | auto dstRect = SkRect::Make(nextDims); |
1131 | if (rescaleQuality == kHigh_SkFilterQuality) { |
1132 | SkMatrix matrix; |
1133 | matrix.setScaleTranslate((float)srcRect.width()/nextDims.width(), |
1134 | (float)srcRect.height()/nextDims.height(), |
1135 | srcRect.x(), |
1136 | srcRect.y()); |
1137 | std::unique_ptr<GrFragmentProcessor> fp; |
1138 | auto dir = GrBicubicEffect::Direction::kXY; |
1139 | if (nextDims.width() == srcRect.width()) { |
1140 | dir = GrBicubicEffect::Direction::kY; |
1141 | } else if (nextDims.height() == srcRect.height()) { |
1142 | dir = GrBicubicEffect::Direction::kX; |
1143 | } |
1144 | static constexpr auto kWM = GrSamplerState::WrapMode::kClamp; |
1145 | static constexpr auto kKernel = GrBicubicEffect::Kernel::kCatmullRom; |
1146 | fp = GrBicubicEffect::MakeSubset(std::move(texView), prevAlphaType, matrix, kWM, kWM, |
1147 | SkRect::Make(srcRect), kKernel, dir, *this->caps()); |
1148 | if (xform) { |
1149 | fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform)); |
1150 | } |
1151 | GrPaint paint; |
1152 | paint.setColorFragmentProcessor(std::move(fp)); |
1153 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
1154 | tempB->fillRectToRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, |
1155 | dstRect); |
1156 | } else { |
1157 | auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest |
1158 | : GrSamplerState::Filter::kLinear; |
1159 | // Minimizing draw with integer coord src and dev rects can always be kFast. |
1160 | auto constraint = SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint; |
1161 | if (nextDims.width() <= srcRect.width() && nextDims.height() <= srcRect.height()) { |
1162 | constraint = SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint; |
1163 | } |
1164 | tempB->drawTexture(nullptr, |
1165 | std::move(texView), |
1166 | srcAlphaType, |
1167 | filter, |
1168 | GrSamplerState::MipmapMode::kNone, |
1169 | SkBlendMode::kSrc, |
1170 | SK_PMColor4fWHITE, |
1171 | SkRect::Make(srcRect), |
1172 | dstRect, |
1173 | GrAA::kNo, |
1174 | GrQuadAAFlags::kNone, |
1175 | constraint, |
1176 | SkMatrix::I(), |
1177 | std::move(xform)); |
1178 | } |
1179 | texView = tempB->readSurfaceView(); |
1180 | tempA = std::move(tempB); |
1181 | srcRect = SkIRect::MakeSize(nextDims); |
1182 | } |
1183 | SkASSERT(tempA); |
1184 | return tempA; |
1185 | } |
1186 | |
1187 | GrSemaphoresSubmitted GrSurfaceContext::flush(SkSurface::BackendSurfaceAccess access, |
1188 | const GrFlushInfo& info, |
1189 | const GrBackendSurfaceMutableState* newState) { |
1190 | ASSERT_SINGLE_OWNER |
1191 | if (fContext->abandoned()) { |
1192 | if (info.fSubmittedProc) { |
1193 | info.fSubmittedProc(info.fSubmittedContext, false); |
1194 | } |
1195 | if (info.fFinishedProc) { |
1196 | info.fFinishedProc(info.fFinishedContext); |
1197 | } |
1198 | return GrSemaphoresSubmitted::kNo; |
1199 | } |
1200 | SkDEBUGCODE(this->validate();) |
1201 | GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext" , "flush" , fContext); |
1202 | |
1203 | return this->drawingManager()->flushSurface(this->asSurfaceProxy(), access, info, newState); |
1204 | } |
1205 | |
1206 | GrSurfaceContext::PixelTransferResult GrSurfaceContext::transferPixels(GrColorType dstCT, |
1207 | const SkIRect& rect) { |
1208 | SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width()); |
1209 | SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height()); |
1210 | auto direct = fContext->asDirectContext(); |
1211 | if (!direct) { |
1212 | return {}; |
1213 | } |
1214 | auto rtProxy = this->asRenderTargetProxy(); |
1215 | if (rtProxy && rtProxy->wrapsVkSecondaryCB()) { |
1216 | return {}; |
1217 | } |
1218 | |
1219 | auto proxy = this->asSurfaceProxy(); |
1220 | auto supportedRead = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(), |
1221 | proxy->backendFormat(), dstCT); |
1222 | // Fail if read color type does not have all of dstCT's color channels and those missing color |
1223 | // channels are in the src. |
1224 | uint32_t dstChannels = GrColorTypeChannelFlags(dstCT); |
1225 | uint32_t legalReadChannels = GrColorTypeChannelFlags(supportedRead.fColorType); |
1226 | uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType()); |
1227 | if ((~legalReadChannels & dstChannels) & srcChannels) { |
1228 | return {}; |
1229 | } |
1230 | |
1231 | if (!this->caps()->transferFromSurfaceToBufferSupport() || |
1232 | !supportedRead.fOffsetAlignmentForTransferBuffer) { |
1233 | return {}; |
1234 | } |
1235 | |
1236 | size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width(); |
1237 | size_t size = rowBytes * rect.height(); |
1238 | auto buffer = direct->priv().resourceProvider()->createBuffer( |
1239 | size, GrGpuBufferType::kXferGpuToCpu, GrAccessPattern::kStream_GrAccessPattern); |
1240 | if (!buffer) { |
1241 | return {}; |
1242 | } |
1243 | auto srcRect = rect; |
1244 | bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin; |
1245 | if (flip) { |
1246 | srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight, |
1247 | this->height() - rect.fTop); |
1248 | } |
1249 | this->drawingManager()->newTransferFromRenderTask(this->asSurfaceProxyRef(), srcRect, |
1250 | this->colorInfo().colorType(), |
1251 | supportedRead.fColorType, buffer, 0); |
1252 | PixelTransferResult result; |
1253 | result.fTransferBuffer = std::move(buffer); |
1254 | auto at = this->colorInfo().alphaType(); |
1255 | if (supportedRead.fColorType != dstCT || flip) { |
1256 | result.fPixelConverter = [w = rect.width(), h = rect.height(), dstCT, supportedRead, at]( |
1257 | void* dst, const void* src) { |
1258 | GrImageInfo srcInfo(supportedRead.fColorType, at, nullptr, w, h); |
1259 | GrImageInfo dstInfo(dstCT, at, nullptr, w, h); |
1260 | GrConvertPixels(dstInfo, dst, dstInfo.minRowBytes(), |
1261 | srcInfo, src, srcInfo.minRowBytes(), |
1262 | /* flipY = */ false); |
1263 | }; |
1264 | } |
1265 | return result; |
1266 | } |
1267 | |
1268 | #ifdef SK_DEBUG |
1269 | void GrSurfaceContext::validate() const { |
1270 | SkASSERT(fReadView.proxy()); |
1271 | fReadView.proxy()->validate(fContext); |
1272 | if (this->colorInfo().colorType() != GrColorType::kUnknown) { |
1273 | SkASSERT(fContext->priv().caps()->areColorTypeAndFormatCompatible( |
1274 | this->colorInfo().colorType(), fReadView.proxy()->backendFormat())); |
1275 | } |
1276 | this->onValidate(); |
1277 | } |
1278 | #endif |
1279 | |