Class: ChunkyPNG::Palette

Inherits:
SortedSet
  • Object
show all
Defined in:
lib/chunky_png/palette.rb

Overview

A palette describes the set of colors that is being used for an image.

A PNG image can contain an explicit palette which defines the colors of that image, but can also use an implicit palette, e.g. all truecolor colors or all grayscale colors.

This palette supports decoding colors from a palette if an explicit palette is provided in a PNG datastream, and it supports encoding colors to an explicit palette (stores as PLTE & tRNS chunks in a PNG file).

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(enum, decoding_map = nil) ⇒ Palette

Builds a new palette given a set (Enumerable instance) of colors.



21
22
23
24
# File 'lib/chunky_png/palette.rb', line 21

def initialize(enum, decoding_map = nil)
  super(enum)
  @decoding_map = decoding_map if decoding_map
end

Class Method Details

.from_canvas(canvas) ⇒ ChunkyPNG::Palette

Builds a palette instance from a given canvas.



63
64
65
66
67
68
# File 'lib/chunky_png/palette.rb', line 63

def self.from_canvas(canvas)
  # Although we don't need to call .uniq.sort before initializing, because
  # Palette subclasses SortedSet, we get significantly better performance
  # by doing so.
  new(canvas.pixels.uniq.sort)
end

.from_chunks(palette_chunk, transparency_chunk = nil) ⇒ ChunkyPNG::Palette

Builds a palette instance from a PLTE chunk and optionally a tRNS chunk from a PNG datastream.

This method will cerate a palette that is suitable for decoding an image.

See Also:



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/chunky_png/palette.rb', line 37

def self.from_chunks(palette_chunk, transparency_chunk = nil)
  return nil if palette_chunk.nil?

  decoding_map = []
  index = 0

  palatte_bytes = palette_chunk.content.unpack('C*')
  if transparency_chunk
    alpha_channel = transparency_chunk.content.unpack('C*')
  else
    alpha_channel = []
  end

  index = 0
  palatte_bytes.each_slice(3) do |bytes|
    bytes << alpha_channel.fetch(index, ChunkyPNG::Color::MAX)
    decoding_map << ChunkyPNG::Color.rgba(*bytes)
    index += 1
  end

  new(decoding_map, decoding_map)
end

.from_pixels(pixels) ⇒ ChunkyPNG::Palette

Builds a palette instance from a given set of pixels.



74
75
76
# File 'lib/chunky_png/palette.rb', line 74

def self.from_pixels(pixels)
  new(pixels)
end

Instance Method Details

#[](index) ⇒ ChunkyPNG::Color

Returns a color, given the position in the original palette chunk.

See Also:



148
149
150
# File 'lib/chunky_png/palette.rb', line 148

def [](index)
  @decoding_map[index]
end

#best_color_settingsInteger

Determines the most suitable colormode for this palette.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/chunky_png/palette.rb', line 195

def best_color_settings
  if black_and_white?
    [ChunkyPNG::COLOR_GRAYSCALE, 1]
  elsif grayscale?
    if opaque?
      [ChunkyPNG::COLOR_GRAYSCALE, 8]
    else
      [ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 8]
    end
  elsif indexable?
    [ChunkyPNG::COLOR_INDEXED, determine_bit_depth]
  elsif opaque?
    [ChunkyPNG::COLOR_TRUECOLOR, 8]
  else
    [ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 8]
  end
end

#black_and_white?true, false

Check whether this palette only contains bacl and white.

See Also:

  • Color#grayscale??


104
105
106
# File 'lib/chunky_png/palette.rb', line 104

def black_and_white?
  entries == [ChunkyPNG::Color::BLACK, ChunkyPNG::Color::WHITE]
end

#can_decode?true, false

Checks whether this palette is suitable for decoding an image from a datastream.

This requires that the positions of the colors in the original palette chunk is known, which is stored as an array in the @decoding_map instance variable.



126
127
128
# File 'lib/chunky_png/palette.rb', line 126

def can_decode?
  !@decoding_map.nil?
end

#can_encode?true, false

Checks whether this palette is suitable for encoding an image from to datastream.

This requires that the position of the color in the future palette chunk is known, which is stored as a hash in the @encoding_map instance variable.



139
140
141
# File 'lib/chunky_png/palette.rb', line 139

def can_encode?
  !@encoding_map.nil?
end

#determine_bit_depthInteger

Determines the minimal bit depth required for an indexed image



216
217
218
219
220
221
222
223
224
# File 'lib/chunky_png/palette.rb', line 216

def determine_bit_depth
  case size
  when 1..2 then 1
  when 3..4 then 2
  when 5..16 then 4
  when 17..256 then 8
  else nil
  end
end

#grayscale?true, false

Check whether this palette only contains grayscale colors.

See Also:

  • Color#grayscale??


96
97
98
# File 'lib/chunky_png/palette.rb', line 96

def grayscale?
  all? { |color| Color.grayscale?(color) }
end

#index(color) ⇒ Integer

Returns the position of a color in the palette

See Also:



156
157
158
# File 'lib/chunky_png/palette.rb', line 156

def index(color)
  color.nil? ? 0 : @encoding_map[color]
end

#indexable?true, false

Checks whether the size of this palette is suitable for indexed storage.



81
82
83
# File 'lib/chunky_png/palette.rb', line 81

def indexable?
  size <= 256
end

#opaque?true, false

Check whether this palette only contains opaque colors.

See Also:



88
89
90
# File 'lib/chunky_png/palette.rb', line 88

def opaque?
  all? { |color| Color.opaque?(color) }
end

#opaque_paletteChunkyPNG::Palette

Returns a palette with all the opaque variants of the colors in this palette.

See Also:



113
114
115
# File 'lib/chunky_png/palette.rb', line 113

def opaque_palette
  self.class.new(map { |c| ChunkyPNG::Color.opaque!(c) })
end

#to_plte_chunkChunkyPNG::Chunk::Palette

Note:

A PLTE chunk should only be included if the image is encoded using index colors. After this chunk has been built, the palette becomes suitable for encoding an image.

Creates a PLTE chunk that corresponds with this palette to store the r, g, and b channels of all colors.

See Also:



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/chunky_png/palette.rb', line 180

def to_plte_chunk
  @encoding_map = {}
  colors        = []

  each_with_index do |color, index|
    @encoding_map[color] = index
    colors += ChunkyPNG::Color.to_truecolor_bytes(color)
  end

  ChunkyPNG::Chunk::Palette.new('PLTE', colors.pack('C*'))
end

#to_trns_chunkChunkyPNG::Chunk::Transparency

Creates a tRNS chunk that corresponds with this palette to store the alpha channel of all colors.

Note that this chunk can be left out of every color in the palette is opaque, and the image is encoded using indexed colors.



167
168
169
# File 'lib/chunky_png/palette.rb', line 167

def to_trns_chunk
  ChunkyPNG::Chunk::Transparency.new('tRNS', map { |c| ChunkyPNG::Color.a(c) }.pack('C*'))
end