odin-blend2d

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

bl_demo_gradients.cpp (12214B)


      1 #include <blend2d.h>
      2 
      3 #include "bl_qt_headers.h"
      4 #include "bl_qt_canvas.h"
      5 
      6 class MainWindow : public QWidget {
      7   Q_OBJECT
      8 public:
      9   // Widgets.
     10   QComboBox _renderer_select;
     11   QComboBox _gradient_type_select;
     12   QComboBox _extend_mode_select;
     13   QSlider _parameter_slider1;
     14   QSlider _parameter_slider2;
     15   QLabel _parameter_label1;
     16   QLabel _parameter_label2;
     17   QSlider _stop_sliders[9];
     18   QCheckBox _control_check_box;
     19   QCheckBox _dither_check_box;
     20   QBLCanvas _canvas;
     21 
     22   // Canvas data.
     23   BLPoint _pts[2] {};
     24   BLGradientType _gradient_type = BL_GRADIENT_TYPE_LINEAR;
     25   BLExtendMode _gradient_extend_mode = BL_EXTEND_MODE_PAD;
     26   size_t _num_points = 2;
     27   size_t _closest_vertex = SIZE_MAX;
     28   size_t _grabbed_vertex = SIZE_MAX;
     29   int _grabbed_x = 0;
     30   int _grabbed_y = 0;
     31 
     32   MainWindow() {
     33     setWindowTitle(QLatin1String("Gradients"));
     34 
     35     QVBoxLayout* vBox = new QVBoxLayout();
     36     vBox->setContentsMargins(0, 0, 0, 0);
     37     vBox->setSpacing(0);
     38 
     39     QGridLayout* grid = new QGridLayout();
     40     grid->setContentsMargins(5, 5, 5, 5);
     41     grid->setSpacing(5);
     42 
     43     QBLCanvas::init_renderer_select_box(&_renderer_select);
     44     connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int)));
     45 
     46     _gradient_type_select.addItem("Linear", QVariant(int(BL_GRADIENT_TYPE_LINEAR)));
     47     _gradient_type_select.addItem("Radial", QVariant(int(BL_GRADIENT_TYPE_RADIAL)));
     48     _gradient_type_select.addItem("Conic", QVariant(int(BL_GRADIENT_TYPE_CONIC)));
     49     connect(&_gradient_type_select, SIGNAL(activated(int)), SLOT(onGradientTypeChanged(int)));
     50 
     51     _extend_mode_select.addItem("Pad", QVariant(int(BL_EXTEND_MODE_PAD)));
     52     _extend_mode_select.addItem("Repeat", QVariant(int(BL_EXTEND_MODE_REPEAT)));
     53     _extend_mode_select.addItem("Reflect", QVariant(int(BL_EXTEND_MODE_REFLECT)));
     54     connect(&_extend_mode_select, SIGNAL(activated(int)), SLOT(onExtendModeChanged(int)));
     55 
     56     _parameter_slider1.setOrientation(Qt::Horizontal);
     57     _parameter_slider1.setMinimum(0);
     58     _parameter_slider1.setMaximum(720);
     59     _parameter_slider1.setSliderPosition(360);
     60     connect(&_parameter_slider1, SIGNAL(valueChanged(int)), SLOT(onParameterChanged(int)));
     61 
     62     _parameter_slider2.setOrientation(Qt::Horizontal);
     63     _parameter_slider2.setMinimum(0);
     64     _parameter_slider2.setMaximum(720);
     65     _parameter_slider2.setSliderPosition(0);
     66     connect(&_parameter_slider2, SIGNAL(valueChanged(int)), SLOT(onParameterChanged(int)));
     67 
     68     _control_check_box.setText("Control");
     69     _control_check_box.setChecked(true);
     70     connect(&_control_check_box, SIGNAL(stateChanged(int)), SLOT(onParameterChanged(int)));
     71 
     72     _dither_check_box.setText("Dither");
     73     connect(&_dither_check_box, SIGNAL(stateChanged(int)), SLOT(onParameterChanged(int)));
     74 
     75     const uint32_t initialColors[3] = {
     76       0xFF000000,
     77       0xFFFF0000,
     78       0xFFFFFFFF
     79     };
     80 
     81     for (uint32_t stop_id = 0; stop_id < 3; stop_id++) {
     82       uint32_t color = initialColors[stop_id];
     83       for (uint32_t component = 0; component < 3; component++, color <<= 8) {
     84         uint32_t sliderId = stop_id * 3 + component;
     85         _stop_sliders[sliderId].setOrientation(Qt::Horizontal);
     86         _stop_sliders[sliderId].setMinimum(0);
     87         _stop_sliders[sliderId].setMaximum(255);
     88         _stop_sliders[sliderId].setSliderPosition((color >> 16) & 0xFF);
     89 
     90         static const char channels[] = "R:\0G:\0B:\0";
     91         grid->addWidget(new QLabel(QString::fromLatin1(channels + component * 3, 2)), component, stop_id * 2 + 2);
     92         grid->addWidget(&_stop_sliders[sliderId], component, stop_id * 2 + 3);
     93         connect(&_stop_sliders[sliderId], SIGNAL(valueChanged(int)), SLOT(onParameterChanged(int)));
     94       }
     95     }
     96 
     97     QPushButton* randomizeButton = new QPushButton("Random");
     98     connect(randomizeButton, SIGNAL(clicked()), SLOT(onRandomizeVertices()));
     99 
    100     grid->addWidget(new QLabel("Renderer:"), 0, 0, Qt::AlignRight);
    101     grid->addWidget(&_renderer_select, 0, 1);
    102     grid->addWidget(new QLabel("Gradient:"), 1, 0, Qt::AlignRight);
    103     grid->addWidget(&_gradient_type_select, 1, 1);
    104     grid->addItem(new QSpacerItem(0, 10), 0, 2);
    105     grid->addWidget(new QLabel("Extend Mode:"), 2, 0);
    106     grid->addWidget(&_extend_mode_select, 2, 1);
    107 
    108     grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 7);
    109     grid->addWidget(randomizeButton, 0, 8);
    110 
    111     grid->addWidget(&_parameter_label1, 3, 0, Qt::AlignRight);
    112     grid->addWidget(&_parameter_slider1, 3, 1, 1, 8);
    113 
    114     grid->addWidget(&_parameter_label2, 4, 0, Qt::AlignRight);
    115     grid->addWidget(&_parameter_slider2, 4, 1, 1, 8);
    116 
    117     grid->addWidget(&_control_check_box, 1, 8);
    118     grid->addWidget(&_dither_check_box, 2, 8);
    119 
    120     vBox->addItem(grid);
    121     vBox->addWidget(&_canvas);
    122     setLayout(vBox);
    123 
    124     _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1);
    125     _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1);
    126     _canvas.on_mouse_event = std::bind(&MainWindow::on_mouse_event, this, std::placeholders::_1);
    127 
    128     onInit();
    129     updateLabels();
    130   }
    131 
    132   void updateLabels() {
    133     switch (_gradient_type) {
    134       case BL_GRADIENT_TYPE_LINEAR:
    135         _parameter_label1.setText("(Unused)");
    136         _parameter_label2.setText("(Unused)");
    137         break;
    138 
    139       case BL_GRADIENT_TYPE_RADIAL:
    140         _parameter_label1.setText("Center Rad");
    141         _parameter_label2.setText("Focal Rad");
    142         break;
    143 
    144       case BL_GRADIENT_TYPE_CONIC:
    145         _parameter_label1.setText("Angle");
    146         _parameter_label2.setText("Repeat");
    147         break;
    148     }
    149   }
    150 
    151   void keyPressEvent(QKeyEvent *event) override {}
    152 
    153   void onInit() {
    154     _pts[0].reset(350, 300);
    155     _pts[1].reset(200, 150);
    156   }
    157 
    158   size_t getClosestVertex(BLPoint p, double maxDistance) noexcept {
    159     size_t closestIndex = SIZE_MAX;
    160     double closestDistance = std::numeric_limits<double>::max();
    161     for (size_t i = 0; i < _num_points; i++) {
    162       double d = std::hypot(_pts[i].x - p.x, _pts[i].y - p.y);
    163       if (d < closestDistance && d < maxDistance) {
    164         closestIndex = i;
    165         closestDistance = d;
    166       }
    167     }
    168     return closestIndex;
    169   }
    170 
    171   double sliderAngle(double scale) {
    172     return double(_parameter_slider1.value()) / 720.0 * scale;
    173   }
    174 
    175   void on_mouse_event(QMouseEvent* event) {
    176     double mx = event->position().x() * devicePixelRatio();
    177     double my = event->position().y() * devicePixelRatio();
    178 
    179     if (event->type() == QEvent::MouseButtonPress) {
    180       if (event->button() == Qt::LeftButton) {
    181         if (_closest_vertex != SIZE_MAX) {
    182           _grabbed_vertex = _closest_vertex;
    183           _grabbed_x = mx;
    184           _grabbed_y = my;
    185           _canvas.update_canvas();
    186         }
    187       }
    188     }
    189 
    190     if (event->type() == QEvent::MouseButtonRelease) {
    191       if (event->button() == Qt::LeftButton) {
    192         if (_grabbed_vertex != SIZE_MAX) {
    193           _grabbed_vertex = SIZE_MAX;
    194           _canvas.update_canvas();
    195         }
    196       }
    197     }
    198 
    199     if (event->type() == QEvent::MouseMove) {
    200       if (_grabbed_vertex == SIZE_MAX) {
    201         _closest_vertex = getClosestVertex(BLPoint(mx, my), 5);
    202         _canvas.update_canvas();
    203       }
    204       else {
    205         _pts[_grabbed_vertex] = BLPoint(mx, my);
    206         _canvas.update_canvas();
    207       }
    208     }
    209   }
    210 
    211   Q_SLOT void onRendererChanged(int index) {
    212     _canvas.set_renderer_type(_renderer_select.itemData(index).toInt());
    213   }
    214 
    215   Q_SLOT void onGradientTypeChanged(int index) {
    216     _num_points = index == BL_GRADIENT_TYPE_CONIC ? 1 : 2;
    217     _gradient_type = BLGradientType(index);
    218     updateLabels();
    219     _canvas.update_canvas();
    220   }
    221 
    222   Q_SLOT void onExtendModeChanged(int index) {
    223     _gradient_extend_mode = (BLExtendMode)index;
    224     _canvas.update_canvas();
    225   }
    226 
    227   Q_SLOT void onParameterChanged(int value) {
    228     _canvas.update_canvas();
    229   }
    230 
    231   Q_SLOT void onRandomizeVertices() {
    232     _pts[0].x = (double(rand() % 65536) / 65535.0) * (_canvas.image_width()  - 1) + 0.5;
    233     _pts[0].y = (double(rand() % 65536) / 65535.0) * (_canvas.image_height() - 1) + 0.5;
    234     _pts[1].x = (double(rand() % 65536) / 65535.0) * (_canvas.image_width()  - 1) + 0.5;
    235     _pts[1].y = (double(rand() % 65536) / 65535.0) * (_canvas.image_height() - 1) + 0.5;
    236     _canvas.update_canvas();
    237   }
    238 
    239   void on_render_blend2d(BLContext& ctx) noexcept {
    240     if (_dither_check_box.isChecked()) {
    241       ctx.set_gradient_quality(BL_GRADIENT_QUALITY_DITHER);
    242     }
    243 
    244     BLGradient gradient;
    245     gradient.set_type(_gradient_type);
    246     gradient.set_extend_mode(_gradient_extend_mode);
    247     gradient.reset_stops();
    248 
    249     constexpr double offsets[] = { 0.0, 0.5, 1.0 };
    250     for (uint32_t stop_id = 0; stop_id < 3; stop_id++) {
    251       uint32_t r = uint32_t(_stop_sliders[stop_id * 3 + 0].value());
    252       uint32_t g = uint32_t(_stop_sliders[stop_id * 3 + 1].value());
    253       uint32_t b = uint32_t(_stop_sliders[stop_id * 3 + 2].value());
    254       gradient.add_stop(offsets[stop_id], BLRgba32(r, g, b));
    255     }
    256 
    257     if (_gradient_type == BL_GRADIENT_TYPE_LINEAR) {
    258       gradient.set_values(BLLinearGradientValues(_pts[0].x, _pts[0].y, _pts[1].x, _pts[1].y));
    259     }
    260     else if (_gradient_type == BL_GRADIENT_TYPE_RADIAL) {
    261       double s1 = double(_parameter_slider1.value());
    262       double s2 = double(_parameter_slider2.value());
    263       gradient.set_values(BLRadialGradientValues(_pts[0].x, _pts[0].y, _pts[1].x, _pts[1].y, s1, s2));
    264     }
    265     else {
    266       double a = sliderAngle(3.14159265358979323846 * 2.0);
    267       double s2 = double(_parameter_slider2.value());
    268       gradient.set_values(BLConicGradientValues(_pts[0].x, _pts[0].y, a, s2 / 100.0 + 1.0));
    269     }
    270 
    271     ctx.fill_all(gradient);
    272 
    273     if (_control_check_box.isChecked()) {
    274       for (size_t i = 0; i < _num_points; i++) {
    275         ctx.stroke_circle(_pts[i].x, _pts[i].y, 3, i == _closest_vertex ? BLRgba32(0xFF00FFFFu) : BLRgba32(0xFF007FFFu));
    276       }
    277     }
    278   }
    279 
    280   void on_render_qt(QPainter& ctx) noexcept {
    281     ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QColor(255, 0, 0));
    282     ctx.setRenderHint(QPainter::Antialiasing, true);
    283 
    284     QGradientStops stops;
    285     const double offsets[] = { 0.0, 0.5, 1.0 };
    286 
    287     for (uint32_t stop_id = 0; stop_id < 3; stop_id++) {
    288       uint32_t r = uint32_t(_stop_sliders[stop_id * 3 + 0].value());
    289       uint32_t g = uint32_t(_stop_sliders[stop_id * 3 + 1].value());
    290       uint32_t b = uint32_t(_stop_sliders[stop_id * 3 + 2].value());
    291       stops.append(QGradientStop(qreal(offsets[stop_id]), QColor(qRgb(r, g, b))));
    292     }
    293 
    294     QGradient::Spread spread = QGradient::PadSpread;
    295     if (_gradient_extend_mode == BL_EXTEND_MODE_REPEAT) spread = QGradient::RepeatSpread;
    296     if (_gradient_extend_mode == BL_EXTEND_MODE_REFLECT) spread = QGradient::ReflectSpread;
    297 
    298     switch (_gradient_type) {
    299       case BL_GRADIENT_TYPE_LINEAR: {
    300         QLinearGradient g(_pts[0].x, _pts[0].y, _pts[1].x, _pts[1].y);
    301         g.setStops(stops);
    302         g.setSpread(spread);
    303         ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QBrush(g));
    304         break;
    305       }
    306 
    307       case BL_GRADIENT_TYPE_RADIAL: {
    308         QRadialGradient g(
    309           qreal(_pts[0].x), qreal(_pts[0].y), qreal(_parameter_slider1.value()),
    310           qreal(_pts[1].x), qreal(_pts[1].y), qreal(_parameter_slider2.value()));
    311         g.setStops(stops);
    312         g.setSpread(spread);
    313         ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QBrush(g));
    314         break;
    315       }
    316 
    317       case BL_GRADIENT_TYPE_CONIC: {
    318         QConicalGradient g(_pts[0].x, _pts[0].y, sliderAngle(360.0));
    319         g.setSpread(spread);
    320         g.setStops(stops);
    321         ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QBrush(g));
    322         break;
    323       }
    324     }
    325 
    326     if (_control_check_box.isChecked()) {
    327       for (size_t i = 0; i < _num_points; i++) {
    328         QColor color = bl_rgba_to_qcolor(i == _closest_vertex ? BLRgba32(0xFF00FFFFu) : BLRgba32(0xFF007FFFu));
    329         ctx.setPen(QPen(color, 1.0f));
    330         ctx.setBrush(QBrush());
    331         ctx.drawEllipse(QPointF(_pts[i].x, _pts[i].y), 3, 3);
    332       }
    333     }
    334   }
    335 };
    336 
    337 int main(int argc, char *argv[]) {
    338   QApplication app(argc, argv);
    339   MainWindow win;
    340 
    341   win.setMinimumSize(QSize(700, 650));
    342   win.show();
    343 
    344   return app.exec();
    345 }
    346 
    347 #include "bl_demo_gradients.moc"