bl_bench_backend_skia.cpp (17689B)
1 // This file is part of Blend2D project <https://blend2d.com> 2 // 3 // See LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #ifdef BL_BENCH_ENABLE_SKIA 7 8 #include "bl_bench_app.h" 9 #include "bl_bench_backend.h" 10 11 #include <skia/core/SkBitmap.h> 12 #include <skia/core/SkCanvas.h> 13 #include <skia/core/SkColor.h> 14 #include <skia/core/SkImageInfo.h> 15 #include <skia/core/SkPaint.h> 16 #include <skia/core/SkPath.h> 17 #include <skia/core/SkTypes.h> 18 #include <skia/effects/SkGradientShader.h> 19 20 namespace blbench { 21 22 static inline SkIRect to_sk_irect(const BLRectI& rect) { 23 return SkIRect::MakeXYWH(rect.x, rect.y, rect.w, rect.h); 24 } 25 26 static inline SkRect to_sk_rect(const BLRect& rect) { 27 return SkRect::MakeXYWH(SkScalar(rect.x), SkScalar(rect.y), SkScalar(rect.w), SkScalar(rect.h)); 28 } 29 30 static uint32_t to_sk_blend_mode(BLCompOp comp_op) { 31 switch (comp_op) { 32 case BL_COMP_OP_SRC_OVER : return uint32_t(SkBlendMode::kSrcOver); 33 case BL_COMP_OP_SRC_COPY : return uint32_t(SkBlendMode::kSrc); 34 case BL_COMP_OP_SRC_IN : return uint32_t(SkBlendMode::kSrcIn); 35 case BL_COMP_OP_SRC_OUT : return uint32_t(SkBlendMode::kSrcOut); 36 case BL_COMP_OP_SRC_ATOP : return uint32_t(SkBlendMode::kSrcATop); 37 case BL_COMP_OP_DST_OVER : return uint32_t(SkBlendMode::kDstOver); 38 case BL_COMP_OP_DST_COPY : return uint32_t(SkBlendMode::kDst); 39 case BL_COMP_OP_DST_IN : return uint32_t(SkBlendMode::kDstIn); 40 case BL_COMP_OP_DST_OUT : return uint32_t(SkBlendMode::kDstOut); 41 case BL_COMP_OP_DST_ATOP : return uint32_t(SkBlendMode::kDstATop); 42 case BL_COMP_OP_XOR : return uint32_t(SkBlendMode::kXor); 43 case BL_COMP_OP_CLEAR : return uint32_t(SkBlendMode::kClear); 44 case BL_COMP_OP_PLUS : return uint32_t(SkBlendMode::kPlus); 45 case BL_COMP_OP_MODULATE : return uint32_t(SkBlendMode::kModulate); 46 case BL_COMP_OP_MULTIPLY : return uint32_t(SkBlendMode::kMultiply); 47 case BL_COMP_OP_SCREEN : return uint32_t(SkBlendMode::kScreen); 48 case BL_COMP_OP_OVERLAY : return uint32_t(SkBlendMode::kOverlay); 49 case BL_COMP_OP_DARKEN : return uint32_t(SkBlendMode::kDarken); 50 case BL_COMP_OP_LIGHTEN : return uint32_t(SkBlendMode::kLighten); 51 case BL_COMP_OP_COLOR_DODGE: return uint32_t(SkBlendMode::kColorDodge); 52 case BL_COMP_OP_COLOR_BURN : return uint32_t(SkBlendMode::kColorBurn); 53 case BL_COMP_OP_HARD_LIGHT : return uint32_t(SkBlendMode::kHardLight); 54 case BL_COMP_OP_SOFT_LIGHT : return uint32_t(SkBlendMode::kSoftLight); 55 case BL_COMP_OP_DIFFERENCE : return uint32_t(SkBlendMode::kDifference); 56 case BL_COMP_OP_EXCLUSION : return uint32_t(SkBlendMode::kExclusion); 57 58 default: return 0xFFFFFFFFu; 59 } 60 } 61 62 struct SkiaModule final : public Backend { 63 SkCanvas* _sk_canvas {}; 64 SkBitmap _sk_surface; 65 SkBitmap _sk_sprites[4]; 66 67 SkBlendMode _blend_mode {}; 68 SkTileMode _gradient_tile_mode {}; 69 70 SkiaModule(); 71 ~SkiaModule() override; 72 73 template<typename RectT> 74 sk_sp<SkShader> create_shader(StyleKind style, const RectT& rect); 75 76 bool supports_comp_op(BLCompOp comp_op) const override; 77 bool supports_style(StyleKind style) const override; 78 79 void before_run() override; 80 void flush() override; 81 void after_run() override; 82 83 void render_rect_a(RenderOp op) override; 84 void render_rect_f(RenderOp op) override; 85 void render_rect_rotated(RenderOp op) override; 86 void render_round_f(RenderOp op) override; 87 void render_round_rotated(RenderOp op) override; 88 void render_polygon(RenderOp op, uint32_t complexity) override; 89 void render_shape(RenderOp op, ShapeData shape) override; 90 }; 91 92 SkiaModule::SkiaModule() { 93 strcpy(_name, "Skia"); 94 } 95 SkiaModule::~SkiaModule() {} 96 97 template<typename RectT> 98 sk_sp<SkShader> SkiaModule::create_shader(StyleKind style, const RectT& rect) { 99 static const SkScalar positions3[3] = { 100 SkScalar(0.0), 101 SkScalar(0.5), 102 SkScalar(1.0) 103 }; 104 105 static const SkScalar positions4[4] = { 106 SkScalar(0.0), 107 SkScalar(0.33), 108 SkScalar(0.66), 109 SkScalar(1.0) 110 }; 111 112 switch (style) { 113 case StyleKind::kLinearPad: 114 case StyleKind::kLinearRepeat: 115 case StyleKind::kLinearReflect: { 116 SkPoint pts[2] = { 117 SkScalar(rect.x + rect.w * 0.2), 118 SkScalar(rect.y + rect.h * 0.2), 119 SkScalar(rect.x + rect.w * 0.8), 120 SkScalar(rect.y + rect.h * 0.8) 121 }; 122 123 SkColor colors[3] = { 124 _rnd_color.next_rgba32().value, 125 _rnd_color.next_rgba32().value, 126 _rnd_color.next_rgba32().value 127 }; 128 129 return SkGradientShader::MakeLinear(pts, colors, positions3, 3, _gradient_tile_mode); 130 } 131 132 case StyleKind::kRadialPad: 133 case StyleKind::kRadialRepeat: 134 case StyleKind::kRadialReflect: { 135 double cx = rect.x + rect.w / 2.0; 136 double cy = rect.y + rect.h / 2.0; 137 double cr = (rect.w + rect.h) / 4.0; 138 double fx = cx - cr / 2; 139 double fy = cy - cr / 2; 140 141 SkColor colors[3] = { 142 _rnd_color.next_rgba32().value, 143 _rnd_color.next_rgba32().value, 144 _rnd_color.next_rgba32().value 145 }; 146 147 return SkGradientShader::MakeTwoPointConical( 148 SkPoint::Make(SkScalar(cx), SkScalar(cy)), 149 SkScalar(cr), 150 SkPoint::Make(SkScalar(fx), SkScalar(fy)), 151 SkScalar(0.0), 152 colors, positions3, 3, _gradient_tile_mode); 153 } 154 155 case StyleKind::kConic: { 156 double cx = rect.x + rect.w / 2; 157 double cy = rect.y + rect.h / 2; 158 BLRgba32 c = _rnd_color.next_rgba32(); 159 160 SkColor colors[4] = { 161 c.value, 162 _rnd_color.next_rgba32().value, 163 _rnd_color.next_rgba32().value, 164 c.value 165 }; 166 167 return SkGradientShader::MakeSweep(SkScalar(cx), SkScalar(cy), colors, positions4, 4); 168 } 169 170 case StyleKind::kPatternNN: 171 case StyleKind::kPatternBI: { 172 uint32_t sprite_id = nextSpriteId(); 173 SkFilterMode filter_mode = style == StyleKind::kPatternNN ? SkFilterMode::kNearest : SkFilterMode::kLinear; 174 SkMatrix m = SkMatrix::Translate(SkScalar(rect.x), SkScalar(rect.y)); 175 return _sk_sprites[sprite_id].makeShader(SkSamplingOptions(filter_mode), m); 176 } 177 178 default: { 179 return sk_sp<SkShader>{}; 180 } 181 } 182 } 183 184 bool SkiaModule::supports_comp_op(BLCompOp comp_op) const { 185 return to_sk_blend_mode(comp_op) != 0xFFFFFFFFu; 186 } 187 188 bool SkiaModule::supports_style(StyleKind style) const { 189 return style == StyleKind::kSolid || 190 style == StyleKind::kLinearPad || 191 style == StyleKind::kLinearRepeat || 192 style == StyleKind::kLinearReflect || 193 style == StyleKind::kRadialPad || 194 style == StyleKind::kRadialRepeat || 195 style == StyleKind::kRadialReflect || 196 style == StyleKind::kConic || 197 style == StyleKind::kPatternNN || 198 style == StyleKind::kPatternBI ; 199 } 200 201 void SkiaModule::before_run() { 202 int w = int(_params.screen_w); 203 int h = int(_params.screen_h); 204 StyleKind style = _params.style; 205 206 // Initialize the sprites. 207 for (uint32_t i = 0; i < kBenchNumSprites; i++) { 208 const BLImage& sprite = _sprites[i]; 209 210 BLImageData sprite_data; 211 sprite.get_data(&sprite_data); 212 213 SkImageInfo sprite_info = SkImageInfo::Make(sprite_data.size.w, sprite_data.size.h, kBGRA_8888_SkColorType, kPremul_SkAlphaType); 214 _sk_sprites[i].installPixels(sprite_info, sprite_data.pixel_data, size_t(sprite_data.stride)); 215 } 216 217 // Initialize the surface and the context. 218 BLImageData surface_data; 219 _surface.create(w, h, _params.format); 220 _surface.make_mutable(&surface_data); 221 222 SkImageInfo surface_info = SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, kPremul_SkAlphaType); 223 _sk_surface.installPixels(surface_info, surface_data.pixel_data, size_t(surface_data.stride)); 224 _sk_surface.erase(0x00000000, SkIRect::MakeXYWH(0, 0, w, h)); 225 226 _sk_canvas = new SkCanvas(_sk_surface); 227 228 // Setup globals. 229 _blend_mode = SkBlendMode(to_sk_blend_mode(_params.comp_op)); 230 _gradient_tile_mode = SkTileMode::kClamp; 231 232 switch (style) { 233 case StyleKind::kLinearPad : _gradient_tile_mode = SkTileMode::kClamp ; break; 234 case StyleKind::kLinearRepeat : _gradient_tile_mode = SkTileMode::kRepeat; break; 235 case StyleKind::kLinearReflect: _gradient_tile_mode = SkTileMode::kMirror; break; 236 case StyleKind::kRadialPad : _gradient_tile_mode = SkTileMode::kClamp ; break; 237 case StyleKind::kRadialRepeat : _gradient_tile_mode = SkTileMode::kRepeat; break; 238 case StyleKind::kRadialReflect: _gradient_tile_mode = SkTileMode::kMirror; break; 239 240 default: 241 break; 242 } 243 } 244 245 void SkiaModule::flush() { 246 // Nothing... 247 } 248 249 void SkiaModule::after_run() { 250 delete _sk_canvas; 251 _sk_canvas = nullptr; 252 _sk_surface.reset(); 253 254 for (uint32_t i = 0; i < kBenchNumSprites; i++) { 255 _sk_sprites[i].reset(); 256 } 257 } 258 259 void SkiaModule::render_rect_a(RenderOp op) { 260 BLSizeI bounds(_params.screen_w, _params.screen_h); 261 StyleKind style = _params.style; 262 int wh = _params.shape_size; 263 264 SkPaint p; 265 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 266 p.setAntiAlias(true); 267 p.setBlendMode(_blend_mode); 268 p.setStrokeWidth(SkScalar(_params.stroke_width)); 269 270 if (style == StyleKind::kSolid) { 271 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 272 BLRectI rect = _rnd_coord.next_rect_i(bounds, wh, wh); 273 274 p.setColor(_rnd_color.next_rgba32().value); 275 _sk_canvas->drawIRect(to_sk_irect(rect), p); 276 } 277 } 278 else { 279 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 280 BLRectI rect = _rnd_coord.next_rect_i(bounds, wh, wh); 281 282 p.setShader(create_shader(style, rect)); 283 _sk_canvas->drawIRect(to_sk_irect(rect), p); 284 } 285 } 286 } 287 288 void SkiaModule::render_rect_f(RenderOp op) { 289 BLSize bounds(_params.screen_w, _params.screen_h); 290 StyleKind style = _params.style; 291 double wh = _params.shape_size; 292 293 SkPaint p; 294 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 295 p.setAntiAlias(true); 296 p.setBlendMode(_blend_mode); 297 p.setStrokeWidth(SkScalar(_params.stroke_width)); 298 299 if (style == StyleKind::kSolid) { 300 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 301 BLRect rect = _rnd_coord.next_rect(bounds, wh, wh); 302 303 p.setColor(_rnd_color.next_rgba32().value); 304 _sk_canvas->drawRect(to_sk_rect(rect), p); 305 } 306 } 307 else { 308 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 309 BLRect rect = _rnd_coord.next_rect(bounds, wh, wh); 310 311 p.setShader(create_shader(style, rect)); 312 _sk_canvas->drawRect(to_sk_rect(rect), p); 313 } 314 } 315 } 316 317 void SkiaModule::render_rect_rotated(RenderOp op) { 318 BLSize bounds(_params.screen_w, _params.screen_h); 319 StyleKind style = _params.style; 320 321 double cx = double(_params.screen_w) * 0.5; 322 double cy = double(_params.screen_h) * 0.5; 323 double wh = _params.shape_size; 324 double angle = 0.0; 325 326 SkPaint p; 327 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 328 p.setAntiAlias(true); 329 p.setBlendMode(_blend_mode); 330 p.setStrokeWidth(SkScalar(_params.stroke_width)); 331 332 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) { 333 BLRect rect = _rnd_coord.next_rect(bounds, wh, wh); 334 335 _sk_canvas->rotate(SkRadiansToDegrees(angle), SkScalar(cx), SkScalar(cy)); 336 337 if (style == StyleKind::kSolid) 338 p.setColor(_rnd_color.next_rgba32().value); 339 else 340 p.setShader(create_shader(style, rect)); 341 342 _sk_canvas->drawRect(to_sk_rect(rect), p); 343 _sk_canvas->resetMatrix(); 344 } 345 } 346 347 void SkiaModule::render_round_f(RenderOp op) { 348 BLSize bounds(_params.screen_w, _params.screen_h); 349 StyleKind style = _params.style; 350 double wh = _params.shape_size; 351 352 SkPaint p; 353 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 354 p.setAntiAlias(true); 355 p.setBlendMode(_blend_mode); 356 p.setStrokeWidth(SkScalar(_params.stroke_width)); 357 358 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 359 BLRect rect = _rnd_coord.next_rect(bounds, wh, wh); 360 double radius = _rnd_extra.next_double(4.0, 40.0); 361 362 if (style == StyleKind::kSolid) 363 p.setColor(_rnd_color.next_rgba32().value); 364 else 365 p.setShader(create_shader(style, rect)); 366 367 _sk_canvas->drawRoundRect(to_sk_rect(rect), SkScalar(radius), SkScalar(radius), p); 368 } 369 } 370 371 void SkiaModule::render_round_rotated(RenderOp op) { 372 BLSize bounds(_params.screen_w, _params.screen_h); 373 StyleKind style = _params.style; 374 375 double cx = double(_params.screen_w) * 0.5; 376 double cy = double(_params.screen_h) * 0.5; 377 double wh = _params.shape_size; 378 double angle = 0.0; 379 380 SkPaint p; 381 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 382 p.setAntiAlias(true); 383 p.setBlendMode(_blend_mode); 384 p.setStrokeWidth(SkScalar(_params.stroke_width)); 385 386 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) { 387 _sk_canvas->rotate(SkRadiansToDegrees(angle), SkScalar(cx), SkScalar(cy)); 388 389 BLRect rect = _rnd_coord.next_rect(bounds, wh, wh); 390 double radius = _rnd_extra.next_double(4.0, 40.0); 391 392 if (style == StyleKind::kSolid) 393 p.setColor(_rnd_color.next_rgba32().value); 394 else 395 p.setShader(create_shader(style, rect)); 396 397 _sk_canvas->drawRoundRect(to_sk_rect(rect), SkScalar(radius), SkScalar(radius), p); 398 _sk_canvas->resetMatrix(); 399 } 400 } 401 402 void SkiaModule::render_polygon(RenderOp op, uint32_t complexity) { 403 static constexpr uint32_t kPointCapacity = 128; 404 405 BLSizeI bounds(_params.screen_w - _params.shape_size, 406 _params.screen_h - _params.shape_size); 407 StyleKind style = _params.style; 408 double wh = double(_params.shape_size); 409 410 if (complexity > kPointCapacity) { 411 return; 412 } 413 414 SkPoint points[kPointCapacity]; 415 416 SkPaint p; 417 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 418 p.setAntiAlias(true); 419 p.setBlendMode(_blend_mode); 420 p.setStrokeWidth(SkScalar(_params.stroke_width)); 421 422 // SKIA cannot draw a polygon without having a path, so we have two cases here. 423 if (op != RenderOp::kStroke) { 424 SkPathFillType fillType = op == RenderOp::kFillEvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding; 425 426 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 427 BLPoint base(_rnd_coord.nextPoint(bounds)); 428 429 SkPath path; 430 path.setFillType(fillType); 431 432 double x, y; 433 x = _rnd_coord.next_double(base.x, base.x + wh); 434 y = _rnd_coord.next_double(base.y, base.y + wh); 435 path.moveTo(SkPoint::Make(SkScalar(x), SkScalar(y))); 436 437 for (uint32_t j = 1; j < complexity; j++) { 438 x = _rnd_coord.next_double(base.x, base.x + wh); 439 y = _rnd_coord.next_double(base.y, base.y + wh); 440 path.lineTo(SkPoint::Make(SkScalar(x), SkScalar(y))); 441 } 442 443 if (style == StyleKind::kSolid) { 444 p.setColor(_rnd_color.next_rgba32().value); 445 } 446 else { 447 BLRect rect(base.x, base.y, wh, wh); 448 p.setShader(create_shader(style, rect)); 449 } 450 451 _sk_canvas->drawPath(path, p); 452 } 453 } 454 else { 455 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 456 BLPoint base(_rnd_coord.nextPoint(bounds)); 457 458 for (uint32_t j = 0; j < complexity; j++) { 459 double x = _rnd_coord.next_double(base.x, base.x + wh); 460 double y = _rnd_coord.next_double(base.y, base.y + wh); 461 points[j].set(SkScalar(x), SkScalar(y)); 462 } 463 464 if (style == StyleKind::kSolid) { 465 p.setColor(_rnd_color.next_rgba32().value); 466 } 467 else { 468 BLRect rect(base.x, base.y, wh, wh); 469 p.setShader(create_shader(style, rect)); 470 } 471 472 _sk_canvas->drawPoints(SkCanvas::kPolygon_PointMode, complexity, points, p); 473 } 474 } 475 } 476 477 void SkiaModule::render_shape(RenderOp op, ShapeData shape) { 478 BLSizeI bounds(_params.screen_w - _params.shape_size, 479 _params.screen_h - _params.shape_size); 480 StyleKind style = _params.style; 481 double wh = double(_params.shape_size); 482 483 SkPathFillType fillType = op == RenderOp::kFillEvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding; 484 485 SkPath path; 486 path.setFillType(fillType); 487 488 ShapeIterator it(shape); 489 while (it.has_command()) { 490 if (it.is_move_to()) { 491 path.moveTo(SkScalar(it.x(0) * wh), SkScalar(it.y(0) * wh)); 492 } 493 else if (it.is_line_to()) { 494 path.lineTo(SkScalar(it.x(0) * wh), SkScalar(it.y(0) * wh)); 495 } 496 else if (it.is_quad_to()) { 497 path.quadTo( 498 SkScalar(it.x(0) * wh), SkScalar(it.y(0) * wh), 499 SkScalar(it.x(1) * wh), SkScalar(it.y(1) * wh)); 500 } 501 else if (it.is_cubic_to()) { 502 path.cubicTo( 503 SkScalar(it.x(0) * wh), SkScalar(it.y(0) * wh), 504 SkScalar(it.x(1) * wh), SkScalar(it.y(1) * wh), 505 SkScalar(it.x(2) * wh), SkScalar(it.y(2) * wh)); 506 } 507 else { 508 path.close(); 509 } 510 it.next(); 511 } 512 513 SkPaint p; 514 p.setStyle(op == RenderOp::kStroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style); 515 p.setAntiAlias(true); 516 p.setBlendMode(_blend_mode); 517 p.setStrokeWidth(SkScalar(_params.stroke_width)); 518 519 for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) { 520 BLPoint base(_rnd_coord.nextPoint(bounds)); 521 522 _sk_canvas->translate(SkScalar(base.x), SkScalar(base.y)); 523 524 if (style == StyleKind::kSolid) { 525 p.setColor(_rnd_color.next_rgba32().value); 526 } 527 else { 528 BLRect rect(0, 0, wh, wh); 529 p.setShader(create_shader(style, rect)); 530 } 531 532 _sk_canvas->drawPath(path, p); 533 _sk_canvas->resetMatrix(); 534 } 535 } 536 537 Backend* create_skia_backend() { 538 return new SkiaModule(); 539 } 540 541 } // {blbench} 542 543 #endif // BL_BENCH_ENABLE_SKIA