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