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