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 "SkAutoMalloc.h" 9#include "SkBlurMask.h" 10#include "SkBlurMaskFilter.h" 11#include "SkLayerDrawLooper.h" 12#include "SkPaint.h" 13#include "SkPath.h" 14#include "SkRandom.h" 15#include "SkReadBuffer.h" 16#include "SkTypeface.h" 17#include "SkUtils.h" 18#include "SkWriteBuffer.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 = SkToInt(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 = SkToInt(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::MakeDefault()); 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_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 141DEF_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 paint.setLooper(looperBuilder.detach()); 150 paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 151 SkBlurMask::ConvertRadiusToSigma(1))); 152 153 // copy the paint using the copy constructor and check they are the same 154 SkPaint copiedPaint = paint; 155 REPORTER_ASSERT(reporter, paint == copiedPaint); 156 157 // copy the paint using the equal operator and check they are the same 158 copiedPaint = paint; 159 REPORTER_ASSERT(reporter, paint == copiedPaint); 160 161 // clean the paint and check they are back to their initial states 162 SkPaint cleanPaint; 163 paint.reset(); 164 copiedPaint.reset(); 165 REPORTER_ASSERT(reporter, cleanPaint == paint); 166 REPORTER_ASSERT(reporter, cleanPaint == copiedPaint); 167} 168 169// found and fixed for webkit: mishandling when we hit recursion limit on 170// mostly degenerate cubic flatness test 171DEF_TEST(Paint_regression_cubic, reporter) { 172 SkPath path, stroke; 173 SkPaint paint; 174 175 path.moveTo(460.2881309415525f, 176 303.250847066498f); 177 path.cubicTo(463.36378422175284f, 178 302.1169735073363f, 179 456.32239330810046f, 180 304.720354932878f, 181 453.15255460013304f, 182 305.788586869862f); 183 184 SkRect fillR, strokeR; 185 fillR = path.getBounds(); 186 187 paint.setStyle(SkPaint::kStroke_Style); 188 paint.setStrokeWidth(SkIntToScalar(2)); 189 paint.getFillPath(path, &stroke); 190 strokeR = stroke.getBounds(); 191 192 SkRect maxR = fillR; 193 SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter()); 194 SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ? 195 paint.getStrokeWidth() * miter : 196 paint.getStrokeWidth(); 197 maxR.inset(-inset, -inset); 198 199 // test that our stroke didn't explode 200 REPORTER_ASSERT(reporter, maxR.contains(strokeR)); 201} 202 203DEF_TEST(Paint_flattening, reporter) { 204 const SkFilterQuality levels[] = { 205 kNone_SkFilterQuality, 206 kLow_SkFilterQuality, 207 kMedium_SkFilterQuality, 208 kHigh_SkFilterQuality, 209 }; 210 const SkPaint::Hinting hinting[] = { 211 SkPaint::kNo_Hinting, 212 SkPaint::kSlight_Hinting, 213 SkPaint::kNormal_Hinting, 214 SkPaint::kFull_Hinting, 215 }; 216 const SkPaint::Align align[] = { 217 SkPaint::kLeft_Align, 218 SkPaint::kCenter_Align, 219 SkPaint::kRight_Align 220 }; 221 const SkPaint::Cap caps[] = { 222 SkPaint::kButt_Cap, 223 SkPaint::kRound_Cap, 224 SkPaint::kSquare_Cap, 225 }; 226 const SkPaint::Join joins[] = { 227 SkPaint::kMiter_Join, 228 SkPaint::kRound_Join, 229 SkPaint::kBevel_Join, 230 }; 231 const SkPaint::TextEncoding encodings[] = { 232 SkPaint::kUTF8_TextEncoding, 233 SkPaint::kUTF16_TextEncoding, 234 SkPaint::kUTF32_TextEncoding, 235 SkPaint::kGlyphID_TextEncoding, 236 }; 237 const SkPaint::Style styles[] = { 238 SkPaint::kFill_Style, 239 SkPaint::kStroke_Style, 240 SkPaint::kStrokeAndFill_Style, 241 }; 242 243#define FOR_SETUP(index, array, setter) \ 244 for (size_t index = 0; index < SK_ARRAY_COUNT(array); ++index) { \ 245 paint.setter(array[index]); \ 246 247 SkPaint paint; 248 paint.setFlags(0x1234); 249 250 FOR_SETUP(i, levels, setFilterQuality) 251 FOR_SETUP(j, hinting, setHinting) 252 FOR_SETUP(k, align, setTextAlign) 253 FOR_SETUP(l, caps, setStrokeCap) 254 FOR_SETUP(m, joins, setStrokeJoin) 255 FOR_SETUP(n, encodings, setTextEncoding) 256 FOR_SETUP(p, styles, setStyle) 257 258 SkBinaryWriteBuffer writer; 259 paint.flatten(writer); 260 261 SkAutoMalloc buf(writer.bytesWritten()); 262 writer.writeToMemory(buf.get()); 263 SkReadBuffer reader(buf.get(), writer.bytesWritten()); 264 265 SkPaint paint2; 266 paint2.unflatten(reader); 267 REPORTER_ASSERT(reporter, paint2 == paint); 268 269 }}}}}}} 270#undef FOR_SETUP 271 272} 273 274// found and fixed for android: not initializing rect for string's of length 0 275DEF_TEST(Paint_regression_measureText, reporter) { 276 277 SkPaint paint; 278 paint.setTextSize(12.0f); 279 280 SkRect r; 281 r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN); 282 283 // test that the rect was reset 284 paint.measureText("", 0, &r); 285 REPORTER_ASSERT(reporter, r.isEmpty()); 286} 287 288#define ASSERT(expr) REPORTER_ASSERT(r, expr) 289 290DEF_TEST(Paint_MoreFlattening, r) { 291 SkPaint paint; 292 paint.setColor(0x00AABBCC); 293 paint.setTextScaleX(1.0f); // Default value, ignored. 294 paint.setTextSize(19); 295 paint.setBlendMode(SkBlendMode::kModulate); 296 paint.setLooper(nullptr); // Default value, ignored. 297 298 SkBinaryWriteBuffer writer; 299 paint.flatten(writer); 300 301 SkAutoMalloc buf(writer.bytesWritten()); 302 writer.writeToMemory(buf.get()); 303 SkReadBuffer reader(buf.get(), writer.bytesWritten()); 304 305 SkPaint other; 306 other.unflatten(reader); 307 ASSERT(reader.offset() == writer.bytesWritten()); 308 309 // No matter the encoding, these must always hold. 310 ASSERT(other.getColor() == paint.getColor()); 311 ASSERT(other.getTextScaleX() == paint.getTextScaleX()); 312 ASSERT(other.getTextSize() == paint.getTextSize()); 313 ASSERT(other.getLooper() == paint.getLooper()); 314 ASSERT(other.getBlendMode() == paint.getBlendMode()); 315} 316 317DEF_TEST(Paint_getHash, r) { 318 // Try not to inspect the actual hash values in here. 319 // We might want to change the hash function. 320 321 SkPaint paint; 322 const uint32_t defaultHash = paint.getHash(); 323 324 // Check that some arbitrary field affects the hash. 325 paint.setColor(0xFF00FF00); 326 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 327 paint.setColor(SK_ColorBLACK); // Reset to default value. 328 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 329 330 // SkTypeface is the first field we hash, so test it specially. 331 paint.setTypeface(SkTypeface::MakeDefault()); 332 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 333 paint.setTypeface(nullptr); 334 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 335 336 // This is part of fBitfields, the last field we hash. 337 paint.setHinting(SkPaint::kSlight_Hinting); 338 REPORTER_ASSERT(r, paint.getHash() != defaultHash); 339 paint.setHinting(SkPaint::kNormal_Hinting); 340 REPORTER_ASSERT(r, paint.getHash() == defaultHash); 341} 342 343#include "SkColorMatrixFilter.h" 344 345DEF_TEST(Paint_nothingToDraw, r) { 346 SkPaint paint; 347 348 REPORTER_ASSERT(r, !paint.nothingToDraw()); 349 paint.setAlpha(0); 350 REPORTER_ASSERT(r, paint.nothingToDraw()); 351 352 paint.setAlpha(0xFF); 353 paint.setBlendMode(SkBlendMode::kDst); 354 REPORTER_ASSERT(r, paint.nothingToDraw()); 355 356 paint.setAlpha(0); 357 paint.setBlendMode(SkBlendMode::kSrcOver); 358 359 SkColorMatrix cm; 360 cm.setIdentity(); // does not change alpha 361 paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat)); 362 REPORTER_ASSERT(r, paint.nothingToDraw()); 363 364 cm.postTranslate(0, 0, 0, 1); // wacks alpha 365 paint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat)); 366 REPORTER_ASSERT(r, !paint.nothingToDraw()); 367} 368