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/SkColorSpace.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkSurface.h"
16 #include "include/core/SkTileMode.h"
17 #include "src/core/SkSpecialSurface.h"
18 #include "src/core/SkSurfacePriv.h"
19 #include "src/image/SkImage_Base.h"
20
21 #if defined(SK_GANESH)
22 #include "include/gpu/GrDirectContext.h"
23 #include "include/gpu/GrRecordingContext.h"
24 #include "src/gpu/SkBackingFit.h"
25 #include "src/gpu/ganesh/GrImageInfo.h"
26 #include "src/gpu/ganesh/GrProxyProvider.h"
27 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
28 #include "src/gpu/ganesh/GrTextureProxy.h"
29 #include "src/image/SkImage_Gpu.h"
30 #include "src/shaders/SkImageShader.h"
31 #endif
32
33 // Currently, the raster imagefilters can only handle certain imageinfos. Call this to know if
34 // a given info is supported.
valid_for_imagefilters(const SkImageInfo & info)35 static bool valid_for_imagefilters(const SkImageInfo& info) {
36 // no support for other swizzles/depths yet
37 return info.colorType() == kN32_SkColorType;
38 }
39
SkSpecialImage(const SkIRect & subset,uint32_t uniqueID,const SkColorInfo & colorInfo,const SkSurfaceProps & props)40 SkSpecialImage::SkSpecialImage(const SkIRect& subset,
41 uint32_t uniqueID,
42 const SkColorInfo& colorInfo,
43 const SkSurfaceProps& props)
44 : fSubset(subset)
45 , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID)
46 , fColorInfo(colorInfo)
47 , fProps(props) {
48 }
49
makeSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at,const SkSurfaceProps & props) const50 sk_sp<SkSpecialSurface> SkSpecialImage::makeSurface(SkColorType colorType,
51 const SkColorSpace* colorSpace,
52 const SkISize& size,
53 SkAlphaType at,
54 const SkSurfaceProps& props) const {
55 return this->onMakeSurface(colorType, colorSpace, size, at, props);
56 }
57
makeTightSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at) const58 sk_sp<SkSurface> SkSpecialImage::makeTightSurface(SkColorType colorType,
59 const SkColorSpace* colorSpace,
60 const SkISize& size,
61 SkAlphaType at) const {
62 return this->onMakeTightSurface(colorType, colorSpace, size, at);
63 }
64
asImage(const SkIRect * subset) const65 sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
66 if (subset) {
67 SkIRect absolute = subset->makeOffset(this->subset().topLeft());
68 return this->onAsImage(&absolute);
69 } else {
70 return this->onAsImage(nullptr);
71 }
72 }
73
asShader(SkTileMode tileMode,const SkSamplingOptions & sampling,const SkMatrix & lm) const74 sk_sp<SkShader> SkSpecialImage::asShader(SkTileMode tileMode,
75 const SkSamplingOptions& sampling,
76 const SkMatrix& lm) const {
77 return this->onAsShader(tileMode, sampling, lm);
78 }
79
asShader(const SkSamplingOptions & sampling) const80 sk_sp<SkShader> SkSpecialImage::asShader(const SkSamplingOptions& sampling) const {
81 return this->asShader(sampling, SkMatrix::I());
82 }
83
asShader(const SkSamplingOptions & sampling,const SkMatrix & lm) const84 sk_sp<SkShader> SkSpecialImage::asShader(const SkSamplingOptions& sampling,
85 const SkMatrix& lm) const {
86 return this->asShader(SkTileMode::kClamp, sampling, lm);
87 }
88
89 #if SK_GRAPHITE
90 #include "src/gpu/graphite/TextureProxyView.h"
91
isGraphiteBacked() const92 bool SkSpecialImage::isGraphiteBacked() const {
93 return SkToBool(this->textureProxyView());
94 }
95
textureProxyView() const96 skgpu::graphite::TextureProxyView SkSpecialImage::textureProxyView() const {
97 return this->onTextureProxyView();
98 }
99
onTextureProxyView() const100 skgpu::graphite::TextureProxyView SkSpecialImage::onTextureProxyView() const {
101 // To get here we would need to be trying to retrieve a Graphite-backed resource from
102 // either a raster or Ganesh-backed special image. That should never happen.
103 // TODO: re-enable this assert. Right now, since image filters can fallback to raster
104 // in Graphite, we can get here.
105 //SkASSERT(false);
106 return {};
107 }
108 #endif
109
110 #ifdef SK_DEBUG
RectFits(const SkIRect & rect,int width,int height)111 bool SkSpecialImage::RectFits(const SkIRect& rect, int width, int height) {
112 if (0 == width && 0 == height) {
113 SkASSERT(0 == rect.fLeft && 0 == rect.fRight && 0 == rect.fTop && 0 == rect.fBottom);
114 return true;
115 }
116
117 return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight &&
118 rect.fRight >= 0 && rect.fRight <= width &&
119 rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom &&
120 rect.fBottom >= 0 && rect.fBottom <= height;
121 }
122 #endif
123
MakeFromImage(GrRecordingContext * rContext,const SkIRect & subset,sk_sp<SkImage> image,const SkSurfaceProps & props)124 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(GrRecordingContext* rContext,
125 const SkIRect& subset,
126 sk_sp<SkImage> image,
127 const SkSurfaceProps& props) {
128 SkASSERT(RectFits(subset, image->width(), image->height()));
129
130 #if defined(SK_GANESH)
131 if (rContext) {
132 auto [view, ct] = as_IB(image)->asView(rContext, GrMipmapped::kNo);
133 return MakeDeferredFromGpu(rContext,
134 subset,
135 image->uniqueID(),
136 std::move(view),
137 { ct, image->alphaType(), image->refColorSpace() },
138 props);
139 }
140 #endif
141
142 // raster to gpu is supported here, but gpu to raster is not
143 SkBitmap bm;
144 if (as_IB(image)->getROPixels(nullptr, &bm)) {
145 return MakeFromRaster(subset, bm, props);
146 }
147 return nullptr;
148 }
149
150 ///////////////////////////////////////////////////////////////////////////////
151
152 class SkSpecialImage_Raster final : public SkSpecialImage {
153 public:
SkSpecialImage_Raster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)154 SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps& props)
155 : SkSpecialImage(subset, bm.getGenerationID(), bm.info().colorInfo(), props)
156 , fBitmap(bm) {
157 SkASSERT(bm.pixelRef());
158 SkASSERT(fBitmap.getPixels());
159 }
160
getSize() const161 size_t getSize() const override { return fBitmap.computeByteSize(); }
162
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint) const163 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling,
164 const SkPaint* paint) const override {
165 SkRect dst = SkRect::MakeXYWH(x, y,
166 this->subset().width(), this->subset().height());
167
168 canvas->drawImageRect(fBitmap.asImage(), SkRect::Make(this->subset()), dst,
169 sampling, paint, SkCanvas::kStrict_SrcRectConstraint);
170 }
171
onGetROPixels(SkBitmap * bm) const172 bool onGetROPixels(SkBitmap* bm) const override {
173 return fBitmap.extractSubset(bm, this->subset());
174 }
175
176 #if defined(SK_GANESH)
onView(GrRecordingContext * context) const177 GrSurfaceProxyView onView(GrRecordingContext* context) const override {
178 if (context) {
179 return std::get<0>(GrMakeCachedBitmapProxyView(
180 context, fBitmap, /*label=*/"SpecialImageRaster_OnView", GrMipmapped::kNo));
181 }
182
183 return {};
184 }
185 #endif
186
onMakeSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at,const SkSurfaceProps & props) const187 sk_sp<SkSpecialSurface> onMakeSurface(SkColorType colorType, const SkColorSpace* colorSpace,
188 const SkISize& size, SkAlphaType at,
189 const SkSurfaceProps& props) const override {
190 // Ignore the requested color type, the raster backend currently only supports N32
191 colorType = kN32_SkColorType; // TODO: find ways to allow f16
192 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
193 return SkSpecialSurface::MakeRaster(info, props);
194 }
195
onMakeSubset(const SkIRect & subset) const196 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
197 // No need to extract subset, onGetROPixels handles that when needed
198 return SkSpecialImage::MakeFromRaster(subset, fBitmap, this->props());
199 }
200
onAsImage(const SkIRect * subset) const201 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
202 if (subset) {
203 SkBitmap subsetBM;
204
205 if (!fBitmap.extractSubset(&subsetBM, *subset)) {
206 return nullptr;
207 }
208
209 return subsetBM.asImage();
210 }
211
212 return fBitmap.asImage();
213 }
214
onAsShader(SkTileMode tileMode,const SkSamplingOptions & sampling,const SkMatrix & lm) const215 sk_sp<SkShader> onAsShader(SkTileMode tileMode,
216 const SkSamplingOptions& sampling,
217 const SkMatrix& lm) const override {
218 // TODO(skbug.com/12784): SkImage::makeShader() doesn't support a subset yet, but SkBitmap
219 // supports subset views so create the shader from the subset bitmap instead of fBitmap.
220 SkBitmap subsetBM;
221 if (!this->getROPixels(&subsetBM)) {
222 return nullptr;
223 }
224 return subsetBM.asImage()->makeShader(tileMode, tileMode, sampling, lm);
225 }
226
onMakeTightSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at) const227 sk_sp<SkSurface> onMakeTightSurface(SkColorType colorType, const SkColorSpace* colorSpace,
228 const SkISize& size, SkAlphaType at) const override {
229 // Ignore the requested color type, the raster backend currently only supports N32
230 colorType = kN32_SkColorType; // TODO: find ways to allow f16
231 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
232 return SkSurface::MakeRaster(info);
233 }
234
235 private:
236 SkBitmap fBitmap;
237 };
238
MakeFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)239 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromRaster(const SkIRect& subset,
240 const SkBitmap& bm,
241 const SkSurfaceProps& props) {
242 SkASSERT(RectFits(subset, bm.width(), bm.height()));
243
244 if (!bm.pixelRef()) {
245 return nullptr;
246 }
247
248 const SkBitmap* srcBM = &bm;
249 SkBitmap tmp;
250 // ImageFilters only handle N32 at the moment, so force our src to be that
251 if (!valid_for_imagefilters(bm.info())) {
252 if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
253 !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
254 {
255 return nullptr;
256 }
257 srcBM = &tmp;
258 }
259 return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
260 }
261
CopyFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)262 sk_sp<SkSpecialImage> SkSpecialImage::CopyFromRaster(const SkIRect& subset,
263 const SkBitmap& bm,
264 const SkSurfaceProps& props) {
265 SkASSERT(RectFits(subset, bm.width(), bm.height()));
266
267 if (!bm.pixelRef()) {
268 return nullptr;
269 }
270
271 SkBitmap tmp;
272 SkImageInfo info = bm.info().makeDimensions(subset.size());
273 // As in MakeFromRaster, must force src to N32 for ImageFilters
274 if (!valid_for_imagefilters(bm.info())) {
275 info = info.makeColorType(kN32_SkColorType);
276 }
277 if (!tmp.tryAllocPixels(info)) {
278 return nullptr;
279 }
280 if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) {
281 return nullptr;
282 }
283
284 // Since we're making a copy of the raster, the resulting special image is the exact size
285 // of the requested subset of the original and no longer needs to be offset by subset's left
286 // and top, since those were relative to the original's buffer.
287 return sk_make_sp<SkSpecialImage_Raster>(
288 SkIRect::MakeWH(subset.width(), subset.height()), tmp, props);
289 }
290
291 #if defined(SK_GANESH)
292 ///////////////////////////////////////////////////////////////////////////////
wrap_proxy_in_image(GrRecordingContext * context,GrSurfaceProxyView view,const SkColorInfo & colorInfo)293 static sk_sp<SkImage> wrap_proxy_in_image(GrRecordingContext* context,
294 GrSurfaceProxyView view,
295 const SkColorInfo& colorInfo) {
296
297 return sk_make_sp<SkImage_Gpu>(sk_ref_sp(context),
298 kNeedNewImageUniqueID,
299 std::move(view),
300 colorInfo);
301 }
302
303 class SkSpecialImage_Gpu final : public SkSpecialImage {
304 public:
SkSpecialImage_Gpu(GrRecordingContext * context,const SkIRect & subset,uint32_t uniqueID,GrSurfaceProxyView view,const SkColorInfo & colorInfo,const SkSurfaceProps & props)305 SkSpecialImage_Gpu(GrRecordingContext* context,
306 const SkIRect& subset,
307 uint32_t uniqueID,
308 GrSurfaceProxyView view,
309 const SkColorInfo& colorInfo,
310 const SkSurfaceProps& props)
311 : SkSpecialImage(subset, uniqueID, colorInfo, props)
312 , fContext(context)
313 , fView(std::move(view)) {
314 }
315
getSize() const316 size_t getSize() const override {
317 return fView.proxy()->gpuMemorySize();
318 }
319
onDraw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint) const320 void onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling,
321 const SkPaint* paint) const override {
322 SkRect dst = SkRect::MakeXYWH(x, y,
323 this->subset().width(), this->subset().height());
324
325 // TODO: In this instance we know we're going to draw a sub-portion of the backing
326 // texture into the canvas so it is okay to wrap it in an SkImage. This poses
327 // some problems for full deferral however in that when the deferred SkImage_Gpu
328 // instantiates itself it is going to have to either be okay with having a larger
329 // than expected backing texture (unlikely) or the 'fit' of the SurfaceProxy needs
330 // to be tightened (if it is deferred).
331 sk_sp<SkImage> img = sk_sp<SkImage>(
332 new SkImage_Gpu(sk_ref_sp(canvas->recordingContext()),
333 this->uniqueID(),
334 fView,
335 this->colorInfo()));
336
337 canvas->drawImageRect(img, SkRect::Make(this->subset()), dst,
338 sampling, paint, SkCanvas::kStrict_SrcRectConstraint);
339 }
340
onGetContext() const341 GrRecordingContext* onGetContext() const override { return fContext; }
342
onView(GrRecordingContext * context) const343 GrSurfaceProxyView onView(GrRecordingContext* context) const override { return fView; }
344
onGetROPixels(SkBitmap * dst) const345 bool onGetROPixels(SkBitmap* dst) const override {
346 // This should never be called: All GPU image filters are implemented entirely on the GPU,
347 // so we never perform read-back.
348 SkASSERT(false);
349 return false;
350 }
351
onMakeSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at,const SkSurfaceProps & props) const352 sk_sp<SkSpecialSurface> onMakeSurface(SkColorType colorType, const SkColorSpace* colorSpace,
353 const SkISize& size, SkAlphaType at,
354 const SkSurfaceProps& props) const override {
355 if (!fContext) {
356 return nullptr;
357 }
358
359 SkImageInfo ii = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
360
361 return SkSpecialSurface::MakeRenderTarget(fContext, ii, props, fView.origin());
362 }
363
onMakeSubset(const SkIRect & subset) const364 sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
365 return SkSpecialImage::MakeDeferredFromGpu(fContext,
366 subset,
367 this->uniqueID(),
368 fView,
369 this->colorInfo(),
370 this->props());
371 }
372
onAsImage(const SkIRect * subset) const373 sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
374 GrSurfaceProxy* proxy = fView.proxy();
375 if (subset) {
376 if (proxy->isFunctionallyExact() && *subset == SkIRect::MakeSize(proxy->dimensions())) {
377 proxy->priv().exactify(false);
378 // The existing GrTexture is already tight so reuse it in the SkImage
379 return wrap_proxy_in_image(fContext, fView, this->colorInfo());
380 }
381
382 auto subsetView = GrSurfaceProxyView::Copy(fContext,
383 fView,
384 GrMipmapped::kNo,
385 *subset,
386 SkBackingFit::kExact,
387 skgpu::Budgeted::kYes,
388 /*label=*/"SkSpecialImage_AsImage");
389 if (!subsetView) {
390 return nullptr;
391 }
392 SkASSERT(subsetView.asTextureProxy());
393 SkASSERT(subsetView.proxy()->priv().isExact());
394
395 // MDB: this is acceptable (wrapping subsetProxy in an SkImage) bc Copy will
396 // return a kExact-backed proxy
397 return wrap_proxy_in_image(fContext, std::move(subsetView), this->colorInfo());
398 }
399
400 proxy->priv().exactify(true);
401
402 return wrap_proxy_in_image(fContext, fView, this->colorInfo());
403 }
404
onAsShader(SkTileMode tileMode,const SkSamplingOptions & sampling,const SkMatrix & lm) const405 sk_sp<SkShader> onAsShader(SkTileMode tileMode,
406 const SkSamplingOptions& sampling,
407 const SkMatrix& lm) const override {
408 // The special image's logical (0,0) is at its subset's topLeft() so we need to account for
409 // that in the local matrix used when sampling.
410 SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
411 subsetOrigin.postConcat(lm);
412 // However, we don't need to modify the subset itself since that is defined with respect to
413 // the base image, and the local matrix is applied before any tiling/clamping.
414 const SkRect subset = SkRect::Make(this->subset());
415
416 // asImage() w/o a subset makes no copy; create the SkImageShader directly to remember the
417 // subset used to access the image.
418 return SkImageShader::MakeSubset(
419 this->asImage(), subset, tileMode, tileMode, sampling, &subsetOrigin);
420 }
421
onMakeTightSurface(SkColorType colorType,const SkColorSpace * colorSpace,const SkISize & size,SkAlphaType at) const422 sk_sp<SkSurface> onMakeTightSurface(SkColorType colorType, const SkColorSpace* colorSpace,
423 const SkISize& size, SkAlphaType at) const override {
424 // TODO (michaelludwig): Why does this ignore colorType but onMakeSurface doesn't ignore it?
425 // Once makeTightSurface() goes away, should this type overriding behavior be moved into
426 // onMakeSurface() or is this unnecessary?
427 colorType = colorSpace && colorSpace->gammaIsLinear()
428 ? kRGBA_F16_SkColorType : kRGBA_8888_SkColorType;
429 SkImageInfo info = SkImageInfo::Make(size, colorType, at, sk_ref_sp(colorSpace));
430 return SkSurface::MakeRenderTarget(
431 fContext, skgpu::Budgeted::kYes, info, 0, fView.origin(), nullptr);
432 }
433
434 private:
435 GrRecordingContext* fContext;
436 GrSurfaceProxyView fView;
437 };
438
MakeDeferredFromGpu(GrRecordingContext * context,const SkIRect & subset,uint32_t uniqueID,GrSurfaceProxyView view,const GrColorInfo & colorInfo,const SkSurfaceProps & props)439 sk_sp<SkSpecialImage> SkSpecialImage::MakeDeferredFromGpu(GrRecordingContext* context,
440 const SkIRect& subset,
441 uint32_t uniqueID,
442 GrSurfaceProxyView view,
443 const GrColorInfo& colorInfo,
444 const SkSurfaceProps& props) {
445 if (!context || context->abandoned() || !view.asTextureProxy()) {
446 return nullptr;
447 }
448
449 SkColorType ct = GrColorTypeToSkColorType(colorInfo.colorType());
450
451 SkASSERT(RectFits(subset, view.proxy()->width(), view.proxy()->height()));
452 return sk_make_sp<SkSpecialImage_Gpu>(context, subset, uniqueID, std::move(view),
453 SkColorInfo(ct,
454 colorInfo.alphaType(),
455 colorInfo.refColorSpace()),
456 props);
457 }
458 #endif
459