xref: /aosp_15_r20/external/skia/src/core/SkSpecialImage.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkColorType.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkPoint.h"
16 #include "include/core/SkShader.h"
17 #include "include/private/base/SkAssert.h"
18 #include "src/core/SkNextID.h"
19 #include "src/image/SkImage_Base.h"
20 #include "src/shaders/SkImageShader.h"
21 
22 // Currently, the raster imagefilters can only handle certain imageinfos. Call this to know if
23 // a given info is supported.
valid_for_imagefilters(const SkImageInfo & info)24 static bool valid_for_imagefilters(const SkImageInfo& info) {
25     // no support for other swizzles/depths yet
26     return info.colorType() == kN32_SkColorType;
27 }
28 
SkSpecialImage(const SkIRect & subset,uint32_t uniqueID,const SkColorInfo & colorInfo,const SkSurfaceProps & props)29 SkSpecialImage::SkSpecialImage(const SkIRect& subset,
30                                uint32_t uniqueID,
31                                const SkColorInfo& colorInfo,
32                                const SkSurfaceProps& props)
33     : fSubset(subset)
34     , fUniqueID(kNeedNewImageUniqueID_SpecialImage == uniqueID ? SkNextID::ImageID() : uniqueID)
35     , fColorInfo(colorInfo)
36     , fProps(props) {
37 }
38 
draw(SkCanvas * canvas,SkScalar x,SkScalar y,const SkSamplingOptions & sampling,const SkPaint * paint,bool strict) const39 void SkSpecialImage::draw(SkCanvas* canvas,
40                           SkScalar x, SkScalar y,
41                           const SkSamplingOptions& sampling,
42                           const SkPaint* paint, bool strict) const {
43     SkRect dst = SkRect::MakeXYWH(x, y, this->subset().width(), this->subset().height());
44 
45     canvas->drawImageRect(this->asImage(), SkRect::Make(this->subset()), dst,
46                           sampling, paint, strict ? SkCanvas::kStrict_SrcRectConstraint
47                                                   : SkCanvas::kFast_SrcRectConstraint);
48 }
49 
50 // TODO(skbug.com/12784): Once bitmap images work with SkImageShader::MakeSubset(), this does not
51 // need to be virtual anymore.
asShader(SkTileMode tileMode,const SkSamplingOptions & sampling,const SkMatrix & lm,bool strict) const52 sk_sp<SkShader> SkSpecialImage::asShader(SkTileMode tileMode,
53                                          const SkSamplingOptions& sampling,
54                                          const SkMatrix& lm,
55                                          bool strict) const {
56     // The special image's logical (0,0) is at its subset's topLeft() so we need to account for
57     // that in the local matrix used when sampling.
58     SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
59     subsetOrigin.postConcat(lm);
60 
61     if (strict) {
62         // However, we don't need to modify the subset itself since that is defined with respect
63         // to the base image, and the local matrix is applied before any tiling/clamping.
64         const SkRect subset = SkRect::Make(this->subset());
65 
66         // asImage() w/o a subset makes no copy; create the SkImageShader directly to remember
67         // the subset used to access the image.
68         return SkImageShader::MakeSubset(
69                 this->asImage(), subset, tileMode, tileMode, sampling, &subsetOrigin);
70     } else {
71         // Ignore 'subset' other than its origin translation applied to the local matrix.
72         return this->asImage()->makeShader(tileMode, tileMode, sampling, subsetOrigin);
73     }
74 }
75 
76 class SkSpecialImage_Raster final : public SkSpecialImage {
77 public:
SkSpecialImage_Raster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)78     SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm, const SkSurfaceProps& props)
79             : SkSpecialImage(subset, bm.getGenerationID(), bm.info().colorInfo(), props)
80             , fBitmap(bm) {
81         SkASSERT(bm.pixelRef());
82         SkASSERT(fBitmap.getPixels());
83     }
84 
getROPixels(SkBitmap * bm) const85     bool getROPixels(SkBitmap* bm) const {
86         return fBitmap.extractSubset(bm, this->subset());
87     }
88 
backingStoreDimensions() const89     SkISize backingStoreDimensions() const override { return fBitmap.dimensions(); }
90 
getSize() const91     size_t getSize() const override { return fBitmap.computeByteSize(); }
92 
asImage() const93     sk_sp<SkImage> asImage() const override { return fBitmap.asImage(); }
94 
onMakeBackingStoreSubset(const SkIRect & subset) const95     sk_sp<SkSpecialImage> onMakeBackingStoreSubset(const SkIRect& subset) const override {
96         // No need to extract subset, onGetROPixels handles that when needed
97         return SkSpecialImages::MakeFromRaster(subset, fBitmap, this->props());
98     }
99 
asShader(SkTileMode tileMode,const SkSamplingOptions & sampling,const SkMatrix & lm,bool strict) const100     sk_sp<SkShader> asShader(SkTileMode tileMode,
101                              const SkSamplingOptions& sampling,
102                              const SkMatrix& lm,
103                              bool strict) const override {
104         if (strict) {
105             // TODO(skbug.com/12784): SkImage::makeShader() doesn't support a subset yet, but
106             // SkBitmap supports subset views so create the shader from the subset bitmap instead of
107             // fBitmap.
108             SkBitmap subsetBM;
109             if (!this->getROPixels(&subsetBM)) {
110                 return nullptr;
111             }
112             return subsetBM.makeShader(tileMode, tileMode, sampling, lm);
113         } else {
114             // The special image's logical (0,0) is at its subset's topLeft() so we need to
115             // account for that in the local matrix used when sampling.
116             SkMatrix subsetOrigin = SkMatrix::Translate(-this->subset().topLeft());
117             subsetOrigin.postConcat(lm);
118             return fBitmap.makeShader(tileMode, tileMode, sampling, subsetOrigin);
119         }
120     }
121 
122 private:
123     SkBitmap fBitmap;
124 };
125 
126 namespace SkSpecialImages {
127 
MakeFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)128 sk_sp<SkSpecialImage> MakeFromRaster(const SkIRect& subset,
129                                      const SkBitmap& bm,
130                                      const SkSurfaceProps& props) {
131     SkASSERT(bm.bounds().contains(subset));
132 
133     if (!bm.pixelRef()) {
134         return nullptr;
135     }
136 
137     const SkBitmap* srcBM = &bm;
138     SkBitmap tmp;
139     // ImageFilters only handle N32 at the moment, so force our src to be that
140     if (!valid_for_imagefilters(bm.info())) {
141         if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) ||
142             !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0))
143         {
144             return nullptr;
145         }
146         srcBM = &tmp;
147     }
148     return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props);
149 }
150 
CopyFromRaster(const SkIRect & subset,const SkBitmap & bm,const SkSurfaceProps & props)151 sk_sp<SkSpecialImage> CopyFromRaster(const SkIRect& subset,
152                                      const SkBitmap& bm,
153                                      const SkSurfaceProps& props) {
154     SkASSERT(bm.bounds().contains(subset));
155 
156     if (!bm.pixelRef()) {
157         return nullptr;
158     }
159 
160     SkBitmap tmp;
161     SkImageInfo info = bm.info().makeDimensions(subset.size());
162     // As in MakeFromRaster, must force src to N32 for ImageFilters
163     if (!valid_for_imagefilters(bm.info())) {
164         info = info.makeColorType(kN32_SkColorType);
165     }
166     if (!tmp.tryAllocPixels(info)) {
167         return nullptr;
168     }
169     if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) {
170         return nullptr;
171     }
172 
173     // Since we're making a copy of the raster, the resulting special image is the exact size
174     // of the requested subset of the original and no longer needs to be offset by subset's left
175     // and top, since those were relative to the original's buffer.
176     return sk_make_sp<SkSpecialImage_Raster>(
177             SkIRect::MakeWH(subset.width(), subset.height()), tmp, props);
178 }
179 
MakeFromRaster(const SkIRect & subset,sk_sp<SkImage> image,const SkSurfaceProps & props)180 sk_sp<SkSpecialImage> MakeFromRaster(const SkIRect& subset,
181                                      sk_sp<SkImage> image,
182                                      const SkSurfaceProps& props) {
183     if (!image || subset.isEmpty()) {
184         return nullptr;
185     }
186 
187     SkASSERT(image->bounds().contains(subset));
188     SkASSERT(!image->isTextureBacked());
189 
190     // This will not work if the image is uploaded to a GPU render target.
191     SkBitmap bm;
192     if (as_IB(image)->getROPixels(nullptr, &bm)) {
193         return MakeFromRaster(subset, bm, props);
194     }
195     return nullptr;
196 }
197 
AsBitmap(const SkSpecialImage * img,SkBitmap * result)198 bool AsBitmap(const SkSpecialImage* img, SkBitmap* result) {
199     if (!img || img->isGaneshBacked() || img->isGraphiteBacked()) {
200         return false;
201     }
202     auto rasterImg = static_cast<const SkSpecialImage_Raster*>(img);
203     return rasterImg->getROPixels(result);
204 }
205 
206 }  // namespace SkSpecialImages
207