odin-blend2d

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

bl_bench_backend_qt.cpp (20355B)


      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_QT
      7 
      8 #include "bl_bench_app.h"
      9 #include "bl_bench_backend.h"
     10 
     11 #include <QtCore>
     12 #include <QtGui>
     13 
     14 namespace blbench {
     15 
     16 static inline QColor to_qt_color(const BLRgba32& rgba) {
     17   return QColor(rgba.r(), rgba.g(), rgba.b(), rgba.a());
     18 }
     19 
     20 static uint32_t to_qt_format(BLFormat format) {
     21   switch (format) {
     22     case BL_FORMAT_PRGB32: return QImage::Format_ARGB32_Premultiplied;
     23     case BL_FORMAT_XRGB32: return QImage::Format_RGB32;
     24 
     25     default:
     26       return 0xFFFFFFFFu;
     27   }
     28 }
     29 
     30 static uint32_t to_qt_operator(BLCompOp comp_op) {
     31   switch (comp_op) {
     32     case BL_COMP_OP_SRC_OVER   : return QPainter::CompositionMode_SourceOver;
     33     case BL_COMP_OP_SRC_COPY   : return QPainter::CompositionMode_Source;
     34     case BL_COMP_OP_SRC_IN     : return QPainter::CompositionMode_SourceIn;
     35     case BL_COMP_OP_SRC_OUT    : return QPainter::CompositionMode_SourceOut;
     36     case BL_COMP_OP_SRC_ATOP   : return QPainter::CompositionMode_SourceAtop;
     37     case BL_COMP_OP_DST_OVER   : return QPainter::CompositionMode_DestinationOver;
     38     case BL_COMP_OP_DST_COPY   : return QPainter::CompositionMode_Destination;
     39     case BL_COMP_OP_DST_IN     : return QPainter::CompositionMode_DestinationIn;
     40     case BL_COMP_OP_DST_OUT    : return QPainter::CompositionMode_DestinationOut;
     41     case BL_COMP_OP_DST_ATOP   : return QPainter::CompositionMode_DestinationAtop;
     42     case BL_COMP_OP_XOR        : return QPainter::CompositionMode_Xor;
     43     case BL_COMP_OP_CLEAR      : return QPainter::CompositionMode_Clear;
     44     case BL_COMP_OP_PLUS       : return QPainter::CompositionMode_Plus;
     45     case BL_COMP_OP_MULTIPLY   : return QPainter::CompositionMode_Multiply;
     46     case BL_COMP_OP_SCREEN     : return QPainter::CompositionMode_Screen;
     47     case BL_COMP_OP_OVERLAY    : return QPainter::CompositionMode_Overlay;
     48     case BL_COMP_OP_DARKEN     : return QPainter::CompositionMode_Darken;
     49     case BL_COMP_OP_LIGHTEN    : return QPainter::CompositionMode_Lighten;
     50     case BL_COMP_OP_COLOR_DODGE: return QPainter::CompositionMode_ColorDodge;
     51     case BL_COMP_OP_COLOR_BURN : return QPainter::CompositionMode_ColorBurn;
     52     case BL_COMP_OP_HARD_LIGHT : return QPainter::CompositionMode_HardLight;
     53     case BL_COMP_OP_SOFT_LIGHT : return QPainter::CompositionMode_SoftLight;
     54     case BL_COMP_OP_DIFFERENCE : return QPainter::CompositionMode_Difference;
     55     case BL_COMP_OP_EXCLUSION  : return QPainter::CompositionMode_Exclusion;
     56 
     57     default:
     58       return 0xFFFFFFFFu;
     59   }
     60 }
     61 
     62 struct QtModule : public Backend {
     63   QImage* _qt_surface {};
     64   QImage* _qt_sprites[kBenchNumSprites] {};
     65   QPainter* _qt_context {};
     66 
     67   // Initialized by before_run().
     68   uint32_t _gradient_spread {};
     69 
     70   QtModule();
     71   ~QtModule() override;
     72 
     73   void serialize_info(JSONBuilder& json) const override;
     74 
     75   template<typename RectT>
     76   inline QBrush create_brush(StyleKind style, const RectT& rect);
     77 
     78   bool supports_comp_op(BLCompOp comp_op) const override;
     79   bool supports_style(StyleKind style) const override;
     80 
     81   void before_run() override;
     82   void flush() override;
     83   void after_run() override;
     84 
     85   void render_rect_a(RenderOp op) override;
     86   void render_rect_f(RenderOp op) override;
     87   void render_rect_rotated(RenderOp op) override;
     88   void render_round_f(RenderOp op) override;
     89   void render_round_rotated(RenderOp op) override;
     90   void render_polygon(RenderOp op, uint32_t complexity) override;
     91   void render_shape(RenderOp op, ShapeData shape) override;
     92 };
     93 
     94 QtModule::QtModule() {
     95   strcpy(_name, "Qt6");
     96 }
     97 QtModule::~QtModule() {}
     98 
     99 void QtModule::serialize_info(JSONBuilder& json) const {
    100   json.before_record()
    101       .add_key("version")
    102       .add_string(qVersion());
    103 }
    104 
    105 template<typename RectT>
    106 inline QBrush QtModule::create_brush(StyleKind style, const RectT& rect) {
    107   switch (style) {
    108     case StyleKind::kLinearPad:
    109     case StyleKind::kLinearRepeat:
    110     case StyleKind::kLinearReflect: {
    111       double x0 = rect.x + rect.w * 0.2;
    112       double y0 = rect.y + rect.h * 0.2;
    113       double x1 = rect.x + rect.w * 0.8;
    114       double y1 = rect.y + rect.h * 0.8;
    115 
    116       QLinearGradient g((qreal)x0, (qreal)y0, (qreal)x1, (qreal)y1);
    117       g.setColorAt(qreal(0.0), to_qt_color(_rnd_color.next_rgba32()));
    118       g.setColorAt(qreal(0.5), to_qt_color(_rnd_color.next_rgba32()));
    119       g.setColorAt(qreal(1.0), to_qt_color(_rnd_color.next_rgba32()));
    120       g.setSpread(static_cast<QGradient::Spread>(_gradient_spread));
    121       return QBrush(g);
    122     }
    123 
    124     case StyleKind::kRadialPad:
    125     case StyleKind::kRadialRepeat:
    126     case StyleKind::kRadialReflect: {
    127       double cx = rect.x + rect.w / 2;
    128       double cy = rect.y + rect.h / 2;
    129       double cr = (rect.w + rect.h) / 4;
    130       double fx = cx - cr / 2;
    131       double fy = cy - cr / 2;
    132 
    133       QRadialGradient g(qreal(cx), qreal(cy), qreal(cr), qreal(fx), qreal(fy), qreal(0));
    134       g.setColorAt(qreal(0.0), to_qt_color(_rnd_color.next_rgba32()));
    135       g.setColorAt(qreal(0.5), to_qt_color(_rnd_color.next_rgba32()));
    136       g.setColorAt(qreal(1.0), to_qt_color(_rnd_color.next_rgba32()));
    137       g.setSpread(static_cast<QGradient::Spread>(_gradient_spread));
    138       return QBrush(g);
    139     }
    140 
    141     case StyleKind::kConic: {
    142       double cx = rect.x + rect.w / 2;
    143       double cy = rect.y + rect.h / 2;
    144       QColor c(to_qt_color(_rnd_color.next_rgba32()));
    145 
    146       QConicalGradient g(qreal(cx), qreal(cy), qreal(0));
    147       g.setColorAt(qreal(0.00), c);
    148       g.setColorAt(qreal(0.33), to_qt_color(_rnd_color.next_rgba32()));
    149       g.setColorAt(qreal(0.66), to_qt_color(_rnd_color.next_rgba32()));
    150       g.setColorAt(qreal(1.00), c);
    151       return QBrush(g);
    152     }
    153 
    154     case StyleKind::kPatternNN:
    155     case StyleKind::kPatternBI:
    156     default: {
    157       QBrush brush(*_qt_sprites[nextSpriteId()]);
    158 
    159       // FIXME: It seems that Qt will never use subpixel filtering when drawing
    160       // an unscaled image. The test suite, however, expects that path to be
    161       // triggered. To fix this, we scale the image slightly (it should have no
    162       // visual impact) to prevent Qt using nearest-neighbor fast-path.
    163       qreal scale =  style == StyleKind::kPatternNN ? 1.0 : 1.00001;
    164 
    165       brush.setTransform(QTransform(scale, qreal(0), qreal(0), scale, qreal(rect.x), qreal(rect.y)));
    166       return brush;
    167     }
    168   }
    169 }
    170 
    171 bool QtModule::supports_comp_op(BLCompOp comp_op) const {
    172   return to_qt_operator(comp_op) != 0xFFFFFFFFu;
    173 }
    174 
    175 bool QtModule::supports_style(StyleKind style) const {
    176   return style == StyleKind::kSolid         ||
    177          style == StyleKind::kLinearPad     ||
    178          style == StyleKind::kLinearRepeat  ||
    179          style == StyleKind::kLinearReflect ||
    180          style == StyleKind::kRadialPad     ||
    181          style == StyleKind::kRadialRepeat  ||
    182          style == StyleKind::kRadialReflect ||
    183          style == StyleKind::kConic         ||
    184          style == StyleKind::kPatternNN     ||
    185          style == StyleKind::kPatternBI     ;
    186 }
    187 
    188 void QtModule::before_run() {
    189   int w = int(_params.screen_w);
    190   int h = int(_params.screen_h);
    191   StyleKind style = _params.style;
    192 
    193   // Initialize the sprites.
    194   for (uint32_t i = 0; i < kBenchNumSprites; i++) {
    195     const BLImage& sprite = _sprites[i];
    196 
    197     BLImageData sprite_data;
    198     sprite.get_data(&sprite_data);
    199 
    200     QImage* qt_sprite = new QImage(
    201       static_cast<unsigned char*>(sprite_data.pixel_data),
    202       sprite_data.size.w,
    203       sprite_data.size.h,
    204       int(sprite_data.stride), QImage::Format_ARGB32_Premultiplied);
    205 
    206     _qt_sprites[i] = qt_sprite;
    207   }
    208 
    209   // Initialize the surface and the context.
    210   BLImageData surface_data;
    211   _surface.create(w, h, _params.format);
    212   _surface.make_mutable(&surface_data);
    213 
    214   int stride = int(surface_data.stride);
    215   int qtFormat = to_qt_format(BLFormat(surface_data.format));
    216 
    217   _qt_surface = new QImage(
    218     (unsigned char*)surface_data.pixel_data, w, h,
    219     stride, static_cast<QImage::Format>(qtFormat));
    220 
    221   if (_qt_surface == nullptr) {
    222     return;
    223   }
    224 
    225   _qt_context = new QPainter(_qt_surface);
    226 
    227   if (_qt_context == nullptr) {
    228     return;
    229   }
    230 
    231   // Setup the context.
    232   _qt_context->setCompositionMode(QPainter::CompositionMode_Source);
    233   _qt_context->fillRect(0, 0, w, h, QColor(0, 0, 0, 0));
    234 
    235   _qt_context->setCompositionMode(
    236     static_cast<QPainter::CompositionMode>(
    237       to_qt_operator(_params.comp_op)));
    238 
    239   _qt_context->setRenderHint(QPainter::Antialiasing, true);
    240   _qt_context->setRenderHint(QPainter::SmoothPixmapTransform, _params.style != StyleKind::kPatternNN);
    241 
    242   // Setup globals.
    243   _gradient_spread = QGradient::PadSpread;
    244 
    245   switch (style) {
    246     case StyleKind::kLinearPad      : _gradient_spread = QGradient::PadSpread    ; break;
    247     case StyleKind::kLinearRepeat   : _gradient_spread = QGradient::RepeatSpread ; break;
    248     case StyleKind::kLinearReflect  : _gradient_spread = QGradient::ReflectSpread; break;
    249     case StyleKind::kRadialPad      : _gradient_spread = QGradient::PadSpread    ; break;
    250     case StyleKind::kRadialRepeat   : _gradient_spread = QGradient::RepeatSpread ; break;
    251     case StyleKind::kRadialReflect  : _gradient_spread = QGradient::ReflectSpread; break;
    252 
    253     default:
    254       break;
    255   }
    256 }
    257 
    258 void QtModule::flush() {
    259   // Nothing...
    260 }
    261 
    262 void QtModule::after_run() {
    263   // Free the surface & the context.
    264   delete _qt_context;
    265   delete _qt_surface;
    266 
    267   _qt_context = nullptr;
    268   _qt_surface = nullptr;
    269 
    270   // Free the sprites.
    271   for (uint32_t i = 0; i < kBenchNumSprites; i++) {
    272     delete _qt_sprites[i];
    273     _qt_sprites[i] = nullptr;
    274   }
    275 }
    276 
    277 void QtModule::render_rect_a(RenderOp op) {
    278   BLSizeI bounds(_params.screen_w, _params.screen_h);
    279   StyleKind style = _params.style;
    280   int wh = _params.shape_size;
    281 
    282   if (op == RenderOp::kStroke)
    283     _qt_context->setBrush(Qt::NoBrush);
    284 
    285   if (style == StyleKind::kSolid) {
    286     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    287       BLRectI rect(_rnd_coord.next_rect_i(bounds, wh, wh));
    288       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    289 
    290       if (op == RenderOp::kStroke) {
    291         _qt_context->setPen(color);
    292         _qt_context->drawRect(QRectF(rect.x, rect.y, rect.w, rect.h));
    293       }
    294       else {
    295         _qt_context->fillRect(QRect(rect.x, rect.y, rect.w, rect.h), color);
    296       }
    297     }
    298   }
    299   else {
    300     if ((style == StyleKind::kPatternNN || style == StyleKind::kPatternBI) && op != RenderOp::kStroke) {
    301       for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    302         BLRectI rect(_rnd_coord.next_rect_i(bounds, wh, wh));
    303         const QImage& sprite = *_qt_sprites[nextSpriteId()];
    304 
    305         _qt_context->drawImage(QPoint(rect.x, rect.y), sprite);
    306       }
    307     }
    308     else {
    309       for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    310         BLRectI rect(_rnd_coord.next_rect_i(bounds, wh, wh));
    311         QBrush brush(create_brush<BLRectI>(style, rect));
    312 
    313         if (op == RenderOp::kStroke) {
    314           QPen pen(brush, qreal(_params.stroke_width));
    315           pen.setJoinStyle(Qt::MiterJoin);
    316           _qt_context->setPen(pen);
    317           _qt_context->drawRect(QRectF(rect.x, rect.y, rect.w, rect.h));
    318         }
    319         else {
    320           _qt_context->fillRect(QRect(rect.x, rect.y, rect.w, rect.h), brush);
    321         }
    322       }
    323     }
    324   }
    325 }
    326 
    327 void QtModule::render_rect_f(RenderOp op) {
    328   BLSize bounds(_params.screen_w, _params.screen_h);
    329   StyleKind style = _params.style;
    330   double wh = _params.shape_size;
    331 
    332   if (op == RenderOp::kStroke)
    333     _qt_context->setBrush(Qt::NoBrush);
    334 
    335   if (style == StyleKind::kSolid) {
    336     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    337       BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    338       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    339 
    340       if (op == RenderOp::kStroke) {
    341         _qt_context->setPen(color);
    342         _qt_context->drawRect(QRectF(rect.x, rect.y, rect.w, rect.h));
    343       }
    344       else {
    345         _qt_context->fillRect(QRectF(rect.x, rect.y, rect.w, rect.h), color);
    346       }
    347     }
    348   }
    349   else {
    350     for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    351       BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    352       QBrush brush(create_brush<BLRect>(style, rect));
    353 
    354       if (op == RenderOp::kStroke) {
    355         QPen pen(brush, qreal(_params.stroke_width));
    356         pen.setJoinStyle(Qt::MiterJoin);
    357 
    358         _qt_context->setPen(pen);
    359         _qt_context->drawRect(QRectF(rect.x, rect.y, rect.w, rect.h));
    360       }
    361       else {
    362         _qt_context->fillRect(QRectF(rect.x, rect.y, rect.w, rect.h), brush);
    363       }
    364     }
    365   }
    366 }
    367 
    368 void QtModule::render_rect_rotated(RenderOp op) {
    369   BLSize bounds(_params.screen_w, _params.screen_h);
    370   StyleKind style = _params.style;
    371 
    372   double cx = double(_params.screen_w) * 0.5;
    373   double cy = double(_params.screen_h) * 0.5;
    374   double wh = _params.shape_size;
    375   double angle = 0.0;
    376 
    377   if (op == RenderOp::kStroke)
    378     _qt_context->setBrush(Qt::NoBrush);
    379 
    380   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) {
    381     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    382 
    383     QTransform transform;
    384     transform.translate(cx, cy);
    385     transform.rotateRadians(angle);
    386     transform.translate(-cx, -cy);
    387     _qt_context->setTransform(transform, false);
    388 
    389     if (style == StyleKind::kSolid) {
    390       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    391 
    392       if (op == RenderOp::kStroke) {
    393         QPen pen(color, qreal(_params.stroke_width));
    394         pen.setJoinStyle(Qt::MiterJoin);
    395 
    396         _qt_context->setPen(pen);
    397         _qt_context->drawRect(QRectF(rect.x, rect.y, rect.w, rect.h));
    398       }
    399       else {
    400         _qt_context->fillRect(QRectF(rect.x, rect.y, rect.w, rect.h), color);
    401       }
    402     }
    403     else {
    404       QBrush brush(create_brush<BLRect>(style, rect));
    405 
    406       if (op == RenderOp::kStroke) {
    407         QPen pen(brush, qreal(_params.stroke_width));
    408         pen.setJoinStyle(Qt::MiterJoin);
    409 
    410         _qt_context->setPen(pen);
    411         _qt_context->drawRect(QRectF(rect.x, rect.y, rect.w, rect.h));
    412       }
    413       else {
    414         _qt_context->fillRect(QRectF(rect.x, rect.y, rect.w, rect.h), brush);
    415       }
    416     }
    417 
    418     _qt_context->resetTransform();
    419   }
    420 }
    421 
    422 void QtModule::render_round_f(RenderOp op) {
    423   BLSize bounds(_params.screen_w, _params.screen_h);
    424   StyleKind style = _params.style;
    425   double wh = _params.shape_size;
    426 
    427   if (op == RenderOp::kStroke)
    428     _qt_context->setBrush(Qt::NoBrush);
    429   else
    430     _qt_context->setPen(QPen(Qt::NoPen));
    431 
    432   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    433     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    434     double radius = _rnd_extra.next_double(4.0, 40.0);
    435 
    436     if (style == StyleKind::kSolid) {
    437       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    438 
    439       if (op == RenderOp::kStroke)
    440         _qt_context->setPen(QPen(color, qreal(_params.stroke_width)));
    441       else
    442         _qt_context->setBrush(QBrush(color));
    443     }
    444     else {
    445       QBrush brush(create_brush<BLRect>(style, rect));
    446 
    447       if (op == RenderOp::kStroke)
    448         _qt_context->setPen(QPen(brush, qreal(_params.stroke_width)));
    449       else
    450         _qt_context->setBrush(brush);
    451     }
    452 
    453     _qt_context->drawRoundedRect(
    454       QRectF(rect.x, rect.y, rect.w, rect.h),
    455       std::min(rect.w * 0.5, radius),
    456       std::min(rect.h * 0.5, radius));
    457   }
    458 }
    459 
    460 void QtModule::render_round_rotated(RenderOp op) {
    461   BLSize bounds(_params.screen_w, _params.screen_h);
    462   StyleKind style = _params.style;
    463 
    464   double cx = double(_params.screen_w) * 0.5;
    465   double cy = double(_params.screen_h) * 0.5;
    466   double wh = _params.shape_size;
    467   double angle = 0.0;
    468 
    469   if (op == RenderOp::kStroke)
    470     _qt_context->setBrush(Qt::NoBrush);
    471   else
    472     _qt_context->setPen(QPen(Qt::NoPen));
    473 
    474   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++, angle += 0.01) {
    475     BLRect rect(_rnd_coord.next_rect(bounds, wh, wh));
    476     double radius = _rnd_extra.next_double(4.0, 40.0);
    477 
    478     QTransform transform;
    479     transform.translate(cx, cy);
    480     transform.rotateRadians(angle);
    481     transform.translate(-cx, -cy);
    482     _qt_context->setTransform(transform, false);
    483 
    484     if (style == StyleKind::kSolid) {
    485       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    486       if (op == RenderOp::kStroke)
    487         _qt_context->setPen(QPen(color, qreal(_params.stroke_width)));
    488       else
    489         _qt_context->setBrush(QBrush(color));
    490     }
    491     else {
    492       QBrush brush(create_brush<BLRect>(style, rect));
    493       if (op == RenderOp::kStroke)
    494         _qt_context->setPen(QPen(brush, qreal(_params.stroke_width)));
    495       else
    496         _qt_context->setBrush(brush);
    497     }
    498 
    499     _qt_context->drawRoundedRect(
    500       QRectF(rect.x, rect.y, rect.w, rect.h),
    501       std::min(rect.w * 0.5, radius),
    502       std::min(rect.h * 0.5, radius));
    503 
    504     _qt_context->resetTransform();
    505   }
    506 }
    507 
    508 void QtModule::render_polygon(RenderOp op, uint32_t complexity) {
    509   BLSizeI bounds(_params.screen_w - _params.shape_size,
    510                  _params.screen_h - _params.shape_size);
    511   StyleKind style = _params.style;
    512   double wh = double(_params.shape_size);
    513 
    514   Qt::FillRule fillRule = op == RenderOp::kFillEvenOdd ? Qt::OddEvenFill : Qt::WindingFill;
    515 
    516   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    517     BLPoint base(_rnd_coord.nextPoint(bounds));
    518 
    519     double x = _rnd_coord.next_double(base.x, base.x + wh);
    520     double y = _rnd_coord.next_double(base.y, base.y + wh);
    521 
    522     QPainterPath path;
    523     path.setFillRule(fillRule);
    524     path.moveTo(x, y);
    525 
    526     for (uint32_t p = 1; p < complexity; p++) {
    527       x = _rnd_coord.next_double(base.x, base.x + wh);
    528       y = _rnd_coord.next_double(base.y, base.y + wh);
    529       path.lineTo(x, y);
    530     }
    531 
    532     if (style == StyleKind::kSolid) {
    533       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    534 
    535       if (op == RenderOp::kStroke) {
    536         QPen pen(color, qreal(_params.stroke_width));
    537         pen.setJoinStyle(Qt::MiterJoin);
    538         _qt_context->strokePath(path, pen);
    539       }
    540       else {
    541         _qt_context->fillPath(path, QBrush(color));
    542       }
    543     }
    544     else {
    545       BLRect rect(base.x, base.y, wh, wh);
    546       QBrush brush(create_brush<BLRect>(style, rect));
    547 
    548       if (op == RenderOp::kStroke) {
    549         QPen pen(brush, qreal(_params.stroke_width));
    550         pen.setJoinStyle(Qt::MiterJoin);
    551         _qt_context->strokePath(path, pen);
    552       }
    553       else {
    554         _qt_context->fillPath(path, brush);
    555       }
    556     }
    557   }
    558 }
    559 
    560 void QtModule::render_shape(RenderOp op, ShapeData shape) {
    561   BLSizeI bounds(_params.screen_w - _params.shape_size,
    562                  _params.screen_h - _params.shape_size);
    563   StyleKind style = _params.style;
    564   double wh = double(_params.shape_size);
    565 
    566   ShapeIterator it(shape);
    567   QPainterPath path;
    568   while (it.has_command()) {
    569     if (it.is_move_to()) {
    570       path.moveTo(it.x(0) * wh, it.y(0) * wh);
    571     }
    572     else if (it.is_line_to()) {
    573       path.lineTo(it.x(0) * wh, it.y(0) * wh);
    574     }
    575     else if (it.is_quad_to()) {
    576       path.quadTo(
    577         it.x(0) * wh, it.y(0) * wh,
    578         it.x(1) * wh, it.y(1) * wh);
    579     }
    580     else if (it.is_cubic_to()) {
    581       path.cubicTo(
    582         it.x(0) * wh, it.y(0) * wh,
    583         it.x(1) * wh, it.y(1) * wh,
    584         it.x(2) * wh, it.y(2) * wh);
    585     }
    586     else {
    587       path.closeSubpath();
    588     }
    589     it.next();
    590   }
    591 
    592   Qt::FillRule fillRule = op == RenderOp::kFillEvenOdd ? Qt::OddEvenFill : Qt::WindingFill;
    593 
    594   for (uint32_t i = 0, quantity = _params.quantity; i < quantity; i++) {
    595     BLPoint base(_rnd_coord.nextPoint(bounds));
    596 
    597     _qt_context->save();
    598     _qt_context->translate(qreal(base.x), qreal(base.y));
    599 
    600     if (style == StyleKind::kSolid) {
    601       QColor color(to_qt_color(_rnd_color.next_rgba32()));
    602 
    603       if (op == RenderOp::kStroke) {
    604         QPen pen(color, qreal(_params.stroke_width));
    605         pen.setJoinStyle(Qt::MiterJoin);
    606         _qt_context->strokePath(path, pen);
    607       }
    608       else {
    609         _qt_context->fillPath(path, QBrush(color));
    610       }
    611     }
    612     else {
    613       BLRect rect(0, 0, wh, wh);
    614       QBrush brush(create_brush<BLRect>(style, rect));
    615 
    616       if (op == RenderOp::kStroke) {
    617         QPen pen(brush, qreal(_params.stroke_width));
    618         pen.setJoinStyle(Qt::MiterJoin);
    619         _qt_context->strokePath(path, pen);
    620       }
    621       else {
    622         _qt_context->fillPath(path, brush);
    623       }
    624     }
    625 
    626     _qt_context->restore();
    627   }
    628 }
    629 
    630 Backend* create_qt_backend() {
    631   return new QtModule();
    632 }
    633 
    634 } // {blbench}
    635 
    636 #endif // BL_BENCH_ENABLE_QT