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 "GrBitmapTextureMaker.h"
14 #include "GrCaps.h"
15 #include "GrContext.h"
16 #include "GrContextPriv.h"
17 #include "GrGpu.h"
18 #include "GrImageTextureMaker.h"
19 #include "GrRenderTargetContext.h"
20 #include "GrResourceProvider.h"
21 #include "GrSemaphore.h"
22 #include "GrTextureAdjuster.h"
23 #include "GrTexturePriv.h"
24 #include "GrTextureProxy.h"
25 #include "GrTextureToYUVPlanes.h"
26 #include "effects/GrNonlinearColorSpaceXformEffect.h"
27 #include "effects/GrYUVEffect.h"
28 #include "SkCanvas.h"
29 #include "SkCrossContextImageData.h"
30 #include "SkBitmapCache.h"
31 #include "SkGr.h"
32 #include "SkImage_Gpu.h"
33 #include "SkImageCacherator.h"
34 #include "SkImageInfoPriv.h"
35 #include "SkMipMap.h"
36 #include "SkPixelRef.h"
37 #include "SkReadPixelsRec.h"
38
SkImage_Gpu(GrContext * context,uint32_t uniqueID,SkAlphaType at,sk_sp<GrTextureProxy> proxy,sk_sp<SkColorSpace> colorSpace,SkBudgeted budgeted)39 SkImage_Gpu::SkImage_Gpu(GrContext* context, uint32_t uniqueID, SkAlphaType at,
40 sk_sp<GrTextureProxy> proxy,
41 sk_sp<SkColorSpace> colorSpace, SkBudgeted budgeted)
42 : INHERITED(proxy->width(), proxy->height(), uniqueID)
43 , fContext(context)
44 , fProxy(std::move(proxy))
45 , fAlphaType(at)
46 , fBudgeted(budgeted)
47 , fColorSpace(std::move(colorSpace))
48 , fAddedRasterVersionToCache(false) {
49 }
50
~SkImage_Gpu()51 SkImage_Gpu::~SkImage_Gpu() {
52 if (fAddedRasterVersionToCache.load()) {
53 SkNotifyBitmapGenIDIsStale(this->uniqueID());
54 }
55 }
56
onImageInfo() const57 SkImageInfo SkImage_Gpu::onImageInfo() const {
58 SkColorType ct;
59 if (!GrPixelConfigToColorType(fProxy->config(), &ct)) {
60 ct = kUnknown_SkColorType;
61 }
62 return SkImageInfo::Make(fProxy->width(), fProxy->height(), ct, fAlphaType, fColorSpace);
63 }
64
make_info(int w,int h,SkAlphaType at,sk_sp<SkColorSpace> colorSpace)65 static SkImageInfo make_info(int w, int h, SkAlphaType at, sk_sp<SkColorSpace> colorSpace) {
66 return SkImageInfo::MakeN32(w, h, at, std::move(colorSpace));
67 }
68
getROPixels(SkBitmap * dst,SkColorSpace * dstColorSpace,CachingHint chint) const69 bool SkImage_Gpu::getROPixels(SkBitmap* dst, SkColorSpace* dstColorSpace, CachingHint chint) const {
70 const auto desc = SkBitmapCacheDesc::Make(this);
71 if (SkBitmapCache::Find(desc, dst)) {
72 SkASSERT(dst->getGenerationID() == this->uniqueID());
73 SkASSERT(dst->isImmutable());
74 SkASSERT(dst->getPixels());
75 return true;
76 }
77
78 SkImageInfo ii = make_info(this->width(), this->height(), this->alphaType(),
79 sk_ref_sp(dstColorSpace));
80 if (!dst->tryAllocPixels(ii)) {
81 return false;
82 }
83
84 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
85 fProxy,
86 fColorSpace);
87 if (!sContext) {
88 return false;
89 }
90
91 if (!sContext->readPixels(dst->info(), dst->getPixels(), dst->rowBytes(), 0, 0)) {
92 return false;
93 }
94
95 dst->pixelRef()->setImmutableWithID(this->uniqueID());
96 if (kAllow_CachingHint == chint) {
97 SkBitmapCache::Add(desc, *dst);
98 fAddedRasterVersionToCache.store(true);
99 }
100 return true;
101 }
102
asTextureProxyRef(GrContext * context,const GrSamplerParams & params,SkColorSpace * dstColorSpace,sk_sp<SkColorSpace> * texColorSpace,SkScalar scaleAdjust[2]) const103 sk_sp<GrTextureProxy> SkImage_Gpu::asTextureProxyRef(GrContext* context,
104 const GrSamplerParams& params,
105 SkColorSpace* dstColorSpace,
106 sk_sp<SkColorSpace>* texColorSpace,
107 SkScalar scaleAdjust[2]) const {
108 if (context != fContext) {
109 SkASSERT(0);
110 return nullptr;
111 }
112
113 if (texColorSpace) {
114 *texColorSpace = this->fColorSpace;
115 }
116
117 GrTextureAdjuster adjuster(fContext, fProxy, this->alphaType(), this->bounds(),
118 this->uniqueID(), this->fColorSpace.get());
119 return adjuster.refTextureProxySafeForParams(params, nullptr, scaleAdjust);
120 }
121
apply_premul(const SkImageInfo & info,void * pixels,size_t rowBytes)122 static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
123 switch (info.colorType()) {
124 case kRGBA_8888_SkColorType:
125 case kBGRA_8888_SkColorType:
126 break;
127 default:
128 return; // nothing to do
129 }
130
131 // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
132 // and in either case, the alpha-byte is always in the same place, so we can safely call
133 // SkPreMultiplyColor()
134 //
135 SkColor* row = (SkColor*)pixels;
136 for (int y = 0; y < info.height(); ++y) {
137 for (int x = 0; x < info.width(); ++x) {
138 row[x] = SkPreMultiplyColor(row[x]);
139 }
140 }
141 }
142
onGetTextureHandle(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const143 GrBackendObject SkImage_Gpu::onGetTextureHandle(bool flushPendingGrContextIO,
144 GrSurfaceOrigin* origin) const {
145 GrTextureProxy* proxy = this->peekProxy();
146 SkASSERT(proxy);
147
148 GrSurface* surface = proxy->instantiate(fContext->resourceProvider());
149 if (surface && surface->asTexture()) {
150 if (flushPendingGrContextIO) {
151 fContext->prepareSurfaceForExternalIO(surface);
152 }
153 if (origin) {
154 *origin = surface->origin();
155 }
156 return surface->asTexture()->getTextureHandle();
157 }
158 return 0;
159 }
160
onGetTexture() const161 GrTexture* SkImage_Gpu::onGetTexture() const {
162 GrTextureProxy* proxy = this->peekProxy();
163 if (!proxy) {
164 return nullptr;
165 }
166
167 return proxy->instantiate(fContext->resourceProvider());
168 }
169
onReadYUV8Planes(const SkISize sizes[3],void * const planes[3],const size_t rowBytes[3],SkYUVColorSpace colorSpace) const170 bool SkImage_Gpu::onReadYUV8Planes(const SkISize sizes[3], void* const planes[3],
171 const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
172 if (GrTextureToYUVPlanes(fContext, fProxy, sizes, planes, rowBytes, colorSpace)) {
173 return true;
174 }
175
176 return INHERITED::onReadYUV8Planes(sizes, planes, rowBytes, colorSpace);
177 }
178
onReadPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const179 bool SkImage_Gpu::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
180 int srcX, int srcY, CachingHint) const {
181 if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
182 return false;
183 }
184
185 SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
186 if (!rec.trim(this->width(), this->height())) {
187 return false;
188 }
189
190 // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
191 // GrRenderTargetContext::onReadPixels
192 uint32_t flags = 0;
193 if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
194 // let the GPU perform this transformation for us
195 flags = GrContext::kUnpremul_PixelOpsFlag;
196 }
197
198 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
199 fProxy,
200 fColorSpace);
201 if (!sContext) {
202 return false;
203 }
204
205 if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
206 return false;
207 }
208
209 // do we have to manually fix-up the alpha channel?
210 // src dst
211 // unpremul premul fix manually
212 // premul unpremul done by kUnpremul_PixelOpsFlag
213 // all other combos need to change.
214 //
215 // Should this be handled by Ganesh? todo:?
216 //
217 if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
218 apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
219 }
220 return true;
221 }
222
onMakeSubset(const SkIRect & subset) const223 sk_sp<SkImage> SkImage_Gpu::onMakeSubset(const SkIRect& subset) const {
224 GrSurfaceDesc desc = fProxy->desc();
225 desc.fWidth = subset.width();
226 desc.fHeight = subset.height();
227
228 sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
229 desc,
230 SkBackingFit::kExact,
231 fBudgeted));
232 if (!sContext) {
233 return nullptr;
234 }
235
236 if (!sContext->copy(fProxy.get(), subset, SkIPoint::Make(0, 0))) {
237 return nullptr;
238 }
239
240 // MDB: this call is okay bc we know 'sContext' was kExact
241 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
242 fAlphaType, sContext->asTextureProxyRef(),
243 fColorSpace, fBudgeted);
244 }
245
246 ///////////////////////////////////////////////////////////////////////////////////////////////////
247
new_wrapped_texture_common(GrContext * ctx,const GrBackendTextureDesc & desc,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,GrWrapOwnership ownership,SkImage::TextureReleaseProc releaseProc,SkImage::ReleaseContext releaseCtx)248 static sk_sp<SkImage> new_wrapped_texture_common(GrContext* ctx, const GrBackendTextureDesc& desc,
249 SkAlphaType at, sk_sp<SkColorSpace> colorSpace,
250 GrWrapOwnership ownership,
251 SkImage::TextureReleaseProc releaseProc,
252 SkImage::ReleaseContext releaseCtx) {
253 if (desc.fWidth <= 0 || desc.fHeight <= 0) {
254 return nullptr;
255 }
256
257 sk_sp<GrTexture> tex = ctx->resourceProvider()->wrapBackendTexture(desc, ownership);
258 if (!tex) {
259 return nullptr;
260 }
261 if (releaseProc) {
262 tex->setRelease(releaseProc, releaseCtx);
263 }
264
265 const SkBudgeted budgeted = (kAdoptAndCache_GrWrapOwnership == ownership)
266 ? SkBudgeted::kYes : SkBudgeted::kNo;
267 sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeWrapped(std::move(tex)));
268 return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
269 at, std::move(proxy), std::move(colorSpace), budgeted);
270 }
271
MakeFromTexture(GrContext * ctx,const GrBackendTextureDesc & desc,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC)272 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
273 SkAlphaType at, sk_sp<SkColorSpace> cs,
274 TextureReleaseProc releaseP, ReleaseContext releaseC) {
275 return new_wrapped_texture_common(ctx, desc, at, std::move(cs), kBorrow_GrWrapOwnership,
276 releaseP, releaseC);
277 }
278
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTextureDesc & desc,SkAlphaType at,sk_sp<SkColorSpace> cs)279 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx, const GrBackendTextureDesc& desc,
280 SkAlphaType at, sk_sp<SkColorSpace> cs) {
281 return new_wrapped_texture_common(ctx, desc, at, std::move(cs), kAdopt_GrWrapOwnership,
282 nullptr, nullptr);
283 }
284
make_from_yuv_textures_copy(GrContext * ctx,SkYUVColorSpace colorSpace,bool nv12,const GrBackendObject yuvTextureHandles[],const SkISize yuvSizes[],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)285 static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace,
286 bool nv12,
287 const GrBackendObject yuvTextureHandles[],
288 const SkISize yuvSizes[],
289 GrSurfaceOrigin origin,
290 sk_sp<SkColorSpace> imageColorSpace) {
291 const SkBudgeted budgeted = SkBudgeted::kYes;
292
293 if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 ||
294 yuvSizes[1].fHeight <= 0) {
295 return nullptr;
296 }
297 if (!nv12 && (yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0)) {
298 return nullptr;
299 }
300
301 const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig;
302
303 GrBackendTextureDesc yDesc;
304 yDesc.fConfig = kConfig;
305 yDesc.fOrigin = origin;
306 yDesc.fSampleCnt = 0;
307 yDesc.fTextureHandle = yuvTextureHandles[0];
308 yDesc.fWidth = yuvSizes[0].fWidth;
309 yDesc.fHeight = yuvSizes[0].fHeight;
310
311 GrBackendTextureDesc uDesc;
312 uDesc.fConfig = kConfig;
313 uDesc.fOrigin = origin;
314 uDesc.fSampleCnt = 0;
315 uDesc.fTextureHandle = yuvTextureHandles[1];
316 uDesc.fWidth = yuvSizes[1].fWidth;
317 uDesc.fHeight = yuvSizes[1].fHeight;
318
319 sk_sp<GrSurfaceProxy> yProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, yDesc);
320 sk_sp<GrSurfaceProxy> uProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, uDesc);
321 sk_sp<GrSurfaceProxy> vProxy;
322
323 if (nv12) {
324 vProxy = uProxy;
325 } else {
326 GrBackendTextureDesc vDesc;
327 vDesc.fConfig = kConfig;
328 vDesc.fOrigin = origin;
329 vDesc.fSampleCnt = 0;
330 vDesc.fTextureHandle = yuvTextureHandles[2];
331 vDesc.fWidth = yuvSizes[2].fWidth;
332 vDesc.fHeight = yuvSizes[2].fHeight;
333
334 vProxy = GrSurfaceProxy::MakeWrappedBackend(ctx, vDesc);
335 }
336 if (!yProxy || !uProxy || !vProxy) {
337 return nullptr;
338 }
339
340 const int width = yuvSizes[0].fWidth;
341 const int height = yuvSizes[0].fHeight;
342
343 // Needs to be a render target in order to draw to it for the yuv->rgb conversion.
344 sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeRenderTargetContext(
345 SkBackingFit::kExact,
346 width, height,
347 kRGBA_8888_GrPixelConfig,
348 std::move(imageColorSpace),
349 0,
350 origin));
351 if (!renderTargetContext) {
352 return nullptr;
353 }
354
355 GrPaint paint;
356 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
357 paint.addColorFragmentProcessor(
358 GrYUVEffect::MakeYUVToRGB(ctx->resourceProvider(),
359 sk_ref_sp(yProxy->asTextureProxy()),
360 sk_ref_sp(uProxy->asTextureProxy()),
361 sk_ref_sp(vProxy->asTextureProxy()), yuvSizes, colorSpace, nv12));
362
363 const SkRect rect = SkRect::MakeIWH(width, height);
364
365 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
366
367 if (!renderTargetContext->accessRenderTarget()) {
368 return nullptr;
369 }
370 ctx->flushSurfaceWrites(renderTargetContext->accessRenderTarget());
371
372 // MDB: this call is okay bc we know 'renderTargetContext' was exact
373 return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
374 kOpaque_SkAlphaType, renderTargetContext->asTextureProxyRef(),
375 renderTargetContext->refColorSpace(), budgeted);
376 }
377
MakeFromYUVTexturesCopy(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendObject yuvTextureHandles[3],const SkISize yuvSizes[3],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)378 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
379 const GrBackendObject yuvTextureHandles[3],
380 const SkISize yuvSizes[3], GrSurfaceOrigin origin,
381 sk_sp<SkColorSpace> imageColorSpace) {
382 return make_from_yuv_textures_copy(ctx, colorSpace, false, yuvTextureHandles, yuvSizes, origin,
383 std::move(imageColorSpace));
384 }
385
MakeFromNV12TexturesCopy(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendObject yuvTextureHandles[2],const SkISize yuvSizes[2],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)386 sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
387 const GrBackendObject yuvTextureHandles[2],
388 const SkISize yuvSizes[2],
389 GrSurfaceOrigin origin,
390 sk_sp<SkColorSpace> imageColorSpace) {
391 return make_from_yuv_textures_copy(ctx, colorSpace, true, yuvTextureHandles, yuvSizes, origin,
392 std::move(imageColorSpace));
393 }
394
create_image_from_maker(GrContext * context,GrTextureMaker * maker,SkAlphaType at,uint32_t id,SkColorSpace * dstColorSpace)395 static sk_sp<SkImage> create_image_from_maker(GrContext* context, GrTextureMaker* maker,
396 SkAlphaType at, uint32_t id,
397 SkColorSpace* dstColorSpace) {
398 sk_sp<SkColorSpace> texColorSpace;
399 sk_sp<GrTextureProxy> proxy(maker->refTextureProxyForParams(GrSamplerParams::ClampNoFilter(),
400 dstColorSpace,
401 &texColorSpace, nullptr));
402 if (!proxy) {
403 return nullptr;
404 }
405 return sk_make_sp<SkImage_Gpu>(context, id, at,
406 std::move(proxy), std::move(texColorSpace), SkBudgeted::kNo);
407 }
408
makeTextureImage(GrContext * context,SkColorSpace * dstColorSpace) const409 sk_sp<SkImage> SkImage::makeTextureImage(GrContext* context, SkColorSpace* dstColorSpace) const {
410 if (!context) {
411 return nullptr;
412 }
413 if (GrTexture* peek = as_IB(this)->peekTexture()) {
414 return peek->getContext() == context ? sk_ref_sp(const_cast<SkImage*>(this)) : nullptr;
415 }
416
417 if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
418 GrImageTextureMaker maker(context, cacher, this, kDisallow_CachingHint);
419 return create_image_from_maker(context, &maker, this->alphaType(),
420 this->uniqueID(), dstColorSpace);
421 }
422
423 if (const SkBitmap* bmp = as_IB(this)->onPeekBitmap()) {
424 GrBitmapTextureMaker maker(context, *bmp);
425 return create_image_from_maker(context, &maker, this->alphaType(),
426 this->uniqueID(), dstColorSpace);
427 }
428 return nullptr;
429 }
430
MakeFromEncoded(GrContext * context,sk_sp<SkData> encoded,SkColorSpace * dstColorSpace)431 std::unique_ptr<SkCrossContextImageData> SkCrossContextImageData::MakeFromEncoded(
432 GrContext* context, sk_sp<SkData> encoded, SkColorSpace* dstColorSpace) {
433 sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(std::move(encoded));
434 if (!codecImage) {
435 return nullptr;
436 }
437
438 // Some backends or drivers don't support (safely) moving resources between contexts
439 if (!context->caps()->crossContextTextureSupport()) {
440 return std::unique_ptr<SkCrossContextImageData>(
441 new SkCrossContextImageData(std::move(codecImage)));
442 }
443
444 sk_sp<SkImage> textureImage = codecImage->makeTextureImage(context, dstColorSpace);
445 if (!textureImage) {
446 // TODO: Force decode to raster here? Do mip-mapping, like getDeferredTextureImageData?
447 return std::unique_ptr<SkCrossContextImageData>(
448 new SkCrossContextImageData(std::move(codecImage)));
449 }
450
451 // Crack open the gpu image, extract the backend data, stick it in the SkCCID
452 GrTexture* texture = as_IB(textureImage)->peekTexture();
453 SkASSERT(texture);
454
455 GrBackendTextureDesc desc;
456 desc.fFlags = kNone_GrBackendTextureFlag;
457 desc.fOrigin = texture->origin();
458 desc.fWidth = texture->width();
459 desc.fHeight = texture->height();
460 desc.fConfig = texture->config();
461 desc.fSampleCnt = 0;
462
463 auto textureData = texture->texturePriv().detachBackendTexture();
464 SkASSERT(textureData);
465
466 SkImageInfo info = as_IB(textureImage)->onImageInfo();
467 return std::unique_ptr<SkCrossContextImageData>(new SkCrossContextImageData(
468 desc, std::move(textureData), info.alphaType(), info.refColorSpace()));
469 }
470
MakeFromCrossContextImageData(GrContext * context,std::unique_ptr<SkCrossContextImageData> ccid)471 sk_sp<SkImage> SkImage::MakeFromCrossContextImageData(
472 GrContext* context, std::unique_ptr<SkCrossContextImageData> ccid) {
473 if (ccid->fImage) {
474 // No pre-existing GPU resource. We could upload it now (with makeTextureImage),
475 // but we'd need a dstColorSpace.
476 return ccid->fImage;
477 }
478
479 if (ccid->fTextureData) {
480 ccid->fTextureData->attachToContext(context);
481 }
482
483 // This texture was created by Ganesh on another thread (see MakeFromEncoded, above).
484 // Thus, we can import it back into our cache and treat it as our own (again).
485 GrWrapOwnership ownership = kAdoptAndCache_GrWrapOwnership;
486 return new_wrapped_texture_common(context, ccid->fDesc, ccid->fAlphaType,
487 std::move(ccid->fColorSpace), ownership, nullptr, nullptr);
488 }
489
makeNonTextureImage() const490 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
491 if (!this->isTextureBacked()) {
492 return sk_ref_sp(const_cast<SkImage*>(this));
493 }
494 SkImageInfo info = as_IB(this)->onImageInfo();
495 size_t rowBytes = info.minRowBytes();
496 size_t size = info.getSafeSize(rowBytes);
497 auto data = SkData::MakeUninitialized(size);
498 if (!data) {
499 return nullptr;
500 }
501 SkPixmap pm(info, data->writable_data(), rowBytes);
502 if (!this->readPixels(pm, 0, 0, kDisallow_CachingHint)) {
503 return nullptr;
504 }
505 return MakeRasterData(info, data, rowBytes);
506 }
507
508 ///////////////////////////////////////////////////////////////////////////////////////////////////
509
510 namespace {
511 struct MipMapLevelData {
512 void* fPixelData;
513 size_t fRowBytes;
514 };
515
516 struct DeferredTextureImage {
517 uint32_t fContextUniqueID;
518 // Right now, the destination color mode is only considered when generating mipmaps
519 SkDestinationSurfaceColorMode fColorMode;
520 // We don't store a SkImageInfo because it contains a ref-counted SkColorSpace.
521 int fWidth;
522 int fHeight;
523 SkColorType fColorType;
524 SkAlphaType fAlphaType;
525 void* fColorSpace;
526 size_t fColorSpaceSize;
527 int fMipMapLevelCount;
528 // The fMipMapLevelData array may contain more than 1 element.
529 // It contains fMipMapLevelCount elements.
530 // That means this struct's size is not known at compile-time.
531 MipMapLevelData fMipMapLevelData[1];
532 };
533 } // anonymous namespace
534
should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param)535 static bool should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param) {
536 // There is a bug in the mipmap pre-generation logic in use in getDeferredTextureImageData.
537 // This can cause runaway memory leaks, so we are disabling this path until we can
538 // investigate further. crbug.com/669775
539 return false;
540 }
541
542 namespace {
543
544 class DTIBufferFiller
545 {
546 public:
DTIBufferFiller(char * bufferAsCharPtr)547 explicit DTIBufferFiller(char* bufferAsCharPtr)
548 : bufferAsCharPtr_(bufferAsCharPtr) {}
549
fillMember(const void * source,size_t memberOffset,size_t size)550 void fillMember(const void* source, size_t memberOffset, size_t size) {
551 memcpy(bufferAsCharPtr_ + memberOffset, source, size);
552 }
553
554 private:
555
556 char* bufferAsCharPtr_;
557 };
558 }
559
560 #define FILL_MEMBER(bufferFiller, member, source) \
561 bufferFiller.fillMember(source, \
562 offsetof(DeferredTextureImage, member), \
563 sizeof(DeferredTextureImage::member));
564
getDeferredTextureImageData(const GrContextThreadSafeProxy & proxy,const DeferredTextureImageUsageParams params[],int paramCnt,void * buffer,SkColorSpace * dstColorSpace) const565 size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
566 const DeferredTextureImageUsageParams params[],
567 int paramCnt, void* buffer,
568 SkColorSpace* dstColorSpace) const {
569 // Extract relevant min/max values from the params array.
570 int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
571 SkFilterQuality highestFilterQuality = params[0].fQuality;
572 bool useMipMaps = should_use_mip_maps(params[0]);
573 for (int i = 1; i < paramCnt; ++i) {
574 if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel)
575 lowestPreScaleMipLevel = params[i].fPreScaleMipLevel;
576 if (highestFilterQuality < params[i].fQuality)
577 highestFilterQuality = params[i].fQuality;
578 useMipMaps |= should_use_mip_maps(params[i]);
579 }
580
581 const bool fillMode = SkToBool(buffer);
582 if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) {
583 return 0;
584 }
585
586 // Calculate scaling parameters.
587 bool isScaled = lowestPreScaleMipLevel != 0;
588
589 SkISize scaledSize;
590 if (isScaled) {
591 // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the
592 // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level.
593 scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(),
594 lowestPreScaleMipLevel - 1);
595 } else {
596 scaledSize = SkISize::Make(this->width(), this->height());
597 }
598
599 // We never want to scale at higher than SW medium quality, as SW medium matches GPU high.
600 SkFilterQuality scaleFilterQuality = highestFilterQuality;
601 if (scaleFilterQuality > kMedium_SkFilterQuality) {
602 scaleFilterQuality = kMedium_SkFilterQuality;
603 }
604
605 const int maxTextureSize = proxy.fCaps->maxTextureSize();
606 if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
607 return 0;
608 }
609
610 SkAutoPixmapStorage pixmap;
611 SkImageInfo info;
612 size_t pixelSize = 0;
613 if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable()) {
614 info = pixmap.info();
615 pixelSize = SkAlign8(pixmap.getSafeSize());
616 } else {
617 // Here we're just using presence of data to know whether there is a codec behind the image.
618 // In the future we will access the cacherator and get the exact data that we want to (e.g.
619 // yuv planes) upload.
620 sk_sp<SkData> data(this->refEncoded());
621 if (!data && !this->peekPixels(nullptr)) {
622 return 0;
623 }
624 if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
625 // Generator backed image. Tweak info to trigger correct kind of decode.
626 SkImageCacherator::CachedFormat cacheFormat = cacher->chooseCacheFormat(
627 dstColorSpace, proxy.fCaps.get());
628 info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(),
629 scaledSize.height());
630 } else {
631 info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
632 }
633 if (kIndex_8_SkColorType == info.colorType()) {
634 // Force Index8 to be N32 instead. Index8 is unsupported in Ganesh.
635 info = info.makeColorType(kN32_SkColorType);
636 }
637 pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
638 if (fillMode) {
639 pixmap.alloc(info);
640 if (isScaled) {
641 if (!this->scalePixels(pixmap, scaleFilterQuality,
642 SkImage::kDisallow_CachingHint)) {
643 return 0;
644 }
645 } else {
646 if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
647 return 0;
648 }
649 }
650 SkASSERT(!pixmap.ctable());
651 }
652 }
653 int mipMapLevelCount = 1;
654 if (useMipMaps) {
655 // SkMipMap only deals with the mipmap levels it generates, which does
656 // not include the base level.
657 // That means it generates and holds levels 1-x instead of 0-x.
658 // So the total mipmap level count is 1 more than what
659 // SkMipMap::ComputeLevelCount returns.
660 mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1;
661
662 // We already initialized pixelSize to the size of the base level.
663 // SkMipMap will generate the extra mipmap levels. Their sizes need to
664 // be added to the total.
665 // Index 0 here does not refer to the base mipmap level -- it is
666 // SkMipMap's first generated mipmap level (level 1).
667 for (int currentMipMapLevelIndex = mipMapLevelCount - 2; currentMipMapLevelIndex >= 0;
668 currentMipMapLevelIndex--) {
669 SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(),
670 currentMipMapLevelIndex);
671 SkImageInfo mipInfo = info.makeWH(mipSize.fWidth, mipSize.fHeight);
672 pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr));
673 }
674 }
675 size_t size = 0;
676 size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage));
677 size += dtiSize;
678 size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData);
679 // We subtract 1 because DeferredTextureImage already includes the base
680 // level in its size
681 size_t pixelOffset = size;
682 size += pixelSize;
683 size_t colorSpaceOffset = 0;
684 size_t colorSpaceSize = 0;
685 if (info.colorSpace()) {
686 colorSpaceOffset = size;
687 colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
688 size += colorSpaceSize;
689 }
690 if (!fillMode) {
691 return size;
692 }
693 char* bufferAsCharPtr = reinterpret_cast<char*>(buffer);
694 char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset;
695 void* pixels = pixelsAsCharPtr;
696
697 memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))),
698 pixmap.addr(), pixmap.getSafeSize());
699
700 // If the context has sRGB support, and we're intending to render to a surface with an attached
701 // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB)
702 // aware mip-mapping.
703 SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
704 if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) &&
705 info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) {
706 colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
707 }
708
709 SkASSERT(info == pixmap.info());
710 size_t rowBytes = pixmap.rowBytes();
711 static_assert(std::is_standard_layout<DeferredTextureImage>::value,
712 "offsetof, which we use below, requires the type have standard layout");
713 auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr};
714 FILL_MEMBER(dtiBufferFiller, fColorMode, &colorMode);
715 FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID);
716 int width = info.width();
717 FILL_MEMBER(dtiBufferFiller, fWidth, &width);
718 int height = info.height();
719 FILL_MEMBER(dtiBufferFiller, fHeight, &height);
720 SkColorType colorType = info.colorType();
721 FILL_MEMBER(dtiBufferFiller, fColorType, &colorType);
722 SkAlphaType alphaType = info.alphaType();
723 FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType);
724 FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount);
725 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData),
726 &pixels, sizeof(pixels));
727 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes),
728 &rowBytes, sizeof(rowBytes));
729 if (colorSpaceSize) {
730 void* colorSpace = bufferAsCharPtr + colorSpaceOffset;
731 FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace);
732 FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize);
733 info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
734 } else {
735 memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace),
736 0, sizeof(DeferredTextureImage::fColorSpace));
737 memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize),
738 0, sizeof(DeferredTextureImage::fColorSpaceSize));
739 }
740
741 // Fill in the mipmap levels if they exist
742 char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.getSafeSize());
743
744 if (useMipMaps) {
745 static_assert(std::is_standard_layout<MipMapLevelData>::value,
746 "offsetof, which we use below, requires the type have a standard layout");
747
748 std::unique_ptr<SkMipMap> mipmaps(SkMipMap::Build(pixmap, colorMode, nullptr));
749 // SkMipMap holds only the mipmap levels it generates.
750 // A programmer can use the data they provided to SkMipMap::Build as level 0.
751 // So the SkMipMap provides levels 1-x but it stores them in its own
752 // range 0-(x-1).
753 for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1;
754 generatedMipLevelIndex++) {
755 SkMipMap::Level mipLevel;
756 mipmaps->getLevel(generatedMipLevelIndex, &mipLevel);
757
758 // Make sure the mipmap data is after the start of the buffer
759 SkASSERT(mipLevelPtr > bufferAsCharPtr);
760 // Make sure the mipmap data starts before the end of the buffer
761 SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize);
762 // Make sure the mipmap data ends before the end of the buffer
763 SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <=
764 bufferAsCharPtr + pixelOffset + pixelSize);
765
766 // getSafeSize includes rowbyte padding except for the last row,
767 // right?
768
769 memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.getSafeSize());
770
771 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) +
772 sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
773 offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*));
774 size_t rowBytes = mipLevel.fPixmap.rowBytes();
775 memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) +
776 sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
777 offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes));
778
779 mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize());
780 }
781 }
782 return size;
783 }
784
MakeFromDeferredTextureImageData(GrContext * context,const void * data,SkBudgeted budgeted)785 sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void* data,
786 SkBudgeted budgeted) {
787 if (!data) {
788 return nullptr;
789 }
790 const DeferredTextureImage* dti = reinterpret_cast<const DeferredTextureImage*>(data);
791
792 if (!context || context->uniqueID() != dti->fContextUniqueID) {
793 return nullptr;
794 }
795 int mipLevelCount = dti->fMipMapLevelCount;
796 SkASSERT(mipLevelCount >= 1);
797 sk_sp<SkColorSpace> colorSpace;
798 if (dti->fColorSpaceSize) {
799 colorSpace = SkColorSpace::Deserialize(dti->fColorSpace, dti->fColorSpaceSize);
800 }
801 SkImageInfo info = SkImageInfo::Make(dti->fWidth, dti->fHeight,
802 dti->fColorType, dti->fAlphaType, colorSpace);
803 if (mipLevelCount == 1) {
804 SkPixmap pixmap;
805 pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, dti->fMipMapLevelData[0].fRowBytes);
806 sk_sp<GrTextureProxy> proxy(GrUploadPixmapToTextureProxy(context->resourceProvider(),
807 pixmap, budgeted));
808 if (!proxy) {
809 return nullptr;
810 }
811 return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, pixmap.alphaType(),
812 std::move(proxy), std::move(colorSpace), budgeted);
813 } else {
814 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
815 for (int i = 0; i < mipLevelCount; i++) {
816 texels[i].fPixels = dti->fMipMapLevelData[i].fPixelData;
817 texels[i].fRowBytes = dti->fMipMapLevelData[i].fRowBytes;
818 }
819
820 return SkImage::MakeTextureFromMipMap(context, info, texels.get(),
821 mipLevelCount, SkBudgeted::kYes,
822 dti->fColorMode);
823 }
824 }
825
826 ///////////////////////////////////////////////////////////////////////////////////////////////////
827
MakeTextureFromMipMap(GrContext * ctx,const SkImageInfo & info,const GrMipLevel * texels,int mipLevelCount,SkBudgeted budgeted,SkDestinationSurfaceColorMode colorMode)828 sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& info,
829 const GrMipLevel* texels, int mipLevelCount,
830 SkBudgeted budgeted,
831 SkDestinationSurfaceColorMode colorMode) {
832 if (!ctx) {
833 return nullptr;
834 }
835 sk_sp<GrTextureProxy> proxy(GrUploadMipMapToTextureProxy(ctx, info, texels, mipLevelCount,
836 colorMode));
837 if (!proxy) {
838 return nullptr;
839 }
840
841 SkASSERT(proxy->priv().isExact());
842 return sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
843 info.alphaType(), std::move(proxy),
844 info.refColorSpace(), budgeted);
845 }
846
onMakeColorSpace(sk_sp<SkColorSpace> colorSpace) const847 sk_sp<SkImage> SkImage_Gpu::onMakeColorSpace(sk_sp<SkColorSpace> colorSpace) const {
848 sk_sp<SkColorSpace> srcSpace = fColorSpace ? fColorSpace : SkColorSpace::MakeSRGB();
849 auto xform = GrNonlinearColorSpaceXformEffect::Make(srcSpace.get(), colorSpace.get());
850 if (!xform) {
851 return sk_ref_sp(const_cast<SkImage_Gpu*>(this));
852 }
853
854 sk_sp<GrRenderTargetContext> renderTargetContext(fContext->makeRenderTargetContext(
855 SkBackingFit::kExact, this->width(), this->height(), kRGBA_8888_GrPixelConfig, nullptr));
856 if (!renderTargetContext) {
857 return nullptr;
858 }
859
860 GrPaint paint;
861 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
862 paint.addColorTextureProcessor(fContext->resourceProvider(), fProxy, nullptr, SkMatrix::I());
863 paint.addColorFragmentProcessor(std::move(xform));
864
865 const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
866
867 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
868
869 if (!renderTargetContext->accessRenderTarget()) {
870 return nullptr;
871 }
872
873 // MDB: this call is okay bc we know 'renderTargetContext' was exact
874 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
875 fAlphaType, renderTargetContext->asTextureProxyRef(),
876 std::move(colorSpace), fBudgeted);
877
878 }
879