• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Test.h"
9 #include "TestClassDef.h"
10 #include "SkBlurMask.h"
11 #include "SkBlurMaskFilter.h"
12 #include "SkLayerDrawLooper.h"
13 #include "SkPath.h"
14 #include "SkPaint.h"
15 #include "SkRandom.h"
16 #include "SkTypeface.h"
17 #include "SkUtils.h"
18 
uni_to_utf8(const SkUnichar src[],void * dst,int count)19 static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) {
20     char* u8 = (char*)dst;
21     for (int i = 0; i < count; ++i) {
22         int n = SkUTF8_FromUnichar(src[i], u8);
23         u8 += n;
24     }
25     return u8 - (char*)dst;
26 }
27 
uni_to_utf16(const SkUnichar src[],void * dst,int count)28 static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) {
29     uint16_t* u16 = (uint16_t*)dst;
30     for (int i = 0; i < count; ++i) {
31         int n = SkUTF16_FromUnichar(src[i], u16);
32         u16 += n;
33     }
34     return (char*)u16 - (char*)dst;
35 }
36 
uni_to_utf32(const SkUnichar src[],void * dst,int count)37 static size_t uni_to_utf32(const SkUnichar src[], void* dst, int count) {
38     SkUnichar* u32 = (SkUnichar*)dst;
39     if (src != u32) {
40         memcpy(u32, src, count * sizeof(SkUnichar));
41     }
42     return count * sizeof(SkUnichar);
43 }
44 
paint2encoding(const SkPaint & paint)45 static SkTypeface::Encoding paint2encoding(const SkPaint& paint) {
46     SkPaint::TextEncoding enc = paint.getTextEncoding();
47     SkASSERT(SkPaint::kGlyphID_TextEncoding != enc);
48     return (SkTypeface::Encoding)enc;
49 }
50 
find_first_zero(const uint16_t glyphs[],int count)51 static int find_first_zero(const uint16_t glyphs[], int count) {
52     for (int i = 0; i < count; ++i) {
53         if (0 == glyphs[i]) {
54             return i;
55         }
56     }
57     return count;
58 }
59 
test_cmap(skiatest::Reporter * reporter)60 static void test_cmap(skiatest::Reporter* reporter) {
61     static const int NGLYPHS = 64;
62 
63     SkUnichar src[NGLYPHS];
64     SkUnichar dst[NGLYPHS]; // used for utf8, utf16, utf32 storage
65 
66     static const struct {
67         size_t (*fSeedTextProc)(const SkUnichar[], void* dst, int count);
68         SkPaint::TextEncoding   fEncoding;
69     } gRec[] = {
70         { uni_to_utf8,  SkPaint::kUTF8_TextEncoding },
71         { uni_to_utf16, SkPaint::kUTF16_TextEncoding },
72         { uni_to_utf32, SkPaint::kUTF32_TextEncoding },
73     };
74 
75     SkRandom rand;
76     SkPaint paint;
77     paint.setTypeface(SkTypeface::RefDefault())->unref();
78     SkTypeface* face = paint.getTypeface();
79 
80     for (int i = 0; i < 1000; ++i) {
81         // generate some random text
82         for (int j = 0; j < NGLYPHS; ++j) {
83             src[j] = ' ' + j;
84         }
85         // inject some random chars, to sometimes abort early
86         src[rand.nextU() & 63] = rand.nextU() & 0xFFF;
87 
88         for (size_t k = 0; k < SK_ARRAY_COUNT(gRec); ++k) {
89             paint.setTextEncoding(gRec[k].fEncoding);
90 
91             size_t len = gRec[k].fSeedTextProc(src, dst, NGLYPHS);
92 
93             uint16_t    glyphs0[NGLYPHS], glyphs1[NGLYPHS];
94 
95             bool contains = paint.containsText(dst, len);
96             int nglyphs = paint.textToGlyphs(dst, len, glyphs0);
97             int first = face->charsToGlyphs(dst, paint2encoding(paint), glyphs1, NGLYPHS);
98             int index = find_first_zero(glyphs1, NGLYPHS);
99 
100             REPORTER_ASSERT(reporter, NGLYPHS == nglyphs);
101             REPORTER_ASSERT(reporter, index == first);
102             REPORTER_ASSERT(reporter,
103                         !memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t)));
104             if (contains) {
105                 REPORTER_ASSERT(reporter, NGLYPHS == first);
106             } else {
107                 REPORTER_ASSERT(reporter, NGLYPHS > first);
108             }
109         }
110     }
111 }
112 
113 // temparary api for bicubic, just be sure we can set/clear it
test_filterlevel(skiatest::Reporter * reporter)114 static void test_filterlevel(skiatest::Reporter* reporter) {
115     SkPaint p0, p1;
116 
117     REPORTER_ASSERT(reporter,
118                     SkPaint::kNone_FilterLevel == p0.getFilterLevel());
119 
120     static const SkPaint::FilterLevel gLevels[] = {
121         SkPaint::kNone_FilterLevel,
122         SkPaint::kLow_FilterLevel,
123         SkPaint::kMedium_FilterLevel,
124         SkPaint::kHigh_FilterLevel
125     };
126     for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) {
127         p0.setFilterLevel(gLevels[i]);
128         REPORTER_ASSERT(reporter, gLevels[i] == p0.getFilterLevel());
129         p1 = p0;
130         REPORTER_ASSERT(reporter, gLevels[i] == p1.getFilterLevel());
131 
132         p0.reset();
133         REPORTER_ASSERT(reporter,
134                         SkPaint::kNone_FilterLevel == p0.getFilterLevel());
135     }
136 }
137 
test_copy(skiatest::Reporter * reporter)138 static void test_copy(skiatest::Reporter* reporter) {
139     SkPaint paint;
140     // set a few member variables
141     paint.setStyle(SkPaint::kStrokeAndFill_Style);
142     paint.setTextAlign(SkPaint::kLeft_Align);
143     paint.setStrokeWidth(SkIntToScalar(2));
144     // set a few pointers
145     SkLayerDrawLooper* looper = new SkLayerDrawLooper();
146     paint.setLooper(looper)->unref();
147     SkMaskFilter* mask = SkBlurMaskFilter::Create(SkBlurMaskFilter::kNormal_BlurStyle,
148                                       SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(1)));
149     paint.setMaskFilter(mask)->unref();
150 
151     // copy the paint using the copy constructor and check they are the same
152     SkPaint copiedPaint = paint;
153     REPORTER_ASSERT(reporter, paint == copiedPaint);
154 
155 #ifdef SK_BUILD_FOR_ANDROID
156     // the copy constructor should preserve the Generation ID
157     uint32_t paintGenID = paint.getGenerationID();
158     uint32_t copiedPaintGenID = copiedPaint.getGenerationID();
159     REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID);
160     REPORTER_ASSERT(reporter, !memcmp(&paint, &copiedPaint, sizeof(paint)));
161 #endif
162 
163     // copy the paint using the equal operator and check they are the same
164     copiedPaint = paint;
165     REPORTER_ASSERT(reporter, paint == copiedPaint);
166 
167 #ifdef SK_BUILD_FOR_ANDROID
168     // the equals operator should increment the Generation ID
169     REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID);
170     REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
171     copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value
172     REPORTER_ASSERT(reporter, memcmp(&paint, &copiedPaint, sizeof(paint)));
173 #endif
174 
175     // clean the paint and check they are back to their initial states
176     SkPaint cleanPaint;
177     paint.reset();
178     copiedPaint.reset();
179     REPORTER_ASSERT(reporter, cleanPaint == paint);
180     REPORTER_ASSERT(reporter, cleanPaint == copiedPaint);
181 
182 #ifdef SK_BUILD_FOR_ANDROID
183     // the reset function should increment the Generation ID
184     REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID);
185     REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
186     REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &paint, sizeof(cleanPaint)));
187     REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &copiedPaint, sizeof(cleanPaint)));
188 #endif
189 }
190 
191 // found and fixed for webkit: mishandling when we hit recursion limit on
192 // mostly degenerate cubic flatness test
regression_cubic(skiatest::Reporter * reporter)193 static void regression_cubic(skiatest::Reporter* reporter) {
194     SkPath path, stroke;
195     SkPaint paint;
196 
197     path.moveTo(460.2881309415525f,
198                 303.250847066498f);
199     path.cubicTo(463.36378422175284f,
200                  302.1169735073363f,
201                  456.32239330810046f,
202                  304.720354932878f,
203                  453.15255460013304f,
204                  305.788586869862f);
205 
206     SkRect fillR, strokeR;
207     fillR = path.getBounds();
208 
209     paint.setStyle(SkPaint::kStroke_Style);
210     paint.setStrokeWidth(SkIntToScalar(2));
211     paint.getFillPath(path, &stroke);
212     strokeR = stroke.getBounds();
213 
214     SkRect maxR = fillR;
215     SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter());
216     SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ?
217                             SkScalarMul(paint.getStrokeWidth(), miter) :
218                             paint.getStrokeWidth();
219     maxR.inset(-inset, -inset);
220 
221     // test that our stroke didn't explode
222     REPORTER_ASSERT(reporter, maxR.contains(strokeR));
223 }
224 
225 // found and fixed for android: not initializing rect for string's of length 0
regression_measureText(skiatest::Reporter * reporter)226 static void regression_measureText(skiatest::Reporter* reporter) {
227 
228     SkPaint paint;
229     paint.setTextSize(12.0f);
230 
231     SkRect r;
232     r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN);
233 
234     // test that the rect was reset
235     paint.measureText("", 0, &r, 1.0f);
236     REPORTER_ASSERT(reporter, r.isEmpty());
237 }
238 
DEF_TEST(Paint,reporter)239 DEF_TEST(Paint, reporter) {
240     // TODO add general paint tests
241     test_copy(reporter);
242 
243     // regression tests
244     regression_cubic(reporter);
245     regression_measureText(reporter);
246 
247     test_filterlevel(reporter);
248 
249     // need to implement charsToGlyphs on other backends (e.g. linux, win)
250     // before we can run this tests everywhere
251     if (false) {
252        test_cmap(reporter);
253     }
254 }
255