1 /* 2 * Copyright 2018 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 "gm/gm.h" 9 #include "include/core/SkBitmap.h" 10 #include "include/core/SkCanvas.h" 11 #include "include/core/SkColor.h" 12 #include "include/core/SkColorSpace.h" 13 #include "include/core/SkFilterQuality.h" 14 #include "include/core/SkFont.h" 15 #include "include/core/SkImageInfo.h" 16 #include "include/core/SkMatrix.h" 17 #include "include/core/SkPaint.h" 18 #include "include/core/SkPathEffect.h" 19 #include "include/core/SkPixmap.h" 20 #include "include/core/SkPoint.h" 21 #include "include/core/SkRefCnt.h" 22 #include "include/core/SkScalar.h" 23 #include "include/core/SkShader.h" 24 #include "include/core/SkString.h" 25 #include "include/core/SkTileMode.h" 26 #include "include/core/SkTypes.h" 27 #include "include/effects/SkDashPathEffect.h" 28 #include "include/effects/SkGradientShader.h" 29 #include "src/core/SkColorSpaceXformSteps.h" 30 31 #include <math.h> 32 #include <string.h> 33 nearly_equal(SkColor4f x,SkColor4f y)34 static bool nearly_equal(SkColor4f x, SkColor4f y) { 35 const float K = 0.01f; 36 return fabsf(x.fR - y.fR) < K 37 && fabsf(x.fG - y.fG) < K 38 && fabsf(x.fB - y.fB) < K 39 && fabsf(x.fA - y.fA) < K; 40 } 41 fmt(SkColor4f c)42 static SkString fmt(SkColor4f c) { 43 return SkStringPrintf("%.2g %.2g %.2g %.2g", c.fR, c.fG, c.fB, c.fA); 44 } 45 transform(SkColor4f c,SkColorSpace * src,SkColorSpace * dst)46 static SkColor4f transform(SkColor4f c, SkColorSpace* src, SkColorSpace* dst) { 47 SkColorSpaceXformSteps(src, kUnpremul_SkAlphaType, 48 dst, kUnpremul_SkAlphaType).apply(c.vec()); 49 return c; 50 } 51 compare_pixel(const char * label,SkCanvas * canvas,int x,int y,SkColor4f color,SkColorSpace * cs)52 static void compare_pixel(const char* label, 53 SkCanvas* canvas, int x, int y, 54 SkColor4f color, SkColorSpace* cs) { 55 SkPaint paint; 56 SkFont font; 57 auto canvas_cs = canvas->imageInfo().refColorSpace(); 58 59 // I'm not really sure if this makes things easier or harder to follow, 60 // but we sniff the canvas to grab its current y-translate, so that (x,y) 61 // can be written in sort of chunk-relative terms. 62 const SkMatrix& m = canvas->getTotalMatrix(); 63 SkASSERT(m.isTranslate()); 64 SkScalar dy = m.getTranslateY(); 65 SkASSERT(dy == (int)dy); 66 y += (int)dy; 67 68 SkBitmap bm; 69 bm.allocPixels(SkImageInfo::Make(1,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType, canvas_cs)); 70 if (!canvas->readPixels(bm, x,y)) { 71 MarkGMGood(canvas, 140,40); 72 canvas->drawString("can't readPixels() on this canvas :(", 100,20, font, paint); 73 return; 74 } 75 76 SkColor4f pixel; 77 memcpy(&pixel, bm.getAddr(0,0), sizeof(pixel)); 78 79 SkColor4f expected = transform(color,cs, canvas_cs.get()); 80 if (canvas->imageInfo().colorType() < kRGBA_F16_SkColorType) { 81 // We can't expect normalized formats to hold values outside [0,1]. 82 for (int i = 0; i < 4; ++i) { 83 expected[i] = SkTPin(expected[i], 0.0f, 1.0f); 84 } 85 } 86 if (canvas->imageInfo().colorType() == kGray_8_SkColorType) { 87 // Drawing into Gray8 is known to be maybe-totally broken. 88 // TODO: update expectation here to be {lum,lum,lum,1} if we fix Gray8. 89 expected = SkColor4f{NAN, NAN, NAN, 1}; 90 } 91 92 if (nearly_equal(pixel, expected)) { 93 MarkGMGood(canvas, 140,40); 94 } else { 95 MarkGMBad(canvas, 140,40); 96 } 97 98 struct { 99 const char* label; 100 SkColor4f color; 101 } lines[] = { 102 {"Pixel:" , pixel }, 103 {"Expected:", expected}, 104 }; 105 106 SkAutoCanvasRestore saveRestore(canvas, true); 107 canvas->drawString(label, 80,20, font, paint); 108 for (auto l : lines) { 109 canvas->translate(0,20); 110 canvas->drawString(l.label, 80,20, font, paint); 111 canvas->drawString(fmt(l.color).c_str(), 140,20, font, paint); 112 } 113 } 114 115 DEF_SIMPLE_GM(p3, canvas, 450, 1300) { 116 auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); 117 auto srgb = SkColorSpace::MakeSRGB(); 118 __anon340e25860202(SkColor4f c) 119 auto p3_to_srgb = [&](SkColor4f c) { 120 SkPaint p; 121 p.setColor4f(c, p3.get()); 122 return p.getColor4f(); 123 }; 124 125 // Draw a P3 red rectangle and check the corner. 126 { 127 SkPaint paint; 128 paint.setColor4f({1,0,0,1}, p3.get()); 129 130 canvas->drawRect({10,10,70,70}, paint); 131 compare_pixel("drawRect P3 red ", 132 canvas, 10,10, 133 {1,0,0,1}, p3.get()); 134 } 135 136 canvas->translate(0,80); 137 138 // Draw a P3 red bitmap, using a draw. 139 { 140 SkBitmap bm; 141 bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3)); 142 143 SkPaint paint; 144 paint.setColor4f({1,0,0,1}, p3.get()); 145 SkCanvas{bm}.drawPaint(paint); 146 147 canvas->drawBitmap(bm, 10,10); 148 compare_pixel("drawBitmap P3 red, from drawPaint", 149 canvas, 10,10, 150 {1,0,0,1}, p3.get()); 151 } 152 153 canvas->translate(0,80); 154 155 // Draw a P3 red bitmap, using SkBitmap::eraseColor(). 156 { 157 SkBitmap bm; 158 bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3)); 159 160 bm.eraseColor(0xffff0000/*in P3*/); 161 162 canvas->drawBitmap(bm, 10,10); 163 compare_pixel("drawBitmap P3 red, from SkBitmap::eraseColor()", 164 canvas, 10,10, 165 {1,0,0,1}, p3.get()); 166 } 167 168 canvas->translate(0,80); 169 170 // Draw a P3 red bitmap, using SkPixmap::erase(). 171 { 172 SkBitmap bm; 173 bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3)); 174 175 // At the moment only SkPixmap has an erase() that takes an SkColor4f. 176 SkPixmap pm; 177 SkAssertResult(bm.peekPixels(&pm)); 178 SkAssertResult(pm.erase({1,0,0,1} /*in p3*/)); 179 180 canvas->drawBitmap(bm, 10,10); 181 compare_pixel("drawBitmap P3 red, from SkPixmap::erase", 182 canvas, 10,10, 183 {1,0,0,1}, p3.get()); 184 } 185 186 canvas->translate(0,80); 187 188 // Draw a P3 red bitmap wrapped in a shader, using SkPixmap::erase(). 189 { 190 SkBitmap bm; 191 bm.allocPixels(SkImageInfo::Make(60,60, kRGBA_F16_SkColorType, kPremul_SkAlphaType, p3)); 192 193 // At the moment only SkPixmap has an erase() that takes an SkColor4f. 194 SkPixmap pm; 195 SkAssertResult(bm.peekPixels(&pm)); 196 SkAssertResult(pm.erase({1,0,0,1} /*in p3*/)); 197 198 SkPaint paint; 199 paint.setShader(bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat)); 200 201 canvas->drawRect({10,10,70,70}, paint); 202 compare_pixel("drawBitmapAsShader P3 red, from SkPixmap::erase", 203 canvas, 10,10, 204 {1,0,0,1}, p3.get()); 205 } 206 207 canvas->translate(0,80); 208 209 // TODO(mtklein): sample and check the middle points of these gradients too. 210 211 // Draw a gradient from P3 red to P3 green interpolating in unpremul P3, checking the corners. 212 { 213 214 SkPoint points[] = {{10.5,10.5}, {69.5,69.5}}; 215 SkColor4f colors[] = {{1,0,0,1}, {0,1,0,1}}; 216 217 SkPaint paint; 218 paint.setShader(SkGradientShader::MakeLinear(points, colors, p3, 219 nullptr, SK_ARRAY_COUNT(colors), 220 SkTileMode::kClamp)); 221 canvas->drawRect({10,10,70,70}, paint); 222 canvas->save(); 223 compare_pixel("UPM P3 gradient, P3 red", 224 canvas, 10,10, 225 {1,0,0,1}, p3.get()); 226 227 canvas->translate(180, 0); 228 229 compare_pixel("UPM P3 gradient, P3 green", 230 canvas, 69,69, 231 {0,1,0,1}, p3.get()); 232 canvas->restore(); 233 } 234 235 canvas->translate(0,80); 236 237 // Draw a gradient from P3 red to P3 green interpolating in premul P3, checking the corners. 238 { 239 240 SkPoint points[] = {{10.5,10.5}, {69.5,69.5}}; 241 SkColor4f colors[] = {{1,0,0,1}, {0,1,0,1}}; 242 243 SkPaint paint; 244 paint.setShader( 245 SkGradientShader::MakeLinear(points, colors, p3, 246 nullptr, SK_ARRAY_COUNT(colors), 247 SkTileMode::kClamp, 248 SkGradientShader::kInterpolateColorsInPremul_Flag, 249 nullptr/*local matrix*/)); 250 canvas->drawRect({10,10,70,70}, paint); 251 canvas->save(); 252 compare_pixel("PM P3 gradient, P3 red", 253 canvas, 10,10, 254 {1,0,0,1}, p3.get()); 255 256 canvas->translate(180, 0); 257 258 compare_pixel("PM P3 gradient, P3 green", 259 canvas, 69,69, 260 {0,1,0,1}, p3.get()); 261 canvas->restore(); 262 } 263 264 canvas->translate(0,80); 265 266 // Draw a gradient from P3 red to P3 green interpolating in unpremul sRGB, checking the corners. 267 { 268 269 SkPoint points[] = {{10.5,10.5}, {69.5,69.5}}; 270 SkColor4f colors[] = {p3_to_srgb({1,0,0,1}), p3_to_srgb({0,1,0,1})}; 271 272 SkPaint paint; 273 paint.setShader(SkGradientShader::MakeLinear(points, colors, srgb, 274 nullptr, SK_ARRAY_COUNT(colors), 275 SkTileMode::kClamp)); 276 canvas->drawRect({10,10,70,70}, paint); 277 canvas->save(); 278 compare_pixel("UPM sRGB gradient, P3 red", 279 canvas, 10,10, 280 {1,0,0,1}, p3.get()); 281 282 canvas->translate(180, 0); 283 284 compare_pixel("UPM sRGB gradient, P3 green", 285 canvas, 69,69, 286 {0,1,0,1}, p3.get()); 287 canvas->restore(); 288 } 289 290 canvas->translate(0,80); 291 292 // Draw a gradient from P3 red to P3 green interpolating in premul sRGB, checking the corners. 293 { 294 295 SkPoint points[] = {{10.5,10.5}, {69.5,69.5}}; 296 SkColor4f colors[] = {p3_to_srgb({1,0,0,1}), p3_to_srgb({0,1,0,1})}; 297 298 SkPaint paint; 299 paint.setShader( 300 SkGradientShader::MakeLinear(points, colors, srgb, 301 nullptr, SK_ARRAY_COUNT(colors), 302 SkTileMode::kClamp, 303 SkGradientShader::kInterpolateColorsInPremul_Flag, 304 nullptr/*local matrix*/)); 305 canvas->drawRect({10,10,70,70}, paint); 306 canvas->save(); 307 compare_pixel("PM sRGB gradient, P3 red", 308 canvas, 10,10, 309 {1,0,0,1}, p3.get()); 310 311 canvas->translate(180, 0); 312 313 compare_pixel("PM sRGB gradient, P3 green", 314 canvas, 69,69, 315 {0,1,0,1}, p3.get()); 316 canvas->restore(); 317 } 318 319 canvas->translate(0,80); 320 321 // Leon's blue -> green -> red gradient, interpolating in premul. 322 { 323 SkPoint points[] = {{10.5,10.5}, {10.5,69.5}}; 324 SkColor4f colors[] = { {0,0,1,1}, {0,1,0,1}, {1,0,0,1} }; 325 326 SkPaint paint; 327 paint.setShader( 328 SkGradientShader::MakeLinear(points, colors, p3, 329 nullptr, SK_ARRAY_COUNT(colors), 330 SkTileMode::kClamp, 331 SkGradientShader::kInterpolateColorsInPremul_Flag, 332 nullptr/*local matrix*/)); 333 canvas->drawRect({10,10,70,70}, paint); 334 canvas->save(); 335 compare_pixel("Leon's gradient, P3 blue", 336 canvas, 10,10, 337 {0,0,1,1}, p3.get()); 338 339 canvas->translate(180, 0); 340 341 compare_pixel("Leon's gradient, P3 red", 342 canvas, 10,69, 343 {1,0,0,1}, p3.get()); 344 canvas->restore(); 345 } 346 347 canvas->translate(0,80); 348 349 // Draw an A8 image with a P3 red, scaled and not, as a shader or bitmap. 350 { 351 uint8_t mask[256]; 352 for (int i = 0; i < 256; i++) { 353 mask[i] = 255-i; 354 } 355 SkBitmap bm; 356 bm.installPixels(SkImageInfo::MakeA8(16,16), mask, 16); 357 358 SkPaint as_bitmap; 359 as_bitmap.setColor4f({1,0,0,1}, p3.get()); 360 as_bitmap.setFilterQuality(kLow_SkFilterQuality); 361 362 SkPaint as_shader; 363 as_shader.setColor4f({1,0,0,1}, p3.get()); 364 as_shader.setFilterQuality(kLow_SkFilterQuality); 365 as_shader.setShader(bm.makeShader()); 366 367 canvas->drawBitmap(bm, 10,10, &as_bitmap); 368 compare_pixel("A8 sprite bitmap P3 red", 369 canvas, 10,10, 370 {1,0,0,1}, p3.get()); 371 372 canvas->translate(0, 80); 373 374 canvas->save(); 375 canvas->translate(10,10); 376 canvas->drawRect({0,0,16,16}, as_shader); 377 canvas->restore(); 378 compare_pixel("A8 sprite shader P3 red", 379 canvas, 10,10, 380 {1,0,0,1}, p3.get()); 381 382 canvas->translate(0,80); 383 384 canvas->drawBitmapRect(bm, {10,10,70,70}, &as_bitmap); 385 compare_pixel("A8 scaled bitmap P3 red", 386 canvas, 10,10, 387 {1,0,0,1}, p3.get()); 388 389 canvas->translate(0,80); 390 391 canvas->save(); 392 canvas->translate(10,10); 393 canvas->scale(3.75,3.75); 394 canvas->drawRect({0,0,16,16}, as_shader); 395 canvas->restore(); 396 compare_pixel("A8 scaled shader P3 red", 397 canvas, 10,10, 398 {1,0,0,1}, p3.get()); 399 } 400 401 // TODO: draw P3 colors more ways 402 } 403 404 DEF_SIMPLE_GM(p3_ovals, canvas, 450, 320) { 405 auto p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); 406 407 // Test cases that exercise each Op in GrOvalOpFactory.cpp 408 409 // Draw a circle and check the center (CircleOp) 410 { 411 SkPaint paint; 412 paint.setAntiAlias(true); 413 paint.setColor4f({ 1,0,0,1 }, p3.get()); 414 415 canvas->drawCircle(40, 40, 30, paint); 416 compare_pixel("drawCircle P3 red ", 417 canvas, 40, 40, 418 { 1,0,0,1 }, p3.get()); 419 } 420 421 canvas->translate(0, 80); 422 423 // Draw an oval and check the center (EllipseOp) 424 { 425 SkPaint paint; 426 paint.setAntiAlias(true); 427 paint.setColor4f({ 1,0,0,1 }, p3.get()); 428 429 canvas->drawOval({ 20,10,60,70 }, paint); 430 compare_pixel("drawOval P3 red ", 431 canvas, 40, 40, 432 { 1,0,0,1 }, p3.get()); 433 } 434 435 canvas->translate(0, 80); 436 437 // Draw a butt-capped dashed circle and check the top of the stroke (ButtCappedDashedCircleOp) 438 { 439 SkPaint paint; 440 paint.setAntiAlias(true); 441 paint.setColor4f({ 1,0,0,1 }, p3.get()); 442 paint.setStyle(SkPaint::kStroke_Style); 443 float intervals[] = { 70, 10 }; 444 paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); 445 paint.setStrokeWidth(10); 446 447 canvas->drawCircle(40, 40, 30, paint); 448 compare_pixel("drawDashedCircle P3 red ", 449 canvas, 40, 10, 450 { 1,0,0,1 }, p3.get()); 451 } 452 453 canvas->translate(0, 80); 454 455 // Draw an oval with rotation and check the center (DIEllipseOp) 456 { 457 SkPaint paint; 458 paint.setAntiAlias(true); 459 paint.setColor4f({ 1,0,0,1 }, p3.get()); 460 461 canvas->save(); 462 canvas->translate(40, 40); 463 canvas->rotate(45); 464 canvas->drawOval({ -20,-30,20,30 }, paint); 465 canvas->restore(); 466 compare_pixel("drawRotatedOval P3 red ", 467 canvas, 40, 40, 468 { 1,0,0,1 }, p3.get()); 469 } 470 471 canvas->translate(0, 80); 472 } 473