Home | History | Annotate | Download | only in shaders
      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  */
     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 
     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 
     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 
     60 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 
     68 bool SkImageShader::isOpaque() const {
     69     return fImage->isOpaque() && fTileModeX != kDecal_TileMode && fTileModeY != kDecal_TileMode;
     70 }
     71 
     72 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
     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 
     95 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 
    136 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 
    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 
    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 
    183 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 
    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 
    268 void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
    269 
    270 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