File: color.rb

package info (click to toggle)
ruby-rainbow 3.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 132 kB
  • sloc: ruby: 559; makefile: 6
file content (150 lines) | stat: -rw-r--r-- 3,465 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# frozen_string_literal: true

module Rainbow
  class Color
    attr_reader :ground

    def self.build(ground, values)
      unless [1, 3].include?(values.size)
        raise ArgumentError,
              "Wrong number of arguments for color definition, should be 1 or 3"
      end

      color = values.size == 1 ? values.first : values

      case color
      # NOTE: Properly handle versions before/after Ruby 2.4.0.
      # Ruby 2.4+ unifies Fixnum & Bignum into Integer.
      # However previous versions would still use Fixnum.
      # To avoid missing `Fixnum` input, call `0.class` which would
      # return either `Integer` or `Fixnum`.
      when 0.class
        Indexed.new(ground, color)
      when Symbol
        if Named.color_names.include?(color)
          Named.new(ground, color)
        elsif X11Named.color_names.include?(color)
          X11Named.new(ground, color)
        else
          raise ArgumentError,
                "Unknown color name, valid names: " +
                (Named.color_names + X11Named.color_names).join(', ')
        end
      when Array
        RGB.new(ground, *color)
      when String
        RGB.new(ground, *parse_hex_color(color))
      end
    end

    def self.parse_hex_color(hex)
      unless hex =~ /^#?[a-f0-9]{6}/i
        raise ArgumentError,
              "Invalid hexadecimal RGB triplet. Valid format: /^#?[a-f0-9]{6}/i"
      end

      hex = hex.sub(/^#/, '')
      r   = hex[0..1].to_i(16)
      g   = hex[2..3].to_i(16)
      b   = hex[4..5].to_i(16)

      [r, g, b]
    end

    class Indexed < Color
      attr_reader :num

      def initialize(ground, num)
        @ground = ground
        @num = num
      end

      def codes
        code = num + (ground == :foreground ? 30 : 40)

        [code]
      end
    end

    class Named < Indexed
      NAMES = {
        black: 0,
        red: 1,
        green: 2,
        yellow: 3,
        blue: 4,
        magenta: 5,
        cyan: 6,
        white: 7,
        default: 9
      }.freeze

      def self.color_names
        NAMES.keys
      end

      def self.valid_names
        color_names.join(', ')
      end

      def initialize(ground, name)
        unless Named.color_names.include?(name)
          raise ArgumentError,
                "Unknown color name, valid names: #{self.class.valid_names}"
        end

        super(ground, NAMES[name])
      end
    end

    class RGB < Indexed
      attr_reader :r, :g, :b

      def self.to_ansi_domain(value)
        (6 * (value / 256.0)).to_i
      end

      def initialize(ground, *values)
        if values.min.negative? || values.max > 255
          raise ArgumentError, "RGB value outside 0-255 range"
        end

        super(ground, 8)
        @r, @g, @b = values
      end

      def codes
        super + [5, code_from_rgb]
      end

      private

      def code_from_rgb
        16 + self.class.to_ansi_domain(r) * 36 +
          self.class.to_ansi_domain(g) * 6 +
          self.class.to_ansi_domain(b)
      end
    end

    class X11Named < RGB
      include X11ColorNames

      def self.color_names
        NAMES.keys
      end

      def self.valid_names
        color_names.join(', ')
      end

      def initialize(ground, name)
        unless X11Named.color_names.include?(name)
          raise ArgumentError,
                "Unknown color name, valid names: #{self.class.valid_names}"
        end

        super(ground, *NAMES[name])
      end
    end
  end
end