Add originX and originY support to images

Add fields for modifying origin x/y to images. Add support for images to freeze. Hopefully this is the right approach.

Editor:

https://github.com/rive-app/rive/assets/186340/838b2c69-bd25-4c55-85ab-611e423c8983

CPP:

https://github.com/rive-app/rive/assets/186340/8e7d76db-d0b5-4fdb-9ea6-a6d921fee8ef

Diffs=
fcccdeccd Add originX and originY support to images (#5624)

Co-authored-by: Philip Chung <philterdesign@gmail.com>
diff --git a/.rive_head b/.rive_head
index 28bcc96..368df58 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-5b2a52f44ebf18e44b960202844d56424b777aa3
+fcccdeccddd2b9c47992f131f9ba741bccee5da8
diff --git a/dev/defs/shapes/image.json b/dev/defs/shapes/image.json
index a56cec0..fb45809 100644
--- a/dev/defs/shapes/image.json
+++ b/dev/defs/shapes/image.json
@@ -16,6 +16,26 @@
         "string": "assetid"
       },
       "description": "Image drawable for an image asset"
+    },
+    "originX": {
+      "type": "double",
+      "initialValue": "0.5",
+      "animates": true,
+      "key": {
+        "int": 380,
+        "string": "originx"
+      },
+      "description": "Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right)."
+    },
+    "originY": {
+      "type": "double",
+      "initialValue": "0.5",
+      "animates": true,
+      "key": {
+        "int": 381,
+        "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 ee38679..cac7f25 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -825,6 +825,12 @@
             case StarBase::innerRadiusPropertyKey:
                 object->as<StarBase>()->innerRadius(value);
                 break;
+            case ImageBase::originXPropertyKey:
+                object->as<ImageBase>()->originX(value);
+                break;
+            case ImageBase::originYPropertyKey:
+                object->as<ImageBase>()->originY(value);
+                break;
             case CubicDetachedVertexBase::inRotationPropertyKey:
                 object->as<CubicDetachedVertexBase>()->inRotation(value);
                 break;
@@ -1421,6 +1427,10 @@
                 return object->as<PolygonBase>()->cornerRadius();
             case StarBase::innerRadiusPropertyKey:
                 return object->as<StarBase>()->innerRadius();
+            case ImageBase::originXPropertyKey:
+                return object->as<ImageBase>()->originX();
+            case ImageBase::originYPropertyKey:
+                return object->as<ImageBase>()->originY();
             case CubicDetachedVertexBase::inRotationPropertyKey:
                 return object->as<CubicDetachedVertexBase>()->inRotation();
             case CubicDetachedVertexBase::inDistancePropertyKey:
@@ -1768,6 +1778,8 @@
             case CubicMirroredVertexBase::distancePropertyKey:
             case PolygonBase::cornerRadiusPropertyKey:
             case StarBase::innerRadiusPropertyKey:
+            case ImageBase::originXPropertyKey:
+            case ImageBase::originYPropertyKey:
             case CubicDetachedVertexBase::inRotationPropertyKey:
             case CubicDetachedVertexBase::inDistancePropertyKey:
             case CubicDetachedVertexBase::outRotationPropertyKey:
diff --git a/include/rive/generated/shapes/image_base.hpp b/include/rive/generated/shapes/image_base.hpp
index ead835e..7a6e4a6 100644
--- a/include/rive/generated/shapes/image_base.hpp
+++ b/include/rive/generated/shapes/image_base.hpp
@@ -1,5 +1,6 @@
 #ifndef _RIVE_IMAGE_BASE_HPP_
 #define _RIVE_IMAGE_BASE_HPP_
+#include "rive/core/field_types/core_double_type.hpp"
 #include "rive/core/field_types/core_uint_type.hpp"
 #include "rive/drawable.hpp"
 namespace rive
@@ -34,9 +35,13 @@
     uint16_t coreType() const override { return typeKey; }
 
     static const uint16_t assetIdPropertyKey = 206;
+    static const uint16_t originXPropertyKey = 380;
+    static const uint16_t originYPropertyKey = 381;
 
 private:
     uint32_t m_AssetId = -1;
+    float m_OriginX = 0.5f;
+    float m_OriginY = 0.5f;
 
 public:
     inline uint32_t assetId() const { return m_AssetId; }
@@ -50,10 +55,34 @@
         assetIdChanged();
     }
 
+    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 ImageBase& object)
     {
         m_AssetId = object.m_AssetId;
+        m_OriginX = object.m_OriginX;
+        m_OriginY = object.m_OriginY;
         Drawable::copy(object);
     }
 
@@ -64,12 +93,20 @@
             case assetIdPropertyKey:
                 m_AssetId = CoreUintType::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);
     }
 
 protected:
     virtual void assetIdChanged() {}
+    virtual void originXChanged() {}
+    virtual void originYChanged() {}
 };
 } // namespace rive
 
diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp
index 9635f82..7d3c2cd 100644
--- a/src/shapes/image.cpp
+++ b/src/shapes/image.cpp
@@ -35,7 +35,7 @@
     else
     {
         renderer->transform(worldTransform());
-        renderer->translate(-width / 2.0f, -height / 2.0f);
+        renderer->translate(-width * originX(), -height * originY());
         renderer->drawImage(renderImage, blendMode(), renderOpacity());
     }
 
@@ -57,7 +57,8 @@
     }
     else
     {
-        auto mx = xform * worldTransform() * Mat2D::fromTranslate(-width * 0.5f, -height * 0.5f);
+        auto mx = xform * worldTransform() *
+                  Mat2D::fromTranslate(-width * originX(), -height * originY());
         HitTester tester(hinfo->area);
         tester.addRect(AABB(0, 0, (float)width, (float)height), mx);
         if (tester.test())