From fdae7886ac0eb03a6ef18761f06005e835933c97 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 18:38:36 +0800 Subject: [PATCH 01/16] Add more ORBColor API --- Sources/OpenRenderBoxCxx/Color/ORBColor.cpp | 80 ++++++++++++ .../include/OpenRenderBox/ORBColor.h | 20 +++ .../ColorTests.swift | 118 ++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 Sources/OpenRenderBoxCxx/Color/ORBColor.cpp create mode 100644 Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp new file mode 100644 index 0000000..1e6e55d --- /dev/null +++ b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp @@ -0,0 +1,80 @@ +// +// ORBColor.cpp +// OpenRenderBox + +#include +#include + +// sRGB to linear constants +static const float kSRGBToLinearThreshold = 0.04045f; +static const float kSRGBToLinearScale = 12.92f; +static const float kSRGBToLinearGammaOffset = 0.055f; +static const float kSRGBToLinearGammaScale = 1.055f; +static const float kSRGBToLinearGamma = 2.4f; + +// Linear to sRGB constants +static const float kLinearToSRGBThreshold = 0.0031308f; +static const float kLinearToSRGBScale = 12.92f; +static const float kLinearToSRGBGammaScale = 1.055f; +static const float kLinearToSRGBGammaOffset = 0.055f; +static const float kLinearToSRGBGamma = 1.0f / 2.4f; + +static inline float sRGBToLinear(float value) { + float absValue = value > 0.0f ? value : -value; + float result; + if (absValue <= kSRGBToLinearThreshold) { + result = absValue / kSRGBToLinearScale; + } else { + result = powf((absValue + kSRGBToLinearGammaOffset) / kSRGBToLinearGammaScale, kSRGBToLinearGamma); + } + return value < 0.0f ? -result : result; +} + +static inline float linearToSRGB(float value) { + float absValue = value > 0.0f ? value : -value; + float result; + if (absValue <= kLinearToSRGBThreshold) { + result = absValue * kLinearToSRGBScale; + } else { + result = kLinearToSRGBGammaScale * powf(absValue, kLinearToSRGBGamma) - kLinearToSRGBGammaOffset; + } + return value < 0.0f ? -result : result; +} + +ORBColor ORBColorMake(float red, float green, float blue, float alpha) ORB_NOEXCEPT { + return (ORBColor){ red, green, blue, alpha }; +} + +ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) ORB_NOEXCEPT { + return (ORBColor){ + sRGBToLinear(red), + sRGBToLinear(green), + sRGBToLinear(blue), + alpha + }; +} + +ORBColor ORBColorToLinear(ORBColor color) ORB_NOEXCEPT { + return (ORBColor){ + sRGBToLinear(color.red), + sRGBToLinear(color.green), + sRGBToLinear(color.blue), + color.alpha + }; +} + +ORBColor ORBColorFromLinear(ORBColor color) ORB_NOEXCEPT { + return (ORBColor){ + linearToSRGB(color.red), + linearToSRGB(color.green), + linearToSRGB(color.blue), + color.alpha + }; +} + +bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT { + return lhs.red == rhs.red && + lhs.green == rhs.green && + lhs.blue == rhs.blue && + lhs.alpha == rhs.alpha; +} diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h index 4683655..cb36637 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h @@ -17,6 +17,26 @@ typedef struct ORBColor { float alpha; } ORBColor; +ORB_EXPORT +ORBColor ORBColorMake(float red, float green, float blue, float alpha) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.init(red:green:blue:alpha:)); + +ORB_EXPORT +ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.init(linearRed:green:blue:alpha:)); + +ORB_EXPORT +ORBColor ORBColorToLinear(ORBColor color) ORB_NOEXCEPT + ORB_SWIFT_NAME(getter:ORBColor.linear(self:)); + +ORB_EXPORT +ORBColor ORBColorFromLinear(ORBColor color) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.fromLinear(self:)); + +ORB_EXPORT +bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.isEqual(self:to:)); + ORB_EXTERN_C_END ORB_ASSUME_NONNULL_END diff --git a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift new file mode 100644 index 0000000..8d706b3 --- /dev/null +++ b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift @@ -0,0 +1,118 @@ +// +// ColorTests.swift +// OpenRenderBoxCompatibilityTests + +import Testing +import Numerics + +@Suite +struct ColorTests { + // MARK: - ORBColorMake + + @Test(arguments: [ + (red: Float(0.0), green: Float(0.0), blue: Float(0.0), alpha: Float(1.0)), + (red: Float(1.0), green: Float(1.0), blue: Float(1.0), alpha: Float(1.0)), + (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(0.8)), + (red: Float(0.2), green: Float(0.4), blue: Float(0.6), alpha: Float(0.0)), + ]) + func colorMake(red: Float, green: Float, blue: Float, alpha: Float) { + let color = ORBColor(red: red, green: green, blue: blue, alpha: alpha) + #expect(color.red == red) + #expect(color.green == green) + #expect(color.blue == blue) + #expect(color.alpha == alpha) + } + + // MARK: - ORBColorMakeLinear + + @Test(arguments: [ + (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(1.0)), + (red: Float(0.2), green: Float(0.4), blue: Float(0.6), alpha: Float(0.5)), + ]) + func colorMakeLinear(red: Float, green: Float, blue: Float, alpha: Float) { + let color = ORBColor(linearRed: red, green: green, blue: blue, alpha: alpha) + // Alpha should be unchanged + #expect(color.alpha == alpha) + // RGB values should be converted to linear (smaller for typical sRGB values > threshold) + #expect(!color.red.isApproximatelyEqual(to: red, absoluteTolerance: 0.0001)) + #expect(!color.green.isApproximatelyEqual(to: green, absoluteTolerance: 0.0001)) + #expect(!color.blue.isApproximatelyEqual(to: blue, absoluteTolerance: 0.0001)) + } + + // MARK: - ORBColorToLinear + + @Test(arguments: [ + (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(0.8)), + (red: Float(0.3), green: Float(0.6), blue: Float(0.9), alpha: Float(1.0)), + ]) + func colorToLinear(red: Float, green: Float, blue: Float, alpha: Float) { + let srgbColor = ORBColor(red: red, green: green, blue: blue, alpha: alpha) + let linearColor = srgbColor.linear + + // Alpha should be unchanged + #expect(linearColor.alpha == srgbColor.alpha) + // Linear values should differ from sRGB for values > threshold + #expect(!linearColor.red.isApproximatelyEqual(to: srgbColor.red, absoluteTolerance: 0.0001)) + #expect(!linearColor.green.isApproximatelyEqual(to: srgbColor.green, absoluteTolerance: 0.0001)) + #expect(!linearColor.blue.isApproximatelyEqual(to: srgbColor.blue, absoluteTolerance: 0.0001)) + } + + // MARK: - ORBColorFromLinear + + @Test(arguments: [ + (red: Float(0.2), green: Float(0.1), blue: Float(0.5), alpha: Float(0.9)), + (red: Float(0.05), green: Float(0.15), blue: Float(0.25), alpha: Float(1.0)), + ]) + func colorFromLinear(red: Float, green: Float, blue: Float, alpha: Float) { + let linearColor = ORBColor(red: red, green: green, blue: blue, alpha: alpha) + let srgbColor = linearColor.fromLinear() + + // Alpha should be unchanged + #expect(srgbColor.alpha == linearColor.alpha) + // sRGB values should differ from linear for values > threshold + #expect(!srgbColor.red.isApproximatelyEqual(to: linearColor.red, absoluteTolerance: 0.0001)) + #expect(!srgbColor.green.isApproximatelyEqual(to: linearColor.green, absoluteTolerance: 0.0001)) + #expect(!srgbColor.blue.isApproximatelyEqual(to: linearColor.blue, absoluteTolerance: 0.0001)) + } + + // MARK: - Round Trip (toLinear -> fromLinear) + + @Test(arguments: [ + (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(1.0)), + (red: Float(0.0), green: Float(0.0), blue: Float(0.0), alpha: Float(1.0)), + (red: Float(1.0), green: Float(1.0), blue: Float(1.0), alpha: Float(0.5)), + (red: Float(0.3), green: Float(0.6), blue: Float(0.9), alpha: Float(0.8)), + ]) + func colorRoundTrip(red: Float, green: Float, blue: Float, alpha: Float) { + let originalColor = ORBColor(red: red, green: green, blue: blue, alpha: alpha) + let linearColor = originalColor.linear + let roundTripColor = linearColor.fromLinear() + + #expect(roundTripColor.red.isApproximatelyEqual(to: originalColor.red, absoluteTolerance: 0.0001)) + #expect(roundTripColor.green.isApproximatelyEqual(to: originalColor.green, absoluteTolerance: 0.0001)) + #expect(roundTripColor.blue.isApproximatelyEqual(to: originalColor.blue, absoluteTolerance: 0.0001)) + #expect(roundTripColor.alpha == originalColor.alpha) + } + + // MARK: - ORBColorEqualToColor + + @Test + func colorEqualToColor_equal() { + let color1 = ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 1.0) + let color2 = ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 1.0) + #expect(color1.isEqual(to: color2)) + } + + @Test(arguments: [ + (r2: Float(0.6), g2: Float(0.25), b2: Float(0.75), a2: Float(1.0)), // different red + (r2: Float(0.5), g2: Float(0.35), b2: Float(0.75), a2: Float(1.0)), // different green + (r2: Float(0.5), g2: Float(0.25), b2: Float(0.85), a2: Float(1.0)), // different blue + (r2: Float(0.5), g2: Float(0.25), b2: Float(0.75), a2: Float(0.5)), // different alpha + ]) + func colorEqualToColor_notEqual(r2: Float, g2: Float, b2: Float, a2: Float) { + let color1 = ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 1.0) + let color2 = ORBColor(red: r2, green: g2, blue: b2, alpha: a2) + #expect(!color1.isEqual(to: color2)) + } +} + From dab9dbfcc84278bf1c8decca2ed5e5f4a0ad0f80 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 18:47:50 +0800 Subject: [PATCH 02/16] Update ColorTests --- .../ColorTests.swift | 96 +++++++++---------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift index 8d706b3..1c19462 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift @@ -9,105 +9,97 @@ import Numerics struct ColorTests { // MARK: - ORBColorMake - @Test(arguments: [ - (red: Float(0.0), green: Float(0.0), blue: Float(0.0), alpha: Float(1.0)), - (red: Float(1.0), green: Float(1.0), blue: Float(1.0), alpha: Float(1.0)), - (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(0.8)), - (red: Float(0.2), green: Float(0.4), blue: Float(0.6), alpha: Float(0.0)), + @Test("ORBColorMake", arguments: [ + (Float(0.0), Float(0.0), Float(0.0), Float(1.0), ORBColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)), + (Float(1.0), Float(1.0), Float(1.0), Float(1.0), ORBColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)), + (Float(0.5), Float(0.25), Float(0.75), Float(0.8), ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 0.8)), + (Float(0.2), Float(0.4), Float(0.6), Float(0.0), ORBColor(red: 0.2, green: 0.4, blue: 0.6, alpha: 0.0)), ]) - func colorMake(red: Float, green: Float, blue: Float, alpha: Float) { + func colorMake(red: Float, green: Float, blue: Float, alpha: Float, expectColor: ORBColor) { let color = ORBColor(red: red, green: green, blue: blue, alpha: alpha) - #expect(color.red == red) - #expect(color.green == green) - #expect(color.blue == blue) - #expect(color.alpha == alpha) + #expect(color.red == expectColor.red) + #expect(color.green == expectColor.green) + #expect(color.blue == expectColor.blue) + #expect(color.alpha == expectColor.alpha) } // MARK: - ORBColorMakeLinear - @Test(arguments: [ - (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(1.0)), - (red: Float(0.2), green: Float(0.4), blue: Float(0.6), alpha: Float(0.5)), + @Test("ORBColorMakeLinear", arguments: [ + (Float(0.5), Float(0.25), Float(0.75), Float(1.0), ORBColor(linearRed: 0.5, green: 0.25, blue: 0.75, alpha: 1.0)), + (Float(0.2), Float(0.4), Float(0.6), Float(0.5), ORBColor(linearRed: 0.2, green: 0.4, blue: 0.6, alpha: 0.5)), ]) - func colorMakeLinear(red: Float, green: Float, blue: Float, alpha: Float) { + func colorMakeLinear(red: Float, green: Float, blue: Float, alpha: Float, expectColor: ORBColor) { let color = ORBColor(linearRed: red, green: green, blue: blue, alpha: alpha) - // Alpha should be unchanged - #expect(color.alpha == alpha) - // RGB values should be converted to linear (smaller for typical sRGB values > threshold) - #expect(!color.red.isApproximatelyEqual(to: red, absoluteTolerance: 0.0001)) - #expect(!color.green.isApproximatelyEqual(to: green, absoluteTolerance: 0.0001)) - #expect(!color.blue.isApproximatelyEqual(to: blue, absoluteTolerance: 0.0001)) + #expect(color.red == expectColor.red) + #expect(color.green == expectColor.green) + #expect(color.blue == expectColor.blue) + #expect(color.alpha == expectColor.alpha) } // MARK: - ORBColorToLinear - @Test(arguments: [ - (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(0.8)), - (red: Float(0.3), green: Float(0.6), blue: Float(0.9), alpha: Float(1.0)), + @Test("ORBColorToLinear", arguments: [ + (Float(0.5), Float(0.25), Float(0.75), Float(0.8)), + (Float(0.3), Float(0.6), Float(0.9), Float(1.0)), ]) func colorToLinear(red: Float, green: Float, blue: Float, alpha: Float) { let srgbColor = ORBColor(red: red, green: green, blue: blue, alpha: alpha) let linearColor = srgbColor.linear - - // Alpha should be unchanged #expect(linearColor.alpha == srgbColor.alpha) - // Linear values should differ from sRGB for values > threshold - #expect(!linearColor.red.isApproximatelyEqual(to: srgbColor.red, absoluteTolerance: 0.0001)) - #expect(!linearColor.green.isApproximatelyEqual(to: srgbColor.green, absoluteTolerance: 0.0001)) - #expect(!linearColor.blue.isApproximatelyEqual(to: srgbColor.blue, absoluteTolerance: 0.0001)) + #expect(!linearColor.red.isApproximatelyEqual(to: srgbColor.red)) + #expect(!linearColor.green.isApproximatelyEqual(to: srgbColor.green)) + #expect(!linearColor.blue.isApproximatelyEqual(to: srgbColor.blue)) } // MARK: - ORBColorFromLinear - @Test(arguments: [ - (red: Float(0.2), green: Float(0.1), blue: Float(0.5), alpha: Float(0.9)), - (red: Float(0.05), green: Float(0.15), blue: Float(0.25), alpha: Float(1.0)), + @Test("ORBColorFromLinear", arguments: [ + (Float(0.2), Float(0.1), Float(0.5), Float(0.9)), + (Float(0.05), Float(0.15), Float(0.25), Float(1.0)), ]) func colorFromLinear(red: Float, green: Float, blue: Float, alpha: Float) { let linearColor = ORBColor(red: red, green: green, blue: blue, alpha: alpha) let srgbColor = linearColor.fromLinear() - - // Alpha should be unchanged #expect(srgbColor.alpha == linearColor.alpha) - // sRGB values should differ from linear for values > threshold - #expect(!srgbColor.red.isApproximatelyEqual(to: linearColor.red, absoluteTolerance: 0.0001)) - #expect(!srgbColor.green.isApproximatelyEqual(to: linearColor.green, absoluteTolerance: 0.0001)) - #expect(!srgbColor.blue.isApproximatelyEqual(to: linearColor.blue, absoluteTolerance: 0.0001)) + #expect(!srgbColor.red.isApproximatelyEqual(to: linearColor.red)) + #expect(!srgbColor.green.isApproximatelyEqual(to: linearColor.green)) + #expect(!srgbColor.blue.isApproximatelyEqual(to: linearColor.blue)) } // MARK: - Round Trip (toLinear -> fromLinear) - @Test(arguments: [ - (red: Float(0.5), green: Float(0.25), blue: Float(0.75), alpha: Float(1.0)), - (red: Float(0.0), green: Float(0.0), blue: Float(0.0), alpha: Float(1.0)), - (red: Float(1.0), green: Float(1.0), blue: Float(1.0), alpha: Float(0.5)), - (red: Float(0.3), green: Float(0.6), blue: Float(0.9), alpha: Float(0.8)), + @Test("ORBColor Round Trip", arguments: [ + (Float(0.5), Float(0.25), Float(0.75), Float(1.0)), + (Float(0.0), Float(0.0), Float(0.0), Float(1.0)), + (Float(1.0), Float(1.0), Float(1.0), Float(0.5)), + (Float(0.3), Float(0.6), Float(0.9), Float(0.8)), ]) func colorRoundTrip(red: Float, green: Float, blue: Float, alpha: Float) { let originalColor = ORBColor(red: red, green: green, blue: blue, alpha: alpha) let linearColor = originalColor.linear let roundTripColor = linearColor.fromLinear() - #expect(roundTripColor.red.isApproximatelyEqual(to: originalColor.red, absoluteTolerance: 0.0001)) - #expect(roundTripColor.green.isApproximatelyEqual(to: originalColor.green, absoluteTolerance: 0.0001)) - #expect(roundTripColor.blue.isApproximatelyEqual(to: originalColor.blue, absoluteTolerance: 0.0001)) + #expect(roundTripColor.red.isApproximatelyEqual(to: originalColor.red)) + #expect(roundTripColor.green.isApproximatelyEqual(to: originalColor.green)) + #expect(roundTripColor.blue.isApproximatelyEqual(to: originalColor.blue)) #expect(roundTripColor.alpha == originalColor.alpha) } // MARK: - ORBColorEqualToColor - @Test + @Test("ORBColorEqualToColor - equal") func colorEqualToColor_equal() { let color1 = ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 1.0) let color2 = ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 1.0) #expect(color1.isEqual(to: color2)) } - @Test(arguments: [ - (r2: Float(0.6), g2: Float(0.25), b2: Float(0.75), a2: Float(1.0)), // different red - (r2: Float(0.5), g2: Float(0.35), b2: Float(0.75), a2: Float(1.0)), // different green - (r2: Float(0.5), g2: Float(0.25), b2: Float(0.85), a2: Float(1.0)), // different blue - (r2: Float(0.5), g2: Float(0.25), b2: Float(0.75), a2: Float(0.5)), // different alpha + @Test("ORBColorEqualToColor - not equal", arguments: [ + (Float(0.6), Float(0.25), Float(0.75), Float(1.0)), + (Float(0.5), Float(0.35), Float(0.75), Float(1.0)), + (Float(0.5), Float(0.25), Float(0.85), Float(1.0)), + (Float(0.5), Float(0.25), Float(0.75), Float(0.5)), ]) func colorEqualToColor_notEqual(r2: Float, g2: Float, b2: Float, a2: Float) { let color1 = ORBColor(red: 0.5, green: 0.25, blue: 0.75, alpha: 1.0) From 31f691e05bd87de626de58aae20fc55c79e180d9 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 18:51:11 +0800 Subject: [PATCH 03/16] Add internal import to fix Numerics import warning --- Package.resolved | 2 +- Package.swift | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Package.resolved b/Package.resolved index 97bd322..300d62a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "4fcdebfe2c90ba26fcba7ec801dbb697ae67f942fbae6db543f3f15c85ec2747", + "originHash" : "030dd1b5d51c353f6561859605d7a71043caed24528e27ea847be041a4ba12aa", "pins" : [ { "identity" : "darwinprivateframeworks", diff --git a/Package.swift b/Package.swift index 91f85b8..79eb034 100644 --- a/Package.swift +++ b/Package.swift @@ -176,6 +176,7 @@ var sharedCxxSettings: [CXXSetting] = [ ] var sharedSwiftSettings: [SwiftSetting] = [ .swiftLanguageMode(.v5), + .enableUpcomingFeature("InternalImportsByDefault"), ] if libraryEvolutionCondition { // NOTE: -enable-library-evolution will cause module verify failure for `swift build`. @@ -245,7 +246,7 @@ let openRenderBoxTestsTarget = Target.testTarget( let openRenderBoxCompatibilityTestTarget = Target.testTarget( name: "OpenRenderBoxCompatibilityTests", dependencies: [ - .product(name: "RealModule", package: "swift-numerics"), + .product(name: "Numerics", package: "swift-numerics"), ], exclude: ["README.md"], cSettings: sharedCSettings + [.define("SWIFT_TESTING")], From 6f20d232d9d7f59ff05b196c6e57f0e4229be91f Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 19:07:07 +0800 Subject: [PATCH 04/16] Add more ORBColor API --- Sources/OpenRenderBoxCxx/Color/ORBColor.cpp | 56 +++++++++++++++++++ .../OpenRenderBoxCxx/Color/ORBColorSpace.cpp | 42 ++++++++++++++ .../include/OpenRenderBox/ORBColor.h | 38 +++++++++++++ .../include/OpenRenderBox/ORBColorSpace.h | 47 ++++++++++++++++ .../include/OpenRenderBox/OpenRenderBox.h | 1 + 5 files changed, 184 insertions(+) create mode 100644 Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp create mode 100644 Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp index 1e6e55d..0677764 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp @@ -5,6 +5,13 @@ #include #include +// Global color constants +const ORBColor ORBColorClear = { 0.0f, 0.0f, 0.0f, 0.0f }; +const ORBColor ORBColorBlack = { 0.0f, 0.0f, 0.0f, 1.0f }; +const ORBColor ORBColorWhite = { 1.0f, 1.0f, 1.0f, 1.0f }; +const ORBColor ORBColorNull = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; +const float ORBColorInvalidComponent = -32768.0f; + // sRGB to linear constants static const float kSRGBToLinearThreshold = 0.04045f; static const float kSRGBToLinearScale = 12.92f; @@ -78,3 +85,52 @@ bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT { lhs.blue == rhs.blue && lhs.alpha == rhs.alpha; } + +#if ORB_TARGET_OS_DARWIN + +#include + +ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) ORB_NOEXCEPT { + size_t componentCount = premultiplied ? 2 : 1; + return ORBColorFromComponents2(colorSpace, components, componentCount); +} + +ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_NOEXCEPT { + // TODO: Implement proper color space conversion + (void)colorSpace; + (void)componentCount; + return (ORBColor){ + static_cast(components[0]), + static_cast(components[1]), + static_cast(components[2]), + static_cast(components[3]) + }; +} + +ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_NOEXCEPT { + CGColorSpaceRef colorSpace = CGColorGetColorSpace(color); + const CGFloat *components = CGColorGetComponents(color); + size_t componentCount = premultiplied ? 2 : 1; + return ORBColorFromComponents2(colorSpace, components, componentCount); +} + +ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_NOEXCEPT { + CGColorSpaceRef colorSpace = CGColorGetColorSpace(color); + const CGFloat *components = CGColorGetComponents(color); + return ORBColorFromComponents2(colorSpace, components, componentCount); +} + +CGColorRef ORBColorCopyCGColor(ORBColor color) ORB_NOEXCEPT { + CGFloat components[4] = { + static_cast(color.red), + static_cast(color.green), + static_cast(color.blue), + static_cast(color.alpha) + }; + CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + CGColorRef cgColor = CGColorCreate(colorSpace, components); + CGColorSpaceRelease(colorSpace); + return cgColor; +} + +#endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp new file mode 100644 index 0000000..66e2037 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp @@ -0,0 +1,42 @@ +// +// ORBColorSpace.cpp +// OpenRenderBox + +#include + +static const ORBColorSpace kColorModeWorkingColorSpaceTable[16] = { + ORBColorSpaceSRGB, // 0 + ORBColorSpaceExtendedLinear, // 1 + ORBColorSpaceExtendedLinear, // 2 + ORBColorSpaceSRGB, // 3 + ORBColorSpaceSRGB, // 4 + ORBColorSpaceSRGB, // 5 + ORBColorSpaceExtendedLinear, // 6 + ORBColorSpaceExtendedLinear, // 7 + ORBColorSpaceExtendedLinear, // 8 + ORBColorSpaceSRGB, // 9 + ORBColorSpaceExtendedLinear, // 10 + ORBColorSpaceSRGB, // 11 + ORBColorSpaceSRGB, // 12 + ORBColorSpaceExtendedLinear, // 13 + ORBColorSpaceSRGB, // 14 + ORBColorSpaceExtendedLinear, // 15 +}; + +ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_NOEXCEPT { + if (mode > 15) { + return ORBColorSpaceInvalid; + } + return kColorModeWorkingColorSpaceTable[mode]; +} + +bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_NOEXCEPT { + // 0x3804 = 0011 1000 0000 0100 binary + // Extended range modes: 2, 11, 12, 13 + if (mode >= 14) { + return false; + } + uint32_t mask = 0x3804; + return (mask >> mode) & 1; +} + diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h index cb36637..9318d26 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h @@ -5,6 +5,9 @@ #pragma once #include +#if ORB_TARGET_OS_DARWIN +#include +#endif ORB_ASSUME_NONNULL_BEGIN @@ -17,6 +20,17 @@ typedef struct ORBColor { float alpha; } ORBColor; +ORB_EXPORT const ORBColor ORBColorClear + ORB_SWIFT_NAME(ORBColor.clear); +ORB_EXPORT const ORBColor ORBColorBlack + ORB_SWIFT_NAME(ORBColor.black); +ORB_EXPORT const ORBColor ORBColorWhite + ORB_SWIFT_NAME(ORBColor.white); +ORB_EXPORT const ORBColor ORBColorNull + ORB_SWIFT_NAME(ORBColor.null); +ORB_EXPORT const float ORBColorInvalidComponent + ORB_SWIFT_NAME(ORBColor.invalidComponent); + ORB_EXPORT ORBColor ORBColorMake(float red, float green, float blue, float alpha) ORB_NOEXCEPT ORB_SWIFT_NAME(ORBColor.init(red:green:blue:alpha:)); @@ -37,6 +51,30 @@ ORB_EXPORT bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT ORB_SWIFT_NAME(ORBColor.isEqual(self:to:)); +#if ORB_TARGET_OS_DARWIN + +ORB_EXPORT +ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:premultiplied:)); + +ORB_EXPORT +ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:componentCount:)); + +ORB_EXPORT +ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.init(_:premultiplied:)); + +ORB_EXPORT +ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_NOEXCEPT + ORB_SWIFT_NAME(ORBColor.init(_:componentCount:)); + +ORB_EXPORT +CGColorRef ORBColorCopyCGColor(ORBColor color) ORB_NOEXCEPT + ORB_SWIFT_NAME(getter:ORBColor.cgColor(self:)); + +#endif /* ORB_TARGET_OS_DARWIN */ + ORB_EXTERN_C_END ORB_ASSUME_NONNULL_END diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h new file mode 100644 index 0000000..fafb111 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h @@ -0,0 +1,47 @@ +// +// ORBColorSpace.h +// OpenRenderBox + +#pragma once + +#include + +ORB_ASSUME_NONNULL_BEGIN + +ORB_EXTERN_C_BEGIN + +typedef CF_ENUM(uint32_t, ORBColorSpace) { + ORBColorSpaceInvalid = 0, + ORBColorSpaceSRGB = 1, + ORBColorSpaceExtendedLinear = 2, +}; + +typedef CF_ENUM(uint32_t, ORBColorMode) { + ORBColorMode0 = 0, + ORBColorMode1 = 1, + ORBColorMode2 = 2, + ORBColorMode3 = 3, + ORBColorMode4 = 4, + ORBColorMode5 = 5, + ORBColorMode6 = 6, + ORBColorMode7 = 7, + ORBColorMode8 = 8, + ORBColorMode9 = 9, + ORBColorMode10 = 10, + ORBColorMode11 = 11, + ORBColorMode12 = 12, + ORBColorMode13 = 13, + ORBColorMode14 = 14, + ORBColorMode15 = 15, +}; + +ORB_EXPORT +ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_NOEXCEPT; + +ORB_EXPORT +bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_NOEXCEPT; + +ORB_EXTERN_C_END + +ORB_ASSUME_NONNULL_END + diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h index 58b753f..8c24a93 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From ec54226599a1599de19f4c67a701cadea35b0aa8 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 19:08:52 +0800 Subject: [PATCH 05/16] Update shims --- Sources/OpenRenderBoxShims/Export.swift | 2 ++ Tests/OpenRenderBoxCompatibilityTests/Shims.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Sources/OpenRenderBoxShims/Export.swift b/Sources/OpenRenderBoxShims/Export.swift index 197e57c..3fdec01 100644 --- a/Sources/OpenRenderBoxShims/Export.swift +++ b/Sources/OpenRenderBoxShims/Export.swift @@ -5,6 +5,8 @@ #if OPENRENDERBOX_RENDERBOX @_exported public import RenderBox public typealias ORBColor = RBColor +public typealias ORBColorMode = RBColorMode +public typealias ORBColorSpace = RBColorSpace public typealias ORBDevice = RBDevice public typealias ORBLayer = RBLayer public typealias ORBPath = RBPath diff --git a/Tests/OpenRenderBoxCompatibilityTests/Shims.swift b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift index 0160549..1ef54f1 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/Shims.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift @@ -5,6 +5,8 @@ #if OPENRENDERBOX_COMPATIBILITY_TEST @_exported public import RenderBox public typealias ORBColor = RBColor +public typealias ORBColorMode = RBColorMode +public typealias ORBColorSpace = RBColorSpace public typealias ORBDevice = RBDevice public typealias ORBLayer = RBLayer public typealias ORBPath = RBPath From c59840692738bd7757070eb58ccdc947e8db57b7 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 20:02:49 +0800 Subject: [PATCH 06/16] Add Color constant test case --- Sources/OpenRenderBoxCxx/Color/ORBColor.cpp | 22 +++--- .../OpenRenderBoxCxx/Color/ORBColorSpace.cpp | 4 +- .../include/OpenRenderBox/ORBColor.h | 73 ++++++------------- .../include/OpenRenderBox/ORBColorSpace.h | 7 +- .../ColorTests.swift | 21 ++++++ 5 files changed, 57 insertions(+), 70 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp index 0677764..1a3e0f2 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp @@ -9,7 +9,7 @@ const ORBColor ORBColorClear = { 0.0f, 0.0f, 0.0f, 0.0f }; const ORBColor ORBColorBlack = { 0.0f, 0.0f, 0.0f, 1.0f }; const ORBColor ORBColorWhite = { 1.0f, 1.0f, 1.0f, 1.0f }; -const ORBColor ORBColorNull = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; +const ORBColor ORBColorNull = { ORBColorInvalidComponent, ORBColorInvalidComponent, ORBColorInvalidComponent, ORBColorInvalidComponent }; const float ORBColorInvalidComponent = -32768.0f; // sRGB to linear constants @@ -48,11 +48,11 @@ static inline float linearToSRGB(float value) { return value < 0.0f ? -result : result; } -ORBColor ORBColorMake(float red, float green, float blue, float alpha) ORB_NOEXCEPT { +ORBColor ORBColorMake(float red, float green, float blue, float alpha) { return (ORBColor){ red, green, blue, alpha }; } -ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) ORB_NOEXCEPT { +ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) { return (ORBColor){ sRGBToLinear(red), sRGBToLinear(green), @@ -61,7 +61,7 @@ ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) ORB }; } -ORBColor ORBColorToLinear(ORBColor color) ORB_NOEXCEPT { +ORBColor ORBColorToLinear(ORBColor color) { return (ORBColor){ sRGBToLinear(color.red), sRGBToLinear(color.green), @@ -70,7 +70,7 @@ ORBColor ORBColorToLinear(ORBColor color) ORB_NOEXCEPT { }; } -ORBColor ORBColorFromLinear(ORBColor color) ORB_NOEXCEPT { +ORBColor ORBColorFromLinear(ORBColor color) { return (ORBColor){ linearToSRGB(color.red), linearToSRGB(color.green), @@ -79,7 +79,7 @@ ORBColor ORBColorFromLinear(ORBColor color) ORB_NOEXCEPT { }; } -bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT { +bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) { return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue && @@ -90,12 +90,12 @@ bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT { #include -ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) ORB_NOEXCEPT { +ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) { size_t componentCount = premultiplied ? 2 : 1; return ORBColorFromComponents2(colorSpace, components, componentCount); } -ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_NOEXCEPT { +ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) { // TODO: Implement proper color space conversion (void)colorSpace; (void)componentCount; @@ -107,20 +107,20 @@ ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *comp }; } -ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_NOEXCEPT { +ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) { CGColorSpaceRef colorSpace = CGColorGetColorSpace(color); const CGFloat *components = CGColorGetComponents(color); size_t componentCount = premultiplied ? 2 : 1; return ORBColorFromComponents2(colorSpace, components, componentCount); } -ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_NOEXCEPT { +ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) { CGColorSpaceRef colorSpace = CGColorGetColorSpace(color); const CGFloat *components = CGColorGetComponents(color); return ORBColorFromComponents2(colorSpace, components, componentCount); } -CGColorRef ORBColorCopyCGColor(ORBColor color) ORB_NOEXCEPT { +CGColorRef ORBColorCopyCGColor(ORBColor color) { CGFloat components[4] = { static_cast(color.red), static_cast(color.green), diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp index 66e2037..103da23 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp @@ -23,14 +23,14 @@ static const ORBColorSpace kColorModeWorkingColorSpaceTable[16] = { ORBColorSpaceExtendedLinear, // 15 }; -ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_NOEXCEPT { +ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) { if (mode > 15) { return ORBColorSpaceInvalid; } return kColorModeWorkingColorSpaceTable[mode]; } -bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_NOEXCEPT { +bool ORBColorModeHasExtendedRange(ORBColorMode mode) { // 0x3804 = 0011 1000 0000 0100 binary // Extended range modes: 2, 11, 12, 13 if (mode >= 14) { diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h index 9318d26..f84e450 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h @@ -13,65 +13,34 @@ ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN +typedef float ORBColorComponent; + typedef struct ORBColor { - float red; - float green; - float blue; - float alpha; + ORBColorComponent red; + ORBColorComponent green; + ORBColorComponent blue; + ORBColorComponent alpha; } ORBColor; -ORB_EXPORT const ORBColor ORBColorClear - ORB_SWIFT_NAME(ORBColor.clear); -ORB_EXPORT const ORBColor ORBColorBlack - ORB_SWIFT_NAME(ORBColor.black); -ORB_EXPORT const ORBColor ORBColorWhite - ORB_SWIFT_NAME(ORBColor.white); -ORB_EXPORT const ORBColor ORBColorNull - ORB_SWIFT_NAME(ORBColor.null); -ORB_EXPORT const float ORBColorInvalidComponent - ORB_SWIFT_NAME(ORBColor.invalidComponent); - -ORB_EXPORT -ORBColor ORBColorMake(float red, float green, float blue, float alpha) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.init(red:green:blue:alpha:)); - -ORB_EXPORT -ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.init(linearRed:green:blue:alpha:)); - -ORB_EXPORT -ORBColor ORBColorToLinear(ORBColor color) ORB_NOEXCEPT - ORB_SWIFT_NAME(getter:ORBColor.linear(self:)); - -ORB_EXPORT -ORBColor ORBColorFromLinear(ORBColor color) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.fromLinear(self:)); +ORB_EXPORT const ORBColor ORBColorClear ORB_SWIFT_NAME(ORBColor.clear); +ORB_EXPORT const ORBColor ORBColorBlack ORB_SWIFT_NAME(ORBColor.black); +ORB_EXPORT const ORBColor ORBColorWhite ORB_SWIFT_NAME(ORBColor.white); +ORB_EXPORT const ORBColor ORBColorNull ORB_SWIFT_NAME(ORBColor.null); +ORB_EXPORT const ORBColorComponent ORBColorInvalidComponent ORB_SWIFT_NAME(ORBColor.invalidComponent); -ORB_EXPORT -bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.isEqual(self:to:)); +ORB_EXPORT ORBColor ORBColorMake(float red, float green, float blue, float alpha) ORB_SWIFT_NAME(ORBColor.init(red:green:blue:alpha:)); +ORB_EXPORT ORBColor ORBColorMakeLinear(float red, float green, float blue, float alpha) ORB_SWIFT_NAME(ORBColor.init(linearRed:green:blue:alpha:)); +ORB_EXPORT ORBColor ORBColorToLinear(ORBColor color) ORB_SWIFT_NAME(getter:ORBColor.linear(self:)); +ORB_EXPORT ORBColor ORBColorFromLinear(ORBColor color) ORB_SWIFT_NAME(ORBColor.fromLinear(self:)); +ORB_EXPORT bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_SWIFT_NAME(ORBColor.isEqual(self:to:)); #if ORB_TARGET_OS_DARWIN -ORB_EXPORT -ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:premultiplied:)); - -ORB_EXPORT -ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:componentCount:)); - -ORB_EXPORT -ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.init(_:premultiplied:)); - -ORB_EXPORT -ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_NOEXCEPT - ORB_SWIFT_NAME(ORBColor.init(_:componentCount:)); - -ORB_EXPORT -CGColorRef ORBColorCopyCGColor(ORBColor color) ORB_NOEXCEPT - ORB_SWIFT_NAME(getter:ORBColor.cgColor(self:)); +ORB_EXPORT ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:premultiplied:)); +ORB_EXPORT ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:componentCount:)); +ORB_EXPORT ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_SWIFT_NAME(ORBColor.init(_:premultiplied:)); +ORB_EXPORT ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_SWIFT_NAME(ORBColor.init(_:componentCount:)); +ORB_EXPORT CGColorRef ORBColorCopyCGColor(ORBColor color) ORB_SWIFT_NAME(getter:ORBColor.cgColor(self:)); #endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h index fafb111..1d2d615 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h @@ -35,11 +35,8 @@ typedef CF_ENUM(uint32_t, ORBColorMode) { ORBColorMode15 = 15, }; -ORB_EXPORT -ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_NOEXCEPT; - -ORB_EXPORT -bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_NOEXCEPT; +ORB_EXPORT ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.workingColorSpace(self:)); +ORB_EXPORT bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.hasExtendedRange(self:)); ORB_EXTERN_C_END diff --git a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift index 1c19462..237deb8 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift @@ -106,5 +106,26 @@ struct ColorTests { let color2 = ORBColor(red: r2, green: g2, blue: b2, alpha: a2) #expect(!color1.isEqual(to: color2)) } + + // MARK: - Color Constants + + @Test("ORBColor Constant", arguments: [ + (ORBColor.clear, Float(0.0), Float(0.0), Float(0.0), Float(0.0)), + (ORBColor.black, Float(0.0), Float(0.0), Float(0.0), Float(1.0)), + (ORBColor.white, Float(1.0), Float(1.0), Float(1.0), Float(1.0)), + (ORBColor.null, Float(-32768.0), Float(-32768.0), Float(-32768.0), Float(-32768.0)), + ]) + func constant(_ color: ORBColor, expectedRed: Float, expectedGreen: Float, expectedBlue: Float, expectedAlpha: Float) { + #expect(color.red.isApproximatelyEqual(to: expectedRed)) + #expect(color.green.isApproximatelyEqual(to: expectedGreen)) + #expect(color.blue.isApproximatelyEqual(to: expectedBlue)) + #expect(color.alpha.isApproximatelyEqual(to: expectedAlpha)) + } + + @Test("ORBColorInvalidComponent") + func colorInvalidComponent() { + let value = ORBColor.invalidComponent + #expect(value.isApproximatelyEqual(to: -32768.0)) + } } From b29bdf76b8e786d23139a91106382e150c1f7991 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 20:40:17 +0800 Subject: [PATCH 07/16] Add ColorSpace.hpp & cpp --- Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp | 106 ++++++++++++++++++ .../OpenRenderBoxCxx/Color/ColorSpace.hpp | 78 +++++++++++++ .../OpenRenderBoxCxx/include/module.modulemap | 6 + 3 files changed, 190 insertions(+) create mode 100644 Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp create mode 100644 Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp diff --git a/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp new file mode 100644 index 0000000..26e614d --- /dev/null +++ b/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp @@ -0,0 +1,106 @@ +// +// ColorSpace.cpp +// OpenRenderBox + +#include + +#if ORB_TARGET_OS_DARWIN + +#include + +namespace ORB { + +ColorSpaceResult color_space_from_cg_name(CFStringRef name) { + if (name == nullptr) { + return { ColorSpace::LinearSRGB, false }; + } else if (CFEqual(name, kCGColorSpaceSRGB) || + CFEqual(name, kCGColorSpaceExtendedSRGB)) { + return { ColorSpace::SRGB, true }; + } else if (CFEqual(name, kCGColorSpaceLinearSRGB) || + CFEqual(name, kCGColorSpaceExtendedLinearSRGB)) { + return { ColorSpace::LinearSRGB, true }; + } else if (CFEqual(name, kCGColorSpaceDisplayP3) || + CFEqual(name, kCGColorSpaceExtendedDisplayP3)) { + return { ColorSpace::DisplayP3, true }; + } else if (CFEqual(name, kCGColorSpaceLinearDisplayP3) || + CFEqual(name, kCGColorSpaceExtendedLinearDisplayP3)) { + return { ColorSpace::LinearDisplayP3, true }; + } else { + return { ColorSpace::LinearSRGB, false }; + } +} + +ColorSpaceResult color_space_from_cg(CGColorSpaceRef colorSpace) { + if (colorSpace == nullptr) { + return { ColorSpace::LinearSRGB, false }; + } + CFStringRef name = CGColorSpaceGetName(colorSpace); + return color_space_from_cg_name(name); +} + +CGColorSpaceRef cg_color_space(ColorSpace colorSpace, bool extended) { + switch (colorSpace) { + case ColorSpace::LinearSRGB: + return extended ? extended_linear_srgb_colorspace() : linear_srgb_colorspace(); + case ColorSpace::SRGB: + return extended ? extended_srgb_colorspace() : srgb_colorspace(); + case ColorSpace::LinearDisplayP3: + return extended ? extended_linear_display_p3_colorspace() : linear_display_p3_colorspace(); + case ColorSpace::DisplayP3: + return extended ? extended_display_p3_colorspace() : display_p3_colorspace(); + case ColorSpace::PQ: + return pq_colorspace(); + case ColorSpace::Invalid: + abort(); + } +} + +CGColorSpaceRef linear_srgb_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB); + return colorSpace; +} + +CGColorSpaceRef extended_linear_srgb_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB); + return colorSpace; +} + +CGColorSpaceRef srgb_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + return colorSpace; +} + +CGColorSpaceRef extended_srgb_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB); + return colorSpace; +} + +CGColorSpaceRef linear_display_p3_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceLinearDisplayP3); + return colorSpace; +} + +CGColorSpaceRef extended_linear_display_p3_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3); + return colorSpace; +} + +CGColorSpaceRef display_p3_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); + return colorSpace; +} + +CGColorSpaceRef extended_display_p3_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedDisplayP3); + return colorSpace; +} + +CGColorSpaceRef pq_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ); + return colorSpace; +} + +} /* namespace ORB */ + +#endif /* ORB_TARGET_OS_DARWIN */ + diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp new file mode 100644 index 0000000..b04ab76 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp @@ -0,0 +1,78 @@ +// +// ColorSpace.hpp +// OpenRenderBox + +#pragma once + +#include + +#if ORB_TARGET_OS_DARWIN +#include +#include + +namespace ORB { + +/// Internal color space representation used by ORB::cg_color_space +enum class ColorSpace : uint32_t { + LinearSRGB = 0, + SRGB = 1, + LinearDisplayP3 = 2, + DisplayP3 = 3, + Invalid = 4, + PQ = 5, +}; + +/// Result of color_space_from_cg_name / color_space_from_cg +/// Packed as: bits 0-7 = ColorSpace enum, bits 8 = valid flag +struct ColorSpaceResult { + ColorSpace colorSpace; + bool valid; +}; + +/// Converts a CGColorSpace name (CFStringRef) to internal ColorSpace enum. +/// @param name The color space name from CGColorSpaceGetName. +/// @return ColorSpaceResult with colorSpace and valid flag. +ColorSpaceResult color_space_from_cg_name(CFStringRef name); + +/// Converts a CGColorSpaceRef to internal ColorSpace enum. +/// @param colorSpace The CGColorSpace to convert. +/// @return ColorSpaceResult with colorSpace and valid flag. +ColorSpaceResult color_space_from_cg(CGColorSpaceRef colorSpace); + +/// Returns a CGColorSpaceRef for the given internal color space. +/// @param colorSpace The internal color space enum value. +/// @param extended If true, returns the extended range variant of the color space. +/// @return A CGColorSpaceRef. The caller does not own this reference. +CGColorSpaceRef cg_color_space(ColorSpace colorSpace, bool extended); + +/// Returns the linear sRGB color space. +CGColorSpaceRef linear_srgb_colorspace(); + +/// Returns the extended linear sRGB color space. +CGColorSpaceRef extended_linear_srgb_colorspace(); + +/// Returns the sRGB color space. +CGColorSpaceRef srgb_colorspace(); + +/// Returns the extended sRGB color space. +CGColorSpaceRef extended_srgb_colorspace(); + +/// Returns the linear Display P3 color space. +CGColorSpaceRef linear_display_p3_colorspace(); + +/// Returns the extended linear Display P3 color space. +CGColorSpaceRef extended_linear_display_p3_colorspace(); + +/// Returns the Display P3 color space. +CGColorSpaceRef display_p3_colorspace(); + +/// Returns the extended Display P3 color space. +CGColorSpaceRef extended_display_p3_colorspace(); + +/// Returns the PQ (HDR) color space. +CGColorSpaceRef pq_colorspace(); + +} // namespace RB + +#endif /* ORB_TARGET_OS_DARWIN */ + diff --git a/Sources/OpenRenderBoxCxx/include/module.modulemap b/Sources/OpenRenderBoxCxx/include/module.modulemap index 1d90804..f5fac40 100644 --- a/Sources/OpenRenderBoxCxx/include/module.modulemap +++ b/Sources/OpenRenderBoxCxx/include/module.modulemap @@ -7,6 +7,12 @@ module OpenRenderBoxCxx.C { export * } +module OpenRenderBoxCxx.Color { + requires cplusplus + umbrella "OpenRenderBoxCxx/Color" + export * +} + module OpenRenderBoxCxx.Path { requires cplusplus umbrella "OpenRenderBoxCxx/Path" From edf3ddb173515afdb2be979fa2b98f9234a227a9 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 21:09:49 +0800 Subject: [PATCH 08/16] Implement ORBColorCopyCGColor --- Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp | 15 +++++++++++++++ Sources/OpenRenderBoxCxx/Color/ORBColor.cpp | 15 +++++++++++---- .../include/OpenRenderBox/ORBColor.h | 3 ++- .../include/OpenRenderBoxCxx/Color/ColorSpace.hpp | 10 +++++++++- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp index 26e614d..a7e2d3c 100644 --- a/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp @@ -100,7 +100,22 @@ CGColorSpaceRef pq_colorspace() { return colorSpace; } +CGColorSpaceRef gray_colorspace() { + static CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGrayGamma2_2); + return colorSpace; +} + } /* namespace ORB */ +ORB::ColorSpace orb_color_space(ORBColorSpace orbColorSpace) { + return ORB::ColorSpace::LinearSRGB; +} + +ORB::ColorSpace orb_color_space(std::optional orbColorSpace) { + if (!orbColorSpace.has_value()) { + return ORB::ColorSpace::LinearSRGB; + } + return ORB::ColorSpace::LinearSRGB; +} #endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp index 1a3e0f2..fc5ee85 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp @@ -3,8 +3,11 @@ // OpenRenderBox #include +#include #include +using namespace ORB; + // Global color constants const ORBColor ORBColorClear = { 0.0f, 0.0f, 0.0f, 0.0f }; const ORBColor ORBColorBlack = { 0.0f, 0.0f, 0.0f, 1.0f }; @@ -120,16 +123,20 @@ ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) { return ORBColorFromComponents2(colorSpace, components, componentCount); } -CGColorRef ORBColorCopyCGColor(ORBColor color) { +CGColorRef ORBColorCopyCGColor(ORBColor color, ORBColorSpace orbColorSpace) { CGFloat components[4] = { static_cast(color.red), static_cast(color.green), static_cast(color.blue), static_cast(color.alpha) }; - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - CGColorRef cgColor = CGColorCreate(colorSpace, components); - CGColorSpaceRelease(colorSpace); + bool isRedOutOfRange = (color.red < 0.0f || color.red > 1.0f); + bool isGreenOutOfRange = (color.green < 0.0f || color.green > 1.0f); + bool isBlueOutOfRange = (color.blue < 0.0f || color.blue > 1.0f); + ORB::ColorSpace colorSpace = orb_color_space(orbColorSpace); + bool extended = isRedOutOfRange || isGreenOutOfRange || isBlueOutOfRange; + CGColorSpaceRef cgColorSpace = ORB::cg_color_space(colorSpace, extended); + CGColorRef cgColor = CGColorCreate(cgColorSpace, components); return cgColor; } diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h index f84e450..0b4558a 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h @@ -5,6 +5,7 @@ #pragma once #include +#include #if ORB_TARGET_OS_DARWIN #include #endif @@ -40,7 +41,7 @@ ORB_EXPORT ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGF ORB_EXPORT ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:componentCount:)); ORB_EXPORT ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_SWIFT_NAME(ORBColor.init(_:premultiplied:)); ORB_EXPORT ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_SWIFT_NAME(ORBColor.init(_:componentCount:)); -ORB_EXPORT CGColorRef ORBColorCopyCGColor(ORBColor color) ORB_SWIFT_NAME(getter:ORBColor.cgColor(self:)); +ORB_EXPORT CGColorRef ORBColorCopyCGColor(ORBColor color, ORBColorSpace orbColorSpace) ORB_SWIFT_NAME(ORBColor.cgColor(self:colorSpace:)); #endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp index b04ab76..2f9aa96 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp @@ -9,6 +9,7 @@ #if ORB_TARGET_OS_DARWIN #include #include +#include namespace ORB { @@ -72,7 +73,14 @@ CGColorSpaceRef extended_display_p3_colorspace(); /// Returns the PQ (HDR) color space. CGColorSpaceRef pq_colorspace(); -} // namespace RB +/// Returns the gray color space. +CGColorSpaceRef gray_colorspace(); + +} /* namespace ORB */ + +ORB::ColorSpace orb_color_space(ORBColorSpace orbColorSpace); + +ORB::ColorSpace orb_color_space(std::optional orbColorSpace); #endif /* ORB_TARGET_OS_DARWIN */ From 8c890a363009950f1e165ab158836b764e6f060f Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 21:22:08 +0800 Subject: [PATCH 09/16] Add namespace for space and mode --- .../include/OpenRenderBox/ORBColorSpace.h | 8 ++++---- Sources/OpenRenderBoxShims/Export.swift | 2 -- Tests/OpenRenderBoxCompatibilityTests/Shims.swift | 2 -- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h index 1d2d615..14a4163 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h @@ -10,13 +10,13 @@ ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN -typedef CF_ENUM(uint32_t, ORBColorSpace) { +typedef ORB_ENUM(uint32_t, ORBColorSpace) { ORBColorSpaceInvalid = 0, ORBColorSpaceSRGB = 1, ORBColorSpaceExtendedLinear = 2, -}; +} ORB_SWIFT_NAME(ORBColor.Space); -typedef CF_ENUM(uint32_t, ORBColorMode) { +typedef ORB_ENUM(uint32_t, ORBColorMode) { ORBColorMode0 = 0, ORBColorMode1 = 1, ORBColorMode2 = 2, @@ -33,7 +33,7 @@ typedef CF_ENUM(uint32_t, ORBColorMode) { ORBColorMode13 = 13, ORBColorMode14 = 14, ORBColorMode15 = 15, -}; +} ORB_SWIFT_NAME(ORBColor.Mode); ORB_EXPORT ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.workingColorSpace(self:)); ORB_EXPORT bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.hasExtendedRange(self:)); diff --git a/Sources/OpenRenderBoxShims/Export.swift b/Sources/OpenRenderBoxShims/Export.swift index 3fdec01..197e57c 100644 --- a/Sources/OpenRenderBoxShims/Export.swift +++ b/Sources/OpenRenderBoxShims/Export.swift @@ -5,8 +5,6 @@ #if OPENRENDERBOX_RENDERBOX @_exported public import RenderBox public typealias ORBColor = RBColor -public typealias ORBColorMode = RBColorMode -public typealias ORBColorSpace = RBColorSpace public typealias ORBDevice = RBDevice public typealias ORBLayer = RBLayer public typealias ORBPath = RBPath diff --git a/Tests/OpenRenderBoxCompatibilityTests/Shims.swift b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift index 1ef54f1..0160549 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/Shims.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/Shims.swift @@ -5,8 +5,6 @@ #if OPENRENDERBOX_COMPATIBILITY_TEST @_exported public import RenderBox public typealias ORBColor = RBColor -public typealias ORBColorMode = RBColorMode -public typealias ORBColorSpace = RBColorSpace public typealias ORBDevice = RBDevice public typealias ORBLayer = RBLayer public typealias ORBPath = RBPath From 665815d6d1a135bdd172c29878a81eb28a56720c Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 21:58:21 +0800 Subject: [PATCH 10/16] Fix color space convert issue --- Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp | 61 +++++++++++----- Sources/OpenRenderBoxCxx/Color/ORBColor.cpp | 2 +- .../OpenRenderBoxCxx/Color/ORBColorSpace.cpp | 72 +++++++++---------- .../include/OpenRenderBox/ORBColorSpace.h | 6 +- .../OpenRenderBoxCxx/Color/ColorSpace.hpp | 21 ++---- 5 files changed, 92 insertions(+), 70 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp index a7e2d3c..dedb13c 100644 --- a/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ColorSpace.cpp @@ -10,29 +10,29 @@ namespace ORB { -ColorSpaceResult color_space_from_cg_name(CFStringRef name) { +std::optional color_space_from_cg_name(CFStringRef name) { if (name == nullptr) { - return { ColorSpace::LinearSRGB, false }; + return std::nullopt; } else if (CFEqual(name, kCGColorSpaceSRGB) || CFEqual(name, kCGColorSpaceExtendedSRGB)) { - return { ColorSpace::SRGB, true }; + return ColorSpace::SRGB; } else if (CFEqual(name, kCGColorSpaceLinearSRGB) || CFEqual(name, kCGColorSpaceExtendedLinearSRGB)) { - return { ColorSpace::LinearSRGB, true }; + return ColorSpace::LinearSRGB; } else if (CFEqual(name, kCGColorSpaceDisplayP3) || CFEqual(name, kCGColorSpaceExtendedDisplayP3)) { - return { ColorSpace::DisplayP3, true }; + return ColorSpace::DisplayP3; } else if (CFEqual(name, kCGColorSpaceLinearDisplayP3) || CFEqual(name, kCGColorSpaceExtendedLinearDisplayP3)) { - return { ColorSpace::LinearDisplayP3, true }; + return ColorSpace::LinearDisplayP3; } else { - return { ColorSpace::LinearSRGB, false }; + return std::nullopt; } } -ColorSpaceResult color_space_from_cg(CGColorSpaceRef colorSpace) { +std::optional color_space_from_cg(CGColorSpaceRef colorSpace) { if (colorSpace == nullptr) { - return { ColorSpace::LinearSRGB, false }; + return std::nullopt; } CFStringRef name = CGColorSpaceGetName(colorSpace); return color_space_from_cg_name(name); @@ -50,7 +50,7 @@ CGColorSpaceRef cg_color_space(ColorSpace colorSpace, bool extended) { return extended ? extended_display_p3_colorspace() : display_p3_colorspace(); case ColorSpace::PQ: return pq_colorspace(); - case ColorSpace::Invalid: + case ColorSpace::Unknown: abort(); } } @@ -107,15 +107,42 @@ CGColorSpaceRef gray_colorspace() { } /* namespace ORB */ -ORB::ColorSpace orb_color_space(ORBColorSpace orbColorSpace) { - return ORB::ColorSpace::LinearSRGB; +std::optional orb_color_space(ORBColorSpace orbColorSpace) { + switch (orbColorSpace) { + case ORBColorSpaceDefault: + return std::nullopt; + case ORBColorSpaceSRGB: + return ORB::ColorSpace::SRGB; + case ORBColorSpaceLinearSRGB: + return ORB::ColorSpace::LinearSRGB; + case ORBColorSpaceDisplayP3: + return ORB::ColorSpace::DisplayP3; + case ORBColorSpaceLinearDisplayP3: + return ORB::ColorSpace::LinearDisplayP3; + default: + return std::nullopt; + } } -ORB::ColorSpace orb_color_space(std::optional orbColorSpace) { - if (!orbColorSpace.has_value()) { - return ORB::ColorSpace::LinearSRGB; +ORBColorSpace orb_color_space(std::optional colorSpace) { + if (!colorSpace.has_value()) { + return ORBColorSpaceDefault; + } + switch (colorSpace.value()) { + case ORB::ColorSpace::LinearSRGB: + return ORBColorSpaceLinearSRGB; + case ORB::ColorSpace::SRGB: + return ORBColorSpaceSRGB; + case ORB::ColorSpace::LinearDisplayP3: + return ORBColorSpaceLinearDisplayP3; + case ORB::ColorSpace::DisplayP3: + return ORBColorSpaceDisplayP3; + case ORB::ColorSpace::Unknown: + case ORB::ColorSpace::PQ: + return ORBColorSpaceDefault; + default: + return ORBColorSpaceLinearSRGB; } - return ORB::ColorSpace::LinearSRGB; } -#endif /* ORB_TARGET_OS_DARWIN */ +#endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp index fc5ee85..655d9dd 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColor.cpp @@ -133,7 +133,7 @@ CGColorRef ORBColorCopyCGColor(ORBColor color, ORBColorSpace orbColorSpace) { bool isRedOutOfRange = (color.red < 0.0f || color.red > 1.0f); bool isGreenOutOfRange = (color.green < 0.0f || color.green > 1.0f); bool isBlueOutOfRange = (color.blue < 0.0f || color.blue > 1.0f); - ORB::ColorSpace colorSpace = orb_color_space(orbColorSpace); + ORB::ColorSpace colorSpace = orb_color_space(orbColorSpace).value_or(ORB::ColorSpace::LinearSRGB); bool extended = isRedOutOfRange || isGreenOutOfRange || isBlueOutOfRange; CGColorSpaceRef cgColorSpace = ORB::cg_color_space(colorSpace, extended); CGColorRef cgColor = CGColorCreate(cgColorSpace, components); diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp index 103da23..b88f855 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp @@ -4,39 +4,39 @@ #include -static const ORBColorSpace kColorModeWorkingColorSpaceTable[16] = { - ORBColorSpaceSRGB, // 0 - ORBColorSpaceExtendedLinear, // 1 - ORBColorSpaceExtendedLinear, // 2 - ORBColorSpaceSRGB, // 3 - ORBColorSpaceSRGB, // 4 - ORBColorSpaceSRGB, // 5 - ORBColorSpaceExtendedLinear, // 6 - ORBColorSpaceExtendedLinear, // 7 - ORBColorSpaceExtendedLinear, // 8 - ORBColorSpaceSRGB, // 9 - ORBColorSpaceExtendedLinear, // 10 - ORBColorSpaceSRGB, // 11 - ORBColorSpaceSRGB, // 12 - ORBColorSpaceExtendedLinear, // 13 - ORBColorSpaceSRGB, // 14 - ORBColorSpaceExtendedLinear, // 15 -}; - -ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) { - if (mode > 15) { - return ORBColorSpaceInvalid; - } - return kColorModeWorkingColorSpaceTable[mode]; -} - -bool ORBColorModeHasExtendedRange(ORBColorMode mode) { - // 0x3804 = 0011 1000 0000 0100 binary - // Extended range modes: 2, 11, 12, 13 - if (mode >= 14) { - return false; - } - uint32_t mask = 0x3804; - return (mask >> mode) & 1; -} - +//static const ORBColorSpace kColorModeWorkingColorSpaceTable[16] = { +// ORBColorSpaceSRGB, // 0 +// ORBColorSpaceExtendedLinear, // 1 +// ORBColorSpaceExtendedLinear, // 2 +// ORBColorSpaceSRGB, // 3 +// ORBColorSpaceSRGB, // 4 +// ORBColorSpaceSRGB, // 5 +// ORBColorSpaceExtendedLinear, // 6 +// ORBColorSpaceExtendedLinear, // 7 +// ORBColorSpaceExtendedLinear, // 8 +// ORBColorSpaceSRGB, // 9 +// ORBColorSpaceExtendedLinear, // 10 +// ORBColorSpaceSRGB, // 11 +// ORBColorSpaceSRGB, // 12 +// ORBColorSpaceExtendedLinear, // 13 +// ORBColorSpaceSRGB, // 14 +// ORBColorSpaceExtendedLinear, // 15 +//}; +// +//ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) { +// if (mode > 15) { +// return ORBColorSpaceInvalid; +// } +// return kColorModeWorkingColorSpaceTable[mode]; +//} +// +//bool ORBColorModeHasExtendedRange(ORBColorMode mode) { +// // 0x3804 = 0011 1000 0000 0100 binary +// // Extended range modes: 2, 11, 12, 13 +// if (mode >= 14) { +// return false; +// } +// uint32_t mask = 0x3804; +// return (mask >> mode) & 1; +//} +// diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h index 14a4163..d161422 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h @@ -11,9 +11,11 @@ ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN typedef ORB_ENUM(uint32_t, ORBColorSpace) { - ORBColorSpaceInvalid = 0, + ORBColorSpaceDefault = 0, ORBColorSpaceSRGB = 1, - ORBColorSpaceExtendedLinear = 2, + ORBColorSpaceLinearSRGB = 2, + ORBColorSpaceDisplayP3 = 3, + ORBColorSpaceLinearDisplayP3 = 4, } ORB_SWIFT_NAME(ORBColor.Space); typedef ORB_ENUM(uint32_t, ORBColorMode) { diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp index 2f9aa96..e683476 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp @@ -19,26 +19,19 @@ enum class ColorSpace : uint32_t { SRGB = 1, LinearDisplayP3 = 2, DisplayP3 = 3, - Invalid = 4, + Unknown = 4, PQ = 5, }; -/// Result of color_space_from_cg_name / color_space_from_cg -/// Packed as: bits 0-7 = ColorSpace enum, bits 8 = valid flag -struct ColorSpaceResult { - ColorSpace colorSpace; - bool valid; -}; - /// Converts a CGColorSpace name (CFStringRef) to internal ColorSpace enum. /// @param name The color space name from CGColorSpaceGetName. -/// @return ColorSpaceResult with colorSpace and valid flag. -ColorSpaceResult color_space_from_cg_name(CFStringRef name); +/// @return The ColorSpace if recognized, or std::nullopt if not. +std::optional color_space_from_cg_name(CFStringRef name); /// Converts a CGColorSpaceRef to internal ColorSpace enum. /// @param colorSpace The CGColorSpace to convert. -/// @return ColorSpaceResult with colorSpace and valid flag. -ColorSpaceResult color_space_from_cg(CGColorSpaceRef colorSpace); +/// @return The ColorSpace if recognized, or std::nullopt if not. +std::optional color_space_from_cg(CGColorSpaceRef colorSpace); /// Returns a CGColorSpaceRef for the given internal color space. /// @param colorSpace The internal color space enum value. @@ -78,9 +71,9 @@ CGColorSpaceRef gray_colorspace(); } /* namespace ORB */ -ORB::ColorSpace orb_color_space(ORBColorSpace orbColorSpace); +std::optional orb_color_space(ORBColorSpace orbColorSpace); -ORB::ColorSpace orb_color_space(std::optional orbColorSpace); +ORBColorSpace orb_color_space(std::optional colorSpace); #endif /* ORB_TARGET_OS_DARWIN */ From 0263511502d59384ef3eb9de99d8162ce918f3a0 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 22:02:26 +0800 Subject: [PATCH 11/16] Spilit ORBColorMode --- .../{ORBColorSpace.cpp => ORBColorMode.cpp} | 6 +-- .../include/OpenRenderBox/ORBColorMode.h | 39 +++++++++++++++++++ .../include/OpenRenderBox/ORBColorSpace.h | 22 ----------- .../include/OpenRenderBox/OpenRenderBox.h | 1 + 4 files changed, 43 insertions(+), 25 deletions(-) rename Sources/OpenRenderBoxCxx/Color/{ORBColorSpace.cpp => ORBColorMode.cpp} (95%) create mode 100644 Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp similarity index 95% rename from Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp rename to Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp index b88f855..dbce034 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColorSpace.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp @@ -1,9 +1,9 @@ // -// ORBColorSpace.cpp +// ORBColorMode.cpp // OpenRenderBox -#include - +#include +// //static const ORBColorSpace kColorModeWorkingColorSpaceTable[16] = { // ORBColorSpaceSRGB, // 0 // ORBColorSpaceExtendedLinear, // 1 diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h new file mode 100644 index 0000000..982b3b2 --- /dev/null +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h @@ -0,0 +1,39 @@ +// +// ORBColorMode.h +// OpenRenderBox + +#pragma once + +#include +#include + +ORB_ASSUME_NONNULL_BEGIN + +ORB_EXTERN_C_BEGIN + +typedef ORB_ENUM(uint32_t, ORBColorMode) { + ORBColorMode0 = 0, + ORBColorMode1 = 1, + ORBColorMode2 = 2, + ORBColorMode3 = 3, + ORBColorMode4 = 4, + ORBColorMode5 = 5, + ORBColorMode6 = 6, + ORBColorMode7 = 7, + ORBColorMode8 = 8, + ORBColorMode9 = 9, + ORBColorMode10 = 10, + ORBColorMode11 = 11, + ORBColorMode12 = 12, + ORBColorMode13 = 13, + ORBColorMode14 = 14, + ORBColorMode15 = 15, +} ORB_SWIFT_NAME(ORBColor.Mode); + +ORB_EXPORT ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.workingColorSpace(self:)); +ORB_EXPORT bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.hasExtendedRange(self:)); + +ORB_EXTERN_C_END + +ORB_ASSUME_NONNULL_END + diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h index d161422..5382768 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorSpace.h @@ -18,28 +18,6 @@ typedef ORB_ENUM(uint32_t, ORBColorSpace) { ORBColorSpaceLinearDisplayP3 = 4, } ORB_SWIFT_NAME(ORBColor.Space); -typedef ORB_ENUM(uint32_t, ORBColorMode) { - ORBColorMode0 = 0, - ORBColorMode1 = 1, - ORBColorMode2 = 2, - ORBColorMode3 = 3, - ORBColorMode4 = 4, - ORBColorMode5 = 5, - ORBColorMode6 = 6, - ORBColorMode7 = 7, - ORBColorMode8 = 8, - ORBColorMode9 = 9, - ORBColorMode10 = 10, - ORBColorMode11 = 11, - ORBColorMode12 = 12, - ORBColorMode13 = 13, - ORBColorMode14 = 14, - ORBColorMode15 = 15, -} ORB_SWIFT_NAME(ORBColor.Mode); - -ORB_EXPORT ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.workingColorSpace(self:)); -ORB_EXPORT bool ORBColorModeHasExtendedRange(ORBColorMode mode) ORB_SWIFT_NAME(getter:ORBColorMode.hasExtendedRange(self:)); - ORB_EXTERN_C_END ORB_ASSUME_NONNULL_END diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h index 8c24a93..07d626a 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/OpenRenderBox.h @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From 4ffd69328cb7f87338676a8ed2c0b9bf9ee1bdff Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 22:17:06 +0800 Subject: [PATCH 12/16] Update ORBColorMode --- .../OpenRenderBoxCxx/Color/ORBColorMode.cpp | 74 +++++++++---------- .../include/OpenRenderBox/ORBColorMode.h | 1 + 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp b/Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp index dbce034..c7c3ed5 100644 --- a/Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp +++ b/Sources/OpenRenderBoxCxx/Color/ORBColorMode.cpp @@ -3,40 +3,40 @@ // OpenRenderBox #include -// -//static const ORBColorSpace kColorModeWorkingColorSpaceTable[16] = { -// ORBColorSpaceSRGB, // 0 -// ORBColorSpaceExtendedLinear, // 1 -// ORBColorSpaceExtendedLinear, // 2 -// ORBColorSpaceSRGB, // 3 -// ORBColorSpaceSRGB, // 4 -// ORBColorSpaceSRGB, // 5 -// ORBColorSpaceExtendedLinear, // 6 -// ORBColorSpaceExtendedLinear, // 7 -// ORBColorSpaceExtendedLinear, // 8 -// ORBColorSpaceSRGB, // 9 -// ORBColorSpaceExtendedLinear, // 10 -// ORBColorSpaceSRGB, // 11 -// ORBColorSpaceSRGB, // 12 -// ORBColorSpaceExtendedLinear, // 13 -// ORBColorSpaceSRGB, // 14 -// ORBColorSpaceExtendedLinear, // 15 -//}; -// -//ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) { -// if (mode > 15) { -// return ORBColorSpaceInvalid; -// } -// return kColorModeWorkingColorSpaceTable[mode]; -//} -// -//bool ORBColorModeHasExtendedRange(ORBColorMode mode) { -// // 0x3804 = 0011 1000 0000 0100 binary -// // Extended range modes: 2, 11, 12, 13 -// if (mode >= 14) { -// return false; -// } -// uint32_t mask = 0x3804; -// return (mask >> mode) & 1; -//} -// + +ORBColorSpace ORBColorModeWorkingColorSpace(ORBColorMode mode) { + switch (mode) { + case ORBColorMode0: + case ORBColorMode3: + case ORBColorMode4: + case ORBColorMode5: + case ORBColorMode9: + case ORBColorMode11: + case ORBColorMode12: + case ORBColorMode14: + return ORBColorSpaceSRGB; + case ORBColorMode1: + case ORBColorMode2: + case ORBColorMode6: + case ORBColorMode7: + case ORBColorMode8: + case ORBColorMode10: + case ORBColorMode13: + case ORBColorMode15: + return ORBColorSpaceLinearSRGB; + default: + return ORBColorSpaceDefault; + } +} + +bool ORBColorModeHasExtendedRange(ORBColorMode mode) { + switch (mode) { + case ORBColorMode2: + case ORBColorMode11: + case ORBColorMode12: + case ORBColorMode13: + return true; + default: + return false; + } +} \ No newline at end of file diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h index 982b3b2..516d8d2 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColorMode.h @@ -11,6 +11,7 @@ ORB_ASSUME_NONNULL_BEGIN ORB_EXTERN_C_BEGIN +// TODO: Review those names later typedef ORB_ENUM(uint32_t, ORBColorMode) { ORBColorMode0 = 0, ORBColorMode1 = 1, From ffd13172ec602b5d22e29b306e145d31f5565922 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 22:25:31 +0800 Subject: [PATCH 13/16] Update test case --- .../include/OpenRenderBox/ORBColor.h | 4 +- .../ColorTests.swift | 76 ++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h index 0b4558a..557ed27 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBox/ORBColor.h @@ -37,11 +37,13 @@ ORB_EXPORT bool ORBColorEqualToColor(ORBColor lhs, ORBColor rhs) ORB_SWIFT_NAME( #if ORB_TARGET_OS_DARWIN +// TODO: Verify the interface ORB_EXPORT ORBColor ORBColorFromComponents(CGColorSpaceRef colorSpace, const CGFloat *components, bool premultiplied) ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:premultiplied:)); ORB_EXPORT ORBColor ORBColorFromComponents2(CGColorSpaceRef colorSpace, const CGFloat *components, size_t componentCount) ORB_SWIFT_NAME(ORBColor.init(colorSpace:components:componentCount:)); ORB_EXPORT ORBColor ORBColorFromCGColor(CGColorRef color, bool premultiplied) ORB_SWIFT_NAME(ORBColor.init(_:premultiplied:)); ORB_EXPORT ORBColor ORBColorFromCGColor2(CGColorRef color, size_t componentCount) ORB_SWIFT_NAME(ORBColor.init(_:componentCount:)); -ORB_EXPORT CGColorRef ORBColorCopyCGColor(ORBColor color, ORBColorSpace orbColorSpace) ORB_SWIFT_NAME(ORBColor.cgColor(self:colorSpace:)); + +ORB_EXPORT CGColorRef _Nullable ORBColorCopyCGColor(ORBColor color, ORBColorSpace orbColorSpace) CF_RETURNS_RETAINED ORB_SWIFT_NAME(ORBColor.cgColor(self:colorSpace:)); #endif /* ORB_TARGET_OS_DARWIN */ diff --git a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift index 237deb8..c6f4cea 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift @@ -4,6 +4,9 @@ import Testing import Numerics +#if canImport(CoreGraphics) +import CoreGraphics +#endif @Suite struct ColorTests { @@ -127,5 +130,76 @@ struct ColorTests { let value = ORBColor.invalidComponent #expect(value.isApproximatelyEqual(to: -32768.0)) } -} + // MARK: - ORBColorCopyCGColor + + #if canImport(CoreGraphics) + @Test("ORBColorCopyCGColor", arguments: [ + (ORBColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0), ORBColor.Space.SRGB), + (ORBColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0), ORBColor.Space.linearSRGB), + (ORBColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 0.5), ORBColor.Space.displayP3), + (ORBColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0), ORBColor.Space.linearDisplayP3), + ]) + func colorCopyCGColor(color: ORBColor, colorSpace: ORBColor.Space) throws { + let cgColor = try #require(color.cgColor(colorSpace: colorSpace)) + #expect(cgColor.numberOfComponents == 4) + let components = cgColor.components! + #expect(components[0].isApproximatelyEqual(to: CGFloat(color.red))) + #expect(components[1].isApproximatelyEqual(to: CGFloat(color.green))) + #expect(components[2].isApproximatelyEqual(to: CGFloat(color.blue))) + #expect(components[3].isApproximatelyEqual(to: CGFloat(color.alpha))) + } + #endif + + + @Suite + struct ColorModeTests { + // MARK: - ORBColorModeWorkingColorSpace + + @Test("ORBColorModeWorkingColorSpace", arguments: [ + (ORBColor.Mode.mode0, ORBColor.Space.SRGB), + (ORBColor.Mode.mode1, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode2, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode3, ORBColor.Space.SRGB), + (ORBColor.Mode.mode4, ORBColor.Space.SRGB), + (ORBColor.Mode.mode5, ORBColor.Space.SRGB), + (ORBColor.Mode.mode6, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode7, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode8, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode9, ORBColor.Space.SRGB), + (ORBColor.Mode.mode10, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode11, ORBColor.Space.SRGB), + (ORBColor.Mode.mode12, ORBColor.Space.SRGB), + (ORBColor.Mode.mode13, ORBColor.Space.linearSRGB), + (ORBColor.Mode.mode14, ORBColor.Space.SRGB), + (ORBColor.Mode.mode15, ORBColor.Space.linearSRGB), + ]) + func colorModeWorkingColorSpace(mode: ORBColor.Mode, expectedColorSpace: ORBColor.Space) { + #expect(mode.workingColorSpace == expectedColorSpace) + } + + // MARK: - ORBColorModeHasExtendedRange + + @Test("ORBColorModeHasExtendedRange", arguments: [ + (ORBColor.Mode.mode0, false), + (ORBColor.Mode.mode1, false), + (ORBColor.Mode.mode2, true), + (ORBColor.Mode.mode3, false), + (ORBColor.Mode.mode4, false), + (ORBColor.Mode.mode5, false), + (ORBColor.Mode.mode6, false), + (ORBColor.Mode.mode7, false), + (ORBColor.Mode.mode8, false), + (ORBColor.Mode.mode9, false), + (ORBColor.Mode.mode10, false), + (ORBColor.Mode.mode11, true), + (ORBColor.Mode.mode12, true), + (ORBColor.Mode.mode13, true), + (ORBColor.Mode.mode14, false), + (ORBColor.Mode.mode15, false), + ]) + func colorModeHasExtendedRange(mode: ORBColor.Mode, expectedResult: Bool) { + #expect(mode.hasExtendedRange == expectedResult) + } + } +} From 5466d04c199b75cdea2ef09b916e808ce0744efe Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 22:37:34 +0800 Subject: [PATCH 14/16] Update dependency --- Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.resolved b/Package.resolved index 300d62a..e0d27a5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -7,7 +7,7 @@ "location" : "https://github.com/OpenSwiftUIProject/DarwinPrivateFrameworks.git", "state" : { "branch" : "main", - "revision" : "ba41f455eb1f321921cfa15b8e0c0537faae418b" + "revision" : "0d5aa5bc8533ea6a2607a97c0fe0da54726a5148" } }, { From 36914d64d9c33148ad3b66afea14c1926d817e02 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 23:06:24 +0800 Subject: [PATCH 15/16] Fix ubuntu ORB is missing issue --- .../include/OpenRenderBoxCxx/Color/ColorSpace.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp index e683476..241ef7c 100644 --- a/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp +++ b/Sources/OpenRenderBoxCxx/include/OpenRenderBoxCxx/Color/ColorSpace.hpp @@ -9,6 +9,7 @@ #if ORB_TARGET_OS_DARWIN #include #include +#endif /* ORB_TARGET_OS_DARWIN */ #include namespace ORB { @@ -23,6 +24,7 @@ enum class ColorSpace : uint32_t { PQ = 5, }; +#if ORB_TARGET_OS_DARWIN /// Converts a CGColorSpace name (CFStringRef) to internal ColorSpace enum. /// @param name The color space name from CGColorSpaceGetName. /// @return The ColorSpace if recognized, or std::nullopt if not. @@ -68,12 +70,10 @@ CGColorSpaceRef pq_colorspace(); /// Returns the gray color space. CGColorSpaceRef gray_colorspace(); +#endif /* ORB_TARGET_OS_DARWIN */ } /* namespace ORB */ std::optional orb_color_space(ORBColorSpace orbColorSpace); -ORBColorSpace orb_color_space(std::optional colorSpace); - -#endif /* ORB_TARGET_OS_DARWIN */ - +ORBColorSpace orb_color_space(std::optional colorSpace); \ No newline at end of file From 590f3217f9603a76bc8da761b03049e5b02f276f Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 4 Jan 2026 23:08:06 +0800 Subject: [PATCH 16/16] Fix CI issue --- Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift index c6f4cea..b3d1685 100644 --- a/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift +++ b/Tests/OpenRenderBoxCompatibilityTests/ColorTests.swift @@ -151,7 +151,7 @@ struct ColorTests { } #endif - + #if compiler(>=6.2) // old Xcode version swift-testing bug @Suite struct ColorModeTests { // MARK: - ORBColorModeWorkingColorSpace @@ -202,4 +202,5 @@ struct ColorTests { #expect(mode.hasExtendedRange == expectedResult) } } + #endif }