bool SkComposeShader::onAppendStages(const StageRec& rec) const { struct Storage { float fRGBA[4 * SkJumper_kMaxStride]; float fAlpha; }; auto storage = rec.fAlloc->make<Storage>(); if (!as_SB(fSrc)->appendStages(rec)) { return false; } // This outputs r,g,b,a, which we'll need later when we apply the mode, but we save it off now // since fShaderB will overwrite them. rec.fPipeline->append(SkRasterPipeline::store_rgba, storage->fRGBA); if (!as_SB(fDst)->appendStages(rec)) { return false; } // We now have our logical 'dst' in r,g,b,a, but we need it in dr,dg,db,da for the mode/lerp // so we have to shuttle them. If we had a stage the would load_into_dst, then we could // reverse the two shader invocations, and avoid this move... rec.fPipeline->append(SkRasterPipeline::move_src_dst); rec.fPipeline->append(SkRasterPipeline::load_rgba, storage->fRGBA); if (!this->isJustLerp()) { SkBlendMode_AppendStages(fMode, rec.fPipeline); } if (!this->isJustMode()) { rec.fPipeline->append(SkRasterPipeline::lerp_1_float, &fLerpT); } return true; }
void SkComposeShader::toString(SkString* str) const { str->append("SkComposeShader: ("); str->append("dst: "); as_SB(fDst)->toString(str); str->append(" src: "); as_SB(fSrc)->toString(str); str->appendf(" mode: %s", SkBlendMode_Name(fMode)); str->appendf(" lerpT: %g", fLerpT); this->INHERITED::toString(str); str->append(")"); }
bool SkColorFilterShader::onAppendStages(const StageRec& rec) const { if (!as_SB(fShader)->appendStages(rec)) { return false; } fFilter->appendStages(rec.fPipeline, rec.fDstCS, rec.fAlloc, fShader->isOpaque()); return true; }
bool SkPaintToGrPaintWithTexture(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo, const SkPaint& paint, const SkMatrix& viewM, std::unique_ptr<GrFragmentProcessor> fp, bool textureIsAlphaOnly, GrPaint* grPaint) { std::unique_ptr<GrFragmentProcessor> shaderFP; if (textureIsAlphaOnly) { if (const auto* shader = as_SB(paint.getShader())) { shaderFP = shader->asFragmentProcessor(GrFPArgs( context, &viewM, nullptr, paint.getFilterQuality(), &colorSpaceInfo)); if (!shaderFP) { return false; } std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(shaderFP), std::move(fp) }; shaderFP = GrFragmentProcessor::RunInSeries(fpSeries, 2); } else { shaderFP = GrFragmentProcessor::MakeInputPremulAndMulByOutput(std::move(fp)); } } else { shaderFP = GrFragmentProcessor::MulChildByInputAlpha(std::move(fp)); } return SkPaintToGrPaintReplaceShader(context, colorSpaceInfo, paint, std::move(shaderFP), grPaint); }
static sk_sp<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) { SkPoint pts[2] = { {0, 0}, {1, 1} }; SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE }; sk_sp<SkShader> shader = SkGradientShader::MakeLinear( pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); SkShaderBase::AsFPArgs args( ctx, &SkMatrix::I(), &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, nullptr); return as_SB(shader)->asFragmentProcessor(args); }
sk_sp<GrFragmentProcessor> SkNormalMapSourceImpl::asFragmentProcessor( const SkShaderBase::AsFPArgs& args) const { sk_sp<GrFragmentProcessor> mapFP = as_SB(fMapShader)->asFragmentProcessor(args); if (!mapFP) { return nullptr; } return NormalMapFP::Make(std::move(mapFP), fInvCTM); }
static std::unique_ptr<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) { SkPoint pts[2] = { {0, 0}, {1, 1} }; SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE }; sk_sp<SkShader> shader = SkGradientShader::MakeLinear( pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode); GrColorSpaceInfo colorSpaceInfo(nullptr, kRGBA_8888_GrPixelConfig); GrFPArgs args(ctx, &SkMatrix::I(), &SkMatrix::I(), SkFilterQuality::kLow_SkFilterQuality, &colorSpaceInfo); return as_SB(shader)->asFragmentProcessor(args); }
void SkColorFilterShader::toString(SkString* str) const { str->append("SkColorFilterShader: ("); str->append("Shader: "); as_SB(fShader)->toString(str); str->append(" Filter: "); // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added this->INHERITED::toString(str); str->append(")"); }
std::unique_ptr<GrFragmentProcessor> SkComposeShader::asFragmentProcessor( const GrFPArgs& args) const { if (this->isJustMode()) { SkASSERT(fMode != SkBlendMode::kSrc && fMode != SkBlendMode::kDst); // caught in factory if (fMode == SkBlendMode::kClear) { return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), GrConstColorProcessor::InputMode::kIgnore); } } std::unique_ptr<GrFragmentProcessor> fpA(as_SB(fDst)->asFragmentProcessor(args)); if (!fpA) { return nullptr; } std::unique_ptr<GrFragmentProcessor> fpB(as_SB(fSrc)->asFragmentProcessor(args)); if (!fpB) { return nullptr; } // TODO: account for fLerpT when it is < 1 return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB), std::move(fpA), fMode); }
std::unique_ptr<GrFragmentProcessor> SkColorFilterShader::asFragmentProcessor( const GrFPArgs& args) const { auto fp1 = as_SB(fShader)->asFragmentProcessor(args); if (!fp1) { return nullptr; } auto fp2 = fFilter->asFragmentProcessor(args.fContext, *args.fDstColorSpaceInfo); if (!fp2) { return fp1; } std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(fp1), std::move(fp2) }; return GrFragmentProcessor::RunInSeries(fpSeries, 2); }
bool SkPaintPriv::ShouldDither(const SkPaint& p, SkColorType dstCT) { // The paint dither flag can veto. if (!p.isDither()) { return false; } // We always dither 565 or 4444 when requested. if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) { return true; } // Otherwise, dither is only needed for non-const paints. return p.getImageFilter() || p.getMaskFilter() || !p.getShader() || !as_SB(p.getShader())->isConstant(); }
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst, const SkPaint& paint, const SkMatrix& ctm, SkArenaAlloc* alloc) { SkColorSpace* dstCS = dst.colorSpace(); SkPM4f paintColor = SkPM4f_from_SkColor(paint.getColor(), dstCS); auto shader = as_SB(paint.getShader()); SkRasterPipeline_<256> shaderPipeline; if (!shader) { // Having no shader makes things nice and easy... just use the paint color. shaderPipeline.append_uniform_color(alloc, paintColor); bool is_opaque = paintColor.a() == 1.0f, is_constant = true; return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, nullptr, is_opaque, is_constant); } bool is_opaque = shader->isOpaque() && paintColor.a() == 1.0f; bool is_constant = shader->isConstant(); // Check whether the shader prefers to run in burst mode. if (auto* burstCtx = shader->makeBurstPipelineContext( SkShaderBase::ContextRec(paint, ctm, nullptr, SkShaderBase::ContextRec::kPM4f_DstType, dstCS), alloc)) { return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, burstCtx, is_opaque, is_constant); } if (shader->appendStages(&shaderPipeline, dstCS, alloc, ctm, paint)) { if (paintColor.a() != 1.0f) { shaderPipeline.append(SkRasterPipeline::scale_1_float, alloc->make<float>(paintColor.a())); } return SkRasterPipelineBlitter::Create(dst, paint, alloc, shaderPipeline, nullptr, is_opaque, is_constant); } // The shader has opted out of drawing anything. return alloc->make<SkNullBlitter>(); }
SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider(const SkShaderBase::ContextRec &rec, SkArenaAlloc* alloc) const { SkMatrix normTotalInv; if (!this->computeNormTotalInverse(rec, &normTotalInv)) { return nullptr; } // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd SkPaint overridePaint {*(rec.fPaint)}; overridePaint.setAlpha(0xFF); SkShaderBase::ContextRec overrideRec(overridePaint, *(rec.fMatrix), rec.fLocalMatrix, rec.fPreferredDstType, rec.fDstColorSpace); auto* context = as_SB(fMapShader)->makeContext(overrideRec, alloc); if (!context) { return nullptr; } return alloc->make<Provider>(*this, context); }
sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) { const AutoCachePurge autoPurge(this); return as_SB(shader)->makeColorSpace(this); }
SkSpecialImage* SkSpecialSurface::newImageSnapshot() { SkSpecialImage* image = as_SB(this)->onNewImageSnapshot(); as_SB(this)->reset(); return image; // the caller gets the creation ref }
std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::TestCreate( GrProcessorTestData* d) { SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; SkPoint center2 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; SkScalar radius1 = d->fRandom->nextUScalar1(); SkScalar radius2 = d->fRandom->nextUScalar1(); constexpr int kTestTypeMask = (1 << 2) - 1, kTestNativelyFocalBit = (1 << 2), kTestFocalOnCircleBit = (1 << 3), kTestSwappedBit = (1 << 4); // We won't treat isWellDefined and isRadiusIncreasing specially beacuse they // should have high probability to be turned on and off as we're getting random // radii and centers. int mask = d->fRandom->nextU(); int type = mask & kTestTypeMask; if (type == static_cast<int>(TwoPointConicalEffect::Type::kRadial)) { center2 = center1; // Make sure that the radii are different if (SkScalarNearlyZero(radius1 - radius2)) { radius2 += .1f; } } else if (type == static_cast<int>(TwoPointConicalEffect::Type::kStrip)) { radius1 = SkTMax(radius1, .1f); // Make sure that the radius is non-zero radius2 = radius1; // Make sure that the centers are different if (SkScalarNearlyZero(SkPoint::Distance(center1, center2))) { center2.fX += .1f; } } else { // kFocal_Type // Make sure that the centers are different if (SkScalarNearlyZero(SkPoint::Distance(center1, center2))) { center2.fX += .1f; } if (kTestNativelyFocalBit & mask) { radius1 = 0; } if (kTestFocalOnCircleBit & mask) { radius2 = radius1 + SkPoint::Distance(center1, center2); } if (kTestSwappedBit & mask) { std::swap(radius1, radius2); radius2 = 0; } // Make sure that the radii are different if (SkScalarNearlyZero(radius1 - radius2)) { radius2 += .1f; } } if (SkScalarNearlyZero(radius1 - radius2) && SkScalarNearlyZero(SkPoint::Distance(center1, center2))) { radius2 += .1f; // make sure that we're not degenerated } RandomGradientParams params(d->fRandom); auto shader = params.fUseColors4f ? SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, params.fColors4f, params.fColorSpace, params.fStops, params.fColorCount, params.fTileMode) : SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, params.fColors, params.fStops, params.fColorCount, params.fTileMode); GrTest::TestAsFPArgs asFPArgs(d); std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); GrAlwaysAssert(fp); return fp; }
static inline bool skpaint_to_grpaint_impl(GrContext* context, const GrColorSpaceInfo& colorSpaceInfo, const SkPaint& skPaint, const SkMatrix& viewM, std::unique_ptr<GrFragmentProcessor>* shaderProcessor, SkBlendMode* primColorMode, GrPaint* grPaint) { grPaint->setAllowSRGBInputs(colorSpaceInfo.isGammaCorrect()); // Convert SkPaint color to 4f format, including optional linearizing and gamut conversion. GrColor4f origColor = SkColorToUnpremulGrColor4f(skPaint.getColor(), colorSpaceInfo); const GrFPArgs fpArgs(context, &viewM, skPaint.getFilterQuality(), &colorSpaceInfo); // Setup the initial color considering the shader, the SkPaint color, and the presence or not // of per-vertex colors. std::unique_ptr<GrFragmentProcessor> shaderFP; if (!primColorMode || blend_requires_shader(*primColorMode)) { if (shaderProcessor) { shaderFP = std::move(*shaderProcessor); } else if (const auto* shader = as_SB(skPaint.getShader())) { shaderFP = shader->asFragmentProcessor(fpArgs); if (!shaderFP) { return false; } } } // Set this in below cases if the output of the shader/paint-color/paint-alpha/primXfermode is // a known constant value. In that case we can simply apply a color filter during this // conversion without converting the color filter to a GrFragmentProcessor. bool applyColorFilterToPaintColor = false; if (shaderFP) { if (primColorMode) { // There is a blend between the primitive color and the shader color. The shader sees // the opaque paint color. The shader's output is blended using the provided mode by // the primitive color. The blended color is then modulated by the paint's alpha. // The geometry processor will insert the primitive color to start the color chain, so // the GrPaint color will be ignored. GrColor4f shaderInput = origColor.opaque(); shaderFP = GrFragmentProcessor::OverrideInput(std::move(shaderFP), shaderInput); shaderFP = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(shaderFP), *primColorMode); // The above may return null if compose results in a pass through of the prim color. if (shaderFP) { grPaint->addColorFragmentProcessor(std::move(shaderFP)); } // We can ignore origColor here - alpha is unchanged by gamma GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); if (GrColor_WHITE != paintAlpha) { // No gamut conversion - paintAlpha is a (linear) alpha value, splatted to all // color channels. It's value should be treated as the same in ANY color space. grPaint->addColorFragmentProcessor(GrConstColorProcessor::Make( GrColor4f::FromGrColor(paintAlpha), GrConstColorProcessor::InputMode::kModulateRGBA)); } } else { // The shader's FP sees the paint unpremul color grPaint->setColor4f(origColor); grPaint->addColorFragmentProcessor(std::move(shaderFP)); } } else { if (primColorMode) { // There is a blend between the primitive color and the paint color. The blend considers // the opaque paint color. The paint's alpha is applied to the post-blended color. auto processor = GrConstColorProcessor::Make(origColor.opaque(), GrConstColorProcessor::InputMode::kIgnore); processor = GrXfermodeFragmentProcessor::MakeFromSrcProcessor(std::move(processor), *primColorMode); if (processor) { grPaint->addColorFragmentProcessor(std::move(processor)); } grPaint->setColor4f(origColor.opaque()); // We can ignore origColor here - alpha is unchanged by gamma GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); if (GrColor_WHITE != paintAlpha) { // No gamut conversion - paintAlpha is a (linear) alpha value, splatted to all // color channels. It's value should be treated as the same in ANY color space. grPaint->addColorFragmentProcessor(GrConstColorProcessor::Make( GrColor4f::FromGrColor(paintAlpha), GrConstColorProcessor::InputMode::kModulateRGBA)); } } else { // No shader, no primitive color. grPaint->setColor4f(origColor.premul()); applyColorFilterToPaintColor = true; } } SkColorFilter* colorFilter = skPaint.getColorFilter(); if (colorFilter) { if (applyColorFilterToPaintColor) { // If we're in legacy mode, we *must* avoid using the 4f version of the color filter, // because that will combine with the linearized version of the stored color. if (colorSpaceInfo.isGammaCorrect()) { grPaint->setColor4f(GrColor4f::FromSkColor4f( colorFilter->filterColor4f(origColor.toSkColor4f())).premul()); } else { grPaint->setColor4f(SkColorToPremulGrColor4fLegacy( colorFilter->filterColor(skPaint.getColor()))); } } else { auto cfFP = colorFilter->asFragmentProcessor(context, colorSpaceInfo); if (cfFP) { grPaint->addColorFragmentProcessor(std::move(cfFP)); } else { return false; } } } SkMaskFilterBase* maskFilter = as_MFB(skPaint.getMaskFilter()); if (maskFilter) { if (auto mfFP = maskFilter->asFragmentProcessor(fpArgs)) { grPaint->addCoverageFragmentProcessor(std::move(mfFP)); } } // When the xfermode is null on the SkPaint (meaning kSrcOver) we need the XPFactory field on // the GrPaint to also be null (also kSrcOver). SkASSERT(!grPaint->getXPFactory()); if (!skPaint.isSrcOver()) { grPaint->setXPFactory(SkBlendMode_AsXPFactory(skPaint.getBlendMode())); } #ifndef SK_IGNORE_GPU_DITHER // Conservative default, in case GrPixelConfigToColorType() fails. SkColorType ct = SkColorType::kRGB_565_SkColorType; GrPixelConfigToColorType(colorSpaceInfo.config(), &ct); if (SkPaintPriv::ShouldDither(skPaint, ct) && grPaint->numColorFragmentProcessors() > 0 && !colorSpaceInfo.isGammaCorrect()) { auto ditherFP = GrDitherEffect::Make(colorSpaceInfo.config()); if (ditherFP) { grPaint->addColorFragmentProcessor(std::move(ditherFP)); } } #endif return true; }
sk_sp<SkSpecialImage> SkSpecialSurface::makeImageSnapshot() { sk_sp<SkSpecialImage> image(as_SB(this)->onMakeImageSnapshot()); as_SB(this)->reset(); return image; // the caller gets the creation ref }
SkCanvas* SkSpecialSurface::getCanvas() { return as_SB(this)->onGetCanvas(); }