odin-blend2d

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

bl_demo_paths.cpp (12338B)


      1 #include <stdlib.h>
      2 #include <vector>
      3 
      4 #include "bl_qt_headers.h"
      5 #include "bl_qt_canvas.h"
      6 
      7 class MainWindow : public QWidget {
      8   Q_OBJECT
      9 
     10 public:
     11   QTimer _timer;
     12   QSlider _slider;
     13   QBLCanvas _canvas;
     14   QComboBox _renderer_select;
     15   QCheckBox _limit_fps_check;
     16   QComboBox _operation_select;
     17   QComboBox _style_select;
     18 
     19   bool _animate = true;
     20   int _op = 0;
     21   std::vector<BLPoint> _poly;
     22   std::vector<BLPoint> _step;
     23 
     24   BLRandom _random;
     25 
     26   MainWindow() : _random(0x1234) {
     27     QVBoxLayout* vBox = new QVBoxLayout();
     28     vBox->setContentsMargins(0, 0, 0, 0);
     29     vBox->setSpacing(0);
     30 
     31     QGridLayout* grid = new QGridLayout();
     32     grid->setContentsMargins(5, 5, 5, 5);
     33     grid->setSpacing(5);
     34 
     35     QBLCanvas::init_renderer_select_box(&_renderer_select);
     36     _limit_fps_check.setText(QLatin1String("Limit FPS"));
     37 
     38     _operation_select.addItem("Fill Poly", QVariant(int(0)));
     39     _operation_select.addItem("Stroke Poly [W=1]", QVariant(int(1)));
     40     _operation_select.addItem("Stroke Poly [W=3]", QVariant(int(2)));
     41     _operation_select.addItem("Stroke Poly [W=5]", QVariant(int(3)));
     42 
     43     _operation_select.addItem("Fill Quads", QVariant(int(4)));
     44     _operation_select.addItem("Stroke Quads [W=1]", QVariant(int(5)));
     45     _operation_select.addItem("Stroke Quads [W=3]", QVariant(int(6)));
     46     _operation_select.addItem("Stroke Quads [W=5]", QVariant(int(7)));
     47 
     48     _operation_select.addItem("Fill Cubics", QVariant(int(8)));
     49     _operation_select.addItem("Stroke Cubics [W=1]", QVariant(int(9)));
     50     _operation_select.addItem("Stroke Cubics [W=3]", QVariant(int(10)));
     51     _operation_select.addItem("Stroke Cubics [W=5]", QVariant(int(11)));
     52 
     53     _style_select.addItem("Solid Color", QVariant(int(0)));
     54     _style_select.addItem("Linear Gradient", QVariant(int(1)));
     55     _style_select.addItem("Radial Gradient", QVariant(int(2)));
     56     _style_select.addItem("Conic Gradient", QVariant(int(3)));
     57 
     58     _slider.setOrientation(Qt::Horizontal);
     59     _slider.setMinimum(4);
     60     _slider.setMaximum(2000);
     61     _slider.setSliderPosition(10);
     62 
     63     _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1);
     64     _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1);
     65 
     66     connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int)));
     67     connect(&_limit_fps_check, SIGNAL(stateChanged(int)), SLOT(onLimitFpsChanged(int)));
     68     connect(&_operation_select, SIGNAL(activated(int)), SLOT(onOperationChanged(int)));
     69     connect(&_style_select, SIGNAL(activated(int)), SLOT(onStyleChanged(int)));
     70     connect(&_slider, SIGNAL(valueChanged(int)), SLOT(onPolySizeChanged(int)));
     71 
     72     grid->addWidget(new QLabel("Renderer:"), 0, 0);
     73     grid->addWidget(&_renderer_select, 0, 1);
     74     grid->addWidget(new QLabel("Op:"), 0, 2);
     75     grid->addWidget(&_operation_select, 0, 3);
     76     grid->addWidget(new QLabel("Style:"), 0, 4);
     77     grid->addWidget(&_style_select, 0, 5);
     78 
     79     grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 6);
     80     grid->addWidget(&_limit_fps_check, 0, 7);
     81 
     82     grid->addWidget(new QLabel("Count:"), 1, 0, Qt::AlignRight);
     83     grid->addWidget(&_slider, 1, 1, 1, 7);
     84 
     85     vBox->addLayout(grid);
     86     vBox->addWidget(&_canvas);
     87     setLayout(vBox);
     88 
     89     connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer()));
     90     connect(new QShortcut(QKeySequence(Qt::Key_P), this), SIGNAL(activated()), SLOT(onToggleAnimate()));
     91 
     92     onInit();
     93   }
     94 
     95   void showEvent(QShowEvent* event) override { _timer.start(); }
     96   void hideEvent(QHideEvent* event) override { _timer.stop(); }
     97 
     98   void onInit() {
     99     setPolySize(50);
    100     _limit_fps_check.setChecked(true);
    101     _updateTitle();
    102   }
    103 
    104   double randomSign() noexcept { return _random.next_double() < 0.5 ? 1.0 : -1.0; }
    105 
    106   Q_SLOT void onToggleAnimate() { _animate = !_animate; }
    107   Q_SLOT void onStyleChanged(int index) { _canvas.update_canvas(); }
    108   Q_SLOT void onRendererChanged(int index) { _canvas.set_renderer_type(_renderer_select.itemData(index).toInt());  }
    109   Q_SLOT void onLimitFpsChanged(int value) { _timer.setInterval(value ? 1000 / 120 : 0); }
    110   Q_SLOT void onOperationChanged(int index) { _op = index; }
    111   Q_SLOT void onPolySizeChanged(int value) { setPolySize(size_t(value)); }
    112 
    113   Q_SLOT void onTimer() {
    114     if (_animate) {
    115       double w = _canvas.image_width();
    116       double h = _canvas.image_height();
    117 
    118       size_t size = _poly.size();
    119       for (size_t i = 0; i < size; i++) {
    120         BLPoint& vertex = _poly[i];
    121         BLPoint& step = _step[i];
    122 
    123         vertex += step;
    124         if (vertex.x <= 0.0 || vertex.x >= w) {
    125           step.x = -step.x;
    126           vertex.x = bl_min(vertex.x + step.x, w);
    127         }
    128 
    129         if (vertex.y <= 0.0 || vertex.y >= h) {
    130           step.y = -step.y;
    131           vertex.y = bl_min(vertex.y + step.y, h);
    132         }
    133       }
    134     }
    135 
    136     _canvas.update_canvas(true);
    137     _updateTitle();
    138   }
    139 
    140   void on_render_blend2d(BLContext& ctx) noexcept {
    141     ctx.fill_all(BLRgba32(0xFF000000));
    142     ctx.set_fill_rule(BL_FILL_RULE_EVEN_ODD);
    143 
    144     int styleId = _style_select.currentIndex();
    145 
    146     switch (styleId) {
    147       default:
    148       case 0: {
    149         ctx.set_fill_style(BLRgba32(0xFFFFFFFF));
    150         ctx.set_stroke_style(BLRgba32(0xFFFFFFFF));
    151         break;
    152       }
    153 
    154       case 1: {
    155         double w = _canvas.image_width();
    156         double h = _canvas.image_height();
    157 
    158         BLGradient g(BLLinearGradientValues(0, 0, w, h));
    159         g.add_stop(0.0, BLRgba32(0xFFFF0000));
    160         g.add_stop(0.5, BLRgba32(0xFFAF00AF));
    161         g.add_stop(1.0, BLRgba32(0xFF0000FF));
    162 
    163         ctx.set_fill_style(g);
    164         ctx.set_stroke_style(g);
    165         break;
    166       }
    167 
    168       case 2: {
    169         double w = _canvas.image_width();
    170         double h = _canvas.image_height();
    171         double r = bl_min(w, h);
    172 
    173         BLGradient g(BLRadialGradientValues(w * 0.5, h * 0.5, w * 0.5, h * 0.5, r * 0.5));
    174         g.add_stop(0.0, BLRgba32(0xFFFF0000));
    175         g.add_stop(0.5, BLRgba32(0xFFAF00AF));
    176         g.add_stop(1.0, BLRgba32(0xFF0000FF));
    177 
    178         ctx.set_fill_style(g);
    179         ctx.set_stroke_style(g);
    180         break;
    181       }
    182 
    183       case 3: {
    184         double w = _canvas.image_width();
    185         double h = _canvas.image_height();
    186 
    187         BLGradient g(BLConicGradientValues(w * 0.5, h * 0.5, 0.0));
    188         g.add_stop(0.00, BLRgba32(0xFFFF0000));
    189         g.add_stop(0.33, BLRgba32(0xFFAF00AF));
    190         g.add_stop(0.66, BLRgba32(0xFF0000FF));
    191         g.add_stop(1.00, BLRgba32(0xFFFF0000));
    192 
    193         ctx.set_fill_style(g);
    194         ctx.set_stroke_style(g);
    195         break;
    196       }
    197     }
    198 
    199     switch (_op) {
    200       case 0: {
    201         ctx.fill_polygon(_poly.data(), _poly.size());
    202         break;
    203       }
    204 
    205       case 1:
    206       case 2:
    207       case 3: {
    208         ctx.set_stroke_width((_op - 1) * 2 + 1);
    209         ctx.stroke_polygon(_poly.data(), _poly.size());
    210         break;
    211       }
    212 
    213       case 4:
    214       case 5:
    215       case 6:
    216       case 7: {
    217         const BLPoint* poly = _poly.data();
    218         size_t n = _poly.size();
    219         BLPath path;
    220         path.move_to(poly[0]);
    221         for (size_t i = 3; i <= n; i += 2)
    222           path.quad_to(poly[i - 2], poly[i - 1]);
    223 
    224         if (_op == 4) {
    225           ctx.fill_path(path);
    226         }
    227         else {
    228           ctx.set_stroke_width((_op - 5) * 2 + 1);
    229           ctx.stroke_path(path);
    230         }
    231         break;
    232       }
    233 
    234       case 8:
    235       case 9:
    236       case 10:
    237       case 11: {
    238         const BLPoint* poly = _poly.data();
    239         size_t n = _poly.size();
    240         BLPath path;
    241         path.move_to(poly[0]);
    242         for (size_t i = 4; i <= n; i += 3)
    243           path.cubic_to(poly[i - 3], poly[i - 2], poly[i - 1]);
    244 
    245         if (_op == 8) {
    246           ctx.fill_path(path);
    247         }
    248         else {
    249           ctx.set_stroke_width((_op - 9) * 2 + 1);
    250           ctx.stroke_path(path);
    251         }
    252         break;
    253       }
    254     }
    255   }
    256 
    257   void on_render_qt(QPainter& ctx) noexcept {
    258     ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QColor(0, 0, 0));
    259     ctx.setRenderHint(QPainter::Antialiasing, true);
    260 
    261     int styleId = _style_select.currentIndex();
    262     QBrush brush;
    263 
    264     switch (styleId) {
    265       default:
    266       case 0: {
    267         brush = QColor(255, 255, 255);
    268         break;
    269       }
    270 
    271       case 1: {
    272         double w = _canvas.image_width();
    273         double h = _canvas.image_height();
    274 
    275         QLinearGradient g(qreal(0), qreal(0), qreal(w), qreal(h));
    276         g.setColorAt(0.0f, QColor(0xFF, 0x00, 0x00));
    277         g.setColorAt(0.5f, QColor(0xAF, 0x00, 0xAF));
    278         g.setColorAt(1.0f, QColor(0x00, 0x00, 0xFF));
    279 
    280         brush = QBrush(g);
    281         break;
    282       }
    283 
    284       case 2: {
    285         double w = _canvas.image_width();
    286         double h = _canvas.image_height();
    287         double r = bl_min(w, h);
    288 
    289         QRadialGradient g(qreal(w * 0.5), qreal(h * 0.5), qreal(r * 0.5), qreal(w * 0.5), qreal(h * 0.5));
    290         g.setColorAt(0.0f, QColor(0xFF, 0x00, 0x00));
    291         g.setColorAt(0.5f, QColor(0xAF, 0x00, 0xAF));
    292         g.setColorAt(1.0f, QColor(0x00, 0x00, 0xFF));
    293 
    294         brush = QBrush(g);
    295         break;
    296       }
    297 
    298       case 3: {
    299         double w = _canvas.image_width();
    300         double h = _canvas.image_height();
    301 
    302         QConicalGradient g(qreal(w * 0.5), qreal(h * 0.5), 0.0);
    303         g.setColorAt(0.00f, QColor(0xFF, 0x00, 0x00));
    304         g.setColorAt(0.66f, QColor(0xAF, 0x00, 0xAF));
    305         g.setColorAt(0.33f, QColor(0x00, 0x00, 0xFF));
    306         g.setColorAt(1.00f, QColor(0xFF, 0x00, 0x00));
    307 
    308         brush = QBrush(g);
    309         break;
    310       }
    311     }
    312 
    313     ctx.setBrush(brush);
    314 
    315     QPen pen(brush, 1.0f);
    316     pen.setJoinStyle(Qt::MiterJoin);
    317 
    318     switch (_op) {
    319       case 0: {
    320         ctx.setPen(Qt::NoPen);
    321         ctx.drawPolygon(reinterpret_cast<const QPointF*>(_poly.data()), int(_poly.size()), Qt::OddEvenFill);
    322         break;
    323       }
    324 
    325       case 1:
    326       case 2:
    327       case 3:
    328         pen.setWidth((_op - 1) * 2 + 1);
    329         ctx.setBrush(Qt::NoBrush);
    330         ctx.setPen(pen);
    331         ctx.drawPolygon(reinterpret_cast<const QPointF*>(_poly.data()), int(_poly.size()), Qt::OddEvenFill);
    332         break;
    333 
    334       case 4:
    335       case 5:
    336       case 6:
    337       case 7: {
    338         const BLPoint* poly = _poly.data();
    339         size_t n = _poly.size();
    340         QPainterPath path;
    341         path.moveTo(poly[0].x, poly[0].y);
    342         for (size_t i = 3; i <= n; i += 2)
    343           path.quadTo(poly[i - 2].x, poly[i - 2].y, poly[i - 1].x, poly[i - 1].y);
    344 
    345         if (_op == 4) {
    346           path.setFillRule(Qt::OddEvenFill);
    347           ctx.fillPath(path, brush);
    348         }
    349         else {
    350           pen.setWidth((_op - 5) * 2 + 1);
    351           ctx.strokePath(path, pen);
    352         }
    353         break;
    354       }
    355 
    356       case 8:
    357       case 9:
    358       case 10:
    359       case 11: {
    360         const BLPoint* poly = _poly.data();
    361         size_t n = _poly.size();
    362         QPainterPath path;
    363         path.moveTo(poly[0].x, poly[0].y);
    364         for (size_t i = 4; i <= n; i += 3)
    365           path.cubicTo(poly[i - 3].x, poly[i - 3].y, poly[i - 2].x, poly[i - 2].y, poly[i - 1].x, poly[i - 1].y);
    366 
    367         if (_op == 8) {
    368           path.setFillRule(Qt::OddEvenFill);
    369           ctx.fillPath(path, brush);
    370         }
    371         else {
    372           pen.setWidth((_op - 9) * 2 + 1);
    373           ctx.strokePath(path, pen);
    374         }
    375         break;
    376       }
    377     }
    378   }
    379 
    380   void setPolySize(size_t size) {
    381     double w = _canvas.image_width();
    382     double h = _canvas.image_height();
    383     size_t prev = _poly.size();
    384 
    385     _poly.resize(size);
    386     _step.resize(size);
    387 
    388     while (prev < size) {
    389       _poly[prev].reset(_random.next_double() * w,
    390                         _random.next_double() * h);
    391       _step[prev].reset((_random.next_double() * 0.5 + 0.05) * randomSign(),
    392                         (_random.next_double() * 0.5 + 0.05) * randomSign());
    393       prev++;
    394     }
    395   }
    396 
    397   void _updateTitle() {
    398     char buf[256];
    399     snprintf(buf, 256, "Paths [%dx%d] [Size=%zu] [RenderTime=%.2fms FPS=%.1f]",
    400       _canvas.image_width(),
    401       _canvas.image_height(),
    402       _poly.size(),
    403       _canvas.average_render_time(),
    404       _canvas.fps());
    405 
    406     QString title = QString::fromUtf8(buf);
    407     if (title != windowTitle())
    408       setWindowTitle(title);
    409   }
    410 };
    411 
    412 int main(int argc, char *argv[]) {
    413   QApplication app(argc, argv);
    414   MainWindow win;
    415 
    416   win.setMinimumSize(QSize(400, 320));
    417   win.resize(QSize(580, 520));
    418   win.show();
    419 
    420   return app.exec();
    421 }
    422 
    423 #include "bl_demo_paths.moc"