From d620b2327963df26a1bca8fc2593b3f6b69163b4 Mon Sep 17 00:00:00 2001 From: Kacper Kasper Date: Sun, 4 Jan 2026 22:17:56 +0100 Subject: [PATCH] Implement missing methods in PictureBoundingBoxPlayer * Remove const from gradient in StrokeLineGradient. BoundingBoxPlayer needs to apply transform to it. * Extend LayersTest. * Use BStackOrHeapArray for polygons. * Fixes #12937. Change-Id: If0a4abec0168dbe9afe7ea92bb29017ea8c89c79 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10211 Tested-by: Commit checker robot Reviewed-by: waddlesplash --- headers/private/interface/PicturePlayer.h | 3 +- src/kits/interface/PicturePlayer.cpp | 5 +- src/servers/app/PictureBoundingBoxPlayer.cpp | 202 +++++++++++++++--- src/servers/app/ServerPicture.cpp | 5 +- .../servers/app/layers_test/LayersTest.cpp | 73 +++++-- 5 files changed, 236 insertions(+), 52 deletions(-) diff --git a/headers/private/interface/PicturePlayer.h b/headers/private/interface/PicturePlayer.h index 73ef0c0feb..335e707428 100644 --- a/headers/private/interface/PicturePlayer.h +++ b/headers/private/interface/PicturePlayer.h @@ -208,8 +208,7 @@ public: BGradient& gradient, bool fill) {} virtual void DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill) {} virtual void SetFillRule(int32 fillRule) {} - virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, - const BGradient& gradient) {} + virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, BGradient& gradient) {} }; diff --git a/src/kits/interface/PicturePlayer.cpp b/src/kits/interface/PicturePlayer.cpp index 3538ac4282..e3dc147410 100644 --- a/src/kits/interface/PicturePlayer.cpp +++ b/src/kits/interface/PicturePlayer.cpp @@ -101,8 +101,7 @@ public: BGradient& gradient, bool fill); virtual void DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill); virtual void SetFillRule(int32 fillRule); - virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, - const BGradient& gradient); + virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, BGradient& gradient); private: void* fUserData; @@ -626,7 +625,7 @@ CallbackAdapterPlayer::SetFillRule(int32 fillRule) void -CallbackAdapterPlayer::StrokeLineGradient(const BPoint& start, const BPoint& end, const BGradient& gradient) +CallbackAdapterPlayer::StrokeLineGradient(const BPoint& start, const BPoint& end, BGradient& gradient) { fCallbacks->stroke_line_gradient(fUserData, start, end, gradient); } diff --git a/src/servers/app/PictureBoundingBoxPlayer.cpp b/src/servers/app/PictureBoundingBoxPlayer.cpp index b3860ac2cd..efccf43021 100644 --- a/src/servers/app/PictureBoundingBoxPlayer.cpp +++ b/src/servers/app/PictureBoundingBoxPlayer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include //#define DEBUG_TRACE_BB @@ -194,6 +195,23 @@ public: virtual void ScaleBy(double x, double y); virtual void RotateBy(double angleRadians); virtual void BlendLayer(Layer* layer); + virtual void ClipToRect(const BRect& rect, bool inverse); + virtual void ClipToShape(int32 opCount, const uint32 opList[], int32 ptCount, + const BPoint ptList[], bool inverse); + virtual void DrawStringLocations(const char* string, size_t length, const BPoint locations[], + size_t locationCount); + virtual void DrawRectGradient(const BRect& rect, BGradient& gradient, bool fill); + virtual void DrawRoundRectGradient(const BRect& rect, const BPoint& radii, BGradient& gradient, + bool fill); + virtual void DrawBezierGradient(const BPoint controlPoints[4], BGradient& gradient, bool fill); + virtual void DrawArcGradient(const BPoint& center, const BPoint& radii, float startTheta, + float arcTheta, BGradient& gradient, bool fill); + virtual void DrawEllipseGradient(const BRect& rect, BGradient& gradient, bool fill); + virtual void DrawPolygonGradient(size_t numPoints, const BPoint points[], bool isClosed, + BGradient& gradient, bool fill); + virtual void DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill); + virtual void SetFillRule(int32 fillRule); + virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, BGradient& gradient); private: BoundingBoxState* const fState; @@ -248,7 +266,7 @@ expand_rect_for_pen_size(BoundingBoxState* state, RectType& rect) void BoundingBoxCallbacks::MovePenBy(const BPoint& delta) { - TRACE_BB("%p move pen by %.2f %.2f\n", _state, delta.x, delta.y); + TRACE_BB("%p move pen by %.2f %.2f\n", fState, delta.x, delta.y); fState->GetDrawState()->SetPenLocation( fState->GetDrawState()->PenLocation() + delta); @@ -259,7 +277,7 @@ void BoundingBoxCallbacks::StrokeLine(const BPoint& _start, const BPoint& _end) { - TRACE_BB("%p stroke line %.2f %.2f -> %.2f %.2f\n", _state, + TRACE_BB("%p stroke line %.2f %.2f -> %.2f %.2f\n", fState, _start.x, _start.y, _end.x, _end.y); BPoint start = _start; @@ -295,7 +313,7 @@ BoundingBoxCallbacks::StrokeLine(const BPoint& _start, void BoundingBoxCallbacks::DrawRect(const BRect& _rect, bool fill) { - TRACE_BB("%p draw rect fill=%d %.2f %.2f %.2f %.2f\n", _state, fill, + TRACE_BB("%p draw rect fill=%d %.2f %.2f %.2f %.2f\n", fState, fill, _rect.left, _rect.top, _rect.right, _rect.bottom); BRect rect = _rect; @@ -344,7 +362,7 @@ BoundingBoxCallbacks::DrawBezier(const BPoint viewPoints[4], bool fill) { TRACE_BB("%p draw bezier fill=%d (%.2f %.2f) (%.2f %.2f) " "(%.2f %.2f) (%.2f %.2f)\n", - _state, + fState, fill, viewPoints[0].x, viewPoints[0].y, viewPoints[1].x, viewPoints[1].y, @@ -362,7 +380,7 @@ BoundingBoxCallbacks::DrawBezier(const BPoint viewPoints[4], bool fill) void BoundingBoxCallbacks::DrawEllipse(const BRect& _rect, bool fill) { - TRACE_BB("%p draw ellipse fill=%d (%.2f %.2f) (%.2f %.2f)\n", _state, fill, + TRACE_BB("%p draw ellipse fill=%d (%.2f %.2f) (%.2f %.2f)\n", fState, fill, _rect.left, _rect.top, _rect.right, _rect.bottom); BRect rect = _rect; @@ -390,27 +408,9 @@ determine_bounds_polygon(BoundingBoxState* state, int32 numPoints, if (numPoints <= 0) return; - if (numPoints <= 200) { - // fast path: no malloc/free, also avoid - // constructor/destructor calls - char data[200 * sizeof(BPoint)]; - BPoint* points = (BPoint*)data; - - state->PenToLocalTransform().Apply(points, viewPoints, numPoints); - get_polygon_frame(points, numPoints, &outRect); - - } else { - // avoid constructor/destructor calls by - // using malloc instead of new [] - BPoint* points = (BPoint*)malloc(numPoints * sizeof(BPoint)); - if (points == NULL) - return; - - state->PenToLocalTransform().Apply(points, viewPoints, numPoints); - get_polygon_frame(points, numPoints, &outRect); - - free(points); - } + BStackOrHeapArray points(numPoints); + state->PenToLocalTransform().Apply(points, viewPoints, numPoints); + get_polygon_frame(points, numPoints, &outRect); } @@ -769,7 +769,155 @@ BoundingBoxCallbacks::BlendLayer(Layer* layer) } -// #pragma mark - PictureBoundingBoxPlayer +void +BoundingBoxCallbacks::DrawRectGradient(const BRect& _rect, BGradient& gradient, bool fill) +{ + TRACE_BB("%p draw rect gradient fill=%d %.2f %.2f %.2f %.2f\n", fState, fill, + _rect.left, _rect.top, _rect.right, _rect.bottom); + + BRect rect = _rect; + const SimpleTransform transform = fState->PenToLocalTransform(); + transform.Apply(&rect); + transform.Apply(&gradient); + if (!fill) + expand_rect_for_pen_size(fState, rect); + fState->IncludeRect(rect); +} + + +void +BoundingBoxCallbacks::DrawRoundRectGradient(const BRect& rect, const BPoint& radii, + BGradient& gradient, bool fill) +{ + TRACE_BB("%p draw round rect gradient fill=%d %.2f %.2f %.2f %.2f\n", fState, fill, + rect.left, rect.top, rect.right, rect.bottom); + DrawRectGradient(rect, gradient, fill); +} + + +void +BoundingBoxCallbacks::DrawBezierGradient(const BPoint controlPoints[4], BGradient& gradient, + bool fill) +{ + TRACE_BB("%p draw bezier gradient fill=%d (%.2f %.2f) (%.2f %.2f) " + "(%.2f %.2f) (%.2f %.2f)\n", + fState, + fill, + viewPoints[0].x, viewPoints[0].y, + viewPoints[1].x, viewPoints[1].y, + viewPoints[2].x, viewPoints[2].y, + viewPoints[3].x, viewPoints[3].y); + + BRect rect; + determine_bounds_bezier(fState, controlPoints, rect); + fState->PenToLocalTransform().Apply(&gradient); + if (!fill) + expand_rect_for_pen_size(fState, rect); + fState->IncludeRect(rect); +} + + +void +BoundingBoxCallbacks::DrawEllipseGradient(const BRect& _rect, BGradient& gradient, bool fill) +{ + TRACE_BB("%p draw ellipse gradient fill=%d (%.2f %.2f) (%.2f %.2f)\n", fState, fill, + _rect.left, _rect.top, _rect.right, _rect.bottom); + + BRect rect = _rect; + const SimpleTransform transform = fState->PenToLocalTransform(); + transform.Apply(&rect); + transform.Apply(&gradient); + if (!fill) + expand_rect_for_pen_size(fState, rect); + fState->IncludeRect(rect); +} + + +void +BoundingBoxCallbacks::DrawArcGradient(const BPoint& center, const BPoint& radii, float startTheta, + float arcTheta, BGradient& gradient, bool fill) +{ + BRect rect(center.x - radii.x, center.y - radii.y, + center.x + radii.x - 1, center.y + radii.y - 1); + DrawEllipseGradient(rect, gradient, fill); +} + + +void +BoundingBoxCallbacks::DrawPolygonGradient(size_t numPoints, const BPoint points[], bool isClosed, + BGradient& gradient, bool fill) +{ + TRACE_BB("%p draw polygon fill=%d (%ld points)\n", fState, fill, numPoints); + if (numPoints <= 0) + return; + + BRect rect; + determine_bounds_polygon(fState, numPoints, points, rect); + fState->PenToLocalTransform().Apply(&gradient); + if (!fill) + expand_rect_for_pen_size(fState, rect); + fState->IncludeRect(rect); +} + + +void +BoundingBoxCallbacks::DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill) +{ + BRect rect = shape.Bounds(); + + // FIXME: this doesn't work - ShapePainter uses ScreenTransform unconditionally + TRACE_BB("%p stroke shape gradient (bounds %.2f %.2f %.2f %.2f)\n", fState, + rect.left, rect.top, rect.right, rect.bottom); + + const SimpleTransform transform = fState->PenToLocalTransform(); + transform.Apply(&rect); + transform.Apply(&gradient); + if (!fill) + expand_rect_for_pen_size(fState, rect); + fState->IncludeRect(rect); +} + + +void +BoundingBoxCallbacks::ClipToRect(const BRect& rect, bool inverse) +{ + // TODO +} + + +void +BoundingBoxCallbacks::ClipToShape(int32 opCount, const uint32 opList[], int32 ptCount, + const BPoint ptList[], bool inverse) +{ + // TODO +} + + +void +BoundingBoxCallbacks::DrawStringLocations(const char* string, size_t length, + const BPoint locations[], size_t locationCount) +{ + // TODO +} + + +void +BoundingBoxCallbacks::SetFillRule(int32 fillRule) +{ + // TODO +} + + +void +BoundingBoxCallbacks::StrokeLineGradient(const BPoint& start, const BPoint& end, + BGradient& gradient) +{ + TRACE_BB("%p stroke line gradient %.2f %.2f -> %.2f %.2f\n", fState, + start.x, start.y, end.x, end.y); + const SimpleTransform transform = fState->PenToLocalTransform(); + transform.Apply(&gradient); + StrokeLine(start, end); +} /* static */ void diff --git a/src/servers/app/ServerPicture.cpp b/src/servers/app/ServerPicture.cpp index 5dafb81d4f..b2e8d15733 100644 --- a/src/servers/app/ServerPicture.cpp +++ b/src/servers/app/ServerPicture.cpp @@ -305,8 +305,7 @@ public: BGradient& gradient, bool fill); virtual void DrawShapeGradient(const BShape& shape, BGradient& gradient, bool fill); virtual void SetFillRule(int32 fillRule); - virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, - const BGradient& gradient); + virtual void StrokeLineGradient(const BPoint& start, const BPoint& end, BGradient& gradient); private: Canvas* const fCanvas; @@ -570,7 +569,7 @@ CanvasCallbacks::DrawShapeGradient(const BShape& shape, BGradient& gradient, boo void CanvasCallbacks::StrokeLineGradient(const BPoint& _start, const BPoint& _end, - const BGradient& gradient) + BGradient& gradient) { BPoint start = _start; BPoint end = _end; diff --git a/src/tests/servers/app/layers_test/LayersTest.cpp b/src/tests/servers/app/layers_test/LayersTest.cpp index d384d11c5a..26965dbaf7 100644 --- a/src/tests/servers/app/layers_test/LayersTest.cpp +++ b/src/tests/servers/app/layers_test/LayersTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016, Haiku Inc. + * Copyright 2016, 2026, Haiku Inc. * Distributed under the terms of the MIT License. * * Authors: @@ -9,6 +9,9 @@ #include #include +#include +#include +#include #include #include #include @@ -24,11 +27,33 @@ class View : public BView { virtual ~View(); virtual void Draw(BRect); + BGradientLinear g[3]; + BPoint bezierPoints[3][4]; + BShape shapes[3]; + BPolygon polygon; }; View::View(BRect rect) : BView(rect, "gradients", B_FOLLOW_ALL, B_WILL_DRAW) { + SetViewColor(0, 255, 0); + for(int i = 0; i < 3; i++) { + g[i].SetStart(BPoint(0, i * 100)); + g[i].SetEnd(BPoint(0, (i + 1) * 100)); + g[i].AddColor((rgb_color) { 255, 0, 0, 255 }, 255.0); + g[i].AddColor((rgb_color) { 255, 255, 255, 0 }, 0.0); + bezierPoints[i][0] = BPoint(540, i * 100 + 10); + bezierPoints[i][1] = BPoint(610, i * 100 + 10); + bezierPoints[i][2] = BPoint(580, (i + 1) * 100); + bezierPoints[i][3] = BPoint(630, (i + 1) * 100); + shapes[i].MoveTo(BPoint(640, i * 100 + 10)); + shapes[i].LineTo(BPoint(670, i * 100 + 40)); + shapes[i].LineTo(BPoint(700, i * 100 + 40)); + shapes[i].LineTo(BPoint(640, i * 100 + 100)); + shapes[i].Close(); + } + BPoint p[4] = { BPoint(0, 0), BPoint(0, 50), BPoint(90, 90), BPoint(50, 50) }; + polygon.AddPoints(p, 4); } @@ -40,22 +65,36 @@ View::~View() void View::Draw(BRect) { - BGradientRadial g(BPoint(50, 50), 50.0); - g.AddColor((rgb_color) { 0, 0, 0, 255 }, 255.0); - g.AddColor((rgb_color) { 255, 255, 255, 0 }, 0.0); SetHighColor(0, 0, 0); - BeginLayer(255); - // 1, 1 - FillRect(BRect(10, 10, 90, 90)); - // 2, 1 - FillRect(BRect(100, 10, 190, 90), g); - EndLayer(); - BeginLayer(100); - // 1, 2 - FillRect(BRect(10, 100, 90, 190)); - // 2, 2 - FillRect(BRect(100, 100, 190, 190), g); - EndLayer(); + BPoint text(5, 60); + BRect rect(10, 10, 100, 100); + BRect gradient(110, 10, 200, 100); + rect.OffsetBy(30, 0); + gradient.OffsetBy(30, 0); + int opacity[3] = {255, 254, 100}; + for(int i = 0; i < 3; i++) { + BString s; + s << opacity[i]; + DrawString(s, 3, text); + BeginLayer(opacity[i]); + FillRect(rect); + FillRect(gradient, g[i]); + FillRoundRect(gradient.OffsetByCopy(100, 0), 10, 10, g[i]); + FillEllipse(gradient.OffsetByCopy(200, 0), g[i]); + FillArc(gradient.OffsetByCopy(300, 0), 30, 120, g[i]); + FillBezier(bezierPoints[i], g[i]); + BShape sh = shapes[i]; + MovePenTo(B_ORIGIN); + FillShape(&sh, g[i]); + BPolygon p = polygon; + p.MapTo(BRect(0, 0, 90, 90), gradient.OffsetByCopy(600, 0)); + FillPolygon(&p, g[i]); + StrokeLine(BPoint(840, i * 100 + 10), BPoint(930, i * 100 + 100), g[i]); + EndLayer(); + rect.OffsetBy(0, 100); + gradient.OffsetBy(0, 100); + text.y += 100; + } } @@ -72,7 +111,7 @@ class Window : public BWindow { Window::Window() - : BWindow(BRect(100, 100, 300, 300), "Layers-Test", + : BWindow(BRect(100, 100, 1040, 410), "Layers-Test", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS) { BView *view = new View(Bounds());