1 /* 2 * Copyright 2015 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 "SkColorPriv.h" 11 #include "SkPath.h" 12 #include "SkShader.h" 13 #include "SkTextUtils.h" 14 15 enum { 16 kXfermodeCount = (int)SkBlendMode::kLastMode + 1 + 1, // extra for arith 17 kShapeSize = 22, 18 kShapeSpacing = 36, 19 kShapeTypeSpacing = 4 * kShapeSpacing / 3, 20 kPaintSpacing = 4 * kShapeTypeSpacing, 21 kLabelSpacing = 3 * kShapeSize, 22 kMargin = kShapeSpacing / 2, 23 kXfermodeTypeSpacing = kLabelSpacing + 2 * kPaintSpacing + kShapeTypeSpacing, 24 kTitleSpacing = 3 * kShapeSpacing / 4, 25 kSubtitleSpacing = 5 * kShapeSpacing / 8 26 }; 27 28 constexpr SkColor kBGColor = 0xc8d2b887; 29 30 constexpr SkColor kShapeColors[2] = { 31 0x82ff0080, // input color unknown 32 0xff00ffff, // input color opaque 33 }; 34 35 enum Shape { 36 kSquare_Shape, 37 kDiamond_Shape, 38 kOval_Shape, 39 kConcave_Shape, 40 41 kLast_Shape = kConcave_Shape 42 }; 43 44 /** 45 * Verifies AA works properly on all Xfermodes, including arithmetic, with both opaque and unknown 46 * src colors. 47 */ 48 class AAXfermodesGM : public skiagm::GM { 49 public: AAXfermodesGM()50 AAXfermodesGM() {} 51 52 protected: 53 enum DrawingPass { 54 kCheckerboard_Pass, 55 kBackground_Pass, 56 kShape_Pass 57 }; 58 onShortName()59 SkString onShortName() override { 60 return SkString("aaxfermodes"); 61 } 62 onISize()63 SkISize onISize() override { 64 return SkISize::Make(2 * kMargin + 2 * kXfermodeTypeSpacing - 65 (kXfermodeTypeSpacing - (kLabelSpacing + 2 * kPaintSpacing)), 66 2 * kMargin + kTitleSpacing + kSubtitleSpacing + 67 (1 + (int)SkBlendMode::kLastCoeffMode) * kShapeSpacing); 68 } 69 onOnceBeforeDraw()70 void onOnceBeforeDraw() override { 71 fLabelFont.setTypeface(sk_tool_utils::create_portable_typeface()); 72 fLabelFont.setSize(5 * kShapeSize/8); 73 fLabelFont.setSubpixel(true); 74 75 constexpr SkScalar radius = -1.4f * kShapeSize/2; 76 SkPoint pts[4] = { 77 {-radius, 0}, 78 {0, -1.33f * radius}, 79 {radius, 0}, 80 {0, 1.33f * radius} 81 }; 82 fOval.moveTo(pts[0]); 83 fOval.quadTo(pts[1], pts[2]); 84 fOval.quadTo(pts[3], pts[0]); 85 86 fConcave.moveTo(-radius, 0); 87 fConcave.quadTo(0, 0, 0, -radius); 88 fConcave.quadTo(0, 0, radius, 0); 89 fConcave.quadTo(0, 0, 0, radius); 90 fConcave.quadTo(0, 0, -radius, 0); 91 fConcave.close(); 92 } 93 draw_pass(SkCanvas * canvas,DrawingPass drawingPass)94 void draw_pass(SkCanvas* canvas, DrawingPass drawingPass) { 95 SkRect clipRect = 96 { -kShapeSize*11/16, -kShapeSize*11/16, kShapeSize*11/16, kShapeSize*11/16 }; 97 98 canvas->save(); 99 if (kCheckerboard_Pass == drawingPass) { 100 canvas->translate(kMargin, kMargin); 101 } 102 canvas->translate(0, kTitleSpacing); 103 104 for (size_t xfermodeSet = 0; xfermodeSet < 2; xfermodeSet++) { 105 size_t firstMode = ((size_t)SkBlendMode::kLastCoeffMode + 1) * xfermodeSet; 106 canvas->save(); 107 108 if (kShape_Pass == drawingPass) { 109 SkTextUtils::DrawString(canvas, "Src Unknown", 110 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2, 111 kSubtitleSpacing / 2 + fLabelFont.getSize() / 3, fLabelFont, SkPaint(), 112 SkTextUtils::kCenter_Align); 113 SkTextUtils::DrawString(canvas, "Src Opaque", 114 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 + 115 kPaintSpacing, kSubtitleSpacing / 2 + fLabelFont.getSize() / 3, 116 fLabelFont, SkPaint(), SkTextUtils::kCenter_Align); 117 } 118 119 canvas->translate(0, kSubtitleSpacing + kShapeSpacing/2); 120 121 for (size_t m = 0; m <= (size_t)SkBlendMode::kLastCoeffMode; m++) { 122 if (firstMode + m > (size_t)SkBlendMode::kLastMode) { 123 break; 124 } 125 SkBlendMode mode = static_cast<SkBlendMode>(firstMode + m); 126 canvas->save(); 127 128 if (kShape_Pass == drawingPass) { 129 this->drawModeName(canvas, mode); 130 } 131 canvas->translate(kLabelSpacing + kShapeSpacing/2, 0); 132 133 for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) { 134 SkPaint paint; 135 this->setupShapePaint(canvas, kShapeColors[colorIdx], mode, &paint); 136 SkASSERT(colorIdx == 0 || 255 == paint.getAlpha()); 137 canvas->save(); 138 139 for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) { 140 if (kShape_Pass != drawingPass) { 141 canvas->save(); 142 canvas->clipRect(clipRect); 143 if (kCheckerboard_Pass == drawingPass) { 144 sk_tool_utils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6, 145 10); 146 } else { 147 SkASSERT(kBackground_Pass == drawingPass); 148 canvas->drawColor(kBGColor, SkBlendMode::kSrc); 149 } 150 canvas->restore(); 151 } else { 152 this->drawShape(canvas, static_cast<Shape>(shapeIdx), paint, mode); 153 } 154 canvas->translate(kShapeTypeSpacing, 0); 155 } 156 157 canvas->restore(); 158 canvas->translate(kPaintSpacing, 0); 159 } 160 161 canvas->restore(); 162 canvas->translate(0, kShapeSpacing); 163 } 164 165 canvas->restore(); 166 canvas->translate(kXfermodeTypeSpacing, 0); 167 } 168 169 canvas->restore(); 170 } 171 onDraw(SkCanvas * canvas)172 void onDraw(SkCanvas* canvas) override { 173 draw_pass(canvas, kCheckerboard_Pass); 174 canvas->saveLayer(nullptr, nullptr); 175 176 canvas->translate(kMargin, kMargin); 177 draw_pass(canvas, kBackground_Pass); 178 179 SkFont titleFont(fLabelFont); 180 titleFont.setSize(9 * titleFont.getSize() / 8); 181 titleFont.setEmbolden(true); 182 SkTextUtils::DrawString(canvas, "Porter Duff", 183 kLabelSpacing + 4 * kShapeTypeSpacing, 184 kTitleSpacing / 2 + titleFont.getSize() / 3, titleFont, SkPaint(), 185 SkTextUtils::kCenter_Align); 186 SkTextUtils::DrawString(canvas, "Advanced", 187 kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing, 188 kTitleSpacing / 2 + titleFont.getSize() / 3, titleFont, SkPaint(), 189 SkTextUtils::kCenter_Align); 190 191 draw_pass(canvas, kShape_Pass); 192 canvas->restore(); 193 } 194 drawModeName(SkCanvas * canvas,SkBlendMode mode)195 void drawModeName(SkCanvas* canvas, SkBlendMode mode) { 196 const char* modeName = SkBlendMode_Name(mode); 197 SkTextUtils::DrawString(canvas, modeName, kLabelSpacing - kShapeSize / 4, 198 fLabelFont.getSize() / 4, fLabelFont, SkPaint(), 199 SkTextUtils::kRight_Align); 200 } 201 setupShapePaint(SkCanvas * canvas,SkColor color,SkBlendMode mode,SkPaint * paint)202 void setupShapePaint(SkCanvas* canvas, SkColor color, SkBlendMode mode, SkPaint* paint) { 203 paint->setColor(color); 204 205 if (mode == SkBlendMode::kPlus) { 206 // Check for overflow, otherwise we might get confusing AA artifacts. 207 int maxSum = SkTMax(SkTMax(SkColorGetA(kBGColor) + SkColorGetA(color), 208 SkColorGetR(kBGColor) + SkColorGetR(color)), 209 SkTMax(SkColorGetG(kBGColor) + SkColorGetG(color), 210 SkColorGetB(kBGColor) + SkColorGetB(color))); 211 212 if (maxSum > 255) { 213 SkPaint dimPaint; 214 dimPaint.setAntiAlias(false); 215 dimPaint.setBlendMode(SkBlendMode::kDstIn); 216 if (255 != paint->getAlpha()) { 217 // Dim the src and dst colors. 218 dimPaint.setARGB(255 * 255 / maxSum, 0, 0, 0); 219 paint->setAlpha(255 * paint->getAlpha() / maxSum); 220 } else { 221 // Just clear the dst, we need to preserve the paint's opacity. 222 dimPaint.setARGB(0, 0, 0, 0); 223 } 224 canvas->drawRect({ -kShapeSpacing/2, -kShapeSpacing/2, 225 kShapeSpacing/2 + 3 * kShapeTypeSpacing, kShapeSpacing/2 }, 226 dimPaint); 227 } 228 } 229 } 230 drawShape(SkCanvas * canvas,Shape shape,const SkPaint & paint,SkBlendMode mode)231 void drawShape(SkCanvas* canvas, Shape shape, const SkPaint& paint, SkBlendMode mode) { 232 SkASSERT(mode <= SkBlendMode::kLastMode); 233 SkPaint shapePaint(paint); 234 shapePaint.setAntiAlias(kSquare_Shape != shape); 235 shapePaint.setBlendMode(mode); 236 237 switch (shape) { 238 case kSquare_Shape: 239 canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 }, 240 shapePaint); 241 break; 242 243 case kDiamond_Shape: 244 canvas->save(); 245 canvas->rotate(45); 246 canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 }, 247 shapePaint); 248 canvas->restore(); 249 break; 250 251 case kOval_Shape: 252 canvas->save(); 253 canvas->rotate(static_cast<SkScalar>((511 * (int)mode + 257) % 360)); 254 canvas->drawPath(fOval, shapePaint); 255 canvas->restore(); 256 break; 257 258 case kConcave_Shape: 259 canvas->drawPath(fConcave, shapePaint); 260 break; 261 262 default: 263 SK_ABORT("Invalid shape."); 264 } 265 } 266 267 private: 268 SkFont fLabelFont; 269 SkPath fOval; 270 SkPath fConcave; 271 272 typedef skiagm::GM INHERITED; 273 }; 274 DEF_GM( return new AAXfermodesGM; ) 275