1/*
2 * Copyright 2012 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 "SkPaint.h"
9#include "SkPath.h"
10#include "SkRect.h"
11#include "SkStroke.h"
12#include "SkStrokeRec.h"
13#include "Test.h"
14
15static bool equal(const SkRect& a, const SkRect& b) {
16    return  SkScalarNearlyEqual(a.left(), b.left()) &&
17            SkScalarNearlyEqual(a.top(), b.top()) &&
18            SkScalarNearlyEqual(a.right(), b.right()) &&
19            SkScalarNearlyEqual(a.bottom(), b.bottom());
20}
21
22static void test_strokecubic(skiatest::Reporter* reporter) {
23    uint32_t hexCubicVals[] = {
24        0x424c1086, 0x44bcf0cb,  // fX=51.0161362 fY=1511.52478
25        0x424c107c, 0x44bcf0cb,  // fX=51.0160980 fY=1511.52478
26        0x424c10c2, 0x44bcf0cb,  // fX=51.0163651 fY=1511.52478
27        0x424c1119, 0x44bcf0ca,  // fX=51.0166969 fY=1511.52466
28    };
29    SkPoint cubicVals[] = {
30        {51.0161362f, 1511.52478f },
31        {51.0160980f, 1511.52478f },
32        {51.0163651f, 1511.52478f },
33        {51.0166969f, 1511.52466f },
34    };
35    SkPaint paint;
36
37    paint.setStyle(SkPaint::kStroke_Style);
38    paint.setStrokeWidth(0.394537568f);
39    SkPath path, fillPath;
40    path.moveTo(cubicVals[0]);
41    path.cubicTo(cubicVals[1], cubicVals[2], cubicVals[3]);
42    paint.getFillPath(path, &fillPath);
43    path.reset();
44    path.moveTo(SkBits2Float(hexCubicVals[0]), SkBits2Float(hexCubicVals[1]));
45    path.cubicTo(SkBits2Float(hexCubicVals[2]), SkBits2Float(hexCubicVals[3]),
46            SkBits2Float(hexCubicVals[4]), SkBits2Float(hexCubicVals[5]),
47            SkBits2Float(hexCubicVals[6]), SkBits2Float(hexCubicVals[7]));
48    paint.getFillPath(path, &fillPath);
49}
50
51static void test_strokerect(skiatest::Reporter* reporter) {
52    const SkScalar width = SkIntToScalar(10);
53    SkPaint paint;
54
55    paint.setStyle(SkPaint::kStroke_Style);
56    paint.setStrokeWidth(width);
57
58    SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(100) };
59
60    SkRect outer(r);
61    outer.outset(width/2, width/2);
62
63    static const SkPaint::Join joins[] = {
64        SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
65    };
66
67    for (size_t i = 0; i < SK_ARRAY_COUNT(joins); ++i) {
68        paint.setStrokeJoin(joins[i]);
69
70        SkPath path, fillPath;
71        path.addRect(r);
72        paint.getFillPath(path, &fillPath);
73
74        REPORTER_ASSERT(reporter, equal(outer, fillPath.getBounds()));
75
76        bool isMiter = SkPaint::kMiter_Join == joins[i];
77        SkRect nested[2];
78        REPORTER_ASSERT(reporter, fillPath.isNestedFillRects(nested) == isMiter);
79        if (isMiter) {
80            SkRect inner(r);
81            inner.inset(width/2, width/2);
82            REPORTER_ASSERT(reporter, equal(nested[0], outer));
83            REPORTER_ASSERT(reporter, equal(nested[1], inner));
84        }
85    }
86}
87
88static void test_strokerec_equality(skiatest::Reporter* reporter) {
89    {
90        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
91        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
92        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
93
94        // Test that style mismatch is detected.
95        s2.setHairlineStyle();
96        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
97
98        s1.setHairlineStyle();
99        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
100
101        // ResScale is not part of equality.
102        s1.setResScale(2.1f);
103        s2.setResScale(1.2f);
104        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
105        s1.setFillStyle();
106        s2.setFillStyle();
107        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
108        s1.setStrokeStyle(1.0f, false);
109        s2.setStrokeStyle(1.0f, false);
110        s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
111        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
112        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
113    }
114
115    // Stroke parameters on fill or hairline style are not part of equality.
116    {
117        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
118        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
119        for (int i = 0; i < 2; ++i) {
120            s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
121            s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
122            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
123            s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
124            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
125            s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
126            REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
127            s1.setHairlineStyle();
128            s2.setHairlineStyle();
129        }
130    }
131
132    // Stroke parameters on stroke style are part of equality.
133    {
134        SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
135        SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
136        s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
137        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
138        s1.setStrokeStyle(1.0f, false);
139
140        s2.setStrokeStyle(1.0f, true);
141        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
142
143        s2.setStrokeStyle(2.1f, false);
144        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
145
146        s2.setStrokeStyle(1.0f, false);
147        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
148
149        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
150        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
151        s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
152        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
153        s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
154        REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
155
156        // Sets fill.
157        s1.setStrokeStyle(0.0f, true);
158        s2.setStrokeStyle(0.0f, true);
159        REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
160    }
161}
162
163DEF_TEST(Stroke, reporter) {
164    test_strokecubic(reporter);
165    test_strokerect(reporter);
166    test_strokerec_equality(reporter);
167}
168