1 /*
2 * Copyright 2012 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 <cstddef>
9 #include <cstring>
10 #include <type_traits>
11
12 #include "SkAutoPixmapStorage.h"
13 #include "GrBackendSurface.h"
14 #include "GrBackendTextureImageGenerator.h"
15 #include "GrAHardwareBufferImageGenerator.h"
16 #include "GrBitmapTextureMaker.h"
17 #include "GrCaps.h"
18 #include "GrContext.h"
19 #include "GrContextPriv.h"
20 #include "GrGpu.h"
21 #include "GrImageTextureMaker.h"
22 #include "GrRenderTargetContext.h"
23 #include "GrResourceProvider.h"
24 #include "GrSemaphore.h"
25 #include "GrTextureAdjuster.h"
26 #include "GrTexture.h"
27 #include "GrTextureProxy.h"
28 #include "effects/GrNonlinearColorSpaceXformEffect.h"
29 #include "effects/GrYUVEffect.h"
30 #include "SkCanvas.h"
31 #include "SkBitmapCache.h"
32 #include "SkGr.h"
33 #include "SkImage_Gpu.h"
34 #include "SkImageCacherator.h"
35 #include "SkImageInfoPriv.h"
36 #include "SkMipMap.h"
37 #include "SkPixelRef.h"
38 #include "SkReadPixelsRec.h"
39
SkImage_Gpu(GrContext * context,uint32_t uniqueID,SkAlphaType at,sk_sp<GrTextureProxy> proxy,sk_sp<SkColorSpace> colorSpace,SkBudgeted budgeted)40 SkImage_Gpu::SkImage_Gpu(GrContext* context, uint32_t uniqueID, SkAlphaType at,
41 sk_sp<GrTextureProxy> proxy,
42 sk_sp<SkColorSpace> colorSpace, SkBudgeted budgeted)
43 : INHERITED(proxy->width(), proxy->height(), uniqueID)
44 , fContext(context)
45 , fProxy(std::move(proxy))
46 , fAlphaType(at)
47 , fBudgeted(budgeted)
48 , fColorSpace(std::move(colorSpace))
49 , fAddedRasterVersionToCache(false) {
50 }
51
~SkImage_Gpu()52 SkImage_Gpu::~SkImage_Gpu() {
53 if (fAddedRasterVersionToCache.load()) {
54 SkNotifyBitmapGenIDIsStale(this->uniqueID());
55 }
56 }
57
onImageInfo() const58 SkImageInfo SkImage_Gpu::onImageInfo() const {
59 SkColorType ct;
60 if (!GrPixelConfigToColorType(fProxy->config(), &ct)) {
61 ct = kUnknown_SkColorType;
62 }
63 return SkImageInfo::Make(fProxy->width(), fProxy->height(), ct, fAlphaType, fColorSpace);
64 }
65
getROPixels(SkBitmap * dst,SkColorSpace *,CachingHint chint) const66 bool SkImage_Gpu::getROPixels(SkBitmap* dst, SkColorSpace*, CachingHint chint) const {
67 // The SkColorSpace parameter "dstColorSpace" is really just a hint about how/where the bitmap
68 // will be used. The client doesn't expect that we convert to that color space, it's intended
69 // for codec-backed images, to drive our decoding heuristic. In theory we *could* read directly
70 // into that color space (to save the client some effort in whatever they're about to do), but
71 // that would make our use of the bitmap cache incorrect (or much less efficient, assuming we
72 // rolled the dstColorSpace into the key).
73 const auto desc = SkBitmapCacheDesc::Make(this);
74 if (SkBitmapCache::Find(desc, dst)) {
75 SkASSERT(dst->getGenerationID() == this->uniqueID());
76 SkASSERT(dst->isImmutable());
77 SkASSERT(dst->getPixels());
78 return true;
79 }
80
81 SkBitmapCache::RecPtr rec = nullptr;
82 SkPixmap pmap;
83 if (kAllow_CachingHint == chint) {
84 rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
85 if (!rec) {
86 return false;
87 }
88 } else {
89 if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
90 return false;
91 }
92 }
93
94 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
95 fProxy,
96 fColorSpace);
97 if (!sContext) {
98 return false;
99 }
100
101 if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
102 return false;
103 }
104
105 if (rec) {
106 SkBitmapCache::Add(std::move(rec), dst);
107 fAddedRasterVersionToCache.store(true);
108 }
109 return true;
110 }
111
asTextureProxyRef(GrContext * context,const GrSamplerParams & params,SkColorSpace * dstColorSpace,sk_sp<SkColorSpace> * texColorSpace,SkScalar scaleAdjust[2]) const112 sk_sp<GrTextureProxy> SkImage_Gpu::asTextureProxyRef(GrContext* context,
113 const GrSamplerParams& params,
114 SkColorSpace* dstColorSpace,
115 sk_sp<SkColorSpace>* texColorSpace,
116 SkScalar scaleAdjust[2]) const {
117 if (context != fContext) {
118 SkASSERT(0);
119 return nullptr;
120 }
121
122 if (texColorSpace) {
123 *texColorSpace = this->fColorSpace;
124 }
125
126 GrTextureAdjuster adjuster(fContext, fProxy, this->alphaType(), this->bounds(),
127 this->uniqueID(), this->fColorSpace.get());
128 return adjuster.refTextureProxySafeForParams(params, nullptr, scaleAdjust);
129 }
130
apply_premul(const SkImageInfo & info,void * pixels,size_t rowBytes)131 static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
132 switch (info.colorType()) {
133 case kRGBA_8888_SkColorType:
134 case kBGRA_8888_SkColorType:
135 break;
136 default:
137 return; // nothing to do
138 }
139
140 // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
141 // and in either case, the alpha-byte is always in the same place, so we can safely call
142 // SkPreMultiplyColor()
143 //
144 SkColor* row = (SkColor*)pixels;
145 for (int y = 0; y < info.height(); ++y) {
146 for (int x = 0; x < info.width(); ++x) {
147 row[x] = SkPreMultiplyColor(row[x]);
148 }
149 }
150 }
151
onGetTextureHandle(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const152 GrBackendObject SkImage_Gpu::onGetTextureHandle(bool flushPendingGrContextIO,
153 GrSurfaceOrigin* origin) const {
154 SkASSERT(fProxy);
155
156 if (!fProxy->instantiate(fContext->resourceProvider())) {
157 return 0;
158 }
159
160 GrTexture* texture = fProxy->priv().peekTexture();
161
162 if (texture) {
163 if (flushPendingGrContextIO) {
164 fContext->contextPriv().prepareSurfaceForExternalIO(fProxy.get());
165 }
166 if (origin) {
167 *origin = fProxy->origin();
168 }
169 return texture->getTextureHandle();
170 }
171 return 0;
172 }
173
onGetTexture() const174 GrTexture* SkImage_Gpu::onGetTexture() const {
175 GrTextureProxy* proxy = this->peekProxy();
176 if (!proxy) {
177 return nullptr;
178 }
179
180 if (!proxy->instantiate(fContext->resourceProvider())) {
181 return nullptr;
182 }
183
184 return proxy->priv().peekTexture();
185 }
186
onReadPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const187 bool SkImage_Gpu::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
188 int srcX, int srcY, CachingHint) const {
189 if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
190 return false;
191 }
192
193 SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
194 if (!rec.trim(this->width(), this->height())) {
195 return false;
196 }
197
198 // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
199 // GrRenderTargetContext::onReadPixels
200 uint32_t flags = 0;
201 if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
202 // let the GPU perform this transformation for us
203 flags = GrContextPriv::kUnpremul_PixelOpsFlag;
204 }
205
206 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
207 fProxy,
208 fColorSpace);
209 if (!sContext) {
210 return false;
211 }
212
213 if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
214 return false;
215 }
216
217 // do we have to manually fix-up the alpha channel?
218 // src dst
219 // unpremul premul fix manually
220 // premul unpremul done by kUnpremul_PixelOpsFlag
221 // all other combos need to change.
222 //
223 // Should this be handled by Ganesh? todo:?
224 //
225 if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
226 apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
227 }
228 return true;
229 }
230
onMakeSubset(const SkIRect & subset) const231 sk_sp<SkImage> SkImage_Gpu::onMakeSubset(const SkIRect& subset) const {
232 GrSurfaceDesc desc;
233 desc.fConfig = fProxy->config();
234 desc.fWidth = subset.width();
235 desc.fHeight = subset.height();
236 desc.fOrigin = fProxy->origin();
237
238 sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
239 desc,
240 SkBackingFit::kExact,
241 fBudgeted));
242 if (!sContext) {
243 return nullptr;
244 }
245
246 if (!sContext->copy(fProxy.get(), subset, SkIPoint::Make(0, 0))) {
247 return nullptr;
248 }
249
250 // MDB: this call is okay bc we know 'sContext' was kExact
251 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
252 fAlphaType, sContext->asTextureProxyRef(),
253 fColorSpace, fBudgeted);
254 }
255
256 ///////////////////////////////////////////////////////////////////////////////////////////////////
257
new_wrapped_texture_common(GrContext * ctx,const GrBackendTexture & backendTex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,GrWrapOwnership ownership,SkImage::TextureReleaseProc releaseProc,SkImage::ReleaseContext releaseCtx)258 static sk_sp<SkImage> new_wrapped_texture_common(GrContext* ctx,
259 const GrBackendTexture& backendTex,
260 GrSurfaceOrigin origin,
261 SkAlphaType at, sk_sp<SkColorSpace> colorSpace,
262 GrWrapOwnership ownership,
263 SkImage::TextureReleaseProc releaseProc,
264 SkImage::ReleaseContext releaseCtx) {
265 if (backendTex.width() <= 0 || backendTex.height() <= 0) {
266 return nullptr;
267 }
268
269 GrBackendTextureFlags flags = kNone_GrBackendTextureFlag;
270 sk_sp<GrTexture> tex = ctx->resourceProvider()->wrapBackendTexture(backendTex,
271 origin,
272 flags,
273 0,
274 ownership);
275 if (!tex) {
276 return nullptr;
277 }
278 if (releaseProc) {
279 tex->setRelease(releaseProc, releaseCtx);
280 }
281
282 const SkBudgeted budgeted = SkBudgeted::kNo;
283 sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex)));
284 return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
285 at, std::move(proxy), std::move(colorSpace), budgeted);
286 }
287
MakeFromTexture(GrContext * ctx,const GrBackendTextureDesc & desc,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC)288 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
289 SkAlphaType at, sk_sp<SkColorSpace> cs,
290 TextureReleaseProc releaseP, ReleaseContext releaseC) {
291 SkASSERT(!(kRenderTarget_GrBackendTextureFlag & desc.fFlags));
292 GrBackendTexture tex(desc, ctx->contextPriv().getBackend());
293 return new_wrapped_texture_common(ctx, tex, desc.fOrigin, at, std::move(cs),
294 kBorrow_GrWrapOwnership,
295 releaseP, releaseC);
296 }
297
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTextureDesc & desc,SkAlphaType at,sk_sp<SkColorSpace> cs)298 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
299 SkAlphaType at, sk_sp<SkColorSpace> cs) {
300 SkASSERT(!(kRenderTarget_GrBackendTextureFlag & desc.fFlags));
301 GrBackendTexture tex(desc, ctx->contextPriv().getBackend());
302 return new_wrapped_texture_common(ctx, tex, desc.fOrigin, at, std::move(cs),
303 kAdopt_GrWrapOwnership,
304 nullptr, nullptr);
305 }
306
MakeFromTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC)307 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
308 const GrBackendTexture& tex, GrSurfaceOrigin origin,
309 SkAlphaType at, sk_sp<SkColorSpace> cs,
310 TextureReleaseProc releaseP, ReleaseContext releaseC) {
311 return new_wrapped_texture_common(ctx, tex, origin, at, std::move(cs), kBorrow_GrWrapOwnership,
312 releaseP, releaseC);
313 }
314
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> cs)315 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
316 const GrBackendTexture& tex, GrSurfaceOrigin origin,
317 SkAlphaType at, sk_sp<SkColorSpace> cs) {
318 return new_wrapped_texture_common(ctx, tex, origin, at, std::move(cs), kAdopt_GrWrapOwnership,
319 nullptr, nullptr);
320 }
321
make_backend_texture_from_handle(GrBackend backend,int width,int height,GrPixelConfig config,GrBackendObject handle)322 static GrBackendTexture make_backend_texture_from_handle(GrBackend backend,
323 int width, int height,
324 GrPixelConfig config,
325 GrBackendObject handle) {
326 switch (backend) {
327 case kOpenGL_GrBackend: {
328 const GrGLTextureInfo* glInfo = (const GrGLTextureInfo*)(handle);
329 return GrBackendTexture(width, height, config, *glInfo);
330 }
331 #ifdef SK_VULKAN
332 case kVulkan_GrBackend: {
333 const GrVkImageInfo* vkInfo = (const GrVkImageInfo*)(handle);
334 return GrBackendTexture(width, height, *vkInfo);
335 }
336 #endif
337 case kMock_GrBackend: {
338 const GrMockTextureInfo* mockInfo = (const GrMockTextureInfo*)(handle);
339 return GrBackendTexture(width, height, config, *mockInfo);
340 }
341 default:
342 return GrBackendTexture();
343 }
344 }
345
make_from_yuv_textures_copy(GrContext * ctx,SkYUVColorSpace colorSpace,bool nv12,const GrBackendObject yuvTextureHandles[],const SkISize yuvSizes[],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)346 static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace,
347 bool nv12,
348 const GrBackendObject yuvTextureHandles[],
349 const SkISize yuvSizes[],
350 GrSurfaceOrigin origin,
351 sk_sp<SkColorSpace> imageColorSpace) {
352 const SkBudgeted budgeted = SkBudgeted::kYes;
353
354 if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 ||
355 yuvSizes[1].fHeight <= 0) {
356 return nullptr;
357 }
358 if (!nv12 && (yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0)) {
359 return nullptr;
360 }
361
362 const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig;
363
364 GrBackend backend = ctx->contextPriv().getBackend();
365 GrBackendTexture yTex = make_backend_texture_from_handle(backend,
366 yuvSizes[0].fWidth,
367 yuvSizes[0].fHeight,
368 kConfig,
369 yuvTextureHandles[0]);
370 GrBackendTexture uTex = make_backend_texture_from_handle(backend,
371 yuvSizes[1].fWidth,
372 yuvSizes[1].fHeight,
373 kConfig,
374 yuvTextureHandles[1]);
375
376 sk_sp<GrTextureProxy> yProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, yTex, origin);
377 sk_sp<GrTextureProxy> uProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, uTex, origin);
378 sk_sp<GrTextureProxy> vProxy;
379
380 if (nv12) {
381 vProxy = uProxy;
382 } else {
383 GrBackendTexture vTex = make_backend_texture_from_handle(backend,
384 yuvSizes[2].fWidth,
385 yuvSizes[2].fHeight,
386 kConfig,
387 yuvTextureHandles[2]);
388 vProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, vTex, origin);
389 }
390 if (!yProxy || !uProxy || !vProxy) {
391 return nullptr;
392 }
393
394 const int width = yuvSizes[0].fWidth;
395 const int height = yuvSizes[0].fHeight;
396
397 // Needs to be a render target in order to draw to it for the yuv->rgb conversion.
398 sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext(
399 SkBackingFit::kExact,
400 width, height,
401 kRGBA_8888_GrPixelConfig,
402 std::move(imageColorSpace),
403 0,
404 origin));
405 if (!renderTargetContext) {
406 return nullptr;
407 }
408
409 GrPaint paint;
410 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
411 paint.addColorFragmentProcessor(GrYUVEffect::MakeYUVToRGB(yProxy, uProxy, vProxy,
412 yuvSizes, colorSpace, nv12));
413
414 const SkRect rect = SkRect::MakeIWH(width, height);
415
416 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
417
418 if (!renderTargetContext->asSurfaceProxy()) {
419 return nullptr;
420 }
421 ctx->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy());
422
423 // MDB: this call is okay bc we know 'renderTargetContext' was exact
424 return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
425 kOpaque_SkAlphaType, renderTargetContext->asTextureProxyRef(),
426 renderTargetContext->refColorSpace(), budgeted);
427 }
428
MakeFromYUVTexturesCopy(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendObject yuvTextureHandles[3],const SkISize yuvSizes[3],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)429 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
430 const GrBackendObject yuvTextureHandles[3],
431 const SkISize yuvSizes[3], GrSurfaceOrigin origin,
432 sk_sp<SkColorSpace> imageColorSpace) {
433 return make_from_yuv_textures_copy(ctx, colorSpace, false, yuvTextureHandles, yuvSizes, origin,
434 std::move(imageColorSpace));
435 }
436
MakeFromNV12TexturesCopy(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendObject yuvTextureHandles[2],const SkISize yuvSizes[2],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)437 sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
438 const GrBackendObject yuvTextureHandles[2],
439 const SkISize yuvSizes[2],
440 GrSurfaceOrigin origin,
441 sk_sp<SkColorSpace> imageColorSpace) {
442 return make_from_yuv_textures_copy(ctx, colorSpace, true, yuvTextureHandles, yuvSizes, origin,
443 std::move(imageColorSpace));
444 }
445
create_image_from_maker(GrContext * context,GrTextureMaker * maker,SkAlphaType at,uint32_t id,SkColorSpace * dstColorSpace)446 static sk_sp<SkImage> create_image_from_maker(GrContext* context, GrTextureMaker* maker,
447 SkAlphaType at, uint32_t id,
448 SkColorSpace* dstColorSpace) {
449 sk_sp<SkColorSpace> texColorSpace;
450 sk_sp<GrTextureProxy> proxy(maker->refTextureProxyForParams(GrSamplerParams::ClampNoFilter(),
451 dstColorSpace,
452 &texColorSpace, nullptr));
453 if (!proxy) {
454 return nullptr;
455 }
456 return sk_make_sp<SkImage_Gpu>(context, id, at,
457 std::move(proxy), std::move(texColorSpace), SkBudgeted::kNo);
458 }
459
makeTextureImage(GrContext * context,SkColorSpace * dstColorSpace) const460 sk_sp<SkImage> SkImage::makeTextureImage(GrContext* context, SkColorSpace* dstColorSpace) const {
461 if (!context) {
462 return nullptr;
463 }
464 if (GrContext* incumbent = as_IB(this)->context()) {
465 return incumbent == context ? sk_ref_sp(const_cast<SkImage*>(this)) : nullptr;
466 }
467
468 if (this->isLazyGenerated()) {
469 GrImageTextureMaker maker(context, this, kDisallow_CachingHint);
470 return create_image_from_maker(context, &maker, this->alphaType(),
471 this->uniqueID(), dstColorSpace);
472 }
473
474 if (const SkBitmap* bmp = as_IB(this)->onPeekBitmap()) {
475 GrBitmapTextureMaker maker(context, *bmp);
476 return create_image_from_maker(context, &maker, this->alphaType(),
477 this->uniqueID(), dstColorSpace);
478 }
479 return nullptr;
480 }
481
MakeCrossContextFromEncoded(GrContext * context,sk_sp<SkData> encoded,bool buildMips,SkColorSpace * dstColorSpace)482 sk_sp<SkImage> SkImage::MakeCrossContextFromEncoded(GrContext* context, sk_sp<SkData> encoded,
483 bool buildMips, SkColorSpace* dstColorSpace) {
484 sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(std::move(encoded));
485 if (!codecImage) {
486 return nullptr;
487 }
488
489 // Some backends or drivers don't support (safely) moving resources between contexts
490 if (!context || !context->caps()->crossContextTextureSupport()) {
491 return codecImage;
492 }
493
494 // Turn the codec image into a GrTextureProxy
495 GrImageTextureMaker maker(context, codecImage.get(), kDisallow_CachingHint);
496 sk_sp<SkColorSpace> texColorSpace;
497 GrSamplerParams params(SkShader::kClamp_TileMode,
498 buildMips ? GrSamplerParams::kMipMap_FilterMode
499 : GrSamplerParams::kBilerp_FilterMode);
500 sk_sp<GrTextureProxy> proxy(maker.refTextureProxyForParams(params, dstColorSpace,
501 &texColorSpace, nullptr));
502 if (!proxy) {
503 return codecImage;
504 }
505
506 if (!proxy->instantiate(context->resourceProvider())) {
507 return codecImage;
508 }
509 sk_sp<GrTexture> texture = sk_ref_sp(proxy->priv().peekTexture());
510
511 // Flush any writes or uploads
512 context->contextPriv().prepareSurfaceForExternalIO(proxy.get());
513
514 sk_sp<GrSemaphore> sema = context->getGpu()->prepareTextureForCrossContextUsage(texture.get());
515
516 auto gen = GrBackendTextureImageGenerator::Make(std::move(texture), std::move(sema),
517 codecImage->alphaType(),
518 std::move(texColorSpace));
519 return SkImage::MakeFromGenerator(std::move(gen));
520 }
521
522 #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
MakeFromAHardwareBuffer(AHardwareBuffer * graphicBuffer,SkAlphaType at,sk_sp<SkColorSpace> cs)523 sk_sp<SkImage> SkImage::MakeFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at,
524 sk_sp<SkColorSpace> cs) {
525 auto gen = GrAHardwareBufferImageGenerator::Make(graphicBuffer, at, cs);
526 return SkImage::MakeFromGenerator(std::move(gen));
527 }
528 #endif
529
makeNonTextureImage() const530 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
531 if (!this->isTextureBacked()) {
532 return sk_ref_sp(const_cast<SkImage*>(this));
533 }
534 SkImageInfo info = as_IB(this)->onImageInfo();
535 size_t rowBytes = info.minRowBytes();
536 size_t size = info.getSafeSize(rowBytes);
537 auto data = SkData::MakeUninitialized(size);
538 if (!data) {
539 return nullptr;
540 }
541 SkPixmap pm(info, data->writable_data(), rowBytes);
542 if (!this->readPixels(pm, 0, 0, kDisallow_CachingHint)) {
543 return nullptr;
544 }
545 return MakeRasterData(info, data, rowBytes);
546 }
547
548 ///////////////////////////////////////////////////////////////////////////////////////////////////
549
550 namespace {
551 struct MipMapLevelData {
552 void* fPixelData;
553 size_t fRowBytes;
554 };
555
556 struct DeferredTextureImage {
557 uint32_t fContextUniqueID;
558 // Right now, the destination color mode is only considered when generating mipmaps
559 SkDestinationSurfaceColorMode fColorMode;
560 // We don't store a SkImageInfo because it contains a ref-counted SkColorSpace.
561 int fWidth;
562 int fHeight;
563 SkColorType fColorType;
564 SkAlphaType fAlphaType;
565 void* fColorSpace;
566 size_t fColorSpaceSize;
567 int fMipMapLevelCount;
568 // The fMipMapLevelData array may contain more than 1 element.
569 // It contains fMipMapLevelCount elements.
570 // That means this struct's size is not known at compile-time.
571 MipMapLevelData fMipMapLevelData[1];
572 };
573 } // anonymous namespace
574
should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param)575 static bool should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param) {
576 // There is a bug in the mipmap pre-generation logic in use in getDeferredTextureImageData.
577 // This can cause runaway memory leaks, so we are disabling this path until we can
578 // investigate further. crbug.com/669775
579 return false;
580 }
581
582 namespace {
583
584 class DTIBufferFiller
585 {
586 public:
DTIBufferFiller(char * bufferAsCharPtr)587 explicit DTIBufferFiller(char* bufferAsCharPtr)
588 : bufferAsCharPtr_(bufferAsCharPtr) {}
589
fillMember(const void * source,size_t memberOffset,size_t size)590 void fillMember(const void* source, size_t memberOffset, size_t size) {
591 memcpy(bufferAsCharPtr_ + memberOffset, source, size);
592 }
593
594 private:
595
596 char* bufferAsCharPtr_;
597 };
598 }
599
600 #define FILL_MEMBER(bufferFiller, member, source) \
601 bufferFiller.fillMember(source, \
602 offsetof(DeferredTextureImage, member), \
603 sizeof(DeferredTextureImage::member));
604
SupportsColorSpace(SkColorType colorType)605 static bool SupportsColorSpace(SkColorType colorType) {
606 switch (colorType) {
607 case kRGBA_8888_SkColorType:
608 case kBGRA_8888_SkColorType:
609 case kRGBA_F16_SkColorType:
610 return true;
611 default:
612 return false;
613 }
614 }
615
getDeferredTextureImageData(const GrContextThreadSafeProxy & proxy,const DeferredTextureImageUsageParams params[],int paramCnt,void * buffer,SkColorSpace * dstColorSpace,SkColorType dstColorType) const616 size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
617 const DeferredTextureImageUsageParams params[],
618 int paramCnt, void* buffer,
619 SkColorSpace* dstColorSpace,
620 SkColorType dstColorType) const {
621 // Some quick-rejects where is makes no sense to return CPU data
622 // e.g.
623 // - texture backed
624 // - picture backed
625 //
626 if (this->isTextureBacked()) {
627 return 0;
628 }
629 if (as_IB(this)->onCanLazyGenerateOnGPU()) {
630 return 0;
631 }
632
633 bool supportsColorSpace = SupportsColorSpace(dstColorType);
634 // Quick reject if the caller requests a color space with an unsupported color type.
635 if (SkToBool(dstColorSpace) && !supportsColorSpace) {
636 return 0;
637 }
638
639 // Extract relevant min/max values from the params array.
640 int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
641 SkFilterQuality highestFilterQuality = params[0].fQuality;
642 bool useMipMaps = should_use_mip_maps(params[0]);
643 for (int i = 1; i < paramCnt; ++i) {
644 if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel)
645 lowestPreScaleMipLevel = params[i].fPreScaleMipLevel;
646 if (highestFilterQuality < params[i].fQuality)
647 highestFilterQuality = params[i].fQuality;
648 useMipMaps |= should_use_mip_maps(params[i]);
649 }
650
651 const bool fillMode = SkToBool(buffer);
652 if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) {
653 return 0;
654 }
655
656 // Calculate scaling parameters.
657 bool isScaled = lowestPreScaleMipLevel != 0;
658
659 SkISize scaledSize;
660 if (isScaled) {
661 // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the
662 // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level.
663 scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(),
664 lowestPreScaleMipLevel - 1);
665 } else {
666 scaledSize = SkISize::Make(this->width(), this->height());
667 }
668
669 // We never want to scale at higher than SW medium quality, as SW medium matches GPU high.
670 SkFilterQuality scaleFilterQuality = highestFilterQuality;
671 if (scaleFilterQuality > kMedium_SkFilterQuality) {
672 scaleFilterQuality = kMedium_SkFilterQuality;
673 }
674
675 const int maxTextureSize = proxy.fCaps->maxTextureSize();
676 if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
677 return 0;
678 }
679
680 SkAutoPixmapStorage pixmap;
681 SkImageInfo info;
682 size_t pixelSize = 0;
683 if (!isScaled && this->peekPixels(&pixmap) && pixmap.info().colorType() == dstColorType) {
684 info = pixmap.info();
685 pixelSize = SkAlign8(pixmap.getSafeSize());
686 if (!dstColorSpace) {
687 pixmap.setColorSpace(nullptr);
688 info = info.makeColorSpace(nullptr);
689 }
690 } else {
691 if (!this->isLazyGenerated() && !this->peekPixels(nullptr)) {
692 return 0;
693 }
694 if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
695 // Generator backed image. Tweak info to trigger correct kind of decode.
696 SkImageCacherator::CachedFormat cacheFormat = cacher->chooseCacheFormat(
697 dstColorSpace, proxy.fCaps.get());
698 info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(),
699 scaledSize.height());
700 } else {
701 info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
702 if (!dstColorSpace) {
703 info = info.makeColorSpace(nullptr);
704 }
705 }
706 // Force color type to be the requested type.
707 info = info.makeColorType(dstColorType);
708 pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
709 if (fillMode) {
710 // Always decode to N32 and convert to the requested type if necessary.
711 SkImageInfo decodeInfo = info.makeColorType(kN32_SkColorType);
712 SkAutoPixmapStorage decodePixmap;
713 decodePixmap.alloc(decodeInfo);
714
715 if (isScaled) {
716 if (!this->scalePixels(decodePixmap, scaleFilterQuality,
717 SkImage::kDisallow_CachingHint)) {
718 return 0;
719 }
720 } else {
721 if (!this->readPixels(decodePixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
722 return 0;
723 }
724 }
725
726 if (decodeInfo.colorType() != info.colorType()) {
727 pixmap.alloc(info);
728 // Convert and copy the decoded pixmap to the target pixmap.
729 decodePixmap.readPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes(), 0,
730 0);
731 } else {
732 pixmap = std::move(decodePixmap);
733 }
734 }
735 }
736 int mipMapLevelCount = 1;
737 if (useMipMaps) {
738 // SkMipMap only deals with the mipmap levels it generates, which does
739 // not include the base level.
740 // That means it generates and holds levels 1-x instead of 0-x.
741 // So the total mipmap level count is 1 more than what
742 // SkMipMap::ComputeLevelCount returns.
743 mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1;
744
745 // We already initialized pixelSize to the size of the base level.
746 // SkMipMap will generate the extra mipmap levels. Their sizes need to
747 // be added to the total.
748 // Index 0 here does not refer to the base mipmap level -- it is
749 // SkMipMap's first generated mipmap level (level 1).
750 for (int currentMipMapLevelIndex = mipMapLevelCount - 2; currentMipMapLevelIndex >= 0;
751 currentMipMapLevelIndex--) {
752 SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(),
753 currentMipMapLevelIndex);
754 SkImageInfo mipInfo = info.makeWH(mipSize.fWidth, mipSize.fHeight);
755 pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr));
756 }
757 }
758 size_t size = 0;
759 size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage));
760 size += dtiSize;
761 size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData);
762 // We subtract 1 because DeferredTextureImage already includes the base
763 // level in its size
764 size_t pixelOffset = size;
765 size += pixelSize;
766 size_t colorSpaceOffset = 0;
767 size_t colorSpaceSize = 0;
768 SkColorSpaceTransferFn fn;
769 if (info.colorSpace()) {
770 SkASSERT(dstColorSpace);
771 SkASSERT(supportsColorSpace);
772 colorSpaceOffset = size;
773 colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
774 size += colorSpaceSize;
775 } else if (supportsColorSpace && this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
776 // In legacy mode, preserve the color space tag on the SkImage. This is only
777 // supported if the color space has a parametric transfer function.
778 SkASSERT(!dstColorSpace);
779 colorSpaceOffset = size;
780 colorSpaceSize = this->colorSpace()->writeToMemory(nullptr);
781 size += colorSpaceSize;
782 }
783 if (!fillMode) {
784 return size;
785 }
786 char* bufferAsCharPtr = reinterpret_cast<char*>(buffer);
787 char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset;
788 void* pixels = pixelsAsCharPtr;
789
790 memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))),
791 pixmap.addr(), pixmap.getSafeSize());
792
793 // If the context has sRGB support, and we're intending to render to a surface with an attached
794 // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB)
795 // aware mip-mapping.
796 SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
797 if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) &&
798 info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) {
799 SkASSERT(supportsColorSpace);
800 colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
801 }
802
803 SkASSERT(info == pixmap.info());
804 size_t rowBytes = pixmap.rowBytes();
805 static_assert(std::is_standard_layout<DeferredTextureImage>::value,
806 "offsetof, which we use below, requires the type have standard layout");
807 auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr};
808 FILL_MEMBER(dtiBufferFiller, fColorMode, &colorMode);
809 FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID);
810 int width = info.width();
811 FILL_MEMBER(dtiBufferFiller, fWidth, &width);
812 int height = info.height();
813 FILL_MEMBER(dtiBufferFiller, fHeight, &height);
814 SkColorType colorType = info.colorType();
815 FILL_MEMBER(dtiBufferFiller, fColorType, &colorType);
816 SkAlphaType alphaType = info.alphaType();
817 FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType);
818 FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount);
819 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData),
820 &pixels, sizeof(pixels));
821 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes),
822 &rowBytes, sizeof(rowBytes));
823 if (colorSpaceSize) {
824 void* colorSpace = bufferAsCharPtr + colorSpaceOffset;
825 FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace);
826 FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize);
827 if (info.colorSpace()) {
828 info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
829 } else {
830 SkASSERT(this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn));
831 SkASSERT(!dstColorSpace);
832 this->colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
833 }
834 } else {
835 memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace),
836 0, sizeof(DeferredTextureImage::fColorSpace));
837 memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize),
838 0, sizeof(DeferredTextureImage::fColorSpaceSize));
839 }
840
841 // Fill in the mipmap levels if they exist
842 char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.getSafeSize());
843
844 if (useMipMaps) {
845 static_assert(std::is_standard_layout<MipMapLevelData>::value,
846 "offsetof, which we use below, requires the type have a standard layout");
847
848 std::unique_ptr<SkMipMap> mipmaps(SkMipMap::Build(pixmap, colorMode, nullptr));
849 // SkMipMap holds only the mipmap levels it generates.
850 // A programmer can use the data they provided to SkMipMap::Build as level 0.
851 // So the SkMipMap provides levels 1-x but it stores them in its own
852 // range 0-(x-1).
853 for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1;
854 generatedMipLevelIndex++) {
855 SkMipMap::Level mipLevel;
856 mipmaps->getLevel(generatedMipLevelIndex, &mipLevel);
857
858 // Make sure the mipmap data is after the start of the buffer
859 SkASSERT(mipLevelPtr > bufferAsCharPtr);
860 // Make sure the mipmap data starts before the end of the buffer
861 SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize);
862 // Make sure the mipmap data ends before the end of the buffer
863 SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <=
864 bufferAsCharPtr + pixelOffset + pixelSize);
865
866 // getSafeSize includes rowbyte padding except for the last row,
867 // right?
868
869 memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.getSafeSize());
870
871 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) +
872 sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
873 offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*));
874 size_t rowBytes = mipLevel.fPixmap.rowBytes();
875 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) +
876 sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
877 offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes));
878
879 mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize());
880 }
881 }
882 return size;
883 }
884
MakeFromDeferredTextureImageData(GrContext * context,const void * data,SkBudgeted budgeted)885 sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void* data,
886 SkBudgeted budgeted) {
887 if (!data) {
888 return nullptr;
889 }
890 const DeferredTextureImage* dti = reinterpret_cast<const DeferredTextureImage*>(data);
891
892 if (!context || context->uniqueID() != dti->fContextUniqueID || context->abandoned()) {
893 return nullptr;
894 }
895 int mipLevelCount = dti->fMipMapLevelCount;
896 SkASSERT(mipLevelCount >= 1);
897 sk_sp<SkColorSpace> colorSpace;
898 if (dti->fColorSpaceSize) {
899 colorSpace = SkColorSpace::Deserialize(dti->fColorSpace, dti->fColorSpaceSize);
900 }
901 SkImageInfo info = SkImageInfo::Make(dti->fWidth, dti->fHeight,
902 dti->fColorType, dti->fAlphaType, colorSpace);
903 if (mipLevelCount == 1) {
904 SkPixmap pixmap;
905 pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, dti->fMipMapLevelData[0].fRowBytes);
906
907 // Pass nullptr for the |dstColorSpace|. This opts in to more lenient color space
908 // verification. This is ok because we've already verified the color space in
909 // getDeferredTextureImageData().
910 sk_sp<GrTextureProxy> proxy(GrUploadPixmapToTextureProxy(
911 context->resourceProvider(), pixmap, budgeted, nullptr));
912 if (!proxy) {
913 return nullptr;
914 }
915 return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, pixmap.alphaType(),
916 std::move(proxy), std::move(colorSpace), budgeted);
917 } else {
918 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
919 for (int i = 0; i < mipLevelCount; i++) {
920 texels[i].fPixels = dti->fMipMapLevelData[i].fPixelData;
921 texels[i].fRowBytes = dti->fMipMapLevelData[i].fRowBytes;
922 }
923
924 return SkImage::MakeTextureFromMipMap(context, info, texels.get(),
925 mipLevelCount, SkBudgeted::kYes,
926 dti->fColorMode);
927 }
928 }
929
930 ///////////////////////////////////////////////////////////////////////////////////////////////////
931
MakeTextureFromMipMap(GrContext * ctx,const SkImageInfo & info,const GrMipLevel texels[],int mipLevelCount,SkBudgeted budgeted,SkDestinationSurfaceColorMode colorMode)932 sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& info,
933 const GrMipLevel texels[], int mipLevelCount,
934 SkBudgeted budgeted,
935 SkDestinationSurfaceColorMode colorMode) {
936 SkASSERT(mipLevelCount >= 1);
937 if (!ctx) {
938 return nullptr;
939 }
940 sk_sp<GrTextureProxy> proxy(GrUploadMipMapToTextureProxy(ctx, info, texels, mipLevelCount,
941 colorMode));
942 if (!proxy) {
943 return nullptr;
944 }
945
946 SkASSERT(proxy->priv().isExact());
947 return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
948 info.alphaType(), std::move(proxy),
949 info.refColorSpace(), budgeted);
950 }
951
onMakeColorSpace(sk_sp<SkColorSpace> target,SkColorType,SkTransferFunctionBehavior premulBehavior) const952 sk_sp<SkImage> SkImage_Gpu::onMakeColorSpace(sk_sp<SkColorSpace> target, SkColorType,
953 SkTransferFunctionBehavior premulBehavior) const {
954 if (SkTransferFunctionBehavior::kRespect == premulBehavior) {
955 // TODO: Implement this.
956 return nullptr;
957 }
958
959 sk_sp<SkColorSpace> srcSpace = fColorSpace;
960 if (!fColorSpace) {
961 if (target->isSRGB()) {
962 return sk_ref_sp(const_cast<SkImage*>((SkImage*)this));
963 }
964
965 srcSpace = SkColorSpace::MakeSRGB();
966 }
967
968 auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), target.get());
969 if (!xform) {
970 return sk_ref_sp(const_cast<SkImage_Gpu*>(this));
971 }
972
973 sk_sp<GrRenderTargetContext> renderTargetContext(fContext->makeDeferredRenderTargetContext(
974 SkBackingFit::kExact, this->width(), this->height(), kRGBA_8888_GrPixelConfig, nullptr));
975 if (!renderTargetContext) {
976 return nullptr;
977 }
978
979 GrPaint paint;
980 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
981 paint.addColorTextureProcessor(fProxy, nullptr, SkMatrix::I());
982 paint.addColorFragmentProcessor(std::move(xform));
983
984 const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
985
986 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
987
988 if (!renderTargetContext->asTextureProxy()) {
989 return nullptr;
990 }
991
992 // MDB: this call is okay bc we know 'renderTargetContext' was exact
993 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
994 fAlphaType, renderTargetContext->asTextureProxyRef(),
995 std::move(target), fBudgeted);
996
997 }
998
onIsValid(GrContext * context) const999 bool SkImage_Gpu::onIsValid(GrContext* context) const {
1000 // The base class has already checked that context isn't abandoned (if it's not nullptr)
1001 if (fContext->abandoned()) {
1002 return false;
1003 }
1004
1005 if (context && context != fContext) {
1006 return false;
1007 }
1008
1009 return true;
1010 }
1011