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