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