style_train 0.3.2 → 0.4.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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.4.0
data/lib/style_train.rb CHANGED
@@ -16,14 +16,7 @@ module StyleTrain
16
16
  end
17
17
 
18
18
  # The color load!
19
- require 'style_train/color_types/color_type'
20
- require 'style_train/color_types/rgb_color'
21
- require 'style_train/color_types/hsl_color'
22
- require 'style_train/color_types/keyword_color'
23
- require 'style_train/color_types/hex_color'
24
19
  require "style_train/color"
25
-
26
- # Big Picture Stuff, sheets, themes, etc
27
20
  require 'style_train/style'
28
21
  require "style_train/sheet"
29
22
  require 'style_train/theme'
@@ -1,92 +1,507 @@
1
- # This is a delegatory class that passes most everything to the particular ColorType descendant
2
1
  module StyleTrain
3
2
  class Color
4
- attr_accessor :delegate, :background_set
5
-
6
- # Constructor
7
- def initialize( color, opts={} )
8
- opts = Gnash.new(opts).merge(:color => color)
9
- background_opts = opts.delete(:background)
10
- self.background = background_opts if background_opts
11
- if color.is_a?(Color)
12
- self.delegate = color.delegate.dup
13
- self.alpha = opts[:alpha] if opts[:alpha]
14
- self.background = color.background if color.background_set
15
- else
16
- color_types.each do |klass|
17
- if instance = klass.make(opts)
18
- self.delegate = instance
19
- break
20
- end
21
- end
22
- end
23
- end
24
-
25
- def self.color_types
26
- @color_type ||= ColorType.color_types
27
- end
28
-
29
- def color_types
30
- self.class.color_types
31
- end
32
-
33
- # delegated methods
34
- [:alpha, :r, :g, :b].each do |meth|
35
- class_eval "
36
- def #{meth}
37
- self.delegate.#{meth}
3
+ attr_accessor :type, :r, :g, :b,
4
+ :hex, :hex_6, :h, :s, :l
5
+ attr_reader :alpha
6
+ attr_writer :mix_ratio
7
+
8
+ def initialize(*args)
9
+ if type = args.last.is_a?(Hash) && args.last[:type]
10
+ build_for_type(type, args)
11
+ else
12
+ autodetect(args)
13
+ end
14
+
15
+ if non_null?
16
+ if (opts = args.last) && args.last.is_a?(Hash)
17
+ self.alpha = opts[:alpha]
18
+ self.background = opts[:background] if opts[:background]
19
+ end
20
+ end
21
+ self.alpha = 1.0 if alpha.nil? && non_null?
22
+ end
23
+
24
+ def transparent?
25
+ type == :transparent
26
+ end
27
+
28
+ def non_null?
29
+ !transparent?
30
+ end
31
+
32
+ def build_for_type(type, args)
33
+ self.type = type
34
+ color = if type == :transparent
35
+ build_transparent(args)
36
+ elsif type == :keyword
37
+ build_keyword(args)
38
+ elsif type == :hex
39
+ build_hex(args)
40
+ elsif type == :rgb
41
+ build_rgb(args)
42
+ elsif type == :hsl
43
+ build_hsl(args)
44
+ end
45
+ raise ArgumentError, "Unable to build a color of type #{type}, with arguments: #{args.inspect}" unless color
46
+ end
47
+
48
+ def autodetect(args)
49
+ build_transparent(args) ||
50
+ build_keyword(args) ||
51
+ build_hex(args) ||
52
+ build_rgb(args) ||
53
+ build_hsl(args) || raise( AutodetectionError )
54
+ end
55
+
56
+ def build_transparent( args )
57
+ if [:transparent, 'transparent'].include?(args.first)
58
+ self.type = :transparent
59
+ self.r = self.g = self.b = 0
60
+ self.alpha = 0.0
61
+ self
62
+ end
63
+ end
64
+
65
+ def build_keyword( args )
66
+ if (color = args.first).is_a?(Symbol)
67
+ self.type = :keyword
68
+ raise KeywordError, "Color #{color.inspect} not found as a keyword" unless rgb = KEYWORD_MAP[color]
69
+ self.r = rgb[0]/255.0
70
+ self.g = rgb[1]/255.0
71
+ self.b = rgb[2]/255.0
72
+ self
73
+ end
74
+ end
75
+
76
+ def build_hex(args)
77
+ if args.size < 3 && args.first.to_s.match(/^#?([a-f0-9]{3}|[a-f0-9]{6})$/i)
78
+ self.type = :hex
79
+ self.hex = "#{$1}"
80
+ self.hex_6 = self.class.normalize_hex(hex)
81
+ self.r = ((self.hex_6 / 0x10000) & 0xff) / 255.0
82
+ self.g = ((self.hex_6 / 0x100) & 0xff) / 255.0
83
+ self.b = (self.hex_6 & 0xff) /255.0
84
+ self
85
+ end
86
+ end
87
+
88
+ def build_rgb(args)
89
+ array = args[0..2].compact
90
+ first_value = self.class.normalize_for_rgb(array.first) rescue nil
91
+ return nil unless array.size == 3 && first_value
92
+
93
+ self.type = :rgb
94
+ self.r = first_value
95
+ self.g = self.class.normalize_for_rgb(array[1])
96
+ self.b = self.class.normalize_for_rgb(array[2])
97
+ self
98
+ end
99
+
100
+ def build_hsl(args)
101
+ array = args[0..2].compact
102
+ hue = self.class.normalize_hue(array.first) rescue nil
103
+ return nil unless array.size == 3 && hue
104
+
105
+ self.type = :hsl
106
+ self.h = hue
107
+ self.s = self.class.normalize_for_hsl(array[1])
108
+ self.l = self.class.normalize_for_hsl(array[2])
109
+ build_rgb_from_hsl
110
+
111
+ self
112
+ end
113
+
114
+ def self.normalize_hue(value)
115
+ if value.is_a?(String)
116
+ if value.match(/degrees$/)
117
+ normalize_degrees(value)
118
+ else
119
+ normalize_percentage(value) / 100.0
120
+ end
121
+ elsif value.is_a?(Float)
122
+ normalize_ratio(value)
123
+ else
124
+ normalize_degrees(value) # hue does not come it byte values
125
+ end
126
+ end
127
+
128
+ def self.normalize_for_hsl(value)
129
+ if value.is_a?(String)
130
+ normalize_percentage(value)/100.0 if value.match(/%$/)
131
+ elsif value.is_a?(Float)
132
+ normalize_ratio(value)
133
+ else # integer
134
+ normalize_byte(value)/255.0
135
+ end
136
+ end
137
+
138
+ def build_rgb_from_hsl
139
+ self.r = hsl_value_to_rgb( h + 1/3.0 )
140
+ self.g = hsl_value_to_rgb( h )
141
+ self.b = hsl_value_to_rgb( h - 1/3.0 )
142
+ end
143
+
144
+ def hsl_value_to_rgb( value=[h,s,l] )
145
+ value = value + 1 if value < 0
146
+ value = value - 1 if value > 1
147
+ if value * 6 < 1
148
+ hsl_median_1 + (hsl_median_2 - hsl_median_1)*value*6
149
+ elsif value * 2 < 1
150
+ hsl_median_2
151
+ elsif value * 3 < 2
152
+ hsl_median_1 + (hsl_median_2 - hsl_median_1)*(2/3.0 - value)*6
153
+ else
154
+ hsl_median_1
155
+ end
156
+ end
157
+
158
+ def hsl_median_2
159
+ l <= 0.5 ? l*(s+1) : l+s-l*s
160
+ end
161
+
162
+ def hsl_median_1
163
+ l*2-hsl_median_2
164
+ end
165
+
166
+ # CLASS LEVEL VALUE NORMALIZERS -----------------------------------------------
167
+ def self.normalize_for_rgb(value)
168
+ if value.is_a?(String)
169
+ if value.match(percentage_regex)
170
+ normalize_percentage(value)/100.0
171
+ elsif value.match(degree_regex)
172
+ return nil
173
+ else
174
+ normalize_byte(value)/255.0
175
+ end
176
+ elsif value.is_a?(Float)
177
+ normalize_ratio(value)
178
+ else # integer
179
+ normalize_byte(value)/255.0
180
+ end
181
+ end
182
+
183
+ def self.normalize_percentage(value)
184
+ if value.class == String && value.match(percentage_regex)
185
+ value = $1
186
+ end
187
+ value = value.to_f
188
+ raise PercentageError if value > 100.0 || value < 0.0
189
+ value
190
+ end
191
+
192
+ def self.percentage_regex
193
+ /^([\d.]*)%$/
194
+ end
195
+
196
+ def self.normalize_degrees(value)
197
+ return nil if value.is_a?(String) && !value.match(degree_regex)
198
+ (value.to_i % 360)/360.0
199
+ end
200
+
201
+ def self.degree_regex
202
+ /degrees$/
203
+ end
204
+
205
+ def self.normalize_byte(value)
206
+ raise ByteError if value > 255 || value < 0
207
+ value
208
+ end
209
+
210
+ def self.normalize_ratio(value)
211
+ raise RatioError if value > 1 || value < 0
212
+ value
213
+ end
214
+
215
+ def self.ratio_to_byte(value)
216
+ raise RatioError if value < 0 || value > 1.0
217
+ percentage_to_byte(value*100)
218
+ end
219
+
220
+ def self.normalize_hex( hex )
221
+ expanded = ""
222
+ if hex.size == 3
223
+ hex.each_char{|c| expanded << c*2}
224
+ else
225
+ expanded = hex
226
+ end
227
+
228
+ "0x#{expanded}".to_i(16)
229
+ end
230
+
231
+ def self.percentage_to_byte(value)
232
+ (normalize_percentage(value) * 2.55 ).round
233
+ end
234
+
235
+ # OPTIONAL PROPERTIES ----------------------------------------------
236
+ def alpha=(value)
237
+ if value
238
+ @alpha = if value.is_a?(String) || value.is_a?(Fixnum)
239
+ self.class.normalize_percentage(value)/100.0
240
+ else
241
+ self.class.normalize_ratio(value)
38
242
  end
39
- "
40
- end
41
-
42
- def alpha=( value )
43
- self.delegate.alpha = value
44
- end
45
-
46
- def to(key)
47
- self.delegate.to(key)
48
- end
49
-
50
- def =~( color )
51
- if color.is_a?(Color)
52
- self.delegate =~ color.delegate
53
- elsif color.is_a?( ColorType )
54
- self.delegate =~ color
55
- end
56
- end
57
-
58
- def ==( color )
59
- color.is_a?( Color ) && self =~ color && self.background == color.background
60
- end
61
-
62
- # rendering
243
+ end
244
+ end
245
+
246
+ def background=(value)
247
+ @background = if value.is_a?(self.class)
248
+ value
249
+ else
250
+ self.class.new(*value)
251
+ end
252
+ end
253
+
63
254
  def background
64
- @background ||= Color.new(:white).to(:hex)
65
- end
66
-
67
- def background=( opts )
68
- color = opts.is_a?(Hash) && (opts[:color] || opts['color'])
255
+ @background || self.class.new(:white) unless type == :transparent
256
+ end
257
+
258
+ # COMPARITORS ------------------------------------------------------
259
+ COMPARE_TOLERANCE = 0.001
260
+
261
+ def ==(other)
262
+ self =~ other &&
263
+ self.alpha == other.alpha
264
+ end
265
+
266
+ def ===(other)
267
+ self.object_id == other.object_id
268
+ end
269
+
270
+ def =~(other)
271
+ (self.r - other.r).abs < COMPARE_TOLERANCE &&
272
+ (self.g - other.g).abs < COMPARE_TOLERANCE&&
273
+ (self.b - other.b).abs < COMPARE_TOLERANCE
274
+ end
275
+
276
+ # CONVERSIONS ------------------------------------------------------
277
+ def set_hsl
278
+ rgb = [r, g, b]
279
+ max = rgb.max
280
+ min = rgb.min
281
+ avg = ( max + min )/2.0
282
+ diff = ( max - min ).to_f
283
+ lightness = avg
284
+
285
+ if max == min # achromatic
286
+ hue = 0
287
+ saturation = 0
288
+ else
289
+ saturation = if avg > 0.6
290
+ lightness / ( 2 - max - min )
291
+ else
292
+ diff / ( max + min )
293
+ end
294
+
295
+ diff_r = (((max - r) / 6) + (diff / 2)) / diff;
296
+ diff_g = (((max - g) / 6) + (diff / 2)) / diff;
297
+ diff_b = (((max - b) / 6) + (diff / 2)) / diff;
298
+
299
+ hue = if r == max
300
+ diff_b - diff_g
301
+ elsif g == max
302
+ 1/3.0 + diff_r - diff_b
303
+ else
304
+ 2/3.0 + diff_g - diff_r
305
+ end
306
+ end
307
+
308
+
309
+ hue += 1 if hue < 0
310
+ hue -= 1 if hue > 1
311
+
312
+ self.type = :hsl
313
+ self.h = hue
314
+ self.s = saturation
315
+ self.l = lightness
316
+
317
+ self
318
+ end
319
+
320
+ def set_rgb
321
+ self.type = :rgb
322
+ end
323
+
324
+ def set_hex
325
+ self.type = :hex
326
+ self.hex = ""
327
+ self.hex << sprintf( "%02X", r*255 );
328
+ self.hex << sprintf( "%02X", g*255 );
329
+ self.hex << sprintf( "%02X", b*255 );
330
+ self.hex_6 = self.class.normalize_hex(hex)
331
+ end
332
+
333
+ def hsl_to_rgb
334
+ self.hex = nil
335
+ self.hex_6 = nil
336
+ build_rgb_from_hsl
337
+ self
338
+ end
339
+
340
+ # TRANSFORMATIONS --------------------------------------------
341
+ def reset_for_shift
342
+ self.hex = nil
343
+ self.hex_6 = nil
344
+ set_hsl unless :type == :hsl
345
+ end
346
+
347
+ def shift!(value)
348
+ reset_for_shift
349
+ value = value/100.0 if value.is_a?(Integer)
350
+ value = self.class.normalize_percentage(value)/100.0 if value.is_a?(String)
351
+ value = self.class.normalize_ratio(value)
352
+ yield(value)
353
+ hsl_to_rgb
354
+ end
355
+
356
+ def lighten(value=0.1)
357
+ color = self.dup
358
+ color.lighten!(value)
359
+ end
360
+
361
+ def lighten!(value=0.1)
362
+ shift!(value) {|v| self.l = [1.0, l + v].min }
363
+ end
364
+
365
+ def darken(value=0.1)
366
+ color = self.dup
367
+ color.darken!(value)
368
+ end
369
+
370
+ def darken!(value=0.1)
371
+ shift!(value) { |v| self.l = [0.0, l - v].max }
372
+ end
373
+
374
+ def saturate(value=0.1)
375
+ color = self.dup
376
+ color.saturate!(value)
377
+ end
378
+
379
+ def saturate!(value=0.1)
380
+ shift!(value) { |v| self.s = [1.0, s + v].min }
381
+ end
382
+
383
+ def dull(value=0.1)
384
+ color = self.dup
385
+ color.dull!(value)
386
+ end
387
+
388
+ def dull!(value=0.1)
389
+ shift!(value) { |v| self.s = [0.0, s - v].max }
390
+ end
391
+
392
+ alias :brighten :saturate
393
+ alias :brighten! :saturate!
394
+ alias :desaturate :dull
395
+ alias :desaturate! :dull!
396
+
397
+ def shift_hue!(value)
398
+ reset_for_shift
399
+ value = self.class.normalize_hue(value)
400
+ yield(value)
401
+ hsl_to_rgb
402
+ end
403
+
404
+ def rotate(value=10.degrees)
405
+ color = self.dup
406
+ color.rotate!(value)
407
+ end
408
+
409
+ def rotate!(value=10.degrees)
410
+ shift_hue!(value) {|v| self.h = (v + h) % 1 }
411
+ end
412
+
413
+ def compliment
414
+ rotate(0.5)
415
+ end
69
416
 
70
- @background = if color
71
- opts.delete(:alpha)
72
- Color.new(color, opts)
73
- elsif opts.is_a? Color
74
- color = opts.dup # so that the original color's alpha isn't affected
75
- color.alpha = 1.0
76
- color
77
- else
78
- Color.new(opts)
417
+ def dial(divisions=3, offset = 0.0)
418
+ divisions = divisions.to_i
419
+ offset = self.class.normalize_hue(offset)
420
+ set = []
421
+ (1..divisions).each do |n|
422
+ amount = (offset + (n-1)/divisions.to_f)%1
423
+ set << rotate(amount)
79
424
  end
80
- raise ArgumentError, "Background with color: #{color} and options: #{opts} not found" unless @background
81
- raise ArgumentError, "Background color #{@background.inspect} has no delegate color" unless @background.delegate
425
+ set
426
+ end
427
+
428
+ def triangulate(offset = 0.0)
429
+ dial(3, offset)
430
+ end
431
+
432
+ COLDEST_HUE = 240/360.0
433
+ WARMEST_HUE = 0.0
434
+
435
+ def rotate_in_temperature!(value)
436
+ set_hsl
437
+ value = self.class.normalize_hue(value)
438
+ unless [COLDEST_HUE, WARMEST_HUE].include?(h)
439
+ new_h = yield(value)
440
+ rotate!((new_h - h)%1)
441
+ end
442
+ self
443
+ end
444
+
445
+ def warmer!(value=10.degrees)
446
+ rotate_in_temperature!(value) do |v|
447
+ if h < COLDEST_HUE
448
+ [WARMEST_HUE, (h - v)].max.abs
449
+ else
450
+ [1.0-WARMEST_HUE, (h + v)].min.abs
451
+ end
452
+ end
453
+ end
454
+
455
+ def cooler!(value=10.degrees)
456
+ rotate_in_temperature!(value) do |v|
457
+ new_h = if h < COLDEST_HUE
458
+ [COLDEST_HUE, (h + v)].min.abs
459
+ else
460
+ [COLDEST_HUE, (h - v)].max.abs
461
+ end
462
+ end
463
+ end
464
+
465
+ def warmer(value=10.degrees)
466
+ color = self.dup
467
+ color.warmer!(value)
468
+ end
469
+
470
+ def cooler(value=10.degrees)
471
+ color = self.dup
472
+ color.cooler!(value)
473
+ end
474
+
475
+ # COMBINING COLORS -------------------------------------------
476
+ def mix(other, ratio=nil)
477
+ ratio = 1-ratio if ratio
478
+ this_ratio = mix_ratio
479
+ ratio ||= this_ratio
480
+ other_ratio = other.mix_ratio
481
+ ratio ||= other_ratio ? 1-other_ratio : 0.5
482
+
483
+ color = self.dup
484
+ color.r = self.class.mix(color.r, other.r, ratio)
485
+ color.g = self.class.mix(color.g, other.g, ratio)
486
+ color.b = self.class.mix(color.b, other.b, ratio)
487
+ color.alpha = self.class.mix(color.alpha, other.alpha, ratio)
488
+ color
489
+ end
82
490
 
83
- self.background_set = true
84
- @background
491
+ def layer(other)
492
+ color = self.dup
493
+ ratio = 1 - other.alpha
494
+ color.r = self.class.mix(color.r, other.r, ratio)
495
+ color.g = self.class.mix(color.g, other.g, ratio)
496
+ color.b = self.class.mix(color.b, other.b, ratio)
497
+ color.alpha = self.class.blend_alphas( color.alpha, other.alpha )
498
+
499
+ color
85
500
  end
86
501
 
87
502
  def step(end_color, steps=10)
88
- start_color = self.delegate
89
- end_color = end_color.delegate
503
+ start_color = self.dup
504
+ end_color = end_color.dup
90
505
  array = [start_color]
91
506
  (2..(steps-1)).each do |number|
92
507
  ratio = 1 - (steps-number)/steps.to_f
@@ -94,22 +509,279 @@ module StyleTrain
94
509
  end
95
510
  array << end_color
96
511
  array
97
- end
98
-
99
- def render(render_style=nil)
100
- if render_style && render_style.to_sym == :ie
101
- self.background.to(:hex).layer(self.delegate).render
102
- else
103
- self.delegate.render
104
- end
105
- end
106
-
107
- def inspect
108
- "<Color @delegate=#{self.delegate.to_s} @background=#{self.background}>"
109
- end
110
-
111
- def to_s(render_style=nil)
112
- render(render_style)
113
- end
512
+ end
513
+
514
+ def flatten
515
+ background.alpha = 1.0
516
+ background.layer(self)
517
+ end
518
+
519
+ def percent(value)
520
+ self.mix_ratio = self.class.normalize_percentage(value)/100.0
521
+ self
522
+ end
523
+
524
+ def mix_ratio
525
+ ratio = @mix_ratio
526
+ self.mix_ratio = nil
527
+ ratio
528
+ end
529
+
530
+ def self.mix(value_1, value_2, ratio)
531
+ value_1*(ratio) + value_2*(1-ratio)
532
+ end
533
+
534
+ def self.blend_alphas(first, second)
535
+ base, blender = first > second ? [first, second] : [second, first]
536
+ difference = 1.0 - base
537
+ base + difference * blender
538
+ end
539
+
540
+
541
+ # RENDERING -----------------------------------------------------
542
+ def inspect
543
+ "#<#{self.class}:#{self.object_id} @alpha=#{self.alpha} @r=#{self.r} @g=#{self.g} @b=#{self.b}>"
544
+ end
545
+
546
+ def render(as=self.class.render_as)
547
+ if as == :hsl
548
+ render_as_hsl
549
+ elsif as == :hsla
550
+ render_as_hsla
551
+ elsif as == :rgb
552
+ render_as_rgb
553
+ elsif as == :rgba
554
+ render_as_rgba
555
+ elsif as == :flat
556
+ render_as_flat
557
+ else
558
+ render_as_hex
559
+ end
560
+ end
561
+
562
+ alias :to_s :render
563
+
564
+ def render_as_flat
565
+ color = flatten
566
+ color.render
567
+ end
568
+
569
+ def render_as_hex
570
+ if alpha < 1
571
+ render_as_rgba
572
+ else
573
+ set_hex
574
+ "##{hex}"
575
+ end
576
+ end
577
+
578
+ def render_as_rgb
579
+ if alpha < 1
580
+ render_as_rgba
581
+ else
582
+ "rgb(#{r*100}%, #{g*100}%, #{b*100}%)"
583
+ end
584
+ end
585
+
586
+ def render_as_rgba
587
+ "rgba(#{r*100}%, #{g*100}%, #{b*100}%, #{alpha})"
588
+ end
589
+
590
+ def render_as_hsl
591
+ if alpha < 1
592
+ render_as_hsla
593
+ else
594
+ set_hsl
595
+ "hsl(#{(h*360).round}, #{s*100}%, #{l*100}%)"
596
+ end
597
+ end
598
+
599
+ def render_as_hsla
600
+ "hsla(#{(h*360).round}, #{s*100}%, #{l*100}%, #{alpha})"
601
+ end
602
+
603
+ def self.render_as
604
+ @render_as || :hex
605
+ end
606
+
607
+ def self.render_as=(value)
608
+ @render_as = value
609
+ end
610
+
611
+ KEYWORD_MAP = {
612
+ :aliceblue => [240, 248, 255],
613
+ :antiquewhite => [250, 235, 215],
614
+ :aqua => [0, 255, 255],
615
+ :aquamarine => [127, 255, 212],
616
+ :azure => [240, 255, 255],
617
+ :beige => [245, 245, 220],
618
+ :bisque => [255, 228, 196],
619
+ :black => [0, 0, 0],
620
+ :blanchedalmond => [255, 235, 205],
621
+ :blue => [0, 0, 255],
622
+ :blueviolet => [138, 43, 226],
623
+ :brown => [165, 42, 42],
624
+ :burlywood => [222, 184, 135],
625
+ :cadetblue => [95, 158, 160],
626
+ :chartreuse => [127, 255, 0],
627
+ :chocolate => [210, 105, 30],
628
+ :coral => [255, 127, 80],
629
+ :cornflowerblue => [100, 149, 237],
630
+ :cornsilk => [255, 248, 220],
631
+ :crimson => [220, 20, 60],
632
+ :cyan => [0, 255, 255],
633
+ :darkblue => [0, 0, 139],
634
+ :darkcyan => [0, 139, 139],
635
+ :darkgoldenrod => [184, 134, 11],
636
+ :darkgray => [169, 169, 169],
637
+ :darkgreen => [0, 100, 0],
638
+ :darkgrey => [169, 169, 169],
639
+ :darkkhaki => [189, 183, 107],
640
+ :darkmagenta => [139, 0, 139],
641
+ :darkolivegreen => [85, 107, 47],
642
+ :darkorange => [255, 140, 0],
643
+ :darkorchid => [153, 50, 204],
644
+ :darkred => [139, 0, 0],
645
+ :darksalmon => [233, 150, 122],
646
+ :darkseagreen => [143, 188, 143],
647
+ :darkslateblue => [72, 61, 139],
648
+ :darkslategray => [47, 79, 79],
649
+ :darkslategrey => [47, 79, 79],
650
+ :darkturquoise => [0, 206, 209],
651
+ :darkviolet => [148, 0, 211],
652
+ :deeppink => [255, 20, 147],
653
+ :deepskyblue => [0, 191, 255],
654
+ :dimgray => [105, 105, 105],
655
+ :dimgrey => [105, 105, 105],
656
+ :dodgerblue => [30, 144, 255],
657
+ :firebrick => [178, 34, 34],
658
+ :floralwhite => [255, 250, 240],
659
+ :forestgreen => [34, 139, 34],
660
+ :fuchsia => [255, 0, 255],
661
+ :gainsboro => [220, 220, 220],
662
+ :ghostwhite => [248, 248, 255],
663
+ :gold => [255, 215, 0],
664
+ :goldenrod => [218, 165, 32],
665
+ :gray => [128, 128, 128],
666
+ :green => [0, 128, 0],
667
+ :greenyellow => [173, 255, 47],
668
+ :grey => [128, 128, 128],
669
+ :honeydew => [240, 255, 240],
670
+ :hotpink => [255, 105, 180],
671
+ :indianred => [205, 92, 92],
672
+ :indigo => [75, 0, 130],
673
+ :ivory => [255, 255, 240],
674
+ :khaki => [240, 230, 140],
675
+ :lavender => [230, 230, 250],
676
+ :lavenderblush => [255, 240, 245],
677
+ :lawngreen => [124, 252, 0],
678
+ :lemonchiffon => [255, 250, 205],
679
+ :lightblue => [173, 216, 230],
680
+ :lightcoral => [240, 128, 128],
681
+ :lightcyan => [224, 255, 255],
682
+ :lightgoldenrodyellow => [250, 250, 210],
683
+ :lightgray => [211, 211, 211],
684
+ :lightgreen => [144, 238, 144],
685
+ :lightgrey => [211, 211, 211],
686
+ :lightpink => [255, 182, 193],
687
+ :lightsalmon => [255, 160, 122],
688
+ :lightseagreen => [32, 178, 170],
689
+ :lightskyblue => [135, 206, 250],
690
+ :lightslategray => [119, 136, 153],
691
+ :lightslategrey => [119, 136, 153],
692
+ :lightsteelblue => [176, 196, 222],
693
+ :lightyellow => [255, 255, 224],
694
+ :lime => [0, 255, 0],
695
+ :limegreen => [50, 205, 50],
696
+ :linen => [250, 240, 230],
697
+ :magenta => [255, 0, 255],
698
+ :maroon => [128, 0, 0],
699
+ :mediumaquamarine => [102, 205, 170],
700
+ :mediumblue => [0, 0, 205],
701
+ :mediumorchid => [186, 85, 211],
702
+ :mediumpurple => [147, 112, 219],
703
+ :mediumseagreen => [60, 179, 113],
704
+ :mediumslateblue => [123, 104, 238],
705
+ :mediumspringgreen => [0, 250, 154],
706
+ :mediumturquoise => [72, 209, 204],
707
+ :mediumvioletred => [199, 21, 133],
708
+ :midnightblue => [25, 25, 112],
709
+ :mintcream => [245, 255, 250],
710
+ :mistyrose => [255, 228, 225],
711
+ :moccasin => [255, 228, 181],
712
+ :navajowhite => [255, 222, 173],
713
+ :navy => [0, 0, 128],
714
+ :oldlace => [253, 245, 230],
715
+ :olive => [128, 128, 0],
716
+ :olivedrab => [107, 142, 35],
717
+ :orange => [255, 165, 0],
718
+ :orangered => [255, 69, 0],
719
+ :orchid => [218, 112, 214],
720
+ :palegoldenrod => [238, 232, 170],
721
+ :palegreen => [152, 251, 152],
722
+ :paleturquoise => [175, 238, 238],
723
+ :palevioletred => [219, 112, 147],
724
+ :papayawhip => [255, 239, 213],
725
+ :peachpuff => [255, 218, 185],
726
+ :peru => [205, 133, 63],
727
+ :pink => [255, 192, 203],
728
+ :plum => [221, 160, 221],
729
+ :powderblue => [176, 224, 230],
730
+ :purple => [128, 0, 128],
731
+ :red => [255, 0, 0],
732
+ :rosybrown => [188, 143, 143],
733
+ :royalblue => [65, 105, 225],
734
+ :saddlebrown => [139, 69, 19],
735
+ :salmon => [250, 128, 114],
736
+ :sandybrown => [244, 164, 96],
737
+ :seagreen => [46, 139, 87],
738
+ :seashell => [255, 245, 238],
739
+ :sienna => [160, 82, 45],
740
+ :silver => [192, 192, 192],
741
+ :skyblue => [135, 206, 235],
742
+ :slateblue => [106, 90, 205],
743
+ :slategray => [112, 128, 144],
744
+ :slategrey => [112, 128, 144],
745
+ :snow => [255, 250, 250],
746
+ :springgreen => [0, 255, 127],
747
+ :steelblue => [70, 130, 180],
748
+ :tan => [210, 180, 140],
749
+ :teal => [0, 128, 128],
750
+ :thistle => [216, 191, 216],
751
+ :tomato => [255, 99, 71],
752
+ :turquoise => [64, 224, 208],
753
+ :violet => [238, 130, 238],
754
+ :wheat => [245, 222, 179],
755
+ :white => [255, 255, 255],
756
+ :whitesmoke => [245, 245, 245],
757
+ :yellow => [255, 255, 0],
758
+ :yellowgreen => [154, 205, 50]
759
+ }
760
+
761
+ class PercentageError < ArgumentError
762
+ def message
763
+ @message ||= 'Percentages must be between 0 and 100'
764
+ end
765
+ end
766
+
767
+ class KeywordError < ArgumentError; end
768
+
769
+ class RatioError < ArgumentError
770
+ def message
771
+ @message ||= "Ratios must be a decimal value between 0 and 1"
772
+ end
773
+ end
774
+
775
+ class ByteError < ArgumentError
776
+ def message
777
+ @message ||= "Bytes must be between 0 and 255"
778
+ end
779
+ end
780
+
781
+ class AutodetectionError < ArgumentError
782
+ def message
783
+ @message ||= "Unable to determine color type"
784
+ end
785
+ end
114
786
  end
115
- end
787
+ end