Flutter Engine
The Flutter Engine
SkImage_GaneshBase.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
17#include "include/core/SkRect.h"
18#include "include/core/SkSize.h"
24#include "include/gpu/GrTypes.h"
52
53#include <functional>
54#include <memory>
55#include <utility>
56
58class SkImageFilter;
59struct SkIPoint;
60
63 uint32_t uniqueID)
64 : SkImage_Base(std::move(info), uniqueID), fContext(std::move(context)) {}
65
66//////////////////////////////////////////////////////////////////////////////////////////////////
67
69 const GrBackendTexture& tex,
70 GrColorType grCT,
71 SkColorType ct,
72 SkAlphaType at,
74 if (!tex.isValid()) {
75 return false;
76 }
77 SkColorInfo info(ct, at, cs);
79 return false;
80 }
81 GrBackendFormat backendFormat = tex.getBackendFormat();
82 if (!backendFormat.isValid()) {
83 return false;
84 }
85
86 return caps->areColorTypeAndFormatCompatible(grCT, backendFormat);
87}
88
90 const GrBackendTexture& tex,
91 SkAlphaType at) {
92 if (!tex.isValid() || tex.width() <= 0 || tex.height() <= 0) {
93 return false;
94 }
95
96 if (tex.width() > caps->maxTextureSize() || tex.height() > caps->maxTextureSize()) {
97 return false;
98 }
99
100 if (at == kUnknown_SkAlphaType) {
101 return false;
102 }
103
104 GrBackendFormat backendFormat = tex.getBackendFormat();
105 if (!backendFormat.isValid()) {
106 return false;
107 }
108
109 if (!caps->isFormatCompressed(backendFormat)) {
110 return false;
111 }
112
113 return true;
114}
115
116//////////////////////////////////////////////////////////////////////////////////////////////////
117
119 SkBitmap* dst,
120 CachingHint chint) const {
121 if (!fContext->priv().matches(dContext)) {
122 return false;
123 }
124
125 const auto desc = SkBitmapCacheDesc::Make(this);
127 SkASSERT(dst->isImmutable());
128 SkASSERT(dst->getPixels());
129 return true;
130 }
131
132 SkBitmapCache::RecPtr rec = nullptr;
133 SkPixmap pmap;
134 if (kAllow_CachingHint == chint) {
135 rec = SkBitmapCache::Alloc(desc, this->imageInfo(), &pmap);
136 if (!rec) {
137 return false;
138 }
139 } else {
140 if (!dst->tryAllocPixels(this->imageInfo()) || !dst->peekPixels(&pmap)) {
141 return false;
142 }
143 }
144
145 auto [view, ct] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped::kNo);
146 if (!view) {
147 return false;
148 }
149
150 GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
151 auto sContext = dContext->priv().makeSC(std::move(view), std::move(colorInfo));
152 if (!sContext) {
153 return false;
154 }
155
156 if (!sContext->readPixels(dContext, pmap, {0, 0})) {
157 return false;
158 }
159
160 if (rec) {
161 SkBitmapCache::Add(std::move(rec), dst);
163 }
164 return true;
165}
166
168 const SkIRect& subset) const {
169 if (!fContext->priv().matches(direct)) {
170 return nullptr;
171 }
172
173 if (subset.isEmpty()) {
174 return nullptr;
175 }
176
177 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
178 if (!bounds.contains(subset)) {
179 return nullptr;
180 }
181
182 // optimization : return self if the subset == our bounds
183 if (bounds == subset) {
184 return sk_ref_sp(const_cast<SkImage_GaneshBase*>(this));
185 }
186
187 return this->onMakeSubset(direct, subset);
188}
189
191 const SkIRect& subset) const {
192 if (!fContext->priv().matches(direct)) {
193 return nullptr;
194 }
195 auto [view, ct] = skgpu::ganesh::AsView(direct, this, skgpu::Mipmapped::kNo);
196 SkASSERT(view);
198
199 skgpu::Budgeted isBudgeted = view.proxy()->isBudgeted();
200 auto copyView = GrSurfaceProxyView::Copy(direct,
201 std::move(view),
203 subset,
205 isBudgeted,
206 /*label=*/"ImageGpuBase_MakeSubset");
207
208 if (!copyView) {
209 return nullptr;
210 }
211
212 return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(direct),
214 std::move(copyView),
215 this->imageInfo().colorInfo());
216}
217
219 const SkIRect&,
220 RequiredProperties) const {
221 SkDEBUGFAIL("Cannot convert Ganesh-backed image to Graphite");
222 return nullptr;
223}
224
228 RequiredProperties) const {
229 SkDEBUGFAIL("Cannot convert Ganesh-backed image to Graphite");
230 return nullptr;
231}
232
234 const SkImageInfo& dstInfo,
235 void* dstPixels,
236 size_t dstRB,
237 int srcX,
238 int srcY,
239 CachingHint) const {
240 if (!fContext->priv().matches(dContext) ||
241 !SkImageInfoValidConversion(dstInfo, this->imageInfo())) {
242 return false;
243 }
244
245 auto [view, ct] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped::kNo);
246 SkASSERT(view);
247
248 GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
249 auto sContext = dContext->priv().makeSC(std::move(view), colorInfo);
250 if (!sContext) {
251 return false;
252 }
253
254 return sContext->readPixels(dContext, {dstInfo, dstPixels, dstRB}, {srcX, srcY});
255}
256
258 if (context && context->abandoned()) {
259 return false;
260 }
261 if (fContext->priv().abandoned()) {
262 return false;
263 }
264 if (context && !fContext->priv().matches(context)) {
265 return false;
266 }
267 return true;
268}
269
271 SkColorType targetColorType,
272 sk_sp<SkColorSpace> targetCS) const {
273 if (kUnknown_SkColorType == targetColorType || !targetCS) {
274 return nullptr;
275 }
276
277 auto myContext = this->context();
278 // This check is also performed in the subclass, but we do it here for the short-circuit below.
279 if (!myContext || !myContext->priv().matches(dContext)) {
280 return nullptr;
281 }
282
285 if (!colorSpace) {
287 }
288 if (colorType == targetColorType &&
289 (SkColorSpace::Equals(colorSpace, targetCS.get()) || this->isAlphaOnly())) {
290 return sk_ref_sp(const_cast<SkImage_GaneshBase*>(this));
291 }
292
293 return this->onMakeColorTypeAndColorSpace(targetColorType, std::move(targetCS), dContext);
294}
295
298 SkISize dimensions,
299 const GrBackendFormat& backendFormat,
300 skgpu::Mipmapped mipmapped,
302 sk_sp<skgpu::RefCntedCallback> releaseHelper) {
303 SkASSERT(tsp);
305 SkASSERT(releaseHelper);
306
307 if (!fulfillProc) {
308 return nullptr;
309 }
310
311 if (mipmapped == skgpu::Mipmapped::kYes &&
313 // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
314 // well.
315 return nullptr;
316 }
317
318 /**
319 * This class is the lazy instantiation callback for promise images. It manages calling the
320 * client's Fulfill and Release procs. It attempts to reuse a GrTexture instance in
321 * cases where the client provides the same GrPromiseImageTexture as Fulfill results for
322 * multiple SkImages. The created GrTexture is given a key based on a unique ID associated with
323 * the GrPromiseImageTexture.
324 *
325 * A key invalidation message is installed on the GrPromiseImageTexture so that the GrTexture
326 * is deleted once it can no longer be used to instantiate a proxy.
327 */
328 class PromiseLazyInstantiateCallback {
329 public:
330 PromiseLazyInstantiateCallback(SkImages::PromiseImageTextureFulfillProc fulfillProc,
331 sk_sp<skgpu::RefCntedCallback> releaseHelper)
332 : fFulfillProc(fulfillProc), fReleaseHelper(std::move(releaseHelper)) {}
333 PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
334 PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
335 // Because we get wrapped in std::function we must be copyable. But we should never
336 // be copied.
337 SkASSERT(false);
338 }
339 PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
340 PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
341 SkASSERT(false);
342 return *this;
343 }
344
345 ~PromiseLazyInstantiateCallback() {
346 // Our destructor can run on any thread. We trigger the unref of fTexture by message.
347 if (fTexture) {
348 GrResourceCache::ReturnResourceFromThread(std::move(fTexture), fTextureContextID);
349 }
350 }
351
352 GrSurfaceProxy::LazyCallbackResult operator()(GrResourceProvider* resourceProvider,
354 // We use the unique key in a way that is unrelated to the SkImage-based key that the
355 // proxy may receive, hence kUnsynced.
356 static constexpr auto kKeySyncMode =
358
359 // In order to make the SkImage "thread safe" we rely on holding an extra ref to the
360 // texture in the callback and signalling the unref via a message to the resource cache.
361 // We need to extend the callback's lifetime to that of the proxy.
362 static constexpr auto kReleaseCallbackOnInstantiation = false;
363
364 // Our proxy is getting instantiated for the second+ time. We are only allowed to call
365 // Fulfill once. So return our cached result.
366 if (fTexture) {
367 return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
368 } else if (fFulfillProcFailed) {
369 // We've already called fulfill and it failed. Our contract says that we should only
370 // call each callback once.
371 return {};
372 }
373
374 SkImages::PromiseImageTextureContext textureContext = fReleaseHelper->context();
375 sk_sp<GrPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
376
377 if (!promiseTexture) {
378 fFulfillProcFailed = true;
379 return {};
380 }
381
382 const GrBackendTexture& backendTexture = promiseTexture->backendTexture();
383 if (!backendTexture.isValid()) {
384 return {};
385 }
386
387 fTexture = resourceProvider->wrapBackendTexture(
389 if (!fTexture) {
390 return {};
391 }
392 fTexture->setRelease(fReleaseHelper);
393 auto dContext = fTexture->getContext();
394 fTextureContextID = dContext->directContextID();
395 return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
396 }
397
398 private:
400 sk_sp<skgpu::RefCntedCallback> fReleaseHelper;
401 sk_sp<GrTexture> fTexture;
402 GrDirectContext::DirectContextID fTextureContextID;
403 bool fFulfillProcFailed = false;
404 } callback(fulfillProc, std::move(releaseHelper));
405
407 tsp, std::move(callback), backendFormat, dimensions, mipmapped);
408}
409
410namespace SkImages {
412 const SkImage* img,
413 const SkIRect& subset) {
414 if (context == nullptr || img == nullptr) {
415 return nullptr;
416 }
417 auto subsetImg = img->makeSubset(context, subset);
418 return SkImages::TextureFromImage(context, subsetImg.get());
419}
420
423 const SkImageFilter* filter,
424 const SkIRect& subset,
425 const SkIRect& clipBounds,
426 SkIRect* outSubset,
427 SkIPoint* offset) {
428 if (!rContext || !src || !filter) {
429 return nullptr;
430 }
431
433 if (as_IB(src)->isGaneshBacked()) {
434 SkImage_GaneshBase* base = static_cast<SkImage_GaneshBase*>(src.get());
435 origin = base->origin();
436 }
437
439 skif::MakeGaneshBackend(sk_ref_sp(rContext), origin, {}, src->colorType());
440 return as_IFB(filter)->makeImageWithFilter(std::move(backend),
441 std::move(src),
442 subset,
443 clipBounds,
444 outSubset,
445 offset);
446}
447
449 if (!src || !as_IB(src)->isGaneshBacked()) {
450 return nullptr;
451 }
452 return as_IB(src)->directContext();
453}
454
455
456} // namespace SkImages
const char * backend
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
@ kRead_GrIOType
Definition: GrTypesPriv.h:403
@ kBorrow_GrWrapOwnership
Definition: GrTypesPriv.h:79
GrColorType
Definition: GrTypesPriv.h:540
static bool GrTextureTypeHasRestrictedSampling(GrTextureType type)
Definition: GrTypesPriv.h:294
static constexpr GrColorType SkColorTypeToGrColorType(SkColorType ct)
Definition: GrTypesPriv.h:629
GrSurfaceOrigin
Definition: GrTypes.h:147
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
SkAlphaType
Definition: SkAlphaType.h:26
@ kUnknown_SkAlphaType
uninitialized
Definition: SkAlphaType.h:27
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkColorSpace * sk_srgb_singleton()
SkColorType
Definition: SkColorType.h:19
@ kUnknown_SkColorType
uninitialized
Definition: SkColorType.h:20
static SkImageFilter_Base * as_IFB(SkImageFilter *filter)
static bool SkColorInfoIsValid(const SkColorInfo &info)
static bool SkImageInfoValidConversion(const SkImageInfo &dst, const SkImageInfo &src)
static SkImage_Base * as_IB(SkImage *image)
Definition: SkImage_Base.h:201
@ kNeedNewImageUniqueID
Definition: SkImage_Base.h:33
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
const Context & fContext
bool isValid() const
GrTextureType textureType() const
GrBackendFormat getBackendFormat() const
bool isValid() const
bool matches(GrContext_Base *candidate) const
Definition: GrCaps.h:57
int maxTextureSize() const
Definition: GrCaps.h:229
bool areColorTypeAndFormatCompatible(GrColorType grCT, const GrBackendFormat &format) const
Definition: GrCaps.cpp:428
bool isFormatCompressed(const GrBackendFormat &format) const
Definition: GrCaps.cpp:457
GrDirectContextPriv priv()
GrImageContextPriv priv()
virtual SK_API bool abandoned()
GrBackendTexture backendTexture() const
static sk_sp< GrTextureProxy > CreatePromiseProxy(GrContextThreadSafeProxy *, LazyInstantiateCallback &&, const GrBackendFormat &, SkISize dimensions, skgpu::Mipmapped)
std::unique_ptr< skgpu::ganesh::SurfaceContext > makeSC(GrSurfaceProxyView readView, const GrColorInfo &)
static std::enable_if_t< std::is_base_of_v< GrGpuResource, T >, void > ReturnResourceFromThread(sk_sp< T > &&resource, GrDirectContext::DirectContextID id)
sk_sp< GrTexture > wrapBackendTexture(const GrBackendTexture &tex, GrWrapOwnership, GrWrapCacheable, GrIOType)
static GrSurfaceProxyView Copy(GrRecordingContext *context, GrSurfaceProxyView src, skgpu::Mipmapped mipmapped, SkIRect srcRect, SkBackingFit fit, skgpu::Budgeted budgeted, std::string_view label)
void setRelease(sk_sp< skgpu::RefCntedCallback > releaseHelper)
Definition: GrSurface.cpp:60
std::unique_ptr< Rec, RecDeleter > RecPtr
Definition: SkBitmapCache.h:53
static RecPtr Alloc(const SkBitmapCacheDesc &, const SkImageInfo &, SkPixmap *)
static bool Find(const SkBitmapCacheDesc &, SkBitmap *result)
static void Add(RecPtr, SkBitmap *)
static bool Equals(const SkColorSpace *, const SkColorSpace *)
sk_sp< SkImage > makeImageWithFilter(sk_sp< skif::Backend > backend, sk_sp< SkImage > src, const SkIRect &subset, const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset) const
virtual GrDirectContext * directContext() const
Definition: SkImage_Base.h:115
virtual void notifyAddedToRasterCache() const
Definition: SkImage_Base.h:174
static bool ValidateBackendTexture(const GrCaps *, const GrBackendTexture &tex, GrColorType grCT, SkColorType ct, SkAlphaType at, sk_sp< SkColorSpace > cs)
bool isValid(GrRecordingContext *) const final
sk_sp< SkImage > onMakeSubset(GrDirectContext *, const SkIRect &subset) const final
virtual sk_sp< SkImage > onMakeColorTypeAndColorSpace(SkColorType, sk_sp< SkColorSpace >, GrDirectContext *) const=0
sk_sp< SkImage > makeSubset(GrDirectContext *direct, const SkIRect &subset) const final
SkImage_GaneshBase(sk_sp< GrImageContext >, SkImageInfo, uint32_t uniqueID)
static bool ValidateCompressedBackendTexture(const GrCaps *, const GrBackendTexture &tex, SkAlphaType)
sk_sp< SkImage > makeColorTypeAndColorSpace(GrDirectContext *dContext, SkColorType targetColorType, sk_sp< SkColorSpace > targetCS) const final
sk_sp< GrImageContext > fContext
GrImageContext * context() const final
static sk_sp< GrTextureProxy > MakePromiseImageLazyProxy(GrContextThreadSafeProxy *, SkISize dimensions, const GrBackendFormat &, skgpu::Mipmapped, SkImages::PromiseImageTextureFulfillProc, sk_sp< skgpu::RefCntedCallback > releaseHelper)
bool getROPixels(GrDirectContext *, SkBitmap *, CachingHint) const final
bool onReadPixels(GrDirectContext *dContext, const SkImageInfo &dstInfo, void *dstPixels, size_t dstRB, int srcX, int srcY, CachingHint) const override
const SkImageInfo & imageInfo() const
Definition: SkImage.h:279
SkColorSpace * colorSpace() const
Definition: SkImage.cpp:156
SkISize dimensions() const
Definition: SkImage.h:297
SkAlphaType alphaType() const
Definition: SkImage.cpp:154
int width() const
Definition: SkImage.h:285
SkColorType colorType() const
Definition: SkImage.cpp:152
int height() const
Definition: SkImage.h:291
SkIRect bounds() const
Definition: SkImage.h:303
CachingHint
Definition: SkImage.h:463
@ kAllow_CachingHint
allows internally caching decoded and copied pixels
Definition: SkImage.h:464
sk_sp< SkColorSpace > refColorSpace() const
Definition: SkImage.cpp:158
virtual sk_sp< SkImage > makeSubset(GrDirectContext *direct, const SkIRect &subset) const =0
T * get() const
Definition: SkRefCnt.h:303
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
void * PromiseImageTextureContext
SK_API sk_sp< SkImage > TextureFromImage(GrDirectContext *, const SkImage *, skgpu::Mipmapped=skgpu::Mipmapped::kNo, skgpu::Budgeted=skgpu::Budgeted::kYes)
SK_API sk_sp< SkImage > SubsetTextureFrom(GrDirectContext *context, const SkImage *img, const SkIRect &subset)
SK_API sk_sp< SkImage > MakeWithFilter(sk_sp< SkImage > src, const SkImageFilter *filter, const SkIRect &subset, const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset)
SK_API GrDirectContext * GetContext(const SkImage *src)
dst
Definition: cp.py:12
std::tuple< GrSurfaceProxyView, GrColorType > AsView(GrRecordingContext *rContext, const SkImage *img, skgpu::Mipmapped mipmapped, GrImageTexGenPolicy policy)
Budgeted
Definition: GpuTypes.h:35
Mipmapped
Definition: GpuTypes.h:53
sk_sp< Backend > MakeGaneshBackend(sk_sp< GrRecordingContext > context, GrSurfaceOrigin origin, const SkSurfaceProps &surfaceProps, SkColorType colorType)
Definition: ref_ptr.h:256
SeparatedVector2 offset
static SkBitmapCacheDesc Make(const SkImage *)
Definition: SkRect.h:32
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
bool isEmpty() const
Definition: SkRect.h:202
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
Definition: SkSize.h:16
bool isEmpty() const
Definition: SkSize.h:31