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