/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkArenaAlloc.h" #include "SkBitmapController.h" #include "SkBitmapProcShader.h" #include "SkBitmapProvider.h" #include "SkColorTable.h" #include "SkEmptyShader.h" #include "SkImage_Base.h" #include "SkImageShader.h" #include "SkPM4fPriv.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" #include "../jumper/SkJumper.h" SkImageShader::SkImageShader(sk_sp img, TileMode tmx, TileMode tmy, const SkMatrix* matrix) : INHERITED(matrix) , fImage(std::move(img)) , fTileModeX(tmx) , fTileModeY(tmy) {} sk_sp SkImageShader::CreateProc(SkReadBuffer& buffer) { const TileMode tx = (TileMode)buffer.readUInt(); const TileMode ty = (TileMode)buffer.readUInt(); SkMatrix matrix; buffer.readMatrix(&matrix); sk_sp img = buffer.readImage(); if (!img) { return nullptr; } return SkImageShader::Make(std::move(img), tx, ty, &matrix); } void SkImageShader::flatten(SkWriteBuffer& buffer) const { buffer.writeUInt(fTileModeX); buffer.writeUInt(fTileModeY); buffer.writeMatrix(this->getLocalMatrix()); buffer.writeImage(fImage.get()); } bool SkImageShader::isOpaque() const { return fImage->isOpaque(); } SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const { return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY, SkBitmapProvider(fImage.get(), rec.fDstColorSpace), rec, alloc); } SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const { if (texM) { *texM = this->getLocalMatrix(); } if (xy) { xy[0] = (TileMode)fTileModeX; xy[1] = (TileMode)fTileModeY; } return const_cast(fImage.get()); } #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP bool SkImageShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode xy[]) const { const SkBitmap* bm = as_IB(fImage)->onPeekBitmap(); if (!bm) { return false; } if (texture) { *texture = *bm; } if (texM) { *texM = this->getLocalMatrix(); } if (xy) { xy[0] = (TileMode)fTileModeX; xy[1] = (TileMode)fTileModeY; } return true; } #endif static bool bitmap_is_too_big(int w, int h) { // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it // communicates between its matrix-proc and its sampler-proc. Until we can // widen that, we have to reject bitmaps that are larger. // static const int kMaxSize = 65535; return w > kMaxSize || h > kMaxSize; } sk_sp SkImageShader::Make(sk_sp image, TileMode tx, TileMode ty, const SkMatrix* localMatrix) { if (!image || bitmap_is_too_big(image->width(), image->height())) { return sk_make_sp(); } else { return sk_make_sp(image, tx, ty, localMatrix); } } #ifndef SK_IGNORE_TO_STRING void SkImageShader::toString(SkString* str) const { const char* gTileModeName[SkShader::kTileModeCount] = { "clamp", "repeat", "mirror" }; str->appendf("ImageShader: ((%s %s) ", gTileModeName[fTileModeX], gTileModeName[fTileModeY]); fImage->toString(str); this->INHERITED::toString(str); str->append(")"); } #endif /////////////////////////////////////////////////////////////////////////////////////////////////// #if SK_SUPPORT_GPU #include "SkGr.h" #include "GrContext.h" #include "effects/GrSimpleTextureEffect.h" #include "effects/GrBicubicEffect.h" #include "effects/GrSimpleTextureEffect.h" sk_sp SkImageShader::asFragmentProcessor(const AsFPArgs& args) const { SkMatrix lmInverse; if (!this->getLocalMatrix().invert(&lmInverse)) { return nullptr; } if (args.fLocalMatrix) { SkMatrix inv; if (!args.fLocalMatrix->invert(&inv)) { return nullptr; } lmInverse.postConcat(inv); } SkShader::TileMode tm[] = { fTileModeX, fTileModeY }; // Must set wrap and filter on the sampler before requesting a texture. In two places below // we check the matrix scale factors to determine how to interpret the filter quality setting. // This completely ignores the complexity of the drawVertices case where explicit local coords // are provided by the caller. bool doBicubic; GrSamplerParams::FilterMode textureFilterMode = GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(), &doBicubic); GrSamplerParams params(tm, textureFilterMode); sk_sp texColorSpace; SkScalar scaleAdjust[2] = { 1.0f, 1.0f }; sk_sp proxy(as_IB(fImage)->asTextureProxyRef(args.fContext, params, args.fDstColorSpace, &texColorSpace, scaleAdjust)); if (!proxy) { return nullptr; } bool isAlphaOnly = GrPixelConfigIsAlphaOnly(proxy->config()); lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]); sk_sp colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(), args.fDstColorSpace); sk_sp inner; if (doBicubic) { inner = GrBicubicEffect::Make(args.fContext->resourceProvider(), std::move(proxy), std::move(colorSpaceXform), lmInverse, tm); } else { inner = GrSimpleTextureEffect::Make(args.fContext->resourceProvider(), std::move(proxy), std::move(colorSpaceXform), lmInverse, params); } if (isAlphaOnly) { return inner; } return sk_sp(GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner))); } #endif /////////////////////////////////////////////////////////////////////////////////////////////////// #include "SkImagePriv.h" sk_sp SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx, SkShader::TileMode tmy, const SkMatrix* localMatrix, SkCopyPixelsMode cpm) { return SkImageShader::Make(SkMakeImageFromRasterBitmap(src, cpm), tmx, tmy, localMatrix); } static sk_sp SkBitmapProcShader_CreateProc(SkReadBuffer& buffer) { SkMatrix lm; buffer.readMatrix(&lm); sk_sp image = buffer.readBitmapAsImage(); SkShader::TileMode mx = (SkShader::TileMode)buffer.readUInt(); SkShader::TileMode my = (SkShader::TileMode)buffer.readUInt(); return image ? image->makeShader(mx, my, &lm) : nullptr; } SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShaderBase) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageShader) SkFlattenable::Register("SkBitmapProcShader", SkBitmapProcShader_CreateProc, kSkShaderBase_Type); SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dstCS, SkArenaAlloc* alloc, const SkMatrix& ctm, const SkPaint& paint, const SkMatrix* localM) const { auto matrix = SkMatrix::Concat(ctm, this->getLocalMatrix()); if (localM) { matrix.preConcat(*localM); } if (!matrix.invert(&matrix)) { return false; } auto quality = paint.getFilterQuality(); SkBitmapProvider provider(fImage.get(), dstCS); SkDefaultBitmapController controller(SkDefaultBitmapController::CanShadeHQ::kYes); std::unique_ptr state { controller.requestBitmap(provider, matrix, quality) }; if (!state) { return false; } const SkPixmap& pm = state->pixmap(); matrix = state->invMatrix(); quality = state->quality(); auto info = pm.info(); // When the matrix is just an integer translate, bilerp == nearest neighbor. if (quality == kLow_SkFilterQuality && matrix.getType() <= SkMatrix::kTranslate_Mask && matrix.getTranslateX() == (int)matrix.getTranslateX() && matrix.getTranslateY() == (int)matrix.getTranslateY()) { quality = kNone_SkFilterQuality; } // See skia:4649 and the GM image_scale_aligned. if (quality == kNone_SkFilterQuality) { if (matrix.getScaleX() >= 0) { matrix.setTranslateX(nextafterf(matrix.getTranslateX(), floorf(matrix.getTranslateX()))); } if (matrix.getScaleY() >= 0) { matrix.setTranslateY(nextafterf(matrix.getTranslateY(), floorf(matrix.getTranslateY()))); } } p->append(SkRasterPipeline::seed_shader); struct MiscCtx { std::unique_ptr state; SkColor4f paint_color; float matrix[9]; }; auto misc = alloc->make(); misc->state = std::move(state); // Extend lifetime to match the pipeline's. misc->paint_color = SkColor4f_from_SkColor(paint.getColor(), dstCS); if (matrix.asAffine(misc->matrix)) { p->append(SkRasterPipeline::matrix_2x3, misc->matrix); } else { matrix.get9(misc->matrix); p->append(SkRasterPipeline::matrix_perspective, misc->matrix); } auto gather = alloc->make(); gather->pixels = pm.addr(); gather->ctable = pm.ctable() ? pm.ctable()->readColors() : nullptr; gather->stride = pm.rowBytesAsPixels(); // Tiling stages (clamp_x, mirror_y, etc.) are inclusive of their limit, // so we tick down our width and height by one float to make them exclusive. auto ulp_before = [](float f) { uint32_t bits; memcpy(&bits, &f, 4); bits--; memcpy(&f, &bits, 4); return f; }; auto limit_x = alloc->make(ulp_before((float)pm. width())), limit_y = alloc->make(ulp_before((float)pm.height())); auto append_tiling_and_gather = [&] { switch (fTileModeX) { case kClamp_TileMode: p->append(SkRasterPipeline::clamp_x, limit_x); break; case kMirror_TileMode: p->append(SkRasterPipeline::mirror_x, limit_x); break; case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_x, limit_x); break; } switch (fTileModeY) { case kClamp_TileMode: p->append(SkRasterPipeline::clamp_y, limit_y); break; case kMirror_TileMode: p->append(SkRasterPipeline::mirror_y, limit_y); break; case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, limit_y); break; } switch (info.colorType()) { case kAlpha_8_SkColorType: p->append(SkRasterPipeline::gather_a8, gather); break; case kIndex_8_SkColorType: p->append(SkRasterPipeline::gather_i8, gather); break; case kGray_8_SkColorType: p->append(SkRasterPipeline::gather_g8, gather); break; case kRGB_565_SkColorType: p->append(SkRasterPipeline::gather_565, gather); break; case kARGB_4444_SkColorType: p->append(SkRasterPipeline::gather_4444, gather); break; case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: p->append(SkRasterPipeline::gather_8888, gather); break; case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::gather_f16, gather); break; default: SkASSERT(false); } if (dstCS && (!info.colorSpace() || info.gammaCloseToSRGB())) { p->append_from_srgb(info.alphaType()); } }; SkJumper_SamplerCtx* sampler = nullptr; if (quality != kNone_SkFilterQuality) { sampler = alloc->make(); } auto sample = [&](SkRasterPipeline::StockStage setup_x, SkRasterPipeline::StockStage setup_y) { p->append(setup_x, sampler); p->append(setup_y, sampler); append_tiling_and_gather(); p->append(SkRasterPipeline::accumulate, sampler); }; if (quality == kNone_SkFilterQuality) { append_tiling_and_gather(); } else if (quality == kLow_SkFilterQuality) { p->append(SkRasterPipeline::save_xy, sampler); sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_ny); sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_ny); sample(SkRasterPipeline::bilinear_nx, SkRasterPipeline::bilinear_py); sample(SkRasterPipeline::bilinear_px, SkRasterPipeline::bilinear_py); p->append(SkRasterPipeline::move_dst_src); } else { p->append(SkRasterPipeline::save_xy, sampler); sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y); sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n3y); sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n3y); sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n3y); sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n1y); sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_n1y); sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_n1y); sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_n1y); sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p1y); sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p1y); sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p1y); sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p1y); sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_p3y); sample(SkRasterPipeline::bicubic_n1x, SkRasterPipeline::bicubic_p3y); sample(SkRasterPipeline::bicubic_p1x, SkRasterPipeline::bicubic_p3y); sample(SkRasterPipeline::bicubic_p3x, SkRasterPipeline::bicubic_p3y); p->append(SkRasterPipeline::move_dst_src); } auto effective_color_type = [](SkColorType ct) { return ct == kIndex_8_SkColorType ? kN32_SkColorType : ct; }; if (effective_color_type(info.colorType()) == kBGRA_8888_SkColorType) { p->append(SkRasterPipeline::swap_rb); } if (info.colorType() == kAlpha_8_SkColorType) { p->append(SkRasterPipeline::set_rgb, &misc->paint_color); } if (info.colorType() == kAlpha_8_SkColorType || info.alphaType() == kUnpremul_SkAlphaType) { p->append(SkRasterPipeline::premul); } if (quality > kLow_SkFilterQuality) { // Bicubic filtering naturally produces out of range values on both sides. p->append(SkRasterPipeline::clamp_0); p->append(SkRasterPipeline::clamp_a); } append_gamut_transform(p, alloc, info.colorSpace(), dstCS, kPremul_SkAlphaType); return true; }