Add SkRuntimeEffect::makeImage()

Allows creation of an image directly from a RTE. Caller provides the
effect input bindings, image info for the image, and optional local
matrix.

The info's alpha type and colorspace are tags for the output image. No
alpha type or color space conversions are applied to the output of RTE.

CPU does not yet support making kUnpremul images.

Bug: chromium:1151490

Change-Id: I69babc9dbbce4431d756cd7a3eef4753e727d6fe
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/357284
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Salomon 2021-01-23 11:41:54 -05:00 committed by Skia Commit-Bot
parent 9702fc6f38
commit 04aef10c64
8 changed files with 286 additions and 21 deletions

View File

@ -7,6 +7,9 @@ This file includes a list of high level updates for each milestone release.
Milestone 90
------------
* Added SkRuntimeEffect::makeImage() to capture the output of an SkRuntimeEffect in an SkImage.
https://review.skia.org/357284
* Deprecate (and ignore) SkAndroidCodec::ExifOrientation
https://review.skia.org/344763

135
gm/runtimeeffectimage.cpp Normal file
View File

@ -0,0 +1,135 @@
/*
* 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 "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkRuntimeEffect.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
class RuntimeEffectImage : public skiagm::GM {
public:
RuntimeEffectImage() = default;
SkString onShortName() override { return SkString("runtime_effect_image"); }
SkISize onISize() override { return {800, 200}; }
protected:
void onOnceBeforeDraw() override {
SkString sksl(R"(
uniform shader child;
uniform half gAlphaType; // 0 is premul, non-zero unpremul.
half4 main(float2 p) {
half r = fract(p.x/20);
half g = fract(p.y/20);
half b = fract((p.x + 5)/10);
half a = min(distance(p, vec2(50, 50))/50 + 0.3, 1);
half4 result = half4(r, g, b, a);
result *= sample(child);
if (gAlphaType == 0) {
result.rgb *= a;
}
return result;
}
)");
auto [effect, error] = SkRuntimeEffect::Make(sksl);
if (!effect) {
SkDebugf("RuntimeShader error: %s\n", error.c_str());
}
fEffect = std::move(effect);
}
DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
GrRecordingContext* rc = canvas->recordingContext();
SkRuntimeShaderBuilder builder(fEffect);
SkImageInfo info = SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
SkSamplingOptions nn{SkFilterMode::kNearest};
SkSamplingOptions mm{SkFilterMode::kLinear, SkMipmapMode::kNearest};
auto whiteShader = SkShaders::Color(SK_ColorWHITE);
auto imageForShader = GetResourceAsImage("images/ducky.jpg");
if (!imageForShader) {
*errorMsg = "Could not load image";
return DrawResult::kFail;
}
auto imageShader = imageForShader->makeShader(SkTileMode::kRepeat,
SkTileMode::kRepeat,
SkSamplingOptions(SkFilterMode::kLinear),
SkMatrix::Scale(0.2f, 0.2f));
builder.uniform("gAlphaType") = 0.f;
builder.child("child") = whiteShader;
ToolUtils::draw_checkerboard(canvas, SK_ColorWHITE, SK_ColorLTGRAY, 50);
canvas->translate(5, 5);
SkPaint aaPaint;
aaPaint.setAntiAlias(true);
for (const auto& vm : {SkMatrix::Scale(1.5f, 1.5f), SkMatrix::Scale(0.3f, 0.3f)}) {
canvas->save();
canvas->concat(vm);
sk_sp<SkImage> image;
// basic
image = builder.makeImage(rc, nullptr, info, false);
canvas->drawImage(image, 0, 0, nn, &aaPaint);
canvas->translate(105, 0);
// local matrix
SkMatrix lm = SkMatrix::RotateDeg(45, {50, 50});
lm.preTranslate(10, 10);
image = builder.makeImage(rc, &lm, info, false);
canvas->drawImage(image, 0, 0, nn, &aaPaint);
canvas->translate(105, 0);
// unpremul
if (rc) {
// use a uniform to make the effect output be unpremul so it looks the same as the
// premul case when drawn to the canvas.
builder.uniform("gAlphaType") = 1.f;
image = builder.makeImage(rc,
nullptr,
info.makeAlphaType(kUnpremul_SkAlphaType),
false);
builder.uniform("gAlphaType") = 0.f;
canvas->drawImage(image, 0, 0, nn, &aaPaint);
canvas->translate(105, 0);
} else {
// CPU doesn't yet support making kUnpremul images. Just draw the basic one again.
image = builder.makeImage(nullptr, nullptr, info, false);
canvas->drawImage(image, 0, 0, nn, &aaPaint);
canvas->translate(105, 0);
}
// color space
sk_sp<SkColorSpace> cs = SkColorSpace::MakeSRGB()->makeColorSpin();
image = builder.makeImage(rc, nullptr, info.makeColorSpace(std::move(cs)), false);
canvas->drawImage(image, 0, 0, nn, &aaPaint);
canvas->translate(105, 0);
// mipmapped and different child
builder.child("child") = imageShader;
image = builder.makeImage(rc, nullptr, info, true);
builder.child("child") = whiteShader;
canvas->drawImage(image, 0, 0, mm, &aaPaint);
canvas->translate(105, 0);
canvas->restore();
canvas->translate(0, 105*vm.getScaleY());
}
return DrawResult::kOk;
}
private:
sk_sp<SkRuntimeEffect> fEffect;
};
DEF_GM(return new RuntimeEffectImage;)

View File

@ -316,6 +316,7 @@ gm_sources = [
"$_gm/rrects.cpp",
"$_gm/rsxtext.cpp",
"$_gm/runtimecolorfilter.cpp",
"$_gm/runtimeeffectimage.cpp",
"$_gm/runtimefunctions.cpp",
"$_gm/runtimeintrinsics.cpp",
"$_gm/runtimeshader.cpp",

View File

@ -85,6 +85,14 @@ public:
const SkMatrix* localMatrix,
bool isOpaque);
sk_sp<SkImage> makeImage(GrRecordingContext*,
sk_sp<SkData> uniforms,
sk_sp<SkShader> children[],
size_t childCount,
const SkMatrix* localMatrix,
SkImageInfo resultInfo,
bool mipmapped);
sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms);
sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> uniforms,
sk_sp<SkColorFilter> children[],
@ -255,6 +263,10 @@ public:
BuilderChild child(const char* name) { return { this, fEffect->findChild(name) }; }
sk_sp<SkShader> makeShader(const SkMatrix* localMatrix, bool isOpaque);
sk_sp<SkImage> makeImage(GrRecordingContext*,
const SkMatrix* localMatrix,
SkImageInfo resultInfo,
bool mipmapped);
private:
void* writableUniformData();

View File

@ -743,11 +743,13 @@ func (b *taskBuilder) dmFlags(internalHardwareLabel string) {
badSerializeGMs = append(badSerializeGMs, "draw_image_set_alpha_only")
badSerializeGMs = append(badSerializeGMs, "compositor_quads_shader")
badSerializeGMs = append(badSerializeGMs, "wacky_yuv_formats_qtr")
badSerializeGMs = append(badSerializeGMs, "runtime_effect_image")
// This GM forces a path to be convex. That property doesn't survive
// serialization.
badSerializeGMs = append(badSerializeGMs, "analytic_antialias_convex")
for _, test := range badSerializeGMs {
skip("serialize-8888", "gm", "_", test)
}

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@
#include "include/core/SkColorFilter.h"
#include "include/core/SkData.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkChecksum.h"
#include "include/private/SkMutex.h"
@ -31,8 +32,11 @@
#include "include/gpu/GrRecordingContext.h"
#include "src/gpu/GrColorInfo.h"
#include "src/gpu/GrFPArgs.h"
#include "src/gpu/GrImageInfo.h"
#include "src/gpu/GrSurfaceFillContext.h"
#include "src/gpu/effects/GrMatrixEffect.h"
#include "src/gpu/effects/GrSkSLFP.h"
#include "src/image/SkImage_Gpu.h"
#endif
#include <algorithm>
@ -706,6 +710,90 @@ sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<SkData> uniforms,
: nullptr;
}
sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* recordingContext,
sk_sp<SkData> uniforms,
sk_sp<SkShader> children[],
size_t childCount,
const SkMatrix* localMatrix,
SkImageInfo resultInfo,
bool mipmapped) {
if (recordingContext) {
#if SK_SUPPORT_GPU
auto fillContext = GrSurfaceFillContext::Make(recordingContext,
resultInfo,
SkBackingFit::kExact,
/*sample count*/ 1,
GrMipmapped(mipmapped));
if (!fillContext) {
return nullptr;
}
SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
uniforms = get_xformed_uniforms(this,
std::move(uniforms),
&matrixProvider,
resultInfo.colorSpace());
if (!uniforms) {
return nullptr;
}
auto fp = GrSkSLFP::Make(recordingContext,
sk_ref_sp(this),
"runtime_image",
std::move(uniforms));
GrColorInfo colorInfo(resultInfo.colorInfo());
GrFPArgs args(recordingContext,
matrixProvider,
SkSamplingOptions{},
&colorInfo);
for (size_t i = 0; i < childCount; ++i) {
if (!children[i]) {
return nullptr;
}
auto childFP = as_SB(children[i])->asFragmentProcessor(args);
fp->addChild(std::move(childFP));
}
if (localMatrix) {
SkMatrix invLM;
if (!localMatrix->invert(&invLM)) {
return nullptr;
}
fillContext->fillWithFP(invLM, std::move(fp));
} else {
fillContext->fillWithFP(std::move(fp));
}
return sk_sp<SkImage>(new SkImage_Gpu(sk_ref_sp(recordingContext),
kNeedNewImageUniqueID,
fillContext->readSurfaceView(),
resultInfo.colorInfo()));
#else
return nullptr;
#endif
}
if (resultInfo.alphaType() == kUnpremul_SkAlphaType) {
// We don't have a good way of supporting this right now. In this case the runtime effect
// will produce a unpremul value. The shader generated from it is assumed to produce
// premul and RGB get pinned to A. Moreover, after the blend in premul the new dst is
// unpremul'ed, producing a double unpremul result.
return nullptr;
}
auto surf = SkSurface::MakeRaster(resultInfo);
if (!surf) {
return nullptr;
}
SkCanvas* canvas = surf->getCanvas();
SkTLazy<SkCanvas> tempCanvas;
auto shader = this->makeShader(std::move(uniforms), children, childCount, localMatrix, false);
if (!shader) {
return nullptr;
}
SkPaint paint;
paint.setShader(std::move(shader));
paint.setBlendMode(SkBlendMode::kSrc);
canvas->drawPaint(paint);
// TODO: Specify snapshot should have mip levels if mipmapped is true.
return surf->makeImageSnapshot();
}
sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms,
sk_sp<SkColorFilter> children[],
size_t childCount) {
@ -750,6 +838,19 @@ sk_sp<SkShader> SkRuntimeShaderBuilder::makeShader(const SkMatrix* localMatrix,
return fEffect->makeShader(fUniforms, fChildren.data(), fChildren.size(), localMatrix, isOpaque);
}
sk_sp<SkImage> SkRuntimeShaderBuilder::makeImage(GrRecordingContext* recordingContext,
const SkMatrix* localMatrix,
SkImageInfo resultInfo,
bool mipmapped) {
return fEffect->makeImage(recordingContext,
fUniforms,
fChildren.data(),
fChildren.size(),
localMatrix,
resultInfo,
mipmapped);
}
SkRuntimeShaderBuilder::BuilderChild&
SkRuntimeShaderBuilder::BuilderChild::operator=(const sk_sp<SkShader>& val) {
if (fIndex < 0) {

View File

@ -25,6 +25,17 @@ class SkImage_Gpu : public SkImage_GpuBase {
public:
SkImage_Gpu(sk_sp<GrImageContext>, uint32_t uniqueID, GrSurfaceProxyView, SkColorType,
SkAlphaType, sk_sp<SkColorSpace>);
SkImage_Gpu(sk_sp<GrImageContext> context,
uint32_t uniqueID,
GrSurfaceProxyView view,
SkColorInfo info)
: SkImage_Gpu(std::move(context),
uniqueID,
std::move(view),
info.colorType(),
info.alphaType(),
info.refColorSpace()) {}
~SkImage_Gpu() override;
GrSemaphoresSubmitted onFlush(GrDirectContext*, const GrFlushInfo&) override;