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