Skip to content

Commit

Permalink
Merge pull request #47 from EricAtomic/add-rgba-support
Browse files Browse the repository at this point in the history
Add support for RGBA values
  • Loading branch information
swhitty authored Aug 30, 2024
2 parents 67d6bb3 + b5a1ba7 commit a708dc1
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Examples/Sources/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class ViewController: UIViewController {

override func loadView() {
let imageView = UIImageView(frame: UIScreen.main.bounds)
imageView.image = SVG(named: "units-cm.svg", in: .samples)?.rasterize()
imageView.image = SVG(named: "rgba.svg", in: .samples)?.rasterize()
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .white
self.view = imageView
Expand Down
5 changes: 5 additions & 0 deletions Samples.bundle/rgba.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions SwiftDraw/DOM.Color.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ extension DOM {
case none
case currentColor
case keyword(Keyword)
case rgbi(UInt8, UInt8, UInt8)
case rgbf(DOM.Float, DOM.Float, DOM.Float)
case rgbi(UInt8, UInt8, UInt8, DOM.Float)
case rgbf(DOM.Float, DOM.Float, DOM.Float, DOM.Float)
case p3(DOM.Float, DOM.Float, DOM.Float)
case hex(UInt8, UInt8, UInt8)

Expand Down
24 changes: 16 additions & 8 deletions SwiftDraw/LayerTree.Color.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ extension LayerTree.Color {
case let .keyword(c):
let rgbi = c.rgbi
return LayerTree.Color(rgbi.0, rgbi.1, rgbi.2)
case let .rgbi(r, g, b):
return LayerTree.Color(r, g, b)
case let .rgbi(r, g, b, a):
return LayerTree.Color(r, g, b, Float(a))
case let .hex(r, g, b):
return LayerTree.Color(r, g, b)
case let .rgbf(r, g, b):
case let .rgbf(r, g, b, a):
return .rgba(r: Float(r),
g: Float(g),
b: Float(b),
a: 1.0,
a: Float(a),
space: .srgb)
case let .p3(r, g, b):
return .rgba(r: Float(r),
Expand All @@ -84,6 +84,14 @@ extension LayerTree.Color {
a: 1.0,
space: .srgb)
}

init(_ r: UInt8, _ g: UInt8, _ b: UInt8, _ a: DOM.Float) {
self = .rgba(r: Float(r)/255.0,
g: Float(g)/255.0,
b: Float(b)/255.0,
a: a,
space: .srgb)
}

var isOpaque: Bool {
switch self {
Expand All @@ -100,14 +108,14 @@ extension LayerTree.Color {
switch self {
case .none:
return .none
case let .rgba(r: r, g: g, b: b, a: _, space):
case let .rgba(r: r, g: g, b: b, a: a, space):
return .rgba(r: r,
g: g,
b: b,
a: alpha,
a: alpha * a,
space: space)
case .gray(white: let w, a: _):
return .gray(white: w, a: alpha)
case .gray(white: let w, a: let a):
return .gray(white: w, a: alpha * a)
}
}

Expand Down
56 changes: 50 additions & 6 deletions SwiftDraw/Parser.XML.Color.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ extension XMLParser {
return .color(c)
} else if let url = try parseURLSelector(data: data) {
return .url(url)
} else if let c = try parseColorRGBA(data: data) {
return .color(c)
}

throw Error.invalid
Expand Down Expand Up @@ -89,6 +91,17 @@ extension XMLParser {
return try parseColorRGBi(data: data)
}

private func parseColorRGBA(data: String) throws -> DOM.Color? {
var scanner = XMLParser.Scanner(text: data)
guard scanner.scanStringIfPossible("rgba(") else { return nil }

if let c = try? parseColorRGBAf(data: data) {
return c
}

return try parseColorRGBAi(data: data)
}

private func parseURLSelector(data: String) throws -> DOM.URL? {
var scanner = XMLParser.Scanner(text: data)
guard (try? scanner.scanString("url(")) == true else {
Expand All @@ -107,33 +120,64 @@ extension XMLParser {
return url
}

private func parseColorRGBi(data: String) throws -> DOM.Color {
private func parseIntColor(data: String, withAlpha: Bool) throws -> DOM.Color {
var scanner = XMLParser.Scanner(text: data)
try scanner.scanString("rgb(")
try scanner.scanString(withAlpha ? "rgba(" : "rgb(")

let r = try scanner.scanUInt8()
scanner.scanStringIfPossible(",")
let g = try scanner.scanUInt8()
scanner.scanStringIfPossible(",")
let b = try scanner.scanUInt8()
var a: Float = 1.0

if withAlpha {
scanner.scanStringIfPossible(",")
a = try scanner.scanFloat() // Opacity
}

try scanner.scanString(")")
return .rgbi(r, g, b)
return .rgbi(r, g, b, a)
}

private func parseColorRGBf(data: String) throws -> DOM.Color {
private func parseColorRGBi(data: String) throws -> DOM.Color {
return try parseIntColor(data: data, withAlpha: false)
}

private func parseColorRGBAi(data: String) throws -> DOM.Color {
return try parseIntColor(data: data, withAlpha: true)
}

private func parsePercentageColor(data: String, withAlpha: Bool) throws -> DOM.Color {
var scanner = XMLParser.Scanner(text: data)
try scanner.scanString("rgb(")
try scanner.scanString(withAlpha ? "rgba(" : "rgb(")

let r = try scanner.scanPercentage()
scanner.scanStringIfPossible(",")
let g = try scanner.scanPercentage()
scanner.scanStringIfPossible(",")
let b = try scanner.scanPercentage()

var a: Float = 1.0
if withAlpha {
scanner.scanStringIfPossible(",")
a = try scanner.scanFloat() // Opacity
}

try scanner.scanString(")")

return .rgbf(r, g, b)
return .rgbf(r, g, b, a)
}

private func parseColorRGBf(data: String) throws -> DOM.Color {
return try parsePercentageColor(data: data, withAlpha: false)
}

private func parseColorRGBAf(data: String) throws -> DOM.Color {
return try parsePercentageColor(data: data, withAlpha: true)
}


private func parseColorP3(data: String) throws -> DOM.Color? {
var scanner = XMLParser.Scanner(text: data)
guard scanner.scanStringIfPossible("color(display-p3") else { return nil }
Expand Down
18 changes: 14 additions & 4 deletions SwiftDraw/XML.Formatter.SVG.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,23 @@ extension XML.Formatter {
return "currentColor"
case let .keyword(k):
return k.rawValue
case let .rgbi(r, g, b):
return "rgb(\(r), \(g), \(b))"
case let .rgbf(r, g, b):
case let .rgbi(r, g, b, a):
if a == 1.0 {
return "rgb(\(r), \(g), \(b))"
} else {
let aa = String(format: "%.2f", a)
return "rgba(\(r), \(g), \(b), \(aa))"
}
case let .rgbf(r, g, b, a):
let rr = String(format: "%.0f", r * 100)
let gg = String(format: "%.0f", g * 100)
let bb = String(format: "%.0f", b * 100)
return "rgb(\(rr)%, \(gg)%, \(bb)%)"
if a == 1.0 {
return "rgb(\(rr)%, \(gg)%, \(bb)%)"
} else {
let aa = String(format: "%.2f", a)
return "rgba(\(rr)%, \(gg)%, \(bb)%, \(aa))"
}
case let .p3(r, g, b):
return "color(display-p3 \(r), \(g), \(b))"
case let .hex(r, g, b):
Expand Down
2 changes: 1 addition & 1 deletion SwiftDrawTests/LayerTree.BuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ final class LayerTreeBuilderTests: XCTestCase {

func testStrokeAttributes() {
var state = LayerTree.Builder.State()
state.stroke = .color(.rgbf(1.0, 0.0, 0.0))
state.stroke = .color(.rgbf(1.0, 0.0, 0.0, 1.0))
state.strokeOpacity = 0.5
state.strokeWidth = 5.0
state.strokeLineCap = .square
Expand Down
6 changes: 3 additions & 3 deletions SwiftDrawTests/LayerTree.ColorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ final class LayerTreeColorTests: XCTestCase {
let none = DOM.Color.none
let black = DOM.Color.keyword(.black)
let white = DOM.Color.keyword(.white)
let red = DOM.Color.rgbi(255, 0, 0)
let green = DOM.Color.rgbi(0, 255, 0)
let blue = DOM.Color.rgbi(0, 0, 255)
let red = DOM.Color.rgbi(255, 0, 0, 1.0)
let green = DOM.Color.rgbi(0, 255, 0, 1.0)
let blue = DOM.Color.rgbi(0, 0, 255, 1.0)

XCTAssertEqual(Color(none), .none)
XCTAssertEqual(Color(black), .srgb(r: 0.0, g: 0.0, b: 0.0, a: 1.0))
Expand Down
4 changes: 2 additions & 2 deletions SwiftDrawTests/NSImage+ImageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ private extension SVG {
let svg = DOM.SVG(width: 2, height: 2)
svg.childElements.append(DOM.Rect(x: 0, y: 0, width: 1, height: 1))
svg.childElements.append(DOM.Rect(x: 1, y: 1, width: 1, height: 1))
svg.childElements[0].attributes.fill = .color(DOM.Color.rgbi(255, 0, 0))
svg.childElements[1].attributes.fill = .color(DOM.Color.rgbi(0, 0, 255))
svg.childElements[0].attributes.fill = .color(DOM.Color.rgbi(255, 0, 0, 1.0))
svg.childElements[1].attributes.fill = .color(DOM.Color.rgbi(0, 0, 255, 1.0))
return SVG(dom: svg, options: .default)
}
}
Expand Down
23 changes: 17 additions & 6 deletions SwiftDrawTests/Parser.XML.ColorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,27 @@ final class ParserColorTests: XCTestCase {

func testColorRGBi() {
// integer 0-255
XCTAssertEqual(try XMLParser().parseColor("rgb(0,1,2)"), .rgbi(0, 1, 2))
XCTAssertEqual(try XMLParser().parseColor(" rgb( 0 , 1 , 2) "), .rgbi(0, 1, 2))
XCTAssertEqual(try XMLParser().parseColor("rgb(255,100,78)"), .rgbi(255, 100, 78))
XCTAssertEqual(try XMLParser().parseColor("rgb(0,1,2)"), .rgbi(0, 1, 2, 1.0))
XCTAssertEqual(try XMLParser().parseColor(" rgb( 0 , 1 , 2) "), .rgbi(0, 1, 2, 1.0))
XCTAssertEqual(try XMLParser().parseColor("rgb(255,100,78)"), .rgbi(255, 100, 78, 1.0))
}

func testColorRGBf() {
// percentage 0-100%
XCTAssertEqual(try XMLParser().parseColor("rgb(0,1%,99%)"), .rgbf(0.0, 0.01, 0.99))
XCTAssertEqual(try XMLParser().parseColor("rgb( 0%, 52% , 100%) "), .rgbf(0.0, 0.52, 1.0))
XCTAssertEqual(try XMLParser().parseColor("rgb(75%,25%,7%)"), .rgbf(0.75, 0.25, 0.07))
XCTAssertEqual(try XMLParser().parseColor("rgb(0,1%,99%)"), .rgbf(0.0, 0.01, 0.99, 1.0))
XCTAssertEqual(try XMLParser().parseColor("rgb( 0%, 52% , 100%) "), .rgbf(0.0, 0.52, 1.0, 1.0))
XCTAssertEqual(try XMLParser().parseColor("rgb(75%,25%,7%)"), .rgbf(0.75, 0.25, 0.07, 1.0))
}

func testColorRGBA() {
// integer 0-255
XCTAssertEqual(try XMLParser().parseColor("rgba(0,1,2,0.5)"), .rgbi(0, 1, 2, 0.5))
XCTAssertEqual(try XMLParser().parseColor(" rgba( 0 , 1 , 2, 0.6) "), .rgbi(0, 1, 2, 0.6))
XCTAssertEqual(try XMLParser().parseColor("rgba(255,100,78,0.7)"), .rgbi(255, 100, 78, 0.7))
// percentage 0-100%
XCTAssertEqual(try XMLParser().parseColor("rgba(0,1%,99%,0.5)"), .rgbf(0.0, 0.01, 0.99, 0.5))
XCTAssertEqual(try XMLParser().parseColor("rgba( 0%, 52% , 100%, 0.6) "), .rgbf(0.0, 0.52, 1.0, 0.6))
XCTAssertEqual(try XMLParser().parseColor("rgba(75%,25%,7%,0.7)"), .rgbf(0.75, 0.25, 0.07, 0.7))
}

func testColorHex() {
Expand Down
6 changes: 4 additions & 2 deletions SwiftDrawTests/ValueParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ final class ValueParserTests: XCTestCase {
XCTAssertEqual(try parser.parseFill("black"), .color(.keyword(.black)))
XCTAssertEqual(try parser.parseFill("red"), .color(.keyword(.red)))

XCTAssertEqual(try parser.parseFill("rgb(10,20,30)"), .color(.rgbi(10, 20, 30)))
XCTAssertEqual(try parser.parseFill("rgb(10%,20%,100%)"), .color(.rgbf(0.1, 0.2, 1.0)))
XCTAssertEqual(try parser.parseFill("rgb(10,20,30)"), .color(.rgbi(10, 20, 30, 1.0)))
XCTAssertEqual(try parser.parseFill("rgb(10%,20%,100%)"), .color(.rgbf(0.1, 0.2, 1.0, 1.0)))
XCTAssertEqual(try parser.parseFill("rgba(10, 20, 30, 0.5)"), .color(.rgbi(10, 20, 30, 0.5)))
XCTAssertEqual(try parser.parseFill("rgba(10%,20%,100%,0.6)"), .color(.rgbf(0.1, 0.2, 1.0, 0.6)))
XCTAssertEqual(try parser.parseFill("#AAFF00"), .color(.hex(170, 255, 0)))

XCTAssertEqual(try parser.parseFill("url(#test)"), .url(URL(string: "#test")!))
Expand Down

0 comments on commit a708dc1

Please sign in to comment.