1 /* 2 * Copyright 2011 Google Inc. 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.h" 9 #include "sk_tool_utils.h" 10 #include "SkCanvas.h" 11 #include "SkFont.h" 12 #include "SkPath.h" 13 14 namespace skiagm { 15 16 constexpr SkColor gPathColor = SK_ColorBLACK; 17 constexpr SkColor gClipAColor = SK_ColorBLUE; 18 constexpr SkColor gClipBColor = SK_ColorRED; 19 20 class ComplexClipGM : public GM { 21 public: 22 ComplexClipGM(bool aaclip, bool saveLayer, bool invertDraw) 23 : fDoAAClip(aaclip) 24 , fDoSaveLayer(saveLayer) 25 , fInvertDraw(invertDraw) { 26 this->setBGColor(0xFFDEDFDE); 27 } 28 29 protected: 30 SkString onShortName() override { 31 SkString str; 32 str.printf("complexclip_%s%s%s", 33 fDoAAClip ? "aa" : "bw", 34 fDoSaveLayer ? "_layer" : "", 35 fInvertDraw ? "_invert" : ""); 36 return str; 37 } 38 39 SkISize onISize() override { return SkISize::Make(970, 780); } 40 41 void onDraw(SkCanvas* canvas) override { 42 SkPath path; 43 path.moveTo(0, 50) 44 .quadTo(0, 0, 50, 0) 45 .lineTo(175, 0) 46 .quadTo(200, 0, 200, 25) 47 .lineTo(200, 150) 48 .quadTo(200, 200, 150, 200) 49 .lineTo(0, 200) 50 .close() 51 .moveTo(50, 50) 52 .lineTo(150, 50) 53 .lineTo(150, 125) 54 .quadTo(150, 150, 125, 150) 55 .lineTo(50, 150) 56 .close(); 57 if (fInvertDraw) { 58 path.setFillType(SkPath::kInverseEvenOdd_FillType); 59 } else { 60 path.setFillType(SkPath::kEvenOdd_FillType); 61 } 62 SkPaint pathPaint; 63 pathPaint.setAntiAlias(true); 64 pathPaint.setColor(gPathColor); 65 66 SkPath clipA; 67 clipA.addPoly({{10, 20}, {165, 22}, {70, 105}, {165, 177}, {-5, 180}}, false).close(); 68 69 SkPath clipB; 70 clipB.addPoly({{40, 10}, {190, 15}, {195, 190}, {40, 185}, {155, 100}}, false).close(); 71 72 SkFont font(sk_tool_utils::create_portable_typeface(), 20); 73 74 constexpr struct { 75 SkClipOp fOp; 76 const char* fName; 77 } gOps[] = { //extra spaces in names for measureText 78 {kIntersect_SkClipOp, "Isect "}, 79 {kDifference_SkClipOp, "Diff " }, 80 {kUnion_SkClipOp, "Union "}, 81 {kXOR_SkClipOp, "Xor " }, 82 {kReverseDifference_SkClipOp, "RDiff "} 83 }; 84 85 canvas->translate(20, 20); 86 canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4); 87 88 if (fDoSaveLayer) { 89 // We want the layer to appear symmetric relative to actual 90 // device boundaries so we need to "undo" the effect of the 91 // scale and translate 92 SkRect bounds = SkRect::MakeLTRB( 93 4.0f/3.0f * -20, 94 4.0f/3.0f * -20, 95 4.0f/3.0f * (this->getISize().fWidth - 20), 96 4.0f/3.0f * (this->getISize().fHeight - 20)); 97 98 bounds.inset(100, 100); 99 SkPaint boundPaint; 100 boundPaint.setColor(SK_ColorRED); 101 boundPaint.setStyle(SkPaint::kStroke_Style); 102 canvas->drawRect(bounds, boundPaint); 103 canvas->clipRect(bounds); 104 canvas->saveLayer(&bounds, nullptr); 105 } 106 107 for (int invBits = 0; invBits < 4; ++invBits) { 108 canvas->save(); 109 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { 110 this->drawHairlines(canvas, path, clipA, clipB); 111 112 bool doInvA = SkToBool(invBits & 1); 113 bool doInvB = SkToBool(invBits & 2); 114 canvas->save(); 115 // set clip 116 clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : 117 SkPath::kEvenOdd_FillType); 118 clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : 119 SkPath::kEvenOdd_FillType); 120 canvas->clipPath(clipA, fDoAAClip); 121 canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip); 122 123 // In the inverse case we need to prevent the draw from covering the whole 124 // canvas. 125 if (fInvertDraw) { 126 SkRect rectClip = clipA.getBounds(); 127 rectClip.join(path.getBounds()); 128 rectClip.join(path.getBounds()); 129 rectClip.outset(5, 5); 130 canvas->clipRect(rectClip); 131 } 132 133 // draw path clipped 134 canvas->drawPath(path, pathPaint); 135 canvas->restore(); 136 137 138 SkPaint paint; 139 SkScalar txtX = 45; 140 paint.setColor(gClipAColor); 141 const char* aTxt = doInvA ? "InvA " : "A "; 142 canvas->drawSimpleText(aTxt, strlen(aTxt), kUTF8_SkTextEncoding, txtX, 220, font, paint); 143 txtX += font.measureText(aTxt, strlen(aTxt), kUTF8_SkTextEncoding); 144 paint.setColor(SK_ColorBLACK); 145 canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), kUTF8_SkTextEncoding, txtX, 220, 146 font, paint); 147 txtX += font.measureText(gOps[op].fName, strlen(gOps[op].fName), kUTF8_SkTextEncoding); 148 paint.setColor(gClipBColor); 149 const char* bTxt = doInvB ? "InvB " : "B "; 150 canvas->drawSimpleText(bTxt, strlen(bTxt), kUTF8_SkTextEncoding, txtX, 220, font, paint); 151 152 canvas->translate(250,0); 153 } 154 canvas->restore(); 155 canvas->translate(0, 250); 156 } 157 158 if (fDoSaveLayer) { 159 canvas->restore(); 160 } 161 } 162 private: 163 void drawHairlines(SkCanvas* canvas, const SkPath& path, 164 const SkPath& clipA, const SkPath& clipB) { 165 SkPaint paint; 166 paint.setAntiAlias(true); 167 paint.setStyle(SkPaint::kStroke_Style); 168 const SkAlpha fade = 0x33; 169 170 // draw path in hairline 171 paint.setColor(gPathColor); paint.setAlpha(fade); 172 canvas->drawPath(path, paint); 173 174 // draw clips in hair line 175 paint.setColor(gClipAColor); paint.setAlpha(fade); 176 canvas->drawPath(clipA, paint); 177 paint.setColor(gClipBColor); paint.setAlpha(fade); 178 canvas->drawPath(clipB, paint); 179 } 180 181 bool fDoAAClip; 182 bool fDoSaveLayer; 183 bool fInvertDraw; 184 185 typedef GM INHERITED; 186 }; 187 188 ////////////////////////////////////////////////////////////////////////////// 189 190 DEF_GM(return new ComplexClipGM(false, false, false);) 191 DEF_GM(return new ComplexClipGM(false, false, true);) 192 DEF_GM(return new ComplexClipGM(false, true, false);) 193 DEF_GM(return new ComplexClipGM(false, true, true);) 194 DEF_GM(return new ComplexClipGM(true, false, false);) 195 DEF_GM(return new ComplexClipGM(true, false, true);) 196 DEF_GM(return new ComplexClipGM(true, true, false);) 197 DEF_GM(return new ComplexClipGM(true, true, true);) 198 } 199