1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm/gm.h" 9 #include "include/core/SkBlender.h" 10 #include "include/core/SkCanvas.h" 11 #include "include/core/SkCustomMesh.h" 12 #include "include/core/SkSurface.h" 13 #include "include/effects/SkGradientShader.h" 14 #include "src/core/SkCanvasPriv.h" 15 16 #include <memory> 17 18 namespace skiagm { 19 class CustomMeshGM : public skiagm::GM { 20 public: CustomMeshGM()21 CustomMeshGM() {} 22 23 protected: 24 using Attribute = SkCustomMeshSpecification::Attribute; 25 using Varying = SkCustomMeshSpecification::Varying; 26 onISize()27 SkISize onISize() override { return {435, 1180}; } 28 onOnceBeforeDraw()29 void onOnceBeforeDraw() override { 30 { 31 static const Attribute kAttributes[]{ 32 {Attribute::Type::kFloat4, 8, SkString{"xuyv"}}, 33 {Attribute::Type::kUByte4_unorm, 4, SkString{"brag"}}, 34 }; 35 static const Varying kVaryings[]{ 36 {Varying::Type::kHalf4, SkString{"color"}}, 37 {Varying::Type::kFloat2, SkString{"uv"} }, 38 }; 39 static constexpr char kVS[] = R"( 40 half4 unswizzle_color(half4 color) { return color.garb; } 41 42 float2 main(in Attributes attributes, out Varyings varyings) { 43 varyings.color = unswizzle_color(attributes.brag); 44 varyings.uv = attributes.xuyv.yw; 45 return attributes.xuyv.xz; 46 } 47 )"; 48 static constexpr char kFS[] = R"( 49 float2 main(in Varyings varyings, out float4 color) { 50 color = varyings.color; 51 return varyings.uv; 52 } 53 )"; 54 auto [spec, error] = SkCustomMeshSpecification::Make( 55 SkMakeSpan(kAttributes, SK_ARRAY_COUNT(kAttributes)), 56 sizeof(ColorVertex), 57 SkMakeSpan(kVaryings, SK_ARRAY_COUNT(kVaryings)), 58 SkString(kVS), 59 SkString(kFS)); 60 if (!spec) { 61 SkDebugf("%s\n", error.c_str()); 62 } 63 fSpecWithColor = std::move(spec); 64 } 65 { 66 static const Attribute kAttributes[]{ 67 {Attribute::Type::kFloat4, 0, SkString{"xuyv"}}, 68 }; 69 static const Varying kVaryings[]{ 70 {Varying::Type::kFloat2, SkString{"vux2"}}, 71 }; 72 static constexpr char kVS[] = R"( 73 float2 main(in Attributes a, out Varyings v) { 74 v.vux2 = 2*a.xuyv.wy; 75 return a.xuyv.xz; 76 } 77 )"; 78 static constexpr char kFS[] = R"( 79 float2 helper(in float2 vux2) { return vux2.yx/2; } 80 float2 main(in Varyings varyings) { 81 return helper(varyings.vux2); 82 } 83 )"; 84 auto [spec, error] = SkCustomMeshSpecification::Make( 85 SkMakeSpan(kAttributes, SK_ARRAY_COUNT(kAttributes)), 86 sizeof(NoColorVertex), 87 SkMakeSpan(kVaryings, SK_ARRAY_COUNT(kVaryings)), 88 SkString(kVS), 89 SkString(kFS)); 90 if (!spec) { 91 SkDebugf("%s\n", error.c_str()); 92 } 93 fSpecWithNoColor = std::move(spec); 94 } 95 96 static constexpr SkColor kColors[] = {SK_ColorTRANSPARENT, SK_ColorWHITE}; 97 fShader = SkGradientShader::MakeRadial({10, 10}, 98 3, 99 kColors, 100 nullptr, 101 2, 102 SkTileMode::kMirror); 103 } 104 onShortName()105 SkString onShortName() override { return SkString("custommesh"); } 106 onDraw(SkCanvas * canvas,SkString *)107 DrawResult onDraw(SkCanvas* canvas, SkString*) override { 108 int i = 0; 109 for (const sk_sp<SkBlender>& blender : {SkBlender::Mode(SkBlendMode::kDst), 110 SkBlender::Mode(SkBlendMode::kSrc), 111 SkBlender::Mode(SkBlendMode::kSaturation)}) { 112 canvas->save(); 113 for (uint8_t alpha : {0xFF , 0x40}) 114 for (bool colors : {false, true}) 115 for (bool shader : {false, true}) { 116 SkCustomMesh cm; 117 cm.spec = colors ? fSpecWithColor : fSpecWithNoColor; 118 cm.bounds = kRect; 119 // Rather than pile onto the combinatorics we draw every other test case indexed. 120 if ((i & 1) == 0) { 121 cm.vb = colors ? static_cast<const void*>(kColorQuad) 122 : static_cast<const void*>(kNoColorQuad); 123 cm.vcount = 4; 124 cm.mode = SkCustomMesh::Mode::kTriangleStrip; 125 } else { 126 cm.vb = colors ? static_cast<const void*>(kColorIndexedQuad) 127 : static_cast<const void*>(kNoColorIndexedQuad); 128 cm.vcount = 6; 129 cm.icount = 6; 130 cm.indices = kIndices; 131 cm.mode = SkCustomMesh::Mode::kTriangles; 132 } 133 134 SkPaint paint; 135 paint.setColor(SK_ColorGREEN); 136 paint.setShader(shader ? fShader : nullptr); 137 paint.setAlpha(alpha); 138 139 SkCanvasPriv::DrawCustomMesh(canvas, std::move(cm), blender, paint); 140 141 canvas->translate(0, 150); 142 ++i; 143 } 144 canvas->restore(); 145 canvas->translate(150, 0); 146 } 147 return DrawResult::kOk; 148 } 149 150 private: 151 struct ColorVertex { 152 uint32_t pad; 153 uint32_t brag; 154 float xuyv[4]; 155 }; 156 157 struct NoColorVertex { 158 float xuyv[4]; 159 }; 160 161 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 120, 120); 162 static constexpr auto kUV = SkRect::MakeLTRB( 0, 0, 20, 20); 163 164 static constexpr ColorVertex kColorQuad[] { 165 {0, 0x00FFFF00, {kRect.left(), kUV.left(), kRect.top(), kUV.top() }}, 166 {0, 0x00FFFFFF, {kRect.right(), kUV.right(), kRect.top(), kUV.top() }}, 167 {0, 0xFFFF00FF, {kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}}, 168 {0, 0xFFFFFF00, {kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}}, 169 }; 170 171 static constexpr NoColorVertex kNoColorQuad[]{ 172 {{kRect.left(), kUV.left(), kRect.top(), kUV.top() }}, 173 {{kRect.right(), kUV.right(), kRect.top(), kUV.top() }}, 174 {{kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}}, 175 {{kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}}, 176 }; 177 178 // The indexed quads draw the same as the non-indexed. They just have unused vertices that the 179 // index buffer skips over draw with triangles instead of a triangle strip. 180 static constexpr ColorVertex kColorIndexedQuad[] { 181 {0, 0x00FFFF00, {kRect.left(), kUV.left(), kRect.top(), kUV.top() }}, 182 {0, 0x00000000, { 100.f, 0.f, 100.f, 5.f }}, // unused 183 {0, 0x00FFFFFF, {kRect.right(), kUV.right(), kRect.top(), kUV.top() }}, 184 {0, 0x00000000, { 200.f, 10.f, 200.f, 10.f }}, // unused 185 {0, 0xFFFF00FF, {kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}}, 186 {0, 0xFFFFFF00, {kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}}, 187 }; 188 189 static constexpr NoColorVertex kNoColorIndexedQuad[]{ 190 {{kRect.left(), kUV.left(), kRect.top(), kUV.top() }}, 191 {{ 100.f, 0.f, 100.f, 5.f }}, // unused 192 {{kRect.right(), kUV.right(), kRect.top(), kUV.top() }}, 193 {{ 200.f, 10.f, 200.f, 10.f }}, // unused 194 {{kRect.left(), kUV.left(), kRect.bottom(), kUV.bottom()}}, 195 {{kRect.right(), kUV.right(), kRect.bottom(), kUV.bottom()}}, 196 }; 197 198 static constexpr uint16_t kIndices[]{0, 2, 4, 2, 5, 4}; 199 200 sk_sp<SkShader> fShader; 201 202 sk_sp<SkCustomMeshSpecification> fSpecWithColor; 203 sk_sp<SkCustomMeshSpecification> fSpecWithNoColor; 204 }; 205 206 constexpr SkRect CustomMeshGM::kRect; 207 constexpr SkRect CustomMeshGM::kUV; 208 209 constexpr CustomMeshGM::ColorVertex CustomMeshGM::kColorQuad[]; 210 constexpr CustomMeshGM::NoColorVertex CustomMeshGM::kNoColorQuad[]; 211 constexpr CustomMeshGM::ColorVertex CustomMeshGM::kColorIndexedQuad[]; 212 constexpr CustomMeshGM::NoColorVertex CustomMeshGM::kNoColorIndexedQuad[]; 213 214 constexpr uint16_t CustomMeshGM::kIndices[]; 215 216 DEF_GM( return new CustomMeshGM; ) 217 218 class CustomMeshColorSpaceGM : public skiagm::GM { 219 public: CustomMeshColorSpaceGM()220 CustomMeshColorSpaceGM() {} 221 222 protected: 223 using Attribute = SkCustomMeshSpecification::Attribute; 224 using Varying = SkCustomMeshSpecification::Varying; 225 onISize()226 SkISize onISize() override { return {468, 258}; } 227 onOnceBeforeDraw()228 void onOnceBeforeDraw() override { 229 static const Attribute kAttributes[]{ 230 {Attribute::Type::kFloat2, 0, SkString{"pos"} }, 231 {Attribute::Type::kFloat4, 8, SkString{"color"}}, 232 }; 233 static const Varying kVaryings[]{ 234 {Varying::Type::kHalf4, SkString{"color"}}, 235 }; 236 static constexpr char kPremulVS[] = R"( 237 float2 main(in Attributes attributes, out Varyings varyings) { 238 varyings.color = half4(attributes.color.a*attributes.color.rgb, 239 attributes.color.a); 240 return attributes.pos; 241 } 242 )"; 243 static constexpr char kUnpremulVS[] = R"( 244 float2 main(in Attributes attributes, out Varyings varyings) { 245 varyings.color = attributes.color; 246 return attributes.pos; 247 } 248 )"; 249 static constexpr char kFS[] = R"( 250 void main(in Varyings varyings, out half4 color) { 251 color = varyings.color; 252 } 253 )"; 254 for (bool unpremul : {false, true}) { 255 auto at = unpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; 256 auto vs = unpremul ? kUnpremulVS : kPremulVS; 257 for (bool spin : {false, true}) { 258 auto cs = SkColorSpace::MakeSRGB(); 259 if (spin) { 260 cs = cs->makeColorSpin(); 261 } 262 263 auto [spec, error] = SkCustomMeshSpecification::Make( 264 SkMakeSpan(kAttributes, SK_ARRAY_COUNT(kAttributes)), 265 sizeof(Vertex), 266 SkMakeSpan(kVaryings, SK_ARRAY_COUNT(kVaryings)), 267 SkString(vs), 268 SkString(kFS), 269 std::move(cs), 270 at); 271 if (!spec) { 272 SkDebugf("%s\n", error.c_str()); 273 } 274 fSpecs[SpecIndex(unpremul, spin)] = std::move(spec); 275 } 276 } 277 SkPoint pts[] = {{kRect.fLeft, 0}, {kRect.centerX(), 0}}; 278 SkColor colors[] = {SK_ColorWHITE, SK_ColorTRANSPARENT}; 279 fShader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kMirror); 280 } 281 onShortName()282 SkString onShortName() override { return SkString("custommesh_cs"); } 283 onDraw(SkCanvas * canvas,SkString * error)284 DrawResult onDraw(SkCanvas* canvas, SkString* error) override { 285 // Force an intermediate surface if the canvas is in "legacy" mode 286 SkCanvas* c = canvas; 287 sk_sp<SkSurface> surface; 288 if (!c->imageInfo().colorSpace()) { 289 SkImageInfo info = canvas->imageInfo().makeColorSpace(SkColorSpace::MakeSRGB()); 290 surface = canvas->makeSurface(info); 291 if (!surface) { 292 // This GM won't work on configs that use a recording canvas. 293 return DrawResult::kSkip; 294 } 295 c = surface->getCanvas(); 296 c->clear(SK_ColorWHITE); 297 } 298 for (bool useShader : {false, true}) 299 for (bool unpremul : {false, true}) { 300 c->save(); 301 for (bool spin : {false, true}) { 302 SkCustomMesh cm; 303 cm.spec = fSpecs[SpecIndex(unpremul, spin)]; 304 cm.bounds = kRect; 305 cm.vb = kQuad; 306 cm.vcount = 4; 307 cm.mode = SkCustomMesh::Mode::kTriangleStrip; 308 309 SkPaint paint; 310 paint.setShader(useShader ? fShader : nullptr); 311 SkBlendMode mode = useShader ? SkBlendMode::kModulate : SkBlendMode::kDst; 312 SkCanvasPriv::DrawCustomMesh(c, 313 std::move(cm), 314 SkBlender::Mode(mode), 315 paint); 316 317 c->translate(0, kRect.height() + 10); 318 } 319 c->restore(); 320 c->translate(kRect.width() + 10, 0); 321 c->save(); 322 } 323 if (surface) { 324 surface->draw(canvas, 0, 0); 325 } 326 return DrawResult::kOk; 327 } 328 329 private: 330 struct Vertex { 331 SkPoint pos; 332 SkColor4f color; 333 }; 334 SpecIndex(bool spin,bool unpremul)335 static int SpecIndex(bool spin, bool unpremul) { 336 return static_cast<int>(spin) + 2*static_cast<int>(unpremul); 337 } 338 339 static constexpr auto kRect = SkRect::MakeLTRB(20, 20, 120, 120); 340 341 static constexpr Vertex kQuad[] { 342 {{kRect.left() , kRect.top() }, {1, 0, 0, 1}}, 343 {{kRect.right(), kRect.top() }, {0, 1, 0, 0}}, 344 {{kRect.left() , kRect.bottom()}, {1, 1, 0, 0}}, 345 {{kRect.right(), kRect.bottom()}, {0, 0, 1, 1}}, 346 }; 347 348 sk_sp<SkCustomMeshSpecification> fSpecs[4]; 349 350 sk_sp<SkShader> fShader; 351 }; 352 353 constexpr SkRect CustomMeshColorSpaceGM::kRect; 354 355 constexpr CustomMeshColorSpaceGM::Vertex CustomMeshColorSpaceGM::kQuad[]; 356 357 DEF_GM( return new CustomMeshColorSpaceGM; ) 358 359 } // namespace skiagm 360