style_train 0.3.2 → 0.4.0

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