diff --git a/render_2D/plottable/Spectrum.cpp b/render_2D/plottable/Spectrum.cpp index fe5aa4b..1baffd2 100644 --- a/render_2D/plottable/Spectrum.cpp +++ b/render_2D/plottable/Spectrum.cpp @@ -490,7 +490,7 @@ void Spectrum_Control::paint_partition(Painter& painter, int partition_index) { const auto& partition = output.partitions[static_cast(partition_index)]; const auto view = render_state_view(); const auto& state = render_properties(view); - painter.clip(partition.clip); + const auto clip = painter.scoped_clip(partition.clip); paint_curve(painter, partition.maximum, state.max_pen, state.max_brush); paint_curve(painter, partition.minimum, state.min_pen, state.min_brush); paint_curve(painter, partition.current, state.current_pen, state.current_brush); diff --git a/render_2D/render/Blend2D_Cache.cpp b/render_2D/render/Blend2D_Cache.cpp index dadbe83..185e1ac 100644 --- a/render_2D/render/Blend2D_Cache.cpp +++ b/render_2D/render/Blend2D_Cache.cpp @@ -222,11 +222,22 @@ Painter::~Painter() { context_.end(); } -void Painter::clip(RectF value) { +Painter::Clip_Scope::Clip_Scope(Painter& painter, RectF value) { value = value.normalized(); - if (!active_ || value.empty()) + if (!painter.active_ || value.empty()) return; - context_.clip_to_rect(BLRect(value.x, value.y, value.width, value.height)); + painter.context_.save(); + painter.context_.clip_to_rect(BLRect(value.x, value.y, value.width, value.height)); + painter_ = &painter; +} + +Painter::Clip_Scope::~Clip_Scope() { + if (painter_) + painter_->context_.restore(); +} + +Painter::Clip_Scope Painter::scoped_clip(RectF value) { + return Clip_Scope(*this, value); } void Painter::composite(const Blend2D_Color_Cache& source) { diff --git a/render_2D/render/Blend2D_Cache.h b/render_2D/render/Blend2D_Cache.h index 2663238..404e3b5 100644 --- a/render_2D/render/Blend2D_Cache.h +++ b/render_2D/render/Blend2D_Cache.h @@ -28,13 +28,22 @@ private: class Painter { public: + class Clip_Scope { + public: + Clip_Scope(Painter& painter, RectF rect); + ~Clip_Scope(); + Clip_Scope(const Clip_Scope&) = delete; + Clip_Scope& operator=(const Clip_Scope&) = delete; + private: + Painter* painter_{}; + }; Painter(Blend2D_Color_Cache& cache, Size size); ~Painter(); Painter(const Painter&) = delete; Painter& operator=(const Painter&) = delete; [[nodiscard]] explicit operator bool() const noexcept { return active_; } - void clip(RectF rect); + [[nodiscard]] Clip_Scope scoped_clip(RectF rect); void composite(const Blend2D_Color_Cache& source); void line(PointF first, PointF second, const Pen& pen); void polyline(std::span points, const Pen& pen); diff --git a/render_2D/tests/render_2D_Integration_Tests.cpp b/render_2D/tests/render_2D_Integration_Tests.cpp index 4618326..789e0aa 100644 --- a/render_2D/tests/render_2D_Integration_Tests.cpp +++ b/render_2D/tests/render_2D_Integration_Tests.cpp @@ -86,6 +86,28 @@ TEST(Renderive_Core2, EveryCurveInterpolationModeHasDistinctSamplingSemantics) { EXPECT_LE(clipped.front().x, 0.0); EXPECT_GE(clipped.back().x, 100.0); } +TEST(Renderive_Core2, PainterScopedClipRestoresPreviousClip) { + detail::Blend2D_Color_Cache cache; + { + detail::Painter painter(cache, {8, 2}); + { + const auto clip = painter.scoped_clip({0.0, 0.0, 4.0, 2.0}); + painter.rect({0.0, 0.0, 8.0, 2.0}, Pen{.style = Line_Style::None}, + Brush{Color::red(), Brush_Style::Solid}); + } + { + const auto clip = painter.scoped_clip({4.0, 0.0, 4.0, 2.0}); + painter.rect({0.0, 0.0, 8.0, 2.0}, Pen{.style = Line_Style::None}, + Brush{Color::green(), Brush_Style::Solid}); + } + } + const Image_View view = cache.view(); + ASSERT_EQ(view.width, 8); + ASSERT_EQ(view.height, 2); + const auto* row = reinterpret_cast(view.data); + EXPECT_EQ(row[1], pack_rgba(255, 0, 0)); + EXPECT_EQ(row[6], pack_rgba(0, 255, 0)); +} TEST(Renderive_Core2, BicubicHeatmapUsesRealCubicResampling) { const std::array source{ pack_rgba(255, 0, 0), pack_rgba(0, 0, 0), pack_rgba(255, 255, 255), pack_rgba(0, 0, 255),