diff --git a/.gitignore b/.gitignore index 2b3eccb..92559ae 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ DerivedData/ .netrc .claude/ .rb_template/ +.augment/ diff --git a/Package.resolved b/Package.resolved index bc372ce..bcbf67b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", "state" : { "branch" : "main", - "revision" : "145cfde7a6b78248622bb2cd267101a8083bb126" + "revision" : "e8090a2f05b2bba73e7772cde4427b3cd1bf1f0a" } }, { diff --git a/Sources/OpenRenderBox/Path/ORBPath.cpp b/Sources/OpenRenderBox/Path/ORBPath.cpp index 2aadf2e..eee22f7 100644 --- a/Sources/OpenRenderBox/Path/ORBPath.cpp +++ b/Sources/OpenRenderBox/Path/ORBPath.cpp @@ -7,6 +7,8 @@ #include #include +#include +#include // Empty path callbacks (all null) - C++ internal linkage static const ORBPathCallbacks empty_path_callbacks = { @@ -53,6 +55,21 @@ void ORBPathRelease(ORBPath path) { // MARK: - Path Creation +namespace { +ORBPath make_rect(CGRect rect, const CGAffineTransform *transform, ORBPathElement element) { + if (CGRectIsNull(rect)) { + return ORBPathNull; + } + if (transform == nullptr || CGAffineTransformIsIdentity(*transform)) { + // TODO + return ORBPathNull; + } else { + // TODO + return ORBPathNull; + } +} +} /* anonymous namespace */ + // TODO: TO be implemented natively ORBPath ORBPathMakeWithCGPath(CGPathRef cgPath) { @@ -67,12 +84,7 @@ ORBPath ORBPathMakeWithCGPath(CGPathRef cgPath) { } ORBPath ORBPathMakeRect(CGRect rect, const CGAffineTransform *transform) { - CGPathRef cgPath = CGPathCreateWithRect(rect, transform); - ORBPath path = { - reinterpret_cast(const_cast(cgPath)), - &ORBPathCGPathCallbacks, - }; - return path; + return make_rect(rect, transform, ORBPathElementRect); } ORBPath ORBPathMakeEllipse(CGRect rect, const CGAffineTransform *transform) { @@ -146,3 +158,19 @@ bool ORBPathContainsPoints(ORBPath path, uint64_t count, const CGPoint *points, } #endif /* ORB_TARGET_OS_DARWIN */ + +bool ORBPathApplyElements(ORBPath path, void *info, ORBPathApplyCallback callback) { + auto apply = path.callbacks->apply; + bool flag = false; // TODO: calllbacks's flag to indicate whether it supports extra features + if (flag) { + if (callback == nullptr) { + return true; + } + return apply(path.storage, info, callback/*, path.callbacks*/); + } else { + if (callback == nullptr) { + return true; + } + return apply(path.storage, info, callback); + } +} diff --git a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp index a8a42bb..9f2e480 100644 --- a/Sources/OpenRenderBox/Path/ORBPathStorage.cpp +++ b/Sources/OpenRenderBox/Path/ORBPathStorage.cpp @@ -8,6 +8,17 @@ using namespace ORB; +namespace ORB { +namespace Path { +namespace { +bool append_element_callback(void * info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo) { + reinterpret_cast(info)->append_element(element, points, userInfo); + return true; +} +} /* anonymous namespace */ +} /* Path */ +} /* ORB */ + void ORBPathStorageInit(ORBPathStorageRef dst, uint32_t capacity, ORBPathStorageRef source) { if (source != nullptr) { dst->storage = ORB::Path::Storage(capacity, source->storage); @@ -24,16 +35,16 @@ void ORBPathStorageClear(ORBPathStorageRef storage) { storage->storage.clear(); } -bool ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, double const * points, const void * userInfo) { - precondition_failure("TODO"); +bool ORBPathStorageAppendElement(ORBPathStorageRef storage, ORBPathElement element, const CGFloat * points, const void * userInfo) { + return storage->storage.append_element(element, points, userInfo); } -void ORBPathStorageAppendPath(ORBPathStorageRef storage, ORBPath path) { - precondition_failure("TODO"); +bool ORBPathStorageAppendPath(ORBPathStorageRef storage, ORBPath path) { + return ORBPathApplyElements(path, storage, ORB::Path::append_element_callback); } -bool ORBPathStorageApplyElements(ORBPathStorageRef, void *info, ORBPathApplyCallback callback) { - precondition_failure("TODO"); +bool ORBPathStorageApplyElements(ORBPathStorageRef storage, void *info, ORBPathApplyCallback callback) { + return storage->storage.apply_elements(info, callback); } bool ORBPathStorageIsEmpty(ORBPathStorageRef storage) { @@ -59,6 +70,7 @@ CGRect ORBPathStorageGetBoundingRect(ORBPathStorageRef storage) { } CGPathRef ORBPathStorageGetCGPath(ORBPathStorageRef storage) { - precondition_failure("TODO"); + // FIXME + return storage->storage.cgpath(); } #endif diff --git a/Sources/OpenRenderBox/Path/PathStorage.cpp b/Sources/OpenRenderBox/Path/PathStorage.cpp index c75a81f..b7d56bc 100644 --- a/Sources/OpenRenderBox/Path/PathStorage.cpp +++ b/Sources/OpenRenderBox/Path/PathStorage.cpp @@ -2,6 +2,8 @@ // PathStorage.cpp // OpenRenderBox +#include +#include #include #include @@ -11,7 +13,7 @@ namespace ORB { namespace Path { -atomic_long Storage::_last_identifier; +atomic_uint Storage::_last_identifier; Storage::Storage(uint32_t capacity) { _unknown = nullptr; @@ -59,9 +61,21 @@ Storage::Storage(uint32_t capacity, const Storage &storage): Storage(capacity) { Storage::~Storage() { if (_unknown != nullptr) { + auto oldValue = _unknown; _unknown = nullptr; // TODO } + // TODO: MapCache + if (flags().isExternal()) { + #if ORB_TARGET_OS_DARWIN + if (cachedCGPath() != nullptr) { + auto oldCache = cachedCGPath(); + _cached_cgPath = nullptr; + CFRelease(oldCache); + } + #endif + free((void *)external_storage()); + } } bool Storage::operator==(const Storage &other) const ORB_NOEXCEPT { @@ -73,16 +87,68 @@ void Storage::clear() { // TODO } -void * Storage::cgpath() const ORB_NOEXCEPT { - if (_flags.isInline()) { +bool Storage::append_element(ORBPathElement element, const CGFloat *points, const void *info) { + if (element >= ORBPathElementInvalid) { + precondition_failure("invalid path element: %d", element); + } + // TODO: Implement element appending + precondition_failure("TODO"); +} + +bool Storage::apply_elements(void *info, ORBPathApplyCallback callback) const ORB_NOEXCEPT { + // TODO: Add fast-path checks for special callbacks: + // - append_element_callback → append_storage(info, this) + // - NestedCallbacks::apply_elements_callback → apply_elements_fast + // - NestedCallbacks::single_element_callback → single_element_fast + // - Mapper::apply_callback with flags check → MapCache::apply + return apply_elements_(info, callback); +} + +bool Storage::apply_elements_(void *info, ORBPathApplyCallback callback) const ORB_NOEXCEPT { + // TODO: Implement actual element iteration over storage + return true; +} + +#if ORB_TARGET_OS_DARWIN +CGPathRef Storage::cgpath() const ORB_NOEXCEPT { + if (flags().isInline()) { return nullptr; } - if (_cached_cgPath != nullptr) { - return _cached_cgPath; + CGPathRef cached = __atomic_load_n(&_cached_cgPath, __ATOMIC_SEQ_CST); + if (cached != nullptr) { + return cached; + } + static const ORBPathCallbacks callbacks = { + nullptr, + nullptr, + nullptr, + +[](const void *object, void *info, ORBPathApplyCallback callback) -> bool { + auto storage = reinterpret_cast(object); + return storage->apply_elements(info, callback); + }, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + }; + ORBPath path = { + const_cast(reinterpret_cast(this)), + &callbacks + }; + CGPathRef new_path = ORBPathCopyCGPath(path); + CGPathRef expected = nullptr; + if (__atomic_compare_exchange_n(&_cached_cgPath, &expected, new_path, + false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { + return new_path; + } else { + CGPathRelease(new_path); + return expected; } - // TODO: Create CGPath via RBPathCopyCGPath - return nullptr; } +#endif } /* Path */ } /* ORB */ diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h index e5710a8..a070f88 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBBase.h @@ -68,4 +68,4 @@ #if !ORB_TRRET_OS_DARWIN #include "CFCGTypes.h" -#endif \ No newline at end of file +#endif diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h index 4377fba..2b7361d 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPath.h @@ -20,10 +20,13 @@ typedef ORB_ENUM(int32_t, ORBPathElement) { ORBPathElementAddQuadCurveToPoint = 2, ORBPathElementAddCurveToPoint = 3, ORBPathElementCloseSubpath = 4, - + ORBPathElementRect = 5, + ORBPathElementRoundedRect = 6, ORBPathElementFixedRoundedRectCircular = 8, ORBPathElementFixedRoundedRectContinuous = 9, -}; + + ORBPathElementInvalid = 25, +} ORB_SWIFT_NAME(ORBPath.Element); /// Defines the shape of a rounded rectangle's corners. typedef ORB_ENUM(int32_t, ORBPathRoundedCornerStyle) { @@ -37,7 +40,7 @@ typedef ORB_ENUM(int32_t, ORBPathRoundedCornerStyle) { /// Returns true to stop enumeration, false to continue typedef bool (*ORBPathApplyCallback)(void * info, ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); -typedef struct ORBPathCallbacks ORBPathCallbacks; +typedef struct ORBPathCallbacks ORBPathCallbacks ORB_SWIFT_NAME(ORBPath.ApplyCallback); typedef struct ORBPathStorage * ORBPathStorageRef ORB_SWIFT_STRUCT ORB_SWIFT_NAME(ORBPath.Storage); @@ -55,11 +58,9 @@ ORB_EXPORT const ORBPath ORBPathNull ORB_SWIFT_NAME(ORBPath.null); ORB_EXPORT -ORB_REFINED_FOR_SWIFT void ORBPathRetain(ORBPath path) ORB_SWIFT_NAME(ORBPath.retain(self:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT void ORBPathRelease(ORBPath path) ORB_SWIFT_NAME(ORBPath.release(self:)); #if ORB_TARGET_OS_DARWIN @@ -95,6 +96,11 @@ 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 +// MARK: - Apply Callback + +ORB_EXPORT +bool ORBPathApplyElements(ORBPath path, void * info, _Nullable ORBPathApplyCallback callback) ORB_SWIFT_NAME(ORBPath.apply(self:info:callback:)); + 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 45c13d1..aa378a4 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBPathStorage.h @@ -18,53 +18,42 @@ ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN ORB_EXPORT -ORB_REFINED_FOR_SWIFT void ORBPathStorageInit(ORBPathStorageRef dst, uint32_t capacity, ORBPathStorageRef _Nullable source) ORB_SWIFT_NAME(ORBPathStorageRef.initialize(self:capacity:source:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT void ORBPathStorageDestroy(ORBPathStorageRef storage) ORB_SWIFT_NAME(ORBPathStorageRef.destroy(self:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT void ORBPathStorageClear(ORBPathStorageRef storage) ORB_SWIFT_NAME(ORBPathStorageRef.clear(self:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT 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 -void ORBPathStorageAppendPath(ORBPathStorageRef, ORBPath) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:path:)); +bool ORBPathStorageAppendPath(ORBPathStorageRef, ORBPath) ORB_SWIFT_NAME(ORBPathStorageRef.append(self:path:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT bool ORBPathStorageApplyElements(ORBPathStorageRef, void *info, ORBPathApplyCallback _Nullable callback) ORB_SWIFT_NAME(ORBPathStorageRef.apply(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.isEqual(self:to:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT bool ORBPathStorageIsSingleElement(ORBPathStorageRef storage) ORB_SWIFT_NAME(getter:ORBPathStorageRef.isSingleElement(self:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT uint32_t ORBPathStorageGetBezierOrder(ORBPathStorageRef storage) ORB_SWIFT_NAME(getter:ORBPathStorageRef.bezierOrder(self:)); #if ORB_TARGET_OS_DARWIN ORB_EXPORT -ORB_REFINED_FOR_SWIFT CGRect ORBPathStorageGetBoundingRect(ORBPathStorageRef storage) ORB_SWIFT_NAME(getter:ORBPathStorageRef.boundingRect(self:)); ORB_EXPORT -ORB_REFINED_FOR_SWIFT -CGPathRef RBPathStorageGetCGPath(ORBPathStorageRef storage) ORB_SWIFT_NAME(getter:ORBPathStorageRef.cgPath(self:)); +CF_RETURNS_NOT_RETAINED +__nullable CGPathRef ORBPathStorageGetCGPath(ORBPathStorageRef storage) ORB_SWIFT_NAME(getter:ORBPathStorageRef.cgPath(self:)); #endif ORB_EXTERN_C_END diff --git a/Sources/OpenRenderBox/include/OpenRenderBox/ORBUUID.h b/Sources/OpenRenderBox/include/OpenRenderBox/ORBUUID.h index e270b2c..69fd007 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBox/ORBUUID.h +++ b/Sources/OpenRenderBox/include/OpenRenderBox/ORBUUID.h @@ -18,12 +18,10 @@ ORB_EXTERN_C_BEGIN #if ORB_TARGET_OS_DARWIN && __OBJC__ ORB_EXPORT -ORB_REFINED_FOR_SWIFT ORBUUID ORBUUIDInitFromNSUUID(NSUUID *uuid) ORB_SWIFT_NAME(ORBUUID.init(uuid:)); #endif ORB_EXPORT -ORB_REFINED_FOR_SWIFT ORBUUID ORBUUIDInitFromHash(uint64_t words0, uint64_t words1, uint32_t words2) ORB_SWIFT_NAME(ORBUUID.init(_:_:_:)); ORB_EXTERN_C_END diff --git a/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp b/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp index 09d6b4c..1dcf843 100644 --- a/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp +++ b/Sources/OpenRenderBox/include/OpenRenderBoxCxx/Path/PathStorage.hpp @@ -5,9 +5,12 @@ #pragma once #include +#include #include - #include +#if ORB_TARGET_OS_DARWIN +#include +#endif ORB_ASSUME_NONNULL_BEGIN @@ -128,7 +131,7 @@ struct StorageFlags { class Storage { public: ORB_INLINE ORB_CONSTEXPR - const static atomic_long& last_identifier() ORB_NOEXCEPT { + const static atomic_uint& last_identifier() ORB_NOEXCEPT { return _last_identifier; } @@ -138,7 +141,7 @@ class Storage { } private: - static atomic_long _last_identifier; + static atomic_uint _last_identifier; public: Storage(uint32_t capacity); @@ -149,24 +152,29 @@ class Storage { void clear(); -// void append_element(RBPathElement, double const*, void const*); + bool append_element(ORBPathElement element, const CGFloat *points, const void * _Nullable userInfo); // 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; + /// Apply callback to each path element (with fast-path checks) + bool apply_elements(void *info, ORBPathApplyCallback callback) const ORB_NOEXCEPT; + +private: + /// Core element iteration (no fast-path checks) + bool apply_elements_(void *info, ORBPathApplyCallback callback) const ORB_NOEXCEPT; +public: + + #if ORB_TARGET_OS_DARWIN + /// Get cached CGPath, lazily creating if needed (thread-safe with casal) + CGPathRef _Nullable cgpath() const ORB_NOEXCEPT; + #endif public: ORB_INLINE ORB_CONSTEXPR void * unknown() const ORB_NOEXCEPT { return _unknown; } - ORB_INLINE - void * cachedCGPath() const ORB_NOEXCEPT { - return _cached_cgPath; - } - ORB_INLINE ORB_CONSTEXPR StorageFlags flags() const ORB_NOEXCEPT { return _flags; @@ -209,6 +217,13 @@ class Storage { bool isEmpty() const ORB_NOEXCEPT { return actual_size() == 0; } + + #if ORB_TARGET_OS_DARWIN + ORB_INLINE + CGPathRef cachedCGPath() const ORB_NOEXCEPT { + return _cached_cgPath; + } + #endif private: void * _unknown; // 0x00 StorageFlags _flags; // 0x08 @@ -218,7 +233,9 @@ class Storage { void * _reserved1; // 0x20 void * _reserved2; // 0x28 void * _reserved3; // 0x30 - mutable void * _cached_cgPath; // 0x38 - lazily cached CGPath + #if ORB_TARGET_OS_DARWIN + mutable CGPathRef _cached_cgPath; // 0x38 - lazily cached CGPath + #endif }; } /* Path */ } /* ORB */ diff --git a/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift b/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift index aa2e15c..1b6e1ca 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/PathStorageTests.swift @@ -93,6 +93,29 @@ struct PathStorageTests { path1.release() path2.release() } + + @Test("Verify no crash or memleak of ORBPathStorageGetCGPath call") + func storageGetCGPath() { + let rect = CGRect(x: 0, y: 0, width: 100, height: 100) + let path = ORBPath(rect: rect, transform: nil) + let storage = path.storage + storage.initialize(capacity: 64, source: nil) + storage.append(path: path) + for _ in 1 ... 7 { + storage.append(element: .moveToPoint, points: [50, 50], userInfo: nil) + storage.append(element: .addLineToPoint, points: [100, 50], userInfo: nil) + storage.append(element: .closeSubpath, points: [], userInfo: nil) + } + func test(_ s: ORBPath.Storage) { + if let p = s.cgPath { + _ = p.isEmpty + _ = p.boundingBox + } + } + for i in 1 ... 20 { + test(storage) + } + } } #endif