PaintTest.cpp revision fb1fe4f51820731f557e765f8c71cba9a0d28048
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 21static 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 = SkUTF8_FromUnichar(src[i], u8); 25 u8 += n; 26 } 27 return u8 - (char*)dst; 28} 29 30static 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 = SkUTF16_FromUnichar(src[i], u16); 34 u16 += n; 35 } 36 return (char*)u16 - (char*)dst; 37} 38 39static 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 47static 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 53static 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 62DEF_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 119DEF_TEST(Paint_filterlevel, reporter) { 120 SkPaint p0, p1; 121 122 REPORTER_ASSERT(reporter, 123 SkPaint::kNone_FilterLevel == p0.getFilterLevel()); 124 125 static const SkPaint::FilterLevel gLevels[] = { 126 SkPaint::kNone_FilterLevel, 127 SkPaint::kLow_FilterLevel, 128 SkPaint::kMedium_FilterLevel, 129 SkPaint::kHigh_FilterLevel 130 }; 131 for (size_t i = 0; i < SK_ARRAY_COUNT(gLevels); ++i) { 132 p0.setFilterLevel(gLevels[i]); 133 REPORTER_ASSERT(reporter, gLevels[i] == p0.getFilterLevel()); 134 p1 = p0; 135 REPORTER_ASSERT(reporter, gLevels[i] == p1.getFilterLevel()); 136 137 p0.reset(); 138 REPORTER_ASSERT(reporter, 139 SkPaint::kNone_FilterLevel == p0.getFilterLevel()); 140 } 141} 142 143DEF_TEST(Paint_copy, reporter) { 144 SkPaint paint; 145 // set a few member variables 146 paint.setStyle(SkPaint::kStrokeAndFill_Style); 147 paint.setTextAlign(SkPaint::kLeft_Align); 148 paint.setStrokeWidth(SkIntToScalar(2)); 149 // set a few pointers 150 SkLayerDrawLooper::Builder looperBuilder; 151 SkLayerDrawLooper* looper = looperBuilder.detachLooper(); 152 paint.setLooper(looper)->unref(); 153 SkMaskFilter* mask = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, 154 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(1))); 155 paint.setMaskFilter(mask)->unref(); 156 157 // copy the paint using the copy constructor and check they are the same 158 SkPaint copiedPaint = paint; 159 REPORTER_ASSERT(reporter, paint == copiedPaint); 160 161#ifdef SK_BUILD_FOR_ANDROID 162 // the copy constructor should preserve the Generation ID 163 uint32_t paintGenID = paint.getGenerationID(); 164 uint32_t copiedPaintGenID = copiedPaint.getGenerationID(); 165 REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID); 166 REPORTER_ASSERT(reporter, paint == copiedPaint); 167#endif 168 169 // copy the paint using the equal operator and check they are the same 170 copiedPaint = paint; 171 REPORTER_ASSERT(reporter, paint == copiedPaint); 172 173#ifdef SK_BUILD_FOR_ANDROID 174 // the equals operator should increment the Generation ID 175 REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID); 176 REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID); 177 copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value 178 REPORTER_ASSERT(reporter, paint == copiedPaint); // operator== ignores fGenerationID 179#endif 180 181 // clean the paint and check they are back to their initial states 182 SkPaint cleanPaint; 183 paint.reset(); 184 copiedPaint.reset(); 185 REPORTER_ASSERT(reporter, cleanPaint == paint); 186 REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); 187 188#ifdef SK_BUILD_FOR_ANDROID 189 // the reset function should increment the Generation ID 190 REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID); 191 REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID); 192 // operator== ignores fGenerationID 193 REPORTER_ASSERT(reporter, cleanPaint == paint); 194 REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); 195#endif 196} 197 198// found and fixed for webkit: mishandling when we hit recursion limit on 199// mostly degenerate cubic flatness test 200DEF_TEST(Paint_regression_cubic, reporter) { 201 SkPath path, stroke; 202 SkPaint paint; 203 204 path.moveTo(460.2881309415525f, 205 303.250847066498f); 206 path.cubicTo(463.36378422175284f, 207 302.1169735073363f, 208 456.32239330810046f, 209 304.720354932878f, 210 453.15255460013304f, 211 305.788586869862f); 212 213 SkRect fillR, strokeR; 214 fillR = path.getBounds(); 215 216 paint.setStyle(SkPaint::kStroke_Style); 217 paint.setStrokeWidth(SkIntToScalar(2)); 218 paint.getFillPath(path, &stroke); 219 strokeR = stroke.getBounds(); 220 221 SkRect maxR = fillR; 222 SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter()); 223 SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ? 224 SkScalarMul(paint.getStrokeWidth(), miter) : 225 paint.getStrokeWidth(); 226 maxR.inset(-inset, -inset); 227 228 // test that our stroke didn't explode 229 REPORTER_ASSERT(reporter, maxR.contains(strokeR)); 230} 231 232DEF_TEST(Paint_flattening, reporter) { 233 const SkPaint::FilterLevel levels[] = { 234 SkPaint::kNone_FilterLevel, 235 SkPaint::kLow_FilterLevel, 236 SkPaint::kMedium_FilterLevel, 237 SkPaint::kHigh_FilterLevel, 238 }; 239 const SkPaint::Hinting hinting[] = { 240 SkPaint::kNo_Hinting, 241 SkPaint::kSlight_Hinting, 242 SkPaint::kNormal_Hinting, 243 SkPaint::kFull_Hinting, 244 }; 245 const SkPaint::Align align[] = { 246 SkPaint::kLeft_Align, 247 SkPaint::kCenter_Align, 248 SkPaint::kRight_Align 249 }; 250 const SkPaint::Cap caps[] = { 251 SkPaint::kButt_Cap, 252 SkPaint::kRound_Cap, 253 SkPaint::kSquare_Cap, 254 }; 255 const SkPaint::Join joins[] = { 256 SkPaint::kMiter_Join, 257 SkPaint::kRound_Join, 258 SkPaint::kBevel_Join, 259 }; 260 const SkPaint::TextEncoding encodings[] = { 261 SkPaint::kUTF8_TextEncoding, 262 SkPaint::kUTF16_TextEncoding, 263 SkPaint::kUTF32_TextEncoding, 264 SkPaint::kGlyphID_TextEncoding, 265 }; 266 const SkPaint::Style styles[] = { 267 SkPaint::kFill_Style, 268 SkPaint::kStroke_Style, 269 SkPaint::kStrokeAndFill_Style, 270 }; 271 272#define FOR_SETUP(index, array, setter) \ 273 for (size_t index = 0; index < SK_ARRAY_COUNT(array); ++index) { \ 274 paint.setter(array[index]); \ 275 276 SkPaint paint; 277 paint.setFlags(0x1234); 278 279 FOR_SETUP(i, levels, setFilterLevel) 280 FOR_SETUP(j, hinting, setHinting) 281 FOR_SETUP(k, align, setTextAlign) 282 FOR_SETUP(l, caps, setStrokeCap) 283 FOR_SETUP(m, joins, setStrokeJoin) 284 FOR_SETUP(n, encodings, setTextEncoding) 285 FOR_SETUP(p, styles, setStyle) 286 287 SkWriteBuffer writer; 288 paint.flatten(writer); 289 290 const uint32_t* written = writer.getWriter32()->contiguousArray(); 291 SkReadBuffer reader(written, writer.bytesWritten()); 292 293 SkPaint paint2; 294 paint2.unflatten(reader); 295 REPORTER_ASSERT(reporter, paint2 == paint); 296 297 }}}}}}} 298#undef FOR_SETUP 299 300} 301 302// found and fixed for android: not initializing rect for string's of length 0 303DEF_TEST(Paint_regression_measureText, reporter) { 304 305 SkPaint paint; 306 paint.setTextSize(12.0f); 307 308 SkRect r; 309 r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN); 310 311 // test that the rect was reset 312 paint.measureText("", 0, &r); 313 REPORTER_ASSERT(reporter, r.isEmpty()); 314} 315 316#define ASSERT(expr) REPORTER_ASSERT(r, expr) 317 318DEF_TEST(Paint_MoreFlattening, r) { 319 SkPaint paint; 320 paint.setColor(0x00AABBCC); 321 paint.setTextScaleX(1.0f); // Default value, ignored. 322 paint.setTextSize(19); 323 paint.setXfermode(SkXfermode::Create(SkXfermode::kModulate_Mode))->unref(); 324 paint.setLooper(NULL); // Default value, ignored. 325 326 SkWriteBuffer writer; 327 paint.flatten(writer); 328 329 SkReadBuffer reader(writer.getWriter32()->contiguousArray(), writer.bytesWritten()); 330 SkPaint other; 331 other.unflatten(reader); 332 ASSERT(reader.offset() == writer.bytesWritten()); 333 334 // No matter the encoding, these must always hold. 335 ASSERT(other.getColor() == paint.getColor()); 336 ASSERT(other.getTextScaleX() == paint.getTextScaleX()); 337 ASSERT(other.getTextSize() == paint.getTextSize()); 338 ASSERT(other.getLooper() == paint.getLooper()); 339 340 // We have to be a little looser and compare just the modes. Pointers might not be the same. 341 SkXfermode::Mode otherMode, paintMode; 342 ASSERT(other.getXfermode()->asMode(&otherMode)); 343 ASSERT(paint.getXfermode()->asMode(&paintMode)); 344 ASSERT(otherMode == paintMode); 345} 346 347DEF_TEST(Paint_getHash, r) { 348 // Try not to inspect the actual hash values in here. 349 // We might want to change the hash function. 350 351 SkPaint paint; 352 const uint32_t defaultHash = paint.getHash(); 353 354 // Check that some arbitrary field affects the hash. 355 paint.setColor(0xFF00FF00); 356 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 357 paint.setColor(SK_ColorBLACK); // Reset to default value. 358 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 359 360 // SkTypeface is the first field we hash, so test it specially. 361 paint.setTypeface(SkTypeface::RefDefault())->unref(); 362 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 363 paint.setTypeface(NULL); 364 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 365 366 // This is part of fBitfields, the last field we hash. 367 paint.setHinting(SkPaint::kSlight_Hinting); 368 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 369 paint.setHinting(SkPaint::kNormal_Hinting); 370 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 371} 372