1/*
2 * Copyright 2017 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 "Resources.h"
9#include "SampleCode.h"
10#include "sk_tool_utils.h"
11
12#include "SkCanvas.h"
13#include "SkFontMgr.h"
14#include "SkRandom.h"
15#include "SkTypeface.h"
16#include "SkTextBlob.h"
17
18#if SK_SUPPORT_GPU
19#include "GrContext.h"
20#endif
21
22static void make_paint(SkPaint* paint, sk_sp<SkTypeface> typeface) {
23  static const int kTextSize = 56;
24
25  paint->setAntiAlias(true);
26  paint->setColor(0xDE000000);
27  paint->setTypeface(typeface);
28  paint->setTextSize(kTextSize);
29  paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
30}
31
32static sk_sp<SkTypeface> chinese_typeface() {
33#ifdef SK_BUILD_FOR_ANDROID
34    return MakeResourceAsTypeface("fonts/NotoSansCJK-Regular.ttc");
35#elif defined(SK_BUILD_FOR_WIN)
36    return SkTypeface::MakeFromName("SimSun", SkFontStyle());
37#elif defined(SK_BUILD_FOR_MAC)
38    return SkTypeface::MakeFromName("Hiragino Sans GB W3", SkFontStyle());
39#elif defined(SK_BUILD_FOR_IOS)
40    return SkTypeface::MakeFromName("Hiragino Sans GB W3", SkFontStyle());
41#elif defined(SK_BUILD_FOR_UNIX)
42    return SkTypeface::MakeFromName("Noto Sans CJK SC", SkFontStyle());
43#else
44    return nullptr;
45#endif
46}
47
48class ChineseFlingView : public SampleView {
49public:
50    ChineseFlingView() : fBlobs(kNumBlobs) {}
51
52protected:
53    bool onQuery(SkEvent* evt) override {
54        if (SampleCode::TitleQ(*evt)) {
55            SampleCode::TitleR(evt, "chinese-fling");
56            return true;
57        }
58        return this->INHERITED::onQuery(evt);
59    }
60
61    void onDrawContent(SkCanvas* canvas) override {
62        if (!fInitialized) {
63            this->init();
64            fInitialized = true;
65        }
66
67        canvas->clear(0xFFDDDDDD);
68
69        SkPaint paint;
70        make_paint(&paint, fTypeface);
71
72        // draw a consistent run of the 'words' - one word per line
73        int index = fIndex;
74        for (SkScalar y = 0.0f; y < 1024.0f; ) {
75
76            y += -fMetrics.fAscent;
77            canvas->drawTextBlob(fBlobs[index], 0, y, paint);
78
79            y += fMetrics.fDescent + fMetrics.fLeading;
80            ++index;
81            index %= fBlobs.count();
82        }
83        // now "fling" a random amount
84        fIndex += fRand.nextRangeU(5, 20);
85        fIndex %= fBlobs.count();
86    }
87
88private:
89    static constexpr auto kNumBlobs = 200;
90    static constexpr auto kWordLength = 16;
91
92    void init() {
93        fTypeface = chinese_typeface();
94
95        SkPaint paint;
96        make_paint(&paint, fTypeface);
97
98        paint.getFontMetrics(&fMetrics);
99
100        SkUnichar glyphs[kWordLength];
101        for (int32_t i = 0; i < kNumBlobs; ++i) {
102            this->createRandomWord(glyphs);
103
104            SkTextBlobBuilder builder;
105            sk_tool_utils::add_to_text_blob_w_len(&builder, (const char*) glyphs, kWordLength*4,
106                                                  paint, 0, 0);
107
108            fBlobs.emplace_back(builder.make());
109        }
110
111        fIndex = 0;
112    }
113
114    // Construct a random kWordLength character 'word' drawing from the full Chinese set
115    void createRandomWord(SkUnichar glyphs[kWordLength]) {
116        for (int i = 0; i < kWordLength; ++i) {
117            glyphs[i] = fRand.nextRangeU(0x4F00, 0x9FA0);
118        }
119    }
120
121    bool                        fInitialized = false;
122    sk_sp<SkTypeface>           fTypeface;
123    SkPaint::FontMetrics        fMetrics;
124    SkTArray<sk_sp<SkTextBlob>> fBlobs;
125    SkRandom                    fRand;
126    int                         fIndex;
127
128    typedef SkView INHERITED;
129};
130
131class ChineseZoomView : public SampleView {
132public:
133    ChineseZoomView() : fBlobs(kNumBlobs), fScale(15.0f), fTranslate(0.0f) {}
134
135protected:
136    bool onQuery(SkEvent* evt) override {
137        if (SampleCode::TitleQ(*evt)) {
138            SampleCode::TitleR(evt, "chinese-zoom");
139            return true;
140        }
141        SkUnichar uni;
142        if (SampleCode::CharQ(*evt, &uni)) {
143            if ('>' == uni) {
144                fScale += 0.125f;
145                return true;
146            }
147            if ('<' == uni) {
148                fScale -= 0.125f;
149                return true;
150            }
151        }
152        return this->INHERITED::onQuery(evt);
153    }
154
155    void onDrawContent(SkCanvas* canvas) override {
156        bool afterFirstFrame = fInitialized;
157        if (!fInitialized) {
158            this->init();
159            fInitialized = true;
160        }
161
162        canvas->clear(0xFFDDDDDD);
163
164        SkPaint paint;
165        paint.setAntiAlias(true);
166        paint.setColor(0xDE000000);
167        paint.setTypeface(fTypeface);
168        paint.setTextSize(11);
169        paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
170
171        if (afterFirstFrame) {
172#if SK_SUPPORT_GPU
173            GrContext* grContext = canvas->getGrContext();
174            if (grContext) {
175                sk_sp<SkImage> image =
176                grContext->getFontAtlasImage_ForTesting(GrMaskFormat::kA8_GrMaskFormat, 0);
177                canvas->drawImageRect(image,
178                                      SkRect::MakeXYWH(10.0f, 10.0f, 512.0f, 512.0), &paint);
179                image = grContext->getFontAtlasImage_ForTesting(GrMaskFormat::kA8_GrMaskFormat, 1);
180                canvas->drawImageRect(image,
181                                      SkRect::MakeXYWH(522.0f, 10.0f, 512.f, 512.0f), &paint);
182                image = grContext->getFontAtlasImage_ForTesting(GrMaskFormat::kA8_GrMaskFormat, 2);
183                canvas->drawImageRect(image,
184                                      SkRect::MakeXYWH(10.0f, 522.0f, 512.0f, 512.0f), &paint);
185                image = grContext->getFontAtlasImage_ForTesting(GrMaskFormat::kA8_GrMaskFormat, 3);
186                canvas->drawImageRect(image,
187                                      SkRect::MakeXYWH(522.0f, 522.0f, 512.0f, 512.0f), &paint);
188            }
189#endif
190        }
191
192        canvas->scale(fScale, fScale);
193        canvas->translate(0, fTranslate);
194        fTranslate -= 0.5f;
195
196        // draw a consistent run of the 'words' - one word per line
197        SkScalar y = 0;
198        for (int index = 0; index < kNumBlobs; ++index) {
199            y += -fMetrics.fAscent;
200            canvas->drawTextBlob(fBlobs[index], 0, y, paint);
201
202            y += 3*(fMetrics.fDescent - fMetrics.fAscent + fMetrics.fLeading);
203        }
204    }
205
206private:
207    static constexpr auto kNumBlobs = 8;
208    static constexpr auto kParagraphLength = 175;
209
210    void init() {
211        fTypeface = chinese_typeface();
212
213        SkPaint paint;
214        paint.setAntiAlias(true);
215        paint.setColor(0xDE000000);
216        paint.setTypeface(fTypeface);
217        paint.setTextSize(11);
218        paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
219
220        paint.getFontMetrics(&fMetrics);
221
222        SkUnichar glyphs[45];
223        for (int32_t i = 0; i < kNumBlobs; ++i) {
224            SkTextBlobBuilder builder;
225            auto paragraphLength = kParagraphLength;
226            SkScalar y = 0;
227            while (paragraphLength - 45 > 0) {
228                auto currentLineLength = SkTMin(45, paragraphLength - 45);
229                this->createRandomLine(glyphs, currentLineLength);
230
231                sk_tool_utils::add_to_text_blob_w_len(&builder, (const char*) glyphs,
232                                                      currentLineLength*4, paint, 0, y);
233                y += fMetrics.fDescent - fMetrics.fAscent + fMetrics.fLeading;
234                paragraphLength -= 45;
235            }
236            fBlobs.emplace_back(builder.make());
237        }
238
239        fIndex = 0;
240    }
241
242    // Construct a random kWordLength character 'word' drawing from the full Chinese set
243    void createRandomLine(SkUnichar glyphs[45], int lineLength) {
244        for (auto i = 0; i < lineLength; ++i) {
245            glyphs[i] = fRand.nextRangeU(0x4F00, 0x9FA0);
246        }
247    }
248
249    bool                        fInitialized = false;
250    sk_sp<SkTypeface>           fTypeface;
251    SkPaint::FontMetrics        fMetrics;
252    SkTArray<sk_sp<SkTextBlob>> fBlobs;
253    SkRandom                    fRand;
254    SkScalar                    fScale;
255    SkScalar                    fTranslate;
256    int                         fIndex;
257
258    typedef SkView INHERITED;
259};
260
261//////////////////////////////////////////////////////////////////////////////
262
263static SkView* FlingFactory() { return new ChineseFlingView; }
264static SkViewRegister regFling(FlingFactory);
265
266static SkView* ZoomFactory() { return new ChineseZoomView; }
267static SkViewRegister regZoom(ZoomFactory);
268