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 "sk_tool_utils.h"
10#include "SkBitmap.h"
11#include "SkCanvas.h"
12#include "SkGradientShader.h"
13#include "SkImageGenerator.h"
14#include "SkPaint.h"
15#include "SkPath.h"
16#include "SkPathOps.h"
17#include "SkPicture.h"
18#include "SkPictureRecorder.h"
19
20static void draw_vector_logo(SkCanvas* canvas, const SkRect& viewBox) {
21    constexpr char kSkiaStr[] = "SKIA";
22    constexpr SkScalar kGradientPad = .1f;
23    constexpr SkScalar kVerticalSpacing = 0.25f;
24    constexpr SkScalar kAccentScale = 1.20f;
25
26    SkPaint paint;
27    paint.setAntiAlias(true);
28    paint.setSubpixelText(true);
29    paint.setFakeBoldText(true);
30    sk_tool_utils::set_portable_typeface(&paint);
31
32    SkPath path;
33    SkRect iBox, skiBox, skiaBox;
34    paint.getTextPath("SKI", 3, 0, 0, &path);
35    TightBounds(path, &skiBox);
36    paint.getTextPath("I", 1, 0, 0, &path);
37    TightBounds(path, &iBox);
38    iBox.offsetTo(skiBox.fRight - iBox.width(), iBox.fTop);
39
40    const size_t textLen = strlen(kSkiaStr);
41    paint.getTextPath(kSkiaStr, textLen, 0, 0, &path);
42    TightBounds(path, &skiaBox);
43    skiaBox.outset(0, 2 * iBox.width() * (kVerticalSpacing + 1));
44
45    const SkScalar accentSize = iBox.width() * kAccentScale;
46    const SkScalar underlineY = iBox.bottom() +
47        (kVerticalSpacing + SkScalarSqrt(3) / 2) * accentSize;
48    SkMatrix m;
49    m.setRectToRect(skiaBox, viewBox, SkMatrix::kFill_ScaleToFit);
50    SkAutoCanvasRestore acr(canvas, true);
51    canvas->concat(m);
52
53    canvas->drawCircle(iBox.centerX(),
54                       iBox.y() - (0.5f + kVerticalSpacing) * accentSize,
55                       accentSize / 2,
56                       paint);
57
58    path.reset();
59    path.moveTo(iBox.centerX() - accentSize / 2, iBox.bottom() + kVerticalSpacing * accentSize);
60    path.rLineTo(accentSize, 0);
61    path.lineTo(iBox.centerX(), underlineY);
62    canvas->drawPath(path, paint);
63
64    SkRect underlineRect = SkRect::MakeLTRB(iBox.centerX() - iBox.width() * accentSize * 3,
65                                            underlineY,
66                                            iBox.centerX(),
67                                            underlineY + accentSize / 10);
68    const SkPoint pts1[] = { SkPoint::Make(underlineRect.x(), 0),
69                             SkPoint::Make(iBox.centerX(), 0) };
70    const SkScalar pos1[] = { 0, 0.75f };
71    const SkColor colors1[] = { SK_ColorTRANSPARENT, SK_ColorBLACK };
72    SkASSERT(SK_ARRAY_COUNT(pos1) == SK_ARRAY_COUNT(colors1));
73    paint.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos1, SK_ARRAY_COUNT(pos1),
74                                                 SkShader::kClamp_TileMode));
75    canvas->drawRect(underlineRect, paint);
76
77    const SkPoint pts2[] = { SkPoint::Make(iBox.x() - iBox.width() * kGradientPad, 0),
78                             SkPoint::Make(iBox.right() + iBox.width() * kGradientPad, 0) };
79    const SkScalar pos2[] = { 0, .01f, 1.0f/3, 1.0f/3, 2.0f/3, 2.0f/3, .99f, 1 };
80    const SkColor colors2[] = {
81        SK_ColorBLACK,
82        0xffca5139,
83        0xffca5139,
84        0xff8dbd53,
85        0xff8dbd53,
86        0xff5460a5,
87        0xff5460a5,
88        SK_ColorBLACK
89    };
90    SkASSERT(SK_ARRAY_COUNT(pos2) == SK_ARRAY_COUNT(colors2));
91    paint.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos2, SK_ARRAY_COUNT(pos2),
92                                                 SkShader::kClamp_TileMode));
93    canvas->drawText(kSkiaStr, textLen, 0, 0, paint);
94}
95
96// This GM exercises SkPictureImageGenerator features
97// (in particular its matrix vs. bounds semantics).
98class PictureGeneratorGM : public skiagm::GM {
99protected:
100    SkString onShortName() override {
101        return SkString("pictureimagegenerator");
102    }
103
104    SkISize onISize() override {
105        return SkISize::Make(1160, 860);
106    }
107
108    void onOnceBeforeDraw() override {
109        const SkRect rect = SkRect::MakeWH(kPictureWidth, kPictureHeight);
110        SkPictureRecorder recorder;
111        SkCanvas* canvas = recorder.beginRecording(rect);
112        draw_vector_logo(canvas, rect);
113        fPicture = recorder.finishRecordingAsPicture();
114    }
115
116    void onDraw(SkCanvas* canvas) override {
117        const struct {
118            SkISize  size;
119            SkScalar scaleX, scaleY;
120            SkScalar opacity;
121        } configs[] = {
122            { SkISize::Make(200, 100), 1, 1, 1 },
123            { SkISize::Make(200, 200), 1, 1, 1 },
124            { SkISize::Make(200, 200), 1, 2, 1 },
125            { SkISize::Make(400, 200), 2, 2, 1 },
126
127            { SkISize::Make(200, 100), 1, 1, 0.9f  },
128            { SkISize::Make(200, 200), 1, 1, 0.75f },
129            { SkISize::Make(200, 200), 1, 2, 0.5f  },
130            { SkISize::Make(400, 200), 2, 2, 0.25f },
131
132            { SkISize::Make(200, 200), 0.5f, 1,    1 },
133            { SkISize::Make(200, 200), 1,    0.5f, 1 },
134            { SkISize::Make(200, 200), 0.5f, 0.5f, 1 },
135            { SkISize::Make(200, 200), 2,    2,    1 },
136
137            { SkISize::Make(200, 100), -1,  1, 1    },
138            { SkISize::Make(200, 100),  1, -1, 1    },
139            { SkISize::Make(200, 100), -1, -1, 1    },
140            { SkISize::Make(200, 100), -1, -1, 0.5f },
141        };
142
143        auto srgbColorSpace = SkColorSpace::MakeSRGB();
144        const unsigned kDrawsPerRow = 4;
145        const SkScalar kDrawSize = 250;
146
147        for (size_t i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
148            SkPaint p;
149            p.setAlpha(SkScalarRoundToInt(255 * configs[i].opacity));
150
151            SkMatrix m = SkMatrix::MakeScale(configs[i].scaleX, configs[i].scaleY);
152            if (configs[i].scaleX < 0) {
153                m.postTranslate(SkIntToScalar(configs[i].size.width()), 0);
154            }
155            if (configs[i].scaleY < 0) {
156                m.postTranslate(0, SkIntToScalar(configs[i].size.height()));
157            }
158            std::unique_ptr<SkImageGenerator> gen =
159                SkImageGenerator::MakeFromPicture(configs[i].size, fPicture, &m,
160                                                 p.getAlpha() != 255 ? &p : nullptr,
161                                                 SkImage::BitDepth::kU8, srgbColorSpace);
162
163            SkImageInfo bmInfo = gen->getInfo().makeColorSpace(canvas->imageInfo().refColorSpace());
164
165            SkBitmap bm;
166            bm.allocPixels(bmInfo);
167            SkAssertResult(gen->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()));
168
169            const SkScalar x = kDrawSize * (i % kDrawsPerRow);
170            const SkScalar y = kDrawSize * (i / kDrawsPerRow);
171
172            p.setColor(0xfff0f0f0);
173            p.setAlpha(255);
174            canvas->drawRect(SkRect::MakeXYWH(x, y,
175                                              SkIntToScalar(bm.width()),
176                                              SkIntToScalar(bm.height())), p);
177            canvas->drawBitmap(bm, x, y);
178        }
179    }
180
181private:
182    sk_sp<SkPicture> fPicture;
183
184    const SkScalar kPictureWidth = 200;
185    const SkScalar kPictureHeight = 100;
186
187    typedef skiagm::GM INHERITED;
188};
189
190DEF_GM(return new PictureGeneratorGM;)
191