rainbow_gradient 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 46e4617e2a724ec1cdbb5561f8bf20b29645b965
4
+ data.tar.gz: 0c058b23f18f3c63119ae378712bf2cca0f17f2a
5
+ SHA512:
6
+ metadata.gz: 6dac57d8cf2cdb02be565cf15985bbbfc08c1b44021af5508e826cd927e7351b460d93e1d0712a0fca77239c7bedc717e6cf4cddcbfcba52e7a70fdb6ecae745
7
+ data.tar.gz: b3b50b115816cb4c97f22ffe1eaeb193a45f8e39058c7d696adb851e8b1dc1878e1ced023f43e558ffa0120cb33226157ded57f8d77ebf06bb7d9f0b485ac176
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .DS_Store
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rainbow.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sophy Eung
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Rainbow
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rainbow'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rainbow
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( http://github.com/<my-github-username>/rainbow/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = './test/lib/**/*_test.rb'
6
+ end
7
+ task default: :test
@@ -0,0 +1,74 @@
1
+ gradient2_1:
2
+ reverse: true
3
+ color_ranges:
4
+ -
5
+ color1:
6
+ red: 0
7
+ green: 0
8
+ blue: 255
9
+ location: 0
10
+ color2:
11
+ red: 0
12
+ green: 255
13
+ blue: 0
14
+ location: 100
15
+ mid_point: 50
16
+ opacity_ranges:
17
+ -
18
+ opacity1:
19
+ value: 100
20
+ location: 0
21
+ opacity2:
22
+ value: 100
23
+ location: 100
24
+ mid_point: 50
25
+
26
+ gradient2_2:
27
+ reverse: false
28
+ color_ranges:
29
+ -
30
+ color1:
31
+ red: 255
32
+ green: 0
33
+ blue: 255
34
+ location: 20
35
+ color2:
36
+ red: 255
37
+ green: 255
38
+ blue: 0
39
+ location: 75
40
+ mid_point: 50
41
+ opacity_ranges:
42
+ -
43
+ opacity1:
44
+ value: 100
45
+ location: 30
46
+ opacity2:
47
+ value: 35
48
+ location: 85
49
+ mid_point: 50
50
+
51
+ gradient2_3:
52
+ reverse: false
53
+ color_ranges:
54
+ -
55
+ color1:
56
+ red: 0
57
+ green: 0
58
+ blue: 255
59
+ location: 0
60
+ color2:
61
+ red: 0
62
+ green: 255
63
+ blue: 0
64
+ location: 100
65
+ mid_point: 50
66
+ opacity_ranges:
67
+ -
68
+ opacity1:
69
+ value: 30
70
+ location: 30
71
+ opacity2:
72
+ value: 70
73
+ location: 70
74
+ mid_point: 50
@@ -0,0 +1,147 @@
1
+ gradient3_1:
2
+ color_ranges:
3
+ -
4
+ color1:
5
+ red: 0
6
+ green: 0
7
+ blue: 255
8
+ location: 0
9
+ color2:
10
+ red: 0
11
+ green: 255
12
+ blue: 0
13
+ location: 50
14
+ mid_point: 50
15
+ -
16
+ color1:
17
+ red: 0
18
+ green: 255
19
+ blue: 0
20
+ location: 50
21
+ color2:
22
+ red: 255
23
+ green: 255
24
+ blue: 0
25
+ location: 100
26
+ mid_point: 50
27
+ opacity_ranges:
28
+ -
29
+ opacity1:
30
+ value: 100
31
+ location: 0
32
+ opacity2:
33
+ value: 100
34
+ location: 50
35
+ mid_point: 50
36
+ -
37
+ opacity1:
38
+ value: 100
39
+ location: 50
40
+ opacity2:
41
+ value: 100
42
+ location: 100
43
+ mid_point: 50
44
+
45
+ gradient3_2:
46
+ color_ranges:
47
+ -
48
+ color1:
49
+ red: 255
50
+ green: 0
51
+ blue: 0
52
+ location: 20
53
+ color2:
54
+ red: 0
55
+ green: 255
56
+ blue: 0
57
+ location: 50
58
+ mid_point: 75
59
+ -
60
+ color1:
61
+ red: 0
62
+ green: 255
63
+ blue: 0
64
+ location: 50
65
+ color2:
66
+ red: 0
67
+ green: 0
68
+ blue: 0
69
+ location: 78
70
+ mid_point: 50
71
+ opacity_ranges:
72
+ -
73
+ opacity1:
74
+ value: 100
75
+ location: 0
76
+ opacity2:
77
+ value: 100
78
+ location: 50
79
+ mid_point: 50
80
+ -
81
+ opacity1:
82
+ value: 100
83
+ location: 50
84
+ opacity2:
85
+ value: 100
86
+ location: 100
87
+ mid_point: 50
88
+
89
+ gradient3_3:
90
+ color_ranges:
91
+ -
92
+ color1:
93
+ red: 255
94
+ green: 0
95
+ blue: 0
96
+ location: 20
97
+ color2:
98
+ red: 0
99
+ green: 255
100
+ blue: 0
101
+ location: 50
102
+ mid_point: 75
103
+ -
104
+ color1:
105
+ red: 0
106
+ green: 255
107
+ blue: 0
108
+ location: 50
109
+ color2:
110
+ red: 0
111
+ green: 0
112
+ blue: 255
113
+ location: 78
114
+ mid_point: 50
115
+ opacity_ranges:
116
+ -
117
+ opacity1:
118
+ value: 50
119
+ location: 10
120
+ opacity2:
121
+ value: 100
122
+ location: 90
123
+ mid_point: 30
124
+
125
+ gradient3_4:
126
+ color_ranges:
127
+ -
128
+ color1:
129
+ red: 255
130
+ green: 0
131
+ blue: 0
132
+ location: 0
133
+ color2:
134
+ red: 0
135
+ green: 255
136
+ blue: 0
137
+ location: 100
138
+ mid_point: 50
139
+ opacity_ranges:
140
+ -
141
+ opacity1:
142
+ value: 50
143
+ location: 10
144
+ opacity2:
145
+ value: 100
146
+ location: 90
147
+ mid_point: 50
Binary file
@@ -0,0 +1,27 @@
1
+ require './lib/rainbow'
2
+
3
+ first_color = Rainbow::Color.new(ChunkyPNG::Color(255, 255, 255), 0)
4
+ second_color = Rainbow::Color.new(ChunkyPNG::Color(255, 255, 255), 100)
5
+ color_range = Rainbow::ColorRange.new(first_color, second_color, 50)
6
+
7
+ first_opacity = Rainbow::Opacity.new(100, 0)
8
+ second_opacity = Rainbow::Opacity.new(0, 100)
9
+ opacity_range = Rainbow::OpacityRange.new(first_opacity, second_opacity, 50)
10
+
11
+ filename = './example/output/gradient.png'
12
+ args = {
13
+ reverse: false,
14
+ opacity: 100,
15
+ scale: 53,
16
+ angle: 180,
17
+
18
+ style: 'linear',
19
+ gradient: {
20
+ color_ranges: [color_range],
21
+ opacity_ranges: [opacity_range]
22
+ }
23
+ }
24
+ gradient = Rainbow::Gradient.new(400, 50, args)
25
+ gradient.save_as_png(filename)
26
+
27
+ `open #{filename}`
@@ -0,0 +1,5 @@
1
+ require './lib/rainbow'
2
+ require_relative 'gradient_factory'
3
+
4
+ filenames = GradientFactory.create_from_file('gradient2.yml')
5
+ `open #{filenames.join(' ')}`
@@ -0,0 +1,5 @@
1
+ require './lib/rainbow'
2
+ require_relative 'gradient_factory'
3
+
4
+ filenames = GradientFactory.create_from_file('gradient3.yml')
5
+ `open #{filenames.join(' ')}`
@@ -0,0 +1,43 @@
1
+ require 'yaml'
2
+ require 'pathname'
3
+
4
+ module GradientFactory
5
+ extend self
6
+
7
+ DATA_DIR = Pathname('example/data')
8
+ OUTPUT_DIR = Pathname('example/output')
9
+
10
+ def create_from_file(filename)
11
+ data = File.open(DATA_DIR.join(filename)) { |file| file.read }
12
+ data = YAML.load(data)
13
+
14
+ data.each_pair.inject([]) do |filenames, (filename, value)|
15
+ color_ranges = value['color_ranges'].inject([]) do |ranges, data|
16
+ h1, h2 = data['color1'], data['color2']
17
+ color1 = Rainbow::Color.new(ChunkyPNG::Color(h1['red'], h1['green'], h1['blue']), h1['location'])
18
+ color2 = Rainbow::Color.new(ChunkyPNG::Color(h2['red'], h2['green'], h2['blue']), h2['location'])
19
+ ranges << Rainbow::ColorRange.new(color1, color2, data['mid_point'])
20
+ ranges
21
+ end
22
+
23
+ opacity_ranges = value['opacity_ranges'].inject([]) do |ranges, data|
24
+ h1, h2 = data['opacity1'], data['opacity2']
25
+ opacity1 = Rainbow::Opacity.new(h1['value'], h1['location'])
26
+ opacity2 = Rainbow::Opacity.new(h2['value'], h2['location'])
27
+ ranges << Rainbow::OpacityRange.new(opacity1, opacity2, data['mid_point'])
28
+ end
29
+
30
+ args = {
31
+ reverse: value['reverse'],
32
+ style: 'linear',
33
+ gradient: {
34
+ color_ranges: color_ranges,
35
+ opacity_ranges: opacity_ranges
36
+ }
37
+ }
38
+
39
+ gradient = Rainbow::Gradient.new(400, 50, args)
40
+ filenames << gradient.save_as_png(OUTPUT_DIR.join("#{filename}.png"))
41
+ end
42
+ end
43
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,32 @@
1
+ module Rainbow
2
+ class Color
3
+ attr_reader :color, :location
4
+
5
+ # color - Fixnum
6
+ # location - Fixnum (0-100)
7
+ def initialize(color, location)
8
+ @color = color
9
+ @location = location
10
+
11
+ assert_location!
12
+ end
13
+
14
+ def r
15
+ ChunkyPNG::Color.r(color)
16
+ end
17
+
18
+ def g
19
+ ChunkyPNG::Color.g(color)
20
+ end
21
+
22
+ def b
23
+ ChunkyPNG::Color.b(color)
24
+ end
25
+
26
+ private
27
+
28
+ def assert_location!
29
+ raise ArgumentError, 'location must be between 0 and 100' if location < 0 || location > 100
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,161 @@
1
+ module Rainbow
2
+ class ColorRange
3
+ attr_reader :from_color, :to_color, :mid_point, :gradient, :mid_color
4
+ attr_reader :current_x, :width, :first_width, :second_width
5
+ attr_reader :from_location_in_pixel, :to_location_in_pixel,
6
+ :mid_location_in_pixel, :leftover_width
7
+ attr_reader :tmp_current_x, :tmp_diff_r, :tmp_diff_g, :tmp_diff_b,
8
+ :tmp_from_color, :tmp_to_color, :tmp_distance_in_pixel
9
+
10
+ attr_accessor :opacity_ranges
11
+
12
+ def initialize(from_color, to_color, mid_point)
13
+ @from_color = from_color
14
+ @to_color = to_color
15
+ @mid_point = mid_point
16
+
17
+ @current_x = nil
18
+ end
19
+
20
+ def gradient=(gradient)
21
+ @gradient = gradient
22
+ compute_variables!
23
+ end
24
+
25
+ def current_x=(x)
26
+ set_color_variables(x)
27
+ set_opacity_variables(x)
28
+
29
+ @current_x = x
30
+ @tmp_opacity = compute_opacity(x)
31
+ end
32
+
33
+ def current_color
34
+ if less_than_scale_range?
35
+ ChunkyPNG::Color(from_color.r, from_color.g, from_color.b, @tmp_opacity)
36
+ elsif greater_than_scale_range?
37
+ ChunkyPNG::Color(to_color.r, to_color.g, to_color.b, @tmp_opacity)
38
+ else
39
+ ChunkyPNG::Color(color(:r), color(:g), color(:b), @tmp_opacity)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def color(method)
46
+ diff = __send__("tmp_diff_#{method}")
47
+ (tmp_from_color.send(method) + (tmp_current_x / tmp_distance_in_pixel) * diff).to_i
48
+ end
49
+
50
+ def compute_opacity(x)
51
+ ratio = @tmp_opacity_current_x / @tmp_opacity_distance_in_pixel
52
+ opacity = (@tmp_opacity_from + ratio * @tmp_opacity_diff).round
53
+
54
+ opacity < 0 ? 0 : opacity
55
+ end
56
+
57
+ def compute_variables!
58
+ @mid_color = ChunkyPNG::Color(from_color.r + (to_color.r - from_color.r) / 2,
59
+ from_color.g + (to_color.g - from_color.g) / 2,
60
+ from_color.b + (to_color.b - from_color.b) / 2)
61
+
62
+ @width = gradient.width * (to_color.location - from_color.location) / 100
63
+ @first_width = @width * mid_point / 100
64
+ @second_width = @width - @first_width
65
+
66
+ @from_location_in_pixel = from_color.location * gradient.width / 100
67
+ @mid_location_in_pixel = @from_location_in_pixel + @first_width
68
+ @to_location_in_pixel = @from_location_in_pixel + @width
69
+
70
+ @leftover_width = (100 - to_color.location) * gradient.width / 100
71
+ end
72
+
73
+ def in_scale_range?
74
+ gradient.scale_start_location_in_pixel >= current_x && current_x <= gradient.scale_end_location_in_pixel
75
+ end
76
+
77
+ def less_than_scale_range?
78
+ current_x < gradient.scale_start_location_in_pixel
79
+ end
80
+
81
+ def greater_than_scale_range?
82
+ current_x > gradient.scale_end_location_in_pixel
83
+ end
84
+
85
+ def set_opacity_variables(x)
86
+ opacity_ranges.current_x = x
87
+
88
+ @tmp_opacity_current_x = opacity_ranges.tmp_current_x
89
+ @tmp_opacity_from = opacity_ranges.tmp_from
90
+ @tmp_opacity_to = opacity_ranges.tmp_to
91
+ @tmp_opacity_diff = opacity_ranges.tmp_diff
92
+ @tmp_opacity_distance_in_pixel = opacity_ranges.tmp_distance_in_pixel
93
+ end
94
+
95
+ def last_color_location_is_lesser_than_100(x)
96
+ x == to_location_in_pixel && to_color.location < 100
97
+ end
98
+
99
+ def set_color_variables(x)
100
+ if x == 0 && from_color.location > 0
101
+ @tmp_current_x = 0.0
102
+ @tmp_from_color = from_color
103
+ @tmp_to_color = from_color
104
+ @tmp_distance_in_pixel = from_location_in_pixel
105
+ @tmp_diff_r = 0
106
+ @tmp_diff_g = 0
107
+ @tmp_diff_b = 0
108
+ elsif x == to_location_in_pixel && to_color.location < 100
109
+ @tmp_current_x = 0.0
110
+ @tmp_from_color = to_color
111
+ @tmp_to_color = to_color
112
+ @tmp_distance_in_pixel = leftover_width
113
+ @tmp_diff_r = 0
114
+ @tmp_diff_g = 0
115
+ @tmp_diff_b = 0
116
+ elsif x == from_location_in_pixel && gradient.scale_100?
117
+ @tmp_current_x = 0.0
118
+ @tmp_from_color = from_color
119
+ @tmp_to_color = Rainbow::Color.new(mid_color, 0)
120
+ @tmp_distance_in_pixel = first_width
121
+ @tmp_diff_r = @tmp_to_color.r - @tmp_from_color.r
122
+ @tmp_diff_g = @tmp_to_color.g - @tmp_from_color.g
123
+ @tmp_diff_b = @tmp_to_color.b - @tmp_from_color.b
124
+ elsif x == mid_location_in_pixel && gradient.scale_100?
125
+ @tmp_current_x = 0.0
126
+ @tmp_from_color = Rainbow::Color.new(mid_color, 0)
127
+ @tmp_to_color = to_color
128
+ @tmp_distance_in_pixel = second_width
129
+ @tmp_diff_r = @tmp_to_color.r - @tmp_from_color.r
130
+ @tmp_diff_g = @tmp_to_color.g - @tmp_from_color.g
131
+ @tmp_diff_b = @tmp_to_color.b - @tmp_from_color.b
132
+ elsif x == from_location_in_pixel && !gradient.scale_100?
133
+ @tmp_current_x = 0.0
134
+ @tmp_from_color = from_color
135
+ @tmp_to_color = from_color
136
+ @tmp_distance_in_pixel = 1
137
+ @tmp_diff_r = 0
138
+ @tmp_diff_g = 0
139
+ @tmp_diff_b = 0
140
+ elsif x == gradient.scale_start_location_in_pixel && !gradient.scale_100?
141
+ @tmp_current_x = 0.0
142
+ @tmp_from_color = from_color
143
+ @tmp_to_color = Rainbow::Color.new(mid_color, 0)
144
+ @tmp_distance_in_pixel = gradient.scale_half_distance_in_pixel
145
+ @tmp_diff_r = @tmp_to_color.r - @tmp_from_color.r
146
+ @tmp_diff_g = @tmp_to_color.g - @tmp_from_color.g
147
+ @tmp_diff_b = @tmp_to_color.b - @tmp_from_color.b
148
+ elsif x == gradient.scale_mid_location_in_pixel && !gradient.scale_100?
149
+ @tmp_current_x = 0.0
150
+ @tmp_from_color = Rainbow::Color.new(mid_color, 0)
151
+ @tmp_to_color = to_color
152
+ @tmp_distance_in_pixel = gradient.scale_half_distance_in_pixel
153
+ @tmp_diff_r = @tmp_to_color.r - @tmp_from_color.r
154
+ @tmp_diff_g = @tmp_to_color.g - @tmp_from_color.g
155
+ @tmp_diff_b = @tmp_to_color.b - @tmp_from_color.b
156
+ else
157
+ @tmp_current_x += 1
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,185 @@
1
+ module Rainbow
2
+ class Gradient
3
+
4
+ attr_reader :width, :height, :args, :style, :color_ranges, :opacity_ranges
5
+ attr_reader :canvas
6
+ attr_reader :scale_start_location_in_pixel, :scale_mid_location_in_pixel,
7
+ :scale_end_location_in_pixel, :scale_half_distance_in_pixel
8
+
9
+ SUPPORTED_ANGLES = [0, 180]
10
+
11
+ # wdith - The gradient width
12
+ # height - The gradient height
13
+ # args - Hash that describes the property of the gradient
14
+ # {
15
+ # blend_mode: 'norm|diss|dark|...',
16
+ # dither: true|false,
17
+ # opacity: 0..100,
18
+ # gradient: {
19
+ # type: 'solid|noise',
20
+ # smoothness: 0..100,
21
+ # color_ranges: [],
22
+ # opacity_ranges: []
23
+ # },
24
+ # reverse: true|false,
25
+ # style: 'linear|radial|angle|reflected|diamond',
26
+ # align_with_layer: true|false,
27
+ # angle: 0..360,
28
+ # scale: 10..150
29
+ # }
30
+ def initialize(width, height, args)
31
+ @width = width
32
+ @height = height
33
+ @args = args
34
+
35
+ @style = args[:style]
36
+ @color_ranges = args[:gradient][:color_ranges]
37
+ @opacity_ranges = OpacityRanges.new(args[:gradient][:opacity_ranges], self)
38
+
39
+ assert_arguments!
40
+ compute_scale_variables!
41
+ end
42
+
43
+ def create_canvas
44
+ @canvas = ChunkyPNG::Canvas.new(width, height, ChunkyPNG::Color::TRANSPARENT)
45
+
46
+ paint_canvas!(@canvas)
47
+ set_opacity!(@canvas) if opacity != 100
48
+ reverse!(@canvas) if reverse || angle == 180
49
+ end
50
+
51
+ def blend_mode
52
+ args.fetch(:blend_mode, 'norm')
53
+ end
54
+
55
+ def dither
56
+ args.fetch(:dither, false)
57
+ end
58
+
59
+ def opacity
60
+ args.fetch(:opacity, 100)
61
+ end
62
+
63
+ def type
64
+ args[:gradient][:type]
65
+ end
66
+
67
+ def smoothness
68
+ args[:gradient][:smoothness]
69
+ end
70
+
71
+ def reverse
72
+ args.fetch(:reverse, false)
73
+ end
74
+
75
+ def align_with_layer
76
+ args.fetch(:align_with_layer, true)
77
+ end
78
+
79
+ def angle
80
+ value = args.fetch(:angle, 0)
81
+ value % 360
82
+ end
83
+
84
+ def scale
85
+ args.fetch(:scale, 100)
86
+ end
87
+
88
+ def scale_100?
89
+ scale == 100
90
+ end
91
+
92
+ def save_as_png(path)
93
+ create_canvas unless canvas
94
+ canvas.save(path, :fast_rgba)
95
+
96
+ path
97
+ end
98
+
99
+ private
100
+
101
+ def assert_arguments!
102
+ _opacity_ranges = args[:gradient][:opacity_ranges]
103
+
104
+ raise ArgumentError, 'args[:gradient][:color_ranges] cannot not be blank' if !color_ranges || color_ranges.size == 0
105
+ raise ArgumentError, 'args[:gradient][:opacity_ranges] cannot not be blank' if !_opacity_ranges || _opacity_ranges.size == 0
106
+ raise ArgumentError, 'args[:style] - Sorry, for now we support only linear gradient' if args[:style] != 'linear'
107
+ raise ArgumentError, 'args[:scale] must be between 10 and 150' if scale < 10 || scale > 150
108
+ raise ArgumentError, 'Sorry, we support only two colors stop if scale is not 100' if scale != 100 && color_ranges.size != 1
109
+ raise ArgumentError, 'Sorry, we support only 0° or 180° angle' unless SUPPORTED_ANGLES.include?(angle)
110
+ end
111
+
112
+ def compute_scale_variables!
113
+ scale_start = (100 - scale) / 2
114
+ scale_end = scale_start == 0 ? 100 : scale_start + scale
115
+
116
+ @scale_start_location_in_pixel = scale_start * width / 100
117
+ @scale_end_location_in_pixel = scale_end * width / 100
118
+ @scale_half_distance_in_pixel = (@scale_end_location_in_pixel - @scale_start_location_in_pixel) / 2
119
+ @scale_mid_location_in_pixel = @scale_start_location_in_pixel + @scale_half_distance_in_pixel
120
+ end
121
+
122
+ def reverse!(canvas)
123
+ canvas.flip_vertically!
124
+ end
125
+
126
+ def set_opacity!(canvas)
127
+ width.times.each do |x|
128
+ height.times.each do |y|
129
+ color = canvas[x, y]
130
+ red = ChunkyPNG::Color.r(color)
131
+ green = ChunkyPNG::Color.g(color)
132
+ blue = ChunkyPNG::Color.b(color)
133
+ new_alpha = ChunkyPNG::Color.a(color) * opacity / 100
134
+ canvas[x, y] = ChunkyPNG::Color.rgba(red, green, blue, new_alpha)
135
+ end
136
+ end
137
+ end
138
+
139
+ def paint_canvas!(canvas)
140
+ x_coverred = 0
141
+ color_ranges.each_with_index do |color_range, index|
142
+ color_range.gradient = self
143
+ color_range.opacity_ranges = opacity_ranges
144
+
145
+ # When the first color location is not 0
146
+ x_coverred += paint_prefix(canvas, color_range, x_coverred) if index == 0
147
+
148
+ x_coverred += paint_body(canvas, color_range, x_coverred)
149
+
150
+ # When the last color location is not 100
151
+ x_coverred += paint_suffix(canvas, color_range, x_coverred) if index + 1 == color_ranges.size
152
+ end
153
+ end
154
+
155
+ def paint_prefix(canvas, color_range, x_coverred)
156
+ color_range.from_location_in_pixel.times do |x|
157
+ color_range.current_x = x
158
+ height.times { |y| canvas[x, y] = color_range.current_color }
159
+ end
160
+
161
+ color_range.from_location_in_pixel
162
+ end
163
+
164
+ def paint_body(canvas, color_range, x_coverred)
165
+
166
+ color_range.width.times do |x|
167
+ x += x_coverred
168
+ color_range.current_x = x
169
+ height.times { |y| canvas[x, y] = color_range.current_color }
170
+ end
171
+
172
+ color_range.width
173
+ end
174
+
175
+ def paint_suffix(canvas, color_range, x_coverred)
176
+ color_range.leftover_width.times do |x|
177
+ x += x_coverred
178
+ color_range.current_x = x
179
+ height.times { |y| canvas[x, y] = color_range.current_color }
180
+ end
181
+
182
+ color_range.leftover_width
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,23 @@
1
+ module Rainbow
2
+ class Opacity
3
+
4
+ attr_reader :value, :location
5
+
6
+ # value - Fixnum (0-100)
7
+ # location - Fixnum (0-100)
8
+ def initialize(value, location)
9
+ @value_percentage = value
10
+ @value = (255.0 * value / 100).round
11
+ @location = location
12
+
13
+ assert_arguments!
14
+ end
15
+
16
+ private
17
+
18
+ def assert_arguments!
19
+ raise ArgumentError, 'opacity must be between 0 and 100' if @value_percentage < 0 || @value_percentage > 100
20
+ raise ArgumentError, 'location must be between 0 and 100' if location < 0 || location > 100
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,95 @@
1
+ module Rainbow
2
+ class OpacityRange
3
+ attr_reader :from_opacity, :to_opacity, :mid_point, :gradient, :mid_opacity
4
+ attr_reader :width, :first_width, :second_width, :leftover_width
5
+ attr_reader :from_location_in_pixel, :mid_location_in_pixel, :to_location_in_pixel
6
+ attr_reader :tmp_current_x, :tmp_diff, :tmp_from, :tmp_to, :tmp_distance_in_pixel
7
+ attr_reader :scale_start_in_pixel, :scale_end_in_pixel, :scale_mid_in_pixel
8
+
9
+ def initialize(from_opacity, to_opacity, mid_point)
10
+ @from_opacity = from_opacity
11
+ @to_opacity = to_opacity
12
+ @mid_point = mid_point
13
+
14
+ @mid_opacity = from_opacity.value + (to_opacity.value - from_opacity.value) / 2
15
+ end
16
+
17
+ def included?(x)
18
+ @from_location_in_pixel <= x && x <= @to_location_in_pixel
19
+ end
20
+
21
+ def gradient=(gradient)
22
+ @gradient = gradient
23
+ compute_variables
24
+ end
25
+
26
+ def current_x=(x)
27
+ if x == 0 && from_opacity.location > 0
28
+ @tmp_current_x = 0.0
29
+ @tmp_diff = 0
30
+ @tmp_from = from_opacity.value
31
+ @tmp_to = from_opacity.value
32
+ @tmp_distance_in_pixel = from_location_in_pixel
33
+ elsif x == to_location_in_pixel && to_opacity.location < 100
34
+ @tmp_current_x = 0.0
35
+ @tmp_diff = 0
36
+ @tmp_from = to_opacity.value
37
+ @tmp_to = to_opacity.value
38
+ @tmp_distance_in_pixel = leftover_width
39
+ elsif x == from_location_in_pixel && gradient.scale_100?
40
+ @tmp_current_x = 0.0
41
+ @tmp_diff = mid_opacity - from_opacity.value
42
+ @tmp_from = from_opacity.value
43
+ @tmp_to = mid_opacity
44
+ @tmp_distance_in_pixel = first_width
45
+ elsif x == mid_location_in_pixel && gradient.scale_100?
46
+ @tmp_current_x = 0.0
47
+ @tmp_diff = to_opacity.value - mid_opacity
48
+ @tmp_from = mid_opacity
49
+ @tmp_to = to_opacity.value
50
+ @tmp_distance_in_pixel = second_width
51
+ elsif x == from_location_in_pixel && !gradient.scale_100?
52
+ @tmp_current_x = 0.0
53
+ @tmp_diff = 0
54
+ @tmp_from = from_opacity.value
55
+ @tmp_to = from_opacity.value
56
+ @tmp_distance_in_pixel = 1
57
+ elsif x == gradient.scale_start_location_in_pixel && !gradient.scale_100?
58
+ @tmp_current_x = 0.0
59
+ @tmp_diff = mid_opacity - from_opacity.value
60
+ @tmp_from = from_opacity.value
61
+ @tmp_to = from_opacity.value
62
+ @tmp_distance_in_pixel = gradient.scale_half_distance_in_pixel
63
+ elsif x == gradient.scale_mid_location_in_pixel && !gradient.scale_100?
64
+ @tmp_current_x = 0.0
65
+ @tmp_diff = to_opacity.value - mid_opacity
66
+ @tmp_from = mid_opacity
67
+ @tmp_to = to_opacity.value
68
+ @tmp_distance_in_pixel = gradient.scale_half_distance_in_pixel
69
+ else
70
+ @tmp_current_x += 1
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def compute_variables
77
+ @width = (to_opacity.location - from_opacity.location) * gradient.width / 100
78
+ @first_width = @width * mid_point / 100
79
+ @second_width = @width - @first_width
80
+
81
+ @from_location_in_pixel = from_opacity.location * gradient.width / 100
82
+ @mid_location_in_pixel = @from_location_in_pixel + @first_width
83
+ @to_location_in_pixel = @from_location_in_pixel + @width
84
+
85
+ @leftover_width = (100 - to_opacity.location) * gradient.width / 100
86
+
87
+ scale_start = (100 - gradient.scale) / 2
88
+ scale_end = scale_start == 0 ? 100 : scale_start + gradient.scale
89
+
90
+ @scale_start_in_pixel = scale_start * gradient.width / 100
91
+ @scale_end_in_pixel = scale_end * gradient.width / 100
92
+ @scale_mid_in_pixel = @scale_start_in_pixel + (@scale_end_in_pixel - @scale_start_in_pixel) / 2
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,41 @@
1
+ module Rainbow
2
+ class OpacityRanges
3
+ extend Forwardable
4
+ def_delegators :@current_range, :tmp_current_x, :tmp_from, :tmp_to, :tmp_diff, :tmp_distance_in_pixel
5
+
6
+ attr_reader :ranges, :gradient, :current_x
7
+
8
+ def initialize(opacity_ranges, gradient)
9
+ @ranges = opacity_ranges
10
+ @gradient = gradient
11
+
12
+ @ranges.each { |range| range.gradient = gradient }
13
+ end
14
+
15
+ def current_x=(x)
16
+ @current_range = get_range_by_x(x)
17
+ @current_range.current_x = x
18
+ end
19
+
20
+ private
21
+
22
+ def get_range_by_x(x)
23
+ # since x is started at 0
24
+ x += 1
25
+
26
+ range = ranges.find { |r| r.included?(x) }
27
+ return range if range
28
+
29
+ first_range = ranges.first
30
+ last_range = ranges.last
31
+
32
+ if first_range.from_opacity.location > 0 && first_range.from_location_in_pixel > x
33
+ ranges.first
34
+ elsif last_range.to_opacity.location < 100 && x > last_range.to_location_in_pixel
35
+ ranges.last
36
+ else
37
+ raise "Cannot find current opacity range! .(#{first_range.from_location_in_pixel} - #{last_range.to_location_in_pixel}); (#{x})"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Rainbow
2
+ VERSION = '0.0.1'
3
+ end
data/lib/rainbow.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'forwardable'
2
+
3
+ require 'chunky_png'
4
+
5
+ require 'rainbow/version'
6
+ require 'rainbow/color'
7
+ require 'rainbow/color_range'
8
+ require 'rainbow/opacity'
9
+ require 'rainbow/opacity_range'
10
+ require 'rainbow/opacity_ranges'
11
+ require 'rainbow/gradient'
12
+
13
+ module Rainbow
14
+ end
data/rainbow.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rainbow/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rainbow_gradient'
8
+ spec.version = Rainbow::VERSION
9
+ spec.authors = ['Sophy Eung']
10
+ spec.email = ['ungsophy@gmail.com']
11
+ spec.summary = %q{A Ruby library to generate gradient.}
12
+ spec.description = %q{A Ruby library to generate gradient.}
13
+ spec.homepage = 'https://github.com/ungsophy/rainbow'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'chunky_png', '~> 1.3.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.5'
24
+ spec.add_development_dependency 'minitest', '~> 5.3.0'
25
+ spec.add_development_dependency 'rake'
26
+ end
@@ -0,0 +1,35 @@
1
+ require './test/test_helper'
2
+
3
+ describe Rainbow::Color do
4
+ let(:red) { 112 }
5
+ let(:green) { 95 }
6
+ let(:blue) { 25 }
7
+ let(:color_int) { ChunkyPNG::Color(red, green, blue) }
8
+ let(:color) { Rainbow::Color.new(color_int, 90) }
9
+
10
+ describe 'when location is not between 0 and 100' do
11
+ it 'raises ArgumentError' do
12
+ -> { Rainbow::Color.new(color_int, 110) }.must_raise ArgumentError
13
+ -> { Rainbow::Color.new(color_int, 101) }.must_raise ArgumentError
14
+ -> { Rainbow::Color.new(color_int, -1) }.must_raise ArgumentError
15
+ end
16
+ end
17
+
18
+ describe '#r' do
19
+ it 'returns red color code' do
20
+ color.r.must_equal red
21
+ end
22
+ end
23
+
24
+ describe '#g' do
25
+ it 'returns green color code' do
26
+ color.g.must_equal green
27
+ end
28
+ end
29
+
30
+ describe '#b' do
31
+ it 'returns blue color code' do
32
+ color.b.must_equal blue
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ require './test/test_helper'
2
+
3
+ describe Rainbow::Opacity do
4
+ describe 'when opacity is not between 0 and 100' do
5
+ it 'raises ArgumentError' do
6
+ -> { Rainbow::Opacity.new(101, 0) }.must_raise ArgumentError
7
+ -> { Rainbow::Opacity.new(-1, 0) }.must_raise ArgumentError
8
+ end
9
+ end
10
+
11
+ describe 'when location is not between 0 and 100' do
12
+ it 'raises ArgumentError' do
13
+ -> { Rainbow::Opacity.new(10, -1) }.must_raise ArgumentError
14
+ -> { Rainbow::Opacity.new(10, 101) }.must_raise ArgumentError
15
+ end
16
+ end
17
+
18
+ describe '#value' do
19
+ it 'returns value' do
20
+ opacity = Rainbow::Opacity.new(10, 50)
21
+ opacity.value.must_equal 26
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ gem 'minitest'
2
+ require 'minitest/autorun'
3
+ require 'pp'
4
+
5
+ require 'rainbow'
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rainbow_gradient
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sophy Eung
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chunky_png
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 5.3.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 5.3.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A Ruby library to generate gradient.
70
+ email:
71
+ - ungsophy@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - example/data/gradient2.yml
82
+ - example/data/gradient3.yml
83
+ - example/gradient.png
84
+ - example/gradient.rb
85
+ - example/gradient2.rb
86
+ - example/gradient3.rb
87
+ - example/gradient_factory.rb
88
+ - example/output/gradient.png
89
+ - example/output/gradient2_1.png
90
+ - example/output/gradient2_2.png
91
+ - example/output/gradient2_3.png
92
+ - example/output/gradient3_1.png
93
+ - example/output/gradient3_2.png
94
+ - example/output/gradient3_3.png
95
+ - example/output/gradient3_4.png
96
+ - example/psd/gradient.psd
97
+ - example/psd/gradient2.psd
98
+ - example/psd/gradient3.psd
99
+ - lib/rainbow.rb
100
+ - lib/rainbow/color.rb
101
+ - lib/rainbow/color_range.rb
102
+ - lib/rainbow/gradient.rb
103
+ - lib/rainbow/opacity.rb
104
+ - lib/rainbow/opacity_range.rb
105
+ - lib/rainbow/opacity_ranges.rb
106
+ - lib/rainbow/version.rb
107
+ - rainbow.gemspec
108
+ - test/lib/color_test.rb
109
+ - test/lib/opacity_test.rb
110
+ - test/test_helper.rb
111
+ homepage: https://github.com/ungsophy/rainbow
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.2.2
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: A Ruby library to generate gradient.
135
+ test_files:
136
+ - test/lib/color_test.rb
137
+ - test/lib/opacity_test.rb
138
+ - test/test_helper.rb