From 37ba8b450c5038063cda1b082a086a604808b617 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Fri, 31 Jul 2026 20:11:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E7=80=91=E5=B8=83=E5=9B=BE=E5=90=88?= =?UTF-8?q?=E6=88=90bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Demo_Gallery/base/Tool.h | 11 +- Renderive/Axis/Abs_Axis_p.h | 2 +- .../architecture/Frame_Raster_Context.cpp | 410 ++++++++++++++---- Renderive/architecture/Frame_Raster_Context.h | 98 ++++- Renderive/architecture/Render_Data.h | 2 +- Renderive/architecture/Renderable.cpp | 4 +- Renderive/architecture/Renderable.h | 1 + Renderive/architecture/global.h | 85 ++-- Renderive/plot/Plot.cpp | 17 +- Renderive/plot/Plot.h | 2 + Renderive/plottable/Afterglow_p.h | 4 +- Renderive/plottable/Audio_Frequency_p.h | 1 - Renderive/plottable/Hover_Info.cpp | 2 +- Renderive/plottable/Hover_Info.h | 4 +- Renderive/plottable/Multi_Select_Rect_p.h | 2 +- Renderive/plottable/Performance_Shower.cpp | 55 ++- Renderive/plottable/Performance_Shower_p.h | 24 +- Renderive/plottable/Planisphere_p.h | 2 +- Renderive/plottable/Spectrum.cpp | 6 +- Renderive/plottable/Spectrum_p.h | 2 +- Renderive/plottable/Sweep_Frequency_p.h | 2 +- Renderive/plottable/Waterfall_p.h | 6 +- Renderive/plottable/export.h | 2 + Renderive/primitive/Band_Region.cpp | 27 +- Renderive/primitive/Band_Region.h | 1 - Renderive/primitive/Color_Bar.cpp | 2 +- Renderive/primitive/Color_Bar.h | 1 - Renderive/primitive/Curve.cpp | 7 +- Renderive/primitive/Curve_Utils_p.h | 30 +- Renderive/primitive/Fill_Area.cpp | 32 +- Renderive/primitive/Fill_Area.h | 1 - Renderive/primitive/Grid.cpp | 30 +- Renderive/primitive/Grid.h | 1 - Renderive/primitive/Label.cpp | 4 +- Renderive/primitive/Label.h | 1 - Renderive/primitive/Marker.cpp | 29 +- Renderive/primitive/Marker.h | 1 - Renderive/primitive/Point_Set.cpp | 19 +- Renderive/primitive/Point_Set.h | 1 - Renderive/primitive/Raster_Image.cpp | 2 +- Renderive/primitive/Raster_Image_p.h | 6 +- Widget/base/Graphic.h | 6 +- Widget/component/Table/global.h | 1 + main.cmake | 177 ++++++-- tests/Memory_Data_Path_Stress.cpp | 6 +- tests/Performance_Shower_Interaction.cpp | 15 +- 46 files changed, 778 insertions(+), 366 deletions(-) diff --git a/Demo_Gallery/base/Tool.h b/Demo_Gallery/base/Tool.h index d90d3bf..2fc01a9 100644 --- a/Demo_Gallery/base/Tool.h +++ b/Demo_Gallery/base/Tool.h @@ -268,19 +268,24 @@ static QWidget* create_image_interpolation_combo(renderive::Waterfall* waterfall layout->addWidget(combo); return container; } -static QWidget* create_control(renderive::Abs_Plot* plot, const std::vector& widgets = {}) { +static QWidget* create_control(renderive::Abs_Plot* plot, const std::vector& widgets = {}, std::shared_ptr* performance_shower_out = nullptr) { auto control = new QWidget; auto l = new QVBoxLayout(control); l->setAlignment(Qt::AlignTop); l->setContentsMargins({0, 0, 0, 0}); l->setSpacing(0); + auto performance_shower = renderive::Performance_Shower::Builder(*plot).build(); + if (performance_shower_out) + *performance_shower_out = performance_shower; std::vector ws = { renderive::create_toggle_button("暂停渲染", "开始渲染", [plot]() { plot->is_rendering() ? plot->pause_render() : plot->start_render(); }, plot->is_rendering()), renderive::create_toggle_button("停止性能测试", "启动性能测试", [plot]() { - renderive::set_performance_shower_enabled(*plot, !renderive::performance_shower_enabled(*plot)); - }, renderive::performance_shower_enabled(*plot)), + auto performance_shower = std::dynamic_pointer_cast(plot->frame_lifecycle_observer()); + if (performance_shower) + performance_shower->setEnabled(!performance_shower->enabled()); + }, performance_shower && performance_shower->enabled()), renderive::create_select_color("更改背景色", [plot](const QColor& color) { plot->set_background_color(color); }, plot->background_color()), diff --git a/Renderive/Axis/Abs_Axis_p.h b/Renderive/Axis/Abs_Axis_p.h index 9a3ff0b..9721df0 100644 --- a/Renderive/Axis/Abs_Axis_p.h +++ b/Renderive/Axis/Abs_Axis_p.h @@ -46,7 +46,7 @@ struct Abs_Axis_Private : Typed_Render_Datacoord_start, s->coord_start + s->coord_length}; diff --git a/Renderive/architecture/Frame_Raster_Context.cpp b/Renderive/architecture/Frame_Raster_Context.cpp index 270a582..e5f6861 100644 --- a/Renderive/architecture/Frame_Raster_Context.cpp +++ b/Renderive/architecture/Frame_Raster_Context.cpp @@ -1,76 +1,49 @@ #include "Frame_Raster_Context.h" + #include #include namespace renderive { -Frame_Raster_Context::Frame_Raster_Context(QImage& target, QPointF base_translation) - : target_(target), base_translation_(base_translation) { - if (target_.format() == QImage::Format_ARGB32_Premultiplied && !target_.isNull()) { - blend_image_ready_ = bl_image_.create_from_data( - target_.width(), target_.height(), BL_FORMAT_PRGB32, target_.bits(), - static_cast(target_.bytesPerLine()), BL_DATA_ACCESS_RW) == BL_SUCCESS; + +namespace { +template +void fill_glyph_path(BLContext& context, const Path& path, double dx, double dy, const QColor& color) { + if (path.isEmpty()) + return; + BLPath converted; + bool open = false; + for (int i = 0; i < path.elementCount(); ++i) { + const auto element = path.elementAt(i); + const int type = static_cast(element.type); + if (type == 0) { + if (open) + converted.close(); + converted.move_to(element.x + dx, element.y + dy); + open = true; + } + else if (type == 1) { + converted.line_to(element.x + dx, element.y + dy); + } + else if (type == 2 && i + 2 < path.elementCount()) { + const auto c1 = path.elementAt(i); + const auto c2 = path.elementAt(i + 1); + const auto end = path.elementAt(i + 2); + converted.cubic_to(c1.x + dx, c1.y + dy, c2.x + dx, c2.y + dy, end.x + dx, end.y + dy); + i += 2; + } } + if (open) + converted.close(); + context.set_fill_style(Raster_Canvas::color_of(color)); + context.fill_path(converted); +} +} // namespace + +BLRgba32 Raster_Canvas::color_of(const QColor& color) { + return BLRgba32(color.red(), color.green(), color.blue(), color.alpha()); } -Frame_Raster_Context::~Frame_Raster_Context() { - finish(); -} - -QPainter& Frame_Raster_Context::qt() { - if (backend_ == Backend::Blend2D) - end_blend(); - if (backend_ != Backend::Qt) { - qt_painter_.begin(&target_); - qt_painter_.translate(base_translation_); - backend_ = Backend::Qt; - } - return qt_painter_; -} - -BLContext* Frame_Raster_Context::blend() { - if (!blend_image_ready_) - return nullptr; - if (backend_ == Backend::Qt) - end_qt(); - if (backend_ != Backend::Blend2D) { - BLContextCreateInfo create_info{}; - create_info.thread_count = 0; - if (bl_context_.begin(bl_image_, create_info) != BL_SUCCESS) - return nullptr; - bl_context_.translate(base_translation_.x(), base_translation_.y()); - backend_ = Backend::Blend2D; - } - return &bl_context_; -} - -void Frame_Raster_Context::end_qt() { - if (backend_ == Backend::Qt) - qt_painter_.end(); - backend_ = Backend::None; -} - -void Frame_Raster_Context::end_blend() { - if (backend_ == Backend::Blend2D) - bl_context_.end(); - backend_ = Backend::None; -} - -void Frame_Raster_Context::finish() { - if (backend_ == Backend::Qt) - end_qt(); - else if (backend_ == Backend::Blend2D) - end_blend(); -} - -bool Frame_Raster_Context::solid_color(const QBrush& brush, BLRgba32& color) { - if (brush.style() != Qt::SolidPattern) - return false; - const QColor value = brush.color(); - color = BLRgba32(value.red(), value.green(), value.blue(), value.alpha()); - return true; -} - -BLStrokeCap Frame_Raster_Context::stroke_cap(Qt::PenCapStyle style) { +BLStrokeCap Raster_Canvas::stroke_cap(Qt::PenCapStyle style) { switch (style) { case Qt::FlatCap: return BL_STROKE_CAP_BUTT; case Qt::SquareCap: return BL_STROKE_CAP_SQUARE; @@ -79,7 +52,7 @@ BLStrokeCap Frame_Raster_Context::stroke_cap(Qt::PenCapStyle style) { } } -BLStrokeJoin Frame_Raster_Context::stroke_join(Qt::PenJoinStyle style) { +BLStrokeJoin Raster_Canvas::stroke_join(Qt::PenJoinStyle style) { switch (style) { case Qt::MiterJoin: return BL_STROKE_JOIN_MITER_CLIP; case Qt::BevelJoin: return BL_STROKE_JOIN_BEVEL; @@ -89,15 +62,286 @@ BLStrokeJoin Frame_Raster_Context::stroke_join(Qt::PenJoinStyle style) { } } +bool Raster_Canvas::configure_stroke() { + if (!active() || pen_.style() == Qt::NoPen) + return false; + const double width = pen_.widthF() == 0.0 ? 1.0 : pen_.widthF(); + context_->set_stroke_style(color_of(pen_.color())); + context_->set_stroke_width(width); + context_->set_stroke_caps(stroke_cap(pen_.capStyle())); + context_->set_stroke_join(stroke_join(pen_.joinStyle())); + context_->set_stroke_miter_limit(pen_.miterLimit()); + context_->set_stroke_dash_offset(pen_.dashOffset() * width); + BLArray dashes; + const QVector pattern = pen_.dashPattern(); + for (qreal dash : pattern) + dashes.append(static_cast(dash) * width); + context_->set_stroke_dash_array(dashes); + return true; +} + +bool Raster_Canvas::configure_fill() { + if (!active() || brush_.style() == Qt::NoBrush) + return false; + context_->set_fill_style(color_of(brush_.color())); + return true; +} + +void Raster_Canvas::save() { + if (!active()) + return; + context_->save(); + states_.push_back({pen_, brush_, font_}); +} + +void Raster_Canvas::restore() { + if (!active()) + return; + context_->restore(); + if (states_.empty()) + return; + pen_ = states_.back().pen; + brush_ = states_.back().brush; + font_ = states_.back().font; + states_.pop_back(); +} + +void Raster_Canvas::setPen(const QPen& pen) { + pen_ = pen; +} + +void Raster_Canvas::setBrush(const QBrush& brush) { + brush_ = brush; +} + +void Raster_Canvas::drawLine(const QPointF& p0, const QPointF& p1) { + if (configure_stroke()) + context_->stroke_line(p0.x(), p0.y(), p1.x(), p1.y()); +} + +void Raster_Canvas::drawRect(const QRectF& rect) { + if (!active()) + return; + if (configure_fill()) + context_->fill_rect(rect.left(), rect.top(), rect.width(), rect.height()); + if (configure_stroke()) + context_->stroke_rect(rect.left(), rect.top(), rect.width(), rect.height()); +} + +void Raster_Canvas::fillRect(const QRectF& rect, const QBrush& brush) { + if (!active() || brush.style() == Qt::NoBrush) + return; + context_->set_fill_style(color_of(brush.color())); + context_->fill_rect(rect.left(), rect.top(), rect.width(), rect.height()); +} + +void Raster_Canvas::drawEllipse(const QPointF& center, double rx, double ry) { + if (!active()) + return; + if (configure_fill()) + context_->fill_ellipse(center.x(), center.y(), rx, ry); + if (configure_stroke()) + context_->stroke_ellipse(center.x(), center.y(), rx, ry); +} + +void Raster_Canvas::drawPolygon(const QPointF* points, int count) { + if (!active() || !points || count < 3) + return; + std::vector polygon; + polygon.reserve(static_cast(count)); + for (int i = 0; i < count; ++i) + polygon.emplace_back(points[i].x(), points[i].y()); + if (configure_fill()) + context_->fill_polygon(polygon.data(), polygon.size()); + if (configure_stroke()) + context_->stroke_polygon(polygon.data(), polygon.size()); +} + +void Raster_Canvas::drawPoint(const QPointF& point) { + if (!active() || pen_.style() == Qt::NoPen) + return; + const double radius = std::max(0.5, (pen_.widthF() == 0.0 ? 1.0 : pen_.widthF()) * 0.5); + context_->set_fill_style(color_of(pen_.color())); + context_->fill_ellipse(point.x(), point.y(), radius, radius); +} + +void Raster_Canvas::drawPoints(const QPointF* points, int count) { + if (!points) + return; + for (int i = 0; i < count; ++i) + drawPoint(points[i]); +} + +bool Raster_Canvas::bind_image(const QImage& source, BLImage& image, QImage& converted) { + if (source.isNull()) + return false; + converted = source.format() == QImage::Format_ARGB32_Premultiplied + ? source + : source.convertToFormat(QImage::Format_ARGB32_Premultiplied); + return image.create_from_data(converted.width(), converted.height(), BL_FORMAT_PRGB32, converted.bits(), + static_cast(converted.bytesPerLine()), BL_DATA_ACCESS_RW) == BL_SUCCESS; +} + +void Raster_Canvas::drawImage(const QPoint& top_left, const QImage& image) { + drawImage(QPointF(top_left), image); +} + +void Raster_Canvas::drawImage(const QPointF& top_left, const QImage& image) { + if (!active() || image.isNull()) + return; + BLImage source_image; + QImage converted; + if (!bind_image(image, source_image, converted)) + return; + context_->blit_image(BLPoint(top_left.x(), top_left.y()), source_image); +} + +void Raster_Canvas::drawImage(const QRectF& target, const QImage& image) { + drawImage(target, image, QRectF(0.0, 0.0, image.width(), image.height())); +} + +void Raster_Canvas::drawImage(const QRectF& target, const QImage& image, const QRectF& source) { + if (!active() || image.isNull() || target.isEmpty() || source.isEmpty()) + return; + BLImage source_image; + QImage converted; + if (!bind_image(image, source_image, converted)) + return; + const BLRect source_rect(source.left(), source.top(), source.width(), source.height()); + const BLRectI source_area(static_cast(std::floor(source_rect.x)), static_cast(std::floor(source_rect.y)), + static_cast(std::ceil(source_rect.w)), static_cast(std::ceil(source_rect.h))); + context_->blit_image(BLRect(target.left(), target.top(), target.width(), target.height()), source_image, source_area); +} + +void Raster_Canvas::drawText(const QPointF& baseline, const QString& text) { + if (!active() || text.isEmpty() || pen_.style() == Qt::NoPen) + return; + const QRawFont raw_font = QRawFont::fromFont(font_); + if (!raw_font.isValid()) + return; + const QVector glyphs = raw_font.glyphIndexesForString(text); + const QVector advances = raw_font.advancesForGlyphIndexes(glyphs); + double x = baseline.x(); + for (int i = 0; i < glyphs.size(); ++i) { + fill_glyph_path(*context_, raw_font.pathForGlyph(glyphs.at(i)), x, baseline.y(), pen_.color()); + if (i < advances.size()) + x += advances.at(i).x(); + } +} + +void Raster_Canvas::drawText(const QRectF& rect, Qt::Alignment alignment, const QString& text) { + if (text.isEmpty()) + return; + const QFontMetricsF metrics(font_); + const double width = metrics.horizontalAdvance(text); + const double height = metrics.height(); + double x = rect.left(); + if (alignment & Qt::AlignHCenter) + x = rect.left() + (rect.width() - width) * 0.5; + else if (alignment & Qt::AlignRight) + x = rect.right() - width; + double baseline = rect.top() + metrics.ascent(); + if (alignment & Qt::AlignVCenter) + baseline = rect.top() + (rect.height() - height) * 0.5 + metrics.ascent(); + else if (alignment & Qt::AlignBottom) + baseline = rect.bottom() - metrics.descent(); + drawText(QPointF(x, baseline), text); +} + +void Raster_Canvas::translate(const QPoint& point) { + if (active()) + context_->translate(point.x(), point.y()); +} + +void Raster_Canvas::translate(const QPointF& point) { + if (active()) + context_->translate(point.x(), point.y()); +} + +void Raster_Canvas::rotate(double degrees) { + if (active()) + context_->rotate(degrees * 3.14159265358979323846 / 180.0); +} + +void Raster_Canvas::scale(double x, double y) { + if (active()) + context_->scale(x, y); +} + +void Raster_Canvas::resetTransform() { + if (active()) + context_->reset_transform(); +} + +void Raster_Canvas::setClipRect(const QRectF& rect) { + if (active()) + context_->clip_to_rect(rect.left(), rect.top(), rect.width(), rect.height()); +} + +void Raster_Canvas::setClipping(bool enabled) { + if (active() && !enabled) + context_->restore_clipping(); +} + +Frame_Raster_Context::Frame_Raster_Context(QImage& target, QPointF base_translation) + : target_(target), base_translation_(base_translation), canvas_(&bl_context_) { + if (target_.format() != QImage::Format_ARGB32_Premultiplied || target_.isNull()) + return; + blend_image_ready_ = bl_image_.create_from_data( + target_.width(), target_.height(), BL_FORMAT_PRGB32, target_.bits(), + static_cast(target_.bytesPerLine()), BL_DATA_ACCESS_RW) == BL_SUCCESS; +} + +Frame_Raster_Context::~Frame_Raster_Context() { + finish(); +} + +BLContext* Frame_Raster_Context::blend() { + if (!blend_image_ready_) + return nullptr; + if (!active_) { + BLContextCreateInfo create_info{}; + create_info.thread_count = 0; + if (bl_context_.begin(bl_image_, create_info) != BL_SUCCESS) + return nullptr; + bl_context_.translate(base_translation_.x(), base_translation_.y()); + active_ = true; + } + return &bl_context_; +} + +Raster_Canvas& Frame_Raster_Context::canvas() { + canvas_.bind(blend()); + return canvas_; +} + +void Frame_Raster_Context::end_blend() { + if (!active_) + return; + bl_context_.end(); + active_ = false; +} + +void Frame_Raster_Context::finish() { + end_blend(); +} + +BLStrokeCap Frame_Raster_Context::stroke_cap(Qt::PenCapStyle style) { + return Raster_Canvas::stroke_cap(style); +} + +BLStrokeJoin Frame_Raster_Context::stroke_join(Qt::PenJoinStyle style) { + return Raster_Canvas::stroke_join(style); +} + bool Frame_Raster_Context::configure_stroke(const QPen& pen) { - BLRgba32 color; - if (pen.style() == Qt::NoPen || !solid_color(pen.brush(), color)) + if (pen.style() == Qt::NoPen) return false; BLContext* context = blend(); if (!context) return false; const double width = pen.widthF() == 0.0 ? 1.0 : pen.widthF(); - context->set_stroke_style(color); + context->set_stroke_style(Raster_Canvas::color_of(pen.color())); context->set_stroke_width(width); context->set_stroke_caps(stroke_cap(pen.capStyle())); context->set_stroke_join(stroke_join(pen.joinStyle())); @@ -114,12 +358,8 @@ bool Frame_Raster_Context::configure_stroke(const QPen& pen) { bool Frame_Raster_Context::stroke_polyline(std::span points, const QPen& pen) { if (points.size() < 2 || pen.style() == Qt::NoPen) return true; - if (!configure_stroke(pen)) { - QPainter& painter = qt(); - painter.setPen(pen); - painter.drawPolyline(points.data(), static_cast(points.size())); + if (!configure_stroke(pen)) return false; - } BLPath path; bool active = false; for (const QPointF& point : points) { @@ -142,21 +382,15 @@ bool Frame_Raster_Context::stroke_polyline(std::span points, cons bool Frame_Raster_Context::fill_polygon(std::span points, const QBrush& brush) { if (points.size() < 3 || brush.style() == Qt::NoBrush) return true; - BLRgba32 color; - BLContext* context = solid_color(brush, color) ? blend() : nullptr; - if (!context) { - QPainter& painter = qt(); - painter.setPen(Qt::NoPen); - painter.setBrush(brush); - painter.drawPolygon(points.data(), static_cast(points.size())); + BLContext* context = blend(); + if (!context) return false; - } - BLPath path; - path.move_to(points.front().x(), points.front().y()); - for (std::size_t i = 1; i < points.size(); ++i) - path.line_to(points[i].x(), points[i].y()); - path.close(); - context->fill_path(path, color); + std::vector polygon; + polygon.reserve(points.size()); + for (const QPointF& point : points) + polygon.emplace_back(point.x(), point.y()); + context->set_fill_style(Raster_Canvas::color_of(brush.color())); + context->fill_polygon(polygon.data(), polygon.size()); return true; } } // namespace renderive diff --git a/Renderive/architecture/Frame_Raster_Context.h b/Renderive/architecture/Frame_Raster_Context.h index 9738534..d85394d 100644 --- a/Renderive/architecture/Frame_Raster_Context.h +++ b/Renderive/architecture/Frame_Raster_Context.h @@ -1,14 +1,98 @@ #pragma once + #include +#include +#include #include -#include #include +#include #include +#include +#include +#include +#include #include #include +#include namespace renderive { -/// @brief Owns mutually exclusive Qt and Blend2D sessions for one pixel target. + +/// @brief Blend2D drawing facade used by the Qt-facing render data. +/// +/// Qt value types remain the public style/data representation, but all pixels +/// are produced by Blend2D. In particular, this class deliberately has no +/// Qt painter backend. +class Raster_Canvas { +public: + explicit Raster_Canvas(BLContext* context = nullptr) : context_(context) {} + + void bind(BLContext* context) noexcept { context_ = context; } + void save(); + void restore(); + void setPen(const QPen& pen); + void setBrush(const QBrush& brush); + void setFont(const QFont& font) { font_ = font; } + [[nodiscard]] const QFont& font() const noexcept { return font_; } + + void drawLine(const QPointF& p0, const QPointF& p1); + void drawLine(int x0, int y0, int x1, int y1) { drawLine(QPointF(x0, y0), QPointF(x1, y1)); } + void drawRect(const QRectF& rect); + void drawRect(const QRect& rect) { drawRect(QRectF(rect)); } + void fillRect(const QRectF& rect, const QBrush& brush); + void fillRect(const QRect& rect, const QBrush& brush) { fillRect(QRectF(rect), brush); } + void fillRect(const QRectF& rect, const QColor& color) { fillRect(rect, QBrush(color)); } + void fillRect(const QRect& rect, const QColor& color) { fillRect(QRectF(rect), QBrush(color)); } + void fillRect(const QRectF& rect, Qt::GlobalColor color) { fillRect(rect, QColor(color)); } + void fillRect(const QRect& rect, Qt::GlobalColor color) { fillRect(rect, QColor(color)); } + void drawEllipse(const QPointF& center, double rx, double ry); + void drawPolygon(const QPointF* points, int count); + void drawPolygon(const QPolygonF& polygon) { drawPolygon(polygon.constData(), polygon.size()); } + void drawPoint(const QPointF& point); + void drawPoints(const QPointF* points, int count); + + void drawImage(const QPoint& top_left, const QImage& image); + void drawImage(const QPointF& top_left, const QImage& image); + void drawImage(const QRectF& target, const QImage& image); + void drawImage(const QRectF& target, const QImage& image, const QRectF& source); + + void drawText(const QPointF& baseline, const QString& text); + void drawText(int x, int y, const QString& text) { drawText(QPointF(x, y), text); } + void drawText(const QRectF& rect, Qt::Alignment alignment, const QString& text); + void drawText(const QRect& rect, Qt::Alignment alignment, const QString& text) { drawText(QRectF(rect), alignment, text); } + + void translate(const QPoint& point); + void translate(const QPointF& point); + void rotate(double degrees); + void scale(double x, double y); + void resetTransform(); + void setClipRect(const QRectF& rect); + void setClipRect(const QRect& rect) { setClipRect(QRectF(rect)); } + void setClipping(bool enabled); + + static BLRgba32 color_of(const QColor& color); + static BLStrokeCap stroke_cap(Qt::PenCapStyle style); + static BLStrokeJoin stroke_join(Qt::PenJoinStyle style); + +private: + struct State { + QPen pen; + QBrush brush; + QFont font; + }; + + [[nodiscard]] bool active() const noexcept { return context_ != nullptr; } + bool configure_stroke(); + bool configure_fill(); + static bool bind_image(const QImage& source, BLImage& image, QImage& converted); + + BLContext* context_{}; + QPen pen_; + QBrush brush_; + QFont font_; + std::vector states_; +}; + +/// @brief Owns one Blend2D session for one pixel target. class Frame_Raster_Context { public: explicit Frame_Raster_Context(QImage& target, QPointF base_translation = {}); @@ -17,28 +101,24 @@ public: Frame_Raster_Context(const Frame_Raster_Context&) = delete; Frame_Raster_Context& operator=(const Frame_Raster_Context&) = delete; - QPainter& qt(); + Raster_Canvas& canvas(); bool stroke_polyline(std::span points, const QPen& pen); bool fill_polygon(std::span points, const QBrush& brush); void finish(); private: - enum class Backend : std::uint8_t { None, Qt, Blend2D }; - BLContext* blend(); - void end_qt(); void end_blend(); - static bool solid_color(const QBrush& brush, BLRgba32& color); static BLStrokeCap stroke_cap(Qt::PenCapStyle style); static BLStrokeJoin stroke_join(Qt::PenJoinStyle style); bool configure_stroke(const QPen& pen); QImage& target_; QPointF base_translation_; - QPainter qt_painter_; BLImage bl_image_; BLContext bl_context_; - Backend backend_ = Backend::None; + Raster_Canvas canvas_; bool blend_image_ready_{}; + bool active_{}; }; } // namespace renderive diff --git a/Renderive/architecture/Render_Data.h b/Renderive/architecture/Render_Data.h index 1d74de1..7192cf2 100644 --- a/Renderive/architecture/Render_Data.h +++ b/Renderive/architecture/Render_Data.h @@ -12,6 +12,7 @@ #include "../base/Memory.h" #include "../base/Object_Semantics.h" #include "Render_Config.h" +#include "Frame_Raster_Context.h" #include "Plot_Render_Context.h" #include "Triple_Buffer.h" #include "Update_Completion.h" @@ -19,7 +20,6 @@ class QEvent; class QKeyEvent; class QMouseEvent; -class QPainter; class QPointF; class QResizeEvent; class QWheelEvent; diff --git a/Renderive/architecture/Renderable.cpp b/Renderive/architecture/Renderable.cpp index 9fd0212..cb6ba3d 100644 --- a/Renderive/architecture/Renderable.cpp +++ b/Renderive/architecture/Renderable.cpp @@ -202,7 +202,7 @@ void Renderable::render_cached(Frame_Raster_Context& context, const Plot_Render_ if (snapshot.frame_metadata) snapshot.frame_metadata->record_renderable_cache_hit(object_name, parent_object_name, tree_depth); std::uint64_t begin_time = steady_now_ns(); - context.qt().drawImage(bounds.topLeft(), cache_image.image()); + context.canvas().drawImage(bounds.topLeft(), cache_image.image()); if (snapshot.frame_metadata) snapshot.frame_metadata->record_renderable_cache_compose(object_name, parent_object_name, tree_depth, steady_now_ns() - begin_time, static_cast(cache_image.sizeInBytes()), static_cast(bounds.width()) * static_cast(bounds.height()), edit_version, cache_ready_version.load(std::memory_order_acquire)); return; @@ -215,7 +215,7 @@ void Renderable::render_cached(Frame_Raster_Context& context, const Plot_Render_ snapshot.frame_metadata->record_renderable_cache_rebuild(object_name, parent_object_name, tree_depth, steady_now_ns() - rebuild_begin_time, cache_image.isNull() ? 0 : static_cast(cache_image.sizeInBytes()), static_cast(bounds.width()) * static_cast(bounds.height()), edit_version, cache_ready_version.load(std::memory_order_acquire)); if (!cache_image.isNull()) { std::uint64_t begin_time = steady_now_ns(); - context.qt().drawImage(bounds.topLeft(), cache_image.image()); + context.canvas().drawImage(bounds.topLeft(), cache_image.image()); if (snapshot.frame_metadata) snapshot.frame_metadata->record_renderable_cache_compose(object_name, parent_object_name, tree_depth, steady_now_ns() - begin_time, static_cast(cache_image.sizeInBytes()), static_cast(bounds.width()) * static_cast(bounds.height()), edit_version, cache_ready_version.load(std::memory_order_acquire)); } diff --git a/Renderive/architecture/Renderable.h b/Renderive/architecture/Renderable.h index 34e7c22..58ab393 100644 --- a/Renderive/architecture/Renderable.h +++ b/Renderive/architecture/Renderable.h @@ -9,6 +9,7 @@ #include "../base/Object_Semantics.h" #include "../base/Pmr_QImage_Buffer.h" #include "Frame_Scheduler.h" +#include "Frame_Raster_Context.h" #include "Render_Data.h" namespace renderive { class Frame_Raster_Context; diff --git a/Renderive/architecture/global.h b/Renderive/architecture/global.h index d390bde..08150ed 100644 --- a/Renderive/architecture/global.h +++ b/Renderive/architecture/global.h @@ -1,8 +1,6 @@ #pragma once + #include -#include -#include -#include #include #include #include @@ -10,24 +8,29 @@ #include #include #include +#include #include +#include #include -// Q_DECL_IMPORT +#include +#include "../Core/Geometry/Types.h" + #ifdef NDEBUG -#define ASSERT(condition, message) ((void)0) // 在发布模式下什么都不做 +#define ASSERT(condition, message) ((void)0) #else #define ASSERT(condition, message) \ - do { \ - if (!(condition)) { \ + do { \ + if (!(condition)) { \ std::cerr << "Assertion failed: " << message \ - << "\nFile: " << __FILE__ \ - << "\nLine: " << __LINE__ \ - << std::endl; \ - throw std::runtime_error(message); \ - } \ - } \ + << "\nFile: " << __FILE__ \ + << "\nLine: " << __LINE__ \ + << std::endl; \ + throw std::runtime_error(message); \ + } \ + } \ while (0) #endif + #if defined(build_lib) #define LIB_DECL #elif defined(build_dll) @@ -37,82 +40,64 @@ #else #define LIB_DECL #endif + namespace renderive { std::vector get_turbo_color_gradient(); class Abs_Axis; +class Raster_Canvas; class Axis; struct Render_Data; struct Renderable; class Abs_Plot; class Plot; -// 注意这仅仅只是一个范围 这是一个向量,大小相反就反向画 -struct LIB_DECL Range { - double origin; - double target; - [[nodiscard]] double size() const { - return qAbs(target - origin); - } - [[nodiscard]] double length() const { - return target - origin; - } - [[nodiscard]] double middle() const { - return origin + (target - origin) / 2; - } - bool operator==(const Range& other) const { - return (origin == other.origin && target == other.target); - } - bool operator!=(const Range& other) const { - return !(*this == other); - } - [[nodiscard]] bool contain(double value) const { - if (origin < target) { - return value > origin - 0.0001 && value < target + 0.0001; - } - else { - return value > target - 0.0001 && value < origin + 0.0001; - } - } -}; + QDebug operator<<(QDebug debug, const Range& range); } // namespace renderive + #define PROP(Type, Name) \ Type Name(); \ void set_##Name(Type); + #define RENDERIVE_DEFINE_STATE_PROP(Owner, State, Type, Field) \ - Type Owner::Field() { \ - return d()->edit_state_value(&State::Field); \ - } \ - void Owner::set_##Field(Type value) { \ + Type Owner::Field() { \ + return d()->edit_state_value(&State::Field); \ + } \ + void Owner::set_##Field(Type value) { \ d()->set_state_value(&State::Field, std::move(value)); \ } + #define RENDERIVE_DEFINE_WEAK_STATE_PROP(Owner, State, Pointee, Field) \ - std::shared_ptr Owner::Field() { \ - return d()->edit_state_value(&State::Field).lock(); \ - } \ - void Owner::set_##Field(std::shared_ptr value) { \ + std::shared_ptr Owner::Field() { \ + return d()->edit_state_value(&State::Field).lock(); \ + } \ + void Owner::set_##Field(std::shared_ptr value) { \ d()->set_state_value(&State::Field, std::weak_ptr(value)); \ } + #define PROP_B(TYPE, FIELD) \ public: \ Builder& set_##FIELD(TYPE value) { \ this->FIELD = std::move(value); \ return *this; \ } + #define PROP_BT(TYPE, FIELD) \ public: \ That& set_##FIELD(TYPE value) { \ static_cast(this)->FIELD = std::move(value); \ return *static_cast(this); \ } + #define PROP_R(TYPE, FIELD) sc->FIELD = this->FIELD; #define PROP_RT(TYPE, FIELD) sc->FIELD = static_cast(this)->FIELD; + #define SETTER(TYPE, FIELD, DEFAULT) \ - \ TYPE FIELD = DEFAULT; \ public: \ Builder& set_##FIELD(TYPE value) { \ this->FIELD = std::move(value); \ return *this; \ } + #undef min #undef max diff --git a/Renderive/plot/Plot.cpp b/Renderive/plot/Plot.cpp index daf9658..ee0d671 100644 --- a/Renderive/plot/Plot.cpp +++ b/Renderive/plot/Plot.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -19,6 +18,7 @@ #include "Plot_p.h" #include "../architecture/Frame_Raster_Context.h" #include "../architecture/Update_Completion.h" +#include "../qt/Widget_Presentation.h" namespace renderive { namespace { int renderable_prepare_priority(const std::shared_ptr& able) { @@ -121,6 +121,14 @@ std::shared_ptr Abs_Plot::get_render_context() const { std::shared_ptr Abs_Plot::get_root_renderable() const { return d->root_renderable; } +void Abs_Plot::set_frame_lifecycle_observer(const std::shared_ptr& observer) { + if (d->destroying.load(std::memory_order_acquire)) + return; + std::atomic_store_explicit(&d->render_context->frame_lifecycle_observer, observer, std::memory_order_release); +} +std::shared_ptr Abs_Plot::frame_lifecycle_observer() const { + return std::atomic_load_explicit(&d->render_context->frame_lifecycle_observer, std::memory_order_acquire); +} std::shared_ptr Abs_Plot::create_renderable_node(const std::shared_ptr& parent, const QString& object_name) const { if (!parent || !parent->is_attached() || !d->root_renderable->shares_plot_with(*parent)) { ASSERT(parent && parent->is_attached() && d->root_renderable->shares_plot_with(*parent), "Abs_Plot create_renderable_node error! parent invalid"); @@ -196,18 +204,17 @@ void Abs_Plot_Private::paint_event(QPaintEvent* event) { std::uint64_t paint_begin_time = steady_now_ns(); auto consumed = pipeline.consume_ready_color(); std::pmr::vector> presented_update_states(memory_resource(Memory_Domain::Update_Completion)); - QPainter painter(q); if (consumed.lease && !consumed.lease.frame->image.isNull()) { if (consumed.new_frame) consumed.lease.frame->metadata.paint_begin_time = paint_begin_time; - painter.drawImage(QPoint(0, 0), consumed.lease.frame->image.image()); + qt::present_frame(q, consumed.lease.frame->image.image(), + QColor::fromRgba(render_context->background_rgba.load(std::memory_order_acquire))); if (consumed.new_frame) presented_update_states = std::move(consumed.lease.frame->presented_update_states); } else { - painter.fillRect(q->rect(), QColor::fromRgba(render_context->background_rgba.load(std::memory_order_acquire))); + qt::present_frame(q, {}, QColor::fromRgba(render_context->background_rgba.load(std::memory_order_acquire))); } - painter.end(); complete_update_states(presented_update_states, Update_Stage::Presented); if (consumed.new_frame && consumed.lease) { consumed.lease.frame->metadata.paint_end_time = steady_now_ns(); diff --git a/Renderive/plot/Plot.h b/Renderive/plot/Plot.h index 4705974..add517d 100644 --- a/Renderive/plot/Plot.h +++ b/Renderive/plot/Plot.h @@ -17,6 +17,8 @@ public: QColor background_color(); [[nodiscard]] std::shared_ptr get_root_renderable() const; [[nodiscard]] std::shared_ptr create_renderable_node(const std::shared_ptr& parent, const QString& object_name = {}) const; + void set_frame_lifecycle_observer(const std::shared_ptr& observer); + [[nodiscard]] std::shared_ptr frame_lifecycle_observer() const; QString object_name; void set_background_color(const QColor& color); void mark_render_state_dirty(); diff --git a/Renderive/plottable/Afterglow_p.h b/Renderive/plottable/Afterglow_p.h index b5f37cc..3d96809 100644 --- a/Renderive/plottable/Afterglow_p.h +++ b/Renderive/plottable/Afterglow_p.h @@ -45,7 +45,7 @@ struct Afterglow_Private : Typed_Render_Data power_range_data, const Afterglow_Render_State* s); - void draw_image(QPainter* painter, const Afterglow_Render_State* s) { + void draw_image(Raster_Canvas* painter, const Afterglow_Render_State* s) { auto frequency_axis = s->frequency_axis.lock(); auto power_axis = s->power_axis.lock(); if (!frequency_axis || !power_axis || image.is_null()) @@ -68,7 +68,7 @@ struct Afterglow_Private : Typed_Render_Datarestore(); } void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override { - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); Afterglow_Render_State* s = render_state(); draw_image(painter, s); } diff --git a/Renderive/plottable/Audio_Frequency_p.h b/Renderive/plottable/Audio_Frequency_p.h index e78c08c..ad73da0 100644 --- a/Renderive/plottable/Audio_Frequency_p.h +++ b/Renderive/plottable/Audio_Frequency_p.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include diff --git a/Renderive/plottable/Hover_Info.cpp b/Renderive/plottable/Hover_Info.cpp index 022b537..fbcfccc 100644 --- a/Renderive/plottable/Hover_Info.cpp +++ b/Renderive/plottable/Hover_Info.cpp @@ -1,7 +1,7 @@ #include "Hover_Info.h" #include "../Axis/Abs_Axis_p.h" namespace renderive { -void Hover_Info_Render_State::draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable) { +void Hover_Info_Render_State::draw_hover(Raster_Canvas* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable) { QPoint pos = hover_info_pos; std::vector lines = renderable->create_hover_string(h, v, pos); QFontMetrics fm(hover_info_font); diff --git a/Renderive/plottable/Hover_Info.h b/Renderive/plottable/Hover_Info.h index a7a6c6e..1b303a2 100644 --- a/Renderive/plottable/Hover_Info.h +++ b/Renderive/plottable/Hover_Info.h @@ -20,7 +20,7 @@ struct LIB_DECL Hover_Info_Render_State { bool use_hover_info = true; bool hover_info_active = false; QPoint hover_info_pos; - void draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable); + void draw_hover(Raster_Canvas* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable); bool hover_ok(Renderable* able) const; }; struct LIB_DECL Hover_Info_Renderable_Interface { @@ -65,7 +65,7 @@ struct LIB_DECL Hover_Info_Renderable_Interface { }; virtual std::vector create_hover_string(Abs_Axis* h, Abs_Axis* v, QPoint pos); virtual ~Hover_Info_Renderable_Interface() = default; - virtual void draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v) { + virtual void draw_hover(Raster_Canvas* painter, Abs_Axis* h, Abs_Axis* v) { return hover_render_state()->draw_hover(painter, h, v, this); } virtual bool hover_ok(Renderable* able) { diff --git a/Renderive/plottable/Multi_Select_Rect_p.h b/Renderive/plottable/Multi_Select_Rect_p.h index 9a0058d..c50ba01 100644 --- a/Renderive/plottable/Multi_Select_Rect_p.h +++ b/Renderive/plottable/Multi_Select_Rect_p.h @@ -98,7 +98,7 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Datahorizontal_axis.lock(); auto vertical_axis = s->vertical_axis.lock(); diff --git a/Renderive/plottable/Performance_Shower.cpp b/Renderive/plottable/Performance_Shower.cpp index cd5da6f..90f1d51 100644 --- a/Renderive/plottable/Performance_Shower.cpp +++ b/Renderive/plottable/Performance_Shower.cpp @@ -4,7 +4,7 @@ #include #include #include "Core/spdlog/export.h" -#include "../plot/Plot_p.h" +#include "../plot/Plot.h" namespace renderive { namespace { double duration_ms(std::uint64_t begin_ns, std::uint64_t end_ns) { @@ -197,39 +197,39 @@ Performance_Frame_Log& frame_log() { static Performance_Frame_Log value; return value; } -std::shared_ptr performance_shower_owner(const Abs_Plot_Private* data) { - if (!data || !data->render_context) - return {}; - auto observer = std::atomic_load_explicit(&data->render_context->frame_lifecycle_observer, std::memory_order_acquire); - return std::dynamic_pointer_cast(observer); -} } RENDERIVE_DEFINE_RENDERABLE_BINDING(Performance_Shower, Performance_Shower_Private, Performance_Shower_Render_State, Performance_Shower_Input_Data, "Performance_Shower") -std::shared_ptr attach_performance_shower(Abs_Plot& plot) { - auto* data = Abs_Plot_Private_Access::data(plot); - if (!data) +Performance_Shower::Builder::Builder(Abs_Plot& plot) noexcept + : plot_(&plot), parent_(plot.get_root_renderable()) {} +std::shared_ptr Performance_Shower::Builder::build() const { + if (!parent_ || !parent_->is_attached()) return {}; - auto shower = performance_shower_owner(data); - if (shower) - return shower; - auto created_shower = renderive::make_shared(); - created_shower->init(data->root_renderable); - std::atomic_store_explicit(&data->render_context->frame_lifecycle_observer, std::static_pointer_cast(created_shower), std::memory_order_release); - return created_shower; + auto result = renderive::make_shared(); + result->init(parent_); + if (plot_) + plot_->set_frame_lifecycle_observer(std::static_pointer_cast(result)); + return result; +} +namespace { +std::shared_ptr performance_shower_owner(const Abs_Plot& plot) { + return std::dynamic_pointer_cast(plot.frame_lifecycle_observer()); +} +} +std::shared_ptr attach_performance_shower(Abs_Plot& plot) { + if (auto current = performance_shower_owner(plot)) + return current; + return Performance_Shower::Builder(plot).build(); } void detach_performance_shower(Abs_Plot& plot) { - auto* data = Abs_Plot_Private_Access::data(plot); - if (!data) + auto shower = performance_shower_owner(plot); + plot.set_frame_lifecycle_observer({}); + if (!shower) return; - auto shower = performance_shower_owner(data); - std::atomic_store_explicit(&data->render_context->frame_lifecycle_observer, std::shared_ptr{}, std::memory_order_release); - if (shower) { - shower->setEnabled(false); - plot.remove_renderable(shower); - } + shower->setEnabled(false); + plot.remove_renderable(shower); } bool performance_shower_enabled(const Abs_Plot& plot) { - auto shower = performance_shower_owner(Abs_Plot_Private_Access::data(plot)); + auto shower = performance_shower_owner(plot); return shower && shower->enabled(); } void set_performance_shower_enabled(Abs_Plot& plot, bool enabled) { @@ -237,8 +237,7 @@ void set_performance_shower_enabled(Abs_Plot& plot, bool enabled) { detach_performance_shower(plot); return; } - auto shower = attach_performance_shower(plot); - if (shower) + if (auto shower = attach_performance_shower(plot)) shower->setEnabled(true); } void Performance_Shower::setEnabled(bool enabled) { diff --git a/Renderive/plottable/Performance_Shower_p.h b/Renderive/plottable/Performance_Shower_p.h index d2aba30..e881b76 100644 --- a/Renderive/plottable/Performance_Shower_p.h +++ b/Renderive/plottable/Performance_Shower_p.h @@ -7,6 +7,7 @@ #include #include "../architecture/Frame_Scheduler.h" #include "../architecture/Renderable.h" +#include "../Diagnostics/Performance_Shower.h" #include "Core/Statistics/Statistics.h" #include "Renderive/architecture/Render_Lease.h" namespace renderive { @@ -257,27 +258,6 @@ private: statistics.update(static_cast(end_ns - begin_ns) / 1000000.0); } }; -struct Performance_Shower_Private; -/// @brief 后台渲染性能信息显示图元。 -class Performance_Shower : public Renderable, public Frame_Lifecycle_Observer { -public: - /// @brief 创建性能显示图元。 - Performance_Shower(); - /// @brief 设置性能显示是否启用。 - void setEnabled(bool enabled); - /// @brief 返回性能显示是否启用。 - bool enabled() const override; - /// @brief 收集一帧生命周期记录。 - void collect_frame(const Frame_Lifecycle_Record& frame) override; - Renderable* renderable() const override { - return const_cast(this); - } -private: - Performance_Shower_Private* d(); - Render_Object_Owner create_render_data() override; - Render_Object_Owner create_render_state() override; - Render_Object_Owner create_input_data() override; -}; struct Performance_Shower_Private : Typed_Render_Data { Frame_Performance_Aggregate aggregate; std::atomic_bool reset_requested{false}; @@ -307,7 +287,7 @@ struct Performance_Shower_Private : Typed_Render_Dataenabled() && panel_rect.contains(pos.toPoint()); } void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot& snapshot) override { - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); if (!q()->enabled() || lines.empty()) return; Performance_Shower_Render_State* state = render_state(); diff --git a/Renderive/plottable/Planisphere_p.h b/Renderive/plottable/Planisphere_p.h index 5980553..0cd5911 100644 --- a/Renderive/plottable/Planisphere_p.h +++ b/Renderive/plottable/Planisphere_p.h @@ -102,7 +102,7 @@ struct Planisphere_Private : Typed_Render_Datagive_points(anchor_points(s), false); } void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override { - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); Q_UNUSED(painter) } }; diff --git a/Renderive/plottable/Spectrum.cpp b/Renderive/plottable/Spectrum.cpp index b33e133..42a9604 100644 --- a/Renderive/plottable/Spectrum.cpp +++ b/Renderive/plottable/Spectrum.cpp @@ -382,9 +382,9 @@ void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) { } void Spectrum_Private::draw_spectrum(Frame_Raster_Context& context, Spectrum_Render_State* s) { if (s->use_sweep_frequency_rect) - draw_axis_rect(&context.qt(), s); + draw_axis_rect(&context.canvas(), s); } -void Spectrum_Private::draw_axis_rect(QPainter* painter, const Spectrum_Render_State* s) const { +void Spectrum_Private::draw_axis_rect(Raster_Canvas* painter, const Spectrum_Render_State* s) const { auto frequency_axis = s->frequency_axis.lock(); auto power_axis = s->power_axis.lock(); if (!frequency_axis || !power_axis) @@ -417,7 +417,7 @@ void Spectrum_Private::rasterize(Frame_Raster_Context& context, const Plot_Rende auto frequency_axis = s->frequency_axis.lock(); auto power_axis = s->power_axis.lock(); if (frequency_axis && power_axis) - q()->draw_hover(&context.qt(), frequency_axis.get(), power_axis.get()); + q()->draw_hover(&context.canvas(), frequency_axis.get(), power_axis.get()); } } std::size_t Spectrum_Private::estimated_frame_memory(const Plot_Render_Snapshot& snapshot) { diff --git a/Renderive/plottable/Spectrum_p.h b/Renderive/plottable/Spectrum_p.h index 3df0819..de1f5cc 100644 --- a/Renderive/plottable/Spectrum_p.h +++ b/Renderive/plottable/Spectrum_p.h @@ -64,7 +64,7 @@ struct Spectrum_Private : Typed_Render_Data& line_data, const Spectrum_Render_State* s); [[nodiscard]] double frequency_at(int index) const; - void draw_axis_rect(QPainter* painter, const Spectrum_Render_State* s) const; + void draw_axis_rect(Raster_Canvas* painter, const Spectrum_Render_State* s) const; void update_marker_items(const Spectrum_Render_State* s); [[nodiscard]] double get_y(double x, bool& ok, Line_Interpolation mode, Interpolation_Value_Domain domain); void draw_power_curve(Frame_Raster_Context& context, Spectrum_Render_State* s, const std::pmr::vector& powers, const QPen& pen, const QBrush& brush); diff --git a/Renderive/plottable/Sweep_Frequency_p.h b/Renderive/plottable/Sweep_Frequency_p.h index 3d6f69e..21a266f 100644 --- a/Renderive/plottable/Sweep_Frequency_p.h +++ b/Renderive/plottable/Sweep_Frequency_p.h @@ -88,7 +88,7 @@ struct Sweep_Frequency_Private : renderive::Typed_Render_Datapen); - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); painter->setPen(s->cur_frequency_pen); double f = block_num > 1 ? offset * frequency_range.length() / (block_num - 1.0) : frequency_range.origin; double ff = render_axis_transform(*frequency_axis).to_pixel(f); diff --git a/Renderive/plottable/Waterfall_p.h b/Renderive/plottable/Waterfall_p.h index 3d3fc17..ee83a79 100644 --- a/Renderive/plottable/Waterfall_p.h +++ b/Renderive/plottable/Waterfall_p.h @@ -442,7 +442,7 @@ struct Waterfall_Private : Typed_Render_Datainterpolation_mode); painter.restore(); } - void draw_image(QPainter& painter, const Axis_Transform_Snapshot& h_transform, const Axis_Transform_Snapshot& v_transform) { + void draw_image(Raster_Canvas& painter, const Axis_Transform_Snapshot& h_transform, const Axis_Transform_Snapshot& v_transform) { if (!raster_cache.draw_visible || raster_cache.image.is_null() || raster_cache.draw_row_count <= 0) return; const int first_count = std::min(raster_cache.draw_row_count, raster_cache.draw_row_count - raster_cache.first_source_row); @@ -477,7 +477,7 @@ struct Waterfall_Private : Typed_Render_Datahover_ok(q())) q()->draw_hover(&painter, frequency_axis.get(), time_axis.get()); diff --git a/Renderive/plottable/export.h b/Renderive/plottable/export.h index 0532039..e4b0b3c 100644 --- a/Renderive/plottable/export.h +++ b/Renderive/plottable/export.h @@ -1,4 +1,5 @@ #pragma once +#include "../Diagnostics/Performance_Shower.h" #include "Afterglow.h" #include "Audio_Frequency.h" #include "Hover_Info.h" @@ -17,3 +18,4 @@ LIB_DECL void detach_performance_shower(Abs_Plot& plot); LIB_DECL bool performance_shower_enabled(const Abs_Plot& plot); LIB_DECL void set_performance_shower_enabled(Abs_Plot& plot, bool enabled); } // namespace renderive + diff --git a/Renderive/primitive/Band_Region.cpp b/Renderive/primitive/Band_Region.cpp index 89317fb..a5a09b0 100644 --- a/Renderive/primitive/Band_Region.cpp +++ b/Renderive/primitive/Band_Region.cpp @@ -1,11 +1,12 @@ #include "Band_Region.h" #include "../Axis/Abs_Axis_p.h" #include "../base/Memory.h" +#include "../qt/Core_Geometry_Adapter.h" namespace renderive { struct Band_Region_Render_State : Render_State, Band_Region_Prop {}; struct Band_Region_Private : Typed_Render_Data { void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override { - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); Band_Region_Render_State* s = render_state(); auto x_axis = s->x_axis.lock(); auto y_axis = s->y_axis.lock(); @@ -13,28 +14,22 @@ struct Band_Region_Private : Typed_Render_Dataorientation == Band_Region_Orientation::X) { - x0 = x_transform.to_pixel(s->range.origin); - x1 = x_transform.to_pixel(s->range.target); - y0 = y_transform.to_pixel(y_range.origin); - y1 = y_transform.to_pixel(y_range.target); + first_range = {s->range.origin, s->range.target}; + second_range = {y_transform.coordinate_range.origin, y_transform.coordinate_range.target}; } else { - x0 = x_transform.to_pixel(x_range.origin); - x1 = x_transform.to_pixel(x_range.target); - y0 = y_transform.to_pixel(s->range.origin); - y1 = y_transform.to_pixel(s->range.target); + first_range = {x_transform.coordinate_range.origin, x_transform.coordinate_range.target}; + second_range = {s->range.origin, s->range.target}; } + const auto geometry = core::prepare_band_geometry(axes, first_range, second_range); painter->save(); painter->setPen(s->pen); painter->setBrush(s->brush); - painter->drawRect(QRectF(QPointF(x0, y0), QPointF(x1, y1)).normalized()); + painter->drawPolygon(qt_bridge::to_qt_polygon(geometry)); painter->restore(); } }; diff --git a/Renderive/primitive/Band_Region.h b/Renderive/primitive/Band_Region.h index 95fac97..6b2dc1f 100644 --- a/Renderive/primitive/Band_Region.h +++ b/Renderive/primitive/Band_Region.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include "../architecture/Renderable.h" #include "../Axis/Abs_Axis.h" diff --git a/Renderive/primitive/Color_Bar.cpp b/Renderive/primitive/Color_Bar.cpp index 85f7105..5fe8be7 100644 --- a/Renderive/primitive/Color_Bar.cpp +++ b/Renderive/primitive/Color_Bar.cpp @@ -37,7 +37,7 @@ struct Color_Bar_Private : Typed_Render_Datarange.length() == 0.0) return; diff --git a/Renderive/primitive/Color_Bar.h b/Renderive/primitive/Color_Bar.h index ab2dd9b..ae62c70 100644 --- a/Renderive/primitive/Color_Bar.h +++ b/Renderive/primitive/Color_Bar.h @@ -1,6 +1,5 @@ #pragma once #include -#include #include #include "../architecture/Renderable.h" #include "Color_Map.h" diff --git a/Renderive/primitive/Curve.cpp b/Renderive/primitive/Curve.cpp index ad47dbf..0e01990 100644 --- a/Renderive/primitive/Curve.cpp +++ b/Renderive/primitive/Curve.cpp @@ -73,7 +73,12 @@ struct Curve_Private : Typed_Render_Datadata_range.length() == 0.0) return; - if (s->sample_mode == Line_Sampling_Mode::Preserve_Extrema) { + if (s->visible_range_only && (s->sample_mode == Line_Sampling_Mode::Exact || s->sample_mode == Line_Sampling_Mode::Preserve_Extrema)) { + points = Curve_Utils::create_core_reduced_points(x_axis.get(), y_axis.get(), s->data_range, samples.size(), samples.size(), s->sample_mode, [this](int i) { + return samples.at(i); + }); + } + else if (s->sample_mode == Line_Sampling_Mode::Preserve_Extrema) { points = Curve_Utils::create_peak_bucket_points(x_axis.get(), y_axis.get(), s->data_range, samples.size(), samples.size(), s->visible_range_only, [this](int i) { return samples.at(i); }); diff --git a/Renderive/primitive/Curve_Utils_p.h b/Renderive/primitive/Curve_Utils_p.h index 43a12d3..6a1a1ac 100644 --- a/Renderive/primitive/Curve_Utils_p.h +++ b/Renderive/primitive/Curve_Utils_p.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include @@ -10,6 +9,7 @@ #include "../Axis/Abs_Axis_p.h" #include "../base/Frame_Memory.h" #include "../base/Interpolation_p.h" +#include "../qt/Core_Geometry_Adapter.h" #include "Curve.h" namespace renderive { @@ -107,6 +107,34 @@ public: return points; } + template + static std::pmr::vector create_core_reduced_points(Abs_Axis* x_axis, + Abs_Axis* y_axis, + const Range& data_range, + int total_count, + int available_count, + Line_Sampling_Mode sampling_mode, + Value_At value_at) { + std::pmr::vector points(frame_memory_resource()); + if (total_count < 2 || available_count < 1 || data_range.length() == 0.0) + return points; + const auto x_transform = render_axis_transform(*x_axis); + const auto y_transform = render_axis_transform(*y_axis); + const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis); + std::vector source; + source.reserve(static_cast(available_count)); + for (int i = 0; i < available_count; ++i) + source.push_back({source_index_to_coord(data_range, total_count, i), value_at(i), static_cast(i)}); + const auto reduced = core::Line_Reduction_Engine::reduce( + source, + qt_bridge::to_core_transform(x_transform), + sampling_mode == Line_Sampling_Mode::Preserve_Extrema ? core::Curve_Reduction_Mode::Preserve_Extrema : core::Curve_Reduction_Mode::Exact); + points.reserve(reduced.size()); + for (const core::Indexed_Sample& sample : reduced) + points.push_back(qt_bridge::to_qt_point(axes.map(sample.coordinate, sample.value))); + return points; + } + static void draw_polyline(Frame_Raster_Context& context, std::span points, const QPen& pen); static void draw_fill_to_y(Frame_Raster_Context& context, std::span points, double baseline_y, const QBrush& brush); static std::pmr::vector representative_fill_points(std::span line_points); diff --git a/Renderive/primitive/Fill_Area.cpp b/Renderive/primitive/Fill_Area.cpp index ad83d84..8ea808f 100644 --- a/Renderive/primitive/Fill_Area.cpp +++ b/Renderive/primitive/Fill_Area.cpp @@ -3,6 +3,7 @@ #include "../Axis/Abs_Axis_p.h" #include "../base/Frame_Memory.h" #include "../base/Memory.h" +#include "../qt/Core_Geometry_Adapter.h" namespace renderive { struct Fill_Area_Input_Data : Latest_Input_Data { std::pmr::vector points{memory_resource(Memory_Domain::Curve)}; @@ -36,36 +37,25 @@ struct Fill_Area_Private : Typed_Render_Datax_axis.lock(); auto y_axis = s->y_axis.lock(); if (!x_axis || !y_axis || data_points.empty()) return; - const auto x_transform = render_axis_transform(*x_axis); - const auto y_transform = render_axis_transform(*y_axis); + const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis); + const std::vector source_points = qt_bridge::to_core_points(data_points); std::pmr::vector polygon(frame_memory_resource()); + std::vector mapped_polygon; if (s->mode == Fill_Area_Mode::To_Baseline) { - polygon.resize(data_points.size() + 2); - double baseline_pixel = y_transform.to_pixel(s->baseline); - double first_x = x_transform.to_pixel(data_points.front().x()); - double last_x = x_transform.to_pixel(data_points.back().x()); - polygon.front() = QPointF(first_x, baseline_pixel); - polygon.back() = QPointF(last_x, baseline_pixel); - for (int i = 0; i < data_points.size(); ++i) { - const QPointF& point = data_points.at(i); - polygon[i + 1] = QPointF(x_transform.to_pixel(point.x()), y_transform.to_pixel(point.y())); - } - } - else { - polygon.resize(data_points.size()); - for (int i = 0; i < data_points.size(); ++i) { - const QPointF& point = data_points.at(i); - polygon[i] = QPointF(x_transform.to_pixel(point.x()), y_transform.to_pixel(point.y())); - } + mapped_polygon = core::prepare_fill_geometry(axes, source_points, s->baseline); } + else + mapped_polygon = core::prepare_curve_geometry(axes, source_points); + polygon.reserve(mapped_polygon.size()); + for (const Point point : mapped_polygon) + polygon.push_back(qt_bridge::to_qt_point(point)); painter->save(); - painter->setRenderHint(QPainter::Antialiasing, s->antialias); painter->setPen(s->pen); painter->setBrush(s->brush); painter->drawPolygon(polygon.data(), polygon.size()); diff --git a/Renderive/primitive/Fill_Area.h b/Renderive/primitive/Fill_Area.h index b7effea..e1f1b20 100644 --- a/Renderive/primitive/Fill_Area.h +++ b/Renderive/primitive/Fill_Area.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include diff --git a/Renderive/primitive/Grid.cpp b/Renderive/primitive/Grid.cpp index 1bb4ae1..6162d97 100644 --- a/Renderive/primitive/Grid.cpp +++ b/Renderive/primitive/Grid.cpp @@ -1,6 +1,7 @@ #include "Grid.h" #include "../Axis/Abs_Axis_p.h" #include "../base/Memory.h" +#include "../qt/Core_Geometry_Adapter.h" namespace renderive { struct Grid_Render_State : Render_State, Grid_Prop {}; struct Grid_Private : Typed_Render_Data { @@ -15,7 +16,7 @@ struct Grid_Private : Typed_Render_Data { ticks.push_back(range.origin + step * i); return ticks; } - void draw_axis_grid(QPainter* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, const QPen& sub_pen, bool sub_grid) { + void draw_axis_grid(Raster_Canvas* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, const QPen& sub_pen, bool sub_grid) { if (pen.style() == Qt::NoPen && (!sub_grid || sub_pen.style() == Qt::NoPen)) return; const auto cross_transform = render_axis_transform(*cross_axis); @@ -33,7 +34,7 @@ struct Grid_Private : Typed_Render_Data { draw_ticks(painter, axis, cross0, cross1, ticks); } } - void draw_division_grid(QPainter* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, int divisions) { + void draw_division_grid(Raster_Canvas* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, int divisions) { if (pen.style() == Qt::NoPen) return; const auto cross_transform = render_axis_transform(*cross_axis); @@ -43,7 +44,7 @@ struct Grid_Private : Typed_Render_Data { painter->setPen(pen); draw_ticks(painter, axis, cross0, cross1, ticks); } - void draw_ticks(QPainter* painter, Abs_Axis* axis, double cross0, double cross1, const std::vector& ticks) { + void draw_ticks(Raster_Canvas* painter, Abs_Axis* axis, double cross0, double cross1, const std::vector& ticks) { const auto transform = render_axis_transform(*axis); bool vertical = transform.orientation == Qt::Horizontal; for (double tick : ticks) { @@ -55,7 +56,7 @@ struct Grid_Private : Typed_Render_Data { } } void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override { - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); Grid_Render_State* s = render_state(); auto x_axis = s->x_axis.lock(); auto y_axis = s->y_axis.lock(); @@ -69,10 +70,23 @@ struct Grid_Private : Typed_Render_Data { y_range = s->y_range; painter->save(); if (s->line_mode == Grid_Line_Mode::Range_Division) { - if (s->x_grid) - draw_division_grid(painter, x_axis.get(), y_axis.get(), x_range, y_range, s->x_pen, s->x_divisions); - if (s->y_grid) - draw_division_grid(painter, y_axis.get(), x_axis.get(), y_range, x_range, s->y_pen, s->y_divisions); + core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis); + if (s->x_range.length() != 0.0) + axes.first.coordinate = {s->x_range.origin, s->x_range.target}; + if (s->y_range.length() != 0.0) + axes.second.coordinate = {s->y_range.origin, s->y_range.target}; + const auto geometry = core::prepare_grid_geometry(axes, static_cast(qMax(1, s->x_divisions)), static_cast(qMax(1, s->y_divisions))); + const std::size_t x_line_count = static_cast(qMax(1, s->x_divisions)) + 1; + if (s->x_grid && s->x_pen.style() != Qt::NoPen) { + painter->setPen(s->x_pen); + for (std::size_t i = 0; i < std::min(x_line_count, geometry.size()); ++i) + painter->drawLine(qt_bridge::to_qt_point(geometry[i].first), qt_bridge::to_qt_point(geometry[i].second)); + } + if (s->y_grid && s->y_pen.style() != Qt::NoPen) { + painter->setPen(s->y_pen); + for (std::size_t i = x_line_count; i < geometry.size(); ++i) + painter->drawLine(qt_bridge::to_qt_point(geometry[i].first), qt_bridge::to_qt_point(geometry[i].second)); + } } else { if (s->x_grid) diff --git a/Renderive/primitive/Grid.h b/Renderive/primitive/Grid.h index 9a79466..e4d30e1 100644 --- a/Renderive/primitive/Grid.h +++ b/Renderive/primitive/Grid.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include "../architecture/Renderable.h" #include "../Axis/Abs_Axis.h" diff --git a/Renderive/primitive/Label.cpp b/Renderive/primitive/Label.cpp index 7d1debf..be26302 100644 --- a/Renderive/primitive/Label.cpp +++ b/Renderive/primitive/Label.cpp @@ -31,7 +31,7 @@ struct Label_Private : Typed_Render_Datasave(); @@ -62,7 +62,7 @@ struct Label_Private : Typed_Render_Datarestore(); } void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override { - QPainter* painter = &context.qt(); + Raster_Canvas* painter = &context.canvas(); Label_Render_State* s = render_state(); auto x_axis = s->x_axis.lock(); auto y_axis = s->y_axis.lock(); diff --git a/Renderive/primitive/Label.h b/Renderive/primitive/Label.h index 7caa2a0..5ed7450 100644 --- a/Renderive/primitive/Label.h +++ b/Renderive/primitive/Label.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include diff --git a/Renderive/primitive/Marker.cpp b/Renderive/primitive/Marker.cpp index 5b0ff9d..687654c 100644 --- a/Renderive/primitive/Marker.cpp +++ b/Renderive/primitive/Marker.cpp @@ -1,6 +1,7 @@ #include "Marker.h" #include "../Axis/Abs_Axis_p.h" #include "../base/Memory.h" +#include "../qt/Core_Geometry_Adapter.h" namespace renderive { struct Marker_Render_State : Render_State, Marker_Prop {}; struct Marker_Input_Data : Latest_Input_Data { @@ -21,31 +22,29 @@ struct Marker_Input_Data : Latest_Input_Data { struct Marker_Private : Typed_Render_Data { std::pmr::vector prepared_items{memory_resource(Memory_Domain::Other)}; bool use_prepared_items = false; - void draw_item(QPainter* painter, const Marker_Item& item, Abs_Axis* x_axis, Abs_Axis* y_axis) { + void draw_item(Raster_Canvas* painter, const Marker_Item& item, Abs_Axis* x_axis, Abs_Axis* y_axis) { const auto x_transform = render_axis_transform(*x_axis); const auto y_transform = render_axis_transform(*y_axis); - double x = x_transform.to_pixel(item.position.x()); - double y = y_transform.to_pixel(item.position.y()); - Range x_range = x_transform.coordinate_range; - Range y_range = y_transform.coordinate_range; - double x0 = x_transform.to_pixel(x_range.origin); - double x1 = x_transform.to_pixel(x_range.target); - double y0 = y_transform.to_pixel(y_range.origin); - double y1 = y_transform.to_pixel(y_range.target); + const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis); + const auto marker = core::prepare_marker_geometry(axes, {item.position.x(), item.position.y()}, item.radius); + const Point vertical_start = axes.map(item.position.x(), y_transform.coordinate_range.origin); + const Point vertical_end = axes.map(item.position.x(), y_transform.coordinate_range.target); + const Point horizontal_start = axes.map(x_transform.coordinate_range.origin, item.position.y()); + const Point horizontal_end = axes.map(x_transform.coordinate_range.target, item.position.y()); painter->setPen(item.pen); painter->setBrush(item.brush); if (item.mode == Marker_Mode::Point) { - painter->drawEllipse(QPointF(x, y), item.radius, item.radius); + painter->drawEllipse(qt_bridge::to_qt_point(marker.center), marker.radius, marker.radius); } else if (item.mode == Marker_Mode::Vertical_Line) { - painter->drawLine(QPointF(x, y0), QPointF(x, y1)); + painter->drawLine(qt_bridge::to_qt_point(vertical_start), qt_bridge::to_qt_point(vertical_end)); } else if (item.mode == Marker_Mode::Horizontal_Line) { - painter->drawLine(QPointF(x0, y), QPointF(x1, y)); + painter->drawLine(qt_bridge::to_qt_point(horizontal_start), qt_bridge::to_qt_point(horizontal_end)); } else { - painter->drawLine(QPointF(x, y0), QPointF(x, y1)); - painter->drawLine(QPointF(x0, y), QPointF(x1, y)); + painter->drawLine(qt_bridge::to_qt_point(vertical_start), qt_bridge::to_qt_point(vertical_end)); + painter->drawLine(qt_bridge::to_qt_point(horizontal_start), qt_bridge::to_qt_point(horizontal_end)); } } void prepare_data(const Plot_Render_Snapshot&) override { @@ -58,7 +57,7 @@ struct Marker_Private : Typed_Render_Datax_axis.lock(); auto y_axis = s->y_axis.lock(); diff --git a/Renderive/primitive/Marker.h b/Renderive/primitive/Marker.h index 6e5d674..a0138f5 100644 --- a/Renderive/primitive/Marker.h +++ b/Renderive/primitive/Marker.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include diff --git a/Renderive/primitive/Point_Set.cpp b/Renderive/primitive/Point_Set.cpp index 468d740..71ab308 100644 --- a/Renderive/primitive/Point_Set.cpp +++ b/Renderive/primitive/Point_Set.cpp @@ -3,6 +3,7 @@ #include "../Axis/Abs_Axis_p.h" #include "../base/Frame_Memory.h" #include "../base/Memory.h" +#include "../qt/Core_Geometry_Adapter.h" namespace renderive { struct Point_Set_Input_Data : Latest_Input_Data { std::pmr::vector points{memory_resource(Memory_Domain::Point_Set)}; @@ -51,7 +52,7 @@ struct Point_Set_Private : Typed_Render_Datasize / 2.0; if (s->shape == Point_Set_Shape::Circle) { painter->drawEllipse(point, half, half); @@ -65,22 +66,20 @@ struct Point_Set_Private : Typed_Render_Datax_axis.lock(); auto y_axis = s->y_axis.lock(); if (!x_axis || !y_axis || data_points.empty()) return; - const auto x_transform = render_axis_transform(*x_axis); - const auto y_transform = render_axis_transform(*y_axis); + const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis); + const std::vector source_points = qt_bridge::to_core_points(data_points); + const std::vector mapped_points = core::prepare_point_set_geometry(axes, source_points); std::pmr::vector points(frame_memory_resource()); - points.reserve(data_points.size()); - for (int i = 0; i < data_points.size(); ++i) { - const QPointF& point = data_points.at(i); - points.push_back(QPointF(x_transform.to_pixel(point.x()), y_transform.to_pixel(point.y()))); - } + points.reserve(mapped_points.size()); + for (const Point point : mapped_points) + points.push_back(qt_bridge::to_qt_point(point)); painter->save(); - painter->setRenderHint(QPainter::Antialiasing, s->antialias); painter->setPen(s->pen); painter->setBrush(s->brush); if (data_colors.size() == points.size()) { diff --git a/Renderive/primitive/Point_Set.h b/Renderive/primitive/Point_Set.h index 88f0196..afdd583 100644 --- a/Renderive/primitive/Point_Set.h +++ b/Renderive/primitive/Point_Set.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include diff --git a/Renderive/primitive/Raster_Image.cpp b/Renderive/primitive/Raster_Image.cpp index c74a3a5..98c6b10 100644 --- a/Renderive/primitive/Raster_Image.cpp +++ b/Renderive/primitive/Raster_Image.cpp @@ -30,7 +30,7 @@ struct Raster_Image_Private : Typed_Render_Datax_axis.lock(); auto y_axis = s->y_axis.lock(); diff --git a/Renderive/primitive/Raster_Image_p.h b/Renderive/primitive/Raster_Image_p.h index fd112f6..c6c8bb4 100644 --- a/Renderive/primitive/Raster_Image_p.h +++ b/Renderive/primitive/Raster_Image_p.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include @@ -56,14 +55,13 @@ public: } } } - void draw(QPainter* painter, const QRectF& target_rect, Image_Interpolation_Mode mode = Image_Interpolation_Mode::Nearest) const { + void draw(Raster_Canvas* painter, const QRectF& target_rect, Image_Interpolation_Mode mode = Image_Interpolation_Mode::Nearest) const { draw(painter, target_rect, QRectF(0.0, 0.0, static_cast(image_data.width()), static_cast(image_data.height())), mode); } - void draw(QPainter* painter, const QRectF& target_rect, const QRectF& source_rect, Image_Interpolation_Mode mode) const { + void draw(Raster_Canvas* painter, const QRectF& target_rect, const QRectF& source_rect, Image_Interpolation_Mode mode) const { if (image_data.isNull()) return; painter->save(); - painter->setRenderHint(QPainter::SmoothPixmapTransform, mode == Image_Interpolation_Mode::Bilinear); painter->drawImage(target_rect, image_data.image(), source_rect); painter->restore(); } diff --git a/Widget/base/Graphic.h b/Widget/base/Graphic.h index ff30e92..758615f 100644 --- a/Widget/base/Graphic.h +++ b/Widget/base/Graphic.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include "Renderive/architecture/global.h" @@ -21,8 +22,9 @@ public: return; it = !it; QPixmap* cur = it ? buffer1.get() : buffer2.get(); - QPainter painter(cur); - painter.fillRect(cur->rect(), Qt::red); + QImage image(cur->size(), QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::red); + *cur = QPixmap::fromImage(std::move(image)); } void update_texture() { QPixmap* cur = it ? buffer1.get() : buffer2.get(); diff --git a/Widget/component/Table/global.h b/Widget/component/Table/global.h index 7c80e31..0fda693 100644 --- a/Widget/component/Table/global.h +++ b/Widget/component/Table/global.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/main.cmake b/main.cmake index 18c6b9a..a07a1c6 100644 --- a/main.cmake +++ b/main.cmake @@ -1,42 +1,41 @@ - -w_use3rd(Qt5) - - -set(renderive_dependencies - global::GTest - global::blend2d -) -rcl_add_dependency_action_targets(renderive_env ${renderive_dependencies}) -library_is_installed_with_rely(renderive_dependencies_installed ${renderive_dependencies}) -if (NOT renderive_dependencies_installed) - message(STATUS "============== Renderive 依赖未安装,请先构建 renderive_env 目标") - return() -else () - find_package(blend2d CONFIG REQUIRED) - if (NOT TARGET blend2d::blend2d) - message(FATAL_ERROR "Renderive依赖错误 blend2d::blend2d was not found") - return() - endif () - find_package(GTest CONFIG REQUIRED) - if (NOT TARGET GTest::gtest) - message(FATAL_ERROR "Renderive依赖错误 GTest::GTest was not found") - return() - endif () -endif () - -option(YSGRAPHIC_CORE_BUILD_TESTS "Build Renderive_Core test targets" ON) -option(YSGRAPHIC_CORE_ENABLE_ASAN "Build Renderive_Core targets with AddressSanitizer" OFF) -option(YSGRAPHIC_CORE_ENABLE_TSAN "Build Renderive_Core targets with ThreadSanitizer" OFF) +option(RENDERIVE_BUILD_TESTS "Build Renderive test targets" ON) +option(RENDERIVE_ENABLE_ASAN "Build Renderive targets with AddressSanitizer" OFF) +option(RENDERIVE_ENABLE_TSAN "Build Renderive targets with ThreadSanitizer" OFF) option(RENDERIVE_MEMORY_DOMAIN_STATS "Enable Renderive memory domain statistics" ON) option(RENDERIVE_MEMORY_GLOBAL_POOL "Enable Renderive small object synchronized pool" ON) option(RENDERIVE_FRAME_ARENA_GROW "Enable Renderive frame arena high-water growth" ON) -if (YSGRAPHIC_CORE_ENABLE_ASAN AND YSGRAPHIC_CORE_ENABLE_TSAN) +set(renderive_dependencies + global::blend2d +) +if (RENDERIVE_BUILD_TESTS) + list(APPEND renderive_dependencies global::GTest) +endif () + +rcl_add_dependency_action_targets(renderive_env ${renderive_dependencies}) +library_is_installed_with_rely(renderive_dependencies_installed ${renderive_dependencies}) +if (NOT renderive_dependencies_installed) + message(STATUS "============== Renderive dependencies are not installed, build renderive_env first") + return() +endif () + +find_package(blend2d CONFIG REQUIRED) +if (NOT TARGET blend2d::blend2d) + message(FATAL_ERROR "Renderive dependency error: blend2d::blend2d was not found") +endif () + +if (RENDERIVE_BUILD_TESTS) + find_package(GTest CONFIG REQUIRED) + if (NOT TARGET GTest::gtest AND NOT TARGET GTest::GTest) + message(FATAL_ERROR "Renderive dependency error: GTest target was not found") + endif () +endif () + +if (RENDERIVE_ENABLE_ASAN AND RENDERIVE_ENABLE_TSAN) message(FATAL_ERROR "Renderive cannot enable ASan and TSan simultaneously") endif () - -if (YSGRAPHIC_CORE_ENABLE_ASAN AND MSVC) +if (RENDERIVE_ENABLE_ASAN AND MSVC) string(REPLACE "/RTC1" "" __PSC_CMAKE_CXX_FLAGS_DEBUG "${__PSC_CMAKE_CXX_FLAGS_DEBUG}") string(REPLACE "/RTC1" "" __PSC_CMAKE_C_FLAGS_DEBUG "${__PSC_CMAKE_C_FLAGS_DEBUG}") set(__PSC_CMAKE_CXX_FLAGS_DEBUG "${__PSC_CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "" FORCE) @@ -48,7 +47,7 @@ endif () function(renderive_apply_sanitizer target) get_target_property(target_type ${target} TYPE) - if (YSGRAPHIC_CORE_ENABLE_ASAN) + if (RENDERIVE_ENABLE_ASAN) if (MSVC) target_link_libraries(${target} PRIVATE Renderive_ASAN_Options) if (target_type STREQUAL "STATIC_LIBRARY") @@ -63,7 +62,7 @@ function(renderive_apply_sanitizer target) endif () endif () endif () - if (YSGRAPHIC_CORE_ENABLE_TSAN) + if (RENDERIVE_ENABLE_TSAN) if (MSVC) message(FATAL_ERROR "Renderive TSan is not supported by MSVC") endif () @@ -84,7 +83,72 @@ function(renderive_enable_qt_target target) ) endfunction() -set(renderive_public_libraries +function(renderive_assert_core_boundary) + foreach(path IN LISTS ARGN) + if (IS_DIRECTORY "${path}") + continue() + endif () + file(READ "${path}" content) + if (content MATCHES "#include[ \t]*" OR + content MATCHES "#include[ \t]*" OR + content MATCHES "QPainter|QImage|QPen|QBrush|QColor|QFont|QString|QPointF|QRectF|QSize|QMouseEvent|QWheelEvent|QKeyEvent|Qt::Orientation|Qt::Alignment|Data_Buffer_Role|_p\\.h") + message(FATAL_ERROR "Renderive_Core boundary violation in ${path}") + endif () + endforeach () +endfunction() + +set(renderive_root "${CMAKE_CURRENT_LIST_DIR}/Renderive") +set(renderive_core_root "${renderive_root}/Core") +set(renderive_widget_root "${CMAKE_CURRENT_LIST_DIR}/Widget") +set(renderive_demo_root "${CMAKE_CURRENT_LIST_DIR}/Demo_Gallery") + +file(GLOB_RECURSE renderive_core_sources CONFIGURE_DEPENDS + "${renderive_core_root}/*.cpp" + "${renderive_core_root}/*.h" +) +renderive_assert_core_boundary(${renderive_core_sources}) + +add_library(Renderive_Core STATIC ${renderive_core_sources}) +target_include_directories(Renderive_Core PUBLIC "${CMAKE_CURRENT_LIST_DIR}") +target_link_libraries(Renderive_Core PUBLIC + blend2d::blend2d +) +renderive_apply_sanitizer(Renderive_Core) + +set(ver 5.14.2) + +set(qt_candidates + "D:/Qt/Qt${ver}" + "C:/Qt/Qt${ver}" + "D:/Qt/${ver}" + "C:/Qt/${ver}" +) +set(t msvc2017_64) +select_first_existing_directory(qb ${qt_candidates}) +set(Qt5_BaseDIR "${qb}/${ver}/${t}") +set(Qt5_DIR "${Qt5_BaseDIR}/lib/cmake/Qt5") +set(Qt5_Deploy "${Qt5_BaseDIR}/bin/windeployqt.exe") + + +message("Qt5_DIR ${Qt5_DIR}") + + +find_package(Qt5 REQUIRED COMPONENTS + Core Gui Widgets Svg Xml + Network Sql + 3DCore 3DRender 3DInput 3DExtras + Quick 3DQuickScene2D QuickWidgets Qml PrintSupport + Multimedia + SerialPort CONFIG +) + +# Qt5.cmake adds a directory-level Qt source include path. Keep it out of the +# Core compile command; Core only needs its own public headers and Blend2D. +set_target_properties(Renderive_Core PROPERTIES + INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}" +) + +set(renderive_qt_public_libraries Qt5::Widgets Qt5::Core Qt5::Svg @@ -93,15 +157,14 @@ set(renderive_public_libraries Qt5::Multimedia Qt5::QuickWidgets Core_Static - blend2d::blend2d + Renderive_Core ) find_package(OpenGL QUIET) if (OpenGL_FOUND) - list(APPEND renderive_public_libraries OpenGL::GL) + list(APPEND renderive_qt_public_libraries OpenGL::GL) endif () -set(renderive_root "${CMAKE_CURRENT_LIST_DIR}/Renderive") file(GLOB_RECURSE renderive_sources CONFIGURE_DEPENDS "${renderive_root}/*.c" "${renderive_root}/*.cpp" @@ -110,10 +173,11 @@ file(GLOB_RECURSE renderive_sources CONFIGURE_DEPENDS ) list(FILTER renderive_sources EXCLUDE REGEX "main\\.cpp$") list(FILTER renderive_sources EXCLUDE REGEX "Performance_Shower(_p)?\\.(cpp|h)$") +list(FILTER renderive_sources EXCLUDE REGEX "[/\\\\]Core[/\\\\]") add_library(Renderive STATIC ${renderive_sources}) target_include_directories(Renderive PUBLIC "${CMAKE_CURRENT_LIST_DIR}") -target_link_libraries(Renderive PUBLIC ${renderive_public_libraries}) +target_link_libraries(Renderive PUBLIC ${renderive_qt_public_libraries}) target_compile_definitions(Renderive PUBLIC RENDERIVE_MEMORY_DOMAIN_STATS=$ RENDERIVE_MEMORY_GLOBAL_POOL=$ @@ -123,16 +187,17 @@ renderive_enable_qt_target(Renderive) renderive_apply_sanitizer(Renderive) set(renderive_diagnostics_sources + "${renderive_root}/Diagnostics/Performance_Shower.h" "${renderive_root}/plottable/Performance_Shower.cpp" "${renderive_root}/plottable/Performance_Shower_p.h" + "${renderive_root}/Core/Telemetry/Csv_Telemetry_Sink.h" ) add_library(Renderive_Diagnostics STATIC ${renderive_diagnostics_sources}) target_include_directories(Renderive_Diagnostics PUBLIC "${CMAKE_CURRENT_LIST_DIR}") -target_link_libraries(Renderive_Diagnostics PUBLIC Renderive) +target_link_libraries(Renderive_Diagnostics PUBLIC Renderive Renderive_Core) renderive_enable_qt_target(Renderive_Diagnostics) renderive_apply_sanitizer(Renderive_Diagnostics) -set(renderive_widget_root "${CMAKE_CURRENT_LIST_DIR}/Widget") file(GLOB_RECURSE renderive_widget_sources CONFIGURE_DEPENDS "${renderive_widget_root}/*.c" "${renderive_widget_root}/*.cpp" @@ -146,15 +211,13 @@ target_link_libraries(Renderive_Widget PUBLIC Renderive) renderive_enable_qt_target(Renderive_Widget) renderive_apply_sanitizer(Renderive_Widget) -if (YSGRAPHIC_CORE_BUILD_TESTS) +if (RENDERIVE_BUILD_TESTS) enable_testing() if (TARGET GTest::gtest) set(renderive_gtest_library GTest::gtest) - elseif (TARGET GTest::GTest) - set(renderive_gtest_library GTest::GTest) else () - message(FATAL_ERROR "GTest target was not found") + set(renderive_gtest_library GTest::GTest) endif () function(renderive_add_gtest target test_name source) @@ -168,6 +231,27 @@ if (YSGRAPHIC_CORE_BUILD_TESTS) add_test(NAME Renderive.${test_name} COMMAND ${target}) endfunction() + function(renderive_add_core_gtest target test_name source) + add_executable(${target} EXCLUDE_FROM_ALL "${CMAKE_CURRENT_LIST_DIR}/tests/${source}") + target_link_libraries(${target} PRIVATE + Renderive_Core + ${renderive_gtest_library} + ${ARGN} + ) + renderive_apply_sanitizer(${target}) + add_test(NAME Renderive.${test_name} COMMAND ${target}) + endfunction() + + renderive_add_core_gtest( + Renderive_Core_Axis_Transform + Axis_Transform + Axis_Transform.cpp + ) + renderive_add_core_gtest( + Renderive_Core_Batches_2_4 + Core_Batches_2_4 + Core_Batches_2_4.cpp + ) renderive_add_gtest( Renderive_ASan_Runtime_Smoke ASan_Runtime_Smoke @@ -260,7 +344,6 @@ if (YSGRAPHIC_CORE_BUILD_TESTS) ) endif () -set(renderive_demo_root "${CMAKE_CURRENT_LIST_DIR}/Demo_Gallery") file(GLOB_RECURSE renderive_demo_sources CONFIGURE_DEPENDS "${renderive_demo_root}/*.c" "${renderive_demo_root}/*.cpp" @@ -278,4 +361,4 @@ renderive_apply_sanitizer(Demo_Gallery_Lib) add_executable(Demo_Gallery "${CMAKE_CURRENT_LIST_DIR}/demo_gallery_main.cpp") target_link_libraries(Demo_Gallery PRIVATE Demo_Gallery_Lib) renderive_enable_qt_target(Demo_Gallery) -renderive_apply_sanitizer(Demo_Gallery) \ No newline at end of file +renderive_apply_sanitizer(Demo_Gallery) diff --git a/tests/Memory_Data_Path_Stress.cpp b/tests/Memory_Data_Path_Stress.cpp index 79142f2..ca78026 100644 --- a/tests/Memory_Data_Path_Stress.cpp +++ b/tests/Memory_Data_Path_Stress.cpp @@ -390,14 +390,14 @@ TEST(Memory_Data_Path_Stress, ExplicitNonMonotonicCurveClipsSegmentsAndBreaksMis } EXPECT_TRUE(saw_break); } -TEST(Memory_Data_Path_Stress, FrameRasterContextPreservesBackendOrder) { +TEST(Memory_Data_Path_Stress, FrameRasterContextPreservesDrawOrder) { QImage image(32, 32, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); renderive::Frame_Raster_Context context(image); - context.qt().fillRect(QRect(0, 0, 32, 32), Qt::red); + context.canvas().fillRect(QRect(0, 0, 32, 32), Qt::red); const std::array line{{QPointF(2.0, 16.0), QPointF(29.0, 16.0)}}; context.stroke_polyline(line, QPen(Qt::blue, 3.0, Qt::SolidLine, Qt::FlatCap)); - context.qt().fillRect(QRect(0, 0, 4, 4), Qt::green); + context.canvas().fillRect(QRect(0, 0, 4, 4), Qt::green); context.finish(); EXPECT_EQ(image.pixelColor(8, 8), QColor(Qt::red)); EXPECT_GT(image.pixelColor(16, 16).blue(), 200); diff --git a/tests/Performance_Shower_Interaction.cpp b/tests/Performance_Shower_Interaction.cpp index fc5b2c3..d616bd8 100644 --- a/tests/Performance_Shower_Interaction.cpp +++ b/tests/Performance_Shower_Interaction.cpp @@ -61,7 +61,9 @@ TEST(Performance_Shower_Interaction, RepeatedToggleDoubleClickAndWheelStayRespon plot.init(); plot.show(); plot.start_render(240); - std::unique_ptr control(create_control(&plot)); + std::shared_ptr performance_shower; + std::unique_ptr control(create_control(&plot, {}, &performance_shower)); + ASSERT_NE(performance_shower, nullptr); control->show(); app.processEvents(); QPushButton* performance_button = find_performance_button(control.get()); @@ -75,7 +77,7 @@ TEST(Performance_Shower_Interaction, RepeatedToggleDoubleClickAndWheelStayRespon } double_click_button(performance_button); pump(app, plot, 200); - renderive::set_performance_shower_enabled(plot, true); + performance_shower->setEnabled(true); pump(app, plot, 200); click_plot(&plot, QPoint(8, 8), QEvent::MouseButtonPress); click_plot(&plot, QPoint(8, 8), QEvent::MouseButtonRelease); @@ -90,7 +92,16 @@ TEST(Performance_Shower_Interaction, RepeatedToggleDoubleClickAndWheelStayRespon pump(app, plot, 10); } pump(app, plot, 200); + EXPECT_TRUE(performance_shower->enabled()); + renderive::set_performance_shower_enabled(plot, false); + EXPECT_FALSE(renderive::performance_shower_enabled(plot)); + auto compatibility_shower = renderive::attach_performance_shower(plot); + ASSERT_NE(compatibility_shower, nullptr); + EXPECT_EQ(renderive::attach_performance_shower(plot), compatibility_shower); + renderive::set_performance_shower_enabled(plot, true); EXPECT_TRUE(renderive::performance_shower_enabled(plot)); + renderive::detach_performance_shower(plot); + EXPECT_FALSE(renderive::performance_shower_enabled(plot)); plot.pause_render(); } int main(int argc, char** argv) {