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