odin-blend2d

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

bl_demo_bounces.cpp (8720B)


      1 #include <stdlib.h>
      2 
      3 #include "bl_qt_headers.h"
      4 #include "bl_qt_canvas.h"
      5 
      6 class MainWindow : public QWidget {
      7   Q_OBJECT
      8 
      9 public:
     10   QTimer _timer;
     11   QBLCanvas _canvas;
     12   QComboBox _renderer_select;
     13   QComboBox _style_select;
     14   QCheckBox _limit_fps_check;
     15 
     16   bool _animate = true;
     17   double _time {};
     18   int _count {};
     19 
     20   enum class StyleId {
     21     kSolid,
     22     kLinear,
     23     kRadial,
     24     kConic
     25   };
     26 
     27   MainWindow() {
     28     QVBoxLayout* vBox = new QVBoxLayout();
     29     vBox->setContentsMargins(0, 0, 0, 0);
     30     vBox->setSpacing(0);
     31 
     32     QGridLayout* grid = new QGridLayout();
     33     grid->setContentsMargins(5, 5, 5, 5);
     34     grid->setSpacing(5);
     35 
     36     QBLCanvas::init_renderer_select_box(&_renderer_select);
     37     _limit_fps_check.setText(QLatin1String("Limit FPS"));
     38 
     39     _style_select.addItem("Solid Color", QVariant(int(StyleId::kSolid)));
     40     _style_select.addItem("Linear Gradient", QVariant(int(StyleId::kLinear)));
     41     _style_select.addItem("Radial Gradient", QVariant(int(StyleId::kRadial)));
     42     _style_select.addItem("Conic Gradient", QVariant(int(StyleId::kConic)));
     43     _style_select.setCurrentIndex(1);
     44 
     45     connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int)));
     46     connect(&_limit_fps_check, SIGNAL(stateChanged(int)), SLOT(onLimitFpsChanged(int)));
     47 
     48     grid->addWidget(new QLabel("Renderer:"), 0, 0);
     49     grid->addWidget(&_renderer_select, 0, 1);
     50 
     51     grid->addWidget(new QLabel("Style:"), 0, 2);
     52     grid->addWidget(&_style_select, 0, 3);
     53 
     54     grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 4);
     55     grid->addWidget(&_limit_fps_check, 0, 5, Qt::AlignRight);
     56 
     57     _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1);
     58     _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1);
     59 
     60     vBox->addItem(grid);
     61     vBox->addWidget(&_canvas);
     62     setLayout(vBox);
     63 
     64     connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer()));
     65     connect(new QShortcut(QKeySequence(Qt::Key_P), this), SIGNAL(activated()), SLOT(onToggleAnimate()));
     66 
     67     onInit();
     68   }
     69 
     70   void showEvent(QShowEvent* event) override { _timer.start(); }
     71   void hideEvent(QHideEvent* event) override { _timer.stop(); }
     72   void keyPressEvent(QKeyEvent* event) override {}
     73 
     74   void onInit() {
     75     _time = 0;
     76     _count = 0;
     77     _limit_fps_check.setChecked(true);
     78     _updateTitle();
     79   }
     80 
     81   Q_SLOT void onToggleAnimate() { _animate = !_animate; }
     82   Q_SLOT void onRendererChanged(int index) { _canvas.set_renderer_type(_renderer_select.itemData(index).toInt()); }
     83   Q_SLOT void onLimitFpsChanged(int value) { _timer.setInterval(value ? 1000 / 120 : 0); }
     84 
     85   Q_SLOT void onTimer() {
     86     if (_animate) {
     87       _time += 2.0;
     88     }
     89 
     90     _updateTitle();
     91     _canvas.update_canvas(true);
     92   }
     93 
     94   inline StyleId getStyleId() const noexcept { return StyleId(_style_select.currentData().toInt()); }
     95 
     96   void on_render_blend2d(BLContext& ctx) noexcept {
     97     ctx.fill_all(BLRgba32(0xFF000000));
     98 
     99     StyleId styleId = getStyleId();
    100     double kMarginSize = 7;
    101     double kSquareSize = 45;
    102     double kFullSize = kSquareSize + kMarginSize * 2.0;
    103     double kHalfSize = kFullSize / 2.0;
    104 
    105     int w = (_canvas.image_width() + kFullSize - 1) / kFullSize;
    106     int h = (_canvas.image_height() + kFullSize - 1) / kFullSize;
    107 
    108     int count = w * h;
    109     _count = count;
    110 
    111     double ix = 0;
    112     double iy = 0;
    113 
    114     double start = 0;
    115     double now = _time;
    116     double PI = 3.14159265359;
    117 
    118     BLGradient gr;
    119 
    120     switch (styleId) {
    121       case StyleId::kSolid:
    122         break;
    123       case StyleId::kLinear:
    124         gr.set_type(BL_GRADIENT_TYPE_LINEAR);
    125         gr.set_values(BLLinearGradientValues(0, kMarginSize, 0, kMarginSize + kSquareSize));
    126         break;
    127       case StyleId::kRadial:
    128         gr.set_type(BL_GRADIENT_TYPE_RADIAL);
    129         gr.set_values(BLRadialGradientValues(kHalfSize, kHalfSize, kHalfSize, kHalfSize - 15, kHalfSize));
    130         break;
    131       case StyleId::kConic:
    132         gr.set_type(BL_GRADIENT_TYPE_CONIC);
    133         gr.set_values(BLConicGradientValues(kHalfSize, kHalfSize, M_PI / -2.0, 1.0));
    134         break;
    135     }
    136 
    137     for (int i = 0; i < count; i++) {
    138       double x = ix * kFullSize;
    139       double y = iy * kFullSize;
    140 
    141       double dur = (now - start) + (i * 50);
    142       double pos = fmod(dur, 3000.0) / 3000.0;
    143       double bouncePos = bl_abs(pos * 2 - 1);
    144       double r = (bouncePos * 50 + 50) / 100;
    145       double b = ((1 - bouncePos) * 50) / 100;
    146 
    147       double rotation = pos * (PI * 2);
    148       double radius = bouncePos * 25;
    149 
    150       ctx.rotate(rotation, x + kHalfSize, y + kHalfSize);
    151       ctx.translate(x, y);
    152 
    153       BLRoundRect round_rect(kMarginSize, kMarginSize, kSquareSize, kSquareSize, radius, radius);
    154 
    155       switch (styleId) {
    156         case StyleId::kSolid: {
    157           ctx.fill_round_rect(round_rect, BLRgba32(int(r * 255), 0, int(b * 255)));
    158           break;
    159         }
    160 
    161         case StyleId::kLinear:
    162         case StyleId::kRadial: {
    163           gr.reset_stops();
    164           gr.add_stop(0, BLRgba32(0xFFFF7F00u));
    165           gr.add_stop(1, BLRgba32(int(r * 255), 0, int(b * 255)));
    166           ctx.fill_round_rect(round_rect, gr);
    167           break;
    168         }
    169 
    170         case StyleId::kConic: {
    171           gr.reset_stops();
    172           gr.add_stop(0.0, BLRgba32(0xFFFF7F00u));
    173           gr.add_stop(0.5, BLRgba32(int(r * 255), 0, int(b * 255)));
    174           gr.add_stop(1.0, BLRgba32(0xFFFF7F00u));
    175           ctx.fill_round_rect(round_rect, gr);
    176           break;
    177         }
    178       }
    179 
    180       ctx.reset_transform();
    181 
    182       if (++ix >= w) { ix = 0; iy++; }
    183     }
    184   }
    185 
    186   void on_render_qt(QPainter& ctx) noexcept {
    187     ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QColor(0, 0, 0));
    188 
    189     ctx.setRenderHint(QPainter::Antialiasing, true);
    190     ctx.setPen(Qt::NoPen);
    191 
    192     StyleId styleId = getStyleId();
    193     double kMarginSize = 7;
    194     double kSquareSize = 45;
    195     double kFullSize = kSquareSize + kMarginSize * 2.0;
    196     double kHalfSize = kFullSize / 2.0;
    197 
    198     int w = (_canvas.image_width() + kFullSize - 1) / kFullSize;
    199     int h = (_canvas.image_height() + kFullSize - 1) / kFullSize;
    200 
    201     int count = w * h;
    202     _count = count;
    203 
    204     double ix = 0;
    205     double iy = 0;
    206 
    207     double start = 0;
    208     double now = _time;
    209 
    210     for (int i = 0; i < count; i++) {
    211       double x = ix * kFullSize;
    212       double y = iy * kFullSize;
    213 
    214       double dur = (now - start) + (i * 50);
    215       double pos = fmod(dur, 3000.0) / 3000.0;
    216       double bouncePos = bl_abs(pos * 2 - 1);
    217       double r = (bouncePos * 50 + 50) / 100;
    218       double b = ((1 - bouncePos) * 50) / 100;
    219 
    220       double rotation = pos * 360;
    221       double radius = bouncePos * 25;
    222 
    223       QTransform m;
    224       m.translate(x + kHalfSize, y + kHalfSize);
    225       m.rotate(rotation);
    226       m.translate(-x - kHalfSize, -y - kHalfSize);
    227 
    228       ctx.save();
    229       ctx.setTransform(m);
    230       ctx.translate(x, y);
    231 
    232       switch (styleId) {
    233         case StyleId::kSolid: {
    234           ctx.setBrush(QBrush(QColor(qRgb(r * 255, 0, int(b * 255)))));
    235           break;
    236         }
    237 
    238         case StyleId::kLinear: {
    239           QLinearGradient gr(0, kMarginSize, 0, kMarginSize + kSquareSize);
    240           gr.setColorAt(0, QColor(qRgb(255, 127, 0)));
    241           gr.setColorAt(1, QColor(qRgb(r * 255, 0, int(b * 255))));
    242           ctx.setBrush(QBrush(gr));
    243           break;
    244         }
    245 
    246         case StyleId::kRadial: {
    247           QRadialGradient gr(kHalfSize, kHalfSize, kHalfSize, kHalfSize, kHalfSize - 15);
    248           gr.setColorAt(0, QColor(qRgb(255, 127, 0)));
    249           gr.setColorAt(1, QColor(qRgb(r * 255, 0, int(b * 255))));
    250           ctx.setBrush(QBrush(gr));
    251           break;
    252         }
    253 
    254         case StyleId::kConic: {
    255           QConicalGradient gr(kHalfSize, kHalfSize, 270);
    256           gr.setColorAt(0.0, QColor(qRgb(r * 255, 0, int(b * 255))));
    257           gr.setColorAt(0.5, QColor(qRgb(255, 127, 0)));
    258           gr.setColorAt(1.0, QColor(qRgb(r * 255, 0, int(b * 255))));
    259           ctx.setBrush(QBrush(gr));
    260           break;
    261         }
    262       }
    263 
    264       ctx.drawRoundedRect(kMarginSize, kMarginSize, kSquareSize, kSquareSize, radius, radius);
    265       ctx.restore();
    266 
    267       if (++ix >= w) { ix = 0; iy++; }
    268     }
    269   }
    270 
    271   void _updateTitle() {
    272     char buf[256];
    273     snprintf(buf, 256, "Bounces [%dx%d] [Count=%d] [RenderTime=%.2fms FPS=%.1f]",
    274       _canvas.image_width(),
    275       _canvas.image_height(),
    276       _count,
    277       _canvas.average_render_time(),
    278       _canvas.fps());
    279 
    280     QString title = QString::fromUtf8(buf);
    281     if (title != windowTitle())
    282       setWindowTitle(title);
    283   }
    284 };
    285 
    286 int main(int argc, char *argv[]) {
    287   QApplication app(argc, argv);
    288   MainWindow win;
    289 
    290   win.setMinimumSize(QSize(400, 320));
    291   win.resize(QSize(580, 520));
    292   win.show();
    293 
    294   return app.exec();
    295 }
    296 
    297 #include "bl_demo_bounces.moc"