| /* | 
 |  * Copyright 2024 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/SkBitmap.h" | 
 | #include "include/core/SkBlendMode.h" | 
 | #include "include/core/SkCanvas.h" | 
 | #include "include/core/SkColor.h" | 
 | #include "include/core/SkPaint.h" | 
 | #include "include/core/SkPath.h" | 
 | #include "include/core/SkRect.h" | 
 | #include "include/pathops/SkPathOps.h" | 
 | #include "src/core/SkStroke.h" | 
 | #include "tools/ToolUtils.h" | 
 |  | 
 | static SkPath cross() { | 
 |     SkPath path; | 
 |     path.addRect(15, 0, 35, 50); | 
 |     path.addRect(0, 15, 50, 35); | 
 |     return path; | 
 | } | 
 |  | 
 | static SkPath circle() { return SkPath::Circle(25, 25, 20); } | 
 |  | 
 | // We implement every op except ReverseDifference: That one can be handled by swapping the paths | 
 | // and using the Difference logic. | 
 | static constexpr SkPathOp kOps[] = { | 
 |         kDifference_SkPathOp, | 
 |         kIntersect_SkPathOp, | 
 |         kUnion_SkPathOp, | 
 |         kXOR_SkPathOp, | 
 | }; | 
 |  | 
 | struct OpAsBlend { | 
 |     SkBlendMode fMode; | 
 |     bool fInverse = false; | 
 | }; | 
 |  | 
 | static OpAsBlend op_blend_mode(SkPathOp op) { | 
 |     switch (op) { | 
 |         case kDifference_SkPathOp: | 
 |             return {SkBlendMode::kClear}; | 
 |         case kIntersect_SkPathOp: | 
 |             return {SkBlendMode::kClear, /*fInverse=*/true}; | 
 |         case kUnion_SkPathOp: | 
 |             return {SkBlendMode::kPlus}; | 
 |         case kXOR_SkPathOp: | 
 |             return {SkBlendMode::kXor}; | 
 |         default: | 
 |             // We don't implement kReverseDifference (see note above) | 
 |             SkASSERT(op == kReverseDifference_SkPathOp); | 
 |             return {SkBlendMode::kSrcOver}; | 
 |     } | 
 | } | 
 |  | 
 | DEF_SIMPLE_GM(pathops_blend, canvas, 130, 60 * std::size(kOps) + 60 + 10) { | 
 |     // Checkerboard background to demonstrate that we're only covering the pixels we want: | 
 |     ToolUtils::draw_checkerboard(canvas); | 
 |  | 
 |     // Two paths that overlap in interesting ways: | 
 |     SkPath p1 = cross(); | 
 |     SkPath p2 = circle(); | 
 |     // One path op (intersect) requires one path be drawn using inverse-fill: | 
 |     SkPath p2inv = p2; | 
 |     p2inv.toggleInverseFillType(); | 
 |  | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |  | 
 |     canvas->translate(10, 10); | 
 |  | 
 |     // Draw the two paths by themselves: | 
 |     { | 
 |         canvas->save(); | 
 |         canvas->drawPath(p1, paint); | 
 |         canvas->translate(60, 0); | 
 |         canvas->drawPath(p2, paint); | 
 |         canvas->restore(); | 
 |         canvas->translate(0, 60); | 
 |     } | 
 |  | 
 |     for (SkPathOp op : kOps) { | 
 |         canvas->save(); | 
 |  | 
 |         // Use PathOps to compute new path, then draw it: | 
 |         { | 
 |             SkPath opPath; | 
 |             Op(p1, p2, op, &opPath); | 
 |             canvas->drawPath(opPath, paint); | 
 |         } | 
 |  | 
 |         canvas->translate(60, 0); | 
 |  | 
 |         // Do raster version of op | 
 |         { | 
 |             auto blend = op_blend_mode(op); | 
 |             // Create a layer. We will use blending to build a mask of the shape we want here. | 
 |             // Note that we're always going to get a SrcOver blend of the final shape when this | 
 |             // layer is restored. The math doesn't work out for most blend modes, because we're | 
 |             // turning the coverage of the resulting shape into the layer's alpha. | 
 |             canvas->saveLayer(SkRect::MakeWH(50, 50), nullptr); | 
 |  | 
 |             // We reuse this paint to apply various blend modes: | 
 |             SkPaint p; | 
 |             p.setAntiAlias(true); | 
 |  | 
 |             // Draw the first shape, using SrcOver. This fills the layer with a mask of that path: | 
 |             p.setBlendMode(SkBlendMode::kSrcOver); | 
 |             canvas->drawPath(p1, p); | 
 |  | 
 |             // Based on the PathOp we're emulating, we set a specific blend mode, and then fill | 
 |             // either the second path -- or its inverse. | 
 |             p.setBlendMode(blend.fMode); | 
 |             canvas->drawPath(blend.fInverse ? p2inv : p2, p); | 
 |  | 
 |             // The layer's alpha channel now contains a mask of the desired shape. Cover the entire | 
 |             // rectangle with whatever paint we ACTUALLY want to draw (eg, blue), using kSrcIn. | 
 |             // This will only draw where the mask was present: | 
 |             p.setBlendMode(SkBlendMode::kSrcIn); | 
 |             p.setColor(SK_ColorBLUE); | 
 |             canvas->drawPaint(p); | 
 |             canvas->restore(); | 
 |         } | 
 |  | 
 |         canvas->restore(); | 
 |         canvas->translate(0, 60); | 
 |     } | 
 | } |