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 "gm.h"
9#include "Resources.h"
10#include "SkCanvas.h"
11#include "SkSurface.h"
12#include "SkTextBlob.h"
13#include "SkTypeface.h"
14#include "sk_tool_utils.h"
15
16/**
17 * This GM tests reusing the same text blobs with distance fields rendering using various
18 * combinations of perspective and non-perspetive matrices, scissor clips, and different x,y params
19 * passed to the draw.
20 */
21class DFTextBlobPerspGM : public skiagm::GM {
22public:
23    DFTextBlobPerspGM() { this->setBGColor(0xFFFFFFFF); }
24
25protected:
26    SkString onShortName() override {
27        SkString name("dftext_blob_persp");
28        name.append(sk_tool_utils::platform_font_manager());
29        return name;
30    }
31
32    SkISize onISize() override { return SkISize::Make(900, 350); }
33
34    void onOnceBeforeDraw() override {
35        for (int i = 0; i < 3; ++i) {
36            SkPaint paint;
37            paint.setTextSize(32);
38            paint.setAntiAlias(i > 0);
39            paint.setLCDRenderText(i > 1);
40            paint.setSubpixelText(true);
41            SkTextBlobBuilder builder;
42            sk_tool_utils::add_to_text_blob(&builder, "SkiaText", paint, 0, 0);
43            fBlobs.emplace_back(builder.make());
44        }
45    }
46
47    virtual void onDraw(SkCanvas* inputCanvas) override {
48    // set up offscreen rendering with distance field text
49#if SK_SUPPORT_GPU
50        GrContext* ctx = inputCanvas->getGrContext();
51        SkISize size = this->onISize();
52        if (!inputCanvas->getBaseLayerSize().isEmpty()) {
53            size = inputCanvas->getBaseLayerSize();
54        }
55        SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType,
56                                                inputCanvas->imageInfo().refColorSpace());
57        SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
58                             SkSurfaceProps::kLegacyFontHost_InitType);
59        auto surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props);
60        SkCanvas* canvas = surface ? surface->getCanvas() : inputCanvas;
61        // init our new canvas with the old canvas's matrix
62        canvas->setMatrix(inputCanvas->getTotalMatrix());
63#else
64        SkCanvas* canvas = inputCanvas;
65#endif
66        SkScalar x = 0, y = 0;
67        SkScalar maxH = 0;
68        for (auto twm : {TranslateWithMatrix::kNo, TranslateWithMatrix::kYes}) {
69            for (auto pm : {PerspMode::kNone, PerspMode::kX, PerspMode::kY, PerspMode::kXY}) {
70                for (auto& blob : fBlobs) {
71                    for (bool clip : {false, true}) {
72                        canvas->save();
73                        SkScalar w = blob->bounds().width();
74                        SkScalar h = blob->bounds().height();
75                        if (clip) {
76                            auto rect =
77                                    SkRect::MakeXYWH(x + 5, y + 5, w * 3.f / 4.f, h * 3.f / 4.f);
78                            canvas->clipRect(rect, false);
79                        }
80                        this->drawBlob(canvas, blob.get(), SK_ColorBLACK, x, y + h, pm, twm);
81                        x += w + 20.f;
82                        maxH = SkTMax(h, maxH);
83                        canvas->restore();
84                    }
85                }
86                x = 0;
87                y += maxH + 20.f;
88                maxH = 0;
89            }
90        }
91#if SK_SUPPORT_GPU
92        // render offscreen buffer
93        if (surface) {
94            SkAutoCanvasRestore acr(inputCanvas, true);
95            // since we prepended this matrix already, we blit using identity
96            inputCanvas->resetMatrix();
97            inputCanvas->drawImage(surface->makeImageSnapshot().get(), 0, 0, nullptr);
98        }
99#endif
100    }
101
102private:
103    enum class PerspMode { kNone, kX, kY, kXY };
104
105    enum class TranslateWithMatrix : bool { kNo, kYes };
106
107    void drawBlob(SkCanvas* canvas, SkTextBlob* blob, SkColor color, SkScalar x, SkScalar y,
108                  PerspMode perspMode, TranslateWithMatrix translateWithMatrix) {
109        canvas->save();
110        SkMatrix persp = SkMatrix::I();
111        switch (perspMode) {
112            case PerspMode::kNone:
113                break;
114            case PerspMode::kX:
115                persp.setPerspX(0.005f);
116                break;
117            case PerspMode::kY:
118                persp.setPerspY(00.005f);
119                break;
120            case PerspMode::kXY:
121                persp.setPerspX(-0.001f);
122                persp.setPerspY(-0.0015f);
123                break;
124        }
125        persp = SkMatrix::Concat(persp, SkMatrix::MakeTrans(-x, -y));
126        persp = SkMatrix::Concat(SkMatrix::MakeTrans(x, y), persp);
127        canvas->concat(persp);
128        if (TranslateWithMatrix::kYes == translateWithMatrix) {
129            canvas->translate(x, y);
130            x = 0;
131            y = 0;
132        }
133        SkPaint paint;
134        paint.setColor(color);
135        canvas->drawTextBlob(blob, x, y, paint);
136        canvas->restore();
137    }
138
139    SkTArray<sk_sp<SkTextBlob>, true> fBlobs;
140    typedef skiagm::GM INHERITED;
141};
142
143DEF_GM(return new DFTextBlobPerspGM;)
144