1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include <string>
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h"
13cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
14cb93a386Sopenharmony_ci#include "include/core/SkFontMgr.h"
15cb93a386Sopenharmony_ci#include "include/core/SkGraphics.h"
16cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
17cb93a386Sopenharmony_ci#include "include/core/SkPoint.h"
18cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
19cb93a386Sopenharmony_ci#include "include/core/SkTextBlob.h"
20cb93a386Sopenharmony_ci#include "include/core/SkTypeface.h"
21cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
22cb93a386Sopenharmony_ci#include "src/core/SkGlyphRun.h"
23cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
24cb93a386Sopenharmony_ci#include "tools/fonts/RandomScalerContext.h"
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_WIN
27cb93a386Sopenharmony_ci    #include "include/ports/SkTypeface_win.h"
28cb93a386Sopenharmony_ci#endif
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ci#include "tests/Test.h"
31cb93a386Sopenharmony_ci
32cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
33cb93a386Sopenharmony_ci#include "src/gpu/text/GrAtlasManager.h"
34cb93a386Sopenharmony_ci#include "src/gpu/text/GrTextBlobCache.h"
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_cistatic void draw(SkCanvas* canvas, int redraw, const SkTArray<sk_sp<SkTextBlob>>& blobs) {
37cb93a386Sopenharmony_ci    int yOffset = 0;
38cb93a386Sopenharmony_ci    for (int r = 0; r < redraw; r++) {
39cb93a386Sopenharmony_ci        for (int i = 0; i < blobs.count(); i++) {
40cb93a386Sopenharmony_ci            const auto& blob = blobs[i];
41cb93a386Sopenharmony_ci            const SkRect& bounds = blob->bounds();
42cb93a386Sopenharmony_ci            yOffset += SkScalarCeilToInt(bounds.height());
43cb93a386Sopenharmony_ci            SkPaint paint;
44cb93a386Sopenharmony_ci            canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint);
45cb93a386Sopenharmony_ci        }
46cb93a386Sopenharmony_ci    }
47cb93a386Sopenharmony_ci}
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_cistatic const int kWidth = 1024;
50cb93a386Sopenharmony_cistatic const int kHeight = 768;
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_cistatic void setup_always_evict_atlas(GrDirectContext* dContext) {
53cb93a386Sopenharmony_ci    dContext->priv().getAtlasManager()->setAtlasDimensionsToMinimum_ForTesting();
54cb93a386Sopenharmony_ci}
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ciclass GrTextBlobTestingPeer {
57cb93a386Sopenharmony_cipublic:
58cb93a386Sopenharmony_ci    static void SetBudget(GrTextBlobCache* cache, size_t budget) {
59cb93a386Sopenharmony_ci        SkAutoSpinlock lock{cache->fSpinLock};
60cb93a386Sopenharmony_ci        cache->fSizeBudget = budget;
61cb93a386Sopenharmony_ci        cache->internalCheckPurge();
62cb93a386Sopenharmony_ci    }
63cb93a386Sopenharmony_ci};
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci// This test hammers the GPU textblobcache and font atlas
66cb93a386Sopenharmony_cistatic void text_blob_cache_inner(skiatest::Reporter* reporter, GrDirectContext* dContext,
67cb93a386Sopenharmony_ci                                  int maxTotalText, int maxGlyphID, int maxFamilies, bool normal,
68cb93a386Sopenharmony_ci                                  bool stressTest) {
69cb93a386Sopenharmony_ci    // setup surface
70cb93a386Sopenharmony_ci    uint32_t flags = 0;
71cb93a386Sopenharmony_ci    SkSurfaceProps props(flags, kRGB_H_SkPixelGeometry);
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci    // configure our context for maximum stressing of cache and atlas
74cb93a386Sopenharmony_ci    if (stressTest) {
75cb93a386Sopenharmony_ci        setup_always_evict_atlas(dContext);
76cb93a386Sopenharmony_ci        GrTextBlobTestingPeer::SetBudget(dContext->priv().getTextBlobCache(), 0);
77cb93a386Sopenharmony_ci    }
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci    SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType,
80cb93a386Sopenharmony_ci                                         kPremul_SkAlphaType);
81cb93a386Sopenharmony_ci    auto surface(SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info, 0, &props));
82cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, surface);
83cb93a386Sopenharmony_ci    if (!surface) {
84cb93a386Sopenharmony_ci        return;
85cb93a386Sopenharmony_ci    }
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci    SkCanvas* canvas = surface->getCanvas();
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_ci    int count = std::min(fm->countFamilies(), maxFamilies);
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci    // make a ton of text
94cb93a386Sopenharmony_ci    SkAutoTArray<uint16_t> text(maxTotalText);
95cb93a386Sopenharmony_ci    for (int i = 0; i < maxTotalText; i++) {
96cb93a386Sopenharmony_ci        text[i] = i % maxGlyphID;
97cb93a386Sopenharmony_ci    }
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    // generate textblobs
100cb93a386Sopenharmony_ci    SkTArray<sk_sp<SkTextBlob>> blobs;
101cb93a386Sopenharmony_ci    for (int i = 0; i < count; i++) {
102cb93a386Sopenharmony_ci        SkFont font;
103cb93a386Sopenharmony_ci        font.setSize(48); // draw big glyphs to really stress the atlas
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci        SkString familyName;
106cb93a386Sopenharmony_ci        fm->getFamilyName(i, &familyName);
107cb93a386Sopenharmony_ci        sk_sp<SkFontStyleSet> set(fm->createStyleSet(i));
108cb93a386Sopenharmony_ci        for (int j = 0; j < set->count(); ++j) {
109cb93a386Sopenharmony_ci            SkFontStyle fs;
110cb93a386Sopenharmony_ci            set->getStyle(j, &fs, nullptr);
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci            // We use a typeface which randomy returns unexpected mask formats to fuzz
113cb93a386Sopenharmony_ci            sk_sp<SkTypeface> orig(set->createTypeface(j));
114cb93a386Sopenharmony_ci            if (normal) {
115cb93a386Sopenharmony_ci                font.setTypeface(orig);
116cb93a386Sopenharmony_ci            } else {
117cb93a386Sopenharmony_ci                font.setTypeface(sk_make_sp<SkRandomTypeface>(orig, SkPaint(), true));
118cb93a386Sopenharmony_ci            }
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_ci            SkTextBlobBuilder builder;
121cb93a386Sopenharmony_ci            for (int aa = 0; aa < 2; aa++) {
122cb93a386Sopenharmony_ci                for (int subpixel = 0; subpixel < 2; subpixel++) {
123cb93a386Sopenharmony_ci                    for (int lcd = 0; lcd < 2; lcd++) {
124cb93a386Sopenharmony_ci                        font.setEdging(SkFont::Edging::kAlias);
125cb93a386Sopenharmony_ci                        if (aa) {
126cb93a386Sopenharmony_ci                            font.setEdging(SkFont::Edging::kAntiAlias);
127cb93a386Sopenharmony_ci                            if (lcd) {
128cb93a386Sopenharmony_ci                                font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
129cb93a386Sopenharmony_ci                            }
130cb93a386Sopenharmony_ci                        }
131cb93a386Sopenharmony_ci                        font.setSubpixel(SkToBool(subpixel));
132cb93a386Sopenharmony_ci                        if (!SkToBool(lcd)) {
133cb93a386Sopenharmony_ci                            font.setSize(160);
134cb93a386Sopenharmony_ci                        }
135cb93a386Sopenharmony_ci                        const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(font,
136cb93a386Sopenharmony_ci                                                                                   maxTotalText,
137cb93a386Sopenharmony_ci                                                                                   0, 0,
138cb93a386Sopenharmony_ci                                                                                   nullptr);
139cb93a386Sopenharmony_ci                        memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t));
140cb93a386Sopenharmony_ci                    }
141cb93a386Sopenharmony_ci                }
142cb93a386Sopenharmony_ci            }
143cb93a386Sopenharmony_ci            blobs.emplace_back(builder.make());
144cb93a386Sopenharmony_ci        }
145cb93a386Sopenharmony_ci    }
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_ci    // create surface where LCD is impossible
148cb93a386Sopenharmony_ci    info = SkImageInfo::Make(kWidth, kHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
149cb93a386Sopenharmony_ci    SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry);
150cb93a386Sopenharmony_ci    auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD));
151cb93a386Sopenharmony_ci    REPORTER_ASSERT(reporter, surface);
152cb93a386Sopenharmony_ci    if (!surface) {
153cb93a386Sopenharmony_ci        return;
154cb93a386Sopenharmony_ci    }
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci    SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas();
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci    // test redraw
159cb93a386Sopenharmony_ci    draw(canvas, 2, blobs);
160cb93a386Sopenharmony_ci    draw(canvasNoLCD, 2, blobs);
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci    // test draw after free
163cb93a386Sopenharmony_ci    dContext->freeGpuResources();
164cb93a386Sopenharmony_ci    draw(canvas, 1, blobs);
165cb93a386Sopenharmony_ci
166cb93a386Sopenharmony_ci    dContext->freeGpuResources();
167cb93a386Sopenharmony_ci    draw(canvasNoLCD, 1, blobs);
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci    // test draw after abandon
170cb93a386Sopenharmony_ci    dContext->abandonContext();
171cb93a386Sopenharmony_ci    draw(canvas, 1, blobs);
172cb93a386Sopenharmony_ci}
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_ci#ifdef SK_ENABLE_SMALL_PAGE
175cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_MOCK_CONTEXT(TextBlobCache, reporter, ctxInfo) {
176cb93a386Sopenharmony_ci    text_blob_cache_inner(reporter, ctxInfo.directContext(), 1024, 256, 30, true, true);
177cb93a386Sopenharmony_ci}
178cb93a386Sopenharmony_ci#else
179cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_MOCK_CONTEXT(TextBlobCache, reporter, ctxInfo) {
180cb93a386Sopenharmony_ci    text_blob_cache_inner(reporter, ctxInfo.directContext(), 1024, 256, 30, true, false);
181cb93a386Sopenharmony_ci}
182cb93a386Sopenharmony_ci#endif
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_MOCK_CONTEXT(TextBlobStressCache, reporter, ctxInfo) {
185cb93a386Sopenharmony_ci    text_blob_cache_inner(reporter, ctxInfo.directContext(), 256, 256, 10, true, true);
186cb93a386Sopenharmony_ci}
187cb93a386Sopenharmony_ci
188cb93a386Sopenharmony_ci#ifdef SK_ENABLE_SMALL_PAGE
189cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_MOCK_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) {
190cb93a386Sopenharmony_ci    text_blob_cache_inner(reporter, ctxInfo.directContext(), 256, 256, 10, false, true);
191cb93a386Sopenharmony_ci}
192cb93a386Sopenharmony_ci#else
193cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_MOCK_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) {
194cb93a386Sopenharmony_ci    text_blob_cache_inner(reporter, ctxInfo.directContext(), 256, 256, 10, false, false);
195cb93a386Sopenharmony_ci}
196cb93a386Sopenharmony_ci#endif
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_MOCK_CONTEXT(TextBlobStressAbnormal, reporter, ctxInfo) {
199cb93a386Sopenharmony_ci    text_blob_cache_inner(reporter, ctxInfo.directContext(), 256, 256, 10, false, true);
200cb93a386Sopenharmony_ci}
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_cistatic const int kScreenDim = 160;
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_cistatic SkBitmap draw_blob(SkTextBlob* blob, SkSurface* surface, SkPoint offset) {
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_ci    SkPaint paint;
207cb93a386Sopenharmony_ci
208cb93a386Sopenharmony_ci    SkCanvas* canvas = surface->getCanvas();
209cb93a386Sopenharmony_ci    canvas->save();
210cb93a386Sopenharmony_ci    canvas->drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
211cb93a386Sopenharmony_ci    canvas->translate(offset.fX, offset.fY);
212cb93a386Sopenharmony_ci    canvas->drawTextBlob(blob, 0, 0, paint);
213cb93a386Sopenharmony_ci    SkBitmap bitmap;
214cb93a386Sopenharmony_ci    bitmap.allocN32Pixels(kScreenDim, kScreenDim);
215cb93a386Sopenharmony_ci    surface->readPixels(bitmap, 0, 0);
216cb93a386Sopenharmony_ci    canvas->restore();
217cb93a386Sopenharmony_ci    return bitmap;
218cb93a386Sopenharmony_ci}
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_cistatic bool compare_bitmaps(const SkBitmap& expected, const SkBitmap& actual) {
221cb93a386Sopenharmony_ci    SkASSERT(expected.width() == actual.width());
222cb93a386Sopenharmony_ci    SkASSERT(expected.height() == actual.height());
223cb93a386Sopenharmony_ci    for (int i = 0; i < expected.width(); ++i) {
224cb93a386Sopenharmony_ci        for (int j = 0; j < expected.height(); ++j) {
225cb93a386Sopenharmony_ci            SkColor expectedColor = expected.getColor(i, j);
226cb93a386Sopenharmony_ci            SkColor actualColor = actual.getColor(i, j);
227cb93a386Sopenharmony_ci            if (expectedColor != actualColor) {
228cb93a386Sopenharmony_ci                return false;
229cb93a386Sopenharmony_ci            }
230cb93a386Sopenharmony_ci        }
231cb93a386Sopenharmony_ci    }
232cb93a386Sopenharmony_ci    return true;
233cb93a386Sopenharmony_ci}
234cb93a386Sopenharmony_ci
235cb93a386Sopenharmony_cistatic sk_sp<SkTextBlob> make_blob() {
236cb93a386Sopenharmony_ci    auto tf = SkTypeface::MakeFromName("Roboto2-Regular", SkFontStyle());
237cb93a386Sopenharmony_ci    SkFont font;
238cb93a386Sopenharmony_ci    font.setTypeface(tf);
239cb93a386Sopenharmony_ci    font.setSubpixel(false);
240cb93a386Sopenharmony_ci    font.setEdging(SkFont::Edging::kAlias);
241cb93a386Sopenharmony_ci    font.setSize(24);
242cb93a386Sopenharmony_ci
243cb93a386Sopenharmony_ci    static char text[] = "HekpqB";
244cb93a386Sopenharmony_ci    static const int maxGlyphLen = sizeof(text) * 4;
245cb93a386Sopenharmony_ci    SkGlyphID glyphs[maxGlyphLen];
246cb93a386Sopenharmony_ci    int glyphCount =
247cb93a386Sopenharmony_ci            font.textToGlyphs(text, sizeof(text), SkTextEncoding::kUTF8, glyphs, maxGlyphLen);
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_ci    SkTextBlobBuilder builder;
250cb93a386Sopenharmony_ci    const auto& runBuffer = builder.allocRun(font, glyphCount, 0, 0);
251cb93a386Sopenharmony_ci    for (int i = 0; i < glyphCount; i++) {
252cb93a386Sopenharmony_ci        runBuffer.glyphs[i] = glyphs[i];
253cb93a386Sopenharmony_ci    }
254cb93a386Sopenharmony_ci    return builder.make();
255cb93a386Sopenharmony_ci}
256cb93a386Sopenharmony_ci
257cb93a386Sopenharmony_ci// Turned off to pass on android and ios devices, which were running out of memory..
258cb93a386Sopenharmony_ci#if 0
259cb93a386Sopenharmony_cistatic sk_sp<SkTextBlob> make_large_blob() {
260cb93a386Sopenharmony_ci    auto tf = SkTypeface::MakeFromName("Roboto2-Regular", SkFontStyle());
261cb93a386Sopenharmony_ci    SkFont font;
262cb93a386Sopenharmony_ci    font.setTypeface(tf);
263cb93a386Sopenharmony_ci    font.setSubpixel(false);
264cb93a386Sopenharmony_ci    font.setEdging(SkFont::Edging::kAlias);
265cb93a386Sopenharmony_ci    font.setSize(24);
266cb93a386Sopenharmony_ci
267cb93a386Sopenharmony_ci    const int mallocSize = 0x3c3c3bd; // x86 size
268cb93a386Sopenharmony_ci    std::unique_ptr<char[]> text{new char[mallocSize + 1]};
269cb93a386Sopenharmony_ci    if (text == nullptr) {
270cb93a386Sopenharmony_ci        return nullptr;
271cb93a386Sopenharmony_ci    }
272cb93a386Sopenharmony_ci    for (int i = 0; i < mallocSize; i++) {
273cb93a386Sopenharmony_ci        text[i] = 'x';
274cb93a386Sopenharmony_ci    }
275cb93a386Sopenharmony_ci    text[mallocSize] = 0;
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci    static const int maxGlyphLen = mallocSize;
278cb93a386Sopenharmony_ci    std::unique_ptr<SkGlyphID[]> glyphs{new SkGlyphID[maxGlyphLen]};
279cb93a386Sopenharmony_ci    int glyphCount =
280cb93a386Sopenharmony_ci            font.textToGlyphs(
281cb93a386Sopenharmony_ci                    text.get(), mallocSize, SkTextEncoding::kUTF8, glyphs.get(), maxGlyphLen);
282cb93a386Sopenharmony_ci    SkTextBlobBuilder builder;
283cb93a386Sopenharmony_ci    const auto& runBuffer = builder.allocRun(font, glyphCount, 0, 0);
284cb93a386Sopenharmony_ci    for (int i = 0; i < glyphCount; i++) {
285cb93a386Sopenharmony_ci        runBuffer.glyphs[i] = glyphs[i];
286cb93a386Sopenharmony_ci    }
287cb93a386Sopenharmony_ci    return builder.make();
288cb93a386Sopenharmony_ci}
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextBlobIntegerOverflowTest, reporter, ctxInfo) {
291cb93a386Sopenharmony_ci    auto dContext = ctxInfo.directContext();
292cb93a386Sopenharmony_ci    const SkImageInfo info =
293cb93a386Sopenharmony_ci            SkImageInfo::Make(kScreenDim, kScreenDim, kN32_SkColorType, kPremul_SkAlphaType);
294cb93a386Sopenharmony_ci    auto surface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info);
295cb93a386Sopenharmony_ci
296cb93a386Sopenharmony_ci    auto blob = make_large_blob();
297cb93a386Sopenharmony_ci    int y = 40;
298cb93a386Sopenharmony_ci    SkBitmap base = draw_blob(blob.get(), surface.get(), {40, y + 0.0f});
299cb93a386Sopenharmony_ci}
300cb93a386Sopenharmony_ci#endif
301cb93a386Sopenharmony_ci
302cb93a386Sopenharmony_cistatic const bool kDumpPngs = true;
303cb93a386Sopenharmony_ci// dump pngs needs a "good" and a "bad" directory to put the results in. This allows the use of the
304cb93a386Sopenharmony_ci// skdiff tool to visualize the differences.
305cb93a386Sopenharmony_ci
306cb93a386Sopenharmony_civoid write_png(const std::string& filename, const SkBitmap& bitmap) {
307cb93a386Sopenharmony_ci    auto data = SkEncodeBitmap(bitmap, SkEncodedImageFormat::kPNG, 0);
308cb93a386Sopenharmony_ci    SkFILEWStream w{filename.c_str()};
309cb93a386Sopenharmony_ci    w.write(data->data(), data->size());
310cb93a386Sopenharmony_ci    w.fsync();
311cb93a386Sopenharmony_ci}
312cb93a386Sopenharmony_ci
313cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextBlobJaggedGlyph, reporter, ctxInfo) {
314cb93a386Sopenharmony_ci    auto direct = ctxInfo.directContext();
315cb93a386Sopenharmony_ci    const SkImageInfo info =
316cb93a386Sopenharmony_ci            SkImageInfo::Make(kScreenDim, kScreenDim, kN32_SkColorType, kPremul_SkAlphaType);
317cb93a386Sopenharmony_ci    auto surface = SkSurface::MakeRenderTarget(direct, SkBudgeted::kNo, info);
318cb93a386Sopenharmony_ci
319cb93a386Sopenharmony_ci    auto blob = make_blob();
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_ci    for (int y = 40; y < kScreenDim - 40; y++) {
322cb93a386Sopenharmony_ci        SkBitmap base = draw_blob(blob.get(), surface.get(), {40, y + 0.0f});
323cb93a386Sopenharmony_ci        SkBitmap half = draw_blob(blob.get(), surface.get(), {40, y + 0.5f});
324cb93a386Sopenharmony_ci        SkBitmap unit = draw_blob(blob.get(), surface.get(), {40, y + 1.0f});
325cb93a386Sopenharmony_ci        bool isOk = compare_bitmaps(base, half) || compare_bitmaps(unit, half);
326cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, isOk);
327cb93a386Sopenharmony_ci        if (!isOk) {
328cb93a386Sopenharmony_ci            if (kDumpPngs) {
329cb93a386Sopenharmony_ci                {
330cb93a386Sopenharmony_ci                    std::string filename = "bad/half-y" + std::to_string(y) + ".png";
331cb93a386Sopenharmony_ci                    write_png(filename, half);
332cb93a386Sopenharmony_ci                }
333cb93a386Sopenharmony_ci                {
334cb93a386Sopenharmony_ci                    std::string filename = "good/half-y" + std::to_string(y) + ".png";
335cb93a386Sopenharmony_ci                    write_png(filename, base);
336cb93a386Sopenharmony_ci                }
337cb93a386Sopenharmony_ci            }
338cb93a386Sopenharmony_ci            break;
339cb93a386Sopenharmony_ci        }
340cb93a386Sopenharmony_ci    }
341cb93a386Sopenharmony_ci
342cb93a386Sopenharmony_ci    // Testing the x direction across all platforms does not workout, because letter spacing can
343cb93a386Sopenharmony_ci    // change based on non-integer advance widths, but this has been useful for diagnosing problems.
344cb93a386Sopenharmony_ci#if 0
345cb93a386Sopenharmony_ci    blob = make_blob();
346cb93a386Sopenharmony_ci    for (int x = 40; x < kScreenDim - 40; x++) {
347cb93a386Sopenharmony_ci        SkBitmap base = draw_blob(blob.get(), surface.get(), {x + 0.0f, 40});
348cb93a386Sopenharmony_ci        SkBitmap half = draw_blob(blob.get(), surface.get(), {x + 0.5f, 40});
349cb93a386Sopenharmony_ci        SkBitmap unit = draw_blob(blob.get(), surface.get(), {x + 1.0f, 40});
350cb93a386Sopenharmony_ci        bool isOk = compare_bitmaps(base, half) || compare_bitmaps(unit, half);
351cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, isOk);
352cb93a386Sopenharmony_ci        if (!isOk) {
353cb93a386Sopenharmony_ci            if (kDumpPngs) {
354cb93a386Sopenharmony_ci                {
355cb93a386Sopenharmony_ci                    std::string filename = "bad/half-x" + std::to_string(x) + ".png";
356cb93a386Sopenharmony_ci                    write_png(filename, half);
357cb93a386Sopenharmony_ci                }
358cb93a386Sopenharmony_ci                {
359cb93a386Sopenharmony_ci                    std::string filename = "good/half-x" + std::to_string(x) + ".png";
360cb93a386Sopenharmony_ci                    write_png(filename, base);
361cb93a386Sopenharmony_ci                }
362cb93a386Sopenharmony_ci            }
363cb93a386Sopenharmony_ci            break;
364cb93a386Sopenharmony_ci        }
365cb93a386Sopenharmony_ci    }
366cb93a386Sopenharmony_ci#endif
367cb93a386Sopenharmony_ci}
368cb93a386Sopenharmony_ci
369cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextBlobSmoothScroll, reporter, ctxInfo) {
370cb93a386Sopenharmony_ci    auto direct = ctxInfo.directContext();
371cb93a386Sopenharmony_ci    const SkImageInfo info =
372cb93a386Sopenharmony_ci            SkImageInfo::Make(kScreenDim, kScreenDim, kN32_SkColorType, kPremul_SkAlphaType);
373cb93a386Sopenharmony_ci    auto surface = SkSurface::MakeRenderTarget(direct, SkBudgeted::kNo, info);
374cb93a386Sopenharmony_ci
375cb93a386Sopenharmony_ci    auto movingBlob = make_blob();
376cb93a386Sopenharmony_ci
377cb93a386Sopenharmony_ci    for (SkScalar y = 40; y < 50; y += 1.0/8.0) {
378cb93a386Sopenharmony_ci        auto expectedBlob = make_blob();
379cb93a386Sopenharmony_ci        auto expectedBitMap = draw_blob(expectedBlob.get(), surface.get(), {40, y});
380cb93a386Sopenharmony_ci        auto movingBitmap = draw_blob(movingBlob.get(), surface.get(), {40, y});
381cb93a386Sopenharmony_ci        bool isOk = compare_bitmaps(expectedBitMap, movingBitmap);
382cb93a386Sopenharmony_ci        REPORTER_ASSERT(reporter, isOk);
383cb93a386Sopenharmony_ci        if (!isOk) {
384cb93a386Sopenharmony_ci            if (kDumpPngs) {
385cb93a386Sopenharmony_ci                {
386cb93a386Sopenharmony_ci                    std::string filename = "bad/scroll-y" + std::to_string(y) + ".png";
387cb93a386Sopenharmony_ci                    write_png(filename, movingBitmap);
388cb93a386Sopenharmony_ci                }
389cb93a386Sopenharmony_ci                {
390cb93a386Sopenharmony_ci                    std::string filename = "good/scroll-y" + std::to_string(y) + ".png";
391cb93a386Sopenharmony_ci                    write_png(filename, expectedBitMap);
392cb93a386Sopenharmony_ci                }
393cb93a386Sopenharmony_ci            }
394cb93a386Sopenharmony_ci            break;
395cb93a386Sopenharmony_ci        }
396cb93a386Sopenharmony_ci    }
397cb93a386Sopenharmony_ci}
398