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