1/*
2 * Copyright 2013 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 "sk_tool_utils.h"
9#include "SkBitmap.h"
10#include "SkPath.h"
11#include "SkRandom.h"
12#include "SkShader.h"
13#include "SkSurface.h"
14
15namespace skiagm {
16
17/**
18 * Renders overlapping shapes with colorburn against a checkerboard.
19 */
20class DstReadShuffle : public GM {
21public:
22    DstReadShuffle() { this->setBGColor(kBackground); }
23
24protected:
25    enum ShapeType {
26        kCircle_ShapeType,
27        kRoundRect_ShapeType,
28        kRect_ShapeType,
29        kConvexPath_ShapeType,
30        kConcavePath_ShapeType,
31        kText_ShapeType,
32        kNumShapeTypes
33    };
34
35    SkString onShortName() override {
36        return SkString("dstreadshuffle");
37    }
38
39    SkISize onISize() override {
40        return SkISize::Make(530, 680);
41    }
42
43    void drawShape(SkCanvas* canvas, SkPaint* paint, ShapeType type) {
44        const SkRect kRect = SkRect::MakeXYWH(0, 0, 75.f, 85.f);
45        switch (type) {
46            case kCircle_ShapeType:
47                canvas->drawCircle(kRect.centerX(), kRect.centerY(), kRect.width() / 2.f, *paint);
48                break;
49            case kRoundRect_ShapeType:
50                canvas->drawRoundRect(kRect, 15.f, 15.f, *paint);
51                break;
52            case kRect_ShapeType:
53                canvas->drawRect(kRect, *paint);
54                break;
55            case kConvexPath_ShapeType:
56                if (fConvexPath.isEmpty()) {
57                    SkPoint points[4];
58                    kRect.toQuad(points);
59                    fConvexPath.moveTo(points[0]);
60                    fConvexPath.quadTo(points[1], points[2]);
61                    fConvexPath.quadTo(points[3], points[0]);
62                    SkASSERT(fConvexPath.isConvex());
63                }
64                canvas->drawPath(fConvexPath, *paint);
65                break;
66            case kConcavePath_ShapeType:
67                if (fConcavePath.isEmpty()) {
68                    SkPoint points[5] = {{50.f, 0.f}};
69                    SkMatrix rot;
70                    rot.setRotate(360.f / 5, 50.f, 70.f);
71                    for (int i = 1; i < 5; ++i) {
72                        rot.mapPoints(points + i, points + i - 1, 1);
73                    }
74                    fConcavePath.moveTo(points[0]);
75                    for (int i = 0; i < 5; ++i) {
76                        fConcavePath.lineTo(points[(2 * i) % 5]);
77                    }
78                    fConcavePath.setFillType(SkPath::kEvenOdd_FillType);
79                    SkASSERT(!fConcavePath.isConvex());
80                }
81                canvas->drawPath(fConcavePath, *paint);
82                break;
83            case kText_ShapeType: {
84                const char* text = "N";
85                paint->setTextSize(100.f);
86                paint->setFakeBoldText(true);
87                sk_tool_utils::set_portable_typeface(paint);
88                canvas->drawText(text, strlen(text), 0.f, 100.f, *paint);
89            }
90            default:
91                break;
92        }
93    }
94
95    static SkColor GetColor(SkRandom* random) {
96        SkColor color = sk_tool_utils::color_to_565(random->nextU() | 0xFF000000);
97        return SkColorSetA(color, 0x80);
98    }
99
100    static void DrawHairlines(SkCanvas* canvas) {
101        if (canvas->imageInfo().alphaType() == kOpaque_SkAlphaType) {
102            canvas->clear(kBackground);
103        } else {
104            canvas->clear(SK_ColorTRANSPARENT);
105        }
106        SkPaint hairPaint;
107        hairPaint.setStyle(SkPaint::kStroke_Style);
108        hairPaint.setStrokeWidth(0);
109        hairPaint.setAntiAlias(true);
110        static constexpr int kNumHairlines = 12;
111        SkPoint pts[] = {{3.f, 7.f}, {29.f, 7.f}};
112        SkRandom colorRandom;
113        SkMatrix rot;
114        rot.setRotate(360.f / kNumHairlines, 15.5f, 12.f);
115        rot.postTranslate(3.f, 0);
116        for (int i = 0; i < 12; ++i) {
117            hairPaint.setColor(GetColor(&colorRandom));
118            canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, hairPaint);
119            rot.mapPoints(pts, 2);
120        }
121    }
122
123    void onDraw(SkCanvas* canvas) override {
124        SkScalar y = 5;
125        for (int i = 0; i < kNumShapeTypes; i++) {
126            SkRandom colorRandom;
127            ShapeType shapeType = static_cast<ShapeType>(i);
128            SkScalar x = 5;
129            for (int r = 0; r <= 15; r++) {
130                SkPaint p;
131                p.setAntiAlias(true);
132                p.setColor(GetColor(&colorRandom));
133                // In order to get some op combining on the GPU backend we do 2 src over
134                // for each xfer mode which requires a dst read
135                p.setBlendMode(r % 3 == 0 ? SkBlendMode::kColorBurn : SkBlendMode::kSrcOver);
136                canvas->save();
137                canvas->translate(x, y);
138                this->drawShape(canvas, &p, shapeType);
139                canvas->restore();
140                x += 15;
141            }
142            y += 110;
143        }
144        // Draw hairlines to a surface and then draw that to the main canvas with a zoom so that
145        // it is easier to see how they blend.
146        SkImageInfo info;
147        // Recording canvases don't have a color type.
148        if (SkColorType::kUnknown_SkColorType == canvas->imageInfo().colorType()) {
149            info = SkImageInfo::MakeN32Premul(35, 35);
150        } else {
151            info = SkImageInfo::Make(35, 35,
152                                     canvas->imageInfo().colorType(),
153                                     canvas->imageInfo().alphaType(),
154                                     canvas->imageInfo().refColorSpace());
155        }
156        auto surf = canvas->makeSurface(info);
157        if (!surf) {
158            // Fall back to raster. Raster supports only one of the 8 bit per-channel RGBA or BGRA
159            // formats. This fall back happens when running with --preAbandonGpuContext.
160            if ((info.colorType() == kRGBA_8888_SkColorType ||
161                 info.colorType() == kBGRA_8888_SkColorType) &&
162                info.colorType() != kN32_SkColorType) {
163                info = SkImageInfo::Make(35, 35,
164                                         kN32_SkColorType,
165                                         canvas->imageInfo().alphaType(),
166                                         canvas->imageInfo().refColorSpace());
167            }
168            surf = SkSurface::MakeRaster(info);
169        }
170        canvas->scale(5.f, 5.f);
171        canvas->translate(67.f, 10.f);
172        DrawHairlines(surf->getCanvas());
173        canvas->drawImage(surf->makeImageSnapshot(), 0.f, 0.f);
174    }
175
176private:
177    static constexpr SkColor kBackground = SK_ColorLTGRAY;
178    SkPath fConcavePath;
179    SkPath fConvexPath;
180    typedef GM INHERITED;
181};
182
183//////////////////////////////////////////////////////////////////////////////
184
185static GM* MyFactory(void*) { return new DstReadShuffle; }
186static GMRegistry reg(MyFactory);
187
188}
189