Rudimentary SkRuntimeImageFilter

This is really just a runtime shader with a late-bound input.

Bug: skia:12074 chromium:1213217
Change-Id: Ie92650a3e1e03d0c43ce4745eb33d49d582094f5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/416476
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Brian Osman 2021-05-26 16:51:08 -04:00 committed by Skia Commit-Bot
parent 9e67891b72
commit a784914e74
7 changed files with 238 additions and 0 deletions

73
gm/runtimeimagefilter.cpp Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPixelRef.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/effects/SkImageFilters.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/utils/SkRandom.h"
#include "src/effects/imagefilters/SkRuntimeImageFilter.h"
#include "tools/ToolUtils.h"
static sk_sp<SkImageFilter> make_filter() {
sk_sp<SkRuntimeEffect> effect = SkRuntimeEffect::MakeForShader(SkString(R"(
uniform shader input;
half4 main(float2 coord) {
coord.x += sin(coord.y / 3) * 4;
return sample(input, coord);
}
)")).effect;
return SkMakeRuntimeImageFilter(std::move(effect),
/*uniforms=*/nullptr,
/*input=*/nullptr);
}
DEF_SIMPLE_GM_BG(rtif_distort, canvas, 500, 750, SK_ColorBLACK) {
SkRect clip = SkRect::MakeWH(250, 250);
SkPaint filterPaint;
filterPaint.setImageFilter(make_filter());
auto draw_layer = [&](SkScalar tx, SkScalar ty, SkMatrix m) {
canvas->save();
canvas->translate(tx, ty);
canvas->clipRect(clip);
canvas->concat(m);
canvas->saveLayer(nullptr, &filterPaint);
const char* str = "The quick brown fox jumped over the lazy dog.";
SkRandom rand;
SkFont font(ToolUtils::create_portable_typeface());
for (int i = 0; i < 25; ++i) {
int x = rand.nextULessThan(500);
int y = rand.nextULessThan(500);
SkPaint paint;
paint.setColor(ToolUtils::color_to_565(rand.nextBits(24) | 0xFF000000));
font.setSize(rand.nextRangeScalar(0, 300));
canvas->drawString(str, SkIntToScalar(x), SkIntToScalar(y), font, paint);
}
canvas->restore();
canvas->restore();
};
draw_layer( 0, 0, SkMatrix::I());
draw_layer(250, 0, SkMatrix::Scale(0.5f, 0.5f));
draw_layer( 0, 250, SkMatrix::RotateDeg(45, {125, 125}));
draw_layer(250, 250, SkMatrix::Scale(0.5f, 0.5f) * SkMatrix::RotateDeg(45, {125, 125}));
draw_layer( 0, 500, SkMatrix::Skew(-0.5f, 0));
SkMatrix p = SkMatrix::I();
p.setPerspX(0.0015f);
p.setPerspY(-0.0015f);
draw_layer(250, 500, p);
}

View File

@ -26,6 +26,7 @@ skia_effects_imagefilter_sources = [
"$_src/effects/imagefilters/SkMorphologyImageFilter.cpp",
"$_src/effects/imagefilters/SkOffsetImageFilter.cpp",
"$_src/effects/imagefilters/SkPictureImageFilter.cpp",
"$_src/effects/imagefilters/SkRuntimeImageFilter.cpp",
"$_src/effects/imagefilters/SkShaderImageFilter.cpp",
"$_src/effects/imagefilters/SkTileImageFilter.cpp",
]

View File

@ -325,6 +325,7 @@ gm_sources = [
"$_gm/runtimecolorfilter.cpp",
"$_gm/runtimeeffectimage.cpp",
"$_gm/runtimefunctions.cpp",
"$_gm/runtimeimagefilter.cpp",
"$_gm/runtimeintrinsics.cpp",
"$_gm/runtimeshader.cpp",
"$_gm/samplerstress.cpp",

View File

@ -491,6 +491,7 @@ void SkRegisterMergeImageFilterFlattenable();
void SkRegisterMorphologyImageFilterFlattenables();
void SkRegisterOffsetImageFilterFlattenable();
void SkRegisterPictureImageFilterFlattenable();
void SkRegisterRuntimeImageFilterFlattenable();
void SkRegisterShaderImageFilterFlattenable();
void SkRegisterTileImageFilterFlattenable();

View File

@ -0,0 +1,139 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/effects/SkImageFilters.h"
#include "include/effects/SkRuntimeEffect.h"
#include "src/core/SkImageFilter_Base.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/core/SkSpecialImage.h"
#include "src/core/SkSpecialSurface.h"
#include "src/core/SkWriteBuffer.h"
#include "src/effects/imagefilters/SkRuntimeImageFilter.h"
namespace {
class SkRuntimeImageFilter final : public SkImageFilter_Base {
public:
SkRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
sk_sp<SkData> uniforms,
sk_sp<SkImageFilter> input)
: INHERITED(&input, 1, /*cropRect=*/nullptr)
, fEffect(std::move(effect))
, fUniforms(std::move(uniforms)) {}
bool affectsTransparentBlack() const override { return true; }
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kTranslate; }
protected:
void flatten(SkWriteBuffer&) const override;
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
private:
friend void ::SkRegisterRuntimeImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkRuntimeImageFilter)
sk_sp<SkRuntimeEffect> fEffect;
sk_sp<SkData> fUniforms;
using INHERITED = SkImageFilter_Base;
};
} // end namespace
sk_sp<SkImageFilter> SkMakeRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
sk_sp<SkData> uniforms,
sk_sp<SkImageFilter> input) {
// Rather than replicate all of the checks from makeShader here, just try to create a shader
// once, to determine if everything is valid.
sk_sp<SkShader> child = nullptr;
auto shader = effect->makeShader(uniforms, &child, 1, nullptr, false);
if (!shader) {
// Could be wrong signature, wrong uniform block size, wrong number/type of children, etc...
return nullptr;
}
return sk_sp<SkImageFilter>(
new SkRuntimeImageFilter(std::move(effect), std::move(uniforms), std::move(input)));
}
void SkRegisterRuntimeImageFilterFlattenable() {
SK_REGISTER_FLATTENABLE(SkRuntimeImageFilter);
}
sk_sp<SkFlattenable> SkRuntimeImageFilter::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
SkString sksl;
buffer.readString(&sksl);
sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
if (!buffer.validate(effect != nullptr)) {
return nullptr;
}
if (common.cropRect()) {
return nullptr;
}
return SkMakeRuntimeImageFilter(std::move(effect), std::move(uniforms), common.getInput(0));
}
void SkRuntimeImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.writeString(fEffect->source().c_str());
buffer.writeDataAsByteArray(fUniforms.get());
}
///////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkSpecialImage> SkRuntimeImageFilter::onFilterImage(const Context& ctx,
SkIPoint* offset) const {
SkIPoint inputOffset = SkIPoint::Make(0, 0);
sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset));
if (!input) {
return nullptr;
}
SkIRect outputBounds = SkIRect(ctx.desiredOutput());
sk_sp<SkSpecialSurface> surf(ctx.makeSurface(outputBounds.size()));
if (!surf) {
return nullptr;
}
SkMatrix ctm = ctx.ctm();
SkMatrix inverse;
SkAssertResult(ctm.invert(&inverse));
SkMatrix localM = inverse *
SkMatrix::Translate(inputOffset) *
SkMatrix::Translate(-input->subset().topLeft());
sk_sp<SkShader> inputShader =
input->asImage()->makeShader(SkSamplingOptions(SkFilterMode::kLinear), &localM);
SkASSERT(inputShader);
auto shader = fEffect->makeShader(fUniforms, &inputShader, 1, nullptr, false);
SkASSERT(shader);
SkPaint paint;
paint.setShader(std::move(shader));
paint.setBlendMode(SkBlendMode::kSrc);
SkCanvas* canvas = surf->getCanvas();
SkASSERT(canvas);
// Translate from layer space into surf's image space
canvas->translate(-outputBounds.fLeft, -outputBounds.fTop);
// Ensure shader parameters are relative to parameter space, not layer space
canvas->concat(ctx.ctm());
canvas->drawPaint(paint);
*offset = outputBounds.topLeft();
return surf->makeImageSnapshot();
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkRuntimeImageFilter_DEFINED
#define SkRuntimeImageFilter_DEFINED
#include "include/core/SkRefCnt.h"
class SkData;
class SkImageFilter;
struct SkRect;
class SkRuntimeEffect;
SK_API sk_sp<SkImageFilter> SkMakeRuntimeImageFilter(sk_sp<SkRuntimeEffect> effect,
sk_sp<SkData> uniforms,
sk_sp<SkImageFilter> input);
#endif

View File

@ -130,6 +130,7 @@
SkRegisterMorphologyImageFilterFlattenables();
SkRegisterOffsetImageFilterFlattenable();
SkRegisterPictureImageFilterFlattenable();
SkRegisterRuntimeImageFilterFlattenable();
SkRegisterShaderImageFilterFlattenable();
SkRegisterTileImageFilterFlattenable();
SK_REGISTER_FLATTENABLE(SkLocalMatrixImageFilter);