• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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