• 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/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/SkPathBuilder.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 = SkPathBuilder().moveTo(pts[0])
93                                .quadTo(pts[1], pts[2])
94                                .quadTo(pts[3], pts[0])
95                                .detach();
96 
97         fConcave = SkPathBuilder().moveTo(-radius, 0)
98                                   .quadTo(0, 0, 0, -radius)
99                                   .quadTo(0, 0, radius, 0)
100                                   .quadTo(0, 0, 0, radius)
101                                   .quadTo(0, 0, -radius, 0)
102                                   .close()
103                                   .detach();
104     }
105 
draw_pass(SkCanvas * canvas,DrawingPass drawingPass)106     void draw_pass(SkCanvas* canvas, DrawingPass drawingPass) {
107         SkRect clipRect =
108                 { -kShapeSize*11/16, -kShapeSize*11/16, kShapeSize*11/16, kShapeSize*11/16 };
109 
110         canvas->save();
111         if (kCheckerboard_Pass == drawingPass) {
112             canvas->translate(kMargin, kMargin);
113         }
114         canvas->translate(0, kTitleSpacing);
115 
116         for (size_t xfermodeSet = 0; xfermodeSet < 2; xfermodeSet++) {
117             size_t firstMode = ((size_t)SkBlendMode::kLastCoeffMode + 1) * xfermodeSet;
118             canvas->save();
119 
120             if (kShape_Pass == drawingPass) {
121                 SkTextUtils::DrawString(canvas, "Src Unknown",
122                         kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2,
123                         kSubtitleSpacing / 2 + fLabelFont.getSize() / 3, fLabelFont, SkPaint(),
124                                         SkTextUtils::kCenter_Align);
125                 SkTextUtils::DrawString(canvas, "Src Opaque",
126                         kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 +
127                         kPaintSpacing, kSubtitleSpacing / 2 + fLabelFont.getSize() / 3,
128                                         fLabelFont, SkPaint(), SkTextUtils::kCenter_Align);
129             }
130 
131             canvas->translate(0, kSubtitleSpacing + kShapeSpacing/2);
132 
133             for (size_t m = 0; m <= (size_t)SkBlendMode::kLastCoeffMode; m++) {
134                 if (firstMode + m > (size_t)SkBlendMode::kLastMode) {
135                     break;
136                 }
137                 SkBlendMode mode = static_cast<SkBlendMode>(firstMode + m);
138                 canvas->save();
139 
140                 if (kShape_Pass == drawingPass) {
141                     this->drawModeName(canvas, mode);
142                 }
143                 canvas->translate(kLabelSpacing + kShapeSpacing/2, 0);
144 
145                 for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) {
146                     SkPaint paint;
147                     this->setupShapePaint(canvas, kShapeColors[colorIdx], mode, &paint);
148                     SkASSERT(colorIdx == 0 || 255 == paint.getAlpha());
149                     canvas->save();
150 
151                     for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) {
152                         if (kShape_Pass != drawingPass) {
153                             canvas->save();
154                             canvas->clipRect(clipRect);
155                             if (kCheckerboard_Pass == drawingPass) {
156                                 ToolUtils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6, 10);
157                             } else {
158                                 SkASSERT(kBackground_Pass == drawingPass);
159                                 canvas->drawColor(kBGColor, SkBlendMode::kSrc);
160                             }
161                             canvas->restore();
162                         } else {
163                             this->drawShape(canvas, static_cast<Shape>(shapeIdx), paint, mode);
164                         }
165                         canvas->translate(kShapeTypeSpacing, 0);
166                     }
167 
168                     canvas->restore();
169                     canvas->translate(kPaintSpacing, 0);
170                 }
171 
172                 canvas->restore();
173                 canvas->translate(0, kShapeSpacing);
174             }
175 
176             canvas->restore();
177             canvas->translate(kXfermodeTypeSpacing, 0);
178         }
179 
180         canvas->restore();
181     }
182 
onDraw(SkCanvas * canvas)183     void onDraw(SkCanvas* canvas) override {
184         draw_pass(canvas, kCheckerboard_Pass);
185         canvas->saveLayer(nullptr, nullptr);
186 
187         canvas->translate(kMargin, kMargin);
188         draw_pass(canvas, kBackground_Pass);
189 
190         SkFont titleFont(fLabelFont);
191         titleFont.setSize(9 * titleFont.getSize() / 8);
192         titleFont.setEmbolden(true);
193         SkTextUtils::DrawString(canvas, "Porter Duff",
194                                 kLabelSpacing + 4 * kShapeTypeSpacing,
195                                 kTitleSpacing / 2 + titleFont.getSize() / 3, titleFont, SkPaint(),
196                                 SkTextUtils::kCenter_Align);
197         SkTextUtils::DrawString(canvas, "Advanced",
198                                 kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing,
199                                 kTitleSpacing / 2 + titleFont.getSize() / 3, titleFont, SkPaint(),
200                                 SkTextUtils::kCenter_Align);
201 
202         draw_pass(canvas, kShape_Pass);
203         canvas->restore();
204     }
205 
drawModeName(SkCanvas * canvas,SkBlendMode mode)206     void drawModeName(SkCanvas* canvas, SkBlendMode mode) {
207         const char* modeName = SkBlendMode_Name(mode);
208         SkTextUtils::DrawString(canvas, modeName, kLabelSpacing - kShapeSize / 4,
209                                 fLabelFont.getSize() / 4, fLabelFont, SkPaint(),
210                                 SkTextUtils::kRight_Align);
211     }
212 
setupShapePaint(SkCanvas * canvas,SkColor color,SkBlendMode mode,SkPaint * paint)213     void setupShapePaint(SkCanvas* canvas, SkColor color, SkBlendMode mode, SkPaint* paint) {
214         paint->setColor(color);
215 
216         if (mode == SkBlendMode::kPlus) {
217             // Check for overflow, otherwise we might get confusing AA artifacts.
218             int maxSum = std::max(std::max(SkColorGetA(kBGColor) + SkColorGetA(color),
219                                        SkColorGetR(kBGColor) + SkColorGetR(color)),
220                                 std::max(SkColorGetG(kBGColor) + SkColorGetG(color),
221                                        SkColorGetB(kBGColor) + SkColorGetB(color)));
222 
223             if (maxSum > 255) {
224                 SkPaint dimPaint;
225                 dimPaint.setAntiAlias(false);
226                 dimPaint.setBlendMode(SkBlendMode::kDstIn);
227                 if (255 != paint->getAlpha()) {
228                     // Dim the src and dst colors.
229                     dimPaint.setARGB(255 * 255 / maxSum, 0, 0, 0);
230                     paint->setAlpha(255 * paint->getAlpha() / maxSum);
231                 } else {
232                     // Just clear the dst, we need to preserve the paint's opacity.
233                     dimPaint.setARGB(0, 0, 0, 0);
234                 }
235                 canvas->drawRect({ -kShapeSpacing/2, -kShapeSpacing/2,
236                                    kShapeSpacing/2 + 3 * kShapeTypeSpacing, kShapeSpacing/2 },
237                                  dimPaint);
238             }
239         }
240     }
241 
drawShape(SkCanvas * canvas,Shape shape,const SkPaint & paint,SkBlendMode mode)242     void drawShape(SkCanvas* canvas, Shape shape, const SkPaint& paint, SkBlendMode mode) {
243         SkASSERT(mode <= SkBlendMode::kLastMode);
244         SkPaint shapePaint(paint);
245         shapePaint.setAntiAlias(kSquare_Shape != shape);
246         shapePaint.setBlendMode(mode);
247 
248         switch (shape) {
249             case kSquare_Shape:
250                 canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 },
251                                  shapePaint);
252                 break;
253 
254             case kDiamond_Shape:
255                 canvas->save();
256                 canvas->rotate(45);
257                 canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 },
258                                  shapePaint);
259                 canvas->restore();
260                 break;
261 
262             case kOval_Shape:
263                 canvas->save();
264                 canvas->rotate(static_cast<SkScalar>((511 * (int)mode + 257) % 360));
265                 canvas->drawPath(fOval, shapePaint);
266                 canvas->restore();
267                 break;
268 
269             case kConcave_Shape:
270                 canvas->drawPath(fConcave, shapePaint);
271                 break;
272 
273             default:
274                 SK_ABORT("Invalid shape.");
275         }
276     }
277 
278 private:
279     SkFont    fLabelFont;
280     SkPath    fOval;
281     SkPath    fConcave;
282 
283     using INHERITED = skiagm::GM;
284 };
285 DEF_GM( return new AAXfermodesGM; )
286