• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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