xref: /aosp_15_r20/external/skia/docs/examples/SkSL_RawImageShaders.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 // Copyright 2024 Google LLC.
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3 #include "tools/fiddle/examples.h"
4 REG_FIDDLE(SkSL_RawImageShaders, 384, 128, false, 0) {
make_image(sk_sp<SkRuntimeEffect> effect,const SkImageInfo & info)5 static sk_sp<SkImage> make_image(sk_sp<SkRuntimeEffect> effect,
6                                  const SkImageInfo& info) {
7   sk_sp<SkSurface> surface = SkSurfaces::Raster(info);
8   SkCanvas* canvas = surface->getCanvas();
9   auto shader = effect->makeShader(/*uniforms=*/ nullptr, /*children=*/ {});
10   if (!shader) {
11       return nullptr;
12   }
13   SkPaint paint;
14   paint.setShader(std::move(shader));
15   paint.setBlendMode(SkBlendMode::kSrc);
16   canvas->drawPaint(paint);
17   return surface->makeImageSnapshot();
18 }
19 
draw(SkCanvas * canvas)20 void draw(SkCanvas* canvas) {
21   // Make a hemispherical normal map image:
22   auto imageInfo = SkImageInfo::MakeN32Premul(128, 128);
23   auto imageShader = SkRuntimeEffect::MakeForShader(SkString(R"(
24     vec4 main(vec2 p) {
25       p = (p / 128) * 2 - 1;
26       float len2 = dot(p, p);
27       vec3 v = (len2 > 1) ? vec3(0, 0, 1) : vec3(p, sqrt(1 - len2));
28       return (v * 0.5 + 0.5).xyz1;
29     })")).effect;
30   auto normalImage = make_image(imageShader, imageInfo);
31 
32   // Make a simple lighting effect:
33   auto litEffect = SkRuntimeEffect::MakeForShader(SkString(R"(
34     uniform shader normals;
35     vec4 main(vec2 p) {
36       vec3 n = normalize(normals.eval(p).xyz * 2 - 1);
37       vec3 l = normalize(vec3(-1, -1, 0.5));
38       return saturate(dot(n, l)).xxx1;
39     })")).effect;
40   SkRuntimeShaderBuilder builder(litEffect);
41   SkPaint paint;
42 
43   // FIRST: Draw the lighting to our (not color managed) canvas.
44   // This is our CORRECT, reference result:
45   builder.child("normals") = normalImage->makeShader(SkSamplingOptions{});
46   paint.setShader(builder.makeShader());
47   canvas->drawRect({0,0,128,128}, paint);
48 
49   // Make an offscreen surface with a wide gamut:
50   auto rec2020 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
51                                        SkNamedGamut::kRec2020);
52   auto info = SkImageInfo::Make(128, 128, kRGBA_F16_SkColorType,
53                                 kPremul_SkAlphaType, rec2020);
54   auto surface = SkSurfaces::Raster(info);
55 
56   // SECOND: Draw the lighting to the offscreen surface. Color management
57   // changes the normals, producing INCORRECT (wrong direction) lighting:
58   surface->getCanvas()->drawPaint(paint);
59   canvas->drawImage(surface->makeImageSnapshot(), 128, 0);
60 
61   // THIRD: Convert the normals to a raw image shader. This ignores color
62   // management for that image, so we get CORRECT lighting again:
63   builder.child("normals") = normalImage->makeRawShader(SkSamplingOptions{});
64   paint.setShader(builder.makeShader());
65   surface->getCanvas()->drawPaint(paint);
66   canvas->drawImage(surface->makeImageSnapshot(), 256, 0);
67 }
68 }  // END FIDDLE
69