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 "include/private/GrRecordingContext.h" |
11 | #include "src/core/SkAutoPixmapStorage.h" |
12 | #include "src/gpu/GrAuditTrail.h" |
13 | #include "src/gpu/GrClip.h" |
14 | #include "src/gpu/GrContextPriv.h" |
15 | #include "src/gpu/GrDataUtils.h" |
16 | #include "src/gpu/GrDrawingManager.h" |
17 | #include "src/gpu/GrGpu.h" |
18 | #include "src/gpu/GrImageInfo.h" |
19 | #include "src/gpu/GrProxyProvider.h" |
20 | #include "src/gpu/GrRecordingContextPriv.h" |
21 | #include "src/gpu/GrRenderTargetContext.h" |
22 | #include "src/gpu/GrSurfaceContextPriv.h" |
23 | #include "src/gpu/GrSurfacePriv.h" |
24 | #include "src/gpu/SkGr.h" |
25 | #include "src/gpu/effects/GrBicubicEffect.h" |
26 | |
27 | #define ASSERT_SINGLE_OWNER \ |
28 | SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(this->singleOwner());) |
29 | #define RETURN_FALSE_IF_ABANDONED if (this->fContext->priv().abandoned()) { return false; } |
30 | |
31 | std::unique_ptr<GrSurfaceContext> GrSurfaceContext::Make(GrRecordingContext* context, |
32 | GrSurfaceProxyView readView, |
33 | GrColorType colorType, |
34 | SkAlphaType alphaType, |
35 | sk_sp<SkColorSpace> colorSpace) { |
36 | // It is probably not necessary to check if the context is abandoned here since uses of the |
37 | // GrSurfaceContext which need the context will mostly likely fail later on without an issue. |
38 | // However having this hear adds some reassurance in case there is a path doesn't handle an |
39 | // abandoned context correctly. It also lets us early out of some extra work. |
40 | if (context->priv().abandoned()) { |
41 | return nullptr; |
42 | } |
43 | GrSurfaceProxy* proxy = readView.proxy(); |
44 | SkASSERT(proxy && proxy->asTextureProxy()); |
45 | |
46 | std::unique_ptr<GrSurfaceContext> surfaceContext; |
47 | if (proxy->asRenderTargetProxy()) { |
48 | SkASSERT(kPremul_SkAlphaType == alphaType || kOpaque_SkAlphaType == alphaType); |
49 | // Will we ever want a swizzle that is not the default write swizzle for the format and |
50 | // colorType here? If so we will need to manually pass that in. |
51 | GrSwizzle writeSwizzle; |
52 | if (colorType != GrColorType::kUnknown) { |
53 | writeSwizzle = |
54 | context->priv().caps()->getWriteSwizzle(proxy->backendFormat(), colorType); |
55 | } |
56 | GrSurfaceProxyView writeView(readView.refProxy(), readView.origin(), writeSwizzle); |
57 | surfaceContext.reset(new GrRenderTargetContext(context, std::move(readView), |
58 | std::move(writeView), colorType, |
59 | std::move(colorSpace), nullptr)); |
60 | } else { |
61 | surfaceContext.reset(new GrSurfaceContext(context, std::move(readView), colorType, |
62 | alphaType, std::move(colorSpace))); |
63 | } |
64 | SkDEBUGCODE(surfaceContext->validate();) |
65 | return surfaceContext; |
66 | } |
67 | |
68 | std::unique_ptr<GrSurfaceContext> GrSurfaceContext::Make(GrRecordingContext* context, |
69 | SkISize dimensions, |
70 | const GrBackendFormat& format, |
71 | GrRenderable renderable, |
72 | int renderTargetSampleCnt, |
73 | GrMipMapped mipMapped, |
74 | GrProtected isProtected, |
75 | GrSurfaceOrigin origin, |
76 | GrColorType colorType, |
77 | SkAlphaType alphaType, |
78 | sk_sp<SkColorSpace> colorSpace, |
79 | SkBackingFit fit, |
80 | SkBudgeted budgeted) { |
81 | GrSwizzle swizzle; |
82 | if (colorType != GrColorType::kUnknown && !context->priv().caps()->isFormatCompressed(format)) { |
83 | swizzle = context->priv().caps()->getReadSwizzle(format, colorType); |
84 | } |
85 | |
86 | sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy( |
87 | format, dimensions, renderable, renderTargetSampleCnt, mipMapped, fit, budgeted, |
88 | isProtected); |
89 | if (!proxy) { |
90 | return nullptr; |
91 | } |
92 | |
93 | GrSurfaceProxyView view(std::move(proxy), origin, swizzle); |
94 | return GrSurfaceContext::Make(context, std::move(view), colorType, alphaType, |
95 | std::move(colorSpace)); |
96 | } |
97 | |
98 | // In MDB mode the reffing of the 'getLastOpsTask' call's result allows in-progress |
99 | // GrOpsTasks to be picked up and added to by renderTargetContexts lower in the call |
100 | // stack. When this occurs with a closed GrOpsTask, a new one will be allocated |
101 | // when the renderTargetContext attempts to use it (via getOpsTask). |
102 | GrSurfaceContext::GrSurfaceContext(GrRecordingContext* context, |
103 | GrSurfaceProxyView readView, |
104 | GrColorType colorType, |
105 | SkAlphaType alphaType, |
106 | sk_sp<SkColorSpace> colorSpace) |
107 | : fContext(context) |
108 | , fReadView(std::move(readView)) |
109 | , fColorInfo(colorType, alphaType, std::move(colorSpace)) { |
110 | SkASSERT(!context->priv().abandoned()); |
111 | } |
112 | |
113 | const GrCaps* GrSurfaceContext::caps() const { return fContext->priv().caps(); } |
114 | |
115 | GrAuditTrail* GrSurfaceContext::auditTrail() { |
116 | return fContext->priv().auditTrail(); |
117 | } |
118 | |
119 | GrDrawingManager* GrSurfaceContext::drawingManager() { |
120 | return fContext->priv().drawingManager(); |
121 | } |
122 | |
123 | const GrDrawingManager* GrSurfaceContext::drawingManager() const { |
124 | return fContext->priv().drawingManager(); |
125 | } |
126 | |
127 | #ifdef SK_DEBUG |
128 | GrSingleOwner* GrSurfaceContext::singleOwner() { |
129 | return fContext->priv().singleOwner(); |
130 | } |
131 | #endif |
132 | |
133 | bool GrSurfaceContext::readPixels(const GrImageInfo& origDstInfo, void* dst, size_t rowBytes, |
134 | SkIPoint pt, GrContext* direct) { |
135 | ASSERT_SINGLE_OWNER |
136 | RETURN_FALSE_IF_ABANDONED |
137 | SkDEBUGCODE(this->validate();) |
138 | GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContext::readPixels" ); |
139 | |
140 | if (!direct && !(direct = fContext->priv().asDirectContext())) { |
141 | return false; |
142 | } |
143 | |
144 | if (!dst) { |
145 | return false; |
146 | } |
147 | |
148 | size_t tightRowBytes = origDstInfo.minRowBytes(); |
149 | if (!rowBytes) { |
150 | rowBytes = tightRowBytes; |
151 | } else if (rowBytes < tightRowBytes) { |
152 | return false; |
153 | } |
154 | |
155 | if (!origDstInfo.isValid()) { |
156 | return false; |
157 | } |
158 | |
159 | GrSurfaceProxy* srcProxy = this->asSurfaceProxy(); |
160 | |
161 | if (srcProxy->framebufferOnly()) { |
162 | return false; |
163 | } |
164 | |
165 | // MDB TODO: delay this instantiation until later in the method |
166 | if (!srcProxy->instantiate(direct->priv().resourceProvider())) { |
167 | return false; |
168 | } |
169 | |
170 | GrSurface* srcSurface = srcProxy->peekSurface(); |
171 | |
172 | auto dstInfo = origDstInfo; |
173 | if (!dstInfo.clip(this->width(), this->height(), &pt, &dst, rowBytes)) { |
174 | return false; |
175 | } |
176 | // Our tight row bytes may have been changed by clipping. |
177 | tightRowBytes = dstInfo.minRowBytes(); |
178 | |
179 | SkColorSpaceXformSteps::Flags flags = SkColorSpaceXformSteps{this->colorInfo(), dstInfo}.flags; |
180 | bool unpremul = flags.unpremul, |
181 | needColorConversion = flags.linearize || flags.gamut_transform || flags.encode, |
182 | premul = flags.premul; |
183 | |
184 | const GrCaps* caps = direct->priv().caps(); |
185 | bool srcIsCompressed = caps->isFormatCompressed(srcSurface->backendFormat()); |
186 | // This is the getImageData equivalent to the canvas2D putImageData fast path. We probably don't |
187 | // care so much about getImageData performance. However, in order to ensure putImageData/ |
188 | // getImageData in "legacy" mode are round-trippable we use the GPU to do the complementary |
189 | // unpremul step to writeSurfacePixels's premul step (which is determined empirically in |
190 | // fContext->vaildaPMUPMConversionExists()). |
191 | GrBackendFormat defaultRGBAFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, |
192 | GrRenderable::kYes); |
193 | GrColorType srcColorType = this->colorInfo().colorType(); |
194 | bool canvas2DFastPath = unpremul && !needColorConversion && |
195 | (GrColorType::kRGBA_8888 == dstInfo.colorType() || |
196 | GrColorType::kBGRA_8888 == dstInfo.colorType()) && |
197 | SkToBool(srcProxy->asTextureProxy()) && |
198 | (srcColorType == GrColorType::kRGBA_8888 || |
199 | srcColorType == GrColorType::kBGRA_8888) && |
200 | defaultRGBAFormat.isValid() && |
201 | direct->priv().validPMUPMConversionExists(); |
202 | |
203 | auto readFlag = caps->surfaceSupportsReadPixels(srcSurface); |
204 | if (readFlag == GrCaps::SurfaceReadPixelsSupport::kUnsupported) { |
205 | return false; |
206 | } |
207 | |
208 | if (readFlag == GrCaps::SurfaceReadPixelsSupport::kCopyToTexture2D || canvas2DFastPath) { |
209 | GrColorType colorType = (canvas2DFastPath || srcIsCompressed) |
210 | ? GrColorType::kRGBA_8888 : this->colorInfo().colorType(); |
211 | sk_sp<SkColorSpace> cs = canvas2DFastPath ? nullptr : this->colorInfo().refColorSpace(); |
212 | |
213 | auto tempCtx = GrRenderTargetContext::Make( |
214 | direct, colorType, std::move(cs), SkBackingFit::kApprox, dstInfo.dimensions(), |
215 | 1, GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
216 | if (!tempCtx) { |
217 | return false; |
218 | } |
219 | |
220 | std::unique_ptr<GrFragmentProcessor> fp; |
221 | if (canvas2DFastPath) { |
222 | fp = direct->priv().createPMToUPMEffect( |
223 | GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType())); |
224 | if (dstInfo.colorType() == GrColorType::kBGRA_8888) { |
225 | fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::BGRA()); |
226 | dstInfo = dstInfo.makeColorType(GrColorType::kRGBA_8888); |
227 | } |
228 | // The render target context is incorrectly tagged as kPremul even though we're writing |
229 | // unpremul data thanks to the PMToUPM effect. Fake out the dst alpha type so we don't |
230 | // double unpremul. |
231 | dstInfo = dstInfo.makeAlphaType(kPremul_SkAlphaType); |
232 | } else { |
233 | fp = GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType()); |
234 | } |
235 | if (!fp) { |
236 | return false; |
237 | } |
238 | GrPaint paint; |
239 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
240 | paint.addColorFragmentProcessor(std::move(fp)); |
241 | |
242 | tempCtx->asRenderTargetContext()->fillRectToRect( |
243 | GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), |
244 | SkRect::MakeWH(dstInfo.width(), dstInfo.height()), |
245 | SkRect::MakeXYWH(pt.fX, pt.fY, dstInfo.width(), dstInfo.height())); |
246 | |
247 | return tempCtx->readPixels(dstInfo, dst, rowBytes, {0, 0}, direct); |
248 | } |
249 | |
250 | bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin; |
251 | |
252 | auto supportedRead = caps->supportedReadPixelsColorType( |
253 | this->colorInfo().colorType(), srcProxy->backendFormat(), dstInfo.colorType()); |
254 | |
255 | bool makeTight = !caps->readPixelsRowBytesSupport() && tightRowBytes != rowBytes; |
256 | |
257 | bool convert = unpremul || premul || needColorConversion || flip || makeTight || |
258 | (dstInfo.colorType() != supportedRead.fColorType); |
259 | |
260 | std::unique_ptr<char[]> tmpPixels; |
261 | GrImageInfo tmpInfo; |
262 | void* readDst = dst; |
263 | size_t readRB = rowBytes; |
264 | if (convert) { |
265 | tmpInfo = {supportedRead.fColorType, this->colorInfo().alphaType(), |
266 | this->colorInfo().refColorSpace(), dstInfo.width(), dstInfo.height()}; |
267 | size_t tmpRB = tmpInfo.minRowBytes(); |
268 | size_t size = tmpRB * tmpInfo.height(); |
269 | // Chrome MSAN bots require the data to be initialized (hence the ()). |
270 | tmpPixels.reset(new char[size]()); |
271 | |
272 | readDst = tmpPixels.get(); |
273 | readRB = tmpRB; |
274 | pt.fY = flip ? srcSurface->height() - pt.fY - dstInfo.height() : pt.fY; |
275 | } |
276 | |
277 | direct->priv().flushSurface(srcProxy); |
278 | |
279 | if (!direct->priv().getGpu()->readPixels(srcSurface, pt.fX, pt.fY, dstInfo.width(), |
280 | dstInfo.height(), this->colorInfo().colorType(), |
281 | supportedRead.fColorType, readDst, readRB)) { |
282 | return false; |
283 | } |
284 | |
285 | if (convert) { |
286 | return GrConvertPixels(dstInfo, dst, rowBytes, tmpInfo, readDst, readRB, flip); |
287 | } |
288 | return true; |
289 | } |
290 | |
291 | bool GrSurfaceContext::writePixels(const GrImageInfo& origSrcInfo, const void* src, size_t rowBytes, |
292 | SkIPoint pt, GrContext* direct) { |
293 | ASSERT_SINGLE_OWNER |
294 | RETURN_FALSE_IF_ABANDONED |
295 | SkDEBUGCODE(this->validate();) |
296 | GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContext::writePixels" ); |
297 | |
298 | if (!direct && !(direct = fContext->priv().asDirectContext())) { |
299 | return false; |
300 | } |
301 | |
302 | if (this->asSurfaceProxy()->readOnly()) { |
303 | return false; |
304 | } |
305 | |
306 | if (!src) { |
307 | return false; |
308 | } |
309 | |
310 | size_t tightRowBytes = origSrcInfo.minRowBytes(); |
311 | if (!rowBytes) { |
312 | rowBytes = tightRowBytes; |
313 | } else if (rowBytes < tightRowBytes) { |
314 | return false; |
315 | } |
316 | |
317 | if (!origSrcInfo.isValid()) { |
318 | return false; |
319 | } |
320 | |
321 | GrSurfaceProxy* dstProxy = this->asSurfaceProxy(); |
322 | |
323 | if (dstProxy->framebufferOnly()) { |
324 | return false; |
325 | } |
326 | |
327 | if (!dstProxy->instantiate(direct->priv().resourceProvider())) { |
328 | return false; |
329 | } |
330 | |
331 | GrSurface* dstSurface = dstProxy->peekSurface(); |
332 | |
333 | auto srcInfo = origSrcInfo; |
334 | if (!srcInfo.clip(this->width(), this->height(), &pt, &src, rowBytes)) { |
335 | return false; |
336 | } |
337 | // Our tight row bytes may have been changed by clipping. |
338 | tightRowBytes = srcInfo.minRowBytes(); |
339 | |
340 | SkColorSpaceXformSteps::Flags flags = SkColorSpaceXformSteps{srcInfo, this->colorInfo()}.flags; |
341 | bool unpremul = flags.unpremul, |
342 | needColorConversion = flags.linearize || flags.gamut_transform || flags.encode, |
343 | premul = flags.premul; |
344 | |
345 | const GrCaps* caps = direct->priv().caps(); |
346 | |
347 | auto rgbaDefaultFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888, |
348 | GrRenderable::kNo); |
349 | |
350 | GrColorType dstColorType = this->colorInfo().colorType(); |
351 | // For canvas2D putImageData performance we have a special code path for unpremul RGBA_8888 srcs |
352 | // that are premultiplied on the GPU. This is kept as narrow as possible for now. |
353 | bool canvas2DFastPath = !caps->avoidWritePixelsFastPath() && premul && !needColorConversion && |
354 | (srcInfo.colorType() == GrColorType::kRGBA_8888 || |
355 | srcInfo.colorType() == GrColorType::kBGRA_8888) && |
356 | SkToBool(this->asRenderTargetContext()) && |
357 | (dstColorType == GrColorType::kRGBA_8888 || |
358 | dstColorType == GrColorType::kBGRA_8888) && |
359 | rgbaDefaultFormat.isValid() && |
360 | direct->priv().validPMUPMConversionExists(); |
361 | |
362 | if (!caps->surfaceSupportsWritePixels(dstSurface) || canvas2DFastPath) { |
363 | GrColorType colorType; |
364 | |
365 | GrBackendFormat format; |
366 | SkAlphaType alphaType; |
367 | GrSwizzle tempReadSwizzle; |
368 | if (canvas2DFastPath) { |
369 | colorType = GrColorType::kRGBA_8888; |
370 | format = rgbaDefaultFormat; |
371 | alphaType = kUnpremul_SkAlphaType; |
372 | } else { |
373 | colorType = this->colorInfo().colorType(); |
374 | format = dstProxy->backendFormat().makeTexture2D(); |
375 | if (!format.isValid()) { |
376 | return false; |
377 | } |
378 | alphaType = this->colorInfo().alphaType(); |
379 | tempReadSwizzle = this->readSwizzle(); |
380 | } |
381 | |
382 | // It is more efficient for us to write pixels into a top left origin so we prefer that. |
383 | // However, if the final proxy isn't a render target then we must use a copy to move the |
384 | // data into it which requires the origins to match. If the final proxy is a render target |
385 | // we can use a draw instead which doesn't have this origin restriction. Thus for render |
386 | // targets we will use top left and otherwise we will make the origins match. |
387 | GrSurfaceOrigin tempOrigin = |
388 | this->asRenderTargetContext() ? kTopLeft_GrSurfaceOrigin : this->origin(); |
389 | auto tempProxy = direct->priv().proxyProvider()->createProxy( |
390 | format, srcInfo.dimensions(), GrRenderable::kNo, 1, GrMipMapped::kNo, |
391 | SkBackingFit::kApprox, SkBudgeted::kYes, GrProtected::kNo); |
392 | if (!tempProxy) { |
393 | return false; |
394 | } |
395 | GrSurfaceProxyView tempView(tempProxy, tempOrigin, tempReadSwizzle); |
396 | GrSurfaceContext tempCtx(direct, tempView, colorType, alphaType, |
397 | this->colorInfo().refColorSpace()); |
398 | |
399 | // In the fast path we always write the srcData to the temp context as though it were RGBA. |
400 | // When the data is really BGRA the write will cause the R and B channels to be swapped in |
401 | // the intermediate surface which gets corrected by a swizzle effect when drawing to the |
402 | // dst. |
403 | if (canvas2DFastPath) { |
404 | srcInfo = srcInfo.makeColorType(GrColorType::kRGBA_8888); |
405 | } |
406 | if (!tempCtx.writePixels(srcInfo, src, rowBytes, {0, 0}, direct)) { |
407 | return false; |
408 | } |
409 | |
410 | if (this->asRenderTargetContext()) { |
411 | std::unique_ptr<GrFragmentProcessor> fp; |
412 | if (canvas2DFastPath) { |
413 | fp = direct->priv().createUPMToPMEffect( |
414 | GrTextureEffect::Make(std::move(tempView), alphaType)); |
415 | // Important: check the original src color type here! |
416 | if (origSrcInfo.colorType() == GrColorType::kBGRA_8888) { |
417 | fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::BGRA()); |
418 | } |
419 | } else { |
420 | fp = GrTextureEffect::Make(std::move(tempView), alphaType); |
421 | } |
422 | if (!fp) { |
423 | return false; |
424 | } |
425 | GrPaint paint; |
426 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
427 | paint.addColorFragmentProcessor(std::move(fp)); |
428 | this->asRenderTargetContext()->fillRectToRect( |
429 | GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), |
430 | SkRect::MakeXYWH(pt.fX, pt.fY, srcInfo.width(), srcInfo.height()), |
431 | SkRect::MakeWH(srcInfo.width(), srcInfo.height())); |
432 | } else { |
433 | SkIRect srcRect = SkIRect::MakeWH(srcInfo.width(), srcInfo.height()); |
434 | SkIPoint dstPoint = SkIPoint::Make(pt.fX, pt.fY); |
435 | if (!this->copy(tempProxy.get(), srcRect, dstPoint)) { |
436 | return false; |
437 | } |
438 | } |
439 | return true; |
440 | } |
441 | |
442 | GrColorType allowedColorType = |
443 | caps->supportedWritePixelsColorType(this->colorInfo().colorType(), |
444 | dstProxy->backendFormat(), |
445 | srcInfo.colorType()).fColorType; |
446 | bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin; |
447 | bool makeTight = !caps->writePixelsRowBytesSupport() && rowBytes != tightRowBytes; |
448 | bool convert = premul || unpremul || needColorConversion || makeTight || |
449 | (srcInfo.colorType() != allowedColorType) || flip; |
450 | |
451 | std::unique_ptr<char[]> tmpPixels; |
452 | GrColorType srcColorType = srcInfo.colorType(); |
453 | if (convert) { |
454 | GrImageInfo tmpInfo(allowedColorType, this->colorInfo().alphaType(), |
455 | this->colorInfo().refColorSpace(), srcInfo.width(), srcInfo.height()); |
456 | auto tmpRB = tmpInfo.minRowBytes(); |
457 | tmpPixels.reset(new char[tmpRB * tmpInfo.height()]); |
458 | |
459 | GrConvertPixels(tmpInfo, tmpPixels.get(), tmpRB, srcInfo, src, rowBytes, flip); |
460 | |
461 | srcColorType = tmpInfo.colorType(); |
462 | rowBytes = tmpRB; |
463 | src = tmpPixels.get(); |
464 | pt.fY = flip ? dstSurface->height() - pt.fY - tmpInfo.height() : pt.fY; |
465 | } |
466 | |
467 | // On platforms that prefer flushes over VRAM use (i.e., ANGLE) we're better off forcing a |
468 | // complete flush here. On platforms that prefer VRAM use over flushes we're better off |
469 | // giving the drawing manager the chance of skipping the flush (i.e., by passing in the |
470 | // destination proxy) |
471 | // TODO: should this policy decision just be moved into the drawing manager? |
472 | direct->priv().flushSurface(caps->preferVRAMUseOverFlushes() ? dstProxy : nullptr); |
473 | |
474 | return direct->priv().getGpu()->writePixels(dstSurface, pt.fX, pt.fY, srcInfo.width(), |
475 | srcInfo.height(), this->colorInfo().colorType(), |
476 | srcColorType, src, rowBytes); |
477 | } |
478 | |
479 | bool GrSurfaceContext::copy(GrSurfaceProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { |
480 | ASSERT_SINGLE_OWNER |
481 | RETURN_FALSE_IF_ABANDONED |
482 | SkDEBUGCODE(this->validate();) |
483 | GR_AUDIT_TRAIL_AUTO_FRAME(this->auditTrail(), "GrSurfaceContextPriv::copy" ); |
484 | |
485 | const GrCaps* caps = fContext->priv().caps(); |
486 | |
487 | SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal); |
488 | SkASSERT(src->backendFormat() == this->asSurfaceProxy()->backendFormat()); |
489 | |
490 | if (this->asSurfaceProxy()->framebufferOnly()) { |
491 | return false; |
492 | } |
493 | |
494 | if (!caps->canCopySurface(this->asSurfaceProxy(), src, srcRect, dstPoint)) { |
495 | return false; |
496 | } |
497 | |
498 | // The swizzle doesn't matter for copies and it is not used. |
499 | return this->drawingManager()->newCopyRenderTask( |
500 | GrSurfaceProxyView(sk_ref_sp(src), this->origin(), GrSwizzle("rgba" )), srcRect, |
501 | this->readSurfaceView(), dstPoint); |
502 | } |
503 | |
504 | std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale( |
505 | const SkImageInfo& info, |
506 | const SkIRect& srcRect, |
507 | SkSurface::RescaleGamma rescaleGamma, |
508 | SkFilterQuality rescaleQuality) { |
509 | auto direct = fContext->priv().asDirectContext(); |
510 | if (!direct) { |
511 | return nullptr; |
512 | } |
513 | auto rtProxy = this->asRenderTargetProxy(); |
514 | if (rtProxy && rtProxy->wrapsVkSecondaryCB()) { |
515 | return nullptr; |
516 | } |
517 | |
518 | if (this->asSurfaceProxy()->framebufferOnly()) { |
519 | return nullptr; |
520 | } |
521 | |
522 | // We rescale by drawing and don't currently support drawing to a kUnpremul destination. |
523 | if (info.alphaType() == kUnpremul_SkAlphaType) { |
524 | return nullptr; |
525 | } |
526 | |
527 | int srcW = srcRect.width(); |
528 | int srcH = srcRect.height(); |
529 | int srcX = srcRect.fLeft; |
530 | int srcY = srcRect.fTop; |
531 | GrSurfaceProxyView texView = this->readSurfaceView(); |
532 | SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint; |
533 | SkAlphaType srcAlphaType = this->colorInfo().alphaType(); |
534 | if (!texView.asTextureProxy()) { |
535 | texView = GrSurfaceProxyView::Copy(fContext, std::move(texView), GrMipMapped::kNo, srcRect, |
536 | SkBackingFit::kApprox, SkBudgeted::kNo); |
537 | if (!texView) { |
538 | return nullptr; |
539 | } |
540 | SkASSERT(texView.asTextureProxy()); |
541 | srcX = 0; |
542 | srcY = 0; |
543 | constraint = SkCanvas::kFast_SrcRectConstraint; |
544 | } |
545 | |
546 | float sx = (float)info.width() / srcW; |
547 | float sy = (float)info.height() / srcH; |
548 | |
549 | // How many bilerp/bicubic steps to do in X and Y. + means upscaling, - means downscaling. |
550 | int stepsX; |
551 | int stepsY; |
552 | if (rescaleQuality > kNone_SkFilterQuality) { |
553 | stepsX = static_cast<int>((sx > 1.f) ? ceil(log2f(sx)) : floor(log2f(sx))); |
554 | stepsY = static_cast<int>((sy > 1.f) ? ceil(log2f(sy)) : floor(log2f(sy))); |
555 | } else { |
556 | stepsX = sx != 1.f; |
557 | stepsY = sy != 1.f; |
558 | } |
559 | SkASSERT(stepsX || stepsY); |
560 | |
561 | // Within a rescaling pass A is the input (if not null) and B is the output. At the end of the |
562 | // pass B is moved to A. If 'this' is the input on the first pass then tempA is null. |
563 | std::unique_ptr<GrRenderTargetContext> tempA; |
564 | std::unique_ptr<GrRenderTargetContext> tempB; |
565 | |
566 | // Assume we should ignore the rescale linear request if the surface has no color space since |
567 | // it's unclear how we'd linearize from an unknown color space. |
568 | if (rescaleGamma == SkSurface::kLinear && this->colorInfo().colorSpace() && |
569 | !this->colorInfo().colorSpace()->gammaIsLinear()) { |
570 | auto cs = this->colorInfo().colorSpace()->makeLinearGamma(); |
571 | auto xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(), srcAlphaType, cs.get(), |
572 | kPremul_SkAlphaType); |
573 | // We'll fall back to kRGBA_8888 if half float not supported. |
574 | auto linearRTC = GrRenderTargetContext::MakeWithFallback( |
575 | fContext, GrColorType::kRGBA_F16, cs, SkBackingFit::kExact, {srcW, srcH}, 1, |
576 | GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
577 | if (!linearRTC) { |
578 | return nullptr; |
579 | } |
580 | linearRTC->drawTexture(GrNoClip(), std::move(texView), srcAlphaType, |
581 | GrSamplerState::Filter::kNearest, SkBlendMode::kSrc, |
582 | SK_PMColor4fWHITE, SkRect::Make(srcRect), SkRect::MakeWH(srcW, srcH), |
583 | GrAA::kNo, GrQuadAAFlags::kNone, constraint, SkMatrix::I(), |
584 | std::move(xform)); |
585 | texView = linearRTC->readSurfaceView(); |
586 | SkASSERT(texView.asTextureProxy()); |
587 | tempA = std::move(linearRTC); |
588 | srcX = 0; |
589 | srcY = 0; |
590 | constraint = SkCanvas::kFast_SrcRectConstraint; |
591 | } |
592 | while (stepsX || stepsY) { |
593 | int nextW = info.width(); |
594 | int nextH = info.height(); |
595 | if (stepsX < 0) { |
596 | nextW = info.width() << (-stepsX - 1); |
597 | stepsX++; |
598 | } else if (stepsX != 0) { |
599 | if (stepsX > 1) { |
600 | nextW = srcW * 2; |
601 | } |
602 | --stepsX; |
603 | } |
604 | if (stepsY < 0) { |
605 | nextH = info.height() << (-stepsY - 1); |
606 | stepsY++; |
607 | } else if (stepsY != 0) { |
608 | if (stepsY > 1) { |
609 | nextH = srcH * 2; |
610 | } |
611 | --stepsY; |
612 | } |
613 | auto input = tempA ? tempA.get() : this; |
614 | GrColorType colorType = input->colorInfo().colorType(); |
615 | auto cs = input->colorInfo().refColorSpace(); |
616 | sk_sp<GrColorSpaceXform> xform; |
617 | auto prevAlphaType = input->colorInfo().alphaType(); |
618 | if (!stepsX && !stepsY) { |
619 | // Might as well fold conversion to final info in the last step. |
620 | cs = info.refColorSpace(); |
621 | colorType = SkColorTypeToGrColorType(info.colorType()); |
622 | xform = GrColorSpaceXform::Make(input->colorInfo().colorSpace(), |
623 | input->colorInfo().alphaType(), cs.get(), |
624 | info.alphaType()); |
625 | } |
626 | tempB = GrRenderTargetContext::MakeWithFallback( |
627 | fContext, colorType, std::move(cs), SkBackingFit::kExact, {nextW, nextH}, 1, |
628 | GrMipMapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin); |
629 | if (!tempB) { |
630 | return nullptr; |
631 | } |
632 | auto dstRect = SkRect::MakeWH(nextW, nextH); |
633 | if (rescaleQuality == kHigh_SkFilterQuality) { |
634 | SkMatrix matrix; |
635 | matrix.setScaleTranslate((float)srcW / nextW, (float)srcH / nextH, srcX, srcY); |
636 | std::unique_ptr<GrFragmentProcessor> fp; |
637 | auto dir = GrBicubicEffect::Direction::kXY; |
638 | if (nextW == srcW) { |
639 | dir = GrBicubicEffect::Direction::kY; |
640 | } else if (nextH == srcH) { |
641 | dir = GrBicubicEffect::Direction::kX; |
642 | } |
643 | static constexpr GrSamplerState::WrapMode kWM = GrSamplerState::WrapMode::kClamp; |
644 | auto subset = SkRect::MakeXYWH(srcX, srcY, srcW, srcH); |
645 | fp = GrBicubicEffect::MakeSubset(std::move(texView), prevAlphaType, matrix, kWM, kWM, |
646 | subset, dir, *this->caps()); |
647 | if (xform) { |
648 | fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform)); |
649 | } |
650 | GrPaint paint; |
651 | paint.addColorFragmentProcessor(std::move(fp)); |
652 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
653 | tempB->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, |
654 | dstRect); |
655 | } else { |
656 | auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest |
657 | : GrSamplerState::Filter::kBilerp; |
658 | auto srcSubset = SkRect::MakeXYWH(srcX, srcY, srcW, srcH); |
659 | tempB->drawTexture(GrNoClip(), std::move(texView), srcAlphaType, filter, |
660 | SkBlendMode::kSrc, SK_PMColor4fWHITE, srcSubset, dstRect, GrAA::kNo, |
661 | GrQuadAAFlags::kNone, constraint, SkMatrix::I(), std::move(xform)); |
662 | } |
663 | texView = tempB->readSurfaceView(); |
664 | tempA = std::move(tempB); |
665 | srcX = srcY = 0; |
666 | srcW = nextW; |
667 | srcH = nextH; |
668 | constraint = SkCanvas::kFast_SrcRectConstraint; |
669 | } |
670 | SkASSERT(tempA); |
671 | return tempA; |
672 | } |
673 | |
674 | GrSurfaceContext::PixelTransferResult GrSurfaceContext::transferPixels(GrColorType dstCT, |
675 | const SkIRect& rect) { |
676 | SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width()); |
677 | SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height()); |
678 | auto direct = fContext->priv().asDirectContext(); |
679 | if (!direct) { |
680 | return {}; |
681 | } |
682 | auto rtProxy = this->asRenderTargetProxy(); |
683 | if (rtProxy && rtProxy->wrapsVkSecondaryCB()) { |
684 | return {}; |
685 | } |
686 | |
687 | auto proxy = this->asSurfaceProxy(); |
688 | auto supportedRead = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(), |
689 | proxy->backendFormat(), dstCT); |
690 | // Fail if read color type does not have all of dstCT's color channels and those missing color |
691 | // channels are in the src. |
692 | uint32_t dstChannels = GrColorTypeChannelFlags(dstCT); |
693 | uint32_t legalReadChannels = GrColorTypeChannelFlags(supportedRead.fColorType); |
694 | uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType()); |
695 | if ((~legalReadChannels & dstChannels) & srcChannels) { |
696 | return {}; |
697 | } |
698 | |
699 | if (!this->caps()->transferFromSurfaceToBufferSupport() || |
700 | !supportedRead.fOffsetAlignmentForTransferBuffer) { |
701 | return {}; |
702 | } |
703 | |
704 | size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width(); |
705 | size_t size = rowBytes * rect.height(); |
706 | auto buffer = direct->priv().resourceProvider()->createBuffer( |
707 | size, GrGpuBufferType::kXferGpuToCpu, GrAccessPattern::kStream_GrAccessPattern); |
708 | if (!buffer) { |
709 | return {}; |
710 | } |
711 | auto srcRect = rect; |
712 | bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin; |
713 | if (flip) { |
714 | srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight, |
715 | this->height() - rect.fTop); |
716 | } |
717 | this->drawingManager()->newTransferFromRenderTask(this->asSurfaceProxyRef(), srcRect, |
718 | this->colorInfo().colorType(), |
719 | supportedRead.fColorType, buffer, 0); |
720 | PixelTransferResult result; |
721 | result.fTransferBuffer = std::move(buffer); |
722 | auto at = this->colorInfo().alphaType(); |
723 | if (supportedRead.fColorType != dstCT || flip) { |
724 | result.fPixelConverter = [w = rect.width(), h = rect.height(), dstCT, supportedRead, at]( |
725 | void* dst, const void* src) { |
726 | GrImageInfo srcInfo(supportedRead.fColorType, at, nullptr, w, h); |
727 | GrImageInfo dstInfo(dstCT, at, nullptr, w, h); |
728 | GrConvertPixels(dstInfo, dst, dstInfo.minRowBytes(), |
729 | srcInfo, src, srcInfo.minRowBytes(), |
730 | /* flipY = */ false); |
731 | }; |
732 | } |
733 | return result; |
734 | } |
735 | |
736 | #ifdef SK_DEBUG |
737 | void GrSurfaceContext::validate() const { |
738 | SkASSERT(fReadView.proxy()); |
739 | fReadView.proxy()->validate(fContext); |
740 | if (this->colorInfo().colorType() != GrColorType::kUnknown) { |
741 | SkASSERT(fContext->priv().caps()->areColorTypeAndFormatCompatible( |
742 | this->colorInfo().colorType(), fReadView.proxy()->backendFormat())); |
743 | } |
744 | this->onValidate(); |
745 | } |
746 | #endif |
747 | |
748 | |