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