Implement SkBlender support in SkVM.

A lot of the SkVM-related setup was already hooked up in
http://review.skia.org/416916. This CL finishes the job by hooking up
the SkPaint's SkBlender field to SkVM, and adds various checks
throughout Skia's software drawing code that can detect the presence of
an SkBlender.

Since only SkVM knows how to render an SkBlender correctly, we now need
to avoid the legacy blitter and RasterPipeline blitter whenever an
SkBlender is present on the paint. This CL fixes the cases that I've
found so far, while testing rendering with ovals and images. I'm sure
there are more cases lurking; these will be uncovered and dealt with in
future CLs.

Change-Id: I1a7b3d6625352b3cba8e4f8d4c61129d08ac6ae7
Bug: skia:12080
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/419576
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2021-06-18 10:15:35 -04:00 committed by Skia Commit-Bot
parent 3177f853ea
commit 9a2aa9708f
7 changed files with 41 additions and 26 deletions

View File

@ -467,6 +467,10 @@ void SkBitmapDevice::drawImageRect(const SkImage* image, const SkRect* src, cons
}
}
if (paint.getBlender()) {
goto USE_SHADER;
}
if (src && !src->contains(bitmapBounds) &&
SkCanvas::kFast_SrcRectConstraint == constraint &&
sampling != SkSamplingOptions()) {

View File

@ -662,6 +662,7 @@ bool SkBlitter::UseLegacyBlitter(const SkPixmap& device,
// The legacy blitters cannot handle any of these complex features (anymore).
if (device.alphaType() == kUnpremul_SkAlphaType ||
paint.getBlender() ||
paint.getBlendMode() > SkBlendMode::kLastCoeffMode ||
(mf && mf->getFormat() == SkMask::k3D_Format)) {
return false;
@ -703,26 +704,28 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device,
// We may tweak the original paint as we go.
SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
// We have the most fast-paths for SrcOver, so see if we can act like SrcOver.
if (paint->getBlendMode() != SkBlendMode::kSrcOver) {
switch (SkInterpretXfermode(*paint, SkColorTypeIsAlwaysOpaque(device.colorType()))) {
case kSrcOver_SkXfermodeInterpretation:
paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
break;
case kSkipDrawing_SkXfermodeInterpretation:
return alloc->make<SkNullBlitter>();
default:
break;
if (!paint->getBlender()) {
// We have the most fast-paths for SrcOver, so see if we can act like SrcOver.
if (paint->getBlendMode() != SkBlendMode::kSrcOver) {
switch (SkInterpretXfermode(*paint, SkColorTypeIsAlwaysOpaque(device.colorType()))) {
case kSrcOver_SkXfermodeInterpretation:
paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
break;
case kSkipDrawing_SkXfermodeInterpretation:
return alloc->make<SkNullBlitter>();
default:
break;
}
}
}
// A Clear blend mode will ignore the entire color pipeline, as if Src mode with 0x00000000.
if (paint->getBlendMode() == SkBlendMode::kClear) {
SkPaint* p = paint.writable();
p->setShader(nullptr);
p->setColorFilter(nullptr);
p->setBlendMode(SkBlendMode::kSrc);
p->setColor(0x00000000);
// A Clear blend mode will ignore the entire color pipeline, as if Src mode with 0x00000000.
if (paint->getBlendMode() == SkBlendMode::kClear) {
SkPaint* p = paint.writable();
p->setShader(nullptr);
p->setColorFilter(nullptr);
p->setBlendMode(SkBlendMode::kSrc);
p->setColor(0x00000000);
}
}
if (paint->getColorFilter()) {

View File

@ -2131,7 +2131,7 @@ void SkCanvas::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
}
if (realPaint.getImageFilter() &&
this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), sampling, realPaint) &&
this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), sampling, realPaint) &&
!image_to_color_filter(&realPaint)) {
// Evaluate the image filter directly on the input image and then draw the result, instead
// of first drawing the image to a temporary layer and filtering.

View File

@ -1127,7 +1127,8 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
SkDraw draw(*this);
draw.fMatrixProvider = &matrixProvider;
if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter() &&
!paint->getBlender()) {
draw.drawBitmapAsMask(bitmap, sampling, *paint);
} else {
SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);

View File

@ -90,6 +90,11 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
const SkMatrixProvider& matrixProvider,
SkArenaAlloc* alloc,
sk_sp<SkShader> clipShader) {
if (paint.getBlender()) {
// The raster pipeline doesn't support SkBlender.
return nullptr;
}
SkColorSpace* dstCS = dst.colorSpace();
SkColorType dstCT = dst.colorType();
SkColor4f paintColor = paint.getColor4f();

View File

@ -357,7 +357,7 @@ namespace {
}
// Clamp to fit destination color format if needed.
if (src_in_gamut) {
if (!params.blender && src_in_gamut) {
// An in-gamut src blended with an in-gamut dst should stay in gamut.
// Being in-gamut implies all channels are in [0,1], so no need to clamp.
// We allow one ulp error above 1.0f, and about that much (~1.2e-7) below 0.
@ -556,6 +556,9 @@ namespace {
shader = sk_make_sp<DitherShader>(std::move(shader));
}
// Add the user blend function.
sk_sp<SkBlender> blender = paint.refBlender();
// The most common blend mode is SrcOver, and it can be strength-reduced
// _greatly_ to Src mode when the shader is opaque.
//
@ -570,8 +573,8 @@ namespace {
// not just a property of the uniforms. The shader program hash includes
// this information, making it safe to use anywhere in the blitter codegen.
SkBlendMode blendMode = paint.getBlendMode();
if (blendMode == SkBlendMode::kSrcOver && shader->isOpaque()) {
blendMode = SkBlendMode::kSrc;
if ((blendMode == SkBlendMode::kSrcOver && shader->isOpaque()) || blender) {
blendMode = SkBlendMode::kSrc;
}
SkColor4f paintColor = paint.getColor4f();
@ -582,7 +585,7 @@ namespace {
return {
std::move(shader),
std::move(clip),
/*blender=*/nullptr,
std::move(blender),
{ device.colorType(), device.alphaType(), device.refColorSpace() },
blendMode,
Coverage::Full, // Placeholder... withCoverage() will change as needed.

View File

@ -604,8 +604,7 @@ static void test_RuntimeEffect_Blenders(skiatest::Reporter* r, GrRecordingContex
}
DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {
// TODO(skia:12080): add CPU support for SkBlender
// test_RuntimeEffect_Blenders(r, /*rContext=*/nullptr);
test_RuntimeEffect_Blenders(r, /*rContext=*/nullptr);
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRuntimeEffect_Blender_GPU, r, ctxInfo) {