Skip to content

Commit 62be73d

Browse files
authored
Merge pull request #70 from swhitty/SVG+Hashable
SVG+Hashable
2 parents 319d62e + 85d25f7 commit 62be73d

File tree

7 files changed

+175
-8
lines changed

7 files changed

+175
-8
lines changed

SwiftDraw/Renderer.CGText.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ struct CGTextTypes: RendererTypes {
3737
typealias Rect = String
3838
typealias Color = String
3939
typealias Gradient = LayerTree.Gradient
40-
typealias Mask = [Any]
40+
typealias Mask = [AnyHashable]
4141
typealias Path = [LayerTree.Shape]
4242
typealias Pattern = String
4343
typealias Transform = String
@@ -513,7 +513,7 @@ public final class CGTextRenderer: Renderer {
513513
}
514514
}
515515

516-
func setClip(mask: [Any], frame: String) {
516+
func setClip(mask: [AnyHashable], frame: String) {
517517
lines.append("ctx.clip(to: \(frame), mask: \(mask))")
518518
}
519519

SwiftDraw/Renderer.CoreGraphics.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ struct CGTypes: RendererTypes {
5656
typealias Image = CGImage
5757
}
5858

59-
final class CGTransformingPattern: Equatable {
59+
struct CGTransformingPattern: Hashable {
6060

6161
let bounds: CGRect
6262
let contents: [RendererCommand<CGTypes>]
@@ -71,9 +71,19 @@ final class CGTransformingPattern: Equatable {
7171
renderer.perform(contents)
7272
}
7373

74+
#if compiler(<6.0)
75+
func hash(into hasher: inout Hasher) {
76+
bounds.origin.x.hash(into: &hasher)
77+
bounds.origin.y.hash(into: &hasher)
78+
bounds.size.width.hash(into: &hasher)
79+
bounds.size.height.hash(into: &hasher)
80+
contents.hash(into: &hasher)
81+
}
82+
7483
static func == (lhs: CGTransformingPattern, rhs: CGTransformingPattern) -> Bool {
75-
lhs === rhs
84+
lhs.bounds == rhs.bounds && lhs.contents == rhs.contents
7685
}
86+
#endif
7787
}
7888

7989
struct CGProvider: RendererTypeProvider {

SwiftDraw/Renderer.LayerTree.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ struct LayerTreeTypes: RendererTypes {
3737
typealias Rect = LayerTree.Rect
3838
typealias Color = LayerTree.Color
3939
typealias Gradient = LayerTree.Gradient
40-
typealias Mask = [Any]
40+
typealias Mask = [AnyHashable]
4141
typealias Path = [LayerTree.Shape]
4242
typealias Pattern = LayerTree.Pattern
4343
typealias Transform = LayerTree.Transform

SwiftDraw/Renderer.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ protocol RendererTypes {
3737
associatedtype Rect: Equatable
3838
associatedtype Color: Equatable
3939
associatedtype Gradient: Equatable
40-
associatedtype Mask
40+
associatedtype Mask: Equatable
4141
associatedtype Path: Equatable
4242
associatedtype Pattern: Equatable
4343
associatedtype Transform: Equatable
@@ -200,3 +200,11 @@ enum RendererCommand<Types: RendererTypes> {
200200
case pushTransparencyLayer
201201
case popTransparencyLayer
202202
}
203+
204+
205+
#if canImport(CoreGraphics)
206+
import CoreGraphics
207+
208+
extension RendererCommand<CGTypes>: Equatable { }
209+
extension RendererCommand<CGTypes>: Hashable { }
210+
#endif

SwiftDraw/SVG+Hashable.swift

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//
2+
// SVG+Hashable.swift
3+
// SwiftDraw
4+
//
5+
// Created by Simon Whitty on 23/2/25.
6+
// Copyright 2025 Simon Whitty
7+
//
8+
// Distributed under the permissive zlib license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/SwiftDraw
12+
//
13+
// This software is provided 'as-is', without any express or implied
14+
// warranty. In no event will the authors be held liable for any damages
15+
// arising from the use of this software.
16+
//
17+
// Permission is granted to anyone to use this software for any purpose,
18+
// including commercial applications, and to alter it and redistribute it
19+
// freely, subject to the following restrictions:
20+
//
21+
// 1. The origin of this software must not be misrepresented; you must not
22+
// claim that you wrote the original software. If you use this software
23+
// in a product, an acknowledgment in the product documentation would be
24+
// appreciated but is not required.
25+
//
26+
// 2. Altered source versions must be plainly marked as such, and must not be
27+
// misrepresented as being the original software.
28+
//
29+
// 3. This notice may not be removed or altered from any source distribution.
30+
//
31+
32+
#if canImport(CoreGraphics) && compiler(<6.0)
33+
import CoreGraphics
34+
35+
public extension SVG {
36+
37+
func hash(into hasher: inout Hasher) {
38+
size.width.hash(into: &hasher)
39+
size.height.hash(into: &hasher)
40+
commands.hash(into: &hasher)
41+
}
42+
}
43+
44+
extension RendererCommand<CGTypes> {
45+
46+
func hash(into hasher: inout Hasher) {
47+
switch self {
48+
case .pushState:
49+
"pushState".hash(into: &hasher)
50+
case .popState:
51+
"popState".hash(into: &hasher)
52+
case let .concatenate(transform: transform):
53+
"concatenate".hash(into: &hasher)
54+
transform.a.hash(into: &hasher)
55+
transform.b.hash(into: &hasher)
56+
transform.c.hash(into: &hasher)
57+
transform.d.hash(into: &hasher)
58+
transform.ty.hash(into: &hasher)
59+
transform.tx.hash(into: &hasher)
60+
case let .translate(tx: tx, ty: ty):
61+
"translate".hash(into: &hasher)
62+
tx.hash(into: &hasher)
63+
ty.hash(into: &hasher)
64+
case let .rotate(angle: angle):
65+
"rotate".hash(into: &hasher)
66+
angle.hash(into: &hasher)
67+
case let .scale(sx: sx, sy: sy):
68+
"scale".hash(into: &hasher)
69+
sx.hash(into: &hasher)
70+
sy.hash(into: &hasher)
71+
case let .setFill(color: color):
72+
"setFill".hash(into: &hasher)
73+
color.hash(into: &hasher)
74+
case let .setFillPattern(pattern):
75+
"setFillPattern".hash(into: &hasher)
76+
pattern.hash(into: &hasher)
77+
case let .setStroke(color: color):
78+
"setStroke".hash(into: &hasher)
79+
color.hash(into: &hasher)
80+
case let .setLine(width: width):
81+
"setLine".hash(into: &hasher)
82+
width.hash(into: &hasher)
83+
case let .setLineCap(cap):
84+
"setLineCap".hash(into: &hasher)
85+
cap.hash(into: &hasher)
86+
case let .setLineJoin(join):
87+
"setLineJoin".hash(into: &hasher)
88+
join.hash(into: &hasher)
89+
case let .setLineMiter(limit: limit):
90+
"setLineMiter".hash(into: &hasher)
91+
limit.hash(into: &hasher)
92+
case let .setClip(path: path, rule: rule):
93+
"setClip".hash(into: &hasher)
94+
path.hash(into: &hasher)
95+
rule.hash(into: &hasher)
96+
case let .setClipMask(mask, frame: frame):
97+
"setClip".hash(into: &hasher)
98+
mask.hash(into: &hasher)
99+
frame.origin.x.hash(into: &hasher)
100+
frame.origin.y.hash(into: &hasher)
101+
frame.size.width.hash(into: &hasher)
102+
frame.size.height.hash(into: &hasher)
103+
case let .setAlpha(alpha):
104+
"setAlpha".hash(into: &hasher)
105+
alpha.hash(into: &hasher)
106+
case let .setBlend(mode: mode):
107+
"setBlend".hash(into: &hasher)
108+
mode.hash(into: &hasher)
109+
case let .stroke(stroke):
110+
"stroke".hash(into: &hasher)
111+
stroke.hash(into: &hasher)
112+
case let .clipStrokeOutline(outline):
113+
"clipStrokeOutline".hash(into: &hasher)
114+
outline.hash(into: &hasher)
115+
case let .fill(fill, rule: rule):
116+
"fill".hash(into: &hasher)
117+
fill.hash(into: &hasher)
118+
rule.hash(into: &hasher)
119+
case let .draw(image: image, in: bounds):
120+
"draw".hash(into: &hasher)
121+
image.hash(into: &hasher)
122+
bounds.origin.x.hash(into: &hasher)
123+
bounds.origin.y.hash(into: &hasher)
124+
bounds.size.width.hash(into: &hasher)
125+
bounds.size.height.hash(into: &hasher)
126+
case let .drawLinearGradient(gradient, from: from, to: to):
127+
"drawLinearGradient".hash(into: &hasher)
128+
gradient.hash(into: &hasher)
129+
from.x.hash(into: &hasher)
130+
from.y.hash(into: &hasher)
131+
to.x.hash(into: &hasher)
132+
to.y.hash(into: &hasher)
133+
case let .drawRadialGradient(gradient, startCenter: startCenter, startRadius: startRadius, endCenter: endCenter, endRadius: endRadius):
134+
"drawRadialGradient".hash(into: &hasher)
135+
gradient.hash(into: &hasher)
136+
startCenter.x.hash(into: &hasher)
137+
startCenter.y.hash(into: &hasher)
138+
startRadius.hash(into: &hasher)
139+
endCenter.x.hash(into: &hasher)
140+
endCenter.y.hash(into: &hasher)
141+
endRadius.hash(into: &hasher)
142+
case .pushTransparencyLayer:
143+
"pushTransparencyLayer".hash(into: &hasher)
144+
case .popTransparencyLayer:
145+
"popTransparencyLayer".hash(into: &hasher)
146+
}
147+
}
148+
}
149+
#endif

SwiftDraw/SVG.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import Foundation
3434
#if canImport(CoreGraphics)
3535
import CoreGraphics
3636

37-
public struct SVG {
37+
public struct SVG: Hashable {
3838
public let size: CGSize
3939

4040
//An Image is simply an array of CoreGraphics draw commands

SwiftDrawTests/MockRenderer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ final class MockRenderer: Renderer {
102102
operations.append("setClip")
103103
}
104104

105-
func setClip(mask: [Any], frame: LayerTree.Rect) {
105+
func setClip(mask: [AnyHashable], frame: LayerTree.Rect) {
106106
operations.append("setClipMask")
107107
}
108108

0 commit comments

Comments
 (0)