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