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