blob: b5105abf0965bbb0c70a8be43583dcdfeecd7e0d [file] [log] [blame]
Jim Van Verth8026ccc2018-10-04 13:10:39 -04001/*
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
8#include "GrBackendSurface.h"
9#include "GrClip.h"
10#include "GrContext.h"
11#include "GrContextPriv.h"
12#include "GrRenderTargetContext.h"
13#include "GrTexture.h"
14#include "GrTextureAdjuster.h"
15#include "SkBitmapCache.h"
16#include "SkImage_Gpu.h"
17#include "SkImage_GpuBase.h"
Jim Van Verth8bbce0e2018-10-08 14:34:52 -040018#include "SkReadPixelsRec.h"
Jim Van Verth8026ccc2018-10-04 13:10:39 -040019
20SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
21 SkAlphaType at, SkBudgeted budgeted, sk_sp<SkColorSpace> cs)
22 : INHERITED(width, height, uniqueID)
23 , fContext(std::move(context))
24 , fAlphaType(at)
25 , fBudgeted(budgeted)
26 , fColorSpace(std::move(cs)) {}
27
28SkImage_GpuBase::~SkImage_GpuBase() {}
29
30//////////////////////////////////////////////////////////////////////////////////////////////////
31
32bool SkImage_GpuBase::ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
33 GrPixelConfig* config, SkColorType ct, SkAlphaType at,
34 sk_sp<SkColorSpace> cs) {
35 if (!tex.isValid()) {
36 return false;
37 }
38 // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
39 // create a fake image info here.
40 SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
41 if (!SkImageInfoIsValid(info)) {
42 return false;
43 }
44
45 return ctx->contextPriv().caps()->validateBackendTexture(tex, ct, config);
46}
47
48//////////////////////////////////////////////////////////////////////////////////////////////////
49
50bool SkImage_GpuBase::getROPixels(SkBitmap* dst, SkColorSpace*, CachingHint chint) const {
51 if (!fContext->contextPriv().resourceProvider()) {
52 // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
53 return false;
54 }
55
56 // The SkColorSpace parameter "dstColorSpace" is really just a hint about how/where the bitmap
57 // will be used. The client doesn't expect that we convert to that color space, it's intended
58 // for codec-backed images, to drive our decoding heuristic. In theory we *could* read directly
59 // into that color space (to save the client some effort in whatever they're about to do), but
60 // that would make our use of the bitmap cache incorrect (or much less efficient, assuming we
61 // rolled the dstColorSpace into the key).
62 const auto desc = SkBitmapCacheDesc::Make(this);
63 if (SkBitmapCache::Find(desc, dst)) {
64 SkASSERT(dst->getGenerationID() == this->uniqueID());
65 SkASSERT(dst->isImmutable());
66 SkASSERT(dst->getPixels());
67 return true;
68 }
69
70 SkBitmapCache::RecPtr rec = nullptr;
71 SkPixmap pmap;
72 if (kAllow_CachingHint == chint) {
73 rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
74 if (!rec) {
75 return false;
76 }
77 } else {
78 if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
79 return false;
80 }
81 }
82
83 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
84 this->asTextureProxyRef(),
85 fColorSpace);
86 if (!sContext) {
87 return false;
88 }
89
90 if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
91 return false;
92 }
93
94 if (rec) {
95 SkBitmapCache::Add(std::move(rec), dst);
96 this->notifyAddedToRasterCache();
97 }
98 return true;
99}
100
101sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset) const {
102 sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();
103
104 GrSurfaceDesc desc;
105 desc.fWidth = subset.width();
106 desc.fHeight = subset.height();
107 desc.fConfig = proxy->config();
108
109 sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
110 desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact, fBudgeted));
111 if (!sContext) {
112 return nullptr;
113 }
114
115 if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
116 return nullptr;
117 }
118
119 // MDB: this call is okay bc we know 'sContext' was kExact
120 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
121 fAlphaType, sContext->asTextureProxyRef(),
122 fColorSpace, fBudgeted);
123}
124
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400125static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
126 switch (info.colorType()) {
127 case kRGBA_8888_SkColorType:
128 case kBGRA_8888_SkColorType:
129 break;
130 default:
131 return; // nothing to do
132 }
133
134 // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
135 // and in either case, the alpha-byte is always in the same place, so we can safely call
136 // SkPreMultiplyColor()
137 //
138 SkColor* row = (SkColor*)pixels;
139 for (int y = 0; y < info.height(); ++y) {
140 for (int x = 0; x < info.width(); ++x) {
141 row[x] = SkPreMultiplyColor(row[x]);
142 }
143 row = (SkColor*)((char*)(row)+rowBytes);
144 }
145}
146
147bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
148 int srcX, int srcY, CachingHint) const {
149 if (!fContext->contextPriv().resourceProvider()) {
150 // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
151 return false;
152 }
153
154 if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
155 return false;
156 }
157
158 SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
159 if (!rec.trim(this->width(), this->height())) {
160 return false;
161 }
162
163 // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
164 // GrRenderTargetContext::onReadPixels
165 uint32_t flags = 0;
166 if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
167 // let the GPU perform this transformation for us
168 flags = GrContextPriv::kUnpremul_PixelOpsFlag;
169 }
170
171 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
172 this->asTextureProxyRef(), fColorSpace);
173 if (!sContext) {
174 return false;
175 }
176
177 if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
178 return false;
179 }
180
181 // do we have to manually fix-up the alpha channel?
182 // src dst
183 // unpremul premul fix manually
184 // premul unpremul done by kUnpremul_PixelOpsFlag
185 // all other combos need to change.
186 //
187 // Should this be handled by Ganesh? todo:?
188 //
189 if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
190 apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
191 }
192 return true;
193}
194
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400195sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
196 const GrSamplerState& params,
197 SkColorSpace* dstColorSpace,
198 sk_sp<SkColorSpace>* texColorSpace,
199 SkScalar scaleAdjust[2]) const {
200 if (context->uniqueID() != fContext->uniqueID()) {
201 SkASSERT(0);
202 return nullptr;
203 }
204
205 GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(), fAlphaType,
206 this->uniqueID(), fColorSpace.get());
207 return adjuster.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
208}
209
210GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
211 GrSurfaceOrigin* origin) const {
212 sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();
213 SkASSERT(proxy);
214
215 if (!fContext->contextPriv().resourceProvider() && !proxy->isInstantiated()) {
216 // This image was created with a DDL context and cannot be instantiated.
217 return GrBackendTexture();
218}
219
220 if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
221 return GrBackendTexture(); // invalid
222 }
223
224 GrTexture* texture = proxy->peekTexture();
225
226 if (texture) {
227 if (flushPendingGrContextIO) {
228 fContext->contextPriv().prepareSurfaceForExternalIO(proxy.get());
229 }
230 if (origin) {
231 *origin = proxy->origin();
232 }
233 return texture->getBackendTexture();
234 }
235 return GrBackendTexture(); // invalid
236}
237
238GrTexture* SkImage_GpuBase::onGetTexture() const {
239 GrTextureProxy* proxy = this->peekProxy();
240 if (!proxy) {
241 return nullptr;
242 }
243
244 sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef();
245 if (!fContext->contextPriv().resourceProvider() && !proxyRef->isInstantiated()) {
246 // This image was created with a DDL context and cannot be instantiated.
247 return nullptr;
248 }
249
250 if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
251 return nullptr;
252 }
253
254 return proxy->peekTexture();
255}
256
257sk_sp<SkImage> SkImage_GpuBase::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
258 SkAlphaType newAlphaType = fAlphaType;
259#if defined(SK_LEGACY_MAKE_COLOR_SPACE_IMPL)
260 if (kUnpremul_SkAlphaType == fAlphaType) {
261 newAlphaType = kPremul_SkAlphaType;
262 }
263#endif
264 auto xform = GrColorSpaceXformEffect::Make(fColorSpace.get(), this->alphaType(),
265 target.get(), newAlphaType);
266 if (!xform) {
267 return sk_ref_sp(const_cast<SkImage_GpuBase*>(this));
268 }
269
270 sk_sp<GrRenderTargetContext> renderTargetContext(
271 fContext->contextPriv().makeDeferredRenderTargetContext(
272 SkBackingFit::kExact, this->width(), this->height(),
273 kRGBA_8888_GrPixelConfig, nullptr));
274 if (!renderTargetContext) {
275 return nullptr;
276 }
277
278 GrPaint paint;
279 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
280 paint.addColorTextureProcessor(this->asTextureProxyRef(), SkMatrix::I());
281 paint.addColorFragmentProcessor(std::move(xform));
282
283 const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
284
285 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
286
287 if (!renderTargetContext->asTextureProxy()) {
288 return nullptr;
289 }
290
291 // MDB: this call is okay bc we know 'renderTargetContext' was exact
292 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
293 newAlphaType, renderTargetContext->asTextureProxyRef(),
294 std::move(target), fBudgeted);
295}
296
297bool SkImage_GpuBase::onIsValid(GrContext* context) const {
298 // The base class has already checked that context isn't abandoned (if it's not nullptr)
299 if (fContext->abandoned()) {
300 return false;
301 }
302
303 if (context && context != fContext.get()) {
304 return false;
305 }
306
307 return true;
308}
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400309
310/////////////////////////////////////////////////////////////////////////////////////////////////
311sk_sp<GrTexture> SkPromiseImageHelper::getTexture(GrResourceProvider* resourceProvider,
312 GrPixelConfig config) {
313 // Releases the promise helper if there are no outstanding hard refs. This means that we
314 // don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
315 if (fReleaseHelper && fReleaseHelper->weak_expired()) {
316 this->resetReleaseHelper();
317 }
318
319 sk_sp<GrTexture> tex;
320 if (!fReleaseHelper) {
321 fFulfillProc(fContext, &fBackendTex);
322 fBackendTex.fConfig = config;
323 if (!fBackendTex.isValid()) {
324 // Even though the GrBackendTexture is not valid, we must call the release
325 // proc to keep our contract of always calling Fulfill and Release in pairs.
326 fReleaseProc(fContext);
327 return sk_sp<GrTexture>();
328 }
329
330 tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
331 if (!tex) {
332 // Even though the GrBackendTexture is not valid, we must call the release
333 // proc to keep our contract of always calling Fulfill and Release in pairs.
334 fReleaseProc(fContext);
335 return sk_sp<GrTexture>();
336 }
337 fReleaseHelper = new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
338 // Take a weak ref
339 fReleaseHelper->weak_ref();
340 } else {
341 SkASSERT(fBackendTex.isValid());
342 tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
343 if (!tex) {
344 // We weren't able to make a texture here, but since we are in this branch
345 // of the calls (promiseHelper.fReleaseHelper is valid) there is already a
346 // texture out there which will call the release proc so we don't need to
347 // call it here.
348 return sk_sp<GrTexture>();
349 }
350
351 SkAssertResult(fReleaseHelper->try_ref());
352 }
353 SkASSERT(tex);
354 // Pass the hard ref off to the texture
355 tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
356 return tex;
357}