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 "src/core/SkSpecialImage.h"
9
10#include "include/core/SkBitmap.h"
11#include "include/core/SkCanvas.h"
12#include "include/core/SkImage.h"
13#include "src/core/SkBitmapCache.h"
14#include "src/core/SkSpecialSurface.h"
15#include "src/core/SkSurfacePriv.h"
16#include "src/image/SkImage_Base.h"
17#include <atomic>
18
19#if SK_SUPPORT_GPU
20#include "include/gpu/GrContext.h"
21#include "include/private/GrRecordingContext.h"
22#include "src/gpu/GrContextPriv.h"
23#include "src/gpu/GrImageInfo.h"
24#include "src/gpu/GrProxyProvider.h"
25#include "src/gpu/GrRecordingContextPriv.h"
26#include "src/gpu/GrSurfaceContext.h"
27#include "src/gpu/GrTextureProxy.h"
28#include "src/image/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.
33static bool valid_for_imagefilters(const SkImageInfo& info) {
34 // no support for other swizzles/depths yet
35 return info.colorType() == kN32_SkColorType;
36}
37
38///////////////////////////////////////////////////////////////////////////////
39class SkSpecialImage_Base : public SkSpecialImage {
40public:
41 SkSpecialImage_Base(const SkIRect& subset, uint32_t uniqueID, const SkSurfaceProps* props)
42 : INHERITED(subset, uniqueID, props) {
43 }
44 ~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 virtual GrRecordingContext* onGetContext() const { return nullptr; }
51
52 virtual SkColorSpace* onGetColorSpace() const = 0;
53
54#if SK_SUPPORT_GPU
55 virtual GrSurfaceProxyView onView(GrRecordingContext* context) const = 0;
56#endif
57
58 // This subset is relative to the backing store's coordinate frame, it has already been mapped
59 // from the content rect by the non-virtual makeSubset().
60 virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
61
62 virtual sk_sp<SkSpecialSurface> onMakeSurface(
63 SkColorType colorType, const SkColorSpace* colorSpace, const SkISize& size,
64 SkAlphaType at, const SkSurfaceProps* = nullptr) const = 0;
65
66 // This subset (when not null) is relative to the backing store's coordinate frame, it has
67 // already been mapped from the content rect by the non-virtual asImage().
68 virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
69
70 virtual sk_sp<SkSurface> onMakeTightSurface(
71 SkColorType colorType, const SkColorSpace* colorSpace,
72 const SkISize& size, SkAlphaType at) const = 0;
73
74private:
75 typedef SkSpecialImage INHERITED;
76};
77
78///////////////////////////////////////////////////////////////////////////////
79static inline const SkSpecialImage_Base* as_SIB(const SkSpecialImage* image) {
80 return static_cast<const SkSpecialImage_Base*>(image);
81}
82
83SkSpecialImage::SkSpecialImage(const SkIRect& subset,
84 uint32_t uniqueID,
85 const SkSurfaceProps* props)
86 : fProps(SkSurfacePropsCopyOrDefault(props))
87 , fSubset(subset)
88 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID) {
89}
90
91sk_sp<SkSpecialImage> SkSpecialImage::makeTextureImage(GrRecordingContext* context) const {
92#if SK_SUPPORT_GPU
93 if (!context) {
94 return nullptr;
95 }
96 if (GrRecordingContext* curContext = as_SIB(this)->onGetContext()) {
97 return curContext->priv().matches(context) ? sk_ref_sp(this) : nullptr;
98 }
99
100 SkBitmap bmp;
101 // At this point, we are definitely not texture-backed, so we must be raster or generator
102 // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
103 // we are strictly raster-backed (i.e. generator images become raster when they are specialized)
104 // in which case getROPixels could turn into peekPixels...
105 if (!this->getROPixels(&bmp)) {
106 return nullptr;
107 }
108
109 if (bmp.empty()) {
110 return SkSpecialImage::MakeFromRaster(SkIRect::MakeEmpty(), bmp, &this->props());
111 }
112
113 // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
114 // semantics). Since this is cached though we would have to bake the fit into the cache key.
115 auto view = GrMakeCachedBitmapProxyView(context, bmp);
116 if (!view.proxy()) {
117 return nullptr;
118 }
119
120 const SkIRect rect = SkIRect::MakeSize(view.proxy()->dimensions());
121
122 // GrMakeCachedBitmapProxyView has uploaded only the specified subset of 'bmp' so we need not
123 // bother with SkBitmap::getSubset
124 return SkSpecialImage::MakeDeferredFromGpu(context,
125 rect,
126 this->uniqueID(),
127 std::move(view),
128 SkColorTypeToGrColorType(bmp.colorType()),
129 sk_ref_sp(this->getColorSpace()),
130 &this->props(),
131 this->alphaType());
132#else
133 return nullptr;
134#endif
135}
136
137void SkSpecialImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
138 return as_SIB(this)->onDraw(canvas, x, y, paint);
139}
140
141bool SkSpecialImage::getROPixels(SkBitmap* bm) const {
142 return as_SIB(this)->onGetROPixels(bm);
143}
144
145bool SkSpecialImage::isTextureBacked() const {
146 return SkToBool(as_SIB(this)->onGetContext());
147}
148
149GrRecordingContext* SkSpecialImage::getContext() const {
150 return as_SIB(this)->onGetContext();
151}
152
153SkColorSpace* SkSpecialImage::getColorSpace() const {
154 return as_SIB(this)->onGetColorSpace();
155}
156
157#if SK_SUPPORT_GPU
158GrSurfaceProxyView SkSpecialImage::view(GrRecordingContext* context) const {
159 return as_SIB(this)->onView(context);
160}
161#endif
162
163sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(
164 SkColorType colorType, const SkColorSpace* colorSpace, const SkISize& size,
165 SkAlphaType at, const SkSurfaceProps* props) const {
166 return as_SIB(this)->onMakeSurface(colorType, colorSpace, size, at, props);
167}
168
169sk_sp<SkSurface> SkSpecialImage::makeTightSurface(
170 SkColorType colorType, const SkColorSpace* colorSpace, const SkISize& size,
171 SkAlphaType at) const {
172 return as_SIB(this)->onMakeTightSurface(colorType, colorSpace, size, at);
173}
174
175sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
176 SkIRect absolute = subset.makeOffset(this->subset().topLeft());
177 return as_SIB(this)->onMakeSubset(absolute);
178}
179
180sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
181 if (subset) {
182 SkIRect absolute = subset->makeOffset(this->subset().topLeft());
183 return as_SIB(this)->onAsImage(&absolute);
184 } else {
185 return as_SIB(this)->onAsImage(nullptr);
186 }
187}
188
189#if defined(SK_DEBUG) || SK_SUPPORT_GPU
190static bool rect_fits(const SkIRect& rect, int width, int height) {
191 if (0 == width && 0 == height) {
192 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
193 return true;
194 }
195
196 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
197 rect.fRight >= 0 && rect.fRight <= width &&
198 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
199 rect.fBottom >= 0 && rect.fBottom <= height;
200}
201#endif
202
203sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(GrRecordingContext* context,
204 const SkIRect& subset,
205 sk_sp<SkImage> image,
206 const SkSurfaceProps* props) {
207 SkASSERT(rect_fits(subset, image->width(), image->height()));
208
209#if SK_SUPPORT_GPU
210 if (const GrSurfaceProxyView* view = as_IB(image)->view(context)) {
211 if (!as_IB(image)->context()->priv().matches(context)) {
212 return nullptr;
213 }
214
215 return MakeDeferredFromGpu(context, subset, image->uniqueID(), *view,
216 SkColorTypeToGrColorType(image->colorType()),
217 image->refColorSpace(), props);
218 } else
219#endif
220 {
221 SkBitmap bm;
222 if (as_IB(image)->getROPixels(&bm)) {
223 return MakeFromRaster(subset, bm, props);
224 }
225 }
226 return nullptr;
227}
228
229///////////////////////////////////////////////////////////////////////////////
230
231class SkSpecialImage_Raster : public SkSpecialImage_Base {
232public:
233 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps* props)
234 : INHERITED(subset, bm.getGenerationID(), props)
235 , fBitmap(bm)
236 {
237 SkASSERT(bm.pixelRef());
238 SkASSERT(fBitmap.getPixels());
239 }
240
241 SkAlphaType alphaType() const override { return fBitmap.alphaType(); }
242
243 SkColorType colorType() const override { return fBitmap.colorType(); }
244
245 size_t getSize() const override { return fBitmap.computeByteSize(); }
246
247 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
248 SkRect dst = SkRect::MakeXYWH(x, y,
249 this->subset().width(), this->subset().height());
250
251 canvas->drawBitmapRect(fBitmap, this->subset(),
252 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
253 }
254
255 bool onGetROPixels(SkBitmap* bm) const override {
256 return fBitmap.extractSubset(bm, this->subset());
257 }
258
259 SkColorSpace* onGetColorSpace() const override {
260 return fBitmap.colorSpace();
261 }
262
263#if SK_SUPPORT_GPU
264 GrSurfaceProxyView onView(GrRecordingContext* context) const override {
265 if (context) {
266 return GrMakeCachedBitmapProxyView(context, fBitmap);
267 }
268
269 return {};
270 }
271#endif
272
273 sk_sp<SkSpecialSurface> onMakeSurface(SkColorType colorType, const SkColorSpace* colorSpace,
274 const SkISize& size, SkAlphaType at,
275 const SkSurfaceProps* props) const override {
276 // Ignore the requested color type, the raster backend currently only supports N32
277 colorType = kN32_SkColorType; // TODO: find ways to allow f16
278 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
279 return SkSpecialSurface::MakeRaster(info, props);
280 }
281
282 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
283 // No need to extract subset, onGetROPixels handles that when needed
284 return SkSpecialImage::MakeFromRaster(subset, fBitmap, &this->props());
285 }
286
287 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
288 if (subset) {
289 SkBitmap subsetBM;
290
291 if (!fBitmap.extractSubset(&subsetBM, *subset)) {
292 return nullptr;
293 }
294
295 return SkImage::MakeFromBitmap(subsetBM);
296 }
297
298 return SkImage::MakeFromBitmap(fBitmap);
299 }
300
301sk_sp<SkSurface> onMakeTightSurface(SkColorType colorType, const SkColorSpace* colorSpace,
302 const SkISize& size, SkAlphaType at) const override {
303 // Ignore the requested color type, the raster backend currently only supports N32
304 colorType = kN32_SkColorType; // TODO: find ways to allow f16
305 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
306 return SkSurface::MakeRaster(info);
307 }
308
309private:
310 SkBitmap fBitmap;
311
312 typedef SkSpecialImage_Base INHERITED;
313};
314
315sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
316 const SkBitmap& bm,
317 const SkSurfaceProps* props) {
318 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
319
320 if (!bm.pixelRef()) {
321 return nullptr;
322 }
323
324 const SkBitmap* srcBM = &bm;
325 SkBitmap tmp;
326 // ImageFilters only handle N32 at the moment, so force our src to be that
327 if (!valid_for_imagefilters(bm.info())) {
328 if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
329 !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
330 {
331 return nullptr;
332 }
333 srcBM = &tmp;
334 }
335 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
336}
337
338sk_sp<SkSpecialImage> SkSpecialImage::CopyFromRaster(const SkIRect& subset,
339 const SkBitmap& bm,
340 const SkSurfaceProps* props) {
341 SkASSERT(rect_fits(subset, bm.width(), bm.height()));
342
343 if (!bm.pixelRef()) {
344 return nullptr;
345 }
346
347 SkBitmap tmp;
348 SkImageInfo info = bm.info().makeDimensions(subset.size());
349 // As in MakeFromRaster, must force src to N32 for ImageFilters
350 if (!valid_for_imagefilters(bm.info())) {
351 info = info.makeColorType(kN32_SkColorType);
352 }
353 if (!tmp.tryAllocPixels(info)) {
354 return nullptr;
355 }
356 if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) {
357 return nullptr;
358 }
359
360 // Since we're making a copy of the raster, the resulting special image is the exact size
361 // of the requested subset of the original and no longer needs to be offset by subset's left
362 // and top, since those were relative to the original's buffer.
363 return sk_make_sp<SkSpecialImage_Raster>(
364 SkIRect::MakeWH(subset.width(), subset.height()), tmp, props);
365}
366
367#if SK_SUPPORT_GPU
368///////////////////////////////////////////////////////////////////////////////
369static sk_sp<SkImage> wrap_proxy_in_image(GrRecordingContext* context, GrSurfaceProxyView view,
370 SkColorType colorType, SkAlphaType alphaType,
371 sk_sp<SkColorSpace> colorSpace) {
372 // CONTEXT TODO: remove this use of 'backdoor' to create an SkImage
373 return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context->priv().backdoor()),
374 kNeedNewImageUniqueID, std::move(view), colorType, alphaType,
375 std::move(colorSpace));
376}
377
378class SkSpecialImage_Gpu : public SkSpecialImage_Base {
379public:
380 SkSpecialImage_Gpu(GrRecordingContext* context, const SkIRect& subset,
381 uint32_t uniqueID, GrSurfaceProxyView view, GrColorType ct,
382 SkAlphaType at, sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props)
383 : INHERITED(subset, uniqueID, props)
384 , fContext(context)
385 , fView(std::move(view))
386 , fColorType(ct)
387 , fAlphaType(at)
388 , fColorSpace(std::move(colorSpace))
389 , fAddedRasterVersionToCache(false) {
390 }
391
392 ~SkSpecialImage_Gpu() override {
393 if (fAddedRasterVersionToCache.load()) {
394 SkNotifyBitmapGenIDIsStale(this->uniqueID());
395 }
396 }
397
398 SkAlphaType alphaType() const override { return fAlphaType; }
399
400 SkColorType colorType() const override { return GrColorTypeToSkColorType(fColorType); }
401
402 size_t getSize() const override {
403 return fView.proxy()->gpuMemorySize(*fContext->priv().caps());
404 }
405
406 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const override {
407 SkRect dst = SkRect::MakeXYWH(x, y,
408 this->subset().width(), this->subset().height());
409
410 // TODO: In this instance we know we're going to draw a sub-portion of the backing
411 // texture into the canvas so it is okay to wrap it in an SkImage. This poses
412 // some problems for full deferral however in that when the deferred SkImage_Gpu
413 // instantiates itself it is going to have to either be okay with having a larger
414 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
415 // to be tightened (if it is deferred).
416 sk_sp<SkImage> img =
417 sk_sp<SkImage>(new SkImage_Gpu(sk_ref_sp(canvas->getGrContext()), this->uniqueID(),
418 fView, this->colorType(), fAlphaType, fColorSpace));
419
420 canvas->drawImageRect(img, this->subset(),
421 dst, paint, SkCanvas::kStrict_SrcRectConstraint);
422 }
423
424 GrRecordingContext* onGetContext() const override { return fContext; }
425
426 GrSurfaceProxyView onView(GrRecordingContext* context) const override { return fView; }
427
428 bool onGetROPixels(SkBitmap* dst) const override {
429 const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->subset());
430 if (SkBitmapCache::Find(desc, dst)) {
431 SkASSERT(dst->getGenerationID() == this->uniqueID());
432 SkASSERT(dst->isImmutable());
433 SkASSERT(dst->getPixels());
434 return true;
435 }
436
437 SkPixmap pmap;
438 SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
439 this->alphaType(), fColorSpace);
440 auto rec = SkBitmapCache::Alloc(desc, info, &pmap);
441 if (!rec) {
442 return false;
443 }
444 auto sContext = GrSurfaceContext::Make(fContext, fView, fColorType, this->alphaType(),
445 fColorSpace);
446 if (!sContext) {
447 return false;
448 }
449
450 if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(),
451 {this->subset().left(), this->subset().top()})) {
452 return false;
453 }
454
455 SkBitmapCache::Add(std::move(rec), dst);
456 fAddedRasterVersionToCache.store(true);
457 return true;
458 }
459
460 SkColorSpace* onGetColorSpace() const override {
461 return fColorSpace.get();
462 }
463
464 sk_sp<SkSpecialSurface> onMakeSurface(SkColorType colorType, const SkColorSpace* colorSpace,
465 const SkISize& size, SkAlphaType at,
466 const SkSurfaceProps* props) const override {
467 if (!fContext) {
468 return nullptr;
469 }
470
471 return SkSpecialSurface::MakeRenderTarget(fContext, size.width(), size.height(),
472 SkColorTypeToGrColorType(colorType),
473 sk_ref_sp(colorSpace), props);
474 }
475
476 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
477 return SkSpecialImage::MakeDeferredFromGpu(fContext,
478 subset,
479 this->uniqueID(),
480 fView,
481 fColorType,
482 fColorSpace,
483 &this->props(),
484 fAlphaType);
485 }
486
487 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
488 GrSurfaceProxy* proxy = fView.proxy();
489 if (subset) {
490 if (proxy->isFunctionallyExact() && *subset == SkIRect::MakeSize(proxy->dimensions())) {
491 proxy->priv().exactify(false);
492 // The existing GrTexture is already tight so reuse it in the SkImage
493 return wrap_proxy_in_image(fContext, fView, this->colorType(), fAlphaType,
494 fColorSpace);
495 }
496
497 auto subsetView = GrSurfaceProxyView::Copy(fContext, fView, GrMipMapped::kNo, *subset,
498 SkBackingFit::kExact, SkBudgeted::kYes);
499 if (!subsetView) {
500 return nullptr;
501 }
502 SkASSERT(subsetView.asTextureProxy());
503 SkASSERT(subsetView.proxy()->priv().isExact());
504
505 // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
506 // return a kExact-backed proxy
507 return wrap_proxy_in_image(fContext, std::move(subsetView), this->colorType(),
508 fAlphaType, fColorSpace);
509 }
510
511 proxy->priv().exactify(true);
512
513 return wrap_proxy_in_image(fContext, fView, this->colorType(), fAlphaType, fColorSpace);
514 }
515
516 sk_sp<SkSurface> onMakeTightSurface(SkColorType colorType, const SkColorSpace* colorSpace,
517 const SkISize& size, SkAlphaType at) const override {
518 // TODO (michaelludwig): Why does this ignore colorType but onMakeSurface doesn't ignore it?
519 // Once makeTightSurface() goes away, should this type overriding behavior be moved into
520 // onMakeSurface() or is this unnecessary?
521 colorType = colorSpace && colorSpace->gammaIsLinear()
522 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
523 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
524 // CONTEXT TODO: remove this use of 'backdoor' to create an SkSurface
525 return SkSurface::MakeRenderTarget(fContext->priv().backdoor(), SkBudgeted::kYes, info);
526 }
527
528private:
529 GrRecordingContext* fContext;
530 GrSurfaceProxyView fView;
531 const GrColorType fColorType;
532 const SkAlphaType fAlphaType;
533 sk_sp<SkColorSpace> fColorSpace;
534 mutable std::atomic<bool> fAddedRasterVersionToCache;
535
536 typedef SkSpecialImage_Base INHERITED;
537};
538
539sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrRecordingContext* context,
540 const SkIRect& subset,
541 uint32_t uniqueID,
542 GrSurfaceProxyView view,
543 GrColorType colorType,
544 sk_sp<SkColorSpace> colorSpace,
545 const SkSurfaceProps* props,
546 SkAlphaType at) {
547 if (!context || context->priv().abandoned() || !view.asTextureProxy()) {
548 return nullptr;
549 }
550 SkASSERT_RELEASE(rect_fits(subset, view.proxy()->width(), view.proxy()->height()));
551 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(view), colorType,
552 at, std::move(colorSpace), props);
553}
554#endif
555