From f7a4f667a46a139dbf5bc1666bb816e14d4791e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ximo=20Casta=C3=B1eda?= Date: Tue, 1 Nov 2022 21:02:18 +0100 Subject: [PATCH] View: provide the transform between different coordinate spaces There's currently no way for an application to convert between view and drawing coordinates with a drawing states stack without keeping track of all the transformations itself, which is not very convenient for helper or library functions. Handle other spaces too, for good measure. Change-Id: Ic8404a1c111e273fff1eebf2f9f59f58246b796c Reviewed-on: https://review.haiku-os.org/c/haiku/+/5775 Tested-by: Commit checker robot Reviewed-by: waddlesplash (cherry picked from commit 241f109ccbc17646932772fb9bd5b9e7c99d5d85) Reviewed-on: https://review.haiku-os.org/c/haiku/+/5787 --- docs/user/interface/View.dox | 89 ++++++++++++++++++++++++ docs/user/interface/_interface_intro.dox | 4 +- headers/os/interface/View.h | 12 ++++ headers/private/app/ServerProtocol.h | 1 + headers/private/interface/ViewPrivate.h | 6 ++ src/kits/interface/View.cpp | 74 +++++++++++++++++++- src/servers/app/ServerWindow.cpp | 17 +++++ 7 files changed, 199 insertions(+), 4 deletions(-) diff --git a/docs/user/interface/View.dox b/docs/user/interface/View.dox index 8bce736f04..bc214d6b94 100644 --- a/docs/user/interface/View.dox +++ b/docs/user/interface/View.dox @@ -272,6 +272,74 @@ */ +// coordinate spaces + + +/*! + \typedef coordinate_space + \brief A coordinate or drawing space. + \see \ref coordinatespaces + + \since Haiku R1 +*/ + + +/*! + \var B_CURRENT_STATE_COORDINATES + \brief The current drawing space. + + \since Haiku R1 +*/ + + +/*! + \var B_PREVIOUS_STATE_COORDINATES + \brief The drawing space of the parent state in the stack. + + \since Haiku R1 +*/ + + +/*! + \var B_VIEW_COORDINATES + \brief The base coordinate space of the view. + + \since Haiku R1 +*/ + + +/*! + \var B_PARENT_VIEW_DRAW_COORDINATES + \brief The current drawing space of the parent view. + + \since Haiku R1 +*/ + + +/*! + \var B_PARENT_VIEW_COORDINATES + \brief The base coordinate space of the parent view. + + \since Haiku R1 +*/ + + +/*! + \var B_WINDOW_COORDINATES + \brief The coordinate space of the owning window. + + \since Haiku R1 +*/ + + +/*! + \var B_SCREEN_COORDINATES + \brief The coordinate space of the screen. + + \since Haiku R1 +*/ + + // view flags @@ -1903,6 +1971,27 @@ SetViewColor(Parent()->ViewColor()); */ +/*! + \fn BAffineTransform BView::TransformTo(coordinate_space basis) const + \brief Return the BAffineTransform to convert from the current drawing + space to \a basis. + + \c B_PREVIOUS_STATE_COORDINATES is equivalent to + \c B_CURRENT_STATE_COORDINATES when there is no parent state. + + \c B_PARENT_VIEW_DRAW_COORDINATES and \c B_PARENT_VIEW_COORDINATES are + equivalent to \c B_WINDOW_COORDINATES when used on a root view. + + \c B_WINDOW_COORDINATES works as \c B_SCREEN_COORDINATES for unattached + views. + + \sa Transform() + \sa \ref coordinatespaces + + \since Haiku R1 +*/ + + /*! \fn void BView::TranslateBy(double x, double y) \brief Translate the current view by coordinates. diff --git a/docs/user/interface/_interface_intro.dox b/docs/user/interface/_interface_intro.dox index e2ef3ada83..9f195c4ed0 100644 --- a/docs/user/interface/_interface_intro.dox +++ b/docs/user/interface/_interface_intro.dox @@ -66,7 +66,5 @@ All drawing operations in a BView use coordinates specified in the drawing space. However, the update rect passed to the Draw method (or passed to the Invalidate method) is in the BView base coordinate space. Conversion between the two can - be done using the affine transform returned by BView::Transform, or in case the - legacy transformation functions are used, by applying the scale and origin returned - by the Scale() and Origin() functions. + be done using the affine transform returned by BView::TransformTo. */ diff --git a/headers/os/interface/View.h b/headers/os/interface/View.h index 70827764d2..e8859c6ff9 100644 --- a/headers/os/interface/View.h +++ b/headers/os/interface/View.h @@ -70,6 +70,16 @@ enum { B_FONT_ALL = 0x000001FF }; +typedef enum { + B_CURRENT_STATE_COORDINATES, + B_PREVIOUS_STATE_COORDINATES, + B_VIEW_COORDINATES, + B_PARENT_VIEW_DRAW_COORDINATES, + B_PARENT_VIEW_COORDINATES, + B_WINDOW_COORDINATES, + B_SCREEN_COORDINATES +} coordinate_space; + // view flags const uint32 B_FULL_UPDATE_ON_RESIZE = 0x80000000UL; /* 31 */ const uint32 _B_RESERVED1_ = 0x40000000UL; /* 30 */ @@ -324,6 +334,8 @@ public: void ScaleBy(double x, double y); void RotateBy(double angleRadians); + BAffineTransform TransformTo(coordinate_space basis) const; + void PushState(); void PopState(); diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index 2c74242512..f76af07ada 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -358,6 +358,7 @@ enum { // transformation in addition to origin/scale AS_VIEW_SET_TRANSFORM, AS_VIEW_GET_TRANSFORM, + AS_VIEW_GET_PARENT_COMPOSITE, AS_VIEW_AFFINE_TRANSLATE, AS_VIEW_AFFINE_SCALE, diff --git a/headers/private/interface/ViewPrivate.h b/headers/private/interface/ViewPrivate.h index 676454a03a..5a27b27876 100644 --- a/headers/private/interface/ViewPrivate.h +++ b/headers/private/interface/ViewPrivate.h @@ -45,6 +45,7 @@ enum { B_VIEW_WHICH_VIEW_COLOR_BIT = 0x00100000, B_VIEW_WHICH_LOW_COLOR_BIT = 0x00200000, B_VIEW_WHICH_HIGH_COLOR_BIT = 0x00400000, + B_VIEW_PARENT_COMPOSITE_BIT = 0x00800000, B_VIEW_ALL_BITS = 0x00ffffff, @@ -131,6 +132,11 @@ class ViewState { float scale; BAffineTransform transform; + // composite transformation stack + BPoint parent_composite_origin; + float parent_composite_scale; + BAffineTransform parent_composite_transform; + // line modes join_mode line_join; cap_mode line_cap; diff --git a/src/kits/interface/View.cpp b/src/kits/interface/View.cpp index 74821b4029..4e31fbe78d 100644 --- a/src/kits/interface/View.cpp +++ b/src/kits/interface/View.cpp @@ -170,6 +170,10 @@ ViewState::ViewState() font_flags = font.Flags(); font_aliasing = false; + parent_composite_transform.Reset(); + parent_composite_scale = 1.0f; + parent_composite_origin.Set(0, 0); + // We only keep the B_VIEW_CLIP_REGION_BIT flag invalidated, // because we should get the clipping region from app_server. // The other flags do not need to be included because the data they @@ -340,7 +344,8 @@ ViewState::UpdateFrom(BPrivate::PortLink &link) clipping_region_used = false; } - valid_flags = ~B_VIEW_CLIP_REGION_BIT; + valid_flags = ~(B_VIEW_CLIP_REGION_BIT | B_VIEW_PARENT_COMPOSITE_BIT) + | (valid_flags & B_VIEW_PARENT_COMPOSITE_BIT); } } // namespace BPrivate @@ -1837,6 +1842,8 @@ BView::PushState() fOwner->fLink->StartMessage(AS_VIEW_PUSH_STATE); + fState->valid_flags &= ~B_VIEW_PARENT_COMPOSITE_BIT; + // initialize origin, scale and transform, new states start "clean". fState->valid_flags |= B_VIEW_SCALE_BIT | B_VIEW_ORIGIN_BIT | B_VIEW_TRANSFORM_BIT; @@ -1989,6 +1996,71 @@ BView::Transform() const } +BAffineTransform +BView::TransformTo(coordinate_space basis) const +{ + if (basis == B_CURRENT_STATE_COORDINATES) + return B_AFFINE_IDENTITY_TRANSFORM; + + if (!fState->IsValid(B_VIEW_PARENT_COMPOSITE_BIT) && fOwner != NULL) { + _CheckLockAndSwitchCurrent(); + + fOwner->fLink->StartMessage(AS_VIEW_GET_PARENT_COMPOSITE); + + int32 code; + if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) { + fOwner->fLink->Read(&fState->parent_composite_transform); + fOwner->fLink->Read(&fState->parent_composite_scale); + fOwner->fLink->Read(&fState->parent_composite_origin); + } + + fState->valid_flags |= B_VIEW_PARENT_COMPOSITE_BIT; + } + + BAffineTransform transform = fState->parent_composite_transform * Transform(); + float scale = fState->parent_composite_scale * Scale(); + transform.PreScaleBy(scale, scale); + BPoint origin = Origin(); + origin.x *= fState->parent_composite_scale; + origin.y *= fState->parent_composite_scale; + origin += fState->parent_composite_origin; + transform.TranslateBy(origin); + + if (basis == B_PREVIOUS_STATE_COORDINATES) { + transform.TranslateBy(-fState->parent_composite_origin); + transform.PreMultiplyInverse(fState->parent_composite_transform); + transform.ScaleBy(1.0f / fState->parent_composite_scale); + return transform; + } + + if (basis == B_VIEW_COORDINATES) + return transform; + + origin = B_ORIGIN; + + if (basis == B_PARENT_VIEW_COORDINATES || basis == B_PARENT_VIEW_DRAW_COORDINATES) { + BView* parent = Parent(); + if (parent != NULL) { + ConvertToParent(&origin); + transform.TranslateBy(origin); + if (basis == B_PARENT_VIEW_DRAW_COORDINATES) + transform = transform.PreMultiplyInverse(parent->TransformTo(B_VIEW_COORDINATES)); + return transform; + } + basis = B_WINDOW_COORDINATES; + } + + ConvertToScreen(&origin); + if (basis == B_WINDOW_COORDINATES) { + BWindow* window = Window(); + if (window != NULL) + origin -= window->Frame().LeftTop(); + } + transform.TranslateBy(origin); + return transform; +} + + void BView::TranslateBy(double x, double y) { diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index f512e8a239..ffe2e03125 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -1640,6 +1640,23 @@ fDesktop->LockSingleWindow(); fLink.Flush(); break; } + case AS_VIEW_GET_PARENT_COMPOSITE: + { + DrawState* state = fCurrentView->CurrentState()->PreviousState(); + + fLink.StartMessage(B_OK); + if (state != NULL) { + fLink.Attach(state->CombinedTransform()); + fLink.Attach(state->CombinedScale()); + fLink.Attach(state->CombinedOrigin()); + } else { + fLink.Attach(BAffineTransform()); + fLink.Attach(1.0f); + fLink.Attach(B_ORIGIN); + } + fLink.Flush(); + break; + } case AS_VIEW_AFFINE_TRANSLATE: { double x, y;