-
Notifications
You must be signed in to change notification settings - Fork 72
/
blends.jl
199 lines (153 loc) · 5.86 KB
/
blends.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
const Blend = CairoPattern
"""
blend(from::Point, to::Point)
Create an empty linear blend.
A blend is a specification of how one color changes into another. Linear blends are
defined by two points: parallel lines through these points define the start and stop
locations of the blend. The blend is defined relative to the current axes origin. This means
that you should be aware of the current axes when you define blends, and when you use them.
To add colors, use `addstop()`.
"""
function blend(from::Point, to::Point)
return Cairo.pattern_create_linear(from.x, from.y, to.x, to.y)
end
"""
blend(centerpos1, rad1, centerpos2, rad2, color1, color2)
Create a radial blend.
Example:
redblue = blend(
pos, 0, # first circle center and radius
pos, tiles.tilewidth/2, # second circle center and radius
"red",
"blue"
)
"""
function blend(centerpos1::Point, rad1, centerpos2::Point, rad2, color1, color2)
newblend = blend(centerpos1, rad1, centerpos2, rad2)
addstop(newblend, 0, color1)
addstop(newblend, 1, color2)
return newblend
end
"""
blend(pt1::Point, pt2::Point, color1, color2)
Create a linear blend.
Example:
redblue = blend(pos, pos, "red", "blue")
"""
function blend(pt1::Point, pt2::Point, color1, color2)
newblend = blend(pt1, pt2)
addstop(newblend, 0, color1)
addstop(newblend, 1, color2)
return newblend
end
"""
blend(from::Point, startradius, to::Point, endradius)
Create an empty radial blend.
Radial blends are defined by two circles that define the start and stop locations. The
first point is the center of the start circle, the first radius is the radius of the first
circle.
A new blend is empty. To add colors, use `addstop()`.
"""
function blend(from::Point, startradius, to::Point, endradius)
return Cairo.pattern_create_radial(from.x, from.y, startradius, to.x, to.y, endradius)
end
"""
addstop(b::Blend, offset, col)
addstop(b::Blend, offset, (r, g, b, a))
addstop(b::Blend, offset, string)
Add a color stop to a blend. The offset specifies the location along the blend's 'control
vector', which varies between 0 (beginning of the blend) and 1 (end of the blend). For
linear blends, the control vector is from the start point to the end point. For radial
blends, the control vector is from any point on the start circle, to the corresponding point
on the end circle.
Examples:
blendredblue = blend(Point(0, 0), 0, Point(0, 0), 1)
addstop(blendredblue, 0, setcolor(sethue("red")..., .2))
addstop(blendredblue, 1, setcolor(sethue("blue")..., .2))
addstop(blendredblue, 0.5, sethue(randomhue()...))
addstop(blendredblue, 0.5, setcolor(randomcolor()...))
"""
function addstop(b::Blend, offset, col::Colors.Colorant)
temp = convert(RGBA, col)
Cairo.pattern_add_color_stop_rgba(b, offset, temp.r, temp.g, temp.b, temp.alpha)
end
function addstop(b::Blend, offset, col::AbstractString)
temp = parse(RGBA, col)
Cairo.pattern_add_color_stop_rgba(b, offset, temp.r, temp.g, temp.b, temp.alpha)
end
function addstop(b::Blend, offset, col::NTuple{4, Number})
Cairo.pattern_add_color_stop_rgba(b, offset, col[1], col[2], col[3], col[4])
end
function addstop(b::Blend, offset, col::NTuple{3, Number})
currentopacity = get_current_alpha()
Cairo.pattern_add_color_stop_rgba(b, offset, col[1], col[2], col[3], currentopacity)
end
"""
setblend(blend)
Start using the named blend for filling graphics.
This aligns the original coordinates of the blend definition with the current axes.
"""
function setblend(b::Blend)
Cairo.set_source(get_current_cr(), b)
end
"""
blendadjust(ablend, center::Point, xscale, yscale, rot=0)
Modify an existing blend by scaling, translating, and rotating it so that it will fill a
shape properly even if the position of the shape is nowhere near the original location of
the blend's definition.
For example, if your blend definition was this (notice the `1`)
blendgoldmagenta = blend(
Point(0, 0), 0, # first circle center and radius
Point(0, 0), 1, # second circle center and radius
"gold",
"magenta"
)
you can use it in a shape that's 100 units across and centered at `pos`, by calling this:
blendadjust(blendgoldmagenta, Point(pos.x, pos.y), 100, 100)
then use `setblend()`:
setblend(blendgoldmagenta)
"""
function blendadjust(ablend, center::Point, xscale, yscale, rot=0)
blendmatrix(ablend,
juliatocairomatrix(
rotationmatrix(-rot) *
scalingmatrix(1/xscale, 1/yscale) *
translationmatrix(-center.x, -center.y) *
cairotojuliamatrix([1 0 0 1 0 0])))
end
"""
blendmatrix(b::Blend, m)
Set the matrix of a blend.
To apply a sequence of matrix transforms to a blend:
```
A = [1 0 0 1 0 0]
Aj = cairotojuliamatrix(A)
Sj = scalingmatrix(2, .2) * Aj
Tj = translationmatrix(10, 0) * Sj
A1 = juliatocairomatrix(Tj)
blendmatrix(b, As)
```
"""
function blendmatrix(b::Blend, m)
cm = Cairo.CairoMatrix(m[1], m[2], m[3], m[4], m[5], m[6])
Cairo.set_matrix(b, cm)
end
"""
setblendextend(blend::Blend, mode)
Specify how color blend patterns are repeated/extended. Supply the blend and one of the following strings:
- "repeat": the pattern is repeated
- "reflect": the pattern is reflected (repeated in reverse)
- "pad": outside the pattern, use the closest color
- "none": outside of the pattern, use transparent pixels
"""
function setblendextend(blend::Blend, mode)
if mode == "repeat" || mode == :repeat
Cairo.pattern_set_extend(blend, Cairo.EXTEND_REPEAT)
elseif mode == "reflect" || mode == :reflect
Cairo.pattern_set_extend(blend, Cairo.EXTEND_REFLECT)
elseif mode == "pad" || mode == :pad
Cairo.pattern_set_extend(blend, Cairo.EXTEND_PAD)
else
Cairo.pattern_set_extend(blend, Cairo.EXTEND_NONE)
end
end