Line data Source code
1 : /*
2 : * Copyright 2016 Google Inc.
3 : *
4 : * Use of this source code is governed by a BSD-style license that can be
5 : * found in the LICENSE file
6 : */
7 :
8 : #include "SkSpecialImage.h"
9 : #include "SkBitmap.h"
10 : #include "SkImage.h"
11 : #include "SkBitmapCache.h"
12 : #include "SkCanvas.h"
13 : #include "SkImage_Base.h"
14 : #include "SkSpecialSurface.h"
15 : #include "SkSurfacePriv.h"
16 : #include "SkPixelRef.h"
17 :
18 : #if SK_SUPPORT_GPU
19 : #include "GrContext.h"
20 : #include "GrContextPriv.h"
21 : #include "GrResourceProvider.h"
22 : #include "GrSurfaceContext.h"
23 : #include "GrSurfaceProxyPriv.h"
24 : #include "GrTexture.h"
25 : #include "GrSamplerParams.h"
26 : #include "GrTextureProxy.h"
27 : #include "SkGr.h"
28 : #include "SkImage_Gpu.h"
29 : #endif
30 :
31 : // Currently the raster imagefilters can only handle certain imageinfos. Call this to know if
32 : // a given info is supported.
33 3 : static bool valid_for_imagefilters(const SkImageInfo& info) {
34 : // no support for other swizzles/depths yet
35 3 : return info.colorType() == kN32_SkColorType;
36 : }
37 :
38 : ///////////////////////////////////////////////////////////////////////////////
39 : class SkSpecialImage_Base : public SkSpecialImage {
40 : public:
41 3 : SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
42 3 : : INHERITED(subset, uniqueID, props) {
43 3 : }
44 3 : ~SkSpecialImage_Base() override { }
45 :
46 : virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
47 :
48 : virtual bool onGetROPixels(SkBitmap*) const = 0;
49 :
50 3 : virtual GrContext* onGetContext() const { return nullptr; }
51 :
52 : virtual SkColorSpace* onGetColorSpace() const = 0;
53 :
54 : #if SK_SUPPORT_GPU
55 : virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const = 0;
56 : #endif
57 :
58 : virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
59 :
60 : virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
61 : const SkISize& size, SkAlphaType at) const = 0;
62 :
63 : virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
64 :
65 : virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
66 : const SkISize& size, SkAlphaType at) const = 0;
67 :
68 : private:
69 : typedef SkSpecialImage INHERITED;
70 : };
71 :
72 : ///////////////////////////////////////////////////////////////////////////////
73 6 : static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
74 6 : return static_cast<const SkSpecialImage_Base*>(image);
75 : }
76 :
77 3 : SkSpecialImage::SkSpecialImage(const SkIRect& subset,
78 : uint32_t uniqueID,
79 3 : const SkSurfaceProps* props)
80 : : fProps(SkSurfacePropsCopyOrDefault(props))
81 : , fSubset(subset)
82 3 : , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
83 3 : }
84 :
85 0 : sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrContext* context) {
86 : #if SK_SUPPORT_GPU
87 0 : if (!context) {
88 0 : return nullptr;
89 : }
90 0 : if (GrContext* curContext = as_SIB(this)->onGetContext()) {
91 0 : return curContext == context ? sk_sp<SkSpecialImage>(SkRef(this)) : nullptr;
92 : }
93 :
94 0 : SkBitmap bmp;
95 : // At this point, we are definitely not texture-backed, so we must be raster or generator
96 : // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
97 : // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
98 : // in which case getROPixels could turn into peekPixels...
99 0 : if (!this->getROPixels(&bmp)) {
100 0 : return nullptr;
101 : }
102 :
103 0 : if (bmp.empty()) {
104 0 : return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
105 : }
106 :
107 : // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
108 : // semantics). Since this is cached though we would have to bake the fit into the cache key.
109 0 : sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(context->resourceProvider(), bmp);
110 0 : if (!proxy) {
111 0 : return nullptr;
112 : }
113 :
114 0 : const SkIRect rect = SkIRect::MakeWH(proxy->width(), proxy->height());
115 :
116 : // GrMakeCachedBitmapProxy has uploaded only the specified subset of 'bmp' so we need not
117 : // bother with SkBitmap::getSubset
118 : return SkSpecialImage::MakeDeferredFromGpu(context,
119 : rect,
120 : this->uniqueID(),
121 0 : std::move(proxy),
122 0 : sk_ref_sp(this->getColorSpace()),
123 0 : &this->props(),
124 0 : this->alphaType());
125 : #else
126 : return nullptr;
127 : #endif
128 : }
129 :
130 0 : void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
131 0 : return as_SIB(this)->onDraw(canvas, x, y, paint);
132 : }
133 :
134 3 : bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
135 3 : return as_SIB(this)->onGetROPixels(bm);
136 : }
137 :
138 3 : bool SkSpecialImage::isTextureBacked() const {
139 3 : return SkToBool(as_SIB(this)->onGetContext());
140 : }
141 :
142 0 : GrContext* SkSpecialImage::getContext() const {
143 0 : return as_SIB(this)->onGetContext();
144 : }
145 :
146 0 : SkColorSpace* SkSpecialImage::getColorSpace() const {
147 0 : return as_SIB(this)->onGetColorSpace();
148 : }
149 :
150 : #if SK_SUPPORT_GPU
151 0 : sk_sp<GrTextureProxy> SkSpecialImage::asTextureProxyRef(GrContext* context) const {
152 0 : return as_SIB(this)->onAsTextureProxyRef(context);
153 : }
154 : #endif
155 :
156 0 : sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(const SkImageFilter::OutputProperties& outProps,
157 : const SkISize& size, SkAlphaType at) const {
158 0 : return as_SIB(this)->onMakeSurface(outProps, size, at);
159 : }
160 :
161 0 : sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputProperties& outProps,
162 : const SkISize& size, SkAlphaType at) const {
163 0 : return as_SIB(this)->onMakeTightSurface(outProps, size, at);
164 : }
165 :
166 0 : sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
167 0 : return as_SIB(this)->onMakeSubset(subset);
168 : }
169 :
170 0 : sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
171 0 : return as_SIB(this)->onAsImage(subset);
172 : }
173 :
174 :
175 : #ifdef SK_DEBUG
176 3 : static bool rect_fits(const SkIRect& rect, int width, int height) {
177 3 : if (0 == width && 0 == height) {
178 0 : SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
179 0 : return true;
180 : }
181 :
182 9 : return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
183 9 : rect.fRight >= 0 && rect.fRight <= width &&
184 9 : rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
185 9 : rect.fBottom >= 0 && rect.fBottom <= height;
186 : }
187 : #endif
188 :
189 0 : sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
190 : sk_sp<SkImage> image,
191 : SkColorSpace* dstColorSpace,
192 : const SkSurfaceProps* props) {
193 0 : SkASSERT(rect_fits(subset, image->width(), image->height()));
194 :
195 : #if SK_SUPPORT_GPU
196 0 : if (sk_sp<GrTextureProxy> proxy = as_IB(image)->asTextureProxyRef()) {
197 0 : GrContext* context = ((SkImage_Gpu*) as_IB(image))->context();
198 :
199 0 : return MakeDeferredFromGpu(context, subset, image->uniqueID(), std::move(proxy),
200 0 : as_IB(image)->onImageInfo().refColorSpace(), props);
201 : } else
202 : #endif
203 : {
204 0 : SkBitmap bm;
205 0 : if (as_IB(image)->getROPixels(&bm, dstColorSpace)) {
206 0 : return MakeFromRaster(subset, bm, props);
207 : }
208 : }
209 0 : return nullptr;
210 : }
211 :
212 : ///////////////////////////////////////////////////////////////////////////////
213 :
214 9 : class SkSpecialImage_Raster : public SkSpecialImage_Base {
215 : public:
216 3 : SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
217 3 : : INHERITED(subset, bm.getGenerationID(), props)
218 3 : , fBitmap(bm)
219 : {
220 3 : SkASSERT(bm.pixelRef());
221 :
222 : // We have to lock now, while bm is still in scope, since it may have come from our
223 : // cache, which means we need to keep it locked until we (the special) are done, since
224 : // we cannot re-generate the cache entry (if bm came from a generator).
225 3 : fBitmap.lockPixels();
226 3 : SkASSERT(fBitmap.getPixels());
227 3 : }
228 :
229 0 : SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
230 :
231 0 : size_t getSize() const override { return fBitmap.getSize(); }
232 :
233 0 : void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
234 : SkRect dst = SkRect::MakeXYWH(x, y,
235 0 : this->subset().width(), this->subset().height());
236 :
237 0 : canvas->drawBitmapRect(fBitmap, this->subset(),
238 0 : dst, paint, SkCanvas::kStrict_SrcRectConstraint);
239 0 : }
240 :
241 3 : bool onGetROPixels(SkBitmap* bm) const override {
242 3 : *bm = fBitmap;
243 3 : return true;
244 : }
245 :
246 0 : SkColorSpace* onGetColorSpace() const override {
247 0 : return fBitmap.colorSpace();
248 : }
249 :
250 : #if SK_SUPPORT_GPU
251 0 : sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext* context) const override {
252 0 : if (context) {
253 0 : return GrMakeCachedBitmapProxy(context->resourceProvider(), fBitmap);
254 : }
255 :
256 0 : return nullptr;
257 : }
258 : #endif
259 :
260 : // TODO: The raster implementations of image filters all currently assume that the pixels are
261 : // legacy N32. Until they actually check the format and operate on sRGB or F16 data appropriately,
262 : // we can't enable this. (They will continue to produce incorrect results, but less-so).
263 : #define RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16 0
264 :
265 0 : sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
266 : const SkISize& size, SkAlphaType at) const override {
267 : #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
268 : SkColorSpace* colorSpace = outProps.colorSpace();
269 : #else
270 0 : SkColorSpace* colorSpace = nullptr;
271 : #endif
272 0 : SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
273 0 : ? kRGBA_F16_SkColorType : kN32_SkColorType;
274 : SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
275 0 : sk_ref_sp(colorSpace));
276 0 : return SkSpecialSurface::MakeRaster(info, nullptr);
277 : }
278 :
279 0 : sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
280 0 : SkBitmap subsetBM;
281 :
282 0 : if (!fBitmap.extractSubset(&subsetBM, subset)) {
283 0 : return nullptr;
284 : }
285 :
286 0 : return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
287 : subsetBM,
288 0 : &this->props());
289 : }
290 :
291 0 : sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
292 0 : if (subset) {
293 0 : SkBitmap subsetBM;
294 :
295 0 : if (!fBitmap.extractSubset(&subsetBM, *subset)) {
296 0 : return nullptr;
297 : }
298 :
299 0 : return SkImage::MakeFromBitmap(subsetBM);
300 : }
301 :
302 0 : return SkImage::MakeFromBitmap(fBitmap);
303 : }
304 :
305 0 : sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
306 : const SkISize& size, SkAlphaType at) const override {
307 : #if RASTER_IMAGE_FILTERS_SUPPORT_SRGB_AND_F16
308 : SkColorSpace* colorSpace = outProps.colorSpace();
309 : #else
310 0 : SkColorSpace* colorSpace = nullptr;
311 : #endif
312 0 : SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
313 0 : ? kRGBA_F16_SkColorType : kN32_SkColorType;
314 : SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
315 0 : sk_ref_sp(colorSpace));
316 0 : return SkSurface::MakeRaster(info);
317 : }
318 :
319 : private:
320 : SkBitmap fBitmap;
321 :
322 : typedef SkSpecialImage_Base INHERITED;
323 : };
324 :
325 3 : sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
326 : const SkBitmap& bm,
327 : const SkSurfaceProps* props) {
328 3 : SkASSERT(rect_fits(subset, bm.width(), bm.height()));
329 :
330 3 : if (!bm.pixelRef()) {
331 0 : return nullptr;
332 : }
333 :
334 3 : const SkBitmap* srcBM = &bm;
335 6 : SkBitmap tmpStorage;
336 : // ImageFilters only handle N32 at the moment, so force our src to be that
337 3 : if (!valid_for_imagefilters(bm.info())) {
338 0 : if (!bm.copyTo(&tmpStorage, kN32_SkColorType)) {
339 0 : return nullptr;
340 : }
341 0 : srcBM = &tmpStorage;
342 : }
343 3 : return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
344 : }
345 :
346 : #if SK_SUPPORT_GPU
347 : ///////////////////////////////////////////////////////////////////////////////
348 : #include "GrTexture.h"
349 :
350 0 : static sk_sp<SkImage> wrap_proxy_in_image(GrContext* context, sk_sp<GrTextureProxy> proxy,
351 : SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
352 0 : return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, alphaType,
353 0 : std::move(proxy), std::move(colorSpace), SkBudgeted::kYes);
354 : }
355 :
356 : class SkSpecialImage_Gpu : public SkSpecialImage_Base {
357 : public:
358 0 : SkSpecialImage_Gpu(GrContext* context, const SkIRect& subset,
359 : uint32_t uniqueID, sk_sp<GrTextureProxy> proxy, SkAlphaType at,
360 : sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
361 0 : : INHERITED(subset, uniqueID, props)
362 : , fContext(context)
363 0 : , fTextureProxy(std::move(proxy))
364 : , fAlphaType(at)
365 0 : , fColorSpace(std::move(colorSpace))
366 0 : , fAddedRasterVersionToCache(false) {
367 0 : }
368 :
369 0 : ~SkSpecialImage_Gpu() override {
370 0 : if (fAddedRasterVersionToCache.load()) {
371 0 : SkNotifyBitmapGenIDIsStale(this->uniqueID());
372 : }
373 0 : }
374 :
375 0 : SkAlphaType alphaType() const override { return fAlphaType; }
376 :
377 0 : size_t getSize() const override { return fTextureProxy->gpuMemorySize(); }
378 :
379 0 : void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
380 : SkRect dst = SkRect::MakeXYWH(x, y,
381 0 : this->subset().width(), this->subset().height());
382 :
383 : // TODO: In this instance we know we're going to draw a sub-portion of the backing
384 : // texture into the canvas so it is okay to wrap it in an SkImage. This poses
385 : // some problems for full deferral however in that when the deferred SkImage_Gpu
386 : // instantiates itself it is going to have to either be okay with having a larger
387 : // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
388 : // to be tightened (if it is deferred).
389 0 : sk_sp<SkImage> img = sk_sp<SkImage>(new SkImage_Gpu(canvas->getGrContext(),
390 0 : this->uniqueID(), fAlphaType,
391 : fTextureProxy,
392 0 : fColorSpace, SkBudgeted::kNo));
393 :
394 0 : canvas->drawImageRect(img, this->subset(),
395 0 : dst, paint, SkCanvas::kStrict_SrcRectConstraint);
396 0 : }
397 :
398 0 : GrContext* onGetContext() const override { return fContext; }
399 :
400 0 : sk_sp<GrTextureProxy> onAsTextureProxyRef(GrContext*) const override {
401 0 : return fTextureProxy;
402 : }
403 :
404 0 : bool onGetROPixels(SkBitmap* dst) const override {
405 0 : const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->width(), this->height());
406 0 : if (SkBitmapCache::Find(desc, dst)) {
407 0 : SkASSERT(dst->getGenerationID() == this->uniqueID());
408 0 : SkASSERT(dst->isImmutable());
409 0 : SkASSERT(dst->getPixels());
410 0 : return true;
411 : }
412 :
413 0 : SkPixmap pmap;
414 : SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
415 0 : this->alphaType(), fColorSpace);
416 0 : auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
417 0 : if (!rec) {
418 0 : return false;
419 : }
420 :
421 0 : sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
422 0 : fTextureProxy, nullptr);
423 0 : if (!sContext) {
424 0 : return false;
425 : }
426 :
427 0 : if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
428 0 : return false;
429 : }
430 :
431 0 : SkBitmapCache::Add(std::move(rec), dst);
432 0 : fAddedRasterVersionToCache.store(true);
433 0 : return true;
434 : }
435 :
436 0 : SkColorSpace* onGetColorSpace() const override {
437 0 : return fColorSpace.get();
438 : }
439 :
440 0 : sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
441 : const SkISize& size, SkAlphaType at) const override {
442 0 : if (!fContext) {
443 0 : return nullptr;
444 : }
445 :
446 0 : SkColorSpace* colorSpace = outProps.colorSpace();
447 : return SkSpecialSurface::MakeRenderTarget(
448 0 : fContext, size.width(), size.height(),
449 0 : GrRenderableConfigForColorSpace(colorSpace), sk_ref_sp(colorSpace));
450 : }
451 :
452 0 : sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
453 0 : return SkSpecialImage::MakeDeferredFromGpu(fContext,
454 : subset,
455 : this->uniqueID(),
456 : fTextureProxy,
457 : fColorSpace,
458 0 : &this->props(),
459 0 : fAlphaType);
460 : }
461 :
462 : // TODO: move all the logic here into the subset-flavor GrSurfaceProxy::copy?
463 0 : sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
464 0 : if (subset) {
465 : // TODO: if this becomes a bottle neck we could base this logic on what the size
466 : // will be when it is finally instantiated - but that is more fraught.
467 0 : if (GrResourceProvider::IsFunctionallyExact(fTextureProxy.get()) &&
468 0 : 0 == subset->fLeft && 0 == subset->fTop &&
469 0 : fTextureProxy->width() == subset->width() &&
470 0 : fTextureProxy->height() == subset->height()) {
471 : // The existing GrTexture is already tight so reuse it in the SkImage
472 0 : return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
473 : }
474 :
475 0 : sk_sp<GrTextureProxy> subsetProxy(GrSurfaceProxy::Copy(fContext, fTextureProxy.get(),
476 0 : *subset, SkBudgeted::kYes));
477 0 : if (!subsetProxy) {
478 0 : return nullptr;
479 : }
480 :
481 0 : SkASSERT(subsetProxy->priv().isExact());
482 : // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
483 : // return a kExact-backed proxy
484 0 : return wrap_proxy_in_image(fContext, std::move(subsetProxy), fAlphaType, fColorSpace);
485 : }
486 :
487 0 : fTextureProxy->priv().exactify();
488 :
489 0 : return wrap_proxy_in_image(fContext, fTextureProxy, fAlphaType, fColorSpace);
490 : }
491 :
492 0 : sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
493 : const SkISize& size, SkAlphaType at) const override {
494 0 : SkColorSpace* colorSpace = outProps.colorSpace();
495 0 : SkColorType colorType = colorSpace && colorSpace->gammaIsLinear()
496 0 : ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
497 : SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), colorType, at,
498 0 : sk_ref_sp(colorSpace));
499 0 : return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kYes, info);
500 : }
501 :
502 : private:
503 : GrContext* fContext;
504 : sk_sp<GrTextureProxy> fTextureProxy;
505 : const SkAlphaType fAlphaType;
506 : sk_sp<SkColorSpace> fColorSpace;
507 : mutable SkAtomic<bool> fAddedRasterVersionToCache;
508 :
509 : typedef SkSpecialImage_Base INHERITED;
510 : };
511 :
512 0 : sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrContext* context,
513 : const SkIRect& subset,
514 : uint32_t uniqueID,
515 : sk_sp<GrTextureProxy> proxy,
516 : sk_sp<SkColorSpace> colorSpace,
517 : const SkSurfaceProps* props,
518 : SkAlphaType at) {
519 0 : SkASSERT(rect_fits(subset, proxy->width(), proxy->height()));
520 0 : return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(proxy), at,
521 0 : std::move(colorSpace), props);
522 : }
523 : #endif
|