lib/chunky_png/chunk.rb in chunky_png-1.3.0 vs lib/chunky_png/chunk.rb in chunky_png-1.3.1
- old
+ new
@@ -1,41 +1,38 @@
module ChunkyPNG
-
# A PNG datastream consists of multiple chunks. This module, and the classes
- # contained within, help with handling these chunks. It supports both
- # reading and writing chunks.
+ # contained within, help with handling these chunks. It supports both reading
+ # and writing chunks.
#
# All chunk types are instances of the {ChunkyPNG::Chunk::Base} class. For
# some chunk types a specialized class is available, e.g. the IHDR chunk is
# represented by the {ChunkyPNG::Chunk::Header} class. These specialized
# classes help accessing the content of the chunk. All other chunks are
# represented by the {ChunkyPNG::Chunk::Generic} class.
#
# @see ChunkyPNG::Datastream
module Chunk
-
# Reads a chunk from an IO stream.
#
- # @param [IO, #read] io The IO stream to read from.
+ # @param io [IO, #read] The IO stream to read from.
# @return [ChunkyPNG::Chung::Base] The loaded chunk instance.
def self.read(io)
-
length, type = io.read(8).unpack('Na4')
content = io.read(length)
crc = io.read(4).unpack('N').first
verify_crc!(type, content, crc)
CHUNK_TYPES.fetch(type, Generic).read(type, content)
end
-
+
# Verifies the CRC of a chunk.
- # @param [String] type The chunk's type.
- # @param [String] content The chunk's content.
- # @param [Integer] content The chunk's content.
- # @raise [RuntimeError] An exception is raised if the found CRC value
- # is not equal to the expected CRC value.
+ # @param type [String] The chunk's type.
+ # @param content [String] The chunk's content.
+ # @param found_crc [Integer] The chunk's found CRC value.
+ # @raise [RuntimeError] An exception is raised if the found CRC value is
+ # not equal to the expected CRC value.
def self.verify_crc!(type, content, found_crc)
expected_crc = Zlib.crc32(content, Zlib.crc32(type))
raise ChunkyPNG::CRCMismatch, "Chuck CRC mismatch!" if found_crc != expected_crc
end
@@ -45,118 +42,123 @@
# A subclass should implement the +content+ method, which gets called when
# the chunk gets written to a PNG datastream
#
# @abstract
class Base
-
# The four-character type indicator for the chunk. This field is used to
# find the correct class for a chunk when it is loaded from a PNG stream.
# @return [String]
attr_accessor :type
# Initializes the chunk instance.
- # @param [String] type The four character chunk type indicator.
- # @param [Hash] attributes A hash of attributes to set on this chunk.
+ # @param type [String] The four character chunk type indicator.
+ # @param attributes [Hash] A hash of attributes to set on this chunk.
def initialize(type, attributes = {})
self.type = type
attributes.each { |k, v| send("#{k}=", v) }
end
# Writes the chunk to the IO stream, using the provided content.
# The checksum will be calculated and appended to the stream.
- # @param [IO] io The IO stream to write to.
- # @param [String] content The content for this chunk.
+ # @param io [IO] The IO stream to write to.
+ # @param content [String] The content for this chunk.
def write_with_crc(io, content)
io << [content.length].pack('N') << type << content
io << [Zlib.crc32(content, Zlib.crc32(type))].pack('N')
end
# Writes the chunk to the IO stream.
#
# It will call the +content+ method to get the content for this chunk,
# and will calculate and append the checksum automatically.
- # @param [IO] io The IO stream to write to.
+ # @param io [IO] The IO stream to write to.
def write(io)
write_with_crc(io, content || '')
end
end
- # The Generic chunk type will read the content from the chunk as it,
+ # The Generic chunk type will read the content from the chunk as it,
# and will write it back as it was read.
class Generic < Base
-
- # The attribute to store the content from the chunk, which gets
+ # The attribute to store the content from the chunk, which gets
# written by the +write+ method.
attr_accessor :content
-
def initialize(type, content = '')
super(type, :content => content)
end
# Creates an instance, given the chunk's type and content.
- # @param [String] type The four character chunk type indicator.
- # @param [String] content The content read from the chunk.
+ # @param type [String] The four character chunk type indicator.
+ # @param content [String] The content read from the chunk.
# @return [ChunkyPNG::Chunk::Generic] The new chunk instance.
def self.read(type, content)
- self.new(type, content)
+ new(type, content)
end
end
- # The header (IHDR) chunk is the first chunk of every PNG image, and
- # contains information about the image: i.e. its width, height, color
+ # The header (IHDR) chunk is the first chunk of every PNG image, and
+ # contains information about the image: i.e. its width, height, color
# depth, color mode, compression method, filtering method and interlace
# method.
#
- # ChunkyPNG supports all values for these variables that are defined in
- # the PNG spec, except for color depth: Only 8-bit depth images are
- # supported. Note that it is still possible to access the chunk for such
- # an image, but ChunkyPNG will raise an exception if you try to access
- # the pixel data.
+ # ChunkyPNG supports all values for these variables that are defined in the
+ # PNG spec, except for color depth: Only 8-bit depth images are supported.
+ # Note that it is still possible to access the chunk for such an image, but
+ # ChunkyPNG will raise an exception if you try to access the pixel data.
class Header < Base
+ attr_accessor :width, :height, :depth, :color, :compression, :filtering,
+ :interlace
- attr_accessor :width, :height, :depth, :color, :compression, :filtering, :interlace
-
def initialize(attrs = {})
super('IHDR', attrs)
@depth ||= 8
@color ||= ChunkyPNG::COLOR_TRUECOLOR
@compression ||= ChunkyPNG::COMPRESSION_DEFAULT
@filtering ||= ChunkyPNG::FILTERING_DEFAULT
@interlace ||= ChunkyPNG::INTERLACING_NONE
end
- # Reads the 13 bytes of content from the header chunk to set the image attributes.
- # @param [String] type The four character chunk type indicator (= "IHDR").
- # @param [String] content The 13 bytes of content read from the chunk.
- # @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the
- # variables set to the values according to the content.
+ # Reads the 13 bytes of content from the header chunk to set the image
+ # attributes.
+ # @param type [String] The four character chunk type indicator (= "IHDR").
+ # @param content [String] The 13 bytes of content read from the chunk.
+ # @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the
+ # variables set to the values according to the content.
def self.read(type, content)
fields = content.unpack('NNC5')
- self.new(:width => fields[0], :height => fields[1], :depth => fields[2], :color => fields[3],
- :compression => fields[4], :filtering => fields[5], :interlace => fields[6])
+ new(:width => fields[0],
+ :height => fields[1],
+ :depth => fields[2],
+ :color => fields[3],
+ :compression => fields[4],
+ :filtering => fields[5],
+ :interlace => fields[6])
end
- # Returns the content for this chunk when it gets written to a file, by packing the
- # image information variables into the correct format.
+ # Returns the content for this chunk when it gets written to a file, by
+ # packing the image information variables into the correct format.
# @return [String] The 13-byte content for the header chunk.
def content
- [width, height, depth, color, compression, filtering, interlace].pack('NNC5')
+ [width, height, depth, color, compression, filtering, interlace].
+ pack('NNC5')
end
end
- # The End (IEND) chunk indicates the last chunk of a PNG stream. It does not
- # contain any data.
+ # The End (IEND) chunk indicates the last chunk of a PNG stream. It does
+ # not contain any data.
class End < Base
-
+
def initialize
super('IEND')
end
-
+
# Reads the END chunk. It will check if the content is empty.
- # @param [String] type The four character chunk type indicator (= "IEND").
- # @param [String] content The content read from the chunk. Should be empty.
+ # @param type [String] The four character chunk type indicator (=
+ # "IEND").
+ # @param content [String] The content read from the chunk. Should be
+ # empty.
# @return [ChunkyPNG::Chunk::End] The new End chunk instance.
# @raise [RuntimeError] Raises an exception if the content was not empty.
def self.read(type, content)
raise ExpectationFailed, 'The IEND chunk should be empty!' if content.bytesize > 0
self.new
@@ -171,84 +173,92 @@
# The Palette (PLTE) chunk contains the image's palette, i.e. the
# 8-bit RGB colors this image is using.
#
# @see ChunkyPNG::Chunk::Transparency
- # @see ChunkyPNG::Palette
+ # @see ChunkyPNG::Palette
class Palette < Generic
end
# A transparency (tRNS) chunk defines the transparency for an image.
#
- # * For indexed images, it contains the alpha channel for the colors defined in the Palette (PLTE) chunk.
- # * For grayscale images, it contains the grayscale teint that should be considered fully transparent.
- # * For truecolor images, it contains the color that should be considered fully transparent.
+ # * For indexed images, it contains the alpha channel for the colors
+ # defined in the Palette (PLTE) chunk.
+ # * For grayscale images, it contains the grayscale teint that should be
+ # considered fully transparent.
+ # * For truecolor images, it contains the color that should be considered
+ # fully transparent.
#
- # Images having a color mode that already includes an alpha channel, this chunk should not be included.
+ # Images having a color mode that already includes an alpha channel, this
+ # chunk should not be included.
#
# @see ChunkyPNG::Chunk::Palette
# @see ChunkyPNG::Palette
class Transparency < Generic
-
# Returns the alpha channel for the palette of an indexed image.
#
- # This method should only be used for images having color mode ChunkyPNG::COLOR_INDEXED (3).
+ # This method should only be used for images having color mode
+ # ChunkyPNG::COLOR_INDEXED (3).
#
- # @return [Array<Integer>] Returns an array of alpha channel values [0-255].
+ # @return [Array<Integer>] Returns an array of alpha channel values
+ # [0-255].
def palette_alpha_channel
content.unpack('C*')
end
-
+
# Returns the truecolor entry to be replaced by transparent pixels,
#
- # This method should only be used for images having color mode ChunkyPNG::COLOR_TRUECOLOR (2).
+ # This method should only be used for images having color mode
+ # ChunkyPNG::COLOR_TRUECOLOR (2).
#
# @return [Integer] The color to replace with fully transparent pixels.
def truecolor_entry(bit_depth)
- values = content.unpack('nnn').map { |c| ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", c) }
+ values = content.unpack('nnn').map do |c|
+ ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", c)
+ end
ChunkyPNG::Color.rgb(*values)
end
# Returns the grayscale entry to be replaced by transparent pixels.
#
- # This method should only be used for images having color mode ChunkyPNG::COLOR_GRAYSCALE (0).
+ # This method should only be used for images having color mode
+ # ChunkyPNG::COLOR_GRAYSCALE (0).
#
- # @return [Integer] The (grayscale) color to replace with fully transparent pixels.
+ # @return [Integer] The (grayscale) color to replace with fully
+ # transparent pixels.
def grayscale_entry(bit_depth)
value = ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", content.unpack('n')[0])
ChunkyPNG::Color.grayscale(value)
end
end
class ImageData < Generic
-
def self.read(type, content)
raise ExpectationFailed, 'The IDAT chunk should not be empty!' if content.bytesize == 0
super
end
-
+
def self.combine_chunks(data_chunks)
Zlib::Inflate.inflate(data_chunks.map { |c| c.content }.join(''))
end
-
+
def self.split_in_chunks(data, level = Zlib::DEFAULT_COMPRESSION, chunk_size = 2147483647)
streamdata = Zlib::Deflate.deflate(data, level)
# TODO: Split long streamdata over multiple chunks
[ ChunkyPNG::Chunk::ImageData.new('IDAT', streamdata) ]
end
end
- # The Text (tEXt) chunk contains keyword/value metadata about the PNG stream.
- # In this chunk, the value is stored uncompressed.
+ # The Text (tEXt) chunk contains keyword/value metadata about the PNG
+ # stream. In this chunk, the value is stored uncompressed.
#
- # The tEXt chunk only supports Latin-1 encoded textual data. If you need UTF-8
- # support, check out the InternationalText chunk type.
+ # The tEXt chunk only supports Latin-1 encoded textual data. If you need
+ # UTF-8 support, check out the InternationalText chunk type.
#
# @see ChunkyPNG::Chunk::CompressedText
# @see ChunkyPNG::Chunk::InternationalText
class Text < Base
-
attr_accessor :keyword, :value
def initialize(keyword, value)
super('tEXt')
@keyword, @value = keyword, value
@@ -257,27 +267,26 @@
def self.read(type, content)
keyword, value = content.unpack('Z*a*')
new(keyword, value)
end
- # Creates the content to write to the stream, by concatenating the keyword
- # with the value, joined by a null character.
+ # Creates the content to write to the stream, by concatenating the
+ # keyword with the value, joined by a null character.
#
# @return The content that should be written to the datastream.
def content
[keyword, value].pack('Z*a*')
end
end
- # The CompressedText (zTXt) chunk contains keyword/value metadata about
- # the PNG stream. In this chunk, the value is compressed using Deflate
+ # The CompressedText (zTXt) chunk contains keyword/value metadata about the
+ # PNG stream. In this chunk, the value is compressed using Deflate
# compression.
#
# @see ChunkyPNG::Chunk::CompressedText
# @see ChunkyPNG::Chunk::InternationalText
class CompressedText < Base
-
attr_accessor :keyword, :value
def initialize(keyword, value)
super('zTXt')
@keyword, @value = keyword, value
@@ -287,42 +296,51 @@
keyword, compression, value = content.unpack('Z*Ca*')
raise ChunkyPNG::NotSupported, "Compression method #{compression.inspect} not supported!" unless compression == ChunkyPNG::COMPRESSION_DEFAULT
new(keyword, Zlib::Inflate.inflate(value))
end
- # Creates the content to write to the stream, by concatenating the keyword
- # with the deflated value, joined by a null character.
+ # Creates the content to write to the stream, by concatenating the
+ # keyword with the deflated value, joined by a null character.
#
# @return The content that should be written to the datastream.
def content
- [keyword, ChunkyPNG::COMPRESSION_DEFAULT, Zlib::Deflate.deflate(value)].pack('Z*Ca*')
+ [keyword, ChunkyPNG::COMPRESSION_DEFAULT, Zlib::Deflate.deflate(value)].
+ pack('Z*Ca*')
end
end
- # The Text (iTXt) chunk contains keyword/value metadata about the PNG stream.
- # The metadata in this chunk can be encoded using UTF-8 characters. Moreover,
- # it is possible to define the language of the metadata, and give a translation
- # of the keyword name. Finally, it supports bot compressed and uncompressed
- # values.
- #
- # @todo This chunk is currently not implemented, but merely read and written
- # back intact.
+ # The Text (iTXt) chunk contains keyword/value metadata about the PNG
+ # stream.
#
+ # The metadata in this chunk can be encoded using UTF-8 characters.
+ # Moreover, it is possible to define the language of the metadata, and give
+ # a translation of the keyword name. Finally, it supports bot compressed
+ # and uncompressed values.
+ #
+ # @todo This chunk is currently not implemented, but merely read and
+ # written back intact.
+ #
# @see ChunkyPNG::Chunk::Text
# @see ChunkyPNG::Chunk::CompressedText
class InternationalText < Generic
-
# TODO
end
- # Maps chunk types to classes, based on the four byte chunk type indicator at the
- # beginning of a chunk.
+ # Maps chunk types to classes, based on the four byte chunk type indicator
+ # at the beginning of a chunk.
#
- # If a chunk type is not specified in this hash, the Generic chunk type will be used.
+ # If a chunk type is not specified in this hash, the Generic chunk type
+ # will be used.
#
# @see ChunkyPNG::Chunk.read
CHUNK_TYPES = {
- 'IHDR' => Header, 'IEND' => End, 'IDAT' => ImageData, 'PLTE' => Palette, 'tRNS' => Transparency,
- 'tEXt' => Text, 'zTXt' => CompressedText, 'iTXt' => InternationalText
+ 'IHDR' => Header,
+ 'IEND' => End,
+ 'IDAT' => ImageData,
+ 'PLTE' => Palette,
+ 'tRNS' => Transparency,
+ 'tEXt' => Text,
+ 'zTXt' => CompressedText,
+ 'iTXt' => InternationalText,
}
end
end