2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkBitmapController.h"
9 #include "SkBitmapProcShader.h"
10 #include "SkBitmapProvider.h"
11 #include "SkColorShader.h"
12 #include "SkColorTable.h"
13 #include "SkEmptyShader.h"
14 #include "SkFixedAlloc.h"
15 #include "SkImage_Base.h"
16 #include "SkImageShader.h"
17 #include "SkImageShaderContext.h"
18 #include "SkPM4fPriv.h"
19 #include "SkReadBuffer.h"
20 #include "SkWriteBuffer.h"
22 SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix)
24 , fImage(std::move(img))
29 sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
30 const TileMode tx = (TileMode)buffer.readUInt();
31 const TileMode ty = (TileMode)buffer.readUInt();
33 buffer.readMatrix(&matrix);
34 sk_sp<SkImage> img = buffer.readImage();
38 return SkImageShader::Make(std::move(img), tx, ty, &matrix);
41 void SkImageShader::flatten(SkWriteBuffer& buffer) const {
42 buffer.writeUInt(fTileModeX);
43 buffer.writeUInt(fTileModeY);
44 buffer.writeMatrix(this->getLocalMatrix());
45 buffer.writeImage(fImage.get());
48 bool SkImageShader::isOpaque() const {
49 return fImage->isOpaque();
52 size_t SkImageShader::onContextSize(const ContextRec& rec) const {
53 return SkBitmapProcLegacyShader::ContextSize(rec, as_IB(fImage)->onImageInfo());
56 SkShader::Context* SkImageShader::onCreateContext(const ContextRec& rec, void* storage) const {
57 return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
58 SkBitmapProvider(fImage.get(), rec.fDstColorSpace),
62 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
64 *texM = this->getLocalMatrix();
67 xy[0] = (TileMode)fTileModeX;
68 xy[1] = (TileMode)fTileModeY;
70 return const_cast<SkImage*>(fImage.get());
73 #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
74 bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const {
75 const SkBitmap* bm = as_IB(fImage)->onPeekBitmap();
84 *texM = this->getLocalMatrix();
87 xy[0] = (TileMode)fTileModeX;
88 xy[1] = (TileMode)fTileModeY;
94 static bool bitmap_is_too_big(int w, int h) {
95 // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
96 // communicates between its matrix-proc and its sampler-proc. Until we can
97 // widen that, we have to reject bitmaps that are larger.
99 static const int kMaxSize = 65535;
101 return w > kMaxSize || h > kMaxSize;
104 // returns true and set color if the bitmap can be drawn as a single color
106 static bool can_use_color_shader(const SkImage* image, SkColor* color) {
107 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
108 // HWUI does not support color shaders (see b/22390304)
112 if (1 != image->width() || 1 != image->height()) {
117 if (!image->peekPixels(&pmap)) {
121 switch (pmap.colorType()) {
122 case kN32_SkColorType:
123 *color = SkUnPreMultiply::PMColorToColor(*pmap.addr32(0, 0));
125 case kRGB_565_SkColorType:
126 *color = SkPixel16ToColor(*pmap.addr16(0, 0));
128 case kIndex_8_SkColorType: {
129 const SkColorTable& ctable = *pmap.ctable();
130 *color = SkUnPreMultiply::PMColorToColor(ctable[*pmap.addr8(0, 0)]);
133 default: // just skip the other configs for now
139 sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty,
140 const SkMatrix* localMatrix,
141 SkTBlitterAllocator* allocator) {
144 if (!image || bitmap_is_too_big(image->width(), image->height())) {
145 if (nullptr == allocator) {
146 shader = new SkEmptyShader;
148 shader = allocator->createT<SkEmptyShader>();
150 } else if (can_use_color_shader(image.get(), &color)) {
151 if (nullptr == allocator) {
152 shader = new SkColorShader(color);
154 shader = allocator->createT<SkColorShader>(color);
157 if (nullptr == allocator) {
158 shader = new SkImageShader(image, tx, ty, localMatrix);
160 shader = allocator->createT<SkImageShader>(image, tx, ty, localMatrix);
163 return sk_sp<SkShader>(shader);
166 #ifndef SK_IGNORE_TO_STRING
167 void SkImageShader::toString(SkString* str) const {
168 const char* gTileModeName[SkShader::kTileModeCount] = {
169 "clamp", "repeat", "mirror"
172 str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]);
173 fImage->toString(str);
174 this->INHERITED::toString(str);
179 ///////////////////////////////////////////////////////////////////////////////////////////////////
184 #include "SkGrPriv.h"
185 #include "effects/GrSimpleTextureEffect.h"
186 #include "effects/GrBicubicEffect.h"
187 #include "effects/GrSimpleTextureEffect.h"
189 sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& args) const {
191 matrix.setIDiv(fImage->width(), fImage->height());
194 if (!this->getLocalMatrix().invert(&lmInverse)) {
197 if (args.fLocalMatrix) {
199 if (!args.fLocalMatrix->invert(&inv)) {
202 lmInverse.postConcat(inv);
204 matrix.preConcat(lmInverse);
206 SkShader::TileMode tm[] = { fTileModeX, fTileModeY };
208 // Must set wrap and filter on the sampler before requesting a texture. In two places below
209 // we check the matrix scale factors to determine how to interpret the filter quality setting.
210 // This completely ignores the complexity of the drawVertices case where explicit local coords
211 // are provided by the caller.
213 GrSamplerParams::FilterMode textureFilterMode =
214 GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(),
216 GrSamplerParams params(tm, textureFilterMode);
217 sk_sp<SkColorSpace> texColorSpace;
218 sk_sp<GrTexture> texture(as_IB(fImage)->asTextureRef(args.fContext, params, args.fDstColorSpace,
224 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
225 args.fDstColorSpace);
226 sk_sp<GrFragmentProcessor> inner;
228 inner = GrBicubicEffect::Make(texture.get(), std::move(colorSpaceXform), matrix, tm);
230 inner = GrSimpleTextureEffect::Make(texture.get(), std::move(colorSpaceXform),
234 if (GrPixelConfigIsAlphaOnly(texture->config())) {
237 return sk_sp<GrFragmentProcessor>(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)));
242 ///////////////////////////////////////////////////////////////////////////////////////////////////
243 #include "SkImagePriv.h"
245 sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx,
246 SkShader::TileMode tmy, const SkMatrix* localMatrix,
247 SkCopyPixelsMode cpm, SkTBlitterAllocator* allocator) {
248 // Until we learn otherwise, it seems that any caller that is passing an allocator must be
249 // assuming that the returned shader will have a stack-frame lifetime, so we assert that
250 // they are also asking for kNever_SkCopyPixelsMode. If that proves otherwise, we can remove
251 // or modify this assert.
252 SkASSERT(!allocator || (kNever_SkCopyPixelsMode == cpm));
254 return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm, allocator),
255 tmx, tmy, localMatrix, allocator);
258 static sk_sp<SkFlattenable> SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) {
260 buffer.readMatrix(&lm);
261 sk_sp<SkImage> image = buffer.readBitmapAsImage();
262 SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt();
263 SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt();
264 return image ? image->makeShader(mx, my, &lm) : nullptr;
267 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShader)
268 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader)
269 SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShader_Type);
270 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
273 bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkFallbackAlloc* scratch,
274 const SkMatrix& ctm, const SkPaint& paint) const {
275 auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix());
276 if (!matrix.invert(&matrix)) {
279 auto quality = paint.getFilterQuality();
281 SkBitmapProvider provider(fImage.get(), dst);
282 SkDefaultBitmapController controller;
283 std::unique_ptr<SkBitmapController::State> state {
284 controller.requestBitmap(provider, matrix, quality)
290 const SkPixmap& pm = state->pixmap();
291 matrix = state->invMatrix();
292 quality = state->quality();
293 auto info = pm.info();
295 // When the matrix is just an integer translate, bilerp == nearest neighbor.
296 if (matrix.getType() <= SkMatrix::kTranslate_Mask &&
297 matrix.getTranslateX() == (int)matrix.getTranslateX() &&
298 matrix.getTranslateY() == (int)matrix.getTranslateY()) {
299 quality = kNone_SkFilterQuality;
302 // See skia:4649 and the GM image_scale_aligned.
303 if (quality == kNone_SkFilterQuality) {
304 if (matrix.getScaleX() >= 0) {
305 matrix.setTranslateX(nextafterf(matrix.getTranslateX(),
306 floorf(matrix.getTranslateX())));
308 if (matrix.getScaleY() >= 0) {
309 matrix.setTranslateY(nextafterf(matrix.getTranslateY(),
310 floorf(matrix.getTranslateY())));
314 auto ctx = scratch->make<SkImageShaderContext>();
315 ctx->state = std::move(state); // Extend lifetime to match the pipeline's.
316 ctx->pixels = pm.addr();
317 ctx->ctable = pm.ctable();
318 ctx->color4f = SkColor4f_from_SkColor(paint.getColor(), dst);
319 ctx->stride = pm.rowBytesAsPixels();
320 ctx->width = pm.width();
321 ctx->height = pm.height();
322 if (matrix.asAffine(ctx->matrix)) {
323 p->append(SkRasterPipeline::matrix_2x3, ctx->matrix);
325 matrix.get9(ctx->matrix);
326 p->append(SkRasterPipeline::matrix_perspective, ctx->matrix);
329 auto append_tiling_and_gather = [&] {
330 switch (fTileModeX) {
331 case kClamp_TileMode: p->append(SkRasterPipeline::clamp_x, &ctx->width); break;
332 case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, &ctx->width); break;
333 case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, &ctx->width); break;
335 switch (fTileModeY) {
336 case kClamp_TileMode: p->append(SkRasterPipeline::clamp_y, &ctx->height); break;
337 case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, &ctx->height); break;
338 case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, &ctx->height); break;
340 switch (info.colorType()) {
341 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, ctx); break;
342 case kIndex_8_SkColorType: p->append(SkRasterPipeline::gather_i8, ctx); break;
343 case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_g8, ctx); break;
344 case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, ctx); break;
345 case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, ctx); break;
346 case kRGBA_8888_SkColorType:
347 case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, ctx); break;
348 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, ctx); break;
349 default: SkASSERT(false);
351 if (info.gammaCloseToSRGB() && dst != nullptr) {
352 p->append(SkRasterPipeline::from_srgb);
356 auto sample = [&](SkRasterPipeline::StockStage sampler) {
357 p->append(sampler, ctx);
358 append_tiling_and_gather();
359 p->append(SkRasterPipeline::accumulate, ctx);
362 if (quality == kNone_SkFilterQuality) {
363 append_tiling_and_gather();
365 p->append(SkRasterPipeline::save_xy, ctx);
366 sample(SkRasterPipeline::bilinear_nn);
367 sample(SkRasterPipeline::bilinear_np);
368 sample(SkRasterPipeline::bilinear_pn);
369 sample(SkRasterPipeline::bilinear_pp);
370 p->append(SkRasterPipeline::move_dst_src);
373 auto effective_color_type = [](SkColorType ct) {
374 return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct;
377 if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) {
378 p->append(SkRasterPipeline::swap_rb);
380 if (info.colorType() == kAlpha_8_SkColorType) {
381 p->append(SkRasterPipeline::set_rgb, &ctx->color4f);
383 if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) {
384 p->append(SkRasterPipeline::premul);
386 return append_gamut_transform(p, scratch, info.colorSpace(), dst);