1/*
2 * Copyright 2011 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 "SkBitmap.h"
9#include "SkCanvas.h"
10#include "SkColor.h"
11#include "SkPaint.h"
12#include "SkPoint.h"
13#include "SkRect.h"
14#include "SkSurface.h"
15#include "SkTypes.h"
16#include "Test.h"
17#include <math.h>
18
19static const SkColor bgColor = SK_ColorWHITE;
20
21static void create(SkBitmap* bm, SkIRect bound) {
22    bm->allocN32Pixels(bound.width(), bound.height());
23}
24
25static void drawBG(SkCanvas* canvas) {
26    canvas->drawColor(bgColor);
27}
28
29/** Assumes that the ref draw was completely inside ref canvas --
30    implies that everything outside is "bgColor".
31    Checks that all overlap is the same and that all non-overlap on the
32    ref is "bgColor".
33 */
34static bool compare(const SkBitmap& ref, const SkIRect& iref,
35                    const SkBitmap& test, const SkIRect& itest)
36{
37    const int xOff = itest.fLeft - iref.fLeft;
38    const int yOff = itest.fTop - iref.fTop;
39
40    for (int y = 0; y < test.height(); ++y) {
41        for (int x = 0; x < test.width(); ++x) {
42            SkColor testColor = test.getColor(x, y);
43            int refX = x + xOff;
44            int refY = y + yOff;
45            SkColor refColor;
46            if (refX >= 0 && refX < ref.width() &&
47                refY >= 0 && refY < ref.height())
48            {
49                refColor = ref.getColor(refX, refY);
50            } else {
51                refColor = bgColor;
52            }
53            if (refColor != testColor) {
54                return false;
55            }
56        }
57    }
58    return true;
59}
60
61DEF_TEST(DrawText, reporter) {
62    SkPaint paint;
63    paint.setColor(SK_ColorGRAY);
64    paint.setTextSize(SkIntToScalar(20));
65
66    SkIRect drawTextRect = SkIRect::MakeWH(64, 64);
67    SkBitmap drawTextBitmap;
68    create(&drawTextBitmap, drawTextRect);
69    SkCanvas drawTextCanvas(drawTextBitmap);
70
71    SkIRect drawPosTextRect = SkIRect::MakeWH(64, 64);
72    SkBitmap drawPosTextBitmap;
73    create(&drawPosTextBitmap, drawPosTextRect);
74    SkCanvas drawPosTextCanvas(drawPosTextBitmap);
75
76    // Two test cases "A" for the normal path through the code, and " " to check the
77    // early return path.
78    const char* cases[] = {"A", " "};
79    for (auto c : cases) {
80        for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) {
81            for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) {
82                SkPoint point = SkPoint::Make(25.0f + offsetX,
83                                              25.0f + offsetY);
84
85                for (int align = 0; align < SkPaint::kAlignCount; ++align) {
86                    paint.setTextAlign(static_cast<SkPaint::Align>(align));
87
88                    for (unsigned int flags = 0; flags < (1 << 3); ++flags) {
89                        static const unsigned int antiAliasFlag = 1;
90                        static const unsigned int subpixelFlag = 1 << 1;
91                        static const unsigned int lcdFlag = 1 << 2;
92
93                        paint.setAntiAlias(SkToBool(flags & antiAliasFlag));
94                        paint.setSubpixelText(SkToBool(flags & subpixelFlag));
95                        paint.setLCDRenderText(SkToBool(flags & lcdFlag));
96
97                        // Test: drawText and drawPosText draw the same.
98                        drawBG(&drawTextCanvas);
99                        drawTextCanvas.drawText(c, 1, point.fX, point.fY, paint);
100
101                        drawBG(&drawPosTextCanvas);
102                        drawPosTextCanvas.drawPosText(c, 1, &point, paint);
103
104                        REPORTER_ASSERT(reporter,
105                                        compare(drawTextBitmap, drawTextRect,
106                                                drawPosTextBitmap, drawPosTextRect));
107                    }
108                }
109            }
110        }
111    }
112}
113
114// Test drawing text at some unusual coordinates.
115// We measure success by not crashing or asserting.
116DEF_TEST(DrawText_weirdCoordinates, r) {
117    auto surface = SkSurface::MakeRasterN32Premul(10,10);
118    auto canvas = surface->getCanvas();
119
120    SkScalar oddballs[] = { 0.0f, (float)INFINITY, (float)NAN, 34359738368.0f };
121
122    for (auto x : oddballs) {
123        canvas->drawString("a", +x, 0.0f, SkPaint());
124        canvas->drawString("a", -x, 0.0f, SkPaint());
125    }
126    for (auto y : oddballs) {
127        canvas->drawString("a", 0.0f, +y, SkPaint());
128        canvas->drawString("a", 0.0f, -y, SkPaint());
129    }
130}
131
132// Test drawing text with some unusual matricies.
133// We measure success by not crashing or asserting.
134DEF_TEST(DrawText_weirdMatricies, r) {
135    auto surface = SkSurface::MakeRasterN32Premul(100,100);
136    auto canvas = surface->getCanvas();
137
138    SkPaint paint;
139    paint.setAntiAlias(true);
140    paint.setLCDRenderText(true);
141
142    struct {
143        SkScalar textSize;
144        SkScalar matrix[9];
145    } testCases[] = {
146        // 2x2 singular
147        {10, { 0,  0,  0,  0,  0,  0,  0,  0,  1}},
148        {10, { 0,  0,  0,  0,  1,  0,  0,  0,  1}},
149        {10, { 0,  0,  0,  1,  0,  0,  0,  0,  1}},
150        {10, { 0,  0,  0,  1,  1,  0,  0,  0,  1}},
151        {10, { 0,  1,  0,  0,  1,  0,  0,  0,  1}},
152        {10, { 1,  0,  0,  0,  0,  0,  0,  0,  1}},
153        {10, { 1,  0,  0,  1,  0,  0,  0,  0,  1}},
154        {10, { 1,  1,  0,  0,  0,  0,  0,  0,  1}},
155        {10, { 1,  1,  0,  1,  1,  0,  0,  0,  1}},
156        // See https://bugzilla.mozilla.org/show_bug.cgi?id=1305085 .
157        { 1, {10, 20,  0, 20, 40,  0,  0,  0,  1}},
158    };
159
160    for (const auto& testCase : testCases) {
161        paint.setTextSize(testCase.textSize);
162        const SkScalar(&m)[9] = testCase.matrix;
163        SkMatrix mat;
164        mat.setAll(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
165        canvas->setMatrix(mat);
166        canvas->drawString("Hamburgefons", 10, 10, paint);
167    }
168}
169