1 /*
2  * Copyright 2014 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 "SkCanvas.h"
11 #include "SkFont.h"
12 #include "SkTypeface.h"
13 
14 /* This test tries to define the effect of using hairline strokes on text.
15  * Provides non-hairline images for reference and consistency checks.
16  * glyph_pos_(h/n)_(s/f/b)
17  *   -> test hairline/non-hairline stroke/fill/stroke+fill.
18  */
19 constexpr SkScalar kTextHeight = 14.0f;
20 constexpr char kText[] = "Proportional Hamburgefons #% fi";
21 
22 static void drawTestCase(SkCanvas* canvas,
23                          SkScalar textScale,
24                          SkScalar strokeWidth,
25                          SkPaint::Style strokeStyle);
26 
draw_gm(SkCanvas * canvas,SkScalar strokeWidth,SkPaint::Style strokeStyle)27 static void draw_gm(SkCanvas* canvas,
28                     SkScalar strokeWidth,
29                     SkPaint::Style strokeStyle) {
30     // There's a black pixel at 40, 40 for reference.
31     canvas->drawPoint(40, 40, SkPaint());
32 
33     // Two reference images.
34     canvas->translate(50.0f, 50.0f);
35     drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
36 
37     canvas->translate(0.0f, 50.0f);
38     drawTestCase(canvas, 3.0f, strokeWidth, strokeStyle);
39 
40     // Uniform scaling test.
41     canvas->translate(0.0f, 100.0f);
42     canvas->save();
43     canvas->scale(3.0f, 3.0f);
44     drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
45     canvas->restore();
46 
47     // Non-uniform scaling test.
48     canvas->translate(0.0f, 100.0f);
49     canvas->save();
50     canvas->scale(3.0f, 6.0f);
51     drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
52     canvas->restore();
53 
54     // Skew test.
55     canvas->translate(0.0f, 80.0f);
56     canvas->save();
57     canvas->scale(3.0f, 3.0f);
58     SkMatrix skew;
59     skew.setIdentity();
60     skew.setSkewX(8.0f / 25.0f);
61     skew.setSkewY(2.0f / 25.0f);
62     canvas->concat(skew);
63     drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
64     canvas->restore();
65 
66     // Perspective test.
67     canvas->translate(0.0f, 80.0f);
68     canvas->save();
69     SkMatrix perspective;
70     perspective.setIdentity();
71     perspective.setPerspX(-SkScalarInvert(340));
72     perspective.setSkewX(8.0f / 25.0f);
73     perspective.setSkewY(2.0f / 25.0f);
74 
75     canvas->concat(perspective);
76     drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
77     canvas->restore();
78 }
79 
drawTestCase(SkCanvas * canvas,SkScalar textScale,SkScalar strokeWidth,SkPaint::Style strokeStyle)80 static void drawTestCase(SkCanvas* canvas,
81                          SkScalar textScale,
82                          SkScalar strokeWidth,
83                          SkPaint::Style strokeStyle) {
84     SkPaint paint;
85     paint.setColor(SK_ColorBLACK);
86     paint.setAntiAlias(true);
87     paint.setStrokeWidth(strokeWidth);
88     paint.setStyle(strokeStyle);
89 
90     SkFont font(sk_tool_utils::create_portable_typeface(), kTextHeight * textScale);
91 
92     // This demonstrates that we can not measure the text if
93     // there's a device transform. The canvas total matrix will
94     // end up being a device transform.
95     bool drawRef = !(canvas->getTotalMatrix().getType() &
96                      ~(SkMatrix::kIdentity_Mask | SkMatrix::kTranslate_Mask));
97 
98     SkRect bounds;
99     if (drawRef) {
100         SkScalar advance = font.measureText(kText, sizeof(kText) - 1, kUTF8_SkTextEncoding,
101                                             &bounds, &paint);
102 
103         paint.setStrokeWidth(0.0f);
104         paint.setStyle(SkPaint::kStroke_Style);
105 
106         // Green box is the measured text bounds.
107         paint.setColor(SK_ColorGREEN);
108         canvas->drawRect(bounds, paint);
109 
110         // Red line is the measured advance from the 0,0 of the text position.
111         paint.setColor(SK_ColorRED);
112         canvas->drawLine(0.0f, 0.0f, advance, 0.0f, paint);
113     }
114 
115     // Black text is the testcase, eg. the text.
116     paint.setColor(SK_ColorBLACK);
117     paint.setStrokeWidth(strokeWidth);
118     paint.setStyle(strokeStyle);
119     canvas->drawSimpleText(kText, sizeof(kText) - 1, kUTF8_SkTextEncoding, 0.0f, 0.0f, font, paint);
120 
121     if (drawRef) {
122         const size_t len = sizeof(kText) - 1;
123         SkGlyphID glyphs[len];
124         const int count = font.textToGlyphs(kText, len, kUTF8_SkTextEncoding, glyphs, len);
125         SkScalar widths[len]; // len is conservative. we really only need 'count'
126         font.getWidthsBounds(glyphs, count, widths, nullptr, &paint);
127 
128         paint.setStrokeWidth(0.0f);
129         paint.setStyle(SkPaint::kStroke_Style);
130 
131         // Magenta lines are the positions for the characters.
132         paint.setColor(SK_ColorMAGENTA);
133         SkScalar w = bounds.x();
134         for (size_t i = 0; i < sizeof(kText) - 1; ++i) {
135             canvas->drawLine(w, 0.0f, w, 5.0f, paint);
136             w += widths[i];
137         }
138     }
139 }
140 
141 DEF_SIMPLE_GM(glyph_pos_h_b, c, 800, 600) {
142     draw_gm(c, 0.0f, SkPaint::kStrokeAndFill_Style);
143 }
144 DEF_SIMPLE_GM(glyph_pos_n_b, c, 800, 600) {
145     draw_gm(c, 1.2f, SkPaint::kStrokeAndFill_Style);
146 }
147 DEF_SIMPLE_GM(glyph_pos_h_s, c, 800, 600) {
148     draw_gm(c, 0.0f, SkPaint::kStroke_Style);
149 }
150 DEF_SIMPLE_GM(glyph_pos_n_s, c, 800, 600) {
151     draw_gm(c, 1.2f, SkPaint::kStroke_Style);
152 }
153 DEF_SIMPLE_GM(glyph_pos_h_f, c, 800, 600) {
154     draw_gm(c, 0.0f, SkPaint::kFill_Style);
155 }
156 DEF_SIMPLE_GM(glyph_pos_n_f, c, 800, 600) {
157     draw_gm(c, 1.2f, SkPaint::kFill_Style);
158 }
159