SkImageShader.cpp revision 1cb41717bc4a44272eab48bd47ca7579425dc22e
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 "GrColorSpaceXform.h"
170#include "GrContext.h"
171#include "effects/GrSimpleTextureEffect.h"
172#include "effects/GrBicubicEffect.h"
173#include "effects/GrSimpleTextureEffect.h"
174
175static GrSamplerState::WrapMode tile_mode_to_wrap_mode(const SkShader::TileMode tileMode) {
176    switch (tileMode) {
177        case SkShader::TileMode::kClamp_TileMode:
178            return GrSamplerState::WrapMode::kClamp;
179        case SkShader::TileMode::kRepeat_TileMode:
180            return GrSamplerState::WrapMode::kRepeat;
181        case SkShader::TileMode::kMirror_TileMode:
182            return GrSamplerState::WrapMode::kMirrorRepeat;
183    }
184    SK_ABORT("Unknown tile mode.");
185    return GrSamplerState::WrapMode::kClamp;
186}
187
188std::unique_ptr<GrFragmentProcessor> SkImageShader::asFragmentProcessor(
189        const AsFPArgs& args) const {
190    SkMatrix lmInverse;
191    if (!this->getLocalMatrix().invert(&lmInverse)) {
192        return nullptr;
193    }
194    if (args.fLocalMatrix) {
195        SkMatrix inv;
196        if (!args.fLocalMatrix->invert(&inv)) {
197            return nullptr;
198        }
199        lmInverse.postConcat(inv);
200    }
201
202    GrSamplerState::WrapMode wrapModes[] = {tile_mode_to_wrap_mode(fTileModeX),
203                                            tile_mode_to_wrap_mode(fTileModeY)};
204
205    // Must set wrap and filter on the sampler before requesting a texture. In two places below
206    // we check the matrix scale factors to determine how to interpret the filter quality setting.
207    // This completely ignores the complexity of the drawVertices case where explicit local coords
208    // are provided by the caller.
209    bool doBicubic;
210    GrSamplerState::Filter textureFilterMode = GrSkFilterQualityToGrFilterMode(
211            args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(), &doBicubic);
212    GrSamplerState samplerState(wrapModes, textureFilterMode);
213    sk_sp<SkColorSpace> texColorSpace;
214    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
215    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(
216            args.fContext, samplerState, args.fDstColorSpace, &texColorSpace, scaleAdjust));
217    if (!proxy) {
218        return nullptr;
219    }
220
221    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
222
223    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
224
225    std::unique_ptr<GrFragmentProcessor> inner;
226    if (doBicubic) {
227        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes);
228    } else {
229        inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
230    }
231    inner = GrColorSpaceXformEffect::Make(std::move(inner), texColorSpace.get(),
232                                          args.fDstColorSpace);
233    if (isAlphaOnly) {
234        return inner;
235    }
236    return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
237}
238
239#endif
240
241///////////////////////////////////////////////////////////////////////////////////////////////////
242#include "SkImagePriv.h"
243
244sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
245                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
246                                   SkCopyPixelsMode cpm) {
247    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
248                               tmx, tmy, localMatrix);
249}
250
251static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
252    SkMatrix lm;
253    buffer.readMatrix(&lm);
254    sk_sp<SkImage> image = buffer.readBitmapAsImage();
255    SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
256    SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
257    return image ? image->makeShader(mx, my, &lm) : nullptr;
258}
259
260SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase)
261SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
262SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShaderBase_Type);
263SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
264
265
266bool SkImageShader::onAppendStages(const StageRec& rec) const {
267    SkRasterPipeline* p = rec.fPipeline;
268    SkArenaAlloc* alloc = rec.fAlloc;
269
270    auto matrix = SkMatrix::Concat(rec.fCTM, this->getLocalMatrix());
271    if (rec.fLocalM) {
272        matrix.preConcat(*rec.fLocalM);
273    }
274
275    if (!matrix.invert(&matrix)) {
276        return false;
277    }
278    auto quality = rec.fPaint.getFilterQuality();
279
280    SkBitmapProvider provider(fImage.get(), rec.fDstCS);
281    SkDefaultBitmapController controller;
282    std::unique_ptr<SkBitmapController::State> state {
283        controller.requestBitmap(provider, matrix, quality)
284    };
285    if (!state) {
286        return false;
287    }
288
289    const SkPixmap& pm = state->pixmap();
290    matrix  = state->invMatrix();
291    quality = state->quality();
292    auto info = pm.info();
293
294    // When the matrix is just an integer translate, bilerp == nearest neighbor.
295    if (quality == kLow_SkFilterQuality &&
296        matrix.getType() <= SkMatrix::kTranslate_Mask &&
297        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
298        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
299        quality = kNone_SkFilterQuality;
300    }
301
302    // See skia:4649 and the GM image_scale_aligned.
303    if (quality == kNone_SkFilterQuality) {
304        if (matrix.getScaleX() >= 0) {
305            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
306                                            floorf(matrix.getTranslateX())));
307        }
308        if (matrix.getScaleY() >= 0) {
309            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
310                                            floorf(matrix.getTranslateY())));
311        }
312    }
313
314    p->append_seed_shader();
315
316    struct MiscCtx {
317        std::unique_ptr<SkBitmapController::State> state;
318        SkColor4f paint_color;
319    };
320    auto misc = alloc->make<MiscCtx>();
321    misc->state       = std::move(state);  // Extend lifetime to match the pipeline's.
322    misc->paint_color = SkColor4f_from_SkColor(rec.fPaint.getColor(), rec.fDstCS);
323    p->append_matrix(alloc, matrix);
324
325    auto gather = alloc->make<SkJumper_GatherCtx>();
326    gather->pixels = pm.writable_addr();  // Don't worry, we won't write to it.
327    gather->stride = pm.rowBytesAsPixels();
328    gather->width  = pm.width();
329    gather->height = pm.height();
330
331    auto limit_x = alloc->make<SkJumper_TileCtx>(),
332         limit_y = alloc->make<SkJumper_TileCtx>();
333    limit_x->scale = pm.width();
334    limit_x->invScale = 1.0f / pm.width();
335    limit_y->scale = pm.height();
336    limit_y->invScale = 1.0f / pm.height();
337
338    auto append_tiling_and_gather = [&] {
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        }
344        switch (fTileModeY) {
345            case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */   break;
346            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y); break;
347            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y); break;
348        }
349        switch (info.colorType()) {
350            case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::gather_a8,   gather); break;
351            case kGray_8_SkColorType:    p->append(SkRasterPipeline::gather_g8,   gather); break;
352            case kRGB_565_SkColorType:   p->append(SkRasterPipeline::gather_565,  gather); break;
353            case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, gather); break;
354            case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_bgra, gather); break;
355            case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, gather); break;
356            case kRGBA_F16_SkColorType:  p->append(SkRasterPipeline::gather_f16,  gather); break;
357            default: SkASSERT(false);
358        }
359        if (rec.fDstCS && (!info.colorSpace() || info.gammaCloseToSRGB())) {
360            p->append_from_srgb(info.alphaType());
361        }
362    };
363
364    SkJumper_SamplerCtx* sampler = nullptr;
365    if (quality != kNone_SkFilterQuality) {
366        sampler = alloc->make<SkJumper_SamplerCtx>();
367    }
368
369    auto sample = [&](SkRasterPipeline::StockStage setup_x,
370                      SkRasterPipeline::StockStage setup_y) {
371        p->append(setup_x, sampler);
372        p->append(setup_y, sampler);
373        append_tiling_and_gather();
374        p->append(SkRasterPipeline::accumulate, sampler);
375    };
376
377    if (quality == kNone_SkFilterQuality) {
378        append_tiling_and_gather();
379    } else if (quality == kLow_SkFilterQuality) {
380        p->append(SkRasterPipeline::save_xy, sampler);
381
382        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
383        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
384        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
385        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
386
387        p->append(SkRasterPipeline::move_dst_src);
388    } else {
389        p->append(SkRasterPipeline::save_xy, sampler);
390
391        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
392        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
393        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
394        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
395
396        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
397        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
398        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
399        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
400
401        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
402        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
403        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
404        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
405
406        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
407        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
408        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
409        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
410
411        p->append(SkRasterPipeline::move_dst_src);
412    }
413
414    if (info.colorType() == kAlpha_8_SkColorType) {
415        p->append(SkRasterPipeline::set_rgb, &misc->paint_color);
416    }
417    if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
418        p->append(SkRasterPipeline::premul);
419    }
420    if (quality > kLow_SkFilterQuality) {
421        // Bicubic filtering naturally produces out of range values on both sides.
422        p->append(SkRasterPipeline::clamp_0);
423        p->append(SkRasterPipeline::clamp_a);
424    }
425    append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS, kPremul_SkAlphaType);
426    return true;
427}
428