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 "SkBlendModePriv.h" 11 #include "SkPath.h" 12 #include "SkShader.h" 13 14 enum { 15 kXfermodeCount = (int)SkBlendMode::kLastMode + 1 + 1, // extra for arith 16 kShapeSize = 22, 17 kShapeSpacing = 36, 18 kShapeTypeSpacing = 4 * kShapeSpacing / 3, 19 kPaintSpacing = 4 * kShapeTypeSpacing, 20 kLabelSpacing = 3 * kShapeSize, 21 kMargin = kShapeSpacing / 2, 22 kXfermodeTypeSpacing = kLabelSpacing + 2 * kPaintSpacing + kShapeTypeSpacing, 23 kTitleSpacing = 3 * kShapeSpacing / 4, 24 kSubtitleSpacing = 5 * kShapeSpacing / 8 25 }; 26 27 constexpr SkColor kBGColor = 0xc8d2b887; 28 29 constexpr SkColor kShapeColors[2] = { 30 0x82ff0080, // input color unknown 31 0xff00ffff, // input color opaque 32 }; 33 34 enum Shape { 35 kSquare_Shape, 36 kDiamond_Shape, 37 kOval_Shape, 38 kConcave_Shape, 39 40 kLast_Shape = kConcave_Shape 41 }; 42 43 /** 44 * Verifies AA works properly on all Xfermodes, including arithmetic, with both opaque and unknown 45 * src colors. 46 */ 47 class AAXfermodesGM : public skiagm::GM { 48 public: AAXfermodesGM()49 AAXfermodesGM() {} 50 51 protected: 52 enum DrawingPass { 53 kCheckerboard_Pass, 54 kBackground_Pass, 55 kShape_Pass 56 }; 57 onShortName()58 SkString onShortName() override { 59 return SkString("aaxfermodes"); 60 } 61 onISize()62 SkISize onISize() override { 63 return SkISize::Make(2 * kMargin + 2 * kXfermodeTypeSpacing - 64 (kXfermodeTypeSpacing - (kLabelSpacing + 2 * kPaintSpacing)), 65 2 * kMargin + kTitleSpacing + kSubtitleSpacing + 66 (1 + (int)SkBlendMode::kLastCoeffMode) * kShapeSpacing); 67 } 68 onOnceBeforeDraw()69 void onOnceBeforeDraw() override { 70 fLabelPaint.setAntiAlias(true); 71 sk_tool_utils::set_portable_typeface(&fLabelPaint); 72 fLabelPaint.setTextSize(5 * kShapeSize/8); 73 fLabelPaint.setSubpixelText(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 fLabelPaint.setTextAlign(SkPaint::kCenter_Align); 110 canvas->drawString("Src Unknown", 111 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2, 112 kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, fLabelPaint); 113 canvas->drawString("Src Opaque", 114 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 + 115 kPaintSpacing, kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, 116 fLabelPaint); 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 SkPaint titlePaint(fLabelPaint); 180 titlePaint.setTextSize(9 * titlePaint.getTextSize() / 8); 181 titlePaint.setFakeBoldText(true); 182 titlePaint.setTextAlign(SkPaint::kCenter_Align); 183 canvas->drawString("Porter Duff", 184 kLabelSpacing + 4 * kShapeTypeSpacing, 185 kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint); 186 canvas->drawString("Advanced", 187 kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing, 188 kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint); 189 190 draw_pass(canvas, kShape_Pass); 191 canvas->restore(); 192 } 193 drawModeName(SkCanvas * canvas,SkBlendMode mode)194 void drawModeName(SkCanvas* canvas, SkBlendMode mode) { 195 const char* modeName = SkBlendMode_Name(mode); 196 fLabelPaint.setTextAlign(SkPaint::kRight_Align); 197 canvas->drawString(modeName, kLabelSpacing - kShapeSize / 4, 198 fLabelPaint.getTextSize() / 4, fLabelPaint); 199 } 200 setupShapePaint(SkCanvas * canvas,GrColor color,SkBlendMode mode,SkPaint * paint)201 void setupShapePaint(SkCanvas* canvas, GrColor color, SkBlendMode mode, SkPaint* paint) { 202 paint->setColor(color); 203 204 if (mode == SkBlendMode::kPlus) { 205 // Check for overflow, otherwise we might get confusing AA artifacts. 206 int maxSum = SkTMax(SkTMax(SkColorGetA(kBGColor) + SkColorGetA(color), 207 SkColorGetR(kBGColor) + SkColorGetR(color)), 208 SkTMax(SkColorGetG(kBGColor) + SkColorGetG(color), 209 SkColorGetB(kBGColor) + SkColorGetB(color))); 210 211 if (maxSum > 255) { 212 SkPaint dimPaint; 213 dimPaint.setAntiAlias(false); 214 dimPaint.setBlendMode(SkBlendMode::kDstIn); 215 if (255 != paint->getAlpha()) { 216 // Dim the src and dst colors. 217 dimPaint.setARGB(255 * 255 / maxSum, 0, 0, 0); 218 paint->setAlpha(255 * paint->getAlpha() / maxSum); 219 } else { 220 // Just clear the dst, we need to preserve the paint's opacity. 221 dimPaint.setARGB(0, 0, 0, 0); 222 } 223 canvas->drawRect({ -kShapeSpacing/2, -kShapeSpacing/2, 224 kShapeSpacing/2 + 3 * kShapeTypeSpacing, kShapeSpacing/2 }, 225 dimPaint); 226 } 227 } 228 } 229 drawShape(SkCanvas * canvas,Shape shape,const SkPaint & paint,SkBlendMode mode)230 void drawShape(SkCanvas* canvas, Shape shape, const SkPaint& paint, SkBlendMode mode) { 231 SkASSERT(mode <= SkBlendMode::kLastMode); 232 SkPaint shapePaint(paint); 233 shapePaint.setAntiAlias(kSquare_Shape != shape); 234 shapePaint.setBlendMode(mode); 235 236 switch (shape) { 237 case kSquare_Shape: 238 canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 }, 239 shapePaint); 240 break; 241 242 case kDiamond_Shape: 243 canvas->save(); 244 canvas->rotate(45); 245 canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 }, 246 shapePaint); 247 canvas->restore(); 248 break; 249 250 case kOval_Shape: 251 canvas->save(); 252 canvas->rotate(static_cast<SkScalar>((511 * (int)mode + 257) % 360)); 253 canvas->drawPath(fOval, shapePaint); 254 canvas->restore(); 255 break; 256 257 case kConcave_Shape: 258 canvas->drawPath(fConcave, shapePaint); 259 break; 260 261 default: 262 SK_ABORT("Invalid shape."); 263 } 264 } 265 266 private: 267 SkPaint fLabelPaint; 268 SkPath fOval; 269 SkPath fConcave; 270 271 typedef skiagm::GM INHERITED; 272 }; 273 DEF_GM( return new AAXfermodesGM; ) 274