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 "SkColorTable.h"
13#include "SkEmptyShader.h"
14#include "SkImage_Base.h"
15#include "SkImageShader.h"
16#include "SkImageShaderContext.h"
17#include "SkPM4fPriv.h"
18#include "SkReadBuffer.h"
19#include "SkWriteBuffer.h"
20
21SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
22    : INHERITED(matrix)
23    , fImage(std::move(img))
24    , fTileModeX(tmx)
25    , fTileModeY(tmy)
26{}
27
28sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
29    const TileMode tx = (TileMode)buffer.readUInt();
30    const TileMode ty = (TileMode)buffer.readUInt();
31    SkMatrix matrix;
32    buffer.readMatrix(&matrix);
33    sk_sp<SkImage> img = buffer.readImage();
34    if (!img) {
35        return nullptr;
36    }
37    return SkImageShader::Make(std::move(img), tx, ty, &matrix);
38}
39
40void SkImageShader::flatten(SkWriteBuffer& buffer) const {
41    buffer.writeUInt(fTileModeX);
42    buffer.writeUInt(fTileModeY);
43    buffer.writeMatrix(this->getLocalMatrix());
44    buffer.writeImage(fImage.get());
45}
46
47bool SkImageShader::isOpaque() const {
48    return fImage->isOpaque();
49}
50
51SkShader::Context* SkImageShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
52    return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
53                                                 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
54                                                 rec, alloc);
55}
56
57SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
58    if (texM) {
59        *texM = this->getLocalMatrix();
60    }
61    if (xy) {
62        xy[0] = (TileMode)fTileModeX;
63        xy[1] = (TileMode)fTileModeY;
64    }
65    return const_cast<SkImage*>(fImage.get());
66}
67
68#ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
69bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
70    const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
71    if (!bm) {
72        return false;
73    }
74
75    if (texture) {
76        *texture = *bm;
77    }
78    if (texM) {
79        *texM = this->getLocalMatrix();
80    }
81    if (xy) {
82        xy[0] = (TileMode)fTileModeX;
83        xy[1] = (TileMode)fTileModeY;
84    }
85    return true;
86}
87#endif
88
89static bool bitmap_is_too_big(int w, int h) {
90    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
91    // communicates between its matrix-proc and its sampler-proc. Until we can
92    // widen that, we have to reject bitmaps that are larger.
93    //
94    static const int kMaxSize = 65535;
95
96    return w > kMaxSize || h > kMaxSize;
97}
98
99sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
100                                    const SkMatrix* localMatrix) {
101    if (!image || bitmap_is_too_big(image->width(), image->height())) {
102        return sk_make_sp<SkEmptyShader>();
103    } else {
104        return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix);
105    }
106}
107
108#ifndef SK_IGNORE_TO_STRING
109void SkImageShader::toString(SkString* str) const {
110    const char* gTileModeName[SkShader::kTileModeCount] = {
111        "clamp", "repeat", "mirror"
112    };
113
114    str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
115    fImage->toString(str);
116    this->INHERITED::toString(str);
117    str->append(")");
118}
119#endif
120
121///////////////////////////////////////////////////////////////////////////////////////////////////
122
123#if SK_SUPPORT_GPU
124
125#include "SkGr.h"
126#include "GrContext.h"
127#include "effects/GrSimpleTextureEffect.h"
128#include "effects/GrBicubicEffect.h"
129#include "effects/GrSimpleTextureEffect.h"
130
131sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const {
132
133    SkMatrix lmInverse;
134    if (!this->getLocalMatrix().invert(&lmInverse)) {
135        return nullptr;
136    }
137    if (args.fLocalMatrix) {
138        SkMatrix inv;
139        if (!args.fLocalMatrix->invert(&inv)) {
140            return nullptr;
141        }
142        lmInverse.postConcat(inv);
143    }
144
145    SkShader::TileMode tm[] = { fTileModeX, fTileModeY };
146
147    // Must set wrap and filter on the sampler before requesting a texture. In two places below
148    // we check the matrix scale factors to determine how to interpret the filter quality setting.
149    // This completely ignores the complexity of the drawVertices case where explicit local coords
150    // are provided by the caller.
151    bool doBicubic;
152    GrSamplerParams::FilterMode textureFilterMode =
153    GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(),
154                                    &doBicubic);
155    GrSamplerParams params(tm, textureFilterMode);
156    sk_sp<SkColorSpace> texColorSpace;
157    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
158    sk_sp<GrTextureProxy> proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params,
159                                                                 args.fDstColorSpace,
160                                                                 &texColorSpace, scaleAdjust));
161    if (!proxy) {
162        return nullptr;
163    }
164
165    bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config());
166
167    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
168
169    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
170                                                                       args.fDstColorSpace);
171    sk_sp<GrFragmentProcessor> inner;
172    if (doBicubic) {
173        inner = GrBicubicEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
174                                      std::move(colorSpaceXform), lmInverse, tm);
175    } else {
176        inner = GrSimpleTextureEffect::Make(args.fContext->resourceProvider(), std::move(proxy),
177                                            std::move(colorSpaceXform), lmInverse, params);
178    }
179
180    if (isAlphaOnly) {
181        return inner;
182    }
183    return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)));
184}
185
186#endif
187
188///////////////////////////////////////////////////////////////////////////////////////////////////
189#include "SkImagePriv.h"
190
191sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
192                                   SkShader::TileMode tmy, const SkMatrix* localMatrix,
193                                   SkCopyPixelsMode cpm) {
194    return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm),
195                               tmx, tmy, localMatrix);
196}
197
198static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
199    SkMatrix lm;
200    buffer.readMatrix(&lm);
201    sk_sp<SkImage> image = buffer.readBitmapAsImage();
202    SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
203    SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
204    return image ? image->makeShader(mx, my, &lm) : nullptr;
205}
206
207SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShader)
208SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
209SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShader_Type);
210SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
211
212
213bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkArenaAlloc* scratch,
214                                   const SkMatrix& ctm, const SkPaint& paint,
215                                   const SkMatrix* localM) const {
216    auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix());
217    if (localM) {
218        matrix.preConcat(*localM);
219    }
220
221    if (!matrix.invert(&matrix)) {
222        return false;
223    }
224    auto quality = paint.getFilterQuality();
225
226    SkBitmapProvider provider(fImage.get(), dst);
227    SkDefaultBitmapController controller(SkDefaultBitmapController::CanShadeHQ::kYes);
228    std::unique_ptr<SkBitmapController::State> state {
229        controller.requestBitmap(provider, matrix, quality)
230    };
231    if (!state) {
232        return false;
233    }
234
235    const SkPixmap& pm = state->pixmap();
236    matrix  = state->invMatrix();
237    quality = state->quality();
238    auto info = pm.info();
239
240    // When the matrix is just an integer translate, bilerp == nearest neighbor.
241    if (quality == kLow_SkFilterQuality &&
242        matrix.getType() <= SkMatrix::kTranslate_Mask &&
243        matrix.getTranslateX() == (int)matrix.getTranslateX() &&
244        matrix.getTranslateY() == (int)matrix.getTranslateY()) {
245        quality = kNone_SkFilterQuality;
246    }
247
248    // See skia:4649 and the GM image_scale_aligned.
249    if (quality == kNone_SkFilterQuality) {
250        if (matrix.getScaleX() >= 0) {
251            matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
252                                            floorf(matrix.getTranslateX())));
253        }
254        if (matrix.getScaleY() >= 0) {
255            matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
256                                            floorf(matrix.getTranslateY())));
257        }
258    }
259
260    auto ctx = scratch->make<SkImageShaderContext>();
261    ctx->state   = std::move(state);  // Extend lifetime to match the pipeline's.
262    ctx->pixels  = pm.addr();
263    ctx->ctable  = pm.ctable();
264    ctx->color4f = SkColor4f_from_SkColor(paint.getColor(), dst);
265    ctx->stride  = pm.rowBytesAsPixels();
266    ctx->width   = (float)pm.width();
267    ctx->height  = (float)pm.height();
268    if (matrix.asAffine(ctx->matrix)) {
269        p->append(SkRasterPipeline::matrix_2x3, ctx->matrix);
270    } else {
271        matrix.get9(ctx->matrix);
272        p->append(SkRasterPipeline::matrix_perspective, ctx->matrix);
273    }
274
275    auto append_tiling_and_gather = [&] {
276        switch (fTileModeX) {
277            case kClamp_TileMode:  p->append(SkRasterPipeline::clamp_x,  &ctx->width); break;
278            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, &ctx->width); break;
279            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, &ctx->width); break;
280        }
281        switch (fTileModeY) {
282            case kClamp_TileMode:  p->append(SkRasterPipeline::clamp_y,  &ctx->height); break;
283            case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, &ctx->height); break;
284            case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, &ctx->height); break;
285        }
286        switch (info.colorType()) {
287            case kAlpha_8_SkColorType:   p->append(SkRasterPipeline::gather_a8,   ctx); break;
288            case kIndex_8_SkColorType:   p->append(SkRasterPipeline::gather_i8,   ctx); break;
289            case kGray_8_SkColorType:    p->append(SkRasterPipeline::gather_g8,   ctx); break;
290            case kRGB_565_SkColorType:   p->append(SkRasterPipeline::gather_565,  ctx); break;
291            case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
292            case kRGBA_8888_SkColorType:
293            case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
294            case kRGBA_F16_SkColorType:  p->append(SkRasterPipeline::gather_f16,  ctx); break;
295            default: SkASSERT(false);
296        }
297        if (info.gammaCloseToSRGB() && dst != nullptr) {
298            p->append_from_srgb(info.alphaType());
299        }
300    };
301
302    auto sample = [&](SkRasterPipeline::StockStage setup_x,
303                      SkRasterPipeline::StockStage setup_y) {
304        p->append(setup_x, ctx);
305        p->append(setup_y, ctx);
306        append_tiling_and_gather();
307        p->append(SkRasterPipeline::accumulate, ctx);
308    };
309
310    if (quality == kNone_SkFilterQuality) {
311        append_tiling_and_gather();
312    } else if (quality == kLow_SkFilterQuality) {
313        p->append(SkRasterPipeline::save_xy, ctx);
314
315        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny);
316        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny);
317        sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py);
318        sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py);
319
320        p->append(SkRasterPipeline::move_dst_src);
321    } else {
322        p->append(SkRasterPipeline::save_xy, ctx);
323
324        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);
325        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y);
326        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y);
327        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y);
328
329        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y);
330        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y);
331        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y);
332        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y);
333
334        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y);
335        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y);
336        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y);
337        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y);
338
339        sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y);
340        sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y);
341        sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y);
342        sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y);
343
344        p->append(SkRasterPipeline::move_dst_src);
345    }
346
347    auto effective_color_type = [](SkColorType ct) {
348        return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct;
349    };
350
351    if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) {
352        p->append(SkRasterPipeline::swap_rb);
353    }
354    if (info.colorType() == kAlpha_8_SkColorType) {
355        p->append(SkRasterPipeline::set_rgb, &ctx->color4f);
356    }
357    if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
358        p->append(SkRasterPipeline::premul);
359    }
360    if (quality > kLow_SkFilterQuality) {
361        // Bicubic filtering naturally produces out of range values on both sides.
362        p->append(SkRasterPipeline::clamp_0);
363        p->append(SkRasterPipeline::clamp_a);
364    }
365    return append_gamut_transform(p, scratch, info.colorSpace(), dst);
366}
367