lib/chunky_png/chunk.rb in chunky_png-0.5.3 vs lib/chunky_png/chunk.rb in chunky_png-0.5.4

- old
+ new

@@ -7,10 +7,12 @@ # All chunck 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. @@ -35,41 +37,83 @@ def self.verify_crc!(type, content, found_crc) expected_crc = Zlib.crc32(content, Zlib.crc32(type)) raise "Chuck CRC mismatch!" if found_crc != expected_crc end + # The base chunk class is the superclass for every chunk type. It contains + # methods to write the chunk to an output stream. + # + # 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. 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. 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 te +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. def write(io) write_with_crc(io, content || '') end end + # 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 + # 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. + # @return [ChunkyPNG::Chunk::Generic] The new chunk instance. def self.read(type, content) self.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 + # 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. class Header < Base attr_accessor :width, :height, :depth, :color, :compression, :filtering, :interlace def initialize(attrs = {}) @@ -79,39 +123,67 @@ @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. 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]) end + # 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') end end + # 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. + # @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 'The IEND chunk should be empty!' if content != '' self.new end + # Returns an empty string, because this chunk should always be empty. + # @return [""] An empty string. def content '' end end + # 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 class Palette < Generic end + # A transparency (tRNS) chunk contains the alpha channel for the colors + # defined in the Palette (PLTE) chunk + # + # @see ChunkyPNG::Chunk::Palette + # @see ChunkyPNG::Palette class Transparency < Generic end class ImageData < Generic @@ -124,10 +196,18 @@ # 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 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) @@ -138,40 +218,70 @@ 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. + # + # @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 + # compression. + # + # @see ChunkyPNG::Chunk::CompressedText + # @see ChunkyPNG::Chunk::InternationalText class CompressedText < Base attr_accessor :keyword, :value def initialize(keyword, value) - super('tEXt') + super('zTXt') @keyword, @value = keyword, value end def self.read(type, content) keyword, compression, value = content.unpack('Z*Ca*') raise "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. + # + # @return The content that should be written to the datastream. def content [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. + # + # @see ChunkyPNG::Chunk::Text + # @see ChunkyPNG::Chunk::CompressedText class InternationalText < Generic + # TODO end - # Maps chunk types to classes. - # If a chunk type is not given in this hash, a generic chunk type will be used. + # 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. + # + # @see ChunkyPNG::Chunk.read CHUNK_TYPES = { 'IHDR' => Header, 'IEND' => End, 'IDAT' => ImageData, 'PLTE' => Palette, 'tRNS' => Transparency, 'tEXt' => Text, 'zTXt' => CompressedText, 'iTXt' => InternationalText } end