SkImageShader.cpp revision dfc0e910dfdf7ad11995d357e44c029d839f2e73
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 "SkEmptyShader.h"
13#include "SkImage_Base.h"
14#include "SkImageShader.h"
15#include "SkPM4fPriv.h"
16#include "SkReadBuffer.h"
17#include "SkWriteBuffer.h"
18#include "../jumper/SkJumper.h"
19
20/**
21 *  We are faster in clamp, so always use that tiling when we can.
22 */
23static 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
34SkImageShader::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
48sk_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
60void 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
68bool SkImageShader::isOpaque() const {
69    return fImage->isOpaque() && fTileModeX != kDecal_TileMode && fTileModeY != kDecal_TileMode;
70}
71
72static bool legacy_shader_can_handle(const SkMatrix& a, const SkMatrix& b) {
73    SkMatrix m = SkMatrix::Concat(a, b);
74    if (!m.isScaleTranslate()) {
75        return false;
76    }
77
78    SkMatrix inv;
79    if (!m.invert(&inv)) {
80        return false;
81    }
82
83    // legacy code uses SkFixed 32.32, so ensure the inverse doesn't map device coordinates
84    // out of range.
85    const SkScalar max_dev_coord = 32767.0f;
86    SkRect src;
87    SkAssertResult(inv.mapRect(&src, SkRect::MakeWH(max_dev_coord, max_dev_coord)));
88
89    // take 1/2 of max signed 32bits so we have room to subtract coordinates
90    const SkScalar max_fixed32dot32 = SK_MaxS32 * 0.5f;
91    if (!SkRect::MakeLTRB(-max_fixed32dot32, -max_fixed32dot32,
92                           max_fixed32dot32, max_fixed32dot32).contains(src)) {
93        return false;
94    }
95
96    // legacy shader impl should be able to handle these matrices
97    return true;
98}
99
100bool SkImageShader::IsRasterPipelineOnly(const SkMatrix& ctm, SkColorType ct, SkAlphaType at,
101                                         SkShader::TileMode tx, SkShader::TileMode ty,
102                                         const SkMatrix& localM) {
103    if (ct != kN32_SkColorType) {
104        return true;
105    }
106    if (at == kUnpremul_SkAlphaType) {
107        return true;
108    }
109#ifndef SK_SUPPORT_LEGACY_TILED_BITMAPS
110    if (tx != ty) {
111        return true;
112    }
113#endif
114    if (tx == kDecal_TileMode || ty == kDecal_TileMode) {
115        return true;
116    }
117    if (!legacy_shader_can_handle(ctm, localM)) {
118        return true;
119    }
120    return false;
121}
122
123bool SkImageShader::onIsRasterPipelineOnly(const SkMatrix& ctm) const {
124    SkBitmapProvider provider(fImage.get(), nullptr);
125    return IsRasterPipelineOnly(ctm, provider.info().colorType(), provider.info().alphaType(),
126                                fTileModeX, fTileModeY, this->getLocalMatrix());
127}
128
129SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
130                                                    SkArenaAlloc* alloc) const {
131    return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
132                                                 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
133                                                 rec, alloc);
134}
135
136SkImage* 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#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
148bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
149    const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
150    if (!bm) {
151        return false;
152    }
153
154    if (texture) {
155        *texture = *bm;
156    }
157    if (texM) {
158        *texM = this->getLocalMatrix();
159    }
160    if (xy) {
161        xy[0] = (TileMode)fTileModeX;
162        xy[1] = (TileMode)fTileModeY;
163    }
164    return true;
165}
166#endif
167
168static bool bitmap_is_too_big(int w, int h) {
169    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
170    // communicates between its matrix-proc and its sampler-proc. Until we can
171    // widen that, we have to reject bitmaps that are larger.
172    //
173    static const int kMaxSize = 65535;
174
175    return w > kMaxSize || h > kMaxSize;
176}
177
178sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
179                                    TileMode tx, TileMode ty,
180                                    const SkMatrix* localMatrix,
181                                    bool clampAsIfUnpremul) {
182    if (!image || bitmap_is_too_big(image->width(), image->height())) {
183        return sk_make_sp<SkEmptyShader>();
184    }
185    return sk_sp<SkShader>{ new SkImageShader(image, tx,ty, localMatrix, clampAsIfUnpremul) };
186}
187
188#ifndef SK_IGNORE_TO_STRING
189void SkImageShader::toString(SkString* str) const {
190    const char* gTileModeName[SkShader::kTileModeCount] = {
191        "clamp", "repeat", "mirror"
192    };
193
194    str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
195    fImage->toString(str);
196    this->INHERITED::toString(str);
197    str->append(")");
198}
199#endif
200
201///////////////////////////////////////////////////////////////////////////////////////////////////
202
203#if SK_SUPPORT_GPU
204
205#include "GrColorSpaceInfo.h"
206#include "GrContext.h"
207#include "GrContextPriv.h"
208#include "SkGr.h"
209#include "effects/GrBicubicEffect.h"
210#include "effects/GrSimpleTextureEffect.h"
211
212static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkShader::TileMode tileMode) {
213    switch (tileMode) {
214        case SkShader::TileMode::kClamp_TileMode:
215            return GrSamplerState::WrapMode::kClamp;
216        case SkShader::TileMode::kRepeat_TileMode:
217            return GrSamplerState::WrapMode::kRepeat;
218        case SkShader::TileMode::kMirror_TileMode:
219            return GrSamplerState::WrapMode::kMirrorRepeat;
220        case SkShader::kDecal_TileMode:
221            // TODO: depending on caps, we should extend WrapMode for decal...
222            return GrSamplerState::WrapMode::kClamp;
223    }
224    SK_ABORT("Unknown tile mode.");
225    return GrSamplerState::WrapMode::kClamp;
226}
227
228std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
229        const GrFPArgs& args) const {
230    SkMatrix lm = this->getLocalMatrix();
231    SkMatrix lmInverse;
232    if (!lm.invert(&lmInverse)) {
233        return nullptr;
234    }
235    if (args.fLocalMatrix) {
236        SkMatrix inv;
237        if (!args.fLocalMatrix->invert(&inv)) {
238            return nullptr;
239        }
240        lmInverse.postConcat(inv);
241        lm.preConcat(*args.fLocalMatrix);
242    }
243
244    GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
245                                            tile_mode_to_wrap_mode(fTileModeY)};
246
247    // Must set wrap and filter on the sampler before requesting a texture. In two places below
248    // we check the matrix scale factors to determine how to interpret the filter quality setting.
249    // This completely ignores the complexity of the drawVertices case where explicit local coords
250    // are provided by the caller.
251    bool doBicubic;
252    GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
253            args.fFilterQuality, *args.fViewMatrix, lm,
254            args.fContext->contextPriv().sharpenMipmappedTextures(), &doBicubic);
255    GrSamplerState samplerState(wrapModes, textureFilterMode);
256    sk_sp<SkColorSpace> texColorSpace;
257    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
258    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(
259            args.fContext, samplerState, args.fDstColorSpaceInfo->colorSpace(), &texColorSpace,
260            scaleAdjust));
261    if (!proxy) {
262        return nullptr;
263    }
264
265    GrPixelConfig config = proxy->config();
266    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(config);
267
268    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
269
270    std::unique_ptr<GrFragmentProcessor> inner;
271    if (doBicubic) {
272        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes);
273    } else {
274        inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
275    }
276    inner = GrColorSpaceXformEffect::Make(std::move(inner), texColorSpace.get(), config,
277                                          args.fDstColorSpaceInfo->colorSpace());
278    if (isAlphaOnly) {
279        return inner;
280    }
281    return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
282}
283
284#endif
285
286///////////////////////////////////////////////////////////////////////////////////////////////////
287#include "SkImagePriv.h"
288
289sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
290                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
291                                   SkCopyPixelsMode cpm) {
292    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
293                               tmx, tmy, localMatrix);
294}
295
296SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase)
297SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
298SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
299
300bool SkImageShader::onAppendStages(const StageRec& rec) const {
301    SkRasterPipeline* p = rec.fPipeline;
302    SkArenaAlloc* alloc = rec.fAlloc;
303
304    SkMatrix matrix;
305    if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
306        return false;
307    }
308    auto quality = rec.fPaint.getFilterQuality();
309
310    SkBitmapProvider provider(fImage.get(), rec.fDstCS);
311    SkDefaultBitmapController controller;
312    std::unique_ptr<SkBitmapController::State> state {
313        controller.requestBitmap(provider, matrix, quality)
314    };
315    if (!state) {
316        return false;
317    }
318
319    const SkPixmap& pm = state->pixmap();
320    matrix  = state->invMatrix();
321    quality = state->quality();
322    auto info = pm.info();
323
324    // When the matrix is just an integer translate, bilerp == nearest neighbor.
325    if (quality == kLow_SkFilterQuality &&
326        matrix.getType() <= SkMatrix::kTranslate_Mask &&
327        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
328        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
329        quality = kNone_SkFilterQuality;
330    }
331
332    // See skia:4649 and the GM image_scale_aligned.
333    if (quality == kNone_SkFilterQuality) {
334        if (matrix.getScaleX() >= 0) {
335            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
336                                            floorf(matrix.getTranslateX())));
337        }
338        if (matrix.getScaleY() >= 0) {
339            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
340                                            floorf(matrix.getTranslateY())));
341        }
342    }
343
344    p->append_seed_shader();
345
346    struct MiscCtx {
347        std::unique_ptr<SkBitmapController::State> state;
348        SkColor4f paint_color;
349    };
350    auto misc = alloc->make<MiscCtx>();
351    misc->state       = std::move(state);  // Extend lifetime to match the pipeline's.
352    misc->paint_color = SkColor4f_from_SkColor(rec.fPaint.getColor(), rec.fDstCS);
353    p->append_matrix(alloc, matrix);
354
355    auto gather = alloc->make<SkJumper_GatherCtx>();
356    gather->pixels = pm.addr();
357    gather->stride = pm.rowBytesAsPixels();
358    gather->width  = pm.width();
359    gather->height = pm.height();
360
361    auto limit_x = alloc->make<SkJumper_TileCtx>(),
362         limit_y = alloc->make<SkJumper_TileCtx>();
363    limit_x->scale = pm.width();
364    limit_x->invScale = 1.0f / pm.width();
365    limit_y->scale = pm.height();
366    limit_y->invScale = 1.0f / pm.height();
367
368    bool is_srgb = rec.fDstCS && (!info.colorSpace() || info.gammaCloseToSRGB());
369
370    SkJumper_DecalTileCtx* decal_ctx = nullptr;
371    bool decal_x_and_y = fTileModeX == kDecal_TileMode && fTileModeY == kDecal_TileMode;
372    if (fTileModeX == kDecal_TileMode || fTileModeY == kDecal_TileMode) {
373        decal_ctx = alloc->make<SkJumper_DecalTileCtx>();
374        decal_ctx->limit_x = limit_x->scale;
375        decal_ctx->limit_y = limit_y->scale;
376    }
377
378    auto append_tiling_and_gather = [&] {
379        if (decal_x_and_y) {
380            p->append(SkRasterPipeline::decal_x_and_y,  decal_ctx);
381        } else {
382            switch (fTileModeX) {
383                case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */     break;
384                case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x);   break;
385                case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x);   break;
386                case kDecal_TileMode:  p->append(SkRasterPipeline::decal_x,  decal_ctx); break;
387            }
388            switch (fTileModeY) {
389                case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */     break;
390                case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y);   break;
391                case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y);   break;
392                case kDecal_TileMode:  p->append(SkRasterPipeline::decal_y,  decal_ctx); break;
393            }
394        }
395
396        void* ctx = gather;
397        switch (info.colorType()) {
398            case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
399            case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_g8,      ctx); break;
400            case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
401            case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
402            case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_bgra,    ctx); break;
403            case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
404            case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
405            case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
406
407            case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
408                                            p->append(SkRasterPipeline::force_opaque       ); break;
409            case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
410                                            p->append(SkRasterPipeline::force_opaque       ); break;
411
412            default: SkASSERT(false);
413        }
414        if (decal_ctx) {
415            p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
416        }
417        if (is_srgb) {
418            p->append(SkRasterPipeline::from_srgb);
419        }
420    };
421
422    auto append_misc = [&] {
423        if (info.colorType() == kAlpha_8_SkColorType) {
424            p->append(SkRasterPipeline::set_rgb, &misc->paint_color);
425        }
426        if (info.colorType() == kAlpha_8_SkColorType ||
427            info.alphaType() == kUnpremul_SkAlphaType) {
428            p->append(SkRasterPipeline::premul);
429        }
430        if (quality > kLow_SkFilterQuality) {
431            // Bicubic filtering naturally produces out of range values on both sides.
432            p->append(SkRasterPipeline::clamp_0);
433            p->append(fClampAsIfUnpremul ? SkRasterPipeline::clamp_1
434                                         : SkRasterPipeline::clamp_a);
435        }
436        append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS,
437                               fClampAsIfUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType);
438        return true;
439    };
440
441    if (quality == kLow_SkFilterQuality            &&
442        info.colorType() == kRGBA_8888_SkColorType &&
443        fTileModeX == SkShader::kClamp_TileMode    &&
444        fTileModeY == SkShader::kClamp_TileMode    &&
445        !is_srgb) {
446
447        p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
448        return append_misc();
449    }
450
451    SkJumper_SamplerCtx* sampler = nullptr;
452    if (quality != kNone_SkFilterQuality) {
453        sampler = alloc->make<SkJumper_SamplerCtx>();
454    }
455
456    auto sample = [&](SkRasterPipeline::StockStage setup_x,
457                      SkRasterPipeline::StockStage setup_y) {
458        p->append(setup_x, sampler);
459        p->append(setup_y, sampler);
460        append_tiling_and_gather();
461        p->append(SkRasterPipeline::accumulate, sampler);
462    };
463
464    if (quality == kNone_SkFilterQuality) {
465        append_tiling_and_gather();
466
467    } else if (quality == kLow_SkFilterQuality) {
468        p->append(SkRasterPipeline::save_xy, sampler);
469
470        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
471        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
472        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
473        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
474
475        p->append(SkRasterPipeline::move_dst_src);
476
477    } else {
478        p->append(SkRasterPipeline::save_xy, sampler);
479
480        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
481        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
482        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
483        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
484
485        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
486        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
487        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
488        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
489
490        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
491        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
492        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
493        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
494
495        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
496        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
497        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
498        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
499
500        p->append(SkRasterPipeline::move_dst_src);
501    }
502
503    return append_misc();
504}
505