1 /*
2 * Copyright 2015 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 "SkArenaAlloc.h"
9 #include "SkBitmapController.h"
10 #include "SkBitmapProcShader.h"
11 #include "SkBitmapProvider.h"
12 #include "SkColorTable.h"
13 #include "SkEmptyShader.h"
14 #include "SkImage_Base.h"
15 #include "SkImageShader.h"
16 #include "SkImageShaderContext.h"
17 #include "SkPM4fPriv.h"
18 #include "SkReadBuffer.h"
19 #include "SkWriteBuffer.h"
20
SkImageShader(sk_sp<SkImage> img,TileMode tmx,TileMode tmy,const SkMatrix * matrix)21 SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
22 : INHERITED(matrix)
23 , fImage(std::move(img))
24 , fTileModeX(tmx)
25 , fTileModeY(tmy)
26 {}
27
CreateProc(SkReadBuffer & buffer)28 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
29 const TileMode tx = (TileMode)buffer.readUInt();
30 const TileMode ty = (TileMode)buffer.readUInt();
31 SkMatrix matrix;
32 buffer.readMatrix(&matrix);
33 sk_sp<SkImage> img = buffer.readImage();
34 if (!img) {
35 return nullptr;
36 }
37 return SkImageShader::Make(std::move(img), tx, ty, &matrix);
38 }
39
flatten(SkWriteBuffer & buffer) const40 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
41 buffer.writeUInt(fTileModeX);
42 buffer.writeUInt(fTileModeY);
43 buffer.writeMatrix(this->getLocalMatrix());
44 buffer.writeImage(fImage.get());
45 }
46
isOpaque() const47 bool SkImageShader::isOpaque() const {
48 return fImage->isOpaque();
49 }
50
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const51 SkShader::Context* SkImageShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
52 return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
53 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
54 rec, alloc);
55 }
56
onIsAImage(SkMatrix * texM,TileMode xy[]) const57 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
58 if (texM) {
59 *texM = this->getLocalMatrix();
60 }
61 if (xy) {
62 xy[0] = (TileMode)fTileModeX;
63 xy[1] = (TileMode)fTileModeY;
64 }
65 return const_cast<SkImage*>(fImage.get());
66 }
67
68 #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
onIsABitmap(SkBitmap * texture,SkMatrix * texM,TileMode xy[]) const69 bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
70 const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
71 if (!bm) {
72 return false;
73 }
74
75 if (texture) {
76 *texture = *bm;
77 }
78 if (texM) {
79 *texM = this->getLocalMatrix();
80 }
81 if (xy) {
82 xy[0] = (TileMode)fTileModeX;
83 xy[1] = (TileMode)fTileModeY;
84 }
85 return true;
86 }
87 #endif
88
bitmap_is_too_big(int w,int h)89 static bool bitmap_is_too_big(int w, int h) {
90 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
91 // communicates between its matrix-proc and its sampler-proc. Until we can
92 // widen that, we have to reject bitmaps that are larger.
93 //
94 static const int kMaxSize = 65535;
95
96 return w > kMaxSize || h > kMaxSize;
97 }
98
Make(sk_sp<SkImage> image,TileMode tx,TileMode ty,const SkMatrix * localMatrix)99 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
100 const SkMatrix* localMatrix) {
101 if (!image || bitmap_is_too_big(image->width(), image->height())) {
102 return sk_make_sp<SkEmptyShader>();
103 } else {
104 return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix);
105 }
106 }
107
108 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const109 void SkImageShader::toString(SkString* str) const {
110 const char* gTileModeName[SkShader::kTileModeCount] = {
111 "clamp", "repeat", "mirror"
112 };
113
114 str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
115 fImage->toString(str);
116 this->INHERITED::toString(str);
117 str->append(")");
118 }
119 #endif
120
121 ///////////////////////////////////////////////////////////////////////////////////////////////////
122
123 #if SK_SUPPORT_GPU
124
125 #include "SkGr.h"
126 #include "GrContext.h"
127 #include "effects/GrSimpleTextureEffect.h"
128 #include "effects/GrBicubicEffect.h"
129 #include "effects/GrSimpleTextureEffect.h"
130
asFragmentProcessor(const AsFPArgs & args) const131 sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const {
132
133 SkMatrix lmInverse;
134 if (!this->getLocalMatrix().invert(&lmInverse)) {
135 return nullptr;
136 }
137 if (args.fLocalMatrix) {
138 SkMatrix inv;
139 if (!args.fLocalMatrix->invert(&inv)) {
140 return nullptr;
141 }
142 lmInverse.postConcat(inv);
143 }
144
145 SkShader::TileMode tm[] = { fTileModeX, fTileModeY };
146
147 // Must set wrap and filter on the sampler before requesting a texture. In two places below
148 // we check the matrix scale factors to determine how to interpret the filter quality setting.
149 // This completely ignores the complexity of the drawVertices case where explicit local coords
150 // are provided by the caller.
151 bool doBicubic;
152 GrSamplerParams::FilterMode textureFilterMode =
153 GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(),
154 &doBicubic);
155 GrSamplerParams params(tm, textureFilterMode);
156 sk_sp<SkColorSpace> texColorSpace;
157 SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
158 sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params,
159 args.fDstColorSpace,
160 &texColorSpace, scaleAdjust));
161 if (!proxy) {
162 return nullptr;
163 }
164
165 bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
166
167 lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
168
169 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
170 args.fDstColorSpace);
171 sk_sp<GrFragmentProcessor> inner;
172 if (doBicubic) {
173 inner = GrBicubicEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
174 std::move(colorSpaceXform), lmInverse, tm);
175 } else {
176 inner = GrSimpleTextureEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
177 std::move(colorSpaceXform), lmInverse, params);
178 }
179
180 if (isAlphaOnly) {
181 return inner;
182 }
183 return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)));
184 }
185
186 #endif
187
188 ///////////////////////////////////////////////////////////////////////////////////////////////////
189 #include "SkImagePriv.h"
190
SkMakeBitmapShader(const SkBitmap & src,SkShader::TileMode tmx,SkShader::TileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode cpm)191 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
192 SkShader::TileMode tmy, const SkMatrix* localMatrix,
193 SkCopyPixelsMode cpm) {
194 return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
195 tmx, tmy, localMatrix);
196 }
197
SkBitmapProcShader_CreateProc(SkReadBuffer & buffer)198 static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
199 SkMatrix lm;
200 buffer.readMatrix(&lm);
201 sk_sp<SkImage> image = buffer.readBitmapAsImage();
202 SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
203 SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
204 return image ? image->makeShader(mx, my, &lm) : nullptr;
205 }
206
207 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShader)
208 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
209 SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShader_Type);
210 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
211
212
onAppendStages(SkRasterPipeline * p,SkColorSpace * dst,SkArenaAlloc * scratch,const SkMatrix & ctm,const SkPaint & paint,const SkMatrix * localM) const213 bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkArenaAlloc* scratch,
214 const SkMatrix& ctm, const SkPaint& paint,
215 const SkMatrix* localM) const {
216 auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix());
217 if (localM) {
218 matrix.preConcat(*localM);
219 }
220
221 if (!matrix.invert(&matrix)) {
222 return false;
223 }
224 auto quality = paint.getFilterQuality();
225
226 SkBitmapProvider provider(fImage.get(), dst);
227 SkDefaultBitmapController controller(SkDefaultBitmapController::CanShadeHQ::kYes);
228 std::unique_ptr<SkBitmapController::State> state {
229 controller.requestBitmap(provider, matrix, quality)
230 };
231 if (!state) {
232 return false;
233 }
234
235 const SkPixmap& pm = state->pixmap();
236 matrix = state->invMatrix();
237 quality = state->quality();
238 auto info = pm.info();
239
240 // When the matrix is just an integer translate, bilerp == nearest neighbor.
241 if (quality == kLow_SkFilterQuality &&
242 matrix.getType() <= SkMatrix::kTranslate_Mask &&
243 matrix.getTranslateX() == (int)matrix.getTranslateX() &&
244 matrix.getTranslateY() == (int)matrix.getTranslateY()) {
245 quality = kNone_SkFilterQuality;
246 }
247
248 // See skia:4649 and the GM image_scale_aligned.
249 if (quality == kNone_SkFilterQuality) {
250 if (matrix.getScaleX() >= 0) {
251 matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
252 floorf(matrix.getTranslateX())));
253 }
254 if (matrix.getScaleY() >= 0) {
255 matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
256 floorf(matrix.getTranslateY())));
257 }
258 }
259
260 auto ctx = scratch->make<SkImageShaderContext>();
261 ctx->state = std::move(state); // Extend lifetime to match the pipeline's.
262 ctx->pixels = pm.addr();
263 ctx->ctable = pm.ctable();
264 ctx->color4f = SkColor4f_from_SkColor(paint.getColor(), dst);
265 ctx->stride = pm.rowBytesAsPixels();
266 ctx->width = (float)pm.width();
267 ctx->height = (float)pm.height();
268 if (matrix.asAffine(ctx->matrix)) {
269 p->append(SkRasterPipeline::matrix_2x3, ctx->matrix);
270 } else {
271 matrix.get9(ctx->matrix);
272 p->append(SkRasterPipeline::matrix_perspective, ctx->matrix);
273 }
274
275 auto append_tiling_and_gather = [&] {
276 switch (fTileModeX) {
277 case kClamp_TileMode: p->append(SkRasterPipeline::clamp_x, &ctx->width); break;
278 case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, &ctx->width); break;
279 case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, &ctx->width); break;
280 }
281 switch (fTileModeY) {
282 case kClamp_TileMode: p->append(SkRasterPipeline::clamp_y, &ctx->height); break;
283 case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, &ctx->height); break;
284 case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, &ctx->height); break;
285 }
286 switch (info.colorType()) {
287 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break;
288 case kIndex_8_SkColorType: p->append(SkRasterPipeline::gather_i8, ctx); break;
289 case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_g8, ctx); break;
290 case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, ctx); break;
291 case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
292 case kRGBA_8888_SkColorType:
293 case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
294 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, ctx); break;
295 default: SkASSERT(false);
296 }
297 if (info.gammaCloseToSRGB() && dst != nullptr) {
298 p->append_from_srgb(info.alphaType());
299 }
300 };
301
302 auto sample = [&](SkRasterPipeline::StockStage setup_x,
303 SkRasterPipeline::StockStage setup_y) {
304 p->append(setup_x, ctx);
305 p->append(setup_y, ctx);
306 append_tiling_and_gather();
307 p->append(SkRasterPipeline::accumulate, ctx);
308 };
309
310 if (quality == kNone_SkFilterQuality) {
311 append_tiling_and_gather();
312 } else if (quality == kLow_SkFilterQuality) {
313 p->append(SkRasterPipeline::save_xy, ctx);
314
315 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
316 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
317 sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
318 sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
319
320 p->append(SkRasterPipeline::move_dst_src);
321 } else {
322 p->append(SkRasterPipeline::save_xy, ctx);
323
324 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
325 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
326 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
327 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
328
329 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
330 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
331 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
332 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
333
334 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
335 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
336 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
337 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
338
339 sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
340 sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
341 sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
342 sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
343
344 p->append(SkRasterPipeline::move_dst_src);
345 }
346
347 auto effective_color_type = [](SkColorType ct) {
348 return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct;
349 };
350
351 if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) {
352 p->append(SkRasterPipeline::swap_rb);
353 }
354 if (info.colorType() == kAlpha_8_SkColorType) {
355 p->append(SkRasterPipeline::set_rgb, &ctx->color4f);
356 }
357 if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
358 p->append(SkRasterPipeline::premul);
359 }
360 if (quality > kLow_SkFilterQuality) {
361 // Bicubic filtering naturally produces out of range values on both sides.
362 p->append(SkRasterPipeline::clamp_0);
363 p->append(SkRasterPipeline::clamp_a);
364 }
365 return append_gamut_transform(p, scratch, info.colorSpace(), dst);
366 }
367