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:
parent
9702fc6f38
commit
04aef10c64
@ -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
135
gm/runtimeeffectimage.cpp
Normal 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;)
|
@ -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",
|
||||
|
@ -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();
|
||||
|
@ -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
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user