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 "GrColor.h" 10 #include "sk_tool_utils.h" 11 #include "SkColorPriv.h" 12 #include "SkPath.h" 13 #include "SkShader.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 fLabelPaint.setAntiAlias(true); 72 sk_tool_utils::set_portable_typeface(&fLabelPaint); 73 fLabelPaint.setTextSize(5 * kShapeSize/8); 74 fLabelPaint.setSubpixelText(true); 75 76 constexpr SkScalar radius = -1.4f * kShapeSize/2; 77 SkPoint pts[4] = { 78 {-radius, 0}, 79 {0, -1.33f * radius}, 80 {radius, 0}, 81 {0, 1.33f * radius} 82 }; 83 fOval.moveTo(pts[0]); 84 fOval.quadTo(pts[1], pts[2]); 85 fOval.quadTo(pts[3], pts[0]); 86 87 fConcave.moveTo(-radius, 0); 88 fConcave.quadTo(0, 0, 0, -radius); 89 fConcave.quadTo(0, 0, radius, 0); 90 fConcave.quadTo(0, 0, 0, radius); 91 fConcave.quadTo(0, 0, -radius, 0); 92 fConcave.close(); 93 } 94 draw_pass(SkCanvas * canvas,DrawingPass drawingPass)95 void draw_pass(SkCanvas* canvas, DrawingPass drawingPass) { 96 SkRect clipRect = 97 { -kShapeSize*11/16, -kShapeSize*11/16, kShapeSize*11/16, kShapeSize*11/16 }; 98 99 canvas->save(); 100 if (kCheckerboard_Pass == drawingPass) { 101 canvas->translate(kMargin, kMargin); 102 } 103 canvas->translate(0, kTitleSpacing); 104 105 for (size_t xfermodeSet = 0; xfermodeSet < 2; xfermodeSet++) { 106 size_t firstMode = ((size_t)SkBlendMode::kLastCoeffMode + 1) * xfermodeSet; 107 canvas->save(); 108 109 if (kShape_Pass == drawingPass) { 110 fLabelPaint.setTextAlign(SkPaint::kCenter_Align); 111 canvas->drawString("Src Unknown", 112 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2, 113 kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, fLabelPaint); 114 canvas->drawString("Src Opaque", 115 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 + 116 kPaintSpacing, kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, 117 fLabelPaint); 118 } 119 120 canvas->translate(0, kSubtitleSpacing + kShapeSpacing/2); 121 122 for (size_t m = 0; m <= (size_t)SkBlendMode::kLastCoeffMode; m++) { 123 if (firstMode + m > (size_t)SkBlendMode::kLastMode) { 124 break; 125 } 126 SkBlendMode mode = static_cast<SkBlendMode>(firstMode + m); 127 canvas->save(); 128 129 if (kShape_Pass == drawingPass) { 130 this->drawModeName(canvas, mode); 131 } 132 canvas->translate(kLabelSpacing + kShapeSpacing/2, 0); 133 134 for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) { 135 SkPaint paint; 136 this->setupShapePaint(canvas, kShapeColors[colorIdx], mode, &paint); 137 SkASSERT(colorIdx == 0 || 255 == paint.getAlpha()); 138 canvas->save(); 139 140 for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) { 141 if (kShape_Pass != drawingPass) { 142 canvas->save(); 143 canvas->clipRect(clipRect); 144 if (kCheckerboard_Pass == drawingPass) { 145 sk_tool_utils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6, 146 10); 147 } else { 148 SkASSERT(kBackground_Pass == drawingPass); 149 canvas->drawColor(kBGColor, SkBlendMode::kSrc); 150 } 151 canvas->restore(); 152 } else { 153 this->drawShape(canvas, static_cast<Shape>(shapeIdx), paint, mode); 154 } 155 canvas->translate(kShapeTypeSpacing, 0); 156 } 157 158 canvas->restore(); 159 canvas->translate(kPaintSpacing, 0); 160 } 161 162 canvas->restore(); 163 canvas->translate(0, kShapeSpacing); 164 } 165 166 canvas->restore(); 167 canvas->translate(kXfermodeTypeSpacing, 0); 168 } 169 170 canvas->restore(); 171 } 172 onDraw(SkCanvas * canvas)173 void onDraw(SkCanvas* canvas) override { 174 draw_pass(canvas, kCheckerboard_Pass); 175 canvas->saveLayer(nullptr, nullptr); 176 177 canvas->translate(kMargin, kMargin); 178 draw_pass(canvas, kBackground_Pass); 179 180 SkPaint titlePaint(fLabelPaint); 181 titlePaint.setTextSize(9 * titlePaint.getTextSize() / 8); 182 titlePaint.setFakeBoldText(true); 183 titlePaint.setTextAlign(SkPaint::kCenter_Align); 184 canvas->drawString("Porter Duff", 185 kLabelSpacing + 4 * kShapeTypeSpacing, 186 kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint); 187 canvas->drawString("Advanced", 188 kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing, 189 kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint); 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 fLabelPaint.setTextAlign(SkPaint::kRight_Align); 198 canvas->drawString(modeName, kLabelSpacing - kShapeSize / 4, 199 fLabelPaint.getTextSize() / 4, fLabelPaint); 200 } 201 setupShapePaint(SkCanvas * canvas,GrColor color,SkBlendMode mode,SkPaint * paint)202 void setupShapePaint(SkCanvas* canvas, GrColor 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 SkPaint fLabelPaint; 269 SkPath fOval; 270 SkPath fConcave; 271 272 typedef skiagm::GM INHERITED; 273 }; 274 DEF_GM( return new AAXfermodesGM; ) 275