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