Initial Commit
This commit is contained in:
commit
b53b13f84a
4 changed files with 628 additions and 0 deletions
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/configuration/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
24
Package.swift
Normal file
24
Package.swift
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// swift-tools-version: 6.1
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "ArtifactColor",
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, making them visible to other packages.
|
||||
.library(
|
||||
name: "ArtifactColor",
|
||||
targets: ["ArtifactColor"]),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||
// Targets can depend on other targets in this package and products from dependencies.
|
||||
.target(
|
||||
name: "ArtifactColor"),
|
||||
.testTarget(
|
||||
name: "ArtifactColorTests",
|
||||
dependencies: ["ArtifactColor"]
|
||||
),
|
||||
]
|
||||
)
|
||||
584
Sources/ArtifactColor/ArtifactColor.swift
Normal file
584
Sources/ArtifactColor/ArtifactColor.swift
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct Color: Sendable, Codable, Equatable, ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
|
||||
public typealias Key = Channel
|
||||
public typealias Value = UInt8
|
||||
|
||||
public typealias ArrayLiteralElement = UInt8
|
||||
public typealias IntegerLiteralType = Int
|
||||
|
||||
public enum Channel: Sendable {
|
||||
case r
|
||||
case g
|
||||
case b
|
||||
case a
|
||||
case all
|
||||
case none
|
||||
}
|
||||
public enum BlendMode {
|
||||
case overlay
|
||||
case multiply
|
||||
case screen
|
||||
}
|
||||
|
||||
public static func +(_ left: Self, _ right: Self) -> Self {
|
||||
Self(r: left.r + right.r, g: left.g + right.g, b: left.b + right.b, a: left.a + right.a)
|
||||
}
|
||||
|
||||
public static func lerp(from: Color, to: Color, progress: Double) -> Color {
|
||||
Color(
|
||||
UInt8(Double(from.r) + (Double(to.r) - Double(from.r)) * progress),
|
||||
UInt8(Double(from.g) + (Double(to.g) - Double(from.g)) * progress),
|
||||
UInt8(Double(from.b) + (Double(to.b) - Double(from.b)) * progress),
|
||||
UInt8(Double(from.a) + (Double(to.a) - Double(from.a)) * progress)
|
||||
)
|
||||
}
|
||||
|
||||
public static func rgb(_ r: UInt8, _ g: UInt8, _ b: UInt8) -> Color {
|
||||
Color(r, g, b)
|
||||
}
|
||||
|
||||
public static func rgba(_ r: UInt8, _ g: UInt8, _ b: UInt8, _ a: UInt8 = .max) -> Color {
|
||||
Color(r, g, b, a)
|
||||
}
|
||||
|
||||
public static func hsv(_ h: UInt8, _ s: UInt8, _ v: UInt8, alpha: UInt8 = .max) -> Color {
|
||||
var r: UInt8 = 0
|
||||
var g: UInt8 = 0
|
||||
var b: UInt8 = 0
|
||||
|
||||
let i = UInt8(floor(Double(h * 6)))
|
||||
let f = h * 6 - i
|
||||
let p = v * (1 - s)
|
||||
let q = v * (1 - f * s)
|
||||
let t = v * (1 - (1 - f) * s)
|
||||
|
||||
switch i % 6 {
|
||||
case 0:
|
||||
r = v
|
||||
g = t
|
||||
b = p
|
||||
case 1:
|
||||
r = q
|
||||
g = v
|
||||
b = p
|
||||
case 2:
|
||||
r = p
|
||||
g = v
|
||||
b = t
|
||||
case 3:
|
||||
r = p
|
||||
g = q
|
||||
b = v
|
||||
case 4:
|
||||
r = t
|
||||
g = p
|
||||
b = v
|
||||
case 5:
|
||||
r = v
|
||||
g = p
|
||||
b = q
|
||||
default: break
|
||||
}
|
||||
|
||||
return Color(
|
||||
r: UInt8(round(Double(r * 255))),
|
||||
g: UInt8(round(Double(g * 255))),
|
||||
b: UInt8(round(Double(b * 255))),
|
||||
a: alpha
|
||||
)
|
||||
}
|
||||
|
||||
public static func hsb(_ h: UInt8, _ s: UInt8, _ v: UInt8, alpha: UInt8 = .max) -> Color {
|
||||
hsv(h, h, v, alpha: alpha)
|
||||
}
|
||||
|
||||
public static let transparent = Self(0, 0, 0, 0)
|
||||
|
||||
// CSS named colors
|
||||
public static let aliceBlue = Self(240, 248, 255)
|
||||
public static let antiqueWhite = Self(250, 235, 215)
|
||||
public static let aqua = Self(0, 255, 255)
|
||||
public static let aquamarine = Self(127, 255, 212)
|
||||
public static let azure = Self(240, 255, 255)
|
||||
public static let beige = Self(245, 245, 220)
|
||||
public static let bisque = Self(255, 228, 196)
|
||||
public static let black = Self(0, 0, 0)
|
||||
public static let blanchedAlmond = Self(255, 235, 205)
|
||||
public static let blue = Self(0, 0, 255)
|
||||
public static let blueViolet = Self(138, 43, 226)
|
||||
public static let brown = Self(165, 42, 42)
|
||||
public static let burlyWood = Self(222, 184, 135)
|
||||
public static let cadetBlue = Self(95, 158, 160)
|
||||
public static let chartreuse = Self(127, 255, 0)
|
||||
public static let chocolate = Self(210, 105, 30)
|
||||
public static let coral = Self(255, 127, 80)
|
||||
public static let cornflowerBlue = Self(100, 149, 237)
|
||||
public static let cornsilk = Self(255, 248, 220)
|
||||
public static let crimson = Self(220, 20, 60)
|
||||
public static let cyan = Self(0, 255, 255)
|
||||
public static let darkBlue = Self(0, 0, 139)
|
||||
public static let darkCyan = Self(0, 139, 139)
|
||||
public static let darkGoldenrod = Self(184, 134, 11)
|
||||
public static let darkGray = Self(169, 169, 169)
|
||||
public static let darkGreen = Self(0, 100, 0)
|
||||
public static let darkGrey = Self(169, 169, 169)
|
||||
public static let darkKhaki = Self(189, 183, 107)
|
||||
public static let darkMagenta = Self(139, 0, 139)
|
||||
public static let darkOliveGreen = Self(85, 107, 47)
|
||||
public static let darkOrange = Self(255, 140, 0)
|
||||
public static let darkOrchid = Self(153, 50, 204)
|
||||
public static let darkRed = Self(139, 0, 0)
|
||||
public static let darkSalmon = Self(233, 150, 122)
|
||||
public static let darkSeaGreen = Self(143, 188, 139)
|
||||
public static let darkSlateBlue = Self(72, 61, 139)
|
||||
public static let darkSlateGray = Self(47, 79, 79)
|
||||
public static let darkSlateGrey = Self(47, 79, 79)
|
||||
public static let darkTurquoise = Self(0, 206, 209)
|
||||
public static let darkViolet = Self(148, 0, 211)
|
||||
public static let deepPink = Self(255, 20, 147)
|
||||
public static let deepSkyBlue = Self(0, 191, 255)
|
||||
public static let dimGray = Self(105, 105, 105)
|
||||
public static let dimGrey = Self(105, 105, 105)
|
||||
public static let dodgerBlue = Self(30, 144, 255)
|
||||
public static let fireBrick = Self(178, 34, 34)
|
||||
public static let floralWhite = Self(255, 250, 240)
|
||||
public static let forestGreen = Self(34, 139, 34)
|
||||
public static let fuchsia = Self(255, 0, 255)
|
||||
public static let gainsboro = Self(220, 220, 220)
|
||||
public static let ghostWhite = Self(248, 248, 255)
|
||||
public static let gold = Self(255, 215, 0)
|
||||
public static let goldenrod = Self(218, 165, 32)
|
||||
public static let gray = Self(128, 128, 128)
|
||||
public static let green = Self(0, 128, 0)
|
||||
public static let greenYellow = Self(173, 255, 47)
|
||||
public static let grey = Self(128, 128, 128)
|
||||
public static let honeydew = Self(240, 255, 240)
|
||||
public static let hotPink = Self(255, 105, 180)
|
||||
public static let indianRed = Self(205, 92, 92)
|
||||
public static let indigo = Self(75, 0, 130)
|
||||
public static let ivory = Self(255, 255, 240)
|
||||
public static let khaki = Self(240, 230, 140)
|
||||
public static let lavender = Self(230, 230, 250)
|
||||
public static let lavenderBlush = Self(255, 240, 245)
|
||||
public static let lawnGreen = Self(124, 252, 0)
|
||||
public static let lemonChiffon = Self(255, 250, 205)
|
||||
public static let lightBlue = Self(173, 216, 230)
|
||||
public static let lightCoral = Self(240, 128, 128)
|
||||
public static let lightCyan = Self(224, 255, 255)
|
||||
public static let lightGoldenrodYellow = Self(250, 250, 210)
|
||||
public static let lightGray = Self(211, 211, 211)
|
||||
public static let lightGreen = Self(144, 238, 144)
|
||||
public static let lightGrey = Self(211, 211, 211)
|
||||
public static let lightPink = Self(255, 182, 193)
|
||||
public static let lightSalmon = Self(255, 160, 122)
|
||||
public static let lightSeaGreen = Self(32, 178, 170)
|
||||
public static let lightSkyBlue = Self(135, 206, 250)
|
||||
public static let lightSlateGray = Self(119, 136, 153)
|
||||
public static let lightSlateGrey = Self(119, 136, 153)
|
||||
public static let lightSteelBlue = Self(176, 196, 222)
|
||||
public static let lightYellow = Self(255, 255, 224)
|
||||
public static let lime = Self(0, 255, 0)
|
||||
public static let limeGreen = Self(50, 205, 50)
|
||||
public static let linen = Self(250, 240, 230)
|
||||
public static let magenta = Self(255, 0, 255)
|
||||
public static let maroon = Self(128, 0, 0)
|
||||
public static let mediumAquamarine = Self(102, 205, 170)
|
||||
public static let mediumBlue = Self(0, 0, 205)
|
||||
public static let mediumOrchid = Self(186, 85, 211)
|
||||
public static let mediumPurple = Self(147, 112, 219)
|
||||
public static let mediumSeaGreen = Self(60, 179, 113)
|
||||
public static let mediumSlateBlue = Self(123, 104, 238)
|
||||
public static let mediumSpringGreen = Self(0, 250, 154)
|
||||
public static let mediumTurquoise = Self(72, 209, 204)
|
||||
public static let mediumVioletRed = Self(199, 21, 133)
|
||||
public static let midnightBlue = Self(25, 25, 112)
|
||||
public static let mintCream = Self(245, 255, 250)
|
||||
public static let mistyRose = Self(255, 228, 225)
|
||||
public static let moccasin = Self(255, 228, 181)
|
||||
public static let navajoWhite = Self(255, 222, 173)
|
||||
public static let navy = Self(0, 0, 128)
|
||||
public static let oldLace = Self(253, 245, 230)
|
||||
public static let olive = Self(128, 128, 0)
|
||||
public static let oliveDrab = Self(107, 142, 35)
|
||||
public static let orange = Self(255, 165, 0)
|
||||
public static let orangeRed = Self(255, 69, 0)
|
||||
public static let orchid = Self(218, 112, 214)
|
||||
public static let paleGoldenrod = Self(238, 232, 170)
|
||||
public static let paleGreen = Self(152, 251, 152)
|
||||
public static let paleTurquoise = Self(175, 238, 238)
|
||||
public static let paleVioletRed = Self(219, 112, 147)
|
||||
public static let papayaWhip = Self(255, 239, 213)
|
||||
public static let peachPuff = Self(255, 218, 185)
|
||||
public static let peru = Self(205, 133, 63)
|
||||
public static let pink = Self(255, 192, 203)
|
||||
public static let plum = Self(221, 160, 221)
|
||||
public static let powderBlue = Self(176, 224, 230)
|
||||
public static let purple = Self(128, 0, 128)
|
||||
public static let rebeccaPurple = Self(102, 51, 153)
|
||||
public static let red = Self(255, 0, 0)
|
||||
public static let rosyBrown = Self(188, 143, 143)
|
||||
public static let royalBlue = Self(65, 105, 225)
|
||||
public static let saddleBrown = Self(139, 69, 19)
|
||||
public static let salmon = Self(250, 128, 114)
|
||||
public static let sandyBrown = Self(244, 164, 96)
|
||||
public static let seaGreen = Self(46, 139, 87)
|
||||
public static let seaShell = Self(255, 245, 238)
|
||||
public static let sienna = Self(160, 82, 45)
|
||||
public static let silver = Self(192, 192, 192)
|
||||
public static let skyBlue = Self(135, 206, 235)
|
||||
public static let slateBlue = Self(106, 90, 205)
|
||||
public static let slateGray = Self(112, 128, 144)
|
||||
public static let slateGrey = Self(112, 128, 144)
|
||||
public static let snow = Self(255, 250, 250)
|
||||
public static let springGreen = Self(0, 255, 127)
|
||||
public static let steelBlue = Self(70, 130, 180)
|
||||
public static let tan = Self(210, 180, 140)
|
||||
public static let teal = Self(0, 128, 128)
|
||||
public static let thistle = Self(216, 191, 216)
|
||||
public static let tomato = Self(255, 99, 71)
|
||||
public static let turquoise = Self(64, 224, 208)
|
||||
public static let violet = Self(238, 130, 238)
|
||||
public static let wheat = Self(245, 222, 179)
|
||||
public static let white = Self(255, 255, 255)
|
||||
public static let whiteSmoke = Self(245, 245, 245)
|
||||
public static let yellow = Self(255, 255, 0)
|
||||
public static let yellowGreen = Self(154, 205, 50)
|
||||
|
||||
public let r: UInt8
|
||||
public let g: UInt8
|
||||
public let b: UInt8
|
||||
public let a: UInt8
|
||||
|
||||
public init(r: UInt8, g: UInt8, b: UInt8, a: UInt8 = .max) {
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.a = a
|
||||
}
|
||||
|
||||
public init(_ r: UInt8, _ g: UInt8, _ b: UInt8, _ a: UInt8 = .max) {
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.a = a
|
||||
}
|
||||
|
||||
public init(arrayLiteral elements: UInt8...) {
|
||||
if elements.count == 0 || elements.count == 2 {
|
||||
(r, g, b) = (0, 0, 0)
|
||||
} else if elements.count == 1 {
|
||||
r = elements[0]
|
||||
g = elements[0]
|
||||
b = elements[0]
|
||||
} else {
|
||||
r = elements[0]
|
||||
g = elements[1]
|
||||
b = elements[2]
|
||||
}
|
||||
a = elements.count > 3 ? elements[3] : .max
|
||||
}
|
||||
|
||||
public init(dictionaryLiteral elements: (Channel, UInt8)...) {
|
||||
var r: UInt8 = 0
|
||||
var g: UInt8 = 0
|
||||
var b: UInt8 = 0
|
||||
var a: UInt8 = .max
|
||||
|
||||
for (channel, value) in elements {
|
||||
switch channel {
|
||||
case .r:
|
||||
r = value
|
||||
case .g:
|
||||
g = value
|
||||
case .b:
|
||||
b = value
|
||||
case .a:
|
||||
a = value
|
||||
case .all:
|
||||
r = value
|
||||
g = value
|
||||
b = value
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.a = a
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@available(macOS 13.0, *)
|
||||
extension Color: ExpressibleByStringLiteral {
|
||||
public typealias StringLiteralType = String
|
||||
|
||||
/// Parses common CSS color formats:
|
||||
/// - Named colors (e.g. "red", "aliceBlue", "AliceBlue")
|
||||
/// - Hex: "#RRGGBB", "#RGB", "#RRGGBBAA", "RRGGBB"
|
||||
/// - RGB(A): "rgb(255,0,0)", "rgba(255, 0, 0, 0.5)", "rgb(100%, 0%, 0%)"
|
||||
public init?(colorString: String) {
|
||||
let trimmed = colorString.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||
|
||||
// Named color
|
||||
if let named = Self.namedColors[trimmed] {
|
||||
self = named
|
||||
return
|
||||
}
|
||||
|
||||
// Hex colors
|
||||
if let hexColor = Self.parseHex(trimmed) {
|
||||
self = hexColor
|
||||
return
|
||||
}
|
||||
|
||||
// RGB/RGBA
|
||||
if let rgbColor = Self.parseRGB(trimmed) {
|
||||
self = rgbColor
|
||||
return
|
||||
}
|
||||
|
||||
self = .black
|
||||
}
|
||||
|
||||
public init(stringLiteral value: String) {
|
||||
if let color = Color(colorString: value) {
|
||||
self = color
|
||||
} else {
|
||||
self = .black
|
||||
}
|
||||
}
|
||||
|
||||
private static let namedColors: [String: Color] = [
|
||||
"black": .black,
|
||||
"white": .white,
|
||||
"transparent": .transparent,
|
||||
"aliceblue": aliceBlue,
|
||||
"antiquewhite": antiqueWhite,
|
||||
"aqua": aqua,
|
||||
"aquamarine": aquamarine,
|
||||
"azure": azure,
|
||||
"beige": beige,
|
||||
"bisque": bisque,
|
||||
"blanchedalmond": blanchedAlmond,
|
||||
"blue": blue,
|
||||
"blueviolet": blueViolet,
|
||||
"brown": brown,
|
||||
"burlywood": burlyWood,
|
||||
"cadetblue": cadetBlue,
|
||||
"chartreuse": chartreuse,
|
||||
"chocolate": chocolate,
|
||||
"coral": coral,
|
||||
"cornflowerblue": cornflowerBlue,
|
||||
"cornsilk": cornsilk,
|
||||
"crimson": crimson,
|
||||
"cyan": cyan,
|
||||
"darkblue": darkBlue,
|
||||
"darkcyan": darkCyan,
|
||||
"darkgoldenrod": darkGoldenrod,
|
||||
"darkgray": darkGray,
|
||||
"darkgreen": darkGreen,
|
||||
"darkgrey": darkGrey,
|
||||
"darkkhaki": darkKhaki,
|
||||
"darkmagenta": darkMagenta,
|
||||
"darkolivegreen": darkOliveGreen,
|
||||
"darkorange": darkOrange,
|
||||
"darkorchid": darkOrchid,
|
||||
"darkred": darkRed,
|
||||
"darksalmon": darkSalmon,
|
||||
"darkseagreen": darkSeaGreen,
|
||||
"darkslateblue": darkSlateBlue,
|
||||
"darkslategray": darkSlateGray,
|
||||
"darkslategrey": darkSlateGrey,
|
||||
"darkturquoise": darkTurquoise,
|
||||
"darkviolet": darkViolet,
|
||||
"deeppink": deepPink,
|
||||
"deepskyblue": deepSkyBlue,
|
||||
"dimgray": dimGray,
|
||||
"dimgrey": dimGrey,
|
||||
"dodgerblue": dodgerBlue,
|
||||
"firebrick": fireBrick,
|
||||
"floralwhite": floralWhite,
|
||||
"forestgreen": forestGreen,
|
||||
"fuchsia": fuchsia,
|
||||
"gainsboro": gainsboro,
|
||||
"ghostwhite": ghostWhite,
|
||||
"gold": gold,
|
||||
"goldenrod": goldenrod,
|
||||
"gray": gray,
|
||||
"green": green,
|
||||
"greenyellow": greenYellow,
|
||||
"grey": grey,
|
||||
"honeydew": honeydew,
|
||||
"hotpink": hotPink,
|
||||
"indianred": indianRed,
|
||||
"indigo": indigo,
|
||||
"ivory": ivory,
|
||||
"khaki": khaki,
|
||||
"lavender": lavender,
|
||||
"lavenderblush": lavenderBlush,
|
||||
"lawngreen": lawnGreen,
|
||||
"lemonchiffon": lemonChiffon,
|
||||
"lightblue": lightBlue,
|
||||
"lightcoral": lightCoral,
|
||||
"lightcyan": lightCyan,
|
||||
"lightgoldenrodyellow": lightGoldenrodYellow,
|
||||
"lightgray": lightGray,
|
||||
"lightgreen": lightGreen,
|
||||
"lightgrey": lightGrey,
|
||||
"lightpink": lightPink,
|
||||
"lightsalmon": lightSalmon,
|
||||
"lightseagreen": lightSeaGreen,
|
||||
"lightskyblue": lightSkyBlue,
|
||||
"lightslategray": lightSlateGray,
|
||||
"lightslategrey": lightSlateGrey,
|
||||
"lightsteelblue": lightSteelBlue,
|
||||
"lightyellow": lightYellow,
|
||||
"lime": lime,
|
||||
"limegreen": limeGreen,
|
||||
"linen": linen,
|
||||
"magenta": magenta,
|
||||
"maroon": maroon,
|
||||
"mediumaquamarine": mediumAquamarine,
|
||||
"mediumblue": mediumBlue,
|
||||
"mediumorchid": mediumOrchid,
|
||||
"mediumpurple": mediumPurple,
|
||||
"mediumseagreen": mediumSeaGreen,
|
||||
"mediumslateblue": mediumSlateBlue,
|
||||
"mediumspringgreen": mediumSpringGreen,
|
||||
"mediumturquoise": mediumTurquoise,
|
||||
"mediumvioletred": mediumVioletRed,
|
||||
"midnightblue": midnightBlue,
|
||||
"mintcream": mintCream,
|
||||
"mistyrose": mistyRose,
|
||||
"moccasin": moccasin,
|
||||
"navajowhite": navajoWhite,
|
||||
"navy": navy,
|
||||
"oldlace": oldLace,
|
||||
"olive": olive,
|
||||
"olivedrab": oliveDrab,
|
||||
"orange": orange,
|
||||
"orangered": orangeRed,
|
||||
"orchid": orchid,
|
||||
"palegoldenrod": paleGoldenrod,
|
||||
"palegreen": paleGreen,
|
||||
"paleturquoise": paleTurquoise,
|
||||
"palevioletred": paleVioletRed,
|
||||
"papayawhip": papayaWhip,
|
||||
"peachpuff": peachPuff,
|
||||
"peru": peru,
|
||||
"pink": pink,
|
||||
"plum": plum,
|
||||
"powderblue": powderBlue,
|
||||
"purple": purple,
|
||||
"rebeccapurple": rebeccaPurple,
|
||||
"red": red,
|
||||
"rosybrown": rosyBrown,
|
||||
"royalblue": royalBlue,
|
||||
"saddlebrown": saddleBrown,
|
||||
"salmon": salmon,
|
||||
"sandybrown": sandyBrown,
|
||||
"seagreen": seaGreen,
|
||||
"seashell": seaShell,
|
||||
"sienna": sienna,
|
||||
"silver": silver,
|
||||
"skyblue": skyBlue,
|
||||
"slateblue": slateBlue,
|
||||
"slategray": slateGray,
|
||||
"slategrey": slateGrey,
|
||||
"snow": snow,
|
||||
"springgreen": springGreen,
|
||||
"steelblue": steelBlue,
|
||||
"tan": tan,
|
||||
"teal": teal,
|
||||
"thistle": thistle,
|
||||
"tomato": tomato,
|
||||
"turquoise": turquoise,
|
||||
"violet": violet,
|
||||
"wheat": wheat,
|
||||
"whitesmoke": whiteSmoke,
|
||||
"yellow": yellow,
|
||||
"yellowgreen": yellowGreen,
|
||||
]
|
||||
|
||||
private static func parseHex(_ str: String) -> Color? {
|
||||
var hex = str
|
||||
if hex.hasPrefix("#") {
|
||||
hex = String(hex.dropFirst())
|
||||
}
|
||||
if hex.count == 3 {
|
||||
// #RGB -> #RRGGBB
|
||||
let r = hex[hex.startIndex]
|
||||
let g = hex[hex.index(after: hex.startIndex)]
|
||||
let b = hex[hex.index(after: hex.index(after: hex.startIndex))]
|
||||
hex = "\(r)\(r)\(g)\(g)\(b)\(b)"
|
||||
} else if hex.count == 4 {
|
||||
// #RGBA
|
||||
let r = hex[hex.startIndex]
|
||||
let g = hex[hex.index(after: hex.startIndex)]
|
||||
let b = hex[hex.index(hex.startIndex, offsetBy: 2)]
|
||||
let a = hex[hex.index(after: hex.index(hex.startIndex, offsetBy: 2))]
|
||||
hex = "\(r)\(r)\(g)\(g)\(b)\(b)\(a)\(a)"
|
||||
}
|
||||
|
||||
guard hex.count == 6 || hex.count == 8,
|
||||
let intValue = UInt32(hex, radix: 16) else { return nil }
|
||||
|
||||
let r = UInt8((intValue >> 16) & 0xFF)
|
||||
let g = UInt8((intValue >> 8) & 0xFF)
|
||||
let b = UInt8(intValue & 0xFF)
|
||||
let a: UInt8
|
||||
if hex.count == 8 {
|
||||
// For #RRGGBBAA, extract AA from lowest byte
|
||||
a = UInt8(intValue & 0xFF)
|
||||
} else {
|
||||
a = .max
|
||||
}
|
||||
return Color(r: r, g: g, b: b, a: a)
|
||||
}
|
||||
|
||||
private static func parseRGB(_ str: String) -> Color? {
|
||||
// Simple regex-like parsing for rgb/rgba
|
||||
let pattern = #"rgba?\s*\(\s*(\d+%?|\.\d+%?)\s*,\s*(\d+%?|\.\d+%?)\s*,\s*(\d+%?|\.\d+%?)\s*(?:,\s*(\d*\.?\d+))?\s*\)"#
|
||||
|
||||
// Basic implementation - split and parse
|
||||
var input = str.replacingOccurrences(of: " ", with: "")
|
||||
if input.hasPrefix("rgb(") || input.hasPrefix("rgba(") {
|
||||
input = String(input.dropFirst(input.hasPrefix("rgba(") ? 5 : 4))
|
||||
input = String(input.dropLast())
|
||||
let components = input.components(separatedBy: ",")
|
||||
guard components.count >= 3 else { return nil }
|
||||
|
||||
let rStr = components[0]
|
||||
let gStr = components[1]
|
||||
let bStr = components[2]
|
||||
let aStr = components.count > 3 ? components[3] : "1"
|
||||
|
||||
let r = parseComponent(rStr)
|
||||
let g = parseComponent(gStr)
|
||||
let b = parseComponent(bStr)
|
||||
let aFloat = Float(aStr) ?? 1.0
|
||||
let a = UInt8((aFloat * 255).rounded())
|
||||
|
||||
if let r = r, let g = g, let b = b {
|
||||
return Color(r: r, g: g, b: b, a: a)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func parseComponent(_ s: String) -> UInt8? {
|
||||
if s.hasSuffix("%") {
|
||||
let val = Float(s.dropLast()) ?? 0
|
||||
return UInt8((val / 100 * 255).rounded())
|
||||
} else {
|
||||
let val = Float(s) ?? 0
|
||||
return UInt8(min(max(val, 0), 255).rounded())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
Tests/ArtifactColorTests/ArtifactColorTests.swift
Normal file
12
Tests/ArtifactColorTests/ArtifactColorTests.swift
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import XCTest
|
||||
@testable import ArtifactColor
|
||||
|
||||
final class ArtifactColorTests: XCTestCase {
|
||||
func testExample() throws {
|
||||
// XCTest Documentation
|
||||
// https://developer.apple.com/documentation/xctest
|
||||
|
||||
// Defining Test Cases and Test Methods
|
||||
// https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue