-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
gradient.go
127 lines (115 loc) · 3.97 KB
/
gradient.go
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
// Copyright (c) 2021-2024 by Richard A. Wilkes. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, version 2.0.
package unison
import (
"fmt"
"github.com/richardwilkes/unison/enums/paintstyle"
"github.com/richardwilkes/unison/enums/tilemode"
)
var _ Ink = &Gradient{}
// Stop provides information about the color and position of one 'color stop' in a gradient.
type Stop struct {
Color ColorProvider
Location float32
}
func (s Stop) String() string {
return fmt.Sprintf("%v:%v", s.Color.GetColor(), s.Location)
}
// Gradient defines a smooth transition between colors across an area. Start and End should hold values from 0 to 1.
// These will be be used to set a relative starting and ending position for the gradient. If StartRadius and EndRadius
// are both greater than 0, then the gradient will be a radial one instead of a linear one.
type Gradient struct {
Stops []Stop
Start Point
StartRadius float32
End Point
EndRadius float32
}
// NewHorizontalEvenlySpacedGradient creates a new gradient with the specified colors evenly spread across the whole
// range.
func NewHorizontalEvenlySpacedGradient(colors ...ColorProvider) *Gradient {
return NewEvenlySpacedGradient(Point{}, Point{X: 1}, 0, 0, colors...)
}
// NewVerticalEvenlySpacedGradient creates a new gradient with the specified colors evenly spread across the whole
// range.
func NewVerticalEvenlySpacedGradient(colors ...ColorProvider) *Gradient {
return NewEvenlySpacedGradient(Point{}, Point{Y: 1}, 0, 0, colors...)
}
// NewEvenlySpacedGradient creates a new gradient with the specified colors evenly spread across the whole range. start
// and end should hold values from 0 to 1, representing the percentage position within the area that will be filled.
func NewEvenlySpacedGradient(start, end Point, startRadius, endRadius float32, colors ...ColorProvider) *Gradient {
gradient := &Gradient{
Start: start,
StartRadius: startRadius,
End: end,
EndRadius: endRadius,
Stops: make([]Stop, len(colors)),
}
switch len(colors) {
case 0:
case 1:
gradient.Stops[0].Color = colors[0]
case 2:
gradient.Stops[0].Color = colors[0]
gradient.Stops[1].Color = colors[1]
gradient.Stops[1].Location = 1
default:
step := 1 / float32(len(colors)-1)
var location float32
for i, color := range colors {
gradient.Stops[i].Color = color
gradient.Stops[i].Location = location
if i < len(colors)-1 {
location += step
} else {
location = 1
}
}
}
return gradient
}
// Paint returns a Paint for this Gradient.
func (g *Gradient) Paint(_ *Canvas, rect Rect, style paintstyle.Enum) *Paint {
paint := NewPaint()
paint.SetStyle(style)
paint.SetColor(Black)
colors := make([]Color, len(g.Stops))
colorPos := make([]float32, len(g.Stops))
for i := range g.Stops {
colors[i] = g.Stops[i].Color.GetColor()
colorPos[i] = g.Stops[i].Location
}
start := Point{
X: rect.X + rect.Width*g.Start.X,
Y: rect.Y + rect.Height*g.Start.Y,
}
end := Point{
X: rect.X + rect.Width*g.End.X,
Y: rect.Y + rect.Height*g.End.Y,
}
var shader *Shader
if g.StartRadius > 0 && g.EndRadius > 0 {
shader = New2PtConicalGradientShader(start, end, g.StartRadius, g.EndRadius, colors, colorPos, tilemode.Clamp,
NewIdentityMatrix())
} else {
shader = NewLinearGradientShader(start, end, colors, colorPos, tilemode.Clamp, NewIdentityMatrix())
}
paint.SetShader(shader)
return paint
}
// Reversed creates a copy of the current Gradient and inverts the locations of each color stop in that copy.
func (g *Gradient) Reversed() *Gradient {
other := *g
other.Stops = make([]Stop, len(g.Stops))
for i, stop := range g.Stops {
stop.Location = 1 - stop.Location
other.Stops[i] = stop
}
return &other
}