odin-blend2d

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

bl_demo_bubbles.cpp (6751B)


      1 #include <stdlib.h>
      2 #include <cmath>
      3 #include <vector>
      4 
      5 #include "bl_qt_headers.h"
      6 #include "bl_qt_canvas.h"
      7 
      8 struct Bubble {
      9   BLPoint p;
     10   double r;
     11   double x;
     12   double a;
     13   double b;
     14   double c;
     15   BLRgba32 colors[2];
     16 };
     17 
     18 static BL_INLINE BLRgba32 random_rgba32(BLRandom& rnd) noexcept {
     19   return BLRgba32(rnd.next_uint32() | 0x55000000u);
     20 }
     21 
     22 class MainWindow : public QWidget {
     23   Q_OBJECT
     24 
     25 public:
     26   QTimer _timer;
     27   QComboBox _renderer_select;
     28   QCheckBox _limit_fps_check;
     29   QSlider _count_slider;
     30   QSlider _parameter_slider;
     31   QBLCanvas _canvas;
     32 
     33   BLRandom _rnd;
     34   std::vector<Bubble> _bubbles;
     35 
     36   bool _animate = true;
     37 
     38   MainWindow() {
     39     QVBoxLayout* vBox = new QVBoxLayout();
     40     vBox->setContentsMargins(0, 0, 0, 0);
     41     vBox->setSpacing(0);
     42 
     43     QGridLayout* grid = new QGridLayout();
     44     grid->setContentsMargins(5, 5, 5, 5);
     45     grid->setSpacing(5);
     46 
     47     QBLCanvas::init_renderer_select_box(&_renderer_select);
     48     _limit_fps_check.setText(QLatin1String("Limit FPS"));
     49 
     50     _count_slider.setMinimum(1);
     51     _count_slider.setMaximum(5000);
     52     _count_slider.setValue(100);
     53     _count_slider.setOrientation(Qt::Horizontal);
     54 
     55     _parameter_slider.setMinimum(0);
     56     _parameter_slider.setMaximum(1000);
     57     _parameter_slider.setValue(100);
     58     _parameter_slider.setOrientation(Qt::Horizontal);
     59 
     60     connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int)));
     61     connect(&_limit_fps_check, SIGNAL(stateChanged(int)), SLOT(onLimitFpsChanged(int)));
     62 
     63     grid->addWidget(new QLabel("Renderer:"), 0, 0);
     64     grid->addWidget(&_renderer_select, 0, 1);
     65     grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 3);
     66     grid->addWidget(&_limit_fps_check, 0, 4, Qt::AlignRight);
     67 
     68     grid->addWidget(new QLabel("Count:"), 1, 0, Qt::AlignRight);
     69     grid->addWidget(&_count_slider, 1, 1, 1, 5);
     70 
     71     grid->addWidget(new QLabel("Param:"), 2, 0, Qt::AlignRight);
     72     grid->addWidget(&_parameter_slider, 2, 1, 1, 5);
     73 
     74     _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1);
     75     _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1);
     76 
     77     vBox->addItem(grid);
     78     vBox->addWidget(&_canvas);
     79     setLayout(vBox);
     80 
     81     connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer()));
     82     connect(new QShortcut(QKeySequence(Qt::Key_P), this), SIGNAL(activated()), SLOT(onToggleAnimate()));
     83 
     84     onInit();
     85   }
     86 
     87   void showEvent(QShowEvent* event) override { _timer.start(); }
     88   void hideEvent(QHideEvent* event) override { _timer.stop(); }
     89   void keyPressEvent(QKeyEvent* event) override {}
     90 
     91   void onInit() {
     92     _rnd.reset(0x123456789ABCDEF);
     93     _limit_fps_check.setChecked(true);
     94     _updateTitle();
     95   }
     96 
     97   Q_SLOT void onToggleAnimate() { _animate = !_animate; }
     98   Q_SLOT void onRendererChanged(int index) { _canvas.set_renderer_type(_renderer_select.itemData(index).toInt()); }
     99   Q_SLOT void onLimitFpsChanged(int value) { _timer.setInterval(value ? 1000 / 120 : 0); }
    100 
    101   Q_SLOT void onTimer() {
    102     size_t count = unsigned(_count_slider.value());
    103 
    104     double w = _canvas.image_width();
    105     double h = _canvas.image_height();
    106 
    107     if (_bubbles.size() > count) {
    108       _bubbles.resize(count);
    109     }
    110 
    111     double param = double(_parameter_slider.value()) * 0.0001;
    112 
    113     auto newBubble = [](double w, double h, double param, BLRandom& rnd) noexcept -> Bubble {
    114       Bubble bubble {};
    115 
    116       double r = rnd.next_double() * 20.0 + 5.0;
    117 
    118       bubble.p = BLPoint(rnd.next_double() * w, h + r);
    119       bubble.r = r;
    120       bubble.x = bubble.p.x;
    121       bubble.a = rnd.next_double() * 3.0 + 1.0;
    122       bubble.b = 0.0;
    123       bubble.c = rnd.next_double() * (param * 0.4 + 0.001) + param * 0.02;
    124       bubble.colors[0] = random_rgba32(rnd);
    125       bubble.colors[1] = BLRgba32(0u);
    126 
    127       return bubble;
    128     };
    129 
    130     if (_animate) {
    131       constexpr double PI = 3.14159265359;
    132       constexpr uint32_t kAddLimit = 10;
    133 
    134       uint32_t added = 0;
    135       while (_bubbles.size() < count && added < kAddLimit) {
    136         Bubble bubble = newBubble(w, h, param, _rnd);
    137         _bubbles.push_back(bubble);
    138         added++;
    139       }
    140 
    141       for (Bubble& bubble : _bubbles) {
    142         bubble.b += bubble.c;
    143 
    144         if (bubble.b > 2.0) {
    145           bubble.b -= 2.0;
    146         }
    147 
    148         bubble.p.x = bubble.x + sin(bubble.b * PI) * 20.0;
    149         bubble.p.y -= bubble.a;
    150 
    151         if (bubble.p.y < -bubble.r) {
    152           bubble = newBubble(w, h, param, _rnd);
    153         }
    154       }
    155     }
    156 
    157     _canvas.update_canvas(true);
    158     _updateTitle();
    159   }
    160 
    161   void on_render_blend2d(BLContext& ctx) noexcept {
    162     ctx.fill_all(BLRgba32(0xFF000000u));
    163     ctx.set_comp_op(BL_COMP_OP_PLUS);
    164 
    165     BLGradient g;
    166     g.set_type(BL_GRADIENT_TYPE_RADIAL);
    167 
    168     double h = _canvas.image_height();
    169 
    170     for (const Bubble& bubble : _bubbles) {
    171       double r = bubble.r;
    172       double f = -r * 0.5 + r * (bubble.p.y / h);
    173 
    174       BLGradientStop stops[2] = {
    175         BLGradientStop(0.0, bubble.colors[0]),
    176         BLGradientStop(1.0, bubble.colors[1])
    177       };
    178 
    179       g.set_values(BLRadialGradientValues(bubble.p.x, bubble.p.y, bubble.p.x, bubble.p.y + f, r));
    180       g.assign_stops(stops, 2);
    181       ctx.fill_rect(BLRectI(int(bubble.p.x - r - 1.0), int(bubble.p.y - r - 1.0), int(r * 2.0) + 2, int(r * 2.0) + 2), g);
    182     }
    183   }
    184 
    185   void on_render_qt(QPainter& ctx) noexcept {
    186     ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QColor(0, 0, 0));
    187     ctx.setRenderHint(QPainter::Antialiasing, true);
    188     ctx.setCompositionMode(QPainter::CompositionMode_Plus);
    189     ctx.setPen(Qt::NoPen);
    190 
    191     double h = _canvas.image_height();
    192 
    193     for (const Bubble& bubble : _bubbles) {
    194       double r = bubble.r;
    195       double f = -r * 0.5 + r * (bubble.p.y / h);
    196 
    197       QRadialGradient g(bubble.p.x, bubble.p.y, r, bubble.p.x, bubble.p.y + f);
    198       g.setInterpolationMode(QGradient::InterpolationMode::ComponentInterpolation);
    199       g.setColorAt(0.0f, bl_rgba_to_qcolor(bubble.colors[0]));
    200       g.setColorAt(1.0f, bl_rgba_to_qcolor(bubble.colors[1]));
    201       ctx.fillRect(QRect(int(bubble.p.x - r - 1.0), int(bubble.p.y - r - 1.0), int(r * 2.0) + 2, int(r * 2.0) + 2), QBrush(g));
    202     }
    203   }
    204 
    205   void _updateTitle() {
    206     char buf[256];
    207     snprintf(buf, 256, "Bubbles [%dx%d] [Count=%d] [RenderTime=%.2fms FPS=%.1f]",
    208       _canvas.image_width(),
    209       _canvas.image_height(),
    210       int(_bubbles.size()),
    211       _canvas.average_render_time(),
    212       _canvas.fps());
    213 
    214     QString title = QString::fromUtf8(buf);
    215     if (title != windowTitle())
    216       setWindowTitle(title);
    217   }
    218 };
    219 
    220 int main(int argc, char *argv[]) {
    221   QApplication app(argc, argv);
    222   MainWindow win;
    223 
    224   win.setMinimumSize(QSize(400, 320));
    225   win.resize(QSize(580, 520));
    226   win.show();
    227 
    228   return app.exec();
    229 }
    230 
    231 #include "bl_demo_bubbles.moc"