gradient 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|