bl_demo_paths.cpp (12338B)
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 _slider; 13 QBLCanvas _canvas; 14 QComboBox _renderer_select; 15 QCheckBox _limit_fps_check; 16 QComboBox _operation_select; 17 QComboBox _style_select; 18 19 bool _animate = true; 20 int _op = 0; 21 std::vector<BLPoint> _poly; 22 std::vector<BLPoint> _step; 23 24 BLRandom _random; 25 26 MainWindow() : _random(0x1234) { 27 QVBoxLayout* vBox = new QVBoxLayout(); 28 vBox->setContentsMargins(0, 0, 0, 0); 29 vBox->setSpacing(0); 30 31 QGridLayout* grid = new QGridLayout(); 32 grid->setContentsMargins(5, 5, 5, 5); 33 grid->setSpacing(5); 34 35 QBLCanvas::init_renderer_select_box(&_renderer_select); 36 _limit_fps_check.setText(QLatin1String("Limit FPS")); 37 38 _operation_select.addItem("Fill Poly", QVariant(int(0))); 39 _operation_select.addItem("Stroke Poly [W=1]", QVariant(int(1))); 40 _operation_select.addItem("Stroke Poly [W=3]", QVariant(int(2))); 41 _operation_select.addItem("Stroke Poly [W=5]", QVariant(int(3))); 42 43 _operation_select.addItem("Fill Quads", QVariant(int(4))); 44 _operation_select.addItem("Stroke Quads [W=1]", QVariant(int(5))); 45 _operation_select.addItem("Stroke Quads [W=3]", QVariant(int(6))); 46 _operation_select.addItem("Stroke Quads [W=5]", QVariant(int(7))); 47 48 _operation_select.addItem("Fill Cubics", QVariant(int(8))); 49 _operation_select.addItem("Stroke Cubics [W=1]", QVariant(int(9))); 50 _operation_select.addItem("Stroke Cubics [W=3]", QVariant(int(10))); 51 _operation_select.addItem("Stroke Cubics [W=5]", QVariant(int(11))); 52 53 _style_select.addItem("Solid Color", QVariant(int(0))); 54 _style_select.addItem("Linear Gradient", QVariant(int(1))); 55 _style_select.addItem("Radial Gradient", QVariant(int(2))); 56 _style_select.addItem("Conic Gradient", QVariant(int(3))); 57 58 _slider.setOrientation(Qt::Horizontal); 59 _slider.setMinimum(4); 60 _slider.setMaximum(2000); 61 _slider.setSliderPosition(10); 62 63 _canvas.on_render_blend2d = std::bind(&MainWindow::on_render_blend2d, this, std::placeholders::_1); 64 _canvas.on_render_qt = std::bind(&MainWindow::on_render_qt, this, std::placeholders::_1); 65 66 connect(&_renderer_select, SIGNAL(activated(int)), SLOT(onRendererChanged(int))); 67 connect(&_limit_fps_check, SIGNAL(stateChanged(int)), SLOT(onLimitFpsChanged(int))); 68 connect(&_operation_select, SIGNAL(activated(int)), SLOT(onOperationChanged(int))); 69 connect(&_style_select, SIGNAL(activated(int)), SLOT(onStyleChanged(int))); 70 connect(&_slider, SIGNAL(valueChanged(int)), SLOT(onPolySizeChanged(int))); 71 72 grid->addWidget(new QLabel("Renderer:"), 0, 0); 73 grid->addWidget(&_renderer_select, 0, 1); 74 grid->addWidget(new QLabel("Op:"), 0, 2); 75 grid->addWidget(&_operation_select, 0, 3); 76 grid->addWidget(new QLabel("Style:"), 0, 4); 77 grid->addWidget(&_style_select, 0, 5); 78 79 grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), 0, 6); 80 grid->addWidget(&_limit_fps_check, 0, 7); 81 82 grid->addWidget(new QLabel("Count:"), 1, 0, Qt::AlignRight); 83 grid->addWidget(&_slider, 1, 1, 1, 7); 84 85 vBox->addLayout(grid); 86 vBox->addWidget(&_canvas); 87 setLayout(vBox); 88 89 connect(&_timer, SIGNAL(timeout()), this, SLOT(onTimer())); 90 connect(new QShortcut(QKeySequence(Qt::Key_P), this), SIGNAL(activated()), SLOT(onToggleAnimate())); 91 92 onInit(); 93 } 94 95 void showEvent(QShowEvent* event) override { _timer.start(); } 96 void hideEvent(QHideEvent* event) override { _timer.stop(); } 97 98 void onInit() { 99 setPolySize(50); 100 _limit_fps_check.setChecked(true); 101 _updateTitle(); 102 } 103 104 double randomSign() noexcept { return _random.next_double() < 0.5 ? 1.0 : -1.0; } 105 106 Q_SLOT void onToggleAnimate() { _animate = !_animate; } 107 Q_SLOT void onStyleChanged(int index) { _canvas.update_canvas(); } 108 Q_SLOT void onRendererChanged(int index) { _canvas.set_renderer_type(_renderer_select.itemData(index).toInt()); } 109 Q_SLOT void onLimitFpsChanged(int value) { _timer.setInterval(value ? 1000 / 120 : 0); } 110 Q_SLOT void onOperationChanged(int index) { _op = index; } 111 Q_SLOT void onPolySizeChanged(int value) { setPolySize(size_t(value)); } 112 113 Q_SLOT void onTimer() { 114 if (_animate) { 115 double w = _canvas.image_width(); 116 double h = _canvas.image_height(); 117 118 size_t size = _poly.size(); 119 for (size_t i = 0; i < size; i++) { 120 BLPoint& vertex = _poly[i]; 121 BLPoint& step = _step[i]; 122 123 vertex += step; 124 if (vertex.x <= 0.0 || vertex.x >= w) { 125 step.x = -step.x; 126 vertex.x = bl_min(vertex.x + step.x, w); 127 } 128 129 if (vertex.y <= 0.0 || vertex.y >= h) { 130 step.y = -step.y; 131 vertex.y = bl_min(vertex.y + step.y, h); 132 } 133 } 134 } 135 136 _canvas.update_canvas(true); 137 _updateTitle(); 138 } 139 140 void on_render_blend2d(BLContext& ctx) noexcept { 141 ctx.fill_all(BLRgba32(0xFF000000)); 142 ctx.set_fill_rule(BL_FILL_RULE_EVEN_ODD); 143 144 int styleId = _style_select.currentIndex(); 145 146 switch (styleId) { 147 default: 148 case 0: { 149 ctx.set_fill_style(BLRgba32(0xFFFFFFFF)); 150 ctx.set_stroke_style(BLRgba32(0xFFFFFFFF)); 151 break; 152 } 153 154 case 1: { 155 double w = _canvas.image_width(); 156 double h = _canvas.image_height(); 157 158 BLGradient g(BLLinearGradientValues(0, 0, w, h)); 159 g.add_stop(0.0, BLRgba32(0xFFFF0000)); 160 g.add_stop(0.5, BLRgba32(0xFFAF00AF)); 161 g.add_stop(1.0, BLRgba32(0xFF0000FF)); 162 163 ctx.set_fill_style(g); 164 ctx.set_stroke_style(g); 165 break; 166 } 167 168 case 2: { 169 double w = _canvas.image_width(); 170 double h = _canvas.image_height(); 171 double r = bl_min(w, h); 172 173 BLGradient g(BLRadialGradientValues(w * 0.5, h * 0.5, w * 0.5, h * 0.5, r * 0.5)); 174 g.add_stop(0.0, BLRgba32(0xFFFF0000)); 175 g.add_stop(0.5, BLRgba32(0xFFAF00AF)); 176 g.add_stop(1.0, BLRgba32(0xFF0000FF)); 177 178 ctx.set_fill_style(g); 179 ctx.set_stroke_style(g); 180 break; 181 } 182 183 case 3: { 184 double w = _canvas.image_width(); 185 double h = _canvas.image_height(); 186 187 BLGradient g(BLConicGradientValues(w * 0.5, h * 0.5, 0.0)); 188 g.add_stop(0.00, BLRgba32(0xFFFF0000)); 189 g.add_stop(0.33, BLRgba32(0xFFAF00AF)); 190 g.add_stop(0.66, BLRgba32(0xFF0000FF)); 191 g.add_stop(1.00, BLRgba32(0xFFFF0000)); 192 193 ctx.set_fill_style(g); 194 ctx.set_stroke_style(g); 195 break; 196 } 197 } 198 199 switch (_op) { 200 case 0: { 201 ctx.fill_polygon(_poly.data(), _poly.size()); 202 break; 203 } 204 205 case 1: 206 case 2: 207 case 3: { 208 ctx.set_stroke_width((_op - 1) * 2 + 1); 209 ctx.stroke_polygon(_poly.data(), _poly.size()); 210 break; 211 } 212 213 case 4: 214 case 5: 215 case 6: 216 case 7: { 217 const BLPoint* poly = _poly.data(); 218 size_t n = _poly.size(); 219 BLPath path; 220 path.move_to(poly[0]); 221 for (size_t i = 3; i <= n; i += 2) 222 path.quad_to(poly[i - 2], poly[i - 1]); 223 224 if (_op == 4) { 225 ctx.fill_path(path); 226 } 227 else { 228 ctx.set_stroke_width((_op - 5) * 2 + 1); 229 ctx.stroke_path(path); 230 } 231 break; 232 } 233 234 case 8: 235 case 9: 236 case 10: 237 case 11: { 238 const BLPoint* poly = _poly.data(); 239 size_t n = _poly.size(); 240 BLPath path; 241 path.move_to(poly[0]); 242 for (size_t i = 4; i <= n; i += 3) 243 path.cubic_to(poly[i - 3], poly[i - 2], poly[i - 1]); 244 245 if (_op == 8) { 246 ctx.fill_path(path); 247 } 248 else { 249 ctx.set_stroke_width((_op - 9) * 2 + 1); 250 ctx.stroke_path(path); 251 } 252 break; 253 } 254 } 255 } 256 257 void on_render_qt(QPainter& ctx) noexcept { 258 ctx.fillRect(0, 0, _canvas.image_width(), _canvas.image_height(), QColor(0, 0, 0)); 259 ctx.setRenderHint(QPainter::Antialiasing, true); 260 261 int styleId = _style_select.currentIndex(); 262 QBrush brush; 263 264 switch (styleId) { 265 default: 266 case 0: { 267 brush = QColor(255, 255, 255); 268 break; 269 } 270 271 case 1: { 272 double w = _canvas.image_width(); 273 double h = _canvas.image_height(); 274 275 QLinearGradient g(qreal(0), qreal(0), qreal(w), qreal(h)); 276 g.setColorAt(0.0f, QColor(0xFF, 0x00, 0x00)); 277 g.setColorAt(0.5f, QColor(0xAF, 0x00, 0xAF)); 278 g.setColorAt(1.0f, QColor(0x00, 0x00, 0xFF)); 279 280 brush = QBrush(g); 281 break; 282 } 283 284 case 2: { 285 double w = _canvas.image_width(); 286 double h = _canvas.image_height(); 287 double r = bl_min(w, h); 288 289 QRadialGradient g(qreal(w * 0.5), qreal(h * 0.5), qreal(r * 0.5), qreal(w * 0.5), qreal(h * 0.5)); 290 g.setColorAt(0.0f, QColor(0xFF, 0x00, 0x00)); 291 g.setColorAt(0.5f, QColor(0xAF, 0x00, 0xAF)); 292 g.setColorAt(1.0f, QColor(0x00, 0x00, 0xFF)); 293 294 brush = QBrush(g); 295 break; 296 } 297 298 case 3: { 299 double w = _canvas.image_width(); 300 double h = _canvas.image_height(); 301 302 QConicalGradient g(qreal(w * 0.5), qreal(h * 0.5), 0.0); 303 g.setColorAt(0.00f, QColor(0xFF, 0x00, 0x00)); 304 g.setColorAt(0.66f, QColor(0xAF, 0x00, 0xAF)); 305 g.setColorAt(0.33f, QColor(0x00, 0x00, 0xFF)); 306 g.setColorAt(1.00f, QColor(0xFF, 0x00, 0x00)); 307 308 brush = QBrush(g); 309 break; 310 } 311 } 312 313 ctx.setBrush(brush); 314 315 QPen pen(brush, 1.0f); 316 pen.setJoinStyle(Qt::MiterJoin); 317 318 switch (_op) { 319 case 0: { 320 ctx.setPen(Qt::NoPen); 321 ctx.drawPolygon(reinterpret_cast<const QPointF*>(_poly.data()), int(_poly.size()), Qt::OddEvenFill); 322 break; 323 } 324 325 case 1: 326 case 2: 327 case 3: 328 pen.setWidth((_op - 1) * 2 + 1); 329 ctx.setBrush(Qt::NoBrush); 330 ctx.setPen(pen); 331 ctx.drawPolygon(reinterpret_cast<const QPointF*>(_poly.data()), int(_poly.size()), Qt::OddEvenFill); 332 break; 333 334 case 4: 335 case 5: 336 case 6: 337 case 7: { 338 const BLPoint* poly = _poly.data(); 339 size_t n = _poly.size(); 340 QPainterPath path; 341 path.moveTo(poly[0].x, poly[0].y); 342 for (size_t i = 3; i <= n; i += 2) 343 path.quadTo(poly[i - 2].x, poly[i - 2].y, poly[i - 1].x, poly[i - 1].y); 344 345 if (_op == 4) { 346 path.setFillRule(Qt::OddEvenFill); 347 ctx.fillPath(path, brush); 348 } 349 else { 350 pen.setWidth((_op - 5) * 2 + 1); 351 ctx.strokePath(path, pen); 352 } 353 break; 354 } 355 356 case 8: 357 case 9: 358 case 10: 359 case 11: { 360 const BLPoint* poly = _poly.data(); 361 size_t n = _poly.size(); 362 QPainterPath path; 363 path.moveTo(poly[0].x, poly[0].y); 364 for (size_t i = 4; i <= n; i += 3) 365 path.cubicTo(poly[i - 3].x, poly[i - 3].y, poly[i - 2].x, poly[i - 2].y, poly[i - 1].x, poly[i - 1].y); 366 367 if (_op == 8) { 368 path.setFillRule(Qt::OddEvenFill); 369 ctx.fillPath(path, brush); 370 } 371 else { 372 pen.setWidth((_op - 9) * 2 + 1); 373 ctx.strokePath(path, pen); 374 } 375 break; 376 } 377 } 378 } 379 380 void setPolySize(size_t size) { 381 double w = _canvas.image_width(); 382 double h = _canvas.image_height(); 383 size_t prev = _poly.size(); 384 385 _poly.resize(size); 386 _step.resize(size); 387 388 while (prev < size) { 389 _poly[prev].reset(_random.next_double() * w, 390 _random.next_double() * h); 391 _step[prev].reset((_random.next_double() * 0.5 + 0.05) * randomSign(), 392 (_random.next_double() * 0.5 + 0.05) * randomSign()); 393 prev++; 394 } 395 } 396 397 void _updateTitle() { 398 char buf[256]; 399 snprintf(buf, 256, "Paths [%dx%d] [Size=%zu] [RenderTime=%.2fms FPS=%.1f]", 400 _canvas.image_width(), 401 _canvas.image_height(), 402 _poly.size(), 403 _canvas.average_render_time(), 404 _canvas.fps()); 405 406 QString title = QString::fromUtf8(buf); 407 if (title != windowTitle()) 408 setWindowTitle(title); 409 } 410 }; 411 412 int main(int argc, char *argv[]) { 413 QApplication app(argc, argv); 414 MainWindow win; 415 416 win.setMinimumSize(QSize(400, 320)); 417 win.resize(QSize(580, 520)); 418 win.show(); 419 420 return app.exec(); 421 } 422 423 #include "bl_demo_paths.moc"