odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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