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"