odin-blend2d

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

bl_bench_backend_cairo.cpp (17642B)


      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_CAIRO
      7 
      8 #include "bl_bench_app.h"
      9 #include "bl_bench_backend.h"
     10 
     11 #include <algorithm>
     12 #include <cairo.h>
     13 
     14 namespace blbench {
     15 
     16 static inline double u8_to_unit(int x) {
     17   constexpr double kDiv255 = 1.0 / 255.0;
     18   return double(x) * kDiv255;
     19 }
     20 
     21 static uint32_t to_cairo_format(uint32_t format) {
     22   switch (format) {
     23     case BL_FORMAT_PRGB32: return CAIRO_FORMAT_ARGB32;
     24     case BL_FORMAT_XRGB32: return CAIRO_FORMAT_RGB24;
     25     case BL_FORMAT_A8    : return CAIRO_FORMAT_A8;
     26     default:
     27       return 0xFFFFFFFFu;
     28   }
     29 }
     30 
     31 static uint32_t to_cairo_operator(uint32_t comp_op) {
     32   switch (comp_op) {
     33     case BL_COMP_OP_SRC_OVER   : return CAIRO_OPERATOR_OVER;
     34     case BL_COMP_OP_SRC_COPY   : return CAIRO_OPERATOR_SOURCE;
     35     case BL_COMP_OP_SRC_IN     : return CAIRO_OPERATOR_IN;
     36     case BL_COMP_OP_SRC_OUT    : return CAIRO_OPERATOR_OUT;
     37     case BL_COMP_OP_SRC_ATOP   : return CAIRO_OPERATOR_ATOP;
     38     case BL_COMP_OP_DST_OVER   : return CAIRO_OPERATOR_DEST_OVER;
     39     case BL_COMP_OP_DST_COPY   : return CAIRO_OPERATOR_DEST;
     40     case BL_COMP_OP_DST_IN     : return CAIRO_OPERATOR_DEST_IN;
     41     case BL_COMP_OP_DST_OUT    : return CAIRO_OPERATOR_DEST_OUT;
     42     case BL_COMP_OP_DST_ATOP   : return CAIRO_OPERATOR_DEST_ATOP;
     43     case BL_COMP_OP_XOR        : return CAIRO_OPERATOR_XOR;
     44     case BL_COMP_OP_CLEAR      : return CAIRO_OPERATOR_CLEAR;
     45     case BL_COMP_OP_PLUS       : return CAIRO_OPERATOR_ADD;
     46     case BL_COMP_OP_MULTIPLY   : return CAIRO_OPERATOR_MULTIPLY;
     47     case BL_COMP_OP_SCREEN     : return CAIRO_OPERATOR_SCREEN;
     48     case BL_COMP_OP_OVERLAY    : return CAIRO_OPERATOR_OVERLAY;
     49     case BL_COMP_OP_DARKEN     : return CAIRO_OPERATOR_DARKEN;
     50     case BL_COMP_OP_LIGHTEN    : return CAIRO_OPERATOR_LIGHTEN;
     51     case BL_COMP_OP_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE;
     52     case BL_COMP_OP_COLOR_BURN : return CAIRO_OPERATOR_COLOR_BURN;
     53     case BL_COMP_OP_HARD_LIGHT : return CAIRO_OPERATOR_HARD_LIGHT;
     54     case BL_COMP_OP_SOFT_LIGHT : return CAIRO_OPERATOR_SOFT_LIGHT;
     55     case BL_COMP_OP_DIFFERENCE : return CAIRO_OPERATOR_DIFFERENCE;
     56     case BL_COMP_OP_EXCLUSION  : return CAIRO_OPERATOR_EXCLUSION;
     57 
     58     default:
     59       return 0xFFFFFFFFu;
     60   }
     61 }
     62 
     63 static void round_rect(cairo_t* ctx, const BLRect& rect, double radius) {
     64   double rw2 = rect.w * 0.5;
     65   double rh2 = rect.h * 0.5;
     66 
     67   double rx = std::min(bl_abs(radius), rw2);
     68   double ry = std::min(bl_abs(radius), rh2);
     69 
     70   double kappa_inv = 1 - 0.551915024494;
     71   double kx = rx * kappa_inv;
     72   double ky = ry * kappa_inv;
     73 
     74   double x0 = rect.x;
     75   double y0 = rect.y;
     76   double x1 = rect.x + rect.w;
     77   double y1 = rect.y + rect.h;
     78 
     79   cairo_move_to(ctx, x0 + rx, y0);
     80   cairo_line_to(ctx, x1 - rx, y0);
     81   cairo_curve_to(ctx, x1 - kx, y0, x1, y0 + ky, x1, y0 + ry);
     82 
     83   cairo_line_to(ctx, x1, y1 - ry);
     84   cairo_curve_to(ctx, x1, y1 - ky, x1 - kx, y1, x1 - rx, y1);
     85 
     86   cairo_line_to(ctx, x0 + rx, y1);
     87   cairo_curve_to(ctx, x0 + kx, y1, x0, y1 - ky, x0, y1 - ry);
     88 
     89   cairo_line_to(ctx, x0, y0 + ry);
     90   cairo_curve_to(ctx, x0, y0 + ky, x0 + kx, y0, x0 + rx, y0);
     91 
     92   cairo_close_path(ctx);
     93 }
     94 
     95 struct CairoModule : public Backend {
     96   cairo_surface_t* _cairo_surface {};
     97   cairo_surface_t* _cairo_sprites[kBenchNumSprites] {};
     98   cairo_t* _cairo_ctx {};
     99 
    100   // Initialized by before_run().
    101   uint32_t _pattern_extend {};
    102   uint32_t _pattern_filter {};
    103 
    104   CairoModule();
    105   ~CairoModule() override;
    106 
    107   void serialize_info(JSONBuilder& json) const override;
    108 
    109   template<typename RectT>
    110   void setup_style(StyleKind style, const RectT& rect);
    111 
    112   bool supports_comp_op(BLCompOp comp_op) const override;
    113   bool supports_style(StyleKind style) const override;
    114 
    115   void before_run() override;
    116   void flush() override;
    117   void after_run() override;
    118 
    119   void render_rect_a(RenderOp op) override;
    120   void render_rect_f(RenderOp op) override;
    121   void render_rect_rotated(RenderOp op) override;
    122   void render_round_f(RenderOp op) override;
    123   void render_round_rotated(RenderOp op) override;
    124   void render_polygon(RenderOp op, uint32_t complexity) override;
    125   void render_shape(RenderOp op, ShapeData shape) override;
    126 };
    127 
    128 CairoModule::CairoModule() {
    129   strcpy(_name, "Cairo");
    130 }
    131 CairoModule::~CairoModule() {}
    132 
    133 void CairoModule::serialize_info(JSONBuilder& json) const {
    134   json.before_record()
    135       .add_key("version")
    136       .add_string(cairo_version_string());
    137 }
    138 
    139 template<typename RectT>
    140 void CairoModule::setup_style(StyleKind style, const RectT& rect) {
    141   switch (style) {
    142     case StyleKind::kSolid: {
    143       BLRgba32 c(_rnd_color.next_rgba32());
    144       cairo_set_source_rgba(_cairo_ctx, u8_to_unit(c.r()), u8_to_unit(c.g()), u8_to_unit(c.b()), u8_to_unit(c.a()));
    145       return;
    146     }
    147 
    148     case StyleKind::kLinearPad:
    149     case StyleKind::kLinearRepeat:
    150     case StyleKind::kLinearReflect:
    151     case StyleKind::kRadialPad:
    152     case StyleKind::kRadialRepeat:
    153     case StyleKind::kRadialReflect: {
    154       double x = rect.x;
    155       double y = rect.y;
    156 
    157       BLRgba32 c0(_rnd_color.next_rgba32());
    158       BLRgba32 c1(_rnd_color.next_rgba32());
    159       BLRgba32 c2(_rnd_color.next_rgba32());
    160 
    161       cairo_pattern_t* pattern {};
    162       if (style < StyleKind::kRadialPad) {
    163         // Linear gradient.
    164         double x0 = rect.x + rect.w * 0.2;
    165         double y0 = rect.y + rect.h * 0.2;
    166         double x1 = rect.x + rect.w * 0.8;
    167         double y1 = rect.y + rect.h * 0.8;
    168         pattern = cairo_pattern_create_linear(x0, y0, x1, y1);
    169 
    170         cairo_pattern_add_color_stop_rgba(pattern, 0.0, u8_to_unit(c0.r()), u8_to_unit(c0.g()), u8_to_unit(c0.b()), u8_to_unit(c0.a()));
    171         cairo_pattern_add_color_stop_rgba(pattern, 0.5, u8_to_unit(c1.r()), u8_to_unit(c1.g()), u8_to_unit(c1.b()), u8_to_unit(c1.a()));
    172         cairo_pattern_add_color_stop_rgba(pattern, 1.0, u8_to_unit(c2.r()), u8_to_unit(c2.g()), u8_to_unit(c2.b()), u8_to_unit(c2.a()));
    173       }
    174       else {
    175         // Radial gradient.
    176         x += double(rect.w) / 2.0;
    177         y += double(rect.h) / 2.0;
    178 
    179         double r = double(rect.w + rect.h) / 4.0;
    180         pattern = cairo_pattern_create_radial(x, y, r, x - r / 2, y - r / 2, 0.0);
    181 
    182         // Color stops in Cairo's radial gradient are reverse to Blend/Qt.
    183         cairo_pattern_add_color_stop_rgba(pattern, 0.0, u8_to_unit(c2.r()), u8_to_unit(c2.g()), u8_to_unit(c2.b()), u8_to_unit(c2.a()));
    184         cairo_pattern_add_color_stop_rgba(pattern, 0.5, u8_to_unit(c1.r()), u8_to_unit(c1.g()), u8_to_unit(c1.b()), u8_to_unit(c1.a()));
    185         cairo_pattern_add_color_stop_rgba(pattern, 1.0, u8_to_unit(c0.r()), u8_to_unit(c0.g()), u8_to_unit(c0.b()), u8_to_unit(c0.a()));
    186       }
    187 
    188       cairo_pattern_set_extend(pattern, cairo_extend_t(_pattern_extend));
    189       cairo_set_source(_cairo_ctx, pattern);
    190       cairo_pattern_destroy(pattern);
    191       return;
    192     }
    193 
    194     case StyleKind::kPatternNN:
    195     case StyleKind::kPatternBI: {
    196       // Matrix associated with cairo_pattern_t is inverse to Blend/Qt.
    197       cairo_matrix_t matrix;
    198       cairo_matrix_init_translate(&matrix, -rect.x, -rect.y);
    199 
    200       cairo_pattern_t* pattern = cairo_pattern_create_for_surface(_cairo_sprites[nextSpriteId()]);
    201       cairo_pattern_set_matrix(pattern, &matrix);
    202       cairo_pattern_set_extend(pattern, cairo_extend_t(_pattern_extend));
    203       cairo_pattern_set_filter(pattern, cairo_filter_t(_pattern_filter));
    204 
    205       cairo_set_source(_cairo_ctx, pattern);
    206       cairo_pattern_destroy(pattern);
    207       return;
    208     }
    209 
    210     default: {
    211       return;
    212     }
    213   }
    214 }
    215 
    216 bool CairoModule::supports_comp_op(BLCompOp comp_op) const {
    217   return to_cairo_operator(comp_op) != 0xFFFFFFFFu;
    218 }
    219 
    220 bool CairoModule::supports_style(StyleKind style) const {
    221   return style == StyleKind::kSolid         ||
    222          style == StyleKind::kLinearPad     ||
    223          style == StyleKind::kLinearRepeat  ||
    224          style == StyleKind::kLinearReflect ||
    225          style == StyleKind::kRadialPad     ||
    226          style == StyleKind::kRadialRepeat  ||
    227          style == StyleKind::kRadialReflect ||
    228          style == StyleKind::kPatternNN     ||
    229          style == StyleKind::kPatternBI     ;
    230 }
    231 
    232 void CairoModule::before_run() {
    233   int w = int(_params.screen_w);
    234   int h = int(_params.screen_h);
    235   StyleKind style = _params.style;
    236 
    237   // Initialize the sprites.
    238   for (uint32_t i = 0; i < kBenchNumSprites; i++) {
    239     const BLImage& sprite = _sprites[i];
    240 
    241     BLImageData sprite_data;
    242     sprite.get_data(&sprite_data);
    243 
    244     int stride = int(sprite_data.stride);
    245     int format = to_cairo_format(sprite_data.format);
    246     unsigned char* pixels = static_cast<unsigned char*>(sprite_data.pixel_data);
    247 
    248     cairo_surface_t* cairo_sprite = cairo_image_surface_create_for_data(
    249       pixels, cairo_format_t(format), sprite_data.size.w, sprite_data.size.h, stride);
    250 
    251     _cairo_sprites[i] = cairo_sprite;
    252   }
    253 
    254   // Initialize the surface and the context.
    255   {
    256     BLImageData surface_data;
    257     _surface.create(w, h, _params.format);
    258     _surface.make_mutable(&surface_data);
    259 
    260     int stride = int(surface_data.stride);
    261     int format = to_cairo_format(surface_data.format);
    262     unsigned char* pixels = (unsigned char*)surface_data.pixel_data;
    263 
    264     _cairo_surface = cairo_image_surface_create_for_data(
    265       pixels, cairo_format_t(format), w, h, stride);
    266 
    267     if (_cairo_surface == nullptr) {
    268       return;
    269     }
    270 
    271     _cairo_ctx = cairo_create(_cairo_surface);
    272     if (_cairo_ctx == nullptr) {
    273       return;
    274     }
    275   }
    276 
    277   // Setup the context.
    278   cairo_set_operator(_cairo_ctx, CAIRO_OPERATOR_CLEAR);
    279   cairo_rectangle(_cairo_ctx, 0, 0, w, h);
    280   cairo_fill(_cairo_ctx);
    281 
    282   cairo_set_operator(_cairo_ctx, cairo_operator_t(to_cairo_operator(_params.comp_op)));
    283   cairo_set_line_width(_cairo_ctx, _params.stroke_width);
    284 
    285   // Setup globals.
    286   _pattern_extend = CAIRO_EXTEND_REPEAT;
    287   _pattern_filter = CAIRO_FILTER_NEAREST;
    288 
    289   switch (style) {
    290     case StyleKind::kSolid          : break;
    291     case StyleKind::kLinearPad      : _pattern_extend = CAIRO_EXTEND_PAD     ; break;
    292     case StyleKind::kLinearRepeat   : _pattern_extend = CAIRO_EXTEND_REPEAT  ; break;
    293     case StyleKind::kLinearReflect  : _pattern_extend = CAIRO_EXTEND_REFLECT ; break;
    294     case StyleKind::kRadialPad      : _pattern_extend = CAIRO_EXTEND_PAD     ; break;
    295     case StyleKind::kRadialRepeat   : _pattern_extend = CAIRO_EXTEND_REPEAT  ; break;
    296     case StyleKind::kRadialReflect  : _pattern_extend = CAIRO_EXTEND_REFLECT ; break;
    297     case StyleKind::kPatternNN      : _pattern_filter = CAIRO_FILTER_NEAREST ; break;
    298     case StyleKind::kPatternBI      : _pattern_filter = CAIRO_FILTER_BILINEAR; break;
    299 
    300     // These are not supported.
    301     case StyleKind::kConic:
    302       break;
    303   }
    304 }
    305 
    306 void CairoModule::flush() {
    307   // Nothing...
    308 }
    309 
    310 void CairoModule::after_run() {
    311   // Free the surface & the context.
    312   cairo_destroy(_cairo_ctx);
    313   cairo_surface_destroy(_cairo_surface);
    314 
    315   _cairo_ctx = nullptr;
    316   _cairo_surface = nullptr;
    317 
    318   // Free the sprites.
    319   for (uint32_t i = 0; i < kBenchNumSprites; i++) {
    320     cairo_surface_destroy(_cairo_sprites[i]);
    321     _cairo_sprites[i] = nullptr;
    322   }
    323 }
    324 
    325 void CairoModule::render_rect_a(RenderOp op) {
    326   BLSizeI bounds(_params.screen_w, _params.screen_h);
    327   StyleKind style = _params.style;
    328 
    329   int wh = _params.shape_size;
    330 
    331   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    332     BLRectI rect(_rnd_coord.next_rect_i(bounds, wh, wh));
    333     setup_style<BLRectI>(style, rect);
    334 
    335     if (op == RenderOp::kStroke) {
    336       cairo_rectangle(_cairo_ctx, rect.x, rect.y, rect.w, rect.h);
    337       cairo_stroke(_cairo_ctx);
    338     }
    339     else {
    340       cairo_rectangle(_cairo_ctx, rect.x, rect.y, rect.w, rect.h);
    341       cairo_fill(_cairo_ctx);
    342     }
    343   }
    344 }
    345 
    346 void CairoModule::render_rect_f(RenderOp op) {
    347   BLSize bounds(_params.screen_w, _params.screen_h);
    348   StyleKind style = _params.style;
    349 
    350   double wh = _params.shape_size;
    351 
    352   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    353     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    354 
    355     setup_style<BLRect>(style, rect);
    356     cairo_rectangle(_cairo_ctx, rect.x, rect.y, rect.w, rect.h);
    357 
    358     if (op == RenderOp::kStroke) {
    359       cairo_stroke(_cairo_ctx);
    360     }
    361     else {
    362       cairo_fill(_cairo_ctx);
    363     }
    364   }
    365 }
    366 
    367 void CairoModule::render_rect_rotated(RenderOp op) {
    368   BLSize bounds(_params.screen_w, _params.screen_h);
    369   StyleKind style = _params.style;
    370 
    371   double cx = double(_params.screen_w) * 0.5;
    372   double cy = double(_params.screen_h) * 0.5;
    373   double wh = _params.shape_size;
    374   double angle = 0.0;
    375 
    376   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) {
    377     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    378 
    379     cairo_translate(_cairo_ctx, cx, cy);
    380     cairo_rotate(_cairo_ctx, angle);
    381     cairo_translate(_cairo_ctx, -cx, -cy);
    382 
    383     setup_style<BLRect>(style, rect);
    384     cairo_rectangle(_cairo_ctx, rect.x, rect.y, rect.w, rect.h);
    385 
    386     if (op == RenderOp::kStroke) {
    387       cairo_stroke(_cairo_ctx);
    388     }
    389     else {
    390       cairo_fill(_cairo_ctx);
    391     }
    392 
    393     cairo_identity_matrix(_cairo_ctx);
    394   }
    395 }
    396 
    397 void CairoModule::render_round_f(RenderOp op) {
    398   BLSize bounds(_params.screen_w, _params.screen_h);
    399   StyleKind style = _params.style;
    400 
    401   double wh = _params.shape_size;
    402 
    403   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    404     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    405     double radius = _rnd_extra.next_double(4.0, 40.0);
    406 
    407     setup_style<BLRect>(style, rect);
    408     round_rect(_cairo_ctx, rect, radius);
    409 
    410     if (op == RenderOp::kStroke) {
    411       cairo_stroke(_cairo_ctx);
    412     }
    413     else {
    414       cairo_fill(_cairo_ctx);
    415     }
    416   }
    417 }
    418 
    419 void CairoModule::render_round_rotated(RenderOp op) {
    420   BLSize bounds(_params.screen_w, _params.screen_h);
    421   StyleKind style = _params.style;
    422 
    423   double cx = double(_params.screen_w) * 0.5;
    424   double cy = double(_params.screen_h) * 0.5;
    425   double wh = _params.shape_size;
    426   double angle = 0.0;
    427 
    428   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) {
    429     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    430     double radius = _rnd_extra.next_double(4.0, 40.0);
    431 
    432     cairo_translate(_cairo_ctx, cx, cy);
    433     cairo_rotate(_cairo_ctx, angle);
    434     cairo_translate(_cairo_ctx, -cx, -cy);
    435 
    436     setup_style<BLRect>(style, rect);
    437     round_rect(_cairo_ctx, rect, radius);
    438 
    439     if (op == RenderOp::kStroke) {
    440       cairo_stroke(_cairo_ctx);
    441     }
    442     else {
    443       cairo_fill(_cairo_ctx);
    444     }
    445 
    446     cairo_identity_matrix(_cairo_ctx);
    447   }
    448 }
    449 
    450 void CairoModule::render_polygon(RenderOp op, uint32_t complexity) {
    451   BLSizeI bounds(_params.screen_w - _params.shape_size,
    452                  _params.screen_h - _params.shape_size);
    453   StyleKind style = _params.style;
    454   double wh = double(_params.shape_size);
    455 
    456   cairo_set_fill_rule(_cairo_ctx, op == RenderOp::kFillEvenOdd ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
    457 
    458   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    459     BLPoint base(_rnd_coord.nextPoint(bounds));
    460 
    461     double x = _rnd_coord.next_double(base.x, base.x + wh);
    462     double y = _rnd_coord.next_double(base.y, base.y + wh);
    463 
    464     cairo_move_to(_cairo_ctx, x, y);
    465     for (uint32_t p = 1; p < complexity; p++) {
    466       x = _rnd_coord.next_double(base.x, base.x + wh);
    467       y = _rnd_coord.next_double(base.y, base.y + wh);
    468       cairo_line_to(_cairo_ctx, x, y);
    469     }
    470     setup_style<BLRect>(style, BLRect(base.x, base.y, wh, wh));
    471 
    472     if (op == RenderOp::kStroke) {
    473       cairo_stroke(_cairo_ctx);
    474     }
    475     else {
    476       cairo_fill(_cairo_ctx);
    477     }
    478   }
    479 }
    480 
    481 void CairoModule::render_shape(RenderOp op, ShapeData shape) {
    482   BLSizeI bounds(_params.screen_w - _params.shape_size,
    483                  _params.screen_h - _params.shape_size);
    484   StyleKind style = _params.style;
    485   double wh = double(_params.shape_size);
    486 
    487   ShapeIterator it(shape);
    488   while (it.has_command()) {
    489     if (it.is_move_to()) {
    490       cairo_move_to(_cairo_ctx, it.x(0) * wh, it.y(0) * wh);
    491     }
    492     else if (it.is_line_to()) {
    493       cairo_line_to(_cairo_ctx, it.x(0) * wh, it.y(0) * wh);
    494     }
    495     else if (it.is_quad_to()) {
    496       double x0 = it.x(-1) * wh;
    497       double y0 = it.y(-1) * wh;
    498       double x1 = it.x(0) * wh;
    499       double y1 = it.y(0) * wh;
    500       double x2 = it.x(1) * wh;
    501       double y2 = it.y(1) * wh;
    502 
    503       cairo_curve_to(_cairo_ctx,
    504         (2.0 / 3.0) * x1 + (1.0 / 3.0) * x0, (2.0 / 3.0) * y1 + (1.0 / 3.0) * y0,
    505         (2.0 / 3.0) * x1 + (1.0 / 3.0) * x2, (2.0 / 3.0) * y1 + (1.0 / 3.0) * y2,
    506         y1, y2);
    507     }
    508     else if (it.is_cubic_to()) {
    509       cairo_curve_to(_cairo_ctx,
    510         it.x(0) * wh, it.y(0) * wh,
    511         it.x(1) * wh, it.y(1) * wh,
    512         it.x(2) * wh, it.y(2) * wh);
    513     }
    514     else {
    515       cairo_close_path(_cairo_ctx);
    516     }
    517     it.next();
    518   }
    519 
    520   // No idea who invented this, but you need a `cairo_t` to create a `cairo_path_t`.
    521   cairo_path_t* path = cairo_copy_path(_cairo_ctx);
    522   cairo_new_path(_cairo_ctx);
    523 
    524   cairo_set_fill_rule(_cairo_ctx, op == RenderOp::kFillEvenOdd ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
    525 
    526   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    527     cairo_save(_cairo_ctx);
    528 
    529     BLPoint base(_rnd_coord.nextPoint(bounds));
    530     setup_style<BLRect>(style, BLRect(base.x, base.y, wh, wh));
    531 
    532     cairo_translate(_cairo_ctx, base.x, base.y);
    533     cairo_append_path(_cairo_ctx, path);
    534 
    535     if (op == RenderOp::kStroke) {
    536       cairo_stroke(_cairo_ctx);
    537     }
    538     else {
    539       cairo_fill(_cairo_ctx);
    540     }
    541 
    542     cairo_restore(_cairo_ctx);
    543   }
    544 
    545   cairo_path_destroy(path);
    546 }
    547 
    548 Backend* create_cairo_backend() {
    549   return new CairoModule();
    550 }
    551 } // {blbench}
    552 
    553 #endif // BL_BENCH_ENABLE_CAIRO