• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkColorSpacePriv.h"
13 #include "SkColorSpaceXformSteps.h"
14 #include "SkEmptyShader.h"
15 #include "SkImage_Base.h"
16 #include "SkImageShader.h"
17 #include "SkReadBuffer.h"
18 #include "SkWriteBuffer.h"
19 
20 /**
21  *  We are faster in clamp, so always use that tiling when we can.
22  */
optimize(SkShader::TileMode tm,int dimension)23 static SkShader::TileMode optimize(SkShader::TileMode tm, int dimension) {
24     SkASSERT(dimension > 0);
25 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
26     // need to update frameworks/base/libs/hwui/tests/unit/SkiaBehaviorTests.cpp:55 to allow
27     // for transforming to clamp.
28     return tm;
29 #else
30     return dimension == 1 ? SkShader::kClamp_TileMode : tm;
31 #endif
32 }
33 
SkImageShader(sk_sp<SkImage> img,TileMode tmx,TileMode tmy,const SkMatrix * localMatrix,bool clampAsIfUnpremul)34 SkImageShader::SkImageShader(sk_sp<SkImage> img,
35                              TileMode tmx, TileMode tmy,
36                              const SkMatrix* localMatrix,
37                              bool clampAsIfUnpremul)
38     : INHERITED(localMatrix)
39     , fImage(std::move(img))
40     , fTileModeX(optimize(tmx, fImage->width()))
41     , fTileModeY(optimize(tmy, fImage->height()))
42     , fClampAsIfUnpremul(clampAsIfUnpremul)
43 {}
44 
45 // fClampAsIfUnpremul is always false when constructed through public APIs,
46 // so there's no need to read or write it here.
47 
CreateProc(SkReadBuffer & buffer)48 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
49     const TileMode tx = (TileMode)buffer.readUInt();
50     const TileMode ty = (TileMode)buffer.readUInt();
51     SkMatrix localMatrix;
52     buffer.readMatrix(&localMatrix);
53     sk_sp<SkImage> img = buffer.readImage();
54     if (!img) {
55         return nullptr;
56     }
57     return SkImageShader::Make(std::move(img), tx, ty, &localMatrix);
58 }
59 
flatten(SkWriteBuffer & buffer) const60 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
61     buffer.writeUInt(fTileModeX);
62     buffer.writeUInt(fTileModeY);
63     buffer.writeMatrix(this->getLocalMatrix());
64     buffer.writeImage(fImage.get());
65     SkASSERT(fClampAsIfUnpremul == false);
66 }
67 
isOpaque() const68 bool SkImageShader::isOpaque() const {
69     return fImage->isOpaque() && fTileModeX != kDecal_TileMode && fTileModeY != kDecal_TileMode;
70 }
71 
72 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
legacy_shader_can_handle(const SkMatrix & inv)73 static bool legacy_shader_can_handle(const SkMatrix& inv) {
74     if (!inv.isScaleTranslate()) {
75         return false;
76     }
77 
78     // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
79     // out of range.
80     const SkScalar max_dev_coord = 32767.0f;
81     SkRect src;
82     SkAssertResult(inv.mapRect(&src, SkRect::MakeWH(max_dev_coord, max_dev_coord)));
83 
84     // take 1/4 of max signed 32bits so we have room to subtract local values
85     const SkScalar max_fixed32dot32 = SK_MaxS32 * 0.25f;
86     if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
87                            max_fixed32dot32, max_fixed32dot32).contains(src)) {
88         return false;
89     }
90 
91     // legacy shader impl should be able to handle these matrices
92     return true;
93 }
94 
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const95 SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
96                                                     SkArenaAlloc* alloc) const {
97     if (fImage->alphaType() == kUnpremul_SkAlphaType) {
98         return nullptr;
99     }
100     if (fImage->colorType() != kN32_SkColorType) {
101         return nullptr;
102     }
103     if (fTileModeX != fTileModeY) {
104         return nullptr;
105     }
106     if (fTileModeX == kDecal_TileMode || fTileModeY == kDecal_TileMode) {
107         return nullptr;
108     }
109 
110     // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer,
111     // so it can't handle bitmaps larger than 65535.
112     //
113     // We back off another bit to 32767 to make small amounts of
114     // intermediate math safe, e.g. in
115     //
116     //     SkFixed fx = ...;
117     //     fx = tile(fx + SK_Fixed1);
118     //
119     // we want to make sure (fx + SK_Fixed1) never overflows.
120     if (fImage-> width() > 32767 ||
121         fImage->height() > 32767) {
122         return nullptr;
123     }
124 
125     SkMatrix inv;
126     if (!this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &inv) ||
127         !legacy_shader_can_handle(inv)) {
128         return nullptr;
129     }
130 
131     return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
132                                                  SkBitmapProvider(fImage.get()), rec, alloc);
133 }
134 #endif
135 
onIsAImage(SkMatrix * texM,TileMode xy[]) const136 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
137     if (texM) {
138         *texM = this->getLocalMatrix();
139     }
140     if (xy) {
141         xy[0] = (TileMode)fTileModeX;
142         xy[1] = (TileMode)fTileModeY;
143     }
144     return const_cast<SkImage*>(fImage.get());
145 }
146 
Make(sk_sp<SkImage> image,TileMode tx,TileMode ty,const SkMatrix * localMatrix,bool clampAsIfUnpremul)147 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
148                                     TileMode tx, TileMode ty,
149                                     const SkMatrix* localMatrix,
150                                     bool clampAsIfUnpremul) {
151     if (!image) {
152         return sk_make_sp<SkEmptyShader>();
153     }
154     return sk_sp<SkShader>{ new SkImageShader(image, tx,ty, localMatrix, clampAsIfUnpremul) };
155 }
156 
157 ///////////////////////////////////////////////////////////////////////////////////////////////////
158 
159 #if SK_SUPPORT_GPU
160 
161 #include "GrColorSpaceInfo.h"
162 #include "GrContext.h"
163 #include "GrContextPriv.h"
164 #include "SkGr.h"
165 #include "effects/GrBicubicEffect.h"
166 #include "effects/GrSimpleTextureEffect.h"
167 
tile_mode_to_wrap_mode(const SkShader::TileMode tileMode)168 static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkShader::TileMode tileMode) {
169     switch (tileMode) {
170         case SkShader::TileMode::kClamp_TileMode:
171             return GrSamplerState::WrapMode::kClamp;
172         case SkShader::TileMode::kRepeat_TileMode:
173             return GrSamplerState::WrapMode::kRepeat;
174         case SkShader::TileMode::kMirror_TileMode:
175             return GrSamplerState::WrapMode::kMirrorRepeat;
176         case SkShader::kDecal_TileMode:
177             return GrSamplerState::WrapMode::kClampToBorder;
178     }
179     SK_ABORT("Unknown tile mode.");
180     return GrSamplerState::WrapMode::kClamp;
181 }
182 
asFragmentProcessor(const GrFPArgs & args) const183 std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
184         const GrFPArgs& args) const {
185     const auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix);
186     SkMatrix lmInverse;
187     if (!lm->invert(&lmInverse)) {
188         return nullptr;
189     }
190 
191     GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
192                                             tile_mode_to_wrap_mode(fTileModeY)};
193 
194     // If either domainX or domainY are un-ignored, a texture domain effect has to be used to
195     // implement the decal mode (while leaving non-decal axes alone). The wrap mode originally
196     // clamp-to-border is reset to clamp since the hw cannot implement it directly.
197     GrTextureDomain::Mode domainX = GrTextureDomain::kIgnore_Mode;
198     GrTextureDomain::Mode domainY = GrTextureDomain::kIgnore_Mode;
199     if (!args.fContext->contextPriv().caps()->clampToBorderSupport()) {
200         if (wrapModes[0] == GrSamplerState::WrapMode::kClampToBorder) {
201             domainX = GrTextureDomain::kDecal_Mode;
202             wrapModes[0] = GrSamplerState::WrapMode::kClamp;
203         }
204         if (wrapModes[1] == GrSamplerState::WrapMode::kClampToBorder) {
205             domainY = GrTextureDomain::kDecal_Mode;
206             wrapModes[1] = GrSamplerState::WrapMode::kClamp;
207         }
208     }
209 
210     // Must set wrap and filter on the sampler before requesting a texture. In two places below
211     // we check the matrix scale factors to determine how to interpret the filter quality setting.
212     // This completely ignores the complexity of the drawVertices case where explicit local coords
213     // are provided by the caller.
214     bool doBicubic;
215     GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
216             args.fFilterQuality, *args.fViewMatrix, *lm,
217             args.fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic);
218     GrSamplerState samplerState(wrapModes, textureFilterMode);
219     SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
220     sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, samplerState,
221                                                                  scaleAdjust));
222     if (!proxy) {
223         return nullptr;
224     }
225 
226     GrPixelConfig config = proxy->config();
227     bool isAlphaOnly = GrPixelConfigIsAlphaOnly(config);
228 
229     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
230 
231     std::unique_ptr<GrFragmentProcessor> inner;
232     if (doBicubic) {
233         // domainX and domainY will properly apply the decal effect with the texture domain used in
234         // the bicubic filter if clamp to border was unsupported in hardware
235         inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY);
236     } else {
237         if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
238             SkRect domain = GrTextureDomain::MakeTexelDomain(
239                     SkIRect::MakeWH(proxy->width(), proxy->height()),
240                     domainX, domainY);
241             inner = GrTextureDomainEffect::Make(std::move(proxy), lmInverse, domain,
242                                                 domainX, domainY, samplerState);
243         } else {
244             inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
245         }
246     }
247     inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(),
248                                           fImage->alphaType(),
249                                           args.fDstColorSpaceInfo->colorSpace());
250     if (isAlphaOnly) {
251         return inner;
252     }
253     return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
254 }
255 
256 #endif
257 
258 ///////////////////////////////////////////////////////////////////////////////////////////////////
259 #include "SkImagePriv.h"
260 
SkMakeBitmapShader(const SkBitmap & src,SkShader::TileMode tmx,SkShader::TileMode tmy,const SkMatrix * localMatrix,SkCopyPixelsMode cpm)261 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
262                                    SkShader::TileMode tmy, const SkMatrix* localMatrix,
263                                    SkCopyPixelsMode cpm) {
264     return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
265                                tmx, tmy, localMatrix);
266 }
267 
RegisterFlattenables()268 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
269 
onAppendStages(const StageRec & rec) const270 bool SkImageShader::onAppendStages(const StageRec& rec) const {
271     SkRasterPipeline* p = rec.fPipeline;
272     SkArenaAlloc* alloc = rec.fAlloc;
273 
274     SkMatrix matrix;
275     if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
276         return false;
277     }
278     auto quality = rec.fPaint.getFilterQuality();
279 
280     SkBitmapProvider provider(fImage.get());
281     const auto* state = SkBitmapController::RequestBitmap(provider, matrix, quality, alloc);
282     if (!state) {
283         return false;
284     }
285 
286     const SkPixmap& pm = state->pixmap();
287     matrix  = state->invMatrix();
288     quality = state->quality();
289     auto info = pm.info();
290 
291     // When the matrix is just an integer translate, bilerp == nearest neighbor.
292     if (quality == kLow_SkFilterQuality &&
293         matrix.getType() <= SkMatrix::kTranslate_Mask &&
294         matrix.getTranslateX() == (int)matrix.getTranslateX() &&
295         matrix.getTranslateY() == (int)matrix.getTranslateY()) {
296         quality = kNone_SkFilterQuality;
297     }
298 
299     // See skia:4649 and the GM image_scale_aligned.
300     if (quality == kNone_SkFilterQuality) {
301         if (matrix.getScaleX() >= 0) {
302             matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
303                                             floorf(matrix.getTranslateX())));
304         }
305         if (matrix.getScaleY() >= 0) {
306             matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
307                                             floorf(matrix.getTranslateY())));
308         }
309     }
310 
311     p->append(SkRasterPipeline::seed_shader);
312     p->append_matrix(alloc, matrix);
313 
314     auto gather = alloc->make<SkRasterPipeline_GatherCtx>();
315     gather->pixels = pm.addr();
316     gather->stride = pm.rowBytesAsPixels();
317     gather->width  = pm.width();
318     gather->height = pm.height();
319 
320     auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
321          limit_y = alloc->make<SkRasterPipeline_TileCtx>();
322     limit_x->scale = pm.width();
323     limit_x->invScale = 1.0f / pm.width();
324     limit_y->scale = pm.height();
325     limit_y->invScale = 1.0f / pm.height();
326 
327     SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
328     bool decal_x_and_y = fTileModeX == kDecal_TileMode && fTileModeY == kDecal_TileMode;
329     if (fTileModeX == kDecal_TileMode || fTileModeY == kDecal_TileMode) {
330         decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
331         decal_ctx->limit_x = limit_x->scale;
332         decal_ctx->limit_y = limit_y->scale;
333     }
334 
335     auto append_tiling_and_gather = [&] {
336         if (decal_x_and_y) {
337             p->append(SkRasterPipeline::decal_x_and_y,  decal_ctx);
338         } else {
339             switch (fTileModeX) {
340                 case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */     break;
341                 case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x);   break;
342                 case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x);   break;
343                 case kDecal_TileMode:  p->append(SkRasterPipeline::decal_x,  decal_ctx); break;
344             }
345             switch (fTileModeY) {
346                 case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */     break;
347                 case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y);   break;
348                 case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y);   break;
349                 case kDecal_TileMode:  p->append(SkRasterPipeline::decal_y,  decal_ctx); break;
350             }
351         }
352 
353         void* ctx = gather;
354         switch (info.colorType()) {
355             case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
356             case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
357             case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
358             case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
359             case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
360             case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
361             case kRGBA_F32_SkColorType:     p->append(SkRasterPipeline::gather_f32,     ctx); break;
362 
363             case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_a8,      ctx);
364                                             p->append(SkRasterPipeline::alpha_to_gray      ); break;
365 
366             case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
367                                             p->append(SkRasterPipeline::force_opaque       ); break;
368 
369             case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
370                                             p->append(SkRasterPipeline::force_opaque       ); break;
371 
372             case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx);
373                                             p->append(SkRasterPipeline::swap_rb            ); break;
374 
375             default: SkASSERT(false);
376         }
377         if (decal_ctx) {
378             p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
379         }
380     };
381 
382     auto append_misc = [&] {
383         // TODO: if ref.fDstCS isn't null, we'll premul here then immediately unpremul
384         // to do the color space transformation.  Might be possible to streamline.
385         if (info.colorType() == kAlpha_8_SkColorType) {
386             // The color for A8 images comes from the (sRGB) paint color.
387             p->append_set_rgb(alloc, rec.fPaint.getColor4f());
388             p->append(SkRasterPipeline::premul);
389         } else if (info.alphaType() == kUnpremul_SkAlphaType) {
390             // Convert unpremul images to premul before we carry on with the rest of the pipeline.
391             p->append(SkRasterPipeline::premul);
392         }
393 
394         if (quality > kLow_SkFilterQuality) {
395             // Bicubic filtering naturally produces out of range values on both sides.
396             p->append(SkRasterPipeline::clamp_0);
397             p->append(fClampAsIfUnpremul ? SkRasterPipeline::clamp_1
398                                          : SkRasterPipeline::clamp_a);
399         }
400 
401         if (rec.fDstCS) {
402             // If color managed, convert from premul source all the way to premul dst color space.
403             auto srcCS = info.colorSpace();
404             if (!srcCS || info.colorType() == kAlpha_8_SkColorType) {
405                 // We treat untagged images as sRGB.
406                 // A8 images get their r,g,b from the paint color, so they're also sRGB.
407                 srcCS = sk_srgb_singleton();
408             }
409             alloc->make<SkColorSpaceXformSteps>(srcCS     , kPremul_SkAlphaType,
410                                                 rec.fDstCS, kPremul_SkAlphaType)
411                 ->apply(p, info.colorType());
412         }
413 
414         return true;
415     };
416 
417     // We've got a fast path for 8888 bilinear clamp/clamp sampling.
418     auto ct = info.colorType();
419     if (true
420         && (ct == kRGBA_8888_SkColorType || ct == kBGRA_8888_SkColorType)
421         && quality == kLow_SkFilterQuality
422         && fTileModeX == SkShader::kClamp_TileMode
423         && fTileModeY == SkShader::kClamp_TileMode) {
424 
425         p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
426         if (ct == kBGRA_8888_SkColorType) {
427             p->append(SkRasterPipeline::swap_rb);
428         }
429         return append_misc();
430     }
431 
432     SkRasterPipeline_SamplerCtx* sampler = nullptr;
433     if (quality != kNone_SkFilterQuality) {
434         sampler = alloc->make<SkRasterPipeline_SamplerCtx>();
435     }
436 
437     auto sample = [&](SkRasterPipeline::StockStage setup_x,
438                       SkRasterPipeline::StockStage setup_y) {
439         p->append(setup_x, sampler);
440         p->append(setup_y, sampler);
441         append_tiling_and_gather();
442         p->append(SkRasterPipeline::accumulate, sampler);
443     };
444 
445     if (quality == kNone_SkFilterQuality) {
446         append_tiling_and_gather();
447 
448     } else if (quality == kLow_SkFilterQuality) {
449         p->append(SkRasterPipeline::save_xy, sampler);
450 
451         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
452         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
453         sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
454         sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
455 
456         p->append(SkRasterPipeline::move_dst_src);
457 
458     } else {
459         p->append(SkRasterPipeline::save_xy, sampler);
460 
461         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
462         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
463         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
464         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
465 
466         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
467         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
468         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
469         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
470 
471         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
472         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
473         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
474         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
475 
476         sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
477         sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
478         sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
479         sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
480 
481         p->append(SkRasterPipeline::move_dst_src);
482     }
483 
484     return append_misc();
485 }
486