SkImageShader.cpp revision 5e34167d53c06c3c4512592bd1477fcf2df97172
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, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
35    : INHERITED(matrix)
36    , fImage(std::move(img))
37    , fTileModeX(optimize(tmx, fImage->width()))
38    , fTileModeY(optimize(tmy, fImage->height()))
39{}
40
41sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
42    const TileMode tx = (TileMode)buffer.readUInt();
43    const TileMode ty = (TileMode)buffer.readUInt();
44    SkMatrix matrix;
45    buffer.readMatrix(&matrix);
46    sk_sp<SkImage> img = buffer.readImage();
47    if (!img) {
48        return nullptr;
49    }
50    return SkImageShader::Make(std::move(img), tx, ty, &matrix);
51}
52
53void SkImageShader::flatten(SkWriteBuffer& buffer) const {
54    buffer.writeUInt(fTileModeX);
55    buffer.writeUInt(fTileModeY);
56    buffer.writeMatrix(this->getLocalMatrix());
57    buffer.writeImage(fImage.get());
58}
59
60bool SkImageShader::isOpaque() const {
61    return fImage->isOpaque();
62}
63
64bool SkImageShader::IsRasterPipelineOnly(const SkMatrix& ctm, SkColorType ct, SkAlphaType at,
65                                         SkShader::TileMode tx, SkShader::TileMode ty,
66                                         const SkMatrix& localM) {
67    if (ct != kN32_SkColorType) {
68        return true;
69    }
70    if (at == kUnpremul_SkAlphaType) {
71        return true;
72    }
73#ifndef SK_SUPPORT_LEGACY_TILED_BITMAPS
74    if (tx != ty) {
75        return true;
76    }
77#endif
78    if (!ctm.isScaleTranslate()) {
79        return true;
80    }
81    if (!localM.isScaleTranslate()) {
82        return true;
83    }
84    return false;
85}
86
87bool SkImageShader::onIsRasterPipelineOnly(const SkMatrix& ctm) const {
88    SkBitmapProvider provider(fImage.get(), nullptr);
89    return IsRasterPipelineOnly(ctm, provider.info().colorType(), provider.info().alphaType(),
90                                fTileModeX, fTileModeY, this->getLocalMatrix());
91}
92
93SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
94                                                    SkArenaAlloc* alloc) const {
95    return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
96                                                 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
97                                                 rec, alloc);
98}
99
100SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
101    if (texM) {
102        *texM = this->getLocalMatrix();
103    }
104    if (xy) {
105        xy[0] = (TileMode)fTileModeX;
106        xy[1] = (TileMode)fTileModeY;
107    }
108    return const_cast<SkImage*>(fImage.get());
109}
110
111#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
112bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
113    const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
114    if (!bm) {
115        return false;
116    }
117
118    if (texture) {
119        *texture = *bm;
120    }
121    if (texM) {
122        *texM = this->getLocalMatrix();
123    }
124    if (xy) {
125        xy[0] = (TileMode)fTileModeX;
126        xy[1] = (TileMode)fTileModeY;
127    }
128    return true;
129}
130#endif
131
132static bool bitmap_is_too_big(int w, int h) {
133    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
134    // communicates between its matrix-proc and its sampler-proc. Until we can
135    // widen that, we have to reject bitmaps that are larger.
136    //
137    static const int kMaxSize = 65535;
138
139    return w > kMaxSize || h > kMaxSize;
140}
141
142sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
143                                    const SkMatrix* localMatrix) {
144    if (!image || bitmap_is_too_big(image->width(), image->height())) {
145        return sk_make_sp<SkEmptyShader>();
146    } else {
147        return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix);
148    }
149}
150
151#ifndef SK_IGNORE_TO_STRING
152void SkImageShader::toString(SkString* str) const {
153    const char* gTileModeName[SkShader::kTileModeCount] = {
154        "clamp", "repeat", "mirror"
155    };
156
157    str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
158    fImage->toString(str);
159    this->INHERITED::toString(str);
160    str->append(")");
161}
162#endif
163
164///////////////////////////////////////////////////////////////////////////////////////////////////
165
166#if SK_SUPPORT_GPU
167
168#include "SkGr.h"
169#include "GrContext.h"
170#include "effects/GrSimpleTextureEffect.h"
171#include "effects/GrBicubicEffect.h"
172#include "effects/GrSimpleTextureEffect.h"
173
174static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkShader::TileMode tileMode) {
175    switch (tileMode) {
176        case SkShader::TileMode::kClamp_TileMode:
177            return GrSamplerState::WrapMode::kClamp;
178        case SkShader::TileMode::kRepeat_TileMode:
179            return GrSamplerState::WrapMode::kRepeat;
180        case SkShader::TileMode::kMirror_TileMode:
181            return GrSamplerState::WrapMode::kMirrorRepeat;
182    }
183    SK_ABORT("Unknown tile mode.");
184    return GrSamplerState::WrapMode::kClamp;
185}
186
187std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
188        const AsFPArgs& args) const {
189    SkMatrix lmInverse;
190    if (!this->getLocalMatrix().invert(&lmInverse)) {
191        return nullptr;
192    }
193    if (args.fLocalMatrix) {
194        SkMatrix inv;
195        if (!args.fLocalMatrix->invert(&inv)) {
196            return nullptr;
197        }
198        lmInverse.postConcat(inv);
199    }
200
201    GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
202                                            tile_mode_to_wrap_mode(fTileModeY)};
203
204    // Must set wrap and filter on the sampler before requesting a texture. In two places below
205    // we check the matrix scale factors to determine how to interpret the filter quality setting.
206    // This completely ignores the complexity of the drawVertices case where explicit local coords
207    // are provided by the caller.
208    bool doBicubic;
209    GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
210            args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(), &doBicubic);
211    GrSamplerState samplerState(wrapModes, textureFilterMode);
212    sk_sp<SkColorSpace> texColorSpace;
213    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
214    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(
215            args.fContext, samplerState, args.fDstColorSpace, &texColorSpace, scaleAdjust));
216    if (!proxy) {
217        return nullptr;
218    }
219
220    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
221
222    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
223
224    std::unique_ptr<GrFragmentProcessor> inner;
225    if (doBicubic) {
226        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes);
227    } else {
228        inner = GrSimpleTextureEffect::Make(std::move(proxy), nullptr, lmInverse, samplerState);
229    }
230    inner = GrColorSpaceXformEffect::Make(std::move(inner), texColorSpace.get(),
231                                          args.fDstColorSpace);
232    if (isAlphaOnly) {
233        return inner;
234    }
235    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
236}
237
238#endif
239
240///////////////////////////////////////////////////////////////////////////////////////////////////
241#include "SkImagePriv.h"
242
243sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
244                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
245                                   SkCopyPixelsMode cpm) {
246    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
247                               tmx, tmy, localMatrix);
248}
249
250static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
251    SkMatrix lm;
252    buffer.readMatrix(&lm);
253    sk_sp<SkImage> image = buffer.readBitmapAsImage();
254    SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
255    SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
256    return image ? image->makeShader(mx, my, &lm) : nullptr;
257}
258
259SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase)
260SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
261SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShaderBase_Type);
262SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
263
264
265bool SkImageShader::onAppendStages(const StageRec& rec) const {
266    SkRasterPipeline* p = rec.fPipeline;
267    SkArenaAlloc* alloc = rec.fAlloc;
268
269    auto matrix = SkMatrix::Concat(rec.fCTM, this->getLocalMatrix());
270    if (rec.fLocalM) {
271        matrix.preConcat(*rec.fLocalM);
272    }
273
274    if (!matrix.invert(&matrix)) {
275        return false;
276    }
277    auto quality = rec.fPaint.getFilterQuality();
278
279    SkBitmapProvider provider(fImage.get(), rec.fDstCS);
280    SkDefaultBitmapController controller;
281    std::unique_ptr<SkBitmapController::State> state {
282        controller.requestBitmap(provider, matrix, quality)
283    };
284    if (!state) {
285        return false;
286    }
287
288    const SkPixmap& pm = state->pixmap();
289    matrix  = state->invMatrix();
290    quality = state->quality();
291    auto info = pm.info();
292
293    // When the matrix is just an integer translate, bilerp == nearest neighbor.
294    if (quality == kLow_SkFilterQuality &&
295        matrix.getType() <= SkMatrix::kTranslate_Mask &&
296        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
297        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
298        quality = kNone_SkFilterQuality;
299    }
300
301    // See skia:4649 and the GM image_scale_aligned.
302    if (quality == kNone_SkFilterQuality) {
303        if (matrix.getScaleX() >= 0) {
304            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
305                                            floorf(matrix.getTranslateX())));
306        }
307        if (matrix.getScaleY() >= 0) {
308            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
309                                            floorf(matrix.getTranslateY())));
310        }
311    }
312
313    p->append(SkRasterPipeline::seed_shader);
314
315    struct MiscCtx {
316        std::unique_ptr<SkBitmapController::State> state;
317        SkColor4f paint_color;
318    };
319    auto misc = alloc->make<MiscCtx>();
320    misc->state       = std::move(state);  // Extend lifetime to match the pipeline's.
321    misc->paint_color = SkColor4f_from_SkColor(rec.fPaint.getColor(), rec.fDstCS);
322    p->append_matrix(alloc, matrix);
323
324    auto gather = alloc->make<SkJumper_GatherCtx>();
325    gather->pixels = pm.writable_addr();  // Don't worry, we won't write to it.
326    gather->stride = pm.rowBytesAsPixels();
327    gather->width  = pm.width();
328    gather->height = pm.height();
329
330    auto limit_x = alloc->make<SkJumper_TileCtx>(),
331         limit_y = alloc->make<SkJumper_TileCtx>();
332    limit_x->scale = pm.width();
333    limit_x->invScale = 1.0f / pm.width();
334    limit_y->scale = pm.height();
335    limit_y->invScale = 1.0f / pm.height();
336
337    auto append_tiling_and_gather = [&] {
338        switch (fTileModeX) {
339            case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */   break;
340            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x); break;
341            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x); break;
342        }
343        switch (fTileModeY) {
344            case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */   break;
345            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y); break;
346            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y); break;
347        }
348        switch (info.colorType()) {
349            case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::gather_a8,   gather); break;
350            case kGray_8_SkColorType:    p->append(SkRasterPipeline::gather_g8,   gather); break;
351            case kRGB_565_SkColorType:   p->append(SkRasterPipeline::gather_565,  gather); break;
352            case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, gather); break;
353            case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_bgra, gather); break;
354            case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, gather); break;
355            case kRGBA_F16_SkColorType:  p->append(SkRasterPipeline::gather_f16,  gather); break;
356            default: SkASSERT(false);
357        }
358        if (rec.fDstCS && (!info.colorSpace() || info.gammaCloseToSRGB())) {
359            p->append_from_srgb(info.alphaType());
360        }
361    };
362
363    SkJumper_SamplerCtx* sampler = nullptr;
364    if (quality != kNone_SkFilterQuality) {
365        sampler = alloc->make<SkJumper_SamplerCtx>();
366    }
367
368    auto sample = [&](SkRasterPipeline::StockStage setup_x,
369                      SkRasterPipeline::StockStage setup_y) {
370        p->append(setup_x, sampler);
371        p->append(setup_y, sampler);
372        append_tiling_and_gather();
373        p->append(SkRasterPipeline::accumulate, sampler);
374    };
375
376    if (quality == kNone_SkFilterQuality) {
377        append_tiling_and_gather();
378    } else if (quality == kLow_SkFilterQuality) {
379        p->append(SkRasterPipeline::save_xy, sampler);
380
381        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
382        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
383        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
384        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
385
386        p->append(SkRasterPipeline::move_dst_src);
387    } else {
388        p->append(SkRasterPipeline::save_xy, sampler);
389
390        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
391        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
392        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
393        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
394
395        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
396        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
397        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
398        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
399
400        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
401        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
402        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
403        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
404
405        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
406        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
407        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
408        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
409
410        p->append(SkRasterPipeline::move_dst_src);
411    }
412
413    if (info.colorType() == kAlpha_8_SkColorType) {
414        p->append(SkRasterPipeline::set_rgb, &misc->paint_color);
415    }
416    if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
417        p->append(SkRasterPipeline::premul);
418    }
419    if (quality > kLow_SkFilterQuality) {
420        // Bicubic filtering naturally produces out of range values on both sides.
421        p->append(SkRasterPipeline::clamp_0);
422        p->append(SkRasterPipeline::clamp_a);
423    }
424    append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS, kPremul_SkAlphaType);
425    return true;
426}
427