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 "SkBlurMask.h" 9 #include "SkBlurMaskFilter.h" 10 #include "SkLayerDrawLooper.h" 11 #include "SkPaint.h" 12 #include "SkPath.h" 13 #include "SkRandom.h" 14 #include "SkReadBuffer.h" 15 #include "SkTypeface.h" 16 #include "SkUtils.h" 17 #include "SkWriteBuffer.h" 18 #include "SkXfermode.h" 19 #include "Test.h" 20 21 static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) { 22 char* u8 = (char*)dst; 23 for (int i = 0; i < count; ++i) { 24 int n = SkToInt(SkUTF8_FromUnichar(src[i], u8)); 25 u8 += n; 26 } 27 return u8 - (char*)dst; 28 } 29 30 static size_t uni_to_utf16(const SkUnichar src[], void* dst, int count) { 31 uint16_t* u16 = (uint16_t*)dst; 32 for (int i = 0; i < count; ++i) { 33 int n = SkToInt(SkUTF16_FromUnichar(src[i], u16)); 34 u16 += n; 35 } 36 return (char*)u16 - (char*)dst; 37 } 38 39 static size_t uni_to_utf32(const SkUnichar src[], void* dst, int count) { 40 SkUnichar* u32 = (SkUnichar*)dst; 41 if (src != u32) { 42 memcpy(u32, src, count * sizeof(SkUnichar)); 43 } 44 return count * sizeof(SkUnichar); 45 } 46 47 static SkTypeface::Encoding paint2encoding(const SkPaint& paint) { 48 SkPaint::TextEncoding enc = paint.getTextEncoding(); 49 SkASSERT(SkPaint::kGlyphID_TextEncoding != enc); 50 return (SkTypeface::Encoding)enc; 51 } 52 53 static int find_first_zero(const uint16_t glyphs[], int count) { 54 for (int i = 0; i < count; ++i) { 55 if (0 == glyphs[i]) { 56 return i; 57 } 58 } 59 return count; 60 } 61 62 DEF_TEST(Paint_cmap, reporter) { 63 // need to implement charsToGlyphs on other backends (e.g. linux, win) 64 // before we can run this tests everywhere 65 return; 66 67 static const int NGLYPHS = 64; 68 69 SkUnichar src[NGLYPHS]; 70 SkUnichar dst[NGLYPHS]; // used for utf8, utf16, utf32 storage 71 72 static const struct { 73 size_t (*fSeedTextProc)(const SkUnichar[], void* dst, int count); 74 SkPaint::TextEncoding fEncoding; 75 } gRec[] = { 76 { uni_to_utf8, SkPaint::kUTF8_TextEncoding }, 77 { uni_to_utf16, SkPaint::kUTF16_TextEncoding }, 78 { uni_to_utf32, SkPaint::kUTF32_TextEncoding }, 79 }; 80 81 SkRandom rand; 82 SkPaint paint; 83 paint.setTypeface(SkTypeface::RefDefault())->unref(); 84 SkTypeface* face = paint.getTypeface(); 85 86 for (int i = 0; i < 1000; ++i) { 87 // generate some random text 88 for (int j = 0; j < NGLYPHS; ++j) { 89 src[j] = ' ' + j; 90 } 91 // inject some random chars, to sometimes abort early 92 src[rand.nextU() & 63] = rand.nextU() & 0xFFF; 93 94 for (size_t k = 0; k < SK_ARRAY_COUNT(gRec); ++k) { 95 paint.setTextEncoding(gRec[k].fEncoding); 96 97 size_t len = gRec[k].fSeedTextProc(src, dst, NGLYPHS); 98 99 uint16_t glyphs0[NGLYPHS], glyphs1[NGLYPHS]; 100 101 bool contains = paint.containsText(dst, len); 102 int nglyphs = paint.textToGlyphs(dst, len, glyphs0); 103 int first = face->charsToGlyphs(dst, paint2encoding(paint), glyphs1, NGLYPHS); 104 int index = find_first_zero(glyphs1, NGLYPHS); 105 106 REPORTER_ASSERT(reporter, NGLYPHS == nglyphs); 107 REPORTER_ASSERT(reporter, index == first); 108 REPORTER_ASSERT(reporter, 0 == memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t))); 109 if (contains) { 110 REPORTER_ASSERT(reporter, NGLYPHS == first); 111 } else { 112 REPORTER_ASSERT(reporter, NGLYPHS > first); 113 } 114 } 115 } 116 } 117 118 // temparary api for bicubic, just be sure we can set/clear it 119 DEF_TEST(Paint_filterQuality, reporter) { 120 SkPaint p0, p1; 121 122 REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality()); 123 124 static const SkFilterQuality gQualitys[] = { 125 kNone_SkFilterQuality, 126 kLow_SkFilterQuality, 127 kMedium_SkFilterQuality, 128 kHigh_SkFilterQuality 129 }; 130 for (size_t i = 0; i < SK_ARRAY_COUNT(gQualitys); ++i) { 131 p0.setFilterQuality(gQualitys[i]); 132 REPORTER_ASSERT(reporter, gQualitys[i] == p0.getFilterQuality()); 133 p1 = p0; 134 REPORTER_ASSERT(reporter, gQualitys[i] == p1.getFilterQuality()); 135 136 p0.reset(); 137 REPORTER_ASSERT(reporter, kNone_SkFilterQuality == p0.getFilterQuality()); 138 } 139 } 140 141 DEF_TEST(Paint_copy, reporter) { 142 SkPaint paint; 143 // set a few member variables 144 paint.setStyle(SkPaint::kStrokeAndFill_Style); 145 paint.setTextAlign(SkPaint::kLeft_Align); 146 paint.setStrokeWidth(SkIntToScalar(2)); 147 // set a few pointers 148 SkLayerDrawLooper::Builder looperBuilder; 149 SkLayerDrawLooper* looper = looperBuilder.detachLooper(); 150 paint.setLooper(looper)->unref(); 151 SkMaskFilter* mask = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, 152 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(1))); 153 paint.setMaskFilter(mask)->unref(); 154 155 // copy the paint using the copy constructor and check they are the same 156 SkPaint copiedPaint = paint; 157 REPORTER_ASSERT(reporter, paint == copiedPaint); 158 159 // copy the paint using the equal operator and check they are the same 160 copiedPaint = paint; 161 REPORTER_ASSERT(reporter, paint == copiedPaint); 162 163 // clean the paint and check they are back to their initial states 164 SkPaint cleanPaint; 165 paint.reset(); 166 copiedPaint.reset(); 167 REPORTER_ASSERT(reporter, cleanPaint == paint); 168 REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); 169 } 170 171 // found and fixed for webkit: mishandling when we hit recursion limit on 172 // mostly degenerate cubic flatness test 173 DEF_TEST(Paint_regression_cubic, reporter) { 174 SkPath path, stroke; 175 SkPaint paint; 176 177 path.moveTo(460.2881309415525f, 178 303.250847066498f); 179 path.cubicTo(463.36378422175284f, 180 302.1169735073363f, 181 456.32239330810046f, 182 304.720354932878f, 183 453.15255460013304f, 184 305.788586869862f); 185 186 SkRect fillR, strokeR; 187 fillR = path.getBounds(); 188 189 paint.setStyle(SkPaint::kStroke_Style); 190 paint.setStrokeWidth(SkIntToScalar(2)); 191 paint.getFillPath(path, &stroke); 192 strokeR = stroke.getBounds(); 193 194 SkRect maxR = fillR; 195 SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter()); 196 SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ? 197 SkScalarMul(paint.getStrokeWidth(), miter) : 198 paint.getStrokeWidth(); 199 maxR.inset(-inset, -inset); 200 201 // test that our stroke didn't explode 202 REPORTER_ASSERT(reporter, maxR.contains(strokeR)); 203 } 204 205 DEF_TEST(Paint_flattening, reporter) { 206 const SkFilterQuality levels[] = { 207 kNone_SkFilterQuality, 208 kLow_SkFilterQuality, 209 kMedium_SkFilterQuality, 210 kHigh_SkFilterQuality, 211 }; 212 const SkPaint::Hinting hinting[] = { 213 SkPaint::kNo_Hinting, 214 SkPaint::kSlight_Hinting, 215 SkPaint::kNormal_Hinting, 216 SkPaint::kFull_Hinting, 217 }; 218 const SkPaint::Align align[] = { 219 SkPaint::kLeft_Align, 220 SkPaint::kCenter_Align, 221 SkPaint::kRight_Align 222 }; 223 const SkPaint::Cap caps[] = { 224 SkPaint::kButt_Cap, 225 SkPaint::kRound_Cap, 226 SkPaint::kSquare_Cap, 227 }; 228 const SkPaint::Join joins[] = { 229 SkPaint::kMiter_Join, 230 SkPaint::kRound_Join, 231 SkPaint::kBevel_Join, 232 }; 233 const SkPaint::TextEncoding encodings[] = { 234 SkPaint::kUTF8_TextEncoding, 235 SkPaint::kUTF16_TextEncoding, 236 SkPaint::kUTF32_TextEncoding, 237 SkPaint::kGlyphID_TextEncoding, 238 }; 239 const SkPaint::Style styles[] = { 240 SkPaint::kFill_Style, 241 SkPaint::kStroke_Style, 242 SkPaint::kStrokeAndFill_Style, 243 }; 244 245 #define FOR_SETUP(index, array, setter) \ 246 for (size_t index = 0; index < SK_ARRAY_COUNT(array); ++index) { \ 247 paint.setter(array[index]); \ 248 249 SkPaint paint; 250 paint.setFlags(0x1234); 251 252 FOR_SETUP(i, levels, setFilterQuality) 253 FOR_SETUP(j, hinting, setHinting) 254 FOR_SETUP(k, align, setTextAlign) 255 FOR_SETUP(l, caps, setStrokeCap) 256 FOR_SETUP(m, joins, setStrokeJoin) 257 FOR_SETUP(n, encodings, setTextEncoding) 258 FOR_SETUP(p, styles, setStyle) 259 260 SkWriteBuffer writer; 261 paint.flatten(writer); 262 263 const uint32_t* written = writer.getWriter32()->contiguousArray(); 264 SkReadBuffer reader(written, writer.bytesWritten()); 265 266 SkPaint paint2; 267 paint2.unflatten(reader); 268 REPORTER_ASSERT(reporter, paint2 == paint); 269 270 }}}}}}} 271 #undef FOR_SETUP 272 273 } 274 275 // found and fixed for android: not initializing rect for string's of length 0 276 DEF_TEST(Paint_regression_measureText, reporter) { 277 278 SkPaint paint; 279 paint.setTextSize(12.0f); 280 281 SkRect r; 282 r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN); 283 284 // test that the rect was reset 285 paint.measureText("", 0, &r); 286 REPORTER_ASSERT(reporter, r.isEmpty()); 287 } 288 289 #define ASSERT(expr) REPORTER_ASSERT(r, expr) 290 291 DEF_TEST(Paint_MoreFlattening, r) { 292 SkPaint paint; 293 paint.setColor(0x00AABBCC); 294 paint.setTextScaleX(1.0f); // Default value, ignored. 295 paint.setTextSize(19); 296 paint.setXfermode(SkXfermode::Create(SkXfermode::kModulate_Mode))->unref(); 297 paint.setLooper(nullptr); // Default value, ignored. 298 299 SkWriteBuffer writer; 300 paint.flatten(writer); 301 302 SkReadBuffer reader(writer.getWriter32()->contiguousArray(), writer.bytesWritten()); 303 SkPaint other; 304 other.unflatten(reader); 305 ASSERT(reader.offset() == writer.bytesWritten()); 306 307 // No matter the encoding, these must always hold. 308 ASSERT(other.getColor() == paint.getColor()); 309 ASSERT(other.getTextScaleX() == paint.getTextScaleX()); 310 ASSERT(other.getTextSize() == paint.getTextSize()); 311 ASSERT(other.getLooper() == paint.getLooper()); 312 313 // We have to be a little looser and compare just the modes. Pointers might not be the same. 314 SkXfermode::Mode otherMode, paintMode; 315 ASSERT(other.getXfermode()->asMode(&otherMode)); 316 ASSERT(paint.getXfermode()->asMode(&paintMode)); 317 ASSERT(otherMode == paintMode); 318 } 319 320 DEF_TEST(Paint_getHash, r) { 321 // Try not to inspect the actual hash values in here. 322 // We might want to change the hash function. 323 324 SkPaint paint; 325 const uint32_t defaultHash = paint.getHash(); 326 327 // Check that some arbitrary field affects the hash. 328 paint.setColor(0xFF00FF00); 329 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 330 paint.setColor(SK_ColorBLACK); // Reset to default value. 331 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 332 333 // SkTypeface is the first field we hash, so test it specially. 334 paint.setTypeface(SkTypeface::RefDefault())->unref(); 335 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 336 paint.setTypeface(nullptr); 337 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 338 339 // This is part of fBitfields, the last field we hash. 340 paint.setHinting(SkPaint::kSlight_Hinting); 341 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 342 paint.setHinting(SkPaint::kNormal_Hinting); 343 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 344 } 345 346 #include "SkColorMatrixFilter.h" 347 348 DEF_TEST(Paint_nothingToDraw, r) { 349 SkPaint paint; 350 351 REPORTER_ASSERT(r, !paint.nothingToDraw()); 352 paint.setAlpha(0); 353 REPORTER_ASSERT(r, paint.nothingToDraw()); 354 355 paint.setAlpha(0xFF); 356 paint.setXfermodeMode(SkXfermode::kDst_Mode); 357 REPORTER_ASSERT(r, paint.nothingToDraw()); 358 359 paint.setAlpha(0); 360 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); 361 362 SkColorMatrix cm; 363 cm.setIdentity(); // does not change alpha 364 paint.setColorFilter(SkColorMatrixFilter::Create(cm))->unref(); 365 REPORTER_ASSERT(r, paint.nothingToDraw()); 366 367 cm.postTranslate(0, 0, 0, 1); // wacks alpha 368 paint.setColorFilter(SkColorMatrixFilter::Create(cm))->unref(); 369 REPORTER_ASSERT(r, !paint.nothingToDraw()); 370 } 371 372