gradient 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +52 -8
- data/lib/gradient.rb +3 -0
- data/lib/gradient/color_point.rb +8 -0
- data/lib/gradient/css_printer.rb +50 -0
- data/lib/gradient/grd.rb +77 -23
- data/lib/gradient/map.rb +78 -3
- data/lib/gradient/opacity_point.rb +8 -0
- data/lib/gradient/point.rb +10 -3
- data/lib/gradient/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f55c2143e51c8034863193036c2609ac11007591
|
4
|
+
data.tar.gz: e042d481cbd1e8b034b611715709555badd24bb6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42aa087d8edd0c5faf199beb34ca5eec83a250b0b0fb1cbeea0f65b9bfe73a2bb045e3a8f5907861ba16001c0aa5de6c356e6323530f39517ec0e1a2542b1fda
|
7
|
+
data.tar.gz: 07f2d6c7e4f6cec682abc15c8f78306b5530a43015b67488b384c6fd83c391a2f510058eb378d034db3b470d50f3f205d3c3c4c78f41e79add0362ef265a12ac
|
data/README.md
CHANGED
@@ -2,6 +2,52 @@
|
|
2
2
|
Library for dealing with color gradients in ruby
|
3
3
|
|
4
4
|
## Usage
|
5
|
+
Gradient works by placing points along two one dimensional planes.
|
6
|
+
One for color and one for opacity.
|
7
|
+
Start by creating a few points and use them to create a gradient map.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
color_points = [
|
11
|
+
Gradient::ColorPoint.new(0, Color::RGB.new(30, 87, 153)),
|
12
|
+
Gradient::ColorPoint.new(0.49, Color::RGB.new(41, 137, 216)),
|
13
|
+
Gradient::ColorPoint.new(0.51, Color::RGB.new(32, 124, 202)),
|
14
|
+
Gradient::ColorPoint.new(1, Color::RGB.new(125, 185, 232)),
|
15
|
+
]
|
16
|
+
|
17
|
+
opacity_points = [
|
18
|
+
Gradient::OpacityPoint.new(0, 1),
|
19
|
+
Gradient::OpacityPoint.new(0.5, 0),
|
20
|
+
Gradient::OpacityPoint.new(1, 1)
|
21
|
+
]
|
22
|
+
|
23
|
+
gradient = Gradient::Map.new(color_points, opacity_points)
|
24
|
+
# => #<Gradient Map #<Point 0 #1e5799ff> #<Point 49.0 #2989d805> #<Point 50.0 #2583d100> #<Point 51.0 #207cca05> #<Point 100 #7db9e8ff>>
|
25
|
+
```
|
26
|
+
|
27
|
+
### Convert to CSS
|
28
|
+
If you use ruby to serve web content you can use Gradient to convert gradient maps in to [CSS3 Gradients](http://www.w3schools.com/css/css3_gradients.asp)
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gradient.to_css
|
32
|
+
# => "background:linear-gradient(to right, rgba(30,87,153,1.0) 0%, rgba(41,137,216,0.02) 49%, rgba(37,131,209,0.0) 50%, rgba(32,124,202,0.02) 51%, rgba(125,185,232,1.0) 100%);"
|
33
|
+
|
34
|
+
gradient.to_css(property: "border-image")
|
35
|
+
# => "border-image:linear-gradient(to right, rgba(30,87,153,1.0) 0%, rgba(41,137,216,0.02) 49%, rgba(37,131,209,0.0) 50%, rgba(32,124,202,0.02) 51%, rgba(125,185,232,1.0) 100%);"
|
36
|
+
```
|
37
|
+
|
38
|
+
If you want some more control of the css generation you can invoke the CSS Printer manually.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
printer = Gradient::CSSPrinter.new(gradient)
|
42
|
+
printer.linear
|
43
|
+
# => "linear-gradient(to right, rgba(30,87,153,1.0) 0%, rgba(41,137,216,0.02) 49%, rgba(37,131,209,0.0) 50%, rgba(32,124,202,0.02) 51%, rgba(125,185,232,1.0) 100%)"
|
44
|
+
|
45
|
+
printer.linear(direction: "to bottom")
|
46
|
+
# => "linear-gradient(to bottom, rgba(30,87,153,1.0) 0%, rgba(41,137,216,0.02) 49%, rgba(37,131,209,0.0) 50%, rgba(32,124,202,0.02) 51%, rgba(125,185,232,1.0) 100%)"
|
47
|
+
|
48
|
+
printer.radial(shape: :circle)
|
49
|
+
# => "radial-gradient(circle, rgba(30,87,153,1.0) 0%, rgba(41,137,216,0.02) 49%, rgba(37,131,209,0.0) 50%, rgba(32,124,202,0.02) 51%, rgba(125,185,232,1.0) 100%)"
|
50
|
+
```
|
5
51
|
|
6
52
|
### Import Adobe Photoshop Gradient (`.grd`) files
|
7
53
|
For many artists a preferred way of creating gradients is through Photoshop.
|
@@ -9,14 +55,8 @@ You are able to parse `.grd` files and turn them in to a hash of `Gradient::Map`
|
|
9
55
|
|
10
56
|
```ruby
|
11
57
|
Gradient::GRD.parse("./kiwi.grd")
|
12
|
-
# => {
|
13
|
-
#
|
14
|
-
# #<Gradient::Point:0x007fae248a9358 @color=RGB [#3d1103], @location=0.0>,
|
15
|
-
# #<Gradient::Point:0x007fae248a9308 @color=RGB [#29860d], @location=0.386>,
|
16
|
-
# #<Gradient::Point:0x007fae248a92b8 @color=RGB [#a0cb1b], @location=0.84>,
|
17
|
-
# #<Gradient::Point:0x007fae248a9268 @color=RGB [#f3f56e], @location=0.927>,
|
18
|
-
# #<Gradient::Point:0x007fae248a9218 @color=RGB [#ffffff], @location=1.0>
|
19
|
-
# ]>
|
58
|
+
# => {
|
59
|
+
# "Kiwi"=> #<Gradient Map #<Point 0.0 #3d1103ff> #<Point 38.6 #29860dff> #<Point 84.0 #a0cb1bff> #<Point 92.7 #f3f56eff> #<Point 100.0 #ffffffff>>
|
20
60
|
# }
|
21
61
|
```
|
22
62
|
|
@@ -49,3 +89,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/zeeraw
|
|
49
89
|
## License
|
50
90
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
51
91
|
|
92
|
+
## Acknowledgments
|
93
|
+
Valek Filippov and the RE-lab team decoded the `.grd` file format and provided
|
94
|
+
an [initial parser implementation](https://gitorious.org/re-lab/graphics/source/781a65604d405f29c2da487820f64de8ddb0724d:photoshop/grd).
|
95
|
+
[Andy Boughton](https://github.com/abought) later created an [implementation in python](https://github.com/abought/grd_to_cmap) which is the base for this library's implementation.
|
data/lib/gradient.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Gradient
|
2
|
+
class CSSPrinter
|
3
|
+
|
4
|
+
ORIENTATIONS = %i(horizontal vertical diagonal_top diagonal_bottom radial)
|
5
|
+
|
6
|
+
def initialize(map)
|
7
|
+
@map = map
|
8
|
+
end
|
9
|
+
|
10
|
+
def css(type: :linear, property: "background", **args)
|
11
|
+
"#{property}:#{send(type,**args)};"
|
12
|
+
end
|
13
|
+
|
14
|
+
def linear(vendor: nil, direction: "to right", angle: nil, repeating: false)
|
15
|
+
angle_or_direction = [angle || direction]
|
16
|
+
arguments = (angle_or_direction + rgba_values)
|
17
|
+
format_css_function(vendor, repeating, "linear-gradient") + "(#{arguments.join(", ")})"
|
18
|
+
end
|
19
|
+
|
20
|
+
def radial(vendor: nil, shape: nil, size: nil, position: nil, repeating: false)
|
21
|
+
shape_and_size_at_position = []
|
22
|
+
shape_and_size_at_position << shape if shape
|
23
|
+
shape_and_size_at_position << size if size
|
24
|
+
shape_and_size_at_position << position if position
|
25
|
+
arguments = rgba_values
|
26
|
+
arguments.unshift(shape_and_size_at_position.join(" ")) if shape_and_size_at_position.any?
|
27
|
+
format_css_function(vendor, repeating, "radial-gradient") + "(#{arguments.join(", ")})"
|
28
|
+
end
|
29
|
+
|
30
|
+
def rgba_values
|
31
|
+
@map.points.map { |point| point_to_rgba(point) }
|
32
|
+
end
|
33
|
+
|
34
|
+
private def point_to_rgba(point)
|
35
|
+
red, green, blue = [:red,:green, :blue].map{|c|point.color.send(c).round}
|
36
|
+
opacity = point.opacity.round(2)
|
37
|
+
location = (point.location * 100).round
|
38
|
+
"rgba(#{red},#{green},#{blue},#{opacity}) #{location}%"
|
39
|
+
end
|
40
|
+
|
41
|
+
private def format_css_function(vendor, repeating, type)
|
42
|
+
css_function = []
|
43
|
+
css_function << "-#{vendor}" if !!vendor
|
44
|
+
css_function << "repeating" if !!repeating
|
45
|
+
css_function << type
|
46
|
+
css_function.join("-")
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/gradient/grd.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Gradient
|
2
2
|
class GRD
|
3
3
|
|
4
|
-
SHIFT_BUFFER = " "
|
5
4
|
COLOR_TERMS = %w(Cyn Mgnt Ylw Blck Rd Grn Bl H Strt Brgh)
|
6
5
|
PARSE_METHODS = {
|
7
6
|
"patt" => :parse_patt,
|
@@ -21,55 +20,93 @@ module Gradient
|
|
21
20
|
|
22
21
|
class << self
|
23
22
|
|
24
|
-
def parse(
|
25
|
-
new
|
23
|
+
def parse(string_buffer)
|
24
|
+
new.tap do |parser|
|
25
|
+
parser.parse(string_buffer)
|
26
|
+
end.maps
|
27
|
+
end
|
28
|
+
|
29
|
+
def read(file)
|
30
|
+
new.tap do |parser|
|
31
|
+
File.open(file, "r") do |file|
|
32
|
+
while (string_buffer = file.gets)
|
33
|
+
parser.parse(string_buffer)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end.maps
|
26
37
|
end
|
27
38
|
|
28
39
|
end
|
29
40
|
|
30
|
-
def initialize
|
41
|
+
def initialize
|
31
42
|
@maps = {}
|
32
|
-
|
43
|
+
|
33
44
|
@gradient_names = []
|
45
|
+
@color_gradients = []
|
46
|
+
@transparency_gradients = []
|
47
|
+
|
34
48
|
@current_object_name = ""
|
35
|
-
@
|
49
|
+
@current_color_gradient = []
|
50
|
+
@current_transparency_gradient = []
|
36
51
|
@current_color = {}
|
37
|
-
@
|
52
|
+
@current_transparency = {}
|
38
53
|
|
39
|
-
|
40
|
-
parse while (@buffer = file.gets)
|
41
|
-
end
|
54
|
+
@shift = 0
|
42
55
|
end
|
43
56
|
|
44
|
-
|
57
|
+
def parse(buffer)
|
58
|
+
@buffer = buffer
|
45
59
|
@offset = 28
|
46
60
|
parse_entry while @offset < @buffer.length
|
47
61
|
flush_current_gradient
|
48
62
|
|
49
|
-
|
50
|
-
|
51
|
-
Gradient::
|
63
|
+
color_gradients = @color_gradients.map do |gradient|
|
64
|
+
clean_color_gradient(gradient).map do |color_step|
|
65
|
+
Gradient::ColorPoint.new(*color_step)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
transparency_gradients = @transparency_gradients.map do |gradient|
|
70
|
+
clean_transparency_gradient(gradient).map do |transparency_step|
|
71
|
+
Gradient::OpacityPoint.new(*transparency_step)
|
52
72
|
end
|
73
|
+
end
|
53
74
|
|
54
|
-
|
75
|
+
gradients = color_gradients.zip(transparency_gradients).map do |color_points|
|
76
|
+
Gradient::Map.new(*color_points)
|
55
77
|
end
|
56
78
|
|
57
79
|
@maps = Hash[ @gradient_names.zip(gradients) ]
|
58
80
|
end
|
59
81
|
|
60
|
-
private def clean_gradient(
|
61
|
-
locations =
|
82
|
+
private def clean_gradient(steps)
|
83
|
+
locations = steps.map { |g| g["Lctn"] }
|
62
84
|
min_location = locations.min
|
63
85
|
max_location = locations.max
|
86
|
+
|
64
87
|
locations = locations.map do |location|
|
65
88
|
((location - min_location) * (1.0 / (max_location - min_location))).round(3)
|
66
89
|
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private def clean_color_gradient(steps)
|
93
|
+
locations = clean_gradient(steps)
|
94
|
+
colors = steps.map do |step|
|
95
|
+
convert_to_color(step)
|
96
|
+
end
|
97
|
+
locations.zip(colors)
|
98
|
+
end
|
67
99
|
|
68
|
-
|
69
|
-
|
100
|
+
private def clean_transparency_gradient(steps)
|
101
|
+
locations = clean_gradient(steps)
|
102
|
+
transparencies = steps.map do |step|
|
103
|
+
convert_to_opacity(step)
|
70
104
|
end
|
105
|
+
locations.zip(transparencies)
|
106
|
+
end
|
71
107
|
|
72
|
-
|
108
|
+
private def convert_to_opacity(opacity_data)
|
109
|
+
opacity_data["Opct"]
|
73
110
|
end
|
74
111
|
|
75
112
|
private def convert_to_color(color_data)
|
@@ -139,15 +176,23 @@ module Gradient
|
|
139
176
|
|
140
177
|
private def flush_current_gradient
|
141
178
|
flush_current_color
|
142
|
-
|
143
|
-
@
|
179
|
+
flush_current_transparency
|
180
|
+
@color_gradients << @current_color_gradient if @current_color_gradient.any?
|
181
|
+
@transparency_gradients << @current_transparency_gradient if @current_transparency_gradient.any?
|
182
|
+
@current_color_gradient = []
|
183
|
+
@current_transparency_gradient = []
|
144
184
|
end
|
145
185
|
|
146
186
|
private def flush_current_color
|
147
|
-
@
|
187
|
+
@current_color_gradient << @current_color if @current_color.any?
|
148
188
|
@current_color = {}
|
149
189
|
end
|
150
190
|
|
191
|
+
private def flush_current_transparency
|
192
|
+
@current_transparency_gradient << @current_transparency if @current_transparency.any?
|
193
|
+
@current_transparency = {}
|
194
|
+
end
|
195
|
+
|
151
196
|
private def parse_patt(name)
|
152
197
|
# TODO: Figure out exactly what this is and implement it.
|
153
198
|
log(name, "patt")
|
@@ -237,6 +282,11 @@ module Gradient
|
|
237
282
|
@current_color[name.strip] = value
|
238
283
|
end
|
239
284
|
|
285
|
+
if @current_object_name == "Trns" && name == "Opct" && type == "#Prc"
|
286
|
+
flush_current_transparency
|
287
|
+
@current_transparency[name.strip] = value / 100
|
288
|
+
end
|
289
|
+
|
240
290
|
continue!(12)
|
241
291
|
end
|
242
292
|
|
@@ -254,6 +304,10 @@ module Gradient
|
|
254
304
|
@current_color[name.strip] = size
|
255
305
|
end
|
256
306
|
|
307
|
+
if @current_object_name == "Trns" && name == "Lctn"
|
308
|
+
@current_transparency[name.strip] = size
|
309
|
+
end
|
310
|
+
|
257
311
|
continue!
|
258
312
|
end
|
259
313
|
|
data/lib/gradient/map.rb
CHANGED
@@ -1,8 +1,83 @@
|
|
1
1
|
module Gradient
|
2
2
|
class Map
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
|
4
|
+
attr_reader :color_points, :opacity_points, :points
|
5
|
+
|
6
|
+
def initialize(color_points=[], opacity_points=[])
|
7
|
+
@color_points = sort_points(Array(color_points))
|
8
|
+
@opacity_points = sort_points(Array(opacity_points))
|
9
|
+
@all_points = sort_points(@color_points + @opacity_points)
|
10
|
+
@locations = @all_points.map { |point| point.location }.uniq
|
11
|
+
@points ||= expand_points
|
6
12
|
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<Gradient Map #{points.map(&:inspect).join(" ")}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_css(**args)
|
19
|
+
@css_printer ||= Gradient::CSSPrinter.new(self)
|
20
|
+
@css_printer.css(**args)
|
21
|
+
end
|
22
|
+
|
23
|
+
private def expand_points
|
24
|
+
new_points = @locations.map do |location|
|
25
|
+
selected_points = @all_points.select { |point| point.location == location }
|
26
|
+
colored_points, opacity_points = selected_points.group_by(&:class).values_at(ColorPoint, OpacityPoint)
|
27
|
+
if colored_points && opacity_points
|
28
|
+
Gradient::Point.new(location, colored_points.first.color, opacity_points.first.opacity)
|
29
|
+
elsif colored_points
|
30
|
+
point = colored_points.first
|
31
|
+
a, b = previous_and_next_in(@opacity_points, point)
|
32
|
+
fraction = location_fraction(a, b, point)
|
33
|
+
opacity = opacity_difference(fraction, a, b)
|
34
|
+
Gradient::Point.new(location, point.color, opacity)
|
35
|
+
elsif opacity_points
|
36
|
+
point = opacity_points.first
|
37
|
+
a, b = previous_and_next_in(@color_points, point)
|
38
|
+
fraction = location_fraction(a, b, point)
|
39
|
+
color = color_difference(fraction, a, b)
|
40
|
+
Gradient::Point.new(location, color, point.opacity)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private def previous_and_next_in(bucket, point)
|
46
|
+
groups = bucket.group_by { |p| point_group(p, point) }
|
47
|
+
a = groups.fetch(:same) { groups.fetch(:less) }.max { |p| p.location }
|
48
|
+
b = groups.fetch(:same) { groups.fetch(:more) }.min { |p| p.location }
|
49
|
+
return a, b
|
50
|
+
end
|
51
|
+
|
52
|
+
private def point_group(a, b)
|
53
|
+
if a.location < b.location
|
54
|
+
:less
|
55
|
+
elsif a.location > b.location
|
56
|
+
:more
|
57
|
+
else
|
58
|
+
:same
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private def color_difference(fraction, a, b)
|
63
|
+
red = a.color.red + fraction * (b.color.red - a.color.red)
|
64
|
+
green = a.color.green + fraction * (b.color.green - a.color.green)
|
65
|
+
blue = a.color.blue + fraction * (b.color.blue - a.color.blue)
|
66
|
+
Color::RGB.new(red, green, blue)
|
67
|
+
end
|
68
|
+
|
69
|
+
private def opacity_difference(fraction, a, b)
|
70
|
+
a.opacity + fraction * (b.opacity - a.opacity)
|
71
|
+
end
|
72
|
+
|
73
|
+
private def location_fraction(a, b, x)
|
74
|
+
return 0 if a.location == b.location
|
75
|
+
(x.location - a.location) / (b.location - a.location)
|
76
|
+
end
|
77
|
+
|
78
|
+
private def sort_points(points)
|
79
|
+
points.sort { |a, b| a.location <=> b.location }
|
80
|
+
end
|
81
|
+
|
7
82
|
end
|
8
83
|
end
|
data/lib/gradient/point.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
module Gradient
|
2
2
|
class Point
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
|
4
|
+
attr_reader :location, :color, :opacity
|
5
|
+
|
6
|
+
def initialize(location, color, opacity)
|
7
|
+
@location, @color, @opacity = location, color, opacity
|
6
8
|
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"#<Point #{location * 100} ##{color.hex}#{"%02x" % (opacity * 255).round}>"
|
12
|
+
end
|
13
|
+
|
7
14
|
end
|
8
15
|
end
|
data/lib/gradient/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gradient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Philip Vieira
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: color
|
@@ -98,8 +98,11 @@ files:
|
|
98
98
|
- bin/setup
|
99
99
|
- gradient.gemspec
|
100
100
|
- lib/gradient.rb
|
101
|
+
- lib/gradient/color_point.rb
|
102
|
+
- lib/gradient/css_printer.rb
|
101
103
|
- lib/gradient/grd.rb
|
102
104
|
- lib/gradient/map.rb
|
105
|
+
- lib/gradient/opacity_point.rb
|
103
106
|
- lib/gradient/point.rb
|
104
107
|
- lib/gradient/version.rb
|
105
108
|
homepage: https://github.com/zeeraw/gradient
|