Class: Postsvg::Color

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/postsvg/color.rb

Overview

Immutable RGB color value object. Use the constructors (+.rgb+, .gray, .cmyk, .parse) instead of new when constructing from other color models; they normalize to integer [0, 255] channels.

Constant Summary collapse

NAMED_COLORS =
{
  "black" => [0, 0, 0], "white" => [255, 255, 255],
  "red" => [255, 0, 0], "green" => [0, 128, 0], "blue" => [0, 0, 255],
  "yellow" => [255, 255, 0], "cyan" => [0, 255, 255],
  "magenta" => [255, 0, 255], "gray" => [128, 128, 128],
  "grey" => [128, 128, 128], "silver" => [192, 192, 192],
  "maroon" => [128, 0, 0], "olive" => [128, 128, 0],
  "navy" => [0, 0, 128], "purple" => [128, 0, 128],
  "teal" => [0, 128, 128], "lime" => [0, 255, 0],
  "aqua" => [0, 255, 255], "fuchsia" => [255, 0, 255],
  "orange" => [255, 165, 0], "pink" => [255, 192, 203],
  "brown" => [165, 42, 42],
}.freeze
BLACK =
Color.new(0, 0, 0).freeze
WHITE =
Color.new(255, 255, 255).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(red, green, blue) ⇒ Color

Returns a new instance of Color.



20
21
22
23
24
25
# File 'lib/postsvg/color.rb', line 20

def initialize(red, green, blue)
  @red = Color.clamp_byte(red)
  @green = Color.clamp_byte(green)
  @blue = Color.clamp_byte(blue)
  freeze
end

Instance Attribute Details

#blueObject (readonly)

Returns the value of attribute blue.



10
11
12
# File 'lib/postsvg/color.rb', line 10

def blue
  @blue
end

#greenObject (readonly)

Returns the value of attribute green.



10
11
12
# File 'lib/postsvg/color.rb', line 10

def green
  @green
end

#redObject (readonly)

Returns the value of attribute red.



10
11
12
# File 'lib/postsvg/color.rb', line 10

def red
  @red
end

Class Method Details

.clamp_byte(value) ⇒ Object



12
13
14
# File 'lib/postsvg/color.rb', line 12

def self.clamp_byte(value)
  Integer === value ? value.clamp(0, 255) : value.to_i.clamp(0, 255)
end

.cmyk(c, m, y, k) ⇒ Object

CMYK in [0.0, 1.0]. Conversion matches PLRM ยง8.2.4 simplified formula; not colorimetrically exact.



40
41
42
43
44
45
46
# File 'lib/postsvg/color.rb', line 40

def self.cmyk(c, m, y, k)
  new(
    scale_unit_to_byte((1 - c) * (1 - k)),
    scale_unit_to_byte((1 - m) * (1 - k)),
    scale_unit_to_byte((1 - y) * (1 - k)),
  )
end

.gray(level) ⇒ Object

Gray in [0.0, 1.0].



33
34
35
36
# File 'lib/postsvg/color.rb', line 33

def self.gray(level)
  v = scale_unit_to_byte(level)
  new(v, v, v)
end

.parse(text) ⇒ Object

Parse CSS / SVG color notations: #rgb, #rrggbb, rgb(r, g, b), rgb(r g b / a), named colors (X11 subset).



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/postsvg/color.rb', line 50

def self.parse(text)
  return text if text.is_a?(Color)

  case text.to_s.downcase
  when /\A#([0-9a-f]{6})\z/
    hex = ::Regexp.last_match(1)
    new(hex[0, 2].to_i(16), hex[2, 2].to_i(16), hex[4, 2].to_i(16))
  when /\A#([0-9a-f]{3})\z/
    hex = ::Regexp.last_match(1)
    new((hex[0] * 2).to_i(16), (hex[1] * 2).to_i(16), (hex[2] * 2).to_i(16))
  when /\argb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/
    new(::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i, ::Regexp.last_match(3).to_i)
  when /\argb\(\s*(\d+)\s+(\d+)\s+(\d+)/
    new(::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i, ::Regexp.last_match(3).to_i)
  when "none", "transparent"
    nil
  else
    rgb = NAMED_COLORS[text.to_s]
    return new(*rgb) if rgb

    raise ArgumentError, "unrecognized color: #{text.inspect}"
  end
end

.rgb(r, g, b) ⇒ Object

RGB triple in PS-native [0.0, 1.0] floats.



28
29
30
# File 'lib/postsvg/color.rb', line 28

def self.rgb(r, g, b)
  new(scale_unit_to_byte(r), scale_unit_to_byte(g), scale_unit_to_byte(b))
end

.scale_unit_to_byte(value) ⇒ Object



16
17
18
# File 'lib/postsvg/color.rb', line 16

def self.scale_unit_to_byte(value)
  (value.clamp(0.0, 1.0) * 255.0).round
end

Instance Method Details

#<=>(other) ⇒ Object



100
101
102
103
104
# File 'lib/postsvg/color.rb', line 100

def <=>(other)
  return nil unless other.is_a?(Color)

  [red, green, blue] <=> [other.red, other.green, other.blue]
end

#eql?(other) ⇒ Boolean Also known as: ==

Returns:

  • (Boolean)


110
111
112
# File 'lib/postsvg/color.rb', line 110

def eql?(other)
  other.is_a?(Color) && other.red == red && other.green == green && other.blue == blue
end

#gray?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/postsvg/color.rb', line 74

def gray?
  red == green && green == blue
end

#gray_levelObject



78
79
80
# File 'lib/postsvg/color.rb', line 78

def gray_level
  (red + green + blue) / 765.0
end

#hashObject



106
107
108
# File 'lib/postsvg/color.rb', line 106

def hash
  [red, green, blue].hash
end

#to_ps_grayObject

Raises:

  • (ArgumentError)


94
95
96
97
98
# File 'lib/postsvg/color.rb', line 94

def to_ps_gray
  raise ArgumentError, "color is not gray" unless gray?

  FormatNumber.call(gray_level)
end

#to_ps_rgbObject



90
91
92
# File 'lib/postsvg/color.rb', line 90

def to_ps_rgb
  to_rgb_unit.map { |c| FormatNumber.call(c) }.join(" ")
end

#to_rgb_unitObject



82
83
84
# File 'lib/postsvg/color.rb', line 82

def to_rgb_unit
  [red / 255.0, green / 255.0, blue / 255.0]
end

#to_svgObject



86
87
88
# File 'lib/postsvg/color.rb', line 86

def to_svg
  format("#%02x%02x%02x", red, green, blue)
end