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