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