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 "GrColorSpaceInfo.h"
169#include "GrContext.h"
170#include "SkGr.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 GrFPArgs& 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.fDstColorSpaceInfo->colorSpace(), &texColorSpace,
216            scaleAdjust));
217    if (!proxy) {
218        return nullptr;
219    }
220
221    GrPixelConfig config = proxy->config();
222    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(config);
223
224    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
225
226    std::unique_ptr<GrFragmentProcessor> inner;
227    if (doBicubic) {
228        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes);
229    } else {
230        inner = GrSimpleTextureEffect::Make(std::move(proxy), lmInverse, samplerState);
231    }
232    inner = GrColorSpaceXformEffect::Make(std::move(inner), texColorSpace.get(), config,
233                                          args.fDstColorSpaceInfo->colorSpace());
234    if (isAlphaOnly) {
235        return inner;
236    }
237    return GrFragmentProcessor::MulChildByInputAlpha(std::move(inner));
238}
239
240#endif
241
242///////////////////////////////////////////////////////////////////////////////////////////////////
243#include "SkImagePriv.h"
244
245sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
246                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
247                                   SkCopyPixelsMode cpm) {
248    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
249                               tmx, tmy, localMatrix);
250}
251
252SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase)
253SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
254SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
255
256bool SkImageShader::onAppendStages(const StageRec& rec) const {
257    SkRasterPipeline* p = rec.fPipeline;
258    SkArenaAlloc* alloc = rec.fAlloc;
259
260    auto matrix = SkMatrix::Concat(rec.fCTM, this->getLocalMatrix());
261    if (rec.fLocalM) {
262        matrix.preConcat(*rec.fLocalM);
263    }
264
265    if (!matrix.invert(&matrix)) {
266        return false;
267    }
268    auto quality = rec.fPaint.getFilterQuality();
269
270    SkBitmapProvider provider(fImage.get(), rec.fDstCS);
271    SkDefaultBitmapController controller;
272    std::unique_ptr<SkBitmapController::State> state {
273        controller.requestBitmap(provider, matrix, quality)
274    };
275    if (!state) {
276        return false;
277    }
278
279    const SkPixmap& pm = state->pixmap();
280    matrix  = state->invMatrix();
281    quality = state->quality();
282    auto info = pm.info();
283
284    // When the matrix is just an integer translate, bilerp == nearest neighbor.
285    if (quality == kLow_SkFilterQuality &&
286        matrix.getType() <= SkMatrix::kTranslate_Mask &&
287        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
288        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
289        quality = kNone_SkFilterQuality;
290    }
291
292    // See skia:4649 and the GM image_scale_aligned.
293    if (quality == kNone_SkFilterQuality) {
294        if (matrix.getScaleX() >= 0) {
295            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
296                                            floorf(matrix.getTranslateX())));
297        }
298        if (matrix.getScaleY() >= 0) {
299            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
300                                            floorf(matrix.getTranslateY())));
301        }
302    }
303
304    p->append_seed_shader();
305
306    struct MiscCtx {
307        std::unique_ptr<SkBitmapController::State> state;
308        SkColor4f paint_color;
309    };
310    auto misc = alloc->make<MiscCtx>();
311    misc->state       = std::move(state);  // Extend lifetime to match the pipeline's.
312    misc->paint_color = SkColor4f_from_SkColor(rec.fPaint.getColor(), rec.fDstCS);
313    p->append_matrix(alloc, matrix);
314
315    auto gather = alloc->make<SkJumper_GatherCtx>();
316    gather->pixels = pm.addr();
317    gather->stride = pm.rowBytesAsPixels();
318    gather->width  = pm.width();
319    gather->height = pm.height();
320
321    auto limit_x = alloc->make<SkJumper_TileCtx>(),
322         limit_y = alloc->make<SkJumper_TileCtx>();
323    limit_x->scale = pm.width();
324    limit_x->invScale = 1.0f / pm.width();
325    limit_y->scale = pm.height();
326    limit_y->invScale = 1.0f / pm.height();
327
328    bool is_srgb = rec.fDstCS && (!info.colorSpace() || info.gammaCloseToSRGB());
329
330    auto append_tiling_and_gather = [&] {
331        switch (fTileModeX) {
332            case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */   break;
333            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x); break;
334            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x); break;
335        }
336        switch (fTileModeY) {
337            case kClamp_TileMode:  /* The gather_xxx stage will clamp for us. */   break;
338            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y); break;
339            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y); break;
340        }
341        void* ctx = gather;
342        switch (info.colorType()) {
343            case kAlpha_8_SkColorType:      p->append(SkRasterPipeline::gather_a8,      ctx); break;
344            case kGray_8_SkColorType:       p->append(SkRasterPipeline::gather_g8,      ctx); break;
345            case kRGB_565_SkColorType:      p->append(SkRasterPipeline::gather_565,     ctx); break;
346            case kARGB_4444_SkColorType:    p->append(SkRasterPipeline::gather_4444,    ctx); break;
347            case kBGRA_8888_SkColorType:    p->append(SkRasterPipeline::gather_bgra,    ctx); break;
348            case kRGBA_8888_SkColorType:    p->append(SkRasterPipeline::gather_8888,    ctx); break;
349            case kRGBA_1010102_SkColorType: p->append(SkRasterPipeline::gather_1010102, ctx); break;
350            case kRGBA_F16_SkColorType:     p->append(SkRasterPipeline::gather_f16,     ctx); break;
351
352            case kRGB_888x_SkColorType:     p->append(SkRasterPipeline::gather_8888,    ctx);
353                                            p->append(SkRasterPipeline::force_opaque       ); break;
354            case kRGB_101010x_SkColorType:  p->append(SkRasterPipeline::gather_1010102, ctx);
355                                            p->append(SkRasterPipeline::force_opaque       ); break;
356
357            default: SkASSERT(false);
358        }
359        if (is_srgb) {
360            p->append(SkRasterPipeline::from_srgb);
361        }
362    };
363
364    auto append_misc = [&] {
365        if (info.colorType() == kAlpha_8_SkColorType) {
366            p->append(SkRasterPipeline::set_rgb, &misc->paint_color);
367        }
368        if (info.colorType() == kAlpha_8_SkColorType ||
369            info.alphaType() == kUnpremul_SkAlphaType) {
370            p->append(SkRasterPipeline::premul);
371        }
372#if defined(SK_LEGACY_HIGH_QUALITY_SCALING_CLAMP)
373        if (quality > kLow_SkFilterQuality) {
374            // Bicubic filtering naturally produces out of range values on both sides.
375            p->append(SkRasterPipeline::clamp_0);
376            p->append(SkRasterPipeline::clamp_a);
377        }
378#endif
379        append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS, kPremul_SkAlphaType);
380        return true;
381    };
382
383    if (quality == kLow_SkFilterQuality            &&
384        info.colorType() == kRGBA_8888_SkColorType &&
385        fTileModeX == SkShader::kClamp_TileMode    &&
386        fTileModeY == SkShader::kClamp_TileMode    &&
387        !is_srgb) {
388
389        p->append(SkRasterPipeline::bilerp_clamp_8888, gather);
390        return append_misc();
391    }
392
393    SkJumper_SamplerCtx* sampler = nullptr;
394    if (quality != kNone_SkFilterQuality) {
395        sampler = alloc->make<SkJumper_SamplerCtx>();
396    }
397
398    auto sample = [&](SkRasterPipeline::StockStage setup_x,
399                      SkRasterPipeline::StockStage setup_y) {
400        p->append(setup_x, sampler);
401        p->append(setup_y, sampler);
402        append_tiling_and_gather();
403        p->append(SkRasterPipeline::accumulate, sampler);
404    };
405
406    if (quality == kNone_SkFilterQuality) {
407        append_tiling_and_gather();
408
409    } else if (quality == kLow_SkFilterQuality) {
410        p->append(SkRasterPipeline::save_xy, sampler);
411
412        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
413        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
414        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
415        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
416
417        p->append(SkRasterPipeline::move_dst_src);
418
419    } else {
420        p->append(SkRasterPipeline::save_xy, sampler);
421
422        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
423        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
424        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
425        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
426
427        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
428        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
429        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
430        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
431
432        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
433        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
434        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
435        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
436
437        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
438        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
439        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
440        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
441
442        p->append(SkRasterPipeline::move_dst_src);
443    }
444
445    return append_misc();
446}
447