Adding origin to text.

Adds origin to text object that works just like the origin of a parametric path. It can be moved in freeze mode too. Adds support in the editor, Flutter runtime, and C++ runtime.

Diffs=
9b8dacbac Adding origin to text. (#5533)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head
index 45184f9..fe43500 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-797fb4cbd547509dac0a34e1039bfad5de8cd80c
+9b8dacbacbfb232303773dd7a6008fdffaecc29c
diff --git a/dev/defs/text/text.json b/dev/defs/text/text.json
index 01868a8..faaa9f7 100644
--- a/dev/defs/text/text.json
+++ b/dev/defs/text/text.json
@@ -50,6 +50,26 @@
         "string": "height"
       },
       "description": "Height of the text object."
+    },
+    "originX": {
+      "type": "double",
+      "initialValue": "0.0",
+      "animates": true,
+      "key": {
+        "int": 363,
+        "string": "originx"
+      },
+      "description": "Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right)."
+    },
+    "originY": {
+      "type": "double",
+      "initialValue": "0.0",
+      "animates": true,
+      "key": {
+        "int": 364,
+        "string": "originy"
+      },
+      "description": "Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom)."
     }
   }
-}
+}
\ No newline at end of file
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index 7698ca9..dd2a0a3 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -868,6 +868,12 @@
             case TextBase::heightPropertyKey:
                 object->as<TextBase>()->height(value);
                 break;
+            case TextBase::originXPropertyKey:
+                object->as<TextBase>()->originX(value);
+                break;
+            case TextBase::originYPropertyKey:
+                object->as<TextBase>()->originY(value);
+                break;
             case DrawableAssetBase::heightPropertyKey:
                 object->as<DrawableAssetBase>()->height(value);
                 break;
@@ -1326,6 +1332,10 @@
                 return object->as<TextBase>()->width();
             case TextBase::heightPropertyKey:
                 return object->as<TextBase>()->height();
+            case TextBase::originXPropertyKey:
+                return object->as<TextBase>()->originX();
+            case TextBase::originYPropertyKey:
+                return object->as<TextBase>()->originY();
             case DrawableAssetBase::heightPropertyKey:
                 return object->as<DrawableAssetBase>()->height();
             case DrawableAssetBase::widthPropertyKey:
@@ -1574,6 +1584,8 @@
             case TextStyleAxisBase::axisValuePropertyKey:
             case TextBase::widthPropertyKey:
             case TextBase::heightPropertyKey:
+            case TextBase::originXPropertyKey:
+            case TextBase::originYPropertyKey:
             case DrawableAssetBase::heightPropertyKey:
             case DrawableAssetBase::widthPropertyKey:
                 return CoreDoubleType::id;
diff --git a/include/rive/generated/text/text_base.hpp b/include/rive/generated/text/text_base.hpp
index 3d5bcf0..8665d52 100644
--- a/include/rive/generated/text/text_base.hpp
+++ b/include/rive/generated/text/text_base.hpp
@@ -39,6 +39,8 @@
     static const uint16_t overflowValuePropertyKey = 287;
     static const uint16_t widthPropertyKey = 285;
     static const uint16_t heightPropertyKey = 286;
+    static const uint16_t originXPropertyKey = 363;
+    static const uint16_t originYPropertyKey = 364;
 
 private:
     uint32_t m_AlignValue = 0;
@@ -46,6 +48,8 @@
     uint32_t m_OverflowValue = 0;
     float m_Width = 0.0f;
     float m_Height = 0.0f;
+    float m_OriginX = 0.0f;
+    float m_OriginY = 0.0f;
 
 public:
     inline uint32_t alignValue() const { return m_AlignValue; }
@@ -103,6 +107,28 @@
         heightChanged();
     }
 
+    inline float originX() const { return m_OriginX; }
+    void originX(float value)
+    {
+        if (m_OriginX == value)
+        {
+            return;
+        }
+        m_OriginX = value;
+        originXChanged();
+    }
+
+    inline float originY() const { return m_OriginY; }
+    void originY(float value)
+    {
+        if (m_OriginY == value)
+        {
+            return;
+        }
+        m_OriginY = value;
+        originYChanged();
+    }
+
     Core* clone() const override;
     void copy(const TextBase& object)
     {
@@ -111,6 +137,8 @@
         m_OverflowValue = object.m_OverflowValue;
         m_Width = object.m_Width;
         m_Height = object.m_Height;
+        m_OriginX = object.m_OriginX;
+        m_OriginY = object.m_OriginY;
         Drawable::copy(object);
     }
 
@@ -133,6 +161,12 @@
             case heightPropertyKey:
                 m_Height = CoreDoubleType::deserialize(reader);
                 return true;
+            case originXPropertyKey:
+                m_OriginX = CoreDoubleType::deserialize(reader);
+                return true;
+            case originYPropertyKey:
+                m_OriginY = CoreDoubleType::deserialize(reader);
+                return true;
         }
         return Drawable::deserialize(propertyKey, reader);
     }
@@ -143,6 +177,8 @@
     virtual void overflowValueChanged() {}
     virtual void widthChanged() {}
     virtual void heightChanged() {}
+    virtual void originXChanged() {}
+    virtual void originYChanged() {}
 };
 } // namespace rive
 
diff --git a/include/rive/text/text.hpp b/include/rive/text/text.hpp
index bc1fa17..d10b8aa 100644
--- a/include/rive/text/text.hpp
+++ b/include/rive/text/text.hpp
@@ -155,6 +155,7 @@
 
 private:
 #ifdef WITH_RIVE_TEXT
+    void updateOriginWorldTransform();
     std::vector<TextValueRun*> m_runs;
     std::vector<TextStyle*> m_renderStyles;
     SimpleArray<Paragraph> m_shape;
@@ -163,6 +164,9 @@
     std::vector<OrderedLine> m_orderedLines;
     GlyphRun m_ellipsisRun;
     std::unique_ptr<RenderPath> m_clipRenderPath;
+    Mat2D m_originWorldTransform;
+    float m_actualWidth = 0.0f;
+    float m_actualHeight = 0.0f;
 #endif
 };
 } // namespace rive
diff --git a/src/text/text.cpp b/src/text/text.cpp
index d70cdf9..4589ccf 100644
--- a/src/text/text.cpp
+++ b/src/text/text.cpp
@@ -259,6 +259,7 @@
     int paragraphIndex = 0;
     int lineIndex = 0;
     float y = 0.0f;
+    float maxX = 0.0f;
 
     int ellipsisLine = -1;
     bool isEllipsisLineLast = false;
@@ -360,6 +361,10 @@
                     style->propagateOpacity(renderOpacity());
                 }
             }
+            if (x > maxX)
+            {
+                maxX = x;
+            }
             if (lineIndex == ellipsisLine)
             {
                 return;
@@ -372,6 +377,27 @@
         }
         y += paragraphSpacing;
     }
+    switch (sizing())
+    {
+        case TextSizing::autoWidth:
+            m_actualWidth = maxX;
+            m_actualHeight = std::max(0.0f, y - paragraphSpacing);
+            break;
+        case TextSizing::autoHeight:
+            m_actualWidth = width();
+            m_actualHeight = std::max(0.0f, y - paragraphSpacing);
+            break;
+        case TextSizing::fixed:
+            m_actualWidth = width();
+            m_actualHeight = height();
+            break;
+    }
+}
+
+void Text::updateOriginWorldTransform()
+{
+    m_originWorldTransform = m_WorldTransform * Mat2D::fromTranslate(-m_actualWidth * originX(),
+                                                                     -m_actualHeight * originY());
 }
 
 void Text::draw(Renderer* renderer)
@@ -382,7 +408,7 @@
         // transformations.
         renderer->save();
     }
-    renderer->transform(worldTransform());
+    renderer->transform(m_originWorldTransform);
     if (overflow() == TextOverflow::clipped && m_clipRenderPath)
     {
         renderer->clipPath(m_clipRenderPath.get());
@@ -474,6 +500,11 @@
 {
     Super::update(value);
 
+    if (hasDirt(value, ComponentDirt::WorldTransform))
+    {
+        updateOriginWorldTransform();
+    }
+
     if (hasDirt(value, ComponentDirt::Path))
     {
         std::vector<Unichar> unichars;