A class that can read an Adobe Color palette file (used for Photoshop swatches) and provide a Hash-like interface to the contents. Not all colour formats in ACO files are supported. Based largely off the information found by Larry Tesler.
Not all Adobe Color files have named colours; all named entries are returned as an array.
pal = Color::Palette::AdobeColor.from_file(my_aco_palette) pal[0] => Color::RGB<...> pal["white"] => [ Color::RGB<...> ] pal["unknown"] => [ Color::RGB<...>, Color::RGB<...>, ... ]
AdobeColor palettes are always indexable by insertion order (an integer key).
Version 2 palettes use UTF-16 colour names.
Contains the "lost" colours in the palette. These colours could not be properly loaded (e.g., L*a*b* is not supported by Color, so it is "lost") or are not understood by the algorithms.
Returns statistics about the nature of the colours loaded.
Create an AdobeColor palette object from the named file.
# File lib/color/palette/adobecolor.rb, line 37 def from_file(filename) File.open(filename, "rb") { |io| Color::Palette::AdobeColor.from_io(io) } end
Create an AdobeColor palette object from the provided IO.
# File lib/color/palette/adobecolor.rb, line 42 def from_io(io) Color::Palette::AdobeColor.new(io.read) end
Create a new AdobeColor palette from the palette file as a string.
# File lib/color/palette/adobecolor.rb, line 58 def initialize(palette) @colors = [] @names = {} @statistics = Hash.new(0) @lost = [] @order = [] @version = nil class << palette def readwords(count = 1) @offset ||= 0 raise IndexError if @offset >= self.size val = self[@offset, count * 2] raise IndexError if val.nil? or val.size < (count * 2) val = val.unpack("n" * count) @offset += count * 2 val end def readutf16(count = 1) @offset ||= 0 raise IndexError if @offset >= self.size val = self[@offset, count * 2] raise IndexError if val.nil? or val.size < (count * 2) @offset += count * 2 val end end @version, count = palette.readwords 2 raise "Unknown AdobeColor palette version #@version." unless @version.between?(1, 2) count.times do space, w, x, y, z = palette.readwords 5 name = nil if @version == 2 raise IndexError unless palette.readwords == [ 0 ] len = palette.readwords name = palette.readutf16(len[0] - 1) raise IndexError unless palette.readwords == [ 0 ] end color = case space when 0 then # RGB @statistics[:rgb] += 1 Color::RGB.new(w / 256, x / 256, y / 256) when 1 then # HS[BV] -- Convert to RGB @statistics[:hsb] += 1 h = w / 65535.0 s = x / 65535.0 v = y / 65535.0 if defined?(Color::HSB) Color::HSB.from_fraction(h, s, v) else @statistics[:converted] += 1 if Color.near_zero_or_less?(s) Color::RGB.from_fraction(v, v, v) else if Color.near_one_or_more?(h) vh = 0 else vh = h * 6.0 end vi = vh.floor v1 = v.to_f * (1 - s.to_f) v2 = v.to_f * (1 - s.to_f * (vh - vi)) v3 = v.to_f * (1 - s.to_f * (1 - (vh - vi))) case vi when 0 then Color::RGB.from_fraction(v, v3, v1) when 1 then Color::RGB.from_fraction(v2, v, v1) when 2 then Color::RGB.from_fraction(v1, v, v3) when 3 then Color::RGB.from_fraction(v1, v2, v) when 4 then Color::RGB.from_fraction(v3, v1, v) else Color::RGB.from_fraction(v, v1, v2) end end end when 2 then # CMYK @statistics[:cmyk] += 1 Color::CMYK.from_percent(100 - (w / 655.35), 100 - (x / 655.35), 100 - (y / 655.35), 100 - (z / 655.35)) when 7 then # L*a*b* @statistics[:lab] += 1 l = [w, 10000].min / 100.0 a = [[-12800, UwToSw[x]].max, 12700].min / 100.0 b = [[-12800, UwToSw[x]].max, 12700].min / 100.0 if defined? Color::Lab Color::Lab.new(l, a, b) else [ space, w, x, y, z ] end when 8 then # Grayscale @statistics[:gray] += 1 g = [w, 10000].min / 100.0 Color::GrayScale.new(g) when 9 then # Wide CMYK @statistics[:wcmyk] += 1 c = [w, 10000].min / 100.0 m = [x, 10000].min / 100.0 y = [y, 10000].min / 100.0 k = [z, 10000].min / 100.0 Color::CMYK.from_percent(c, m, y, k) else @statistics[space] += 1 [ space, w, x, y, z ] end @order << [ color, name ] if color.kind_of? Array @lost << color else @colors << color if name @names[name] ||= [] @names[name] << color end end end end
If a Numeric key
is provided, the single colour value at that
position will be returned. If a String key
is provided, the
colour set (an array) for that colour name will be returned.
# File lib/color/palette/adobecolor.rb, line 200 def [](key) if key.kind_of?(Numeric) @colors[key] else @names[key] end end
Loops through each colour.
# File lib/color/palette/adobecolor.rb, line 209 def each @colors.each { |el| yield el } end
Loops through each named colour set.
# File lib/color/palette/adobecolor.rb, line 214 def each_name #:yields color_name, color_set:# @names.each { |color_name, color_set| yield color_name, color_set } end
# File lib/color/palette/adobecolor.rb, line 218 def size @colors.size end
Provides the colour or colours at the provided selectors.
# File lib/color/palette/adobecolor.rb, line 193 def values_at(*selectors) @colors.values_at(*selectors) end