odin-blend2d

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

bl_demo_rects.cpp (12233B)


      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 _size_slider;
     13   QSlider _count_slider;
     14   QComboBox _renderer_select;
     15   QComboBox _comp_op_select;
     16   QComboBox _shape_type_select;
     17   QCheckBox _limit_fps_check;
     18   QBLCanvas _canvas;
     19 
     20   BLRandom _random;
     21   bool _animate = true;
     22   std::vector<BLPoint> _coords;
     23   std::vector<BLPoint> _steps;
     24   std::vector<BLRgba32> _colors;
     25   BLCompOp _comp_op = BL_COMP_OP_SRC_OVER;
     26   uint32_t _shape_type = 0;
     27   double _rect_size = 64.0;
     28 
     29   enum ShapeType {
     30     kShapeRectA,
     31     kShapeRectU,
     32     kShapeRectPath,
     33     kShapeRoundRect,
     34     kShapePolyPath,
     35   };
     36 
     37   MainWindow() : _random(0x123456789ABCDEF) {
     38     QVBoxLayout* vBox = new QVBoxLayout();
     39     vBox->setContentsMargins(0, 0, 0, 0);
     40     vBox->setSpacing(0);
     41 
     42     QGridLayout* grid = new QGridLayout();
     43     grid->setContentsMargins(5, 5, 5, 5);
     44     grid->setSpacing(5);
     45 
     46     QBLCanvas::init_renderer_select_box(&_renderer_select);
     47     _comp_op_select.addItem("SrcOver", QVariant(int(BL_COMP_OP_SRC_OVER)));
     48     _comp_op_select.addItem("SrcCopy", QVariant(int(BL_COMP_OP_SRC_COPY)));
     49     _comp_op_select.addItem("SrcAtop", QVariant(int(BL_COMP_OP_SRC_ATOP)));
     50     _comp_op_select.addItem("DstAtop", QVariant(int(BL_COMP_OP_DST_ATOP)));
     51     _comp_op_select.addItem("Xor", QVariant(int(BL_COMP_OP_XOR)));
     52     _comp_op_select.addItem("Plus", QVariant(int(BL_COMP_OP_PLUS)));
     53     _comp_op_select.addItem("Multiply", QVariant(int(BL_COMP_OP_MULTIPLY)));
     54     _comp_op_select.addItem("Screen", QVariant(int(BL_COMP_OP_SCREEN)));
     55     _comp_op_select.addItem("Overlay", QVariant(int(BL_COMP_OP_OVERLAY)));
     56     _comp_op_select.addItem("Darken", QVariant(int(BL_COMP_OP_DARKEN)));
     57     _comp_op_select.addItem("Lighten", QVariant(int(BL_COMP_OP_LIGHTEN)));
     58     _comp_op_select.addItem("Color Dodge", QVariant(int(BL_COMP_OP_COLOR_DODGE)));
     59     _comp_op_select.addItem("Color Burn", QVariant(int(BL_COMP_OP_COLOR_BURN)));
     60     _comp_op_select.addItem("Hard Light", QVariant(int(BL_COMP_OP_HARD_LIGHT)));
     61     _comp_op_select.addItem("Soft Light", QVariant(int(BL_COMP_OP_SOFT_LIGHT)));
     62     _comp_op_select.addItem("Difference", QVariant(int(BL_COMP_OP_DIFFERENCE)));
     63     _comp_op_select.addItem("Exclusion", QVariant(int(BL_COMP_OP_EXCLUSION)));
     64 
     65     _shape_type_select.addItem("RectA", QVariant(int(kShapeRectA)));
     66     _shape_type_select.addItem("RectU", QVariant(int(kShapeRectU)));
     67     _shape_type_select.addItem("RectPath", QVariant(int(kShapeRectPath)));
     68     _shape_type_select.addItem("RoundRect", QVariant(int(kShapeRoundRect)));
     69     _shape_type_select.addItem("Polygon", QVariant(int(kShapePolyPath)));
     70 
     71     _limit_fps_check.setText(QLatin1String("Limit FPS"));
     72 
     73     _size_slider.setOrientation(Qt::Horizontal);
     74     _size_slider.setMinimum(1);
     75     _size_slider.setMaximum(128);
     76     _size_slider.setSliderPosition(64);
     77 
     78     _count_slider.setOrientation(Qt::Horizontal);
     79     _count_slider.setMinimum(1);
     80     _count_slider.setMaximum(20000);
     81     _count_slider.setSliderPosition(200);
     82 
     83     _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1);
     84     _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1);
     85 
     86     connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int)));
     87     connect(&_comp_op_select, SIGNAL(activated(int)), SLOT(onCompOpChanged(int)));
     88     connect(&_shape_type_select, SIGNAL(activated(int)), SLOT(onShapeTypeChanged(int)));
     89     connect(&_limit_fps_check, SIGNAL(stateChanged(int)), SLOT(onLimitFpsChanged(int)));
     90     connect(&_size_slider, SIGNAL(valueChanged(int)), SLOT(onSizeChanged(int)));
     91     connect(&_count_slider, SIGNAL(valueChanged(int)), SLOT(onCountChanged(int)));
     92 
     93     grid->addWidget(new QLabel("Renderer:"), 0, 0);
     94     grid->addWidget(&_renderer_select, 0, 1);
     95 
     96     grid->addWidget(new QLabel("Comp Op:"), 0, 2);
     97     grid->addWidget(&_comp_op_select, 0, 3);
     98 
     99     grid->addWidget(new QLabel("Shape:"), 0, 4);
    100     grid->addWidget(&_shape_type_select, 0, 5);
    101 
    102     grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 6);
    103     grid->addWidget(&_limit_fps_check, 0, 7, Qt::AlignRight);
    104 
    105     grid->addWidget(new QLabel("Count:"), 1, 0, 1, 1, Qt::AlignRight);
    106     grid->addWidget(&_count_slider, 1, 1, 1, 7);
    107 
    108     grid->addWidget(new QLabel("Size:"), 2, 0, 1, 1, Qt::AlignRight);
    109     grid->addWidget(&_size_slider, 2, 1, 1, 7);
    110 
    111     vBox->addLayout(grid);
    112     vBox->addWidget(&_canvas);
    113     setLayout(vBox);
    114 
    115     connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer()));
    116     connect(new QShortcut(QKeySequence(Qt::Key_P), this), SIGNAL(activated()), SLOT(onToggleAnimate()));
    117     connect(new QShortcut(QKeySequence(Qt::Key_S), this), SIGNAL(activated()), SLOT(onStep()));
    118 
    119     onInit();
    120   }
    121 
    122   void showEvent(QShowEvent* event) override { _timer.start(); }
    123   void hideEvent(QHideEvent* event) override { _timer.stop(); }
    124   void keyPressEvent(QKeyEvent* event) override {}
    125 
    126   void onInit() {
    127     setCount(_count_slider.sliderPosition());
    128     _limit_fps_check.setChecked(true);
    129     _updateTitle();
    130   }
    131 
    132   double randomSign() noexcept { return _random.next_double() < 0.5 ? 1.0 : -1.0; }
    133   BLRgba32 randomColor() noexcept { return BLRgba32(_random.next_uint32()); }
    134 
    135   Q_SLOT void onToggleAnimate() { _animate = !_animate; }
    136   Q_SLOT void onStep() { step(); }
    137 
    138   Q_SLOT void onRendererChanged(int index) { _canvas.set_renderer_type(_renderer_select.itemData(index).toInt());  }
    139   Q_SLOT void onCompOpChanged(int index) { _comp_op = (BLCompOp)_comp_op_select.itemData(index).toInt(); };
    140   Q_SLOT void onShapeTypeChanged(int index) { _shape_type = _shape_type_select.itemData(index).toInt(); };
    141   Q_SLOT void onLimitFpsChanged(int value) { _timer.setInterval(value ? 1000 / 120 : 0); }
    142   Q_SLOT void onSizeChanged(int value) { _rect_size = value; }
    143   Q_SLOT void onCountChanged(int value) { setCount(size_t(value)); }
    144 
    145   Q_SLOT void onTimer() {
    146     if (_animate) {
    147       step();
    148     }
    149 
    150     _canvas.update_canvas(true);
    151     _updateTitle();
    152   }
    153 
    154   void step() noexcept {
    155     double w = _canvas.image_width();
    156     double h = _canvas.image_height();
    157 
    158     size_t size = _coords.size();
    159     for (size_t i = 0; i < size; i++) {
    160       BLPoint& vertex = _coords[i];
    161       BLPoint& step = _steps[i];
    162 
    163       vertex += step;
    164       if (vertex.x <= 0.0 || vertex.x >= w) {
    165         step.x = -step.x;
    166         vertex.x = bl_min(vertex.x + step.x, w);
    167       }
    168 
    169       if (vertex.y <= 0.0 || vertex.y >= h) {
    170         step.y = -step.y;
    171         vertex.y = bl_min(vertex.y + step.y, h);
    172       }
    173     }
    174   }
    175 
    176   void on_render_blend2d(BLContext& ctx) noexcept {
    177     ctx.set_comp_op(BL_COMP_OP_SRC_COPY);
    178     ctx.fill_all(bl_background_for_comp_op(_comp_op));
    179     ctx.set_comp_op(_comp_op);
    180 
    181     size_t i;
    182     size_t size = _coords.size();
    183 
    184     double rectSize = _rect_size;
    185     double halfSize = _rect_size * 0.5;
    186 
    187     switch (_shape_type) {
    188       case kShapeRectA: {
    189         int rectSizeI = int(_rect_size);
    190         for (i = 0; i < size; i++) {
    191           int x = int(_coords[i].x - halfSize);
    192           int y = int(_coords[i].y - halfSize);
    193           ctx.fill_rect(BLRectI(x, y, rectSizeI, rectSizeI), _colors[i]);
    194         }
    195         break;
    196       }
    197 
    198       case kShapeRectU: {
    199         for (i = 0; i < size; i++) {
    200           double x = _coords[i].x - halfSize;
    201           double y = _coords[i].y - halfSize;
    202           ctx.fill_rect(x, y, rectSize, rectSize, _colors[i]);
    203         }
    204         break;
    205       }
    206 
    207       case kShapeRectPath: {
    208         for (i = 0; i < size; i++) {
    209           double x = _coords[i].x - halfSize;
    210           double y = _coords[i].y - halfSize;
    211 
    212           BLPath path;
    213           path.add_rect(x, y, rectSize, rectSize);
    214           ctx.fill_path(path, _colors[i]);
    215         }
    216         break;
    217       }
    218 
    219       case kShapePolyPath: {
    220         for (i = 0; i < size; i++) {
    221           double x = _coords[i].x - halfSize;
    222           double y = _coords[i].y - halfSize;
    223 
    224           BLPath path;
    225           path.move_to(x + rectSize / 2, y);
    226           path.line_to(x + rectSize, y + rectSize / 3);
    227           path.line_to(x + rectSize - rectSize / 3, y + rectSize);
    228           path.line_to(x + rectSize / 3, y + rectSize);
    229           path.line_to(x, y + rectSize / 3);
    230           ctx.fill_path(path, _colors[i]);
    231         }
    232         break;
    233       }
    234 
    235       case kShapeRoundRect: {
    236         for (i = 0; i < size; i++) {
    237           double x = _coords[i].x - halfSize;
    238           double y = _coords[i].y - halfSize;
    239 
    240           ctx.fill_round_rect(BLRoundRect(x, y, rectSize, rectSize, 10), _colors[i]);
    241         }
    242         break;
    243       }
    244     }
    245   }
    246 
    247   void on_render_qt(QPainter& ctx) noexcept {
    248     ctx.setCompositionMode(QPainter::CompositionMode_Source);
    249     ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), bl_rgba_to_qcolor(bl_background_for_comp_op(_comp_op)));
    250     ctx.setRenderHint(QPainter::Antialiasing, true);
    251     ctx.setCompositionMode(bl_comp_op_to_qt_composition_mode(_comp_op));
    252 
    253     size_t i;
    254     size_t size = _coords.size();
    255 
    256     double rectSize = _rect_size;
    257     double halfSize = _rect_size * 0.5;
    258 
    259     switch (_shape_type) {
    260       case kShapeRectA: {
    261         int rectSizeI = int(_rect_size);
    262         for (i = 0; i < size; i++) {
    263           int x = int(_coords[i].x - halfSize);
    264           int y = int(_coords[i].y - halfSize);
    265           ctx.fillRect(QRect(x, y, rectSizeI, rectSizeI), bl_rgba_to_qcolor(_colors[i]));
    266         }
    267         break;
    268       }
    269 
    270       case kShapeRectU: {
    271         for (i = 0; i < size; i++) {
    272           double x = _coords[i].x - halfSize;
    273           double y = _coords[i].y - halfSize;
    274           ctx.fillRect(QRectF(_coords[i].x - halfSize, _coords[i].y - halfSize, rectSize, rectSize), bl_rgba_to_qcolor(_colors[i]));
    275         }
    276         break;
    277       }
    278 
    279       case kShapeRectPath: {
    280         for (i = 0; i < size; i++) {
    281           double x = _coords[i].x - halfSize;
    282           double y = _coords[i].y - halfSize;
    283 
    284           QPainterPath path;
    285           path.addRect(x, y, rectSize, rectSize);
    286           ctx.fillPath(path, bl_rgba_to_qcolor(_colors[i]));
    287         }
    288         break;
    289       }
    290 
    291       case kShapePolyPath: {
    292         for (i = 0; i < size; i++) {
    293           double x = _coords[i].x - halfSize;
    294           double y = _coords[i].y - halfSize;
    295 
    296           QPainterPath path;
    297           path.moveTo(x + rectSize / 2, y);
    298           path.lineTo(x + rectSize, y + rectSize / 3);
    299           path.lineTo(x + rectSize - rectSize / 3, y + rectSize);
    300           path.lineTo(x + rectSize / 3, y + rectSize);
    301           path.lineTo(x, y + rectSize / 3);
    302           ctx.fillPath(path, bl_rgba_to_qcolor(_colors[i]));
    303         }
    304         break;
    305       }
    306 
    307       case kShapeRoundRect: {
    308         for (i = 0; i < size; i++) {
    309           double x = _coords[i].x - halfSize;
    310           double y = _coords[i].y - halfSize;
    311 
    312           QPainterPath path;
    313           path.addRoundedRect(QRectF(x, y, rectSize, rectSize), 10, 10);
    314           ctx.fillPath(path, bl_rgba_to_qcolor(_colors[i]));
    315         }
    316         break;
    317       }
    318     }
    319   }
    320 
    321   void setCount(size_t size) {
    322     double w = _canvas.image_width();
    323     double h = _canvas.image_height();
    324     size_t i = _coords.size();
    325 
    326     _coords.resize(size);
    327     _steps.resize(size);
    328     _colors.resize(size);
    329 
    330     while (i < size) {
    331       _coords[i].reset(_random.next_double() * w,
    332                        _random.next_double() * h);
    333       _steps[i].reset((_random.next_double() * 0.5 + 0.04) * randomSign(),
    334                       (_random.next_double() * 0.5 + 0.04) * randomSign());
    335       _colors[i].reset(randomColor());
    336       i++;
    337     }
    338   }
    339 
    340   void _updateTitle() {
    341     char buf[256];
    342     snprintf(buf, 256, "Rects [%dx%d] [Size=%d Count=%zu] [RenderTime=%.2fms FPS=%.1f]",
    343       _canvas.image_width(),
    344       _canvas.image_height(),
    345       int(_rect_size),
    346       _coords.size(),
    347       _canvas.average_render_time(),
    348       _canvas.fps());
    349 
    350     QString title = QString::fromUtf8(buf);
    351     if (title != windowTitle())
    352       setWindowTitle(title);
    353   }
    354 };
    355 
    356 int main(int argc, char *argv[]) {
    357   QApplication app(argc, argv);
    358   MainWindow win;
    359 
    360   win.setMinimumSize(QSize(400, 320));
    361   win.resize(QSize(580, 520));
    362   win.show();
    363 
    364   return app.exec();
    365 }
    366 
    367 #include "bl_demo_rects.moc"