From ad32c7d81f2a98d1fabb8f2518756bc6d2167208 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 17 Dec 2025 00:52:50 +0800 Subject: [PATCH 01/30] Update RBPath callbacks --- Sources/OpenRenderBox/Path/ORBPath.cpp | 137 +++++++++++++++++- .../include/OpenRenderBox/ORBPath.h | 64 +++++++- 2 files changed, 198 insertions(+), 3 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index c7c0542..12868bd 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -8,9 +8,142 @@ #include void ORBPathRetain(ORBPath path) { - // TODO + ORBPathRetainCallback retain = path.callbacks->retain; + if (retain != nullptr) { + retain(path); + } } void ORBPathRelease(ORBPath path) { - // TODO + ORBPathReleaseCallback release = path.callbacks->release; + if (release != nullptr) { + release(path); + } } + +#if ORB_TARGET_OS_DARWIN + +#include +#include + +namespace { + +void cgpath_retain(ORBPath path) { + CFRetain(path.storage); +} + +void cgpath_release(ORBPath path) { + CFRelease(path.storage); +} + +bool cgpath_apply(const void *storage, void *info, ORBPathApplyCallback callback) { + CGPathRef cgPath = static_cast(storage); + + __block bool shouldStop = false; + CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { + if (shouldStop) return; + + ORBPathElement orbElement; + double pointBuffer[6]; + + switch (element->type) { + case kCGPathElementMoveToPoint: + orbElement.type = ORBPathElementMoveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementAddLineToPoint: + orbElement.type = ORBPathElementAddLineToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementAddQuadCurveToPoint: + orbElement.type = ORBPathElementAddQuadCurveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + pointBuffer[2] = element->points[1].x; + pointBuffer[3] = element->points[1].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementAddCurveToPoint: + orbElement.type = ORBPathElementAddCurveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + pointBuffer[2] = element->points[1].x; + pointBuffer[3] = element->points[1].y; + pointBuffer[4] = element->points[2].x; + pointBuffer[5] = element->points[2].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementCloseSubpath: + orbElement.type = ORBPathElementCloseSubpath; + orbElement.points = nullptr; + break; + } + + if (callback != nullptr) { + shouldStop = callback(info, orbElement, nullptr); + } + }); + + return !shouldStop; +} + +bool cgpath_isEqual(const void *storage, const void *otherStorage) { + return CGPathEqualToPath(static_cast(storage), static_cast(otherStorage)); +} + +bool cgpath_isEmpty(const void *storage) { + return CGPathIsEmpty(static_cast(storage)); +} + +bool cgpath_isSingleRect(const void *storage) { + // CGPath doesn't have a direct isSingleRect check, return false + return false; +} + +uint32_t cgpath_bezierOrder(const void *storage) { + CGPathRef cgPath = static_cast(storage); + + // Enumerate path elements to determine bezier order + // 1 = linear, 2 = quadratic, 3 = cubic + __block uint32_t order = 1; + CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { + uint32_t *orderPtr = static_cast(info); + if (element->type == kCGPathElementAddCurveToPoint) { + *orderPtr = 3; + } else if (element->type == kCGPathElementAddQuadCurveToPoint && *orderPtr < 3) { + *orderPtr = 2; + } + }); + + return order; +} + +CGRect cgpath_boundingBox(const void *storage) { + return CGPathGetPathBoundingBox(static_cast(storage)); +} + +CGPathRef cgpath_cgPath(const void *storage) { + // Return the CGPath itself + return static_cast(storage); +} + +} // anonymous namespace + +const ORBPathCallbacks ORBPathCGPathCallbacks = { + nullptr, // reserved + cgpath_retain, // retain + cgpath_release, // release + cgpath_apply, // apply + cgpath_isEqual, // isEqual + cgpath_isEmpty, // isEmpty + cgpath_isSingleRect, // isSingleRect + cgpath_bezierOrder, // bezierOrder + cgpath_boundingBox, // boundingBox + cgpath_cgPath, // cgPath +}; + +#endif // ORB_TARGET_OS_DARWIN diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 04c9a56..9b413f9 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -6,6 +6,10 @@ #include +#if ORB_TARGET_OS_DARWIN +#include +#endif + ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN @@ -13,12 +17,70 @@ ORB_EXTERN_C_BEGIN typedef struct ORB_BRIDGED_TYPE(id) ORBPathStorage * ORBPathStorageRef ORB_SWIFT_NAME(ORBPath.Storage); struct ORBPathStorage; +struct ORBPath; + +/// Path element type for path enumeration +typedef ORB_ENUM(int32_t, ORBPathElementType) { + ORBPathElementMoveToPoint = 0, + ORBPathElementAddLineToPoint = 1, + ORBPathElementAddQuadCurveToPoint = 2, + ORBPathElementAddCurveToPoint = 3, + ORBPathElementCloseSubpath = 4, +}; + +/// An element of a path returned by path enumeration +struct ORBPathElement { + ORBPathElementType type; + const double * _Nullable points; +}; +typedef struct ORBPathElement ORBPathElement; + +/// Callback type for path element enumeration +/// Returns true to stop enumeration, false to continue +typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const void * _Nullable userInfo); + +/// Callback function pointer types for ORBPathCallbacks +typedef void (* _Nullable ORBPathRetainCallback)(struct ORBPath path); +typedef void (* _Nullable ORBPathReleaseCallback)(struct ORBPath path); +typedef bool (* _Nullable ORBPathApplyFunction)(const void *storage, void * _Nullable info, ORBPathApplyCallback _Nullable callback); +typedef bool (* _Nullable ORBPathIsEqualCallback)(const void *storage, const void *otherStorage); +typedef bool (* _Nullable ORBPathIsEmptyCallback)(const void *storage); +typedef bool (* _Nullable ORBPathIsSingleRectCallback)(const void *storage); +typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(const void *storage); +#if ORB_TARGET_OS_DARWIN +typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(const void *storage); +typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(const void *storage); +#else +typedef void (* _Nullable ORBPathBoundingBoxCallback)(const void *storage); +typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(const void *storage); +#endif + +/// Callbacks structure for path operations +/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations +typedef struct ORBPathCallbacks { + void * _Nullable reserved; // 0x00: Reserved for future use + ORBPathRetainCallback retain; // 0x08: Retain callback + ORBPathReleaseCallback release; // 0x10: Release callback + ORBPathApplyFunction apply; // 0x18: Enumerate path elements + ORBPathIsEqualCallback isEqual; // 0x20: Compare two paths + ORBPathIsEmptyCallback isEmpty; // 0x28: Check if path is empty + ORBPathIsSingleRectCallback isSingleRect; // 0x30: Check if path is a single rectangle + ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) + ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box + ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation +} ORBPathCallbacks; typedef struct ORBPath { ORBPathStorageRef storage; - void *callbacks; + const ORBPathCallbacks * _Nullable callbacks; } ORBPath; +#if ORB_TARGET_OS_DARWIN +/// Global callbacks for CGPath-backed paths +ORB_EXPORT +const ORBPathCallbacks ORBPathCGPathCallbacks; +#endif + ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathRetain(ORBPath path) ORB_SWIFT_NAME(ORBPath.retain(self:)); From 0b205a8b1419fb552270bc8e33fab2c2b8a78103 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 17 Dec 2025 01:01:39 +0800 Subject: [PATCH 02/30] Update callbacks --- Sources/OpenRenderBox/Path/ORBPath.cpp | 206 +++++++++++-------------- 1 file changed, 89 insertions(+), 117 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 12868bd..05766e0 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -26,124 +26,96 @@ void ORBPathRelease(ORBPath path) { #include #include -namespace { - -void cgpath_retain(ORBPath path) { - CFRetain(path.storage); -} - -void cgpath_release(ORBPath path) { - CFRelease(path.storage); -} - -bool cgpath_apply(const void *storage, void *info, ORBPathApplyCallback callback) { - CGPathRef cgPath = static_cast(storage); - - __block bool shouldStop = false; - CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { - if (shouldStop) return; - - ORBPathElement orbElement; - double pointBuffer[6]; - - switch (element->type) { - case kCGPathElementMoveToPoint: - orbElement.type = ORBPathElementMoveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - orbElement.points = pointBuffer; - break; - case kCGPathElementAddLineToPoint: - orbElement.type = ORBPathElementAddLineToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - orbElement.points = pointBuffer; - break; - case kCGPathElementAddQuadCurveToPoint: - orbElement.type = ORBPathElementAddQuadCurveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - pointBuffer[2] = element->points[1].x; - pointBuffer[3] = element->points[1].y; - orbElement.points = pointBuffer; - break; - case kCGPathElementAddCurveToPoint: - orbElement.type = ORBPathElementAddCurveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - pointBuffer[2] = element->points[1].x; - pointBuffer[3] = element->points[1].y; - pointBuffer[4] = element->points[2].x; - pointBuffer[5] = element->points[2].y; - orbElement.points = pointBuffer; - break; - case kCGPathElementCloseSubpath: - orbElement.type = ORBPathElementCloseSubpath; - orbElement.points = nullptr; - break; - } - - if (callback != nullptr) { - shouldStop = callback(info, orbElement, nullptr); - } - }); - - return !shouldStop; -} - -bool cgpath_isEqual(const void *storage, const void *otherStorage) { - return CGPathEqualToPath(static_cast(storage), static_cast(otherStorage)); -} - -bool cgpath_isEmpty(const void *storage) { - return CGPathIsEmpty(static_cast(storage)); -} - -bool cgpath_isSingleRect(const void *storage) { - // CGPath doesn't have a direct isSingleRect check, return false - return false; -} - -uint32_t cgpath_bezierOrder(const void *storage) { - CGPathRef cgPath = static_cast(storage); - - // Enumerate path elements to determine bezier order - // 1 = linear, 2 = quadratic, 3 = cubic - __block uint32_t order = 1; - CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { - uint32_t *orderPtr = static_cast(info); - if (element->type == kCGPathElementAddCurveToPoint) { - *orderPtr = 3; - } else if (element->type == kCGPathElementAddQuadCurveToPoint && *orderPtr < 3) { - *orderPtr = 2; - } - }); - - return order; -} - -CGRect cgpath_boundingBox(const void *storage) { - return CGPathGetPathBoundingBox(static_cast(storage)); -} - -CGPathRef cgpath_cgPath(const void *storage) { - // Return the CGPath itself - return static_cast(storage); -} - -} // anonymous namespace - const ORBPathCallbacks ORBPathCGPathCallbacks = { - nullptr, // reserved - cgpath_retain, // retain - cgpath_release, // release - cgpath_apply, // apply - cgpath_isEqual, // isEqual - cgpath_isEmpty, // isEmpty - cgpath_isSingleRect, // isSingleRect - cgpath_bezierOrder, // bezierOrder - cgpath_boundingBox, // boundingBox - cgpath_cgPath, // cgPath + nullptr, + // retain - use CFRetain directly + reinterpret_cast(CFRetain), + // release - use CFRelease directly + reinterpret_cast(CFRelease), + // apply + +[](const void *storage, void *info, ORBPathApplyCallback callback) -> bool { + CGPathRef cgPath = static_cast(storage); + __block bool shouldStop = false; + CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { + if (shouldStop) return; + ORBPathElement orbElement; + double pointBuffer[6]; + switch (element->type) { + case kCGPathElementMoveToPoint: + orbElement.type = ORBPathElementMoveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementAddLineToPoint: + orbElement.type = ORBPathElementAddLineToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementAddQuadCurveToPoint: + orbElement.type = ORBPathElementAddQuadCurveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + pointBuffer[2] = element->points[1].x; + pointBuffer[3] = element->points[1].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementAddCurveToPoint: + orbElement.type = ORBPathElementAddCurveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + pointBuffer[2] = element->points[1].x; + pointBuffer[3] = element->points[1].y; + pointBuffer[4] = element->points[2].x; + pointBuffer[5] = element->points[2].y; + orbElement.points = pointBuffer; + break; + case kCGPathElementCloseSubpath: + orbElement.type = ORBPathElementCloseSubpath; + orbElement.points = nullptr; + break; + } + if (callback != nullptr) { + shouldStop = callback(info, orbElement, nullptr); + } + }); + return !shouldStop; + }, + // isEqual + +[](const void *storage, const void *otherStorage) -> bool { + return CGPathEqualToPath(static_cast(storage), static_cast(otherStorage)); + }, + // isEmpty + +[](const void *storage) -> bool { + return CGPathIsEmpty(static_cast(storage)); + }, + // isSingleRect - CGPath doesn't have this, always return false + +[](const void *storage) -> bool { + return false; + }, + // bezierOrder + +[](const void *storage) -> uint32_t { + CGPathRef cgPath = static_cast(storage); + __block uint32_t order = 1; + CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { + uint32_t *orderPtr = static_cast(info); + if (element->type == kCGPathElementAddCurveToPoint) { + *orderPtr = 3; + } else if (element->type == kCGPathElementAddQuadCurveToPoint && *orderPtr < 3) { + *orderPtr = 2; + } + }); + return order; + }, + // boundingBox + +[](const void *storage) -> CGRect { + return CGPathGetPathBoundingBox(static_cast(storage)); + }, + // cgPath - return the CGPath itself + +[](const void *storage) -> CGPathRef { + return static_cast(storage); + }, }; #endif // ORB_TARGET_OS_DARWIN From 3d6f76aea83a6e3970eac084f7415beef3eb5c54 Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 17 Dec 2025 01:11:03 +0800 Subject: [PATCH 03/30] Update callbacks --- Sources/OpenRenderBox/Path/ORBPath.cpp | 27 ++++++++++--------- .../include/OpenRenderBox/ORBPath.h | 18 ++++++------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 05766e0..00a2702 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -26,6 +26,7 @@ void ORBPathRelease(ORBPath path) { #include #include +// FIXME: Not implemented correctly const ORBPathCallbacks ORBPathCGPathCallbacks = { nullptr, // retain - use CFRetain directly @@ -33,8 +34,8 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { // release - use CFRelease directly reinterpret_cast(CFRelease), // apply - +[](const void *storage, void *info, ORBPathApplyCallback callback) -> bool { - CGPathRef cgPath = static_cast(storage); + +[](ORBPathStorageRef storage, void *info, ORBPathApplyCallback callback) -> bool { + CGPathRef cgPath = reinterpret_cast(storage); __block bool shouldStop = false; CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { if (shouldStop) return; @@ -83,20 +84,20 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { return !shouldStop; }, // isEqual - +[](const void *storage, const void *otherStorage) -> bool { - return CGPathEqualToPath(static_cast(storage), static_cast(otherStorage)); + +[](ORBPathStorageRef storage, ORBPathStorageRef otherStorage) -> bool { + return CGPathEqualToPath(reinterpret_cast(storage), reinterpret_cast(otherStorage)); }, // isEmpty - +[](const void *storage) -> bool { - return CGPathIsEmpty(static_cast(storage)); + +[](ORBPathStorageRef storage) -> bool { + return CGPathIsEmpty(reinterpret_cast(storage)); }, // isSingleRect - CGPath doesn't have this, always return false - +[](const void *storage) -> bool { + +[](ORBPathStorageRef storage) -> bool { return false; }, // bezierOrder - +[](const void *storage) -> uint32_t { - CGPathRef cgPath = static_cast(storage); + +[](ORBPathStorageRef storage) -> uint32_t { + CGPathRef cgPath = reinterpret_cast(storage); __block uint32_t order = 1; CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { uint32_t *orderPtr = static_cast(info); @@ -109,12 +110,12 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { return order; }, // boundingBox - +[](const void *storage) -> CGRect { - return CGPathGetPathBoundingBox(static_cast(storage)); + +[](ORBPathStorageRef storage) -> CGRect { + return CGPathGetPathBoundingBox(reinterpret_cast(storage)); }, // cgPath - return the CGPath itself - +[](const void *storage) -> CGPathRef { - return static_cast(storage); + +[](ORBPathStorageRef storage) -> CGPathRef { + return reinterpret_cast(storage); }, }; diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 9b413f9..0dec057 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -42,17 +42,17 @@ typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement eleme /// Callback function pointer types for ORBPathCallbacks typedef void (* _Nullable ORBPathRetainCallback)(struct ORBPath path); typedef void (* _Nullable ORBPathReleaseCallback)(struct ORBPath path); -typedef bool (* _Nullable ORBPathApplyFunction)(const void *storage, void * _Nullable info, ORBPathApplyCallback _Nullable callback); -typedef bool (* _Nullable ORBPathIsEqualCallback)(const void *storage, const void *otherStorage); -typedef bool (* _Nullable ORBPathIsEmptyCallback)(const void *storage); -typedef bool (* _Nullable ORBPathIsSingleRectCallback)(const void *storage); -typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(const void *storage); +typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathStorageRef storage, void * _Nullable info, ORBPathApplyCallback _Nullable callback); +typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathStorageRef storage, ORBPathStorageRef otherStorage); +typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathStorageRef storage); +typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathStorageRef storage); +typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathStorageRef storage); #if ORB_TARGET_OS_DARWIN -typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(const void *storage); -typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(const void *storage); +typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathStorageRef storage); +typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathStorageRef storage); #else -typedef void (* _Nullable ORBPathBoundingBoxCallback)(const void *storage); -typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(const void *storage); +typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathStorageRef storage); +typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathStorageRef storage); #endif /// Callbacks structure for path operations From dcda94e30ff20902b785816658df997e2fef43aa Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 13:03:19 +0800 Subject: [PATCH 04/30] Add ORBPath header --- Sources/OpenRenderBox/Path/ORBPath.cpp | 42 ++++++++++--------- .../include/OpenRenderBox/ORBPath.h | 25 +++++------ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 00a2702..0d79e7b 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -10,14 +10,14 @@ void ORBPathRetain(ORBPath path) { ORBPathRetainCallback retain = path.callbacks->retain; if (retain != nullptr) { - retain(path); + retain(&path); } } void ORBPathRelease(ORBPath path) { ORBPathReleaseCallback release = path.callbacks->release; if (release != nullptr) { - release(path); + release(&path); } } @@ -29,13 +29,17 @@ void ORBPathRelease(ORBPath path) { // FIXME: Not implemented correctly const ORBPathCallbacks ORBPathCGPathCallbacks = { nullptr, - // retain - use CFRetain directly - reinterpret_cast(CFRetain), - // release - use CFRelease directly - reinterpret_cast(CFRelease), + // retain + +[](ORBPathRef path) -> void { + CFRetain(path->storage); + }, + // release + +[](ORBPathRef path) -> void { + CFRelease(path->storage); + }, // apply - +[](ORBPathStorageRef storage, void *info, ORBPathApplyCallback callback) -> bool { - CGPathRef cgPath = reinterpret_cast(storage); + +[](ORBPathRef path, void *info, ORBPathApplyCallback callback) -> bool { + CGPathRef cgPath = reinterpret_cast(path->storage); __block bool shouldStop = false; CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { if (shouldStop) return; @@ -84,20 +88,20 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { return !shouldStop; }, // isEqual - +[](ORBPathStorageRef storage, ORBPathStorageRef otherStorage) -> bool { - return CGPathEqualToPath(reinterpret_cast(storage), reinterpret_cast(otherStorage)); + +[](ORBPathRef path, ORBPathRef otherPath) -> bool { + return CGPathEqualToPath(reinterpret_cast(path->storage), reinterpret_cast(otherPath->storage)); }, // isEmpty - +[](ORBPathStorageRef storage) -> bool { - return CGPathIsEmpty(reinterpret_cast(storage)); + +[](ORBPathRef path) -> bool { + return CGPathIsEmpty(reinterpret_cast(path->storage)); }, // isSingleRect - CGPath doesn't have this, always return false - +[](ORBPathStorageRef storage) -> bool { + +[](ORBPathRef path) -> bool { return false; }, // bezierOrder - +[](ORBPathStorageRef storage) -> uint32_t { - CGPathRef cgPath = reinterpret_cast(storage); + +[](ORBPathRef path) -> uint32_t { + CGPathRef cgPath = reinterpret_cast(path->storage); __block uint32_t order = 1; CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { uint32_t *orderPtr = static_cast(info); @@ -110,12 +114,12 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { return order; }, // boundingBox - +[](ORBPathStorageRef storage) -> CGRect { - return CGPathGetPathBoundingBox(reinterpret_cast(storage)); + +[](ORBPathRef path) -> CGRect { + return CGPathGetPathBoundingBox(reinterpret_cast(path->storage)); }, // cgPath - return the CGPath itself - +[](ORBPathStorageRef storage) -> CGPathRef { - return reinterpret_cast(storage); + +[](ORBPathRef path) -> CGPathRef { + return reinterpret_cast(path->storage); }, }; diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 0dec057..ca08653 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -14,10 +14,11 @@ ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN +typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef ORB_SWIFT_NAME(ORBPath); typedef struct ORB_BRIDGED_TYPE(id) ORBPathStorage * ORBPathStorageRef ORB_SWIFT_NAME(ORBPath.Storage); -struct ORBPathStorage; struct ORBPath; +struct ORBPathStorage; /// Path element type for path enumeration typedef ORB_ENUM(int32_t, ORBPathElementType) { @@ -40,19 +41,19 @@ typedef struct ORBPathElement ORBPathElement; typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const void * _Nullable userInfo); /// Callback function pointer types for ORBPathCallbacks -typedef void (* _Nullable ORBPathRetainCallback)(struct ORBPath path); -typedef void (* _Nullable ORBPathReleaseCallback)(struct ORBPath path); -typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathStorageRef storage, void * _Nullable info, ORBPathApplyCallback _Nullable callback); -typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathStorageRef storage, ORBPathStorageRef otherStorage); -typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathStorageRef storage); -typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathStorageRef storage); -typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathStorageRef storage); +typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); +typedef void (* _Nullable ORBPathReleaseCallback)(ORBPathRef path); +typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathRef path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); +typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathRef path, ORBPathRef otherPath); +typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathRef path); +typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathRef path); +typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathRef path); #if ORB_TARGET_OS_DARWIN -typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathStorageRef storage); -typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathStorageRef storage); +typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); +typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); #else -typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathStorageRef storage); -typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathStorageRef storage); +typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); +typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); #endif /// Callbacks structure for path operations From e539fdc04a63287df6f0a437f1bbb8f869f1be7d Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 13:43:30 +0800 Subject: [PATCH 05/30] Add ORBPathPrivate.h --- Sources/OpenRenderBox/Path/ORBPath.cpp | 110 +++++++++++++++++- Sources/OpenRenderBox/Path/ORBPathPrivate.h | 57 +++++++++ .../include/OpenRenderBox/ORBPath.h | 74 ++++++------ 3 files changed, 200 insertions(+), 41 deletions(-) create mode 100644 Sources/OpenRenderBox/Path/ORBPathPrivate.h diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 0d79e7b..20b3e68 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -5,17 +5,43 @@ // Created by Kyle on 2025/3/25. // -#include +#include "ORBPathPrivate.h" + +// Empty path callbacks (all null) +const ORBPathCallbacks ORBPathEmptyCallbacks = { + nullptr, // reserved + nullptr, // retain + nullptr, // release + nullptr, // apply + nullptr, // isEqual + nullptr, // isEmpty + nullptr, // isSingleRect + nullptr, // bezierOrder + nullptr, // boundingBox + nullptr, // cgPath +}; + +// Empty path (storage = null) +const ORBPath ORBPathEmpty = { + nullptr, + &ORBPathEmptyCallbacks, +}; + +// Null path (storage = 0x1) +const ORBPath ORBPathNull = { + reinterpret_cast(0x1), + &ORBPathEmptyCallbacks, +}; void ORBPathRetain(ORBPath path) { - ORBPathRetainCallback retain = path.callbacks->retain; + auto retain = path.callbacks->retain; if (retain != nullptr) { retain(&path); } } void ORBPathRelease(ORBPath path) { - ORBPathReleaseCallback release = path.callbacks->release; + auto release = path.callbacks->release; if (release != nullptr) { release(&path); } @@ -123,4 +149,82 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { }, }; +// MARK: - Path Creation + +// TODO: TO be implemented natively + +ORBPath ORBPathMakeWithCGPath(CGPathRef cgPath) { + if (cgPath == nullptr) { + return ORBPathNull; + } + CFRetain(cgPath); + return ORBPath { + reinterpret_cast(const_cast(cgPath)), + &ORBPathCGPathCallbacks, + }; +} + +ORBPath ORBPathMakeRect(CGRect rect, const CGAffineTransform *transform) { + CGPathRef cgPath = CGPathCreateWithRect(rect, transform); + ORBPath path = { + reinterpret_cast(const_cast(cgPath)), + &ORBPathCGPathCallbacks, + }; + return path; +} + +ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform *transform) { + CGPathRef cgPath = CGPathCreateWithEllipseInRect(rect, transform); + ORBPath path = { + reinterpret_cast(const_cast(cgPath)), + &ORBPathCGPathCallbacks, + }; + return path; +} + +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform *transform) { + CGPathRef cgPath = CGPathCreateWithRoundedRect(rect, cornerWidth, cornerHeight, transform); + ORBPath path = { + reinterpret_cast(const_cast(cgPath)), + &ORBPathCGPathCallbacks, + }; + return path; +} + +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, const CGAffineTransform *transform) { + CGMutablePathRef cgPath = CGPathCreateMutable(); + + CGFloat minX = CGRectGetMinX(rect); + CGFloat minY = CGRectGetMinY(rect); + CGFloat maxX = CGRectGetMaxX(rect); + CGFloat maxY = CGRectGetMaxY(rect); + + // Start at top-left corner (after the rounded corner) + CGPathMoveToPoint(cgPath, transform, minX + topLeftRadius, minY); + + // Top edge and top-right corner + CGPathAddLineToPoint(cgPath, transform, maxX - topRightRadius, minY); + CGPathAddArc(cgPath, transform, maxX - topRightRadius, minY + topRightRadius, topRightRadius, -M_PI_2, 0, false); + + // Right edge and bottom-right corner + CGPathAddLineToPoint(cgPath, transform, maxX, maxY - bottomRightRadius); + CGPathAddArc(cgPath, transform, maxX - bottomRightRadius, maxY - bottomRightRadius, bottomRightRadius, 0, M_PI_2, false); + + // Bottom edge and bottom-left corner + CGPathAddLineToPoint(cgPath, transform, minX + bottomLeftRadius, maxY); + CGPathAddArc(cgPath, transform, minX + bottomLeftRadius, maxY - bottomLeftRadius, bottomLeftRadius, M_PI_2, M_PI, false); + + // Left edge and top-left corner + CGPathAddLineToPoint(cgPath, transform, minX, minY + topLeftRadius); + CGPathAddArc(cgPath, transform, minX + topLeftRadius, minY + topLeftRadius, topLeftRadius, M_PI, M_PI + M_PI_2, false); + + CGPathCloseSubpath(cgPath); + + ORBPath path = { + reinterpret_cast(cgPath), + &ORBPathCGPathCallbacks, + }; + return path; +} + #endif // ORB_TARGET_OS_DARWIN diff --git a/Sources/OpenRenderBox/Path/ORBPathPrivate.h b/Sources/OpenRenderBox/Path/ORBPathPrivate.h new file mode 100644 index 0000000..60299d3 --- /dev/null +++ b/Sources/OpenRenderBox/Path/ORBPathPrivate.h @@ -0,0 +1,57 @@ +// +// ORBPathPrivate.h +// OpenRenderBox + +#pragma once + +#include + +ORB_ASSUME_NONNULL_BEGIN + +ORB_EXTERN_C_BEGIN + +/// Callback function pointer types for ORBPathCallbacks +typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); +typedef void (* _Nullable ORBPathReleaseCallback)(ORBPathRef path); +typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathRef path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); +typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathRef path, ORBPathRef otherPath); +typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathRef path); +typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathRef path); +typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathRef path); +#if ORB_TARGET_OS_DARWIN +typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); +typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); +#else +typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); +typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); +#endif + +/// Callbacks structure for path operations +/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations +typedef struct ORBPathCallbacks { + void * _Nullable reserved; // 0x00: Reserved for future use + ORBPathRetainCallback retain; // 0x08: Retain callback + ORBPathReleaseCallback release; // 0x10: Release callback + ORBPathApplyFunction apply; // 0x18: Enumerate path elements + ORBPathIsEqualCallback isEqual; // 0x20: Compare two paths + ORBPathIsEmptyCallback isEmpty; // 0x28: Check if path is empty + ORBPathIsSingleRectCallback isSingleRect; // 0x30: Check if path is a single rectangle + ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) + ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box + ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation +} ORBPathCallbacks; + +/// Global empty path callbacks (all null) +ORB_EXPORT +const ORBPathCallbacks ORBPathEmptyCallbacks; + +#if ORB_TARGET_OS_DARWIN +/// Global callbacks for CGPath-backed paths +ORB_EXPORT +const ORBPathCallbacks ORBPathCGPathCallbacks; +#endif + +ORB_EXTERN_C_END + +ORB_ASSUME_NONNULL_END + diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index ca08653..bf4b56c 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -12,13 +12,19 @@ ORB_ASSUME_NONNULL_BEGIN +ORB_IMPLICIT_BRIDGING_ENABLED + ORB_EXTERN_C_BEGIN -typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef ORB_SWIFT_NAME(ORBPath); +typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBMutablePathRef ORB_SWIFT_NAME(ORBPath); +typedef const struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef ORB_SWIFT_NAME(ORBPath); + typedef struct ORB_BRIDGED_TYPE(id) ORBPathStorage * ORBPathStorageRef ORB_SWIFT_NAME(ORBPath.Storage); +typedef const struct ORB_BRIDGED_TYPE(id) ORBPathCallbacks * ORBPathCallbacksRef ORB_SWIFT_NAME(ORBPath.Callbacks); struct ORBPath; struct ORBPathStorage; +struct ORBPathCallbacks; /// Path element type for path enumeration typedef ORB_ENUM(int32_t, ORBPathElementType) { @@ -40,47 +46,18 @@ typedef struct ORBPathElement ORBPathElement; /// Returns true to stop enumeration, false to continue typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const void * _Nullable userInfo); -/// Callback function pointer types for ORBPathCallbacks -typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); -typedef void (* _Nullable ORBPathReleaseCallback)(ORBPathRef path); -typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathRef path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); -typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathRef path, ORBPathRef otherPath); -typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathRef path); -typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathRef path); -typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathRef path); -#if ORB_TARGET_OS_DARWIN -typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); -typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); -#else -typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); -typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); -#endif - -/// Callbacks structure for path operations -/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations -typedef struct ORBPathCallbacks { - void * _Nullable reserved; // 0x00: Reserved for future use - ORBPathRetainCallback retain; // 0x08: Retain callback - ORBPathReleaseCallback release; // 0x10: Release callback - ORBPathApplyFunction apply; // 0x18: Enumerate path elements - ORBPathIsEqualCallback isEqual; // 0x20: Compare two paths - ORBPathIsEmptyCallback isEmpty; // 0x28: Check if path is empty - ORBPathIsSingleRectCallback isSingleRect; // 0x30: Check if path is a single rectangle - ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) - ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box - ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation -} ORBPathCallbacks; - typedef struct ORBPath { - ORBPathStorageRef storage; - const ORBPathCallbacks * _Nullable callbacks; + ORBPathStorageRef _Nullable storage; + ORBPathCallbacksRef _Nullable callbacks; } ORBPath; -#if ORB_TARGET_OS_DARWIN -/// Global callbacks for CGPath-backed paths +/// Global empty path (storage = null, callbacks = &ORBPathEmptyCallbacks) ORB_EXPORT -const ORBPathCallbacks ORBPathCGPathCallbacks; -#endif +const ORBPath ORBPathEmpty; + +/// Global null path (storage = 0x1, callbacks = &ORBPathEmptyCallbacks) +ORB_EXPORT +const ORBPath ORBPathNull; ORB_EXPORT ORB_REFINED_FOR_SWIFT @@ -90,7 +67,28 @@ ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathRelease(ORBPath path) ORB_SWIFT_NAME(ORBPath.release(self:)); +// MARK: - Path Creation + +#if ORB_TARGET_OS_DARWIN +ORB_EXPORT +ORBPath ORBPathMakeWithCGPath(CGPathRef _Nullable cgPath); + +ORB_EXPORT +ORBPath ORBPathMakeRect(CGRect rect, const CGAffineTransform * _Nullable transform); + +ORB_EXPORT +ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform * _Nullable transform); + +ORB_EXPORT +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform * _Nullable transform); + +ORB_EXPORT +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, const CGAffineTransform * _Nullable transform); +#endif + ORB_EXTERN_C_END +ORB_IMPLICIT_BRIDGING_DISABLED + ORB_ASSUME_NONNULL_END From 34586323a5041519415ccbb988574ed7d9c8de3b Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 13:45:52 +0800 Subject: [PATCH 06/30] Update ORBPathStorageAppendPath --- Sources/OpenRenderBox/Path/ORBPathStorage.cpp | 4 +++- .../OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp index 33f33f0..e40860c 100644 --- a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp @@ -24,7 +24,9 @@ void ORBPathStorageClear(ORBPathStorageRef storage) { storage->storage.clear(); } -// ... +void ORBPathStorageAppendPath(ORBPathStorageRef storage, ORBPath path) { + precondition_failure("TODO"); +} bool ORBPathStorageIsEmpty(ORBPathStorageRef storage) { return storage->storage.isEmpty(); diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index e413eab..4804c80 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -27,9 +27,9 @@ ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathStorageClear(ORBPathStorageRef storage) ORB_SWIFT_NAME(ORBPathStorageRef.clear(self:)); -//ORB_EXPORT -//ORB_REFINED_FOR_SWIFT -//void ORBPathStorageAppendPath(ORBPathStorage, ORBPath); +ORB_EXPORT +ORB_REFINED_FOR_SWIFT +void ORBPathStorageAppendPath(ORBPathStorageRef, ORBPath) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:path:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT From 8f8de20d5125723c582133b787c4bccee838ac1d Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 16:25:58 +0800 Subject: [PATCH 07/30] Fix ORBPath duplicate def --- Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index bf4b56c..960a365 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -16,8 +16,8 @@ ORB_IMPLICIT_BRIDGING_ENABLED ORB_EXTERN_C_BEGIN -typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBMutablePathRef ORB_SWIFT_NAME(ORBPath); -typedef const struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef ORB_SWIFT_NAME(ORBPath); +typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBMutablePathRef; +typedef const struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef; typedef struct ORB_BRIDGED_TYPE(id) ORBPathStorage * ORBPathStorageRef ORB_SWIFT_NAME(ORBPath.Storage); typedef const struct ORB_BRIDGED_TYPE(id) ORBPathCallbacks * ORBPathCallbacksRef ORB_SWIFT_NAME(ORBPath.Callbacks); From 8ee84de8c3733c5ca0719e87a37c0bcc850bb48f Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 16:26:12 +0800 Subject: [PATCH 08/30] Update umbrella header --- .../OpenRenderBox/{OpenRenderBox-umbrella.h => OpenRenderBox.h} | 0 Sources/OpenRenderBox/include/module.modulemap | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Sources/OpenRenderBox/include/OpenRenderBox/{OpenRenderBox-umbrella.h => OpenRenderBox.h} (100%) diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox-umbrella.h b/Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h similarity index 100% rename from Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox-umbrella.h rename to Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h diff --git a/Sources/OpenRenderBox/include/module.modulemap b/Sources/OpenRenderBox/include/module.modulemap index 9d63041..ffce96a 100644 --- a/Sources/OpenRenderBox/include/module.modulemap +++ b/Sources/OpenRenderBox/include/module.modulemap @@ -1,5 +1,5 @@ module OpenRenderBox { - umbrella header "OpenRenderBox/OpenRenderBox-umbrella.h" + umbrella header "OpenRenderBox/OpenRenderBox.h" export * } From 106a172c5d7e3158f9ab0bf02c69930174dbf6f1 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 16:45:09 +0800 Subject: [PATCH 09/30] Add ORB_SWIFT_NAME annotations for path creation functions - Add ORBPathMakeWithCGPath, ORBPathMakeRect, ORBPathMakeEllipse, ORBPathMakeRoundedRect, ORBPathMakeUnevenRoundedRect with Swift init names - Add ORB_SWIFT_NAME to ORBPathStorageInit --- Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h | 10 +++++----- .../include/OpenRenderBox/ORBPathStorage.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 960a365..56f4a9c 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -71,19 +71,19 @@ void ORBPathRelease(ORBPath path) ORB_SWIFT_NAME(ORBPath.release(self:)); #if ORB_TARGET_OS_DARWIN ORB_EXPORT -ORBPath ORBPathMakeWithCGPath(CGPathRef _Nullable cgPath); +ORBPath ORBPathMakeWithCGPath(CGPathRef cgPath) ORB_SWIFT_NAME(ORBPath.init(cgPath:)); ORB_EXPORT -ORBPath ORBPathMakeRect(CGRect rect, const CGAffineTransform * _Nullable transform); +ORBPath ORBPathMakeRect(CGRect rect, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(rect:transform:)); ORB_EXPORT -ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform * _Nullable transform); +ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(ellipseIn:transform:)); ORB_EXPORT -ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform * _Nullable transform); +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:cornerWidth:cornerHeight:transform:)); ORB_EXPORT -ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, const CGAffineTransform * _Nullable transform); +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:transform:)); #endif ORB_EXTERN_C_END diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index 4804c80..af5e9e3 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -17,7 +17,7 @@ ORB_EXTERN_C_BEGIN ORB_EXPORT ORB_REFINED_FOR_SWIFT -void ORBPathStorageInit(ORBPathStorageRef dst, uint32_t capacity, ORBPathStorageRef _Nullable source); +void ORBPathStorageInit(ORBPathStorageRef dst, uint32_t capacity, ORBPathStorageRef _Nullable source) ORB_SWIFT_NAME(ORBPathStorageRef.init(self:capacity:source:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT From e5742adbd313d1135be1f417c5a8589f370d2ebd Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 17:16:57 +0800 Subject: [PATCH 10/30] Update PathStorage --- Sources/OpenRenderBox/Path/PathStorage.cpp | 29 +++++++++++++- Sources/OpenRenderBox/Util/assert.cpp | 2 +- .../include/OpenRenderBox/ORBPath.h | 4 +- .../include/OpenRenderBox/ORBPathStorage.h | 2 +- .../OpenRenderBoxCxx/Path/PathStorage.hpp | 39 ++++++++++--------- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/Sources/OpenRenderBox/Path/PathStorage.cpp b/Sources/OpenRenderBox/Path/PathStorage.cpp index c6282c2..c75a81f 100644 --- a/Sources/OpenRenderBox/Path/PathStorage.cpp +++ b/Sources/OpenRenderBox/Path/PathStorage.cpp @@ -5,10 +5,24 @@ #include #include +#if ORB_TARGET_OS_DARWIN +#include +#endif + namespace ORB { namespace Path { atomic_long Storage::_last_identifier; +Storage::Storage(uint32_t capacity) { + _unknown = nullptr; + if (capacity <= 63) { + precondition_failure("invalid capacity"); + } + uint32_t cappedCapacity = fmin(capacity - 16, 4095); + _flags = StorageFlags().withCapacity(cappedCapacity); + _identifier = get_identifier(); +} + Storage::Storage(uint32_t capacity, const Storage &storage): Storage(capacity) { uint32_t originalCapity = flags().capacity(); StorageFlags sourceFlags = storage.flags(); @@ -44,8 +58,8 @@ Storage::Storage(uint32_t capacity, const Storage &storage): Storage(capacity) { } Storage::~Storage() { - if (_unknonw != nullptr) { - _unknonw = nullptr; + if (_unknown != nullptr) { + _unknown = nullptr; // TODO } } @@ -59,5 +73,16 @@ void Storage::clear() { // TODO } +void * Storage::cgpath() const ORB_NOEXCEPT { + if (_flags.isInline()) { + return nullptr; + } + if (_cached_cgPath != nullptr) { + return _cached_cgPath; + } + // TODO: Create CGPath via RBPathCopyCGPath + return nullptr; +} + } /* Path */ } /* ORB */ diff --git a/Sources/OpenRenderBox/Util/assert.cpp b/Sources/OpenRenderBox/Util/assert.cpp index ecd758c..04aae13 100644 --- a/Sources/OpenRenderBox/Util/assert.cpp +++ b/Sources/OpenRenderBox/Util/assert.cpp @@ -19,7 +19,7 @@ void precondition_failure(const char *format, ...) { os_log_error(error_log(), "precondition failure: %s", s); #endif /* ORB_TARGET_OS_DARWIN */ if (error_message == nullptr) { - asprintf(&error_message, "OpenGraph precondition failure: %s.\n", s); + asprintf(&error_message, "OpenRenderBox precondition failure: %s.\n", s); } free(s); } diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 56f4a9c..e0f5978 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -47,8 +47,8 @@ typedef struct ORBPathElement ORBPathElement; typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const void * _Nullable userInfo); typedef struct ORBPath { - ORBPathStorageRef _Nullable storage; - ORBPathCallbacksRef _Nullable callbacks; + ORBPathStorageRef storage; + ORBPathCallbacksRef callbacks; } ORBPath; /// Global empty path (storage = null, callbacks = &ORBPathEmptyCallbacks) diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index af5e9e3..164c2cc 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -17,7 +17,7 @@ ORB_EXTERN_C_BEGIN ORB_EXPORT ORB_REFINED_FOR_SWIFT -void ORBPathStorageInit(ORBPathStorageRef dst, uint32_t capacity, ORBPathStorageRef _Nullable source) ORB_SWIFT_NAME(ORBPathStorageRef.init(self:capacity:source:)); +void ORBPathStorageInit(ORBPathStorageRef dst, uint32_t capacity, ORBPathStorageRef _Nullable source) ORB_SWIFT_NAME(ORBPathStorageRef.initialize(self:capacity:source:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT diff --git a/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp b/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp index 5d0c64b..09d6b4c 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp +++ b/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp @@ -134,24 +134,14 @@ class Storage { ORB_INLINE const static uint32_t get_identifier() ORB_NOEXCEPT { - _last_identifier++; - return _last_identifier; + return atomic_fetch_add_explicit(&_last_identifier, 1, memory_order_relaxed); } private: static atomic_long _last_identifier; public: - Storage(uint32_t capacity) { - _unknonw = nullptr; - if (capacity <= 63) { - precondition_failure("invalid capacity"); - } - _flags = StorageFlags().withCapacity(capacity - 10); - _identifier = get_identifier(); - } - - + Storage(uint32_t capacity); Storage(uint32_t capacity, const ORB::Path::Storage &storage); ~Storage(); @@ -162,10 +152,19 @@ class Storage { // void append_element(RBPathElement, double const*, void const*); // push_values(unsigned char, double const*, unsigned long) // update_single_element() + + /// Get cached CGPath, lazily creating if needed (matches RB::Path::Storage::cgpath) + void * cgpath() const ORB_NOEXCEPT; + public: ORB_INLINE ORB_CONSTEXPR void * unknown() const ORB_NOEXCEPT { - return _unknonw; + return _unknown; + } + + ORB_INLINE + void * cachedCGPath() const ORB_NOEXCEPT { + return _cached_cgPath; } ORB_INLINE ORB_CONSTEXPR @@ -211,11 +210,15 @@ class Storage { return actual_size() == 0; } private: - void * _unknonw; - StorageFlags _flags; - uint32_t _identifier; - Storage* _external_storage; - uint64_t _external_size; + void * _unknown; // 0x00 + StorageFlags _flags; // 0x08 + uint32_t _identifier; // 0x0C + Storage* _external_storage; // 0x10 + uint64_t _external_size; // 0x18 + void * _reserved1; // 0x20 + void * _reserved2; // 0x28 + void * _reserved3; // 0x30 + mutable void * _cached_cgPath; // 0x38 - lazily cached CGPath }; } /* Path */ } /* ORB */ From 683ce2abb6dcef06491846ceda83dcd2d0ea313d Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 17:40:06 +0800 Subject: [PATCH 11/30] Move ORBPathPrivate contents back to ORBPath.h - Move callback typedefs and ORBPathCallbacks struct to ORBPath.h - Move ORBPathEmptyCallbacks and ORBPathCGPathCallbacks declarations to ORBPath.h - Remove ORBPathPrivate.h - Update ORBPath.cpp to include ORBPath.h directly - Fix Storage constructor to use atomic_fetch_add_explicit for identifier - Add cgpath() method with lazy CGPath caching at offset 0x38 --- Sources/OpenRenderBox/Path/ORBPath.cpp | 2 +- Sources/OpenRenderBox/Path/ORBPathPrivate.h | 57 ------------------- .../include/OpenRenderBox/ORBPath.h | 41 +++++++++++++ 3 files changed, 42 insertions(+), 58 deletions(-) delete mode 100644 Sources/OpenRenderBox/Path/ORBPathPrivate.h diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 20b3e68..30c0901 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -5,7 +5,7 @@ // Created by Kyle on 2025/3/25. // -#include "ORBPathPrivate.h" +#include // Empty path callbacks (all null) const ORBPathCallbacks ORBPathEmptyCallbacks = { diff --git a/Sources/OpenRenderBox/Path/ORBPathPrivate.h b/Sources/OpenRenderBox/Path/ORBPathPrivate.h deleted file mode 100644 index 60299d3..0000000 --- a/Sources/OpenRenderBox/Path/ORBPathPrivate.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// ORBPathPrivate.h -// OpenRenderBox - -#pragma once - -#include - -ORB_ASSUME_NONNULL_BEGIN - -ORB_EXTERN_C_BEGIN - -/// Callback function pointer types for ORBPathCallbacks -typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); -typedef void (* _Nullable ORBPathReleaseCallback)(ORBPathRef path); -typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathRef path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); -typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathRef path, ORBPathRef otherPath); -typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathRef path); -typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathRef path); -typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathRef path); -#if ORB_TARGET_OS_DARWIN -typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); -typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); -#else -typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); -typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); -#endif - -/// Callbacks structure for path operations -/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations -typedef struct ORBPathCallbacks { - void * _Nullable reserved; // 0x00: Reserved for future use - ORBPathRetainCallback retain; // 0x08: Retain callback - ORBPathReleaseCallback release; // 0x10: Release callback - ORBPathApplyFunction apply; // 0x18: Enumerate path elements - ORBPathIsEqualCallback isEqual; // 0x20: Compare two paths - ORBPathIsEmptyCallback isEmpty; // 0x28: Check if path is empty - ORBPathIsSingleRectCallback isSingleRect; // 0x30: Check if path is a single rectangle - ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) - ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box - ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation -} ORBPathCallbacks; - -/// Global empty path callbacks (all null) -ORB_EXPORT -const ORBPathCallbacks ORBPathEmptyCallbacks; - -#if ORB_TARGET_OS_DARWIN -/// Global callbacks for CGPath-backed paths -ORB_EXPORT -const ORBPathCallbacks ORBPathCGPathCallbacks; -#endif - -ORB_EXTERN_C_END - -ORB_ASSUME_NONNULL_END - diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index e0f5978..7307a90 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -46,11 +46,52 @@ typedef struct ORBPathElement ORBPathElement; /// Returns true to stop enumeration, false to continue typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const void * _Nullable userInfo); +/// Callback function pointer types for ORBPathCallbacks +typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); +typedef void (* _Nullable ORBPathReleaseCallback)(ORBPathRef path); +typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathRef path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); +typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathRef path, ORBPathRef otherPath); +typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathRef path); +typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathRef path); +typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathRef path); +#if ORB_TARGET_OS_DARWIN +typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); +typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); +#else +typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); +typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); +#endif + +/// Callbacks structure for path operations +/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations +typedef struct ORBPathCallbacks { + void * _Nullable reserved; // 0x00: Reserved for future use + ORBPathRetainCallback retain; // 0x08: Retain callback + ORBPathReleaseCallback release; // 0x10: Release callback + ORBPathApplyFunction apply; // 0x18: Enumerate path elements + ORBPathIsEqualCallback isEqual; // 0x20: Compare two paths + ORBPathIsEmptyCallback isEmpty; // 0x28: Check if path is empty + ORBPathIsSingleRectCallback isSingleRect; // 0x30: Check if path is a single rectangle + ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) + ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box + ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation +} ORBPathCallbacks; + typedef struct ORBPath { ORBPathStorageRef storage; ORBPathCallbacksRef callbacks; } ORBPath; +/// Global empty path callbacks (all null) +ORB_EXPORT +const ORBPathCallbacks ORBPathEmptyCallbacks; + +#if ORB_TARGET_OS_DARWIN +/// Global callbacks for CGPath-backed paths +ORB_EXPORT +const ORBPathCallbacks ORBPathCGPathCallbacks; +#endif + /// Global empty path (storage = null, callbacks = &ORBPathEmptyCallbacks) ORB_EXPORT const ORBPath ORBPathEmpty; From c3e1299c4ba3eb8a5af0abae6fffc26405e24292 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 18:09:34 +0800 Subject: [PATCH 12/30] Add reserved2 field to ORBPathCallbacks at offset 0x50 --- Sources/OpenRenderBox/Path/ORBPath.cpp | 4 +++- Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 30c0901..4625623 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -19,6 +19,7 @@ const ORBPathCallbacks ORBPathEmptyCallbacks = { nullptr, // bezierOrder nullptr, // boundingBox nullptr, // cgPath + nullptr, // reserved2 }; // Empty path (storage = null) @@ -54,7 +55,7 @@ void ORBPathRelease(ORBPath path) { // FIXME: Not implemented correctly const ORBPathCallbacks ORBPathCGPathCallbacks = { - nullptr, + nullptr, // reserved // retain +[](ORBPathRef path) -> void { CFRetain(path->storage); @@ -147,6 +148,7 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { +[](ORBPathRef path) -> CGPathRef { return reinterpret_cast(path->storage); }, + nullptr, // reserved2 }; // MARK: - Path Creation diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 7307a90..0692153 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -75,6 +75,7 @@ typedef struct ORBPathCallbacks { ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation + void * _Nullable reserved2; // 0x50: Reserved for future use } ORBPathCallbacks; typedef struct ORBPath { From 11d2e7bcdc50a5fcb1a0485cd1c304c1791d2af6 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 18:16:50 +0800 Subject: [PATCH 13/30] Add ORBRoundedCornerStyle enum to rounded rect path functions - Add ORBRoundedCornerStyle enum with Circular and Continuous options - Add style parameter to ORBPathMakeRoundedRect before transform - Add style parameter to ORBPathMakeUnevenRoundedRect before transform - TODO: Implement continuous curvature for non-circular style --- Sources/OpenRenderBox/Path/ORBPath.cpp | 6 ++++-- .../OpenRenderBox/include/OpenRenderBox/ORBPath.h | 12 ++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 4625623..acda499 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -184,7 +184,8 @@ ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform *transform) { return path; } -ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform *transform) { +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, ORBRoundedCornerStyle style, const CGAffineTransform *transform) { + // TODO: Handle ORBRoundedCornerStyleContinuous with custom bezier curves CGPathRef cgPath = CGPathCreateWithRoundedRect(rect, cornerWidth, cornerHeight, transform); ORBPath path = { reinterpret_cast(const_cast(cgPath)), @@ -193,7 +194,8 @@ ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerH return path; } -ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, const CGAffineTransform *transform) { +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBRoundedCornerStyle style, const CGAffineTransform *transform) { + // TODO: Handle ORBRoundedCornerStyleContinuous with custom bezier curves CGMutablePathRef cgPath = CGPathCreateMutable(); CGFloat minX = CGRectGetMinX(rect); diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 0692153..7da6d27 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -35,6 +35,14 @@ typedef ORB_ENUM(int32_t, ORBPathElementType) { ORBPathElementCloseSubpath = 4, }; +/// Defines the shape of a rounded rectangle's corners. +typedef ORB_ENUM(int32_t, ORBRoundedCornerStyle) { + /// Quarter-circle rounded rect corners. + ORBRoundedCornerStyleCircular = 0, + /// Continuous curvature rounded rect corners. + ORBRoundedCornerStyleContinuous = 1, +}; + /// An element of a path returned by path enumeration struct ORBPathElement { ORBPathElementType type; @@ -122,10 +130,10 @@ ORB_EXPORT ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(ellipseIn:transform:)); ORB_EXPORT -ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:cornerWidth:cornerHeight:transform:)); +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, ORBRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:cornerWidth:cornerHeight:style:transform:)); ORB_EXPORT -ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:transform:)); +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:style:transform:)); #endif ORB_EXTERN_C_END From 6babd19f248494e8128106c6a86ecb7d31dd6d01 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 18:28:46 +0800 Subject: [PATCH 14/30] Add ORBPathCopyCGPath API with TODO implementation --- Sources/OpenRenderBox/Path/ORBPath.cpp | 5 +++++ Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index acda499..59afad7 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -231,4 +231,9 @@ ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat return path; } +CGPathRef ORBPathCopyCGPath(ORBPath path) { + // TODO: Return a retained copy of the CGPath + return nullptr; +} + #endif // ORB_TARGET_OS_DARWIN diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 7da6d27..f0f33f4 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -134,6 +134,9 @@ ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerH ORB_EXPORT ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:style:transform:)); + +ORB_EXPORT +CGPathRef ORBPathCopyCGPath(ORBPath path) ORB_SWIFT_NAME(getter:ORBPath.cgPath(self:)); #endif ORB_EXTERN_C_END From 346d56909b2d3dde6c9dd07da93ffd6a45661c74 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 20:02:45 +0800 Subject: [PATCH 15/30] Rename ORBPathElementType to ORBPathElement and update callback signature - Delete ORBPathElement struct, use enum directly - Add ORBPathElementFixedRoundedRectCircular and ORBPathElementFixedRoundedRectContinuous - Update ORBPathApplyCallback to pass element and points as separate parameters --- Sources/OpenRenderBox/Path/ORBPath.cpp | 23 ++++++++++--------- Sources/OpenRenderBox/Path/ORBPathStorage.cpp | 4 ++++ .../include/OpenRenderBox/ORBPath.h | 14 ++++------- .../include/OpenRenderBox/ORBPathStorage.h | 4 ++++ 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 59afad7..91daa1a 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -72,44 +72,45 @@ const ORBPathCallbacks ORBPathCGPathCallbacks = { if (shouldStop) return; ORBPathElement orbElement; double pointBuffer[6]; + double *points = nullptr; switch (element->type) { case kCGPathElementMoveToPoint: - orbElement.type = ORBPathElementMoveToPoint; + orbElement = ORBPathElementMoveToPoint; pointBuffer[0] = element->points[0].x; pointBuffer[1] = element->points[0].y; - orbElement.points = pointBuffer; + points = pointBuffer; break; case kCGPathElementAddLineToPoint: - orbElement.type = ORBPathElementAddLineToPoint; + orbElement = ORBPathElementAddLineToPoint; pointBuffer[0] = element->points[0].x; pointBuffer[1] = element->points[0].y; - orbElement.points = pointBuffer; + points = pointBuffer; break; case kCGPathElementAddQuadCurveToPoint: - orbElement.type = ORBPathElementAddQuadCurveToPoint; + orbElement = ORBPathElementAddQuadCurveToPoint; pointBuffer[0] = element->points[0].x; pointBuffer[1] = element->points[0].y; pointBuffer[2] = element->points[1].x; pointBuffer[3] = element->points[1].y; - orbElement.points = pointBuffer; + points = pointBuffer; break; case kCGPathElementAddCurveToPoint: - orbElement.type = ORBPathElementAddCurveToPoint; + orbElement = ORBPathElementAddCurveToPoint; pointBuffer[0] = element->points[0].x; pointBuffer[1] = element->points[0].y; pointBuffer[2] = element->points[1].x; pointBuffer[3] = element->points[1].y; pointBuffer[4] = element->points[2].x; pointBuffer[5] = element->points[2].y; - orbElement.points = pointBuffer; + points = pointBuffer; break; case kCGPathElementCloseSubpath: - orbElement.type = ORBPathElementCloseSubpath; - orbElement.points = nullptr; + orbElement = ORBPathElementCloseSubpath; + points = nullptr; break; } if (callback != nullptr) { - shouldStop = callback(info, orbElement, nullptr); + shouldStop = callback(info, orbElement, points, nullptr); } }); return !shouldStop; diff --git a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp index e40860c..be9ab52 100644 --- a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp @@ -24,6 +24,10 @@ void ORBPathStorageClear(ORBPathStorageRef storage) { storage->storage.clear(); } +void ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, double const * points, const void * userInfo) { + precondition_failure("TODO"); +} + void ORBPathStorageAppendPath(ORBPathStorageRef storage, ORBPath path) { precondition_failure("TODO"); } diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index f0f33f4..6c86210 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -27,12 +27,15 @@ struct ORBPathStorage; struct ORBPathCallbacks; /// Path element type for path enumeration -typedef ORB_ENUM(int32_t, ORBPathElementType) { +typedef ORB_ENUM(int32_t, ORBPathElement) { ORBPathElementMoveToPoint = 0, ORBPathElementAddLineToPoint = 1, ORBPathElementAddQuadCurveToPoint = 2, ORBPathElementAddCurveToPoint = 3, ORBPathElementCloseSubpath = 4, + + ORBPathElementFixedRoundedRectCircular = 8, + ORBPathElementFixedRoundedRectContinuous = 9, }; /// Defines the shape of a rounded rectangle's corners. @@ -43,16 +46,9 @@ typedef ORB_ENUM(int32_t, ORBRoundedCornerStyle) { ORBRoundedCornerStyleContinuous = 1, }; -/// An element of a path returned by path enumeration -struct ORBPathElement { - ORBPathElementType type; - const double * _Nullable points; -}; -typedef struct ORBPathElement ORBPathElement; - /// Callback type for path element enumeration /// Returns true to stop enumeration, false to continue -typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const void * _Nullable userInfo); +typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const double *points, const void * _Nullable userInfo); /// Callback function pointer types for ORBPathCallbacks typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index 164c2cc..8f36895 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -27,6 +27,10 @@ ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathStorageClear(ORBPathStorageRef storage) ORB_SWIFT_NAME(ORBPathStorageRef.clear(self:)); +ORB_EXPORT +ORB_REFINED_FOR_SWIFT +void ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, double const * points, const void * _Nullable userInfo) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:element:points:userInfo:)); + ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathStorageAppendPath(ORBPathStorageRef, ORBPath) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:path:)); From c2c1337856da94392c27b031c79114a5f821d926 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 20:10:52 +0800 Subject: [PATCH 16/30] Add ORBPathContainsPoint and ORBPathContainsPoints APIs - ORBPathContainsPoint: tests if a single point is inside the path - ORBPathContainsPoints: tests multiple points with optional transform - eoFill parameter controls even-odd vs non-zero winding rule - TODO: implement point containment testing --- Package.swift | 4 ++-- Sources/OpenRenderBox/Path/ORBPath.cpp | 9 +++++++++ Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 88be25b..ff8d04f 100644 --- a/Package.swift +++ b/Package.swift @@ -159,12 +159,12 @@ let renderBoxCondtion = envBoolValue("RENDERBOX", default: buildForDarwinPlatfor var sharedCSettings: [CSetting] = [ .unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)), - .unsafeFlags(["-fmodules"]), + // .unsafeFlags(["-fmodules"]), .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), ] var sharedCxxSettings: [CXXSetting] = [ .unsafeFlags(["-I", libSwiftPath], .when(platforms: .nonDarwinPlatforms)), - .unsafeFlags(["-fcxx-modules"]), + // .unsafeFlags(["-fcxx-modules"]), .define("__COREFOUNDATION_FORSWIFTFOUNDATIONONLY__", to: "1", .when(platforms: .nonDarwinPlatforms)), ] var sharedSwiftSettings: [SwiftSetting] = [ diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 91daa1a..161ec50 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -237,4 +237,13 @@ CGPathRef ORBPathCopyCGPath(ORBPath path) { return nullptr; } +bool ORBPathContainsPoint(ORBPath path, CGPoint point, bool eoFill) { + return ORBPathContainsPoints(path, 1, &point, eoFill, nullptr); +} + +bool ORBPathContainsPoints(ORBPath path, uint64_t count, const CGPoint *points, bool eoFill, const CGAffineTransform *transform) { + // TODO: Implement point containment testing with winding rule + return false; +} + #endif // ORB_TARGET_OS_DARWIN diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 6c86210..61d1fd3 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -133,6 +133,12 @@ ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat ORB_EXPORT CGPathRef ORBPathCopyCGPath(ORBPath path) ORB_SWIFT_NAME(getter:ORBPath.cgPath(self:)); + +ORB_EXPORT +bool ORBPathContainsPoint(ORBPath path, CGPoint point, bool eoFill) ORB_SWIFT_NAME(ORBPath.contains(self:point:eoFill:)); + +ORB_EXPORT +bool ORBPathContainsPoints(ORBPath path, uint64_t count, const CGPoint *points, bool eoFill, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.containsPoints(self:count:points:eoFill:transform:)); #endif ORB_EXTERN_C_END From 7e9ba0ff6ff4024d8675e4e4c9f362f7494b01ac Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 22:35:15 +0800 Subject: [PATCH 17/30] Update PathTests --- .../include/OpenRenderBox/ORBPath.h | 23 +++-- .../include/OpenRenderBox/ORBPathStorage.h | 2 +- .../BoxShims.swift | 2 + .../PathStorageTests.swift | 99 +++++++++++++++++++ .../PathTests.swift | 63 ++++++++++++ .../Path/PathTests.swift | 19 ++++ 6 files changed, 197 insertions(+), 11 deletions(-) create mode 100644 Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift create mode 100644 Tests/OpenRenderBoxCompatibilityTests/PathTests.swift create mode 100644 Tests/OpenRenderBoxCxxTests/Path/PathTests.swift diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 61d1fd3..e4c3b80 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -18,9 +18,7 @@ ORB_EXTERN_C_BEGIN typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBMutablePathRef; typedef const struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef; - -typedef struct ORB_BRIDGED_TYPE(id) ORBPathStorage * ORBPathStorageRef ORB_SWIFT_NAME(ORBPath.Storage); -typedef const struct ORB_BRIDGED_TYPE(id) ORBPathCallbacks * ORBPathCallbacksRef ORB_SWIFT_NAME(ORBPath.Callbacks); +typedef struct ORBPathStorage * ORBPathStorageRef ORB_SWIFT_STRUCT ORB_SWIFT_NAME(ORBPath.Storage); struct ORBPath; struct ORBPathStorage; @@ -48,7 +46,7 @@ typedef ORB_ENUM(int32_t, ORBRoundedCornerStyle) { /// Callback type for path element enumeration /// Returns true to stop enumeration, false to continue -typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const double *points, const void * _Nullable userInfo); +typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); /// Callback function pointer types for ORBPathCallbacks typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); @@ -82,11 +80,6 @@ typedef struct ORBPathCallbacks { void * _Nullable reserved2; // 0x50: Reserved for future use } ORBPathCallbacks; -typedef struct ORBPath { - ORBPathStorageRef storage; - ORBPathCallbacksRef callbacks; -} ORBPath; - /// Global empty path callbacks (all null) ORB_EXPORT const ORBPathCallbacks ORBPathEmptyCallbacks; @@ -97,6 +90,11 @@ ORB_EXPORT const ORBPathCallbacks ORBPathCGPathCallbacks; #endif +typedef struct ORBPath { + ORBPathStorageRef storage; + const ORBPathCallbacks * callbacks; +} ORBPath; + /// Global empty path (storage = null, callbacks = &ORBPathEmptyCallbacks) ORB_EXPORT const ORBPath ORBPathEmpty; @@ -113,9 +111,10 @@ ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathRelease(ORBPath path) ORB_SWIFT_NAME(ORBPath.release(self:)); +#if ORB_TARGET_OS_DARWIN + // MARK: - Path Creation -#if ORB_TARGET_OS_DARWIN ORB_EXPORT ORBPath ORBPathMakeWithCGPath(CGPathRef cgPath) ORB_SWIFT_NAME(ORBPath.init(cgPath:)); @@ -131,9 +130,13 @@ ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerH ORB_EXPORT ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:style:transform:)); +// MARK: - CGPath Interoperability + ORB_EXPORT CGPathRef ORBPathCopyCGPath(ORBPath path) ORB_SWIFT_NAME(getter:ORBPath.cgPath(self:)); +// MARK: - Point Containment + ORB_EXPORT bool ORBPathContainsPoint(ORBPath path, CGPoint point, bool eoFill) ORB_SWIFT_NAME(ORBPath.contains(self:point:eoFill:)); diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index 8f36895..fac136f 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -29,7 +29,7 @@ void ORBPathStorageClear(ORBPathStorageRef storage) ORB_SWIFT_NAME(ORBPathStorag ORB_EXPORT ORB_REFINED_FOR_SWIFT -void ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, double const * points, const void * _Nullable userInfo) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:element:points:userInfo:)); +void ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, CGFloat const * points, const void * _Nullable userInfo) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:element:points:userInfo:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT diff --git a/Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift b/Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift index c378bdd..7f6f89b 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift @@ -5,6 +5,8 @@ #if OPENRENDERBOX_COMPATIBILITY_TEST @_exported public import RenderBox public typealias ORBUUID = RBUUID +public typealias ORBPath = RBPath +public typealias ORBRoundedCornerStyle = RBRoundedCornerStyle public let compatibilityTestEnabled = true #else @_exported import OpenRenderBox diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift new file mode 100644 index 0000000..db7c0e6 --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift @@ -0,0 +1,99 @@ +// +// PathStorageTests.swift +// OpenRenderBoxCompatibilityTests + +import Testing + +#if canImport(CoreGraphics) +import CoreGraphics + +@Suite(.enabled(if: compatibilityTestEnabled)) +struct PathStorageTests { + @Test + func storageIsEmpty() { + let path = ORBPath(rect: CGRect(x: 0, y: 0, width: 100, height: 100), transform: nil) + let storage = path.storage + storage.initialize(capacity: 96, source: nil) + #expect(storage.isEmpty == true) + storage.append(path: path) + #expect(storage.isEmpty == false) + storage.destroy() + path.release() + } + + @Test + func storageIsSingleElement() { + let path = ORBPath(rect: CGRect(x: 0, y: 0, width: 100, height: 100), transform: nil) + let storage = path.storage + storage.initialize(capacity: 96, source: nil) + #expect(storage.isSingleElement == false) + storage.append(path: path) + #expect(storage.isSingleElement == true) + storage.destroy() + path.release() + } + + @Test + func storageBezierOrder() { + let path = ORBPath(rect: CGRect(x: 0, y: 0, width: 100, height: 100), transform: nil) + let order = path.storage.bezierOrder + #expect(order == 1) + path.release() + } + + @Test + func storageBoundingRect() { + let rect = CGRect(x: 10, y: 20, width: 100, height: 50) + let path = ORBPath(rect: rect, transform: nil) + let storage = path.storage + storage.initialize(capacity: 96, source: nil) + storage.append(path: path) + let boundingRect = path.storage.boundingRect + #expect(boundingRect.origin.x == rect.origin.x) + #expect(boundingRect.origin.y == rect.origin.y) + #expect(boundingRect.size.width == rect.size.width) + #expect(boundingRect.size.height == rect.size.height) + storage.destroy() + path.release() + } + + @Test + func storageEqualToStorage() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path1 = ORBPath(rect: rect, transform: nil) + let path2 = ORBPath(rect: rect, transform: nil) + let storage1 = path1.storage + let storage2 = path2.storage + #expect(storage1.isEqualTo(storage2) == true) + storage1.initialize(capacity: 96, source: nil) + storage1.append(path: path1) + storage2.initialize(capacity: 96, source: nil) + storage2.append(path: path2) + #expect(storage1.isEqualTo(storage2) == true) + storage1.destroy() + storage2.destroy() + path1.release() + path2.release() + } + + @Test + func storageNotEqual() { + let path1 = ORBPath(rect: CGRect(x: 0, y: 0, width: 100, height: 100), transform: nil) + let path2 = ORBPath(rect: CGRect(x: 0, y: 0, width: 200, height: 200), transform: nil) + let storage1 = path1.storage + let storage2 = path2.storage + #expect(storage1.isEqualTo(storage2) == true) + storage1.initialize(capacity: 96, source: nil) + storage1.append(path: path1) + storage2.initialize(capacity: 96, source: nil) + storage2.append(path: path2) + #expect(storage1.isEqualTo(storage2) == false) + storage1.destroy() + storage2.destroy() + path1.release() + path2.release() + } +} + +#endif + diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift new file mode 100644 index 0000000..e2126c6 --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift @@ -0,0 +1,63 @@ +// +// PathTests.swift +// OpenRenderBoxCompatibilityTests + +import Testing + +#if canImport(CoreGraphics) +import CoreGraphics + +@Suite(.enabled(if: compatibilityTestEnabled)) +struct PathTests { + @Test + func createRectPath() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(rect: rect, transform: nil) + path.release() + } + + @Test + func createEllipsePath() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 50) + let path = ORBPath(ellipseIn: rect, transform: nil) + path.release() + } + + @Test + func createRoundedRectPath() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(roundedRect: rect, cornerWidth: 10, cornerHeight: 10, style: .circular, transform: nil) + path.release() + } + + @Test + func createUnevenRoundedRectPath() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(roundedRect: rect, topLeftRadius: 5, bottomLeftRadius: 10, bottomRightRadius: 15, topRightRadius: 20, style: .continuous, transform: nil) + path.release() + } + + @Test + func pathRetainRelease() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(rect: rect, transform: nil) + path.retain() + path.release() + path.release() + } + + @Test + func pathContainsPoint() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(rect: rect, transform: nil) + let insidePoint = CGPoint(x: 50, y: 50) + let outsidePoint = CGPoint(x: 150, y: 150) + #expect(path.contains(point: insidePoint, eoFill: false) == true) + #expect(path.contains(point: outsidePoint, eoFill: false) == false) + path.release() + + } +} + +#endif + diff --git a/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift b/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift new file mode 100644 index 0000000..b39eb15 --- /dev/null +++ b/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift @@ -0,0 +1,19 @@ +// +// PathTests.swift +// OpenRenderBox +// +// Created by Kyle on 12/21/25. +// + +#if canImport(Darwin) +import OpenRenderBoxCxx.Path +import Testing + +@Suite +struct PathTests { + @Test + func demo() { + let storage = ORB.Path.Storage(3) + } +} +#endif From 6cfa29b22cc6aa62c705fabe17b8027e840d872f Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 23:08:45 +0800 Subject: [PATCH 18/30] Update ORBPathCallbacks --- Sources/OpenRenderBox/Path/ORBPath.cpp | 120 +---------------- .../OpenRenderBox/Path/ORBPathCallbacks.cpp | 123 ++++++++++++++++++ .../include/OpenRenderBox/ORBPath.h | 56 +------- .../include/OpenRenderBox/ORBPathCallbacks.h | 50 +++++++ .../include/OpenRenderBox/ORBPathStorage.h | 1 - 5 files changed, 177 insertions(+), 173 deletions(-) create mode 100644 Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp create mode 100644 Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 161ec50..0e51147 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -6,21 +6,7 @@ // #include - -// Empty path callbacks (all null) -const ORBPathCallbacks ORBPathEmptyCallbacks = { - nullptr, // reserved - nullptr, // retain - nullptr, // release - nullptr, // apply - nullptr, // isEqual - nullptr, // isEmpty - nullptr, // isSingleRect - nullptr, // bezierOrder - nullptr, // boundingBox - nullptr, // cgPath - nullptr, // reserved2 -}; +#include // Empty path (storage = null) const ORBPath ORBPathEmpty = { @@ -50,108 +36,6 @@ void ORBPathRelease(ORBPath path) { #if ORB_TARGET_OS_DARWIN -#include -#include - -// FIXME: Not implemented correctly -const ORBPathCallbacks ORBPathCGPathCallbacks = { - nullptr, // reserved - // retain - +[](ORBPathRef path) -> void { - CFRetain(path->storage); - }, - // release - +[](ORBPathRef path) -> void { - CFRelease(path->storage); - }, - // apply - +[](ORBPathRef path, void *info, ORBPathApplyCallback callback) -> bool { - CGPathRef cgPath = reinterpret_cast(path->storage); - __block bool shouldStop = false; - CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { - if (shouldStop) return; - ORBPathElement orbElement; - double pointBuffer[6]; - double *points = nullptr; - switch (element->type) { - case kCGPathElementMoveToPoint: - orbElement = ORBPathElementMoveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - points = pointBuffer; - break; - case kCGPathElementAddLineToPoint: - orbElement = ORBPathElementAddLineToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - points = pointBuffer; - break; - case kCGPathElementAddQuadCurveToPoint: - orbElement = ORBPathElementAddQuadCurveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - pointBuffer[2] = element->points[1].x; - pointBuffer[3] = element->points[1].y; - points = pointBuffer; - break; - case kCGPathElementAddCurveToPoint: - orbElement = ORBPathElementAddCurveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - pointBuffer[2] = element->points[1].x; - pointBuffer[3] = element->points[1].y; - pointBuffer[4] = element->points[2].x; - pointBuffer[5] = element->points[2].y; - points = pointBuffer; - break; - case kCGPathElementCloseSubpath: - orbElement = ORBPathElementCloseSubpath; - points = nullptr; - break; - } - if (callback != nullptr) { - shouldStop = callback(info, orbElement, points, nullptr); - } - }); - return !shouldStop; - }, - // isEqual - +[](ORBPathRef path, ORBPathRef otherPath) -> bool { - return CGPathEqualToPath(reinterpret_cast(path->storage), reinterpret_cast(otherPath->storage)); - }, - // isEmpty - +[](ORBPathRef path) -> bool { - return CGPathIsEmpty(reinterpret_cast(path->storage)); - }, - // isSingleRect - CGPath doesn't have this, always return false - +[](ORBPathRef path) -> bool { - return false; - }, - // bezierOrder - +[](ORBPathRef path) -> uint32_t { - CGPathRef cgPath = reinterpret_cast(path->storage); - __block uint32_t order = 1; - CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { - uint32_t *orderPtr = static_cast(info); - if (element->type == kCGPathElementAddCurveToPoint) { - *orderPtr = 3; - } else if (element->type == kCGPathElementAddQuadCurveToPoint && *orderPtr < 3) { - *orderPtr = 2; - } - }); - return order; - }, - // boundingBox - +[](ORBPathRef path) -> CGRect { - return CGPathGetPathBoundingBox(reinterpret_cast(path->storage)); - }, - // cgPath - return the CGPath itself - +[](ORBPathRef path) -> CGPathRef { - return reinterpret_cast(path->storage); - }, - nullptr, // reserved2 -}; - // MARK: - Path Creation // TODO: TO be implemented natively @@ -246,4 +130,4 @@ bool ORBPathContainsPoints(ORBPath path, uint64_t count, const CGPoint *points, return false; } -#endif // ORB_TARGET_OS_DARWIN +#endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp b/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp new file mode 100644 index 0000000..fe5455b --- /dev/null +++ b/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp @@ -0,0 +1,123 @@ +// +// ORBPathCallbacks.cpp +// OpenRenderBox + +#include +#include + +// Empty path callbacks (all null) +const ORBPathCallbacks ORBPathEmptyCallbacks = { + nullptr, // reserved + nullptr, // retain + nullptr, // release + nullptr, // apply + nullptr, // isEqual + nullptr, // isEmpty + nullptr, // isSingleRect + nullptr, // bezierOrder + nullptr, // boundingBox + nullptr, // cgPath + nullptr, // reserved2 +}; + +#if ORB_TARGET_OS_DARWIN +// FIXME: Not implemented correctly +const ORBPathCallbacks ORBPathCGPathCallbacks = { + nullptr, // reserved + // retain + +[](const ORBPath *path) -> void { + CFRetain(path->storage); + }, + // release + +[](const ORBPath *path) -> void { + CFRelease(path->storage); + }, + // apply + +[](const ORBPath *path, void *info, ORBPathApplyCallback callback) -> bool { + CGPathRef cgPath = reinterpret_cast(path->storage); + __block bool shouldStop = false; + CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { + if (shouldStop) return; + ORBPathElement orbElement; + double pointBuffer[6]; + double *points = nullptr; + switch (element->type) { + case kCGPathElementMoveToPoint: + orbElement = ORBPathElementMoveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + points = pointBuffer; + break; + case kCGPathElementAddLineToPoint: + orbElement = ORBPathElementAddLineToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + points = pointBuffer; + break; + case kCGPathElementAddQuadCurveToPoint: + orbElement = ORBPathElementAddQuadCurveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + pointBuffer[2] = element->points[1].x; + pointBuffer[3] = element->points[1].y; + points = pointBuffer; + break; + case kCGPathElementAddCurveToPoint: + orbElement = ORBPathElementAddCurveToPoint; + pointBuffer[0] = element->points[0].x; + pointBuffer[1] = element->points[0].y; + pointBuffer[2] = element->points[1].x; + pointBuffer[3] = element->points[1].y; + pointBuffer[4] = element->points[2].x; + pointBuffer[5] = element->points[2].y; + points = pointBuffer; + break; + case kCGPathElementCloseSubpath: + orbElement = ORBPathElementCloseSubpath; + points = nullptr; + break; + } + if (callback != nullptr) { + shouldStop = callback(info, orbElement, points, nullptr); + } + }); + return !shouldStop; + }, + // isEqual + +[](const ORBPath *path, const ORBPath *otherPath) -> bool { + return CGPathEqualToPath(reinterpret_cast(path->storage), reinterpret_cast(otherPath->storage)); + }, + // isEmpty + +[](const ORBPath *path) -> bool { + return CGPathIsEmpty(reinterpret_cast(path->storage)); + }, + // isSingleRect - CGPath doesn't have this, always return false + +[](const ORBPath *path) -> bool { + return false; + }, + // bezierOrder + +[](const ORBPath *path) -> uint32_t { + CGPathRef cgPath = reinterpret_cast(path->storage); + __block uint32_t order = 1; + CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { + uint32_t *orderPtr = static_cast(info); + if (element->type == kCGPathElementAddCurveToPoint) { + *orderPtr = 3; + } else if (element->type == kCGPathElementAddQuadCurveToPoint && *orderPtr < 3) { + *orderPtr = 2; + } + }); + return order; + }, + // boundingBox + +[](const ORBPath *path) -> CGRect { + return CGPathGetPathBoundingBox(reinterpret_cast(path->storage)); + }, + // cgPath - return the CGPath itself + +[](const ORBPath *path) -> CGPathRef { + return reinterpret_cast(path->storage); + }, + nullptr, // reserved2 +}; + +#endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index e4c3b80..fb35825 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -5,25 +5,14 @@ #pragma once #include - #if ORB_TARGET_OS_DARWIN #include #endif ORB_ASSUME_NONNULL_BEGIN -ORB_IMPLICIT_BRIDGING_ENABLED - ORB_EXTERN_C_BEGIN -typedef struct ORB_BRIDGED_TYPE(id) ORBPath * ORBMutablePathRef; -typedef const struct ORB_BRIDGED_TYPE(id) ORBPath * ORBPathRef; -typedef struct ORBPathStorage * ORBPathStorageRef ORB_SWIFT_STRUCT ORB_SWIFT_NAME(ORBPath.Storage); - -struct ORBPath; -struct ORBPathStorage; -struct ORBPathCallbacks; - /// Path element type for path enumeration typedef ORB_ENUM(int32_t, ORBPathElement) { ORBPathElementMoveToPoint = 0, @@ -48,47 +37,9 @@ typedef ORB_ENUM(int32_t, ORBRoundedCornerStyle) { /// Returns true to stop enumeration, false to continue typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); -/// Callback function pointer types for ORBPathCallbacks -typedef void (* _Nullable ORBPathRetainCallback)(ORBPathRef path); -typedef void (* _Nullable ORBPathReleaseCallback)(ORBPathRef path); -typedef bool (* _Nullable ORBPathApplyFunction)(ORBPathRef path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); -typedef bool (* _Nullable ORBPathIsEqualCallback)(ORBPathRef path, ORBPathRef otherPath); -typedef bool (* _Nullable ORBPathIsEmptyCallback)(ORBPathRef path); -typedef bool (* _Nullable ORBPathIsSingleRectCallback)(ORBPathRef path); -typedef uint32_t (* _Nullable ORBPathBezierOrderCallback)(ORBPathRef path); -#if ORB_TARGET_OS_DARWIN -typedef CGRect (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); -typedef CGPathRef _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); -#else -typedef void (* _Nullable ORBPathBoundingBoxCallback)(ORBPathRef path); -typedef void * _Nullable (* _Nullable ORBPathGetCGPathCallback)(ORBPathRef path); -#endif - -/// Callbacks structure for path operations -/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations -typedef struct ORBPathCallbacks { - void * _Nullable reserved; // 0x00: Reserved for future use - ORBPathRetainCallback retain; // 0x08: Retain callback - ORBPathReleaseCallback release; // 0x10: Release callback - ORBPathApplyFunction apply; // 0x18: Enumerate path elements - ORBPathIsEqualCallback isEqual; // 0x20: Compare two paths - ORBPathIsEmptyCallback isEmpty; // 0x28: Check if path is empty - ORBPathIsSingleRectCallback isSingleRect; // 0x30: Check if path is a single rectangle - ORBPathBezierOrderCallback bezierOrder; // 0x38: Get bezier order (1=linear, 2=quad, 3=cubic) - ORBPathBoundingBoxCallback boundingBox; // 0x40: Get bounding box - ORBPathGetCGPathCallback cgPath; // 0x48: Get CGPath representation - void * _Nullable reserved2; // 0x50: Reserved for future use -} ORBPathCallbacks; - -/// Global empty path callbacks (all null) -ORB_EXPORT -const ORBPathCallbacks ORBPathEmptyCallbacks; +struct ORBPathCallbacks; -#if ORB_TARGET_OS_DARWIN -/// Global callbacks for CGPath-backed paths -ORB_EXPORT -const ORBPathCallbacks ORBPathCGPathCallbacks; -#endif +typedef struct ORBPathStorage * ORBPathStorageRef ORB_SWIFT_STRUCT ORB_SWIFT_NAME(ORBPath.Storage); typedef struct ORBPath { ORBPathStorageRef storage; @@ -146,7 +97,4 @@ bool ORBPathContainsPoints(ORBPath path, uint64_t count, const CGPoint *points, ORB_EXTERN_C_END -ORB_IMPLICIT_BRIDGING_DISABLED - ORB_ASSUME_NONNULL_END - diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h new file mode 100644 index 0000000..c82ebf2 --- /dev/null +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -0,0 +1,50 @@ +// +// ORBPathCallbacks.h +// OpenRenderBox + +#pragma once + +#include +#include +#if ORB_TARGET_OS_DARWIN +#include +#endif + +ORB_ASSUME_NONNULL_BEGIN + +ORB_EXTERN_C_BEGIN + +/// Callbacks structure for path operations +/// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations +typedef struct ORBPathCallbacks { + void * _Nullable reserved; // 0x00: Reserved for future use + + void (* _Nullable retain)(const ORBPath *path); // 0x08 + void (* _Nullable release)(const ORBPath *path); // 0x10 + bool (* _Nullable apply)(const ORBPath *path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); // 0x18 + bool (* _Nullable isEqual)(const ORBPath *path, const ORBPath *otherPath); // 0x20 + bool (* _Nullable isEmpty)(const ORBPath *path); // 0x28 + bool (* _Nullable isSingleRect)(const ORBPath *path); // 0x30 + uint32_t (* _Nullable bezierOrder)(const ORBPath *path); // 0x38 + CGRect (* _Nullable boundingBox)(const ORBPath *path); // 0x40 + #if ORB_TARGET_OS_DARWIN + CGPathRef _Nullable (* _Nullable cgPath)(const ORBPath *path); // 0x48 + #else + void * _Nullable (* _Nullable cgPath)(const ORBPath *path); // 0x48 + #endif + void * _Nullable reserved2; // 0x50 +} ORBPathCallbacks ORB_SWIFT_NAME(ORBPath.Callbacks); + +/// Global empty path callbacks (all null) +ORB_EXPORT +const ORBPathCallbacks ORBPathEmptyCallbacks; + +#if ORB_TARGET_OS_DARWIN +/// Global callbacks for CGPath-backed paths +ORB_EXPORT +const ORBPathCallbacks ORBPathCGPathCallbacks; +#endif + +ORB_EXTERN_C_END + +ORB_ASSUME_NONNULL_END diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index fac136f..369450b 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -6,7 +6,6 @@ #include #include - #if ORB_TARGET_OS_DARWIN #include #endif From 883fb0ede57e93da0a8f153a0d8454db659fef33 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 23:14:52 +0800 Subject: [PATCH 19/30] Update swift_name for global object --- .../include/OpenRenderBox/ORBPath.h | 16 ++-- .../include/OpenRenderBox/ORBPathCallbacks.h | 4 +- .../PathCallbacksTests.swift | 73 +++++++++++++++++++ .../{BoxShims.swift => Shims.swift} | 3 +- 4 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift rename Tests/OpenRenderBoxCompatibilityTests/{BoxShims.swift => Shims.swift} (78%) diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index fb35825..bb0dfcf 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -26,12 +26,12 @@ typedef ORB_ENUM(int32_t, ORBPathElement) { }; /// Defines the shape of a rounded rectangle's corners. -typedef ORB_ENUM(int32_t, ORBRoundedCornerStyle) { +typedef ORB_ENUM(int32_t, ORBPathRoundedCornerStyle) { /// Quarter-circle rounded rect corners. - ORBRoundedCornerStyleCircular = 0, + ORBPathRoundedCornerStyleCircular = 0, /// Continuous curvature rounded rect corners. - ORBRoundedCornerStyleContinuous = 1, -}; + ORBPathRoundedCornerStyleContinuous = 1, +} ORB_SWIFT_NAME(ORBPath.RoundedCornerStyle); /// Callback type for path element enumeration /// Returns true to stop enumeration, false to continue @@ -48,11 +48,11 @@ typedef struct ORBPath { /// Global empty path (storage = null, callbacks = &ORBPathEmptyCallbacks) ORB_EXPORT -const ORBPath ORBPathEmpty; +const ORBPath ORBPathEmpty ORB_SWIFT_NAME(ORBPath.empty); /// Global null path (storage = 0x1, callbacks = &ORBPathEmptyCallbacks) ORB_EXPORT -const ORBPath ORBPathNull; +const ORBPath ORBPathNull ORB_SWIFT_NAME(ORBPath.null); ORB_EXPORT ORB_REFINED_FOR_SWIFT @@ -76,10 +76,10 @@ ORB_EXPORT ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(ellipseIn:transform:)); ORB_EXPORT -ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, ORBRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:cornerWidth:cornerHeight:style:transform:)); +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, ORBPathRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:cornerWidth:cornerHeight:style:transform:)); ORB_EXPORT -ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:style:transform:)); +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBPathRoundedCornerStyle style, const CGAffineTransform * _Nullable transform) ORB_SWIFT_NAME(ORBPath.init(roundedRect:topLeftRadius:bottomLeftRadius:bottomRightRadius:topRightRadius:style:transform:)); // MARK: - CGPath Interoperability diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h index c82ebf2..98f1fdd 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -37,12 +37,12 @@ typedef struct ORBPathCallbacks { /// Global empty path callbacks (all null) ORB_EXPORT -const ORBPathCallbacks ORBPathEmptyCallbacks; +const ORBPathCallbacks ORBPathEmptyCallbacks ORB_SWIFT_NAME(ORBPathCallbacks.empty); #if ORB_TARGET_OS_DARWIN /// Global callbacks for CGPath-backed paths ORB_EXPORT -const ORBPathCallbacks ORBPathCGPathCallbacks; +const ORBPathCallbacks ORBPathCGPathCallbacks ORB_SWIFT_NAME(ORBPathCallbacks.cgPath); #endif ORB_EXTERN_C_END diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift new file mode 100644 index 0000000..0e4bdf7 --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift @@ -0,0 +1,73 @@ +// +// PathCallbacksTests.swift +// OpenRenderBoxCompatibilityTests + +import Testing + +#if canImport(CoreGraphics) +import CoreGraphics + +@Suite(.enabled(if: compatibilityTestEnabled)) +struct PathCallbacksTests { + @Test + func emptyCallbacks() { + let callbacks = ORBPathEmptyCallbacks + #expect(callbacks.reserved == nil) + #expect(callbacks.retain == nil) + #expect(callbacks.release == nil) + #expect(callbacks.apply == nil) + #expect(callbacks.isEqual == nil) + #expect(callbacks.isEmpty == nil) + #expect(callbacks.isSingleRect == nil) + #expect(callbacks.bezierOrder == nil) + #expect(callbacks.boundingBox == nil) + #expect(callbacks.cgPath == nil) + #expect(callbacks.reserved2 == nil) + } + + @Test + func cgPathCallbacks() { + let callbacks = ORBPathCGPathCallbacks + #expect(callbacks.reserved == nil) + #expect(callbacks.retain != nil) + #expect(callbacks.release != nil) + #expect(callbacks.apply != nil) + #expect(callbacks.isEqual != nil) + #expect(callbacks.isEmpty != nil) + #expect(callbacks.isSingleRect != nil) + #expect(callbacks.bezierOrder != nil) + #expect(callbacks.boundingBox != nil) + #expect(callbacks.cgPath != nil) + #expect(callbacks.reserved2 == nil) + } + + @Test + func pathUsesCallbacks() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(rect: rect, transform: nil) + #expect(path.callbacks == withUnsafePointer(to: ORBPathCGPathCallbacks) { $0 }) + path.release() + } + + @Test + func emptyPathUsesEmptyCallbacks() { + let emptyPath = ORBPathEmpty + #expect(emptyPath.callbacks == withUnsafePointer(to: ORBPathEmptyCallbacks) { $0 }) + } + + @Test + func nullPathUsesEmptyCallbacks() { + let nullPath = ORBPathNull + #expect(nullPath.callbacks == withUnsafePointer(to: ORBPathEmptyCallbacks) { $0 }) + } + + @Test + func callbacksStructSize() { + // ORBPathCallbacks should be 11 pointers (0x58 bytes on 64-bit) + let expectedSize = 11 * MemoryLayout.size + #expect(MemoryLayout.size == expectedSize) + } +} + +#endif + diff --git a/Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift similarity index 78% rename from Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift rename to Tests/OpenRenderBoxCompatibilityTests/Shims.swift index 7f6f89b..7f51306 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/BoxShims.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift @@ -1,12 +1,11 @@ // -// BoxShims.swift +// Shims.swift // OpenRenderBoxShims #if OPENRENDERBOX_COMPATIBILITY_TEST @_exported public import RenderBox public typealias ORBUUID = RBUUID public typealias ORBPath = RBPath -public typealias ORBRoundedCornerStyle = RBRoundedCornerStyle public let compatibilityTestEnabled = true #else @_exported import OpenRenderBox From 8a375f4900cf8647a0c27d7a2f662362ef0d0ae4 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 23:17:32 +0800 Subject: [PATCH 20/30] Add missing header include --- Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h b/Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h index 27b00d2..75bc534 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/OpenRenderBox.h @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From 0189eafa9dd4fe39b58fa52f54864c9ae3aa7635 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 23:30:52 +0800 Subject: [PATCH 21/30] Update callbacks --- Sources/OpenRenderBox/Path/ORBPath.cpp | 7 ++- .../include/OpenRenderBox/ORBPath.h | 2 +- .../include/OpenRenderBox/ORBPathCallbacks.h | 8 +--- .../PathCallbacksTests.swift | 44 ++----------------- 4 files changed, 11 insertions(+), 50 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 0e51147..e5f25d0 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -8,6 +8,9 @@ #include #include +/// Global empty path callbacks (all null) +ORB_EXPORT const ORBPathCallbacks ORBPathEmptyCallbacks; + // Empty path (storage = null) const ORBPath ORBPathEmpty = { nullptr, @@ -69,7 +72,7 @@ ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform *transform) { return path; } -ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, ORBRoundedCornerStyle style, const CGAffineTransform *transform) { +ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, ORBPathRoundedCornerStyle style, const CGAffineTransform *transform) { // TODO: Handle ORBRoundedCornerStyleContinuous with custom bezier curves CGPathRef cgPath = CGPathCreateWithRoundedRect(rect, cornerWidth, cornerHeight, transform); ORBPath path = { @@ -79,7 +82,7 @@ ORBPath ORBPathMakeRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerH return path; } -ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBRoundedCornerStyle style, const CGAffineTransform *transform) { +ORBPath ORBPathMakeUnevenRoundedRect(CGRect rect, CGFloat topLeftRadius, CGFloat bottomLeftRadius, CGFloat bottomRightRadius, CGFloat topRightRadius, ORBPathRoundedCornerStyle style, const CGAffineTransform *transform) { // TODO: Handle ORBRoundedCornerStyleContinuous with custom bezier curves CGMutablePathRef cgPath = CGPathCreateMutable(); diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index bb0dfcf..e95e6c4 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -37,7 +37,7 @@ typedef ORB_ENUM(int32_t, ORBPathRoundedCornerStyle) { /// Returns true to stop enumeration, false to continue typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); -struct ORBPathCallbacks; +typedef struct ORBPathCallbacks ORBPathCallbacks; typedef struct ORBPathStorage * ORBPathStorageRef ORB_SWIFT_STRUCT ORB_SWIFT_NAME(ORBPath.Storage); diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h index 98f1fdd..4f8b295 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -16,7 +16,7 @@ ORB_EXTERN_C_BEGIN /// Callbacks structure for path operations /// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations -typedef struct ORBPathCallbacks { +typedef struct ORB_SWIFT_NAME(ORBPath.Callbacks) ORBPathCallbacks { void * _Nullable reserved; // 0x00: Reserved for future use void (* _Nullable retain)(const ORBPath *path); // 0x08 @@ -33,11 +33,7 @@ typedef struct ORBPathCallbacks { void * _Nullable (* _Nullable cgPath)(const ORBPath *path); // 0x48 #endif void * _Nullable reserved2; // 0x50 -} ORBPathCallbacks ORB_SWIFT_NAME(ORBPath.Callbacks); - -/// Global empty path callbacks (all null) -ORB_EXPORT -const ORBPathCallbacks ORBPathEmptyCallbacks ORB_SWIFT_NAME(ORBPathCallbacks.empty); +} ORBPathCallbacks; #if ORB_TARGET_OS_DARWIN /// Global callbacks for CGPath-backed paths diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift index 0e4bdf7..c194433 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift @@ -9,25 +9,9 @@ import CoreGraphics @Suite(.enabled(if: compatibilityTestEnabled)) struct PathCallbacksTests { - @Test - func emptyCallbacks() { - let callbacks = ORBPathEmptyCallbacks - #expect(callbacks.reserved == nil) - #expect(callbacks.retain == nil) - #expect(callbacks.release == nil) - #expect(callbacks.apply == nil) - #expect(callbacks.isEqual == nil) - #expect(callbacks.isEmpty == nil) - #expect(callbacks.isSingleRect == nil) - #expect(callbacks.bezierOrder == nil) - #expect(callbacks.boundingBox == nil) - #expect(callbacks.cgPath == nil) - #expect(callbacks.reserved2 == nil) - } - @Test func cgPathCallbacks() { - let callbacks = ORBPathCGPathCallbacks + let callbacks = ORBPath.Callbacks.cgPath #expect(callbacks.reserved == nil) #expect(callbacks.retain != nil) #expect(callbacks.release != nil) @@ -40,32 +24,10 @@ struct PathCallbacksTests { #expect(callbacks.cgPath != nil) #expect(callbacks.reserved2 == nil) } - - @Test - func pathUsesCallbacks() { - let rect = CGRect(x: 0, y: 0, width: 100, height: 100) - let path = ORBPath(rect: rect, transform: nil) - #expect(path.callbacks == withUnsafePointer(to: ORBPathCGPathCallbacks) { $0 }) - path.release() - } - - @Test - func emptyPathUsesEmptyCallbacks() { - let emptyPath = ORBPathEmpty - #expect(emptyPath.callbacks == withUnsafePointer(to: ORBPathEmptyCallbacks) { $0 }) - } - - @Test - func nullPathUsesEmptyCallbacks() { - let nullPath = ORBPathNull - #expect(nullPath.callbacks == withUnsafePointer(to: ORBPathEmptyCallbacks) { $0 }) - } - + @Test func callbacksStructSize() { - // ORBPathCallbacks should be 11 pointers (0x58 bytes on 64-bit) - let expectedSize = 11 * MemoryLayout.size - #expect(MemoryLayout.size == expectedSize) + #expect(MemoryLayout.size == 11 * MemoryLayout.size) } } From 1dc931d9b2d46e7fd8a6939f6f1dd73a600692c8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 21 Dec 2025 23:39:28 +0800 Subject: [PATCH 22/30] Fix ORBPathEmptyCallbacks --- Sources/OpenRenderBox/Path/ORBPath.cpp | 20 +++++++++++++++---- .../OpenRenderBox/Path/ORBPathCallbacks.cpp | 15 -------------- .../Path/PathTests.swift | 2 +- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index e5f25d0..42082dd 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -8,19 +8,31 @@ #include #include -/// Global empty path callbacks (all null) -ORB_EXPORT const ORBPathCallbacks ORBPathEmptyCallbacks; +// Empty path callbacks (all null) - C++ internal linkage +static const ORBPathCallbacks empty_path_callbacks = { + nullptr, // reserved + nullptr, // retain + nullptr, // release + nullptr, // apply + nullptr, // isEqual + nullptr, // isEmpty + nullptr, // isSingleRect + nullptr, // bezierOrder + nullptr, // boundingBox + nullptr, // cgPath + nullptr, // reserved2 +}; // Empty path (storage = null) const ORBPath ORBPathEmpty = { nullptr, - &ORBPathEmptyCallbacks, + &empty_path_callbacks, }; // Null path (storage = 0x1) const ORBPath ORBPathNull = { reinterpret_cast(0x1), - &ORBPathEmptyCallbacks, + &empty_path_callbacks, }; void ORBPathRetain(ORBPath path) { diff --git a/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp b/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp index fe5455b..a3c48d6 100644 --- a/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp @@ -5,21 +5,6 @@ #include #include -// Empty path callbacks (all null) -const ORBPathCallbacks ORBPathEmptyCallbacks = { - nullptr, // reserved - nullptr, // retain - nullptr, // release - nullptr, // apply - nullptr, // isEqual - nullptr, // isEmpty - nullptr, // isSingleRect - nullptr, // bezierOrder - nullptr, // boundingBox - nullptr, // cgPath - nullptr, // reserved2 -}; - #if ORB_TARGET_OS_DARWIN // FIXME: Not implemented correctly const ORBPathCallbacks ORBPathCGPathCallbacks = { diff --git a/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift b/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift index b39eb15..e0b5f95 100644 --- a/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift +++ b/Tests/OpenRenderBoxCxxTests/Path/PathTests.swift @@ -13,7 +13,7 @@ import Testing struct PathTests { @Test func demo() { - let storage = ORB.Path.Storage(3) + _ = ORB.Path.Storage(96) } } #endif From e1691659beb6d198457cb9901d0aad4cbd1dcc8f Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 00:39:22 +0800 Subject: [PATCH 23/30] Complete ORBPathCallbacks implementation --- Sources/OpenRenderBox/Path/ORBPath.cpp | 6 +- .../OpenRenderBox/Path/ORBPathCallbacks.cpp | 135 +++++++----------- .../include/OpenRenderBox/ORBPath.h | 2 +- .../include/OpenRenderBox/ORBPathCallbacks.h | 25 ++-- 4 files changed, 67 insertions(+), 101 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 42082dd..9e61e64 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -10,17 +10,17 @@ // Empty path callbacks (all null) - C++ internal linkage static const ORBPathCallbacks empty_path_callbacks = { - nullptr, // reserved + nullptr, // unknown1 nullptr, // retain nullptr, // release nullptr, // apply nullptr, // isEqual nullptr, // isEmpty - nullptr, // isSingleRect + nullptr, // isSingleElement nullptr, // bezierOrder nullptr, // boundingBox nullptr, // cgPath - nullptr, // reserved2 + nullptr, // unknown2 }; // Empty path (storage = null) diff --git a/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp b/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp index a3c48d6..a2c96a5 100644 --- a/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathCallbacks.cpp @@ -1,108 +1,75 @@ // // ORBPathCallbacks.cpp // OpenRenderBox +// +// Audited for 6.5.1 +// Status: Complete #include #include #if ORB_TARGET_OS_DARWIN -// FIXME: Not implemented correctly + +CG_EXTERN size_t CGPathGetNumberOfPoints(CGPathRef cg_nullable path); +CG_EXTERN size_t CGPathGetNumberOfElements(CGPathRef cg_nullable path); +typedef void (^CGPathApplyBlock2)(const CGPathElement * element, bool *stop); +CG_EXTERN void CGPathApplyWithBlock2(CGPathRef path, CGPathApplyBlock2 CF_NOESCAPE block); + +namespace { + uint32_t cgpath_bezier_order(CGPathRef cgPath) { + size_t pointsCount = CGPathGetNumberOfPoints(cgPath); + size_t elementsCount = CGPathGetNumberOfElements(cgPath); + if (pointsCount > elementsCount) { return 3; } + if (pointsCount != 3 || elementsCount != 3) { return 1; } + bool hasCurve = false; + CGPathApply(cgPath, &hasCurve, [](void *info, const CGPathElement *element) { + if (element->type == kCGPathElementAddQuadCurveToPoint + || element->type == kCGPathElementAddCurveToPoint) { + *static_cast(info) = true; + } + }); + return hasCurve ? 3 : 1; + } +} + const ORBPathCallbacks ORBPathCGPathCallbacks = { - nullptr, // reserved - // retain - +[](const ORBPath *path) -> void { - CFRetain(path->storage); - }, - // release - +[](const ORBPath *path) -> void { - CFRelease(path->storage); - }, - // apply - +[](const ORBPath *path, void *info, ORBPathApplyCallback callback) -> bool { - CGPathRef cgPath = reinterpret_cast(path->storage); + nullptr, + CFRetain, + CFRelease, + +[](const void *object, void *info, ORBPathApplyCallback callback) -> bool { + CGPathRef cgPath = reinterpret_cast(object); __block bool shouldStop = false; - CGPathApplyWithBlock(cgPath, ^(const CGPathElement *element) { - if (shouldStop) return; - ORBPathElement orbElement; - double pointBuffer[6]; - double *points = nullptr; - switch (element->type) { - case kCGPathElementMoveToPoint: - orbElement = ORBPathElementMoveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - points = pointBuffer; - break; - case kCGPathElementAddLineToPoint: - orbElement = ORBPathElementAddLineToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - points = pointBuffer; - break; - case kCGPathElementAddQuadCurveToPoint: - orbElement = ORBPathElementAddQuadCurveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - pointBuffer[2] = element->points[1].x; - pointBuffer[3] = element->points[1].y; - points = pointBuffer; - break; - case kCGPathElementAddCurveToPoint: - orbElement = ORBPathElementAddCurveToPoint; - pointBuffer[0] = element->points[0].x; - pointBuffer[1] = element->points[0].y; - pointBuffer[2] = element->points[1].x; - pointBuffer[3] = element->points[1].y; - pointBuffer[4] = element->points[2].x; - pointBuffer[5] = element->points[2].y; - points = pointBuffer; - break; - case kCGPathElementCloseSubpath: - orbElement = ORBPathElementCloseSubpath; - points = nullptr; - break; - } - if (callback != nullptr) { - shouldStop = callback(info, orbElement, points, nullptr); + CGPathApplyWithBlock2(cgPath, ^(const CGPathElement *element, bool *stop) { + bool result = callback(info, + (ORBPathElement)element->type, + (CGFloat *)element->points, + nullptr); + if (!result) { + *stop = true; + shouldStop = true; } }); return !shouldStop; }, - // isEqual - +[](const ORBPath *path, const ORBPath *otherPath) -> bool { - return CGPathEqualToPath(reinterpret_cast(path->storage), reinterpret_cast(otherPath->storage)); + +[](const void *object, const void *otherObject) -> bool { + return CGPathEqualToPath(static_cast(object), static_cast(otherObject)); }, - // isEmpty - +[](const ORBPath *path) -> bool { - return CGPathIsEmpty(reinterpret_cast(path->storage)); + +[](const void *object) -> bool { + return CGPathIsEmpty(static_cast(object)); }, - // isSingleRect - CGPath doesn't have this, always return false - +[](const ORBPath *path) -> bool { + +[](const void *object) -> bool { return false; }, - // bezierOrder - +[](const ORBPath *path) -> uint32_t { - CGPathRef cgPath = reinterpret_cast(path->storage); - __block uint32_t order = 1; - CGPathApply(cgPath, &order, [](void *info, const CGPathElement *element) { - uint32_t *orderPtr = static_cast(info); - if (element->type == kCGPathElementAddCurveToPoint) { - *orderPtr = 3; - } else if (element->type == kCGPathElementAddQuadCurveToPoint && *orderPtr < 3) { - *orderPtr = 2; - } - }); - return order; + +[](const void *object) -> uint32_t { + return cgpath_bezier_order(static_cast(object)); }, - // boundingBox - +[](const ORBPath *path) -> CGRect { - return CGPathGetPathBoundingBox(reinterpret_cast(path->storage)); + +[](const void *object) -> CGRect { + return CGPathGetPathBoundingBox(static_cast(object)); }, - // cgPath - return the CGPath itself - +[](const ORBPath *path) -> CGPathRef { - return reinterpret_cast(path->storage); + +[](const void *object) -> CGPathRef { + return static_cast(object); }, - nullptr, // reserved2 + nullptr, }; #endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index e95e6c4..4377fba 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -35,7 +35,7 @@ typedef ORB_ENUM(int32_t, ORBPathRoundedCornerStyle) { /// Callback type for path element enumeration /// Returns true to stop enumeration, false to continue -typedef bool (*ORBPathApplyCallback)(void * _Nullable info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); +typedef bool (*ORBPathApplyCallback)(void * info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); typedef struct ORBPathCallbacks ORBPathCallbacks; diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h index 4f8b295..e6de861 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -17,22 +17,21 @@ ORB_EXTERN_C_BEGIN /// Callbacks structure for path operations /// This allows different path storage types (CGPath, custom storage, etc.) to provide their own implementations typedef struct ORB_SWIFT_NAME(ORBPath.Callbacks) ORBPathCallbacks { - void * _Nullable reserved; // 0x00: Reserved for future use - - void (* _Nullable retain)(const ORBPath *path); // 0x08 - void (* _Nullable release)(const ORBPath *path); // 0x10 - bool (* _Nullable apply)(const ORBPath *path, void * _Nullable info, ORBPathApplyCallback _Nullable callback); // 0x18 - bool (* _Nullable isEqual)(const ORBPath *path, const ORBPath *otherPath); // 0x20 - bool (* _Nullable isEmpty)(const ORBPath *path); // 0x28 - bool (* _Nullable isSingleRect)(const ORBPath *path); // 0x30 - uint32_t (* _Nullable bezierOrder)(const ORBPath *path); // 0x38 - CGRect (* _Nullable boundingBox)(const ORBPath *path); // 0x40 + void (* _Nullable unknown1)(const void * object); // 0x00 + const void * _Nonnull (* _Nullable retain)(const void *object); // 0x08 + void (* _Nullable release)(const void *object); // 0x10 + bool (* _Nullable apply)(const void *object, void * info, ORBPathApplyCallback _Nullable callback); // 0x18 + bool (* _Nullable isEqual)(const void *object, const void *otherObject); // 0x20 + bool (* _Nullable isEmpty)(const void *object); // 0x28 + bool (* _Nullable isSingleElement)(const void *object); // 0x30 + uint32_t (* _Nullable bezierOrder)(const void *object); // 0x38 + CGRect (* _Nullable boundingBox)(const void *object); // 0x40 #if ORB_TARGET_OS_DARWIN - CGPathRef _Nullable (* _Nullable cgPath)(const ORBPath *path); // 0x48 + CGPathRef _Nullable (* _Nullable cgPath)(const void *object); // 0x48 #else - void * _Nullable (* _Nullable cgPath)(const ORBPath *path); // 0x48 + void * _Nullable (* _Nullable cgPath)(const void *object); // 0x48 #endif - void * _Nullable reserved2; // 0x50 + void (* _Nullable unknown2)(const void *); // 0x50 } ORBPathCallbacks; #if ORB_TARGET_OS_DARWIN From 3bb6875fec81cd40085958a05c223c005181399c Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 01:18:44 +0800 Subject: [PATCH 24/30] Add ORBPathStorageApplyElements API --- Package.resolved | 2 +- Sources/OpenRenderBox/Path/ORBPathStorage.cpp | 4 ++++ .../include/OpenRenderBox/ORBPathCallbacks.h | 3 +++ .../OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h | 9 ++++++++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Package.resolved b/Package.resolved index 72dfccd..cf03bef 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "77b14da56d67bb9354fb38d4a97117f83dfa94752d93103f728d617a6fda088a", + "originHash" : "690a05887e434b4960ff5a0cb3c6ff7828728045e61fb5fcc5094b76e1d28222", "pins" : [ { "identity" : "darwinprivateframeworks", diff --git a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp index be9ab52..25d2966 100644 --- a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp @@ -32,6 +32,10 @@ void ORBPathStorageAppendPath(ORBPathStorageRef storage, ORBPath path) { precondition_failure("TODO"); } +void ORBPathStorageApplyElements(ORBPathStorageRef, void *info, ORBPathApplyCallback callback) { + precondition_failure("TODO"); +} + bool ORBPathStorageIsEmpty(ORBPathStorageRef storage) { return storage->storage.isEmpty(); } diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h index e6de861..c035265 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -1,6 +1,9 @@ // // ORBPathCallbacks.h // OpenRenderBox +// +// Audited for 6.5.1 +// Status: Complete #pragma once diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index 369450b..0afa4cf 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -1,6 +1,9 @@ // // ORBPathStorage.h // OpenRenderBox +// +// Audited for 6.5.1 +// Status: Complete #pragma once @@ -34,13 +37,17 @@ ORB_EXPORT ORB_REFINED_FOR_SWIFT void ORBPathStorageAppendPath(ORBPathStorageRef, ORBPath) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:path:)); +ORB_EXPORT +ORB_REFINED_FOR_SWIFT +void ORBPathStorageApplyElements(ORBPathStorageRef, void *info, ORBPathApplyCallback callback) ORB_SWIFT_NAME(ORBPathStorageRef.applyElements(self:info:callback:)); + ORB_EXPORT ORB_REFINED_FOR_SWIFT bool ORBPathStorageIsEmpty(ORBPathStorageRef storage) ORB_SWIFT_NAME(getter:ORBPathStorageRef.isEmpty(self:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT -bool ORBPathStorageEqualToStorage(ORBPathStorageRef lhs, ORBPathStorageRef rhs) ORB_SWIFT_NAME(ORBPathStorageRef.isEqualTo(self:_:)); +bool ORBPathStorageEqualToStorage(ORBPathStorageRef lhs, ORBPathStorageRef rhs) ORB_SWIFT_NAME(ORBPathStorageRef.isEqual(self:to:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT From 497382471f28818fdab6454a604d9cb42277871a Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 01:26:15 +0800 Subject: [PATCH 25/30] Update Callbacks API name to boundingRect --- Sources/OpenRenderBox/Path/ORBPath.cpp | 22 +++++++++---------- Sources/OpenRenderBox/Path/ORBPathStorage.cpp | 2 +- .../include/OpenRenderBox/ORBPathCallbacks.h | 2 +- .../include/OpenRenderBox/ORBPathStorage.h | 4 ++-- .../PathCallbacksTests.swift | 8 +++---- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 9e61e64..2aadf2e 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -10,17 +10,17 @@ // Empty path callbacks (all null) - C++ internal linkage static const ORBPathCallbacks empty_path_callbacks = { - nullptr, // unknown1 - nullptr, // retain - nullptr, // release - nullptr, // apply - nullptr, // isEqual - nullptr, // isEmpty - nullptr, // isSingleElement - nullptr, // bezierOrder - nullptr, // boundingBox - nullptr, // cgPath - nullptr, // unknown2 + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, }; // Empty path (storage = null) diff --git a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp index 25d2966..7164798 100644 --- a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp @@ -24,7 +24,7 @@ void ORBPathStorageClear(ORBPathStorageRef storage) { storage->storage.clear(); } -void ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, double const * points, const void * userInfo) { +bool ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, double const * points, const void * userInfo) { precondition_failure("TODO"); } diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h index c035265..39c54bf 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -28,7 +28,7 @@ typedef struct ORB_SWIFT_NAME(ORBPath.Callbacks) ORBPathCallbacks { bool (* _Nullable isEmpty)(const void *object); // 0x28 bool (* _Nullable isSingleElement)(const void *object); // 0x30 uint32_t (* _Nullable bezierOrder)(const void *object); // 0x38 - CGRect (* _Nullable boundingBox)(const void *object); // 0x40 + CGRect (* _Nullable boundingRect)(const void *object); // 0x40 #if ORB_TARGET_OS_DARWIN CGPathRef _Nullable (* _Nullable cgPath)(const void *object); // 0x48 #else diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h index 0afa4cf..3350f9c 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -31,7 +31,7 @@ void ORBPathStorageClear(ORBPathStorageRef storage) ORB_SWIFT_NAME(ORBPathStorag ORB_EXPORT ORB_REFINED_FOR_SWIFT -void ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, CGFloat const * points, const void * _Nullable userInfo) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:element:points:userInfo:)); +bool ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, CGFloat const * points, const void * _Nullable userInfo) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:element:points:userInfo:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT @@ -39,7 +39,7 @@ void ORBPathStorageAppendPath(ORBPathStorageRef, ORBPath) ORB_SWIFT_NAME(ORBPath ORB_EXPORT ORB_REFINED_FOR_SWIFT -void ORBPathStorageApplyElements(ORBPathStorageRef, void *info, ORBPathApplyCallback callback) ORB_SWIFT_NAME(ORBPathStorageRef.applyElements(self:info:callback:)); +void ORBPathStorageApplyElements(ORBPathStorageRef, void *info, ORBPathApplyCallback _Nullable callback) ORB_SWIFT_NAME(ORBPathStorageRef.apply(self:info:callback:)); ORB_EXPORT ORB_REFINED_FOR_SWIFT diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift index c194433..e046da2 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/PathCallbacksTests.swift @@ -12,17 +12,17 @@ struct PathCallbacksTests { @Test func cgPathCallbacks() { let callbacks = ORBPath.Callbacks.cgPath - #expect(callbacks.reserved == nil) + #expect(callbacks.unknown1 == nil) #expect(callbacks.retain != nil) #expect(callbacks.release != nil) #expect(callbacks.apply != nil) #expect(callbacks.isEqual != nil) #expect(callbacks.isEmpty != nil) - #expect(callbacks.isSingleRect != nil) + #expect(callbacks.isSingleElement != nil) #expect(callbacks.bezierOrder != nil) - #expect(callbacks.boundingBox != nil) + #expect(callbacks.boundingRect != nil) #expect(callbacks.cgPath != nil) - #expect(callbacks.reserved2 == nil) + #expect(callbacks.unknown2 == nil) } @Test From abbb946c4d2a0182f47f47a16ff61f1a8671811a Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 01:46:23 +0800 Subject: [PATCH 26/30] Update RenderBox dependency --- Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.resolved b/Package.resolved index cf03bef..bc372ce 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", "state" : { "branch" : "main", - "revision" : "53c0103e449cd0fecc931d8a1af63e7ff036d575" + "revision" : "145cfde7a6b78248622bb2cd267101a8083bb126" } }, { From 53a173c0cd3c0c05126bc4538f4ee341dea003d9 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 01:47:11 +0800 Subject: [PATCH 27/30] [Optimize] Remove empty line --- Tests/OpenRenderBoxCompatibilityTests/PathTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift index e2126c6..85f39bc 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/PathTests.swift @@ -55,7 +55,6 @@ struct PathTests { #expect(path.contains(point: insidePoint, eoFill: false) == true) #expect(path.contains(point: outsidePoint, eoFill: false) == false) path.release() - } } From d29b4ce62447802fc5a6c6c9d38ee6acb3411b63 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 02:02:07 +0800 Subject: [PATCH 28/30] Update build.sh --- Scripts/build.sh | 6 ++++++ 1 file changed, 6 insertions(+) mode change 100644 => 100755 Scripts/build.sh diff --git a/Scripts/build.sh b/Scripts/build.sh old mode 100644 new mode 100755 index 85dad82..3c46504 --- a/Scripts/build.sh +++ b/Scripts/build.sh @@ -9,4 +9,10 @@ OPENRENDERBOX_ROOT="$(dirname $(dirname $(filepath $0)))" cd $OPENRENDERBOX_ROOT +# Set OPENRENDERBOX_LIB_SWIFT_PATH on Linux if swiftly is installed +if [[ "$(uname)" == "Linux" ]] && command -v swiftly &> /dev/null && [[ -z "$OPENRENDERBOX_LIB_SWIFT_PATH" ]]; then + export OPENRENDERBOX_LIB_SWIFT_PATH="$(swiftly use --print-location)/usr/lib/swift" + echo "Set OPENRENDERBOX_LIB_SWIFT_PATH=$OPENRENDERBOX_LIB_SWIFT_PATH" +fi + swift build From c8ce4f118444631ecbdda5f32a72a04f173503be Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 02:02:15 +0800 Subject: [PATCH 29/30] Fix CGRect issue --- .../include/OpenRenderBox/CFCGTypes.h | 124 ++++++++++++++++++ .../include/OpenRenderBox/ORBBase.h | 3 + .../include/OpenRenderBox/ORBPathCallbacks.h | 1 + 3 files changed, 128 insertions(+) create mode 100644 Sources/OpenRenderBox/include/OpenRenderBox/CFCGTypes.h diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/CFCGTypes.h b/Sources/OpenRenderBox/include/OpenRenderBox/CFCGTypes.h new file mode 100644 index 0000000..1ca3d8d --- /dev/null +++ b/Sources/OpenRenderBox/include/OpenRenderBox/CFCGTypes.h @@ -0,0 +1,124 @@ +/* CFCGTypes.h + Copyright (c) 1998-2021, Apple Inc. and the Swift project authors + + Portions Copyright (c) 2014-2021, Apple Inc. and the Swift project authors + Licensed under Apache License v2.0 with Runtime Library Exception + See http://swift.org/LICENSE.txt for license information + See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +*/ + +#if !defined(__COREFOUNDATION_CFCGTYPES__) +#define __COREFOUNDATION_CFCGTYPES__ 1 + +#include +#include +#include +#include + +#define CF_DEFINES_CG_TYPES + +#if defined(__has_attribute) && __has_attribute(objc_boxable) +# define CF_BOXABLE __attribute__((objc_boxable)) +#else +# define CF_BOXABLE +#endif + +/* Definition of `CGFLOAT_TYPE', `CGFLOAT_IS_DOUBLE', `CGFLOAT_MIN', and + * `CGFLOAT_MAX'. */ + + #if defined(__LP64__) && __LP64__ + # define CGFLOAT_TYPE double + # define CGFLOAT_IS_DOUBLE 1 + # define CGFLOAT_MIN DBL_MIN + # define CGFLOAT_MAX DBL_MAX + # define CGFLOAT_EPSILON DBL_EPSILON + #else + # define CGFLOAT_TYPE float + # define CGFLOAT_IS_DOUBLE 0 + # define CGFLOAT_MIN FLT_MIN + # define CGFLOAT_MAX FLT_MAX + # define CGFLOAT_EPSILON FLT_EPSILON + #endif + +/* Definition of the `CGFloat' type and `CGFLOAT_DEFINED'. */ + +typedef CGFLOAT_TYPE CGFloat; +#define CGFLOAT_DEFINED 1 + +/* Points. */ +struct +CGPoint { + CGFloat x; + CGFloat y; +}; +typedef struct CF_BOXABLE CGPoint CGPoint; + +/* Sizes. */ + +struct CGSize { + CGFloat width; + CGFloat height; +}; +typedef struct CF_BOXABLE CGSize CGSize; + +/* Vectors. */ + +#define CGVECTOR_DEFINED 1 + +struct CGVector { + CGFloat dx; + CGFloat dy; +}; +typedef struct CF_BOXABLE CGVector CGVector; + +/* Rectangles. */ + +struct CGRect { + CGPoint origin; + CGSize size; +}; +typedef struct CF_BOXABLE CGRect CGRect; + +/* Rectangle edges. */ + +typedef CF_CLOSED_ENUM(uint32_t, CGRectEdge) { + CGRectMinXEdge, CGRectMinYEdge, CGRectMaxXEdge, CGRectMaxYEdge +}; + +typedef struct CGAffineTransform CGAffineTransform; + +struct CGAffineTransform { + CGFloat a, b, c, d; + CGFloat tx, ty; +}; + +#define CF_DEFINES_CGAFFINETRANSFORMCOMPONENTS + +/* |------------------ CGAffineTransformComponents ----------------| + * + * | a b 0 | | sx 0 0 | | 1 0 0 | | cos(t) sin(t) 0 | | 1 0 0 | + * | c d 0 | = | 0 sy 0 | * | sh 1 0 | * |-sin(t) cos(t) 0 | * | 0 1 0 | + * | tx ty 1 | | 0 0 1 | | 0 0 1 | | 0 0 1 | | tx ty 1 | + * CGAffineTransform scale shear rotation translation + */ +typedef struct CGAffineTransformComponents CGAffineTransformComponents + CF_SWIFT_NAME(CGAffineTransform.Components); + +struct CGAffineTransformComponents { + + /* initial scaling in X and Y dimensions. {sx,sy} */ + /* Negative values indicate the image has been flipped in this dimension. */ + CGSize scale; + + /* shear distortion (sh). Turns rectangles to parallelograms. 0 for no shear. Typically 0. */ + CGFloat horizontalShear; + + /* Rotation angle in radians about the origin. (t) Sign convention for clockwise rotation */ + /* may differ between various Apple frameworks based on origin placement. Please see discussion. */ + CGFloat rotation; + + /* Displacement from the origin (ty, ty) */ + CGVector translation; +}; + +#endif /* ! __COREFOUNDATION_CFCGTYPES__ */ diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h index 800a12d..e5710a8 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h @@ -66,3 +66,6 @@ #define ORB_ORBJC_FOUNDATION 0 #endif /* TARGET_OS_DARWIN && __ORBJC__ */ +#if !ORB_TRRET_OS_DARWIN +#include "CFCGTypes.h" +#endif \ No newline at end of file diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h index 39c54bf..ed62acc 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathCallbacks.h @@ -9,6 +9,7 @@ #include #include +#include #if ORB_TARGET_OS_DARWIN #include #endif From 23ad6013afff47400e910e818d73e0ea9458e7f3 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 22 Dec 2025 02:09:10 +0800 Subject: [PATCH 30/30] Fix isEqual(to:) --- .../PathStorageTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift index db7c0e6..aa2e15c 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift @@ -64,12 +64,12 @@ struct PathStorageTests { let path2 = ORBPath(rect: rect, transform: nil) let storage1 = path1.storage let storage2 = path2.storage - #expect(storage1.isEqualTo(storage2) == true) + #expect(storage1.isEqual(to: storage2) == true) storage1.initialize(capacity: 96, source: nil) storage1.append(path: path1) storage2.initialize(capacity: 96, source: nil) storage2.append(path: path2) - #expect(storage1.isEqualTo(storage2) == true) + #expect(storage1.isEqual(to: storage2) == true) storage1.destroy() storage2.destroy() path1.release() @@ -82,12 +82,12 @@ struct PathStorageTests { let path2 = ORBPath(rect: CGRect(x: 0, y: 0, width: 200, height: 200), transform: nil) let storage1 = path1.storage let storage2 = path2.storage - #expect(storage1.isEqualTo(storage2) == true) + #expect(storage1.isEqual(to: storage2) == true) storage1.initialize(capacity: 96, source: nil) storage1.append(path: path1) storage2.initialize(capacity: 96, source: nil) storage2.append(path: path2) - #expect(storage1.isEqualTo(storage2) == false) + #expect(storage1.isEqual(to: storage2) == false) storage1.destroy() storage2.destroy() path1.release()