lib/chunky_png/datastream.rb in chunky_png-0.0.5 vs lib/chunky_png/datastream.rb in chunky_png-0.5.0
- old
+ new
@@ -1,71 +1,169 @@
module ChunkyPNG
-
+
+ # The Datastream class represents a PNG formatted datastream. It supports
+ # both reading from and writing to strings, stremas and files.
+ #
+ # A PNG datastream begins with the PNG signature, and than contains multiple
+ # chunks, starting with a header (IHDR) chunk and finishing with an end
+ # (IEND) chunk.
+ #
+ # @see ChunkyPNG::Chunk
class Datastream
-
+
+ # The signature that each PNG file or stream should begin with.
SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10].pack('C8')
-
+
+ # The header chunk of this datastream.
+ # @return [ChunkyPNG::Chunk::Header]
attr_accessor :header_chunk
+
+ # All other chunks in this PNG file.
+ # @return [Array<ChunkyPNG::Chunk::Generic>]
attr_accessor :other_chunks
+
+ # The chunk containing the image's palette.
+ # @return [ChunkyPNG::Chunk::Palette]
attr_accessor :palette_chunk
+
+ # The chunk containing the transparency information of the palette.
+ # @return [ChunkyPNG::Chunk::Transparency]
attr_accessor :transparency_chunk
+
+ # The chunks that together compose the images pixel data.
+ # @return [Array<ChunkyPNG::Chunk::ImageData>]
attr_accessor :data_chunks
+
+ # The empty chunk that signals the end of this datastream
+ # @return [ChunkyPNG::Chunk::Header]
attr_accessor :end_chunk
- def self.read(io)
- verify_signature!(io)
- ds = self.new
- until io.eof?
- chunk = ChunkyPNG::Chunk.read(io)
- case chunk
- when ChunkyPNG::Chunk::Header then ds.header_chunk = chunk
- when ChunkyPNG::Chunk::Palette then ds.palette_chunk = chunk
- when ChunkyPNG::Chunk::Transparency then ds.transparency_chunk = chunk
- when ChunkyPNG::Chunk::ImageData then ds.data_chunks << chunk
- when ChunkyPNG::Chunk::End then ds.end_chunk = chunk
- else ds.other_chunks << chunk
+ # Initializes a new Datastream instance.
+ def initialize
+ @other_chunks = []
+ @data_chunks = []
+ end
+
+ #############################################
+ # LOADING DATASTREAMS
+ #############################################
+
+ class << self
+
+ # Reads a PNG datastream from a string.
+ # @param [String] str The PNG encoded string to load from.
+ # @return [ChunkyPNG::Datastream] The loaded datastream instance.
+ def from_blob(str)
+ from_io(StringIO.new(str))
+ end
+
+ alias :from_string :from_blob
+
+ # Reads a PNG datastream from a file.
+ # @param [String] filename The path of the file to load from.
+ # @return [ChunkyPNG::Datastream] The loaded datastream instance.
+ def from_file(filename)
+ ds = nil
+ File.open(filename, 'rb') { |f| ds = from_io(f) }
+ ds
+ end
+
+ # Reads a PNG datastream from an input stream
+ # @param [IO] io The stream to read from.
+ # @return [ChunkyPNG::Datastream] The loaded datastream instance.
+ def from_io(io)
+ verify_signature!(io)
+
+ ds = self.new
+ until io.eof?
+ chunk = ChunkyPNG::Chunk.read(io)
+ case chunk
+ when ChunkyPNG::Chunk::Header then ds.header_chunk = chunk
+ when ChunkyPNG::Chunk::Palette then ds.palette_chunk = chunk
+ when ChunkyPNG::Chunk::Transparency then ds.transparency_chunk = chunk
+ when ChunkyPNG::Chunk::ImageData then ds.data_chunks << chunk
+ when ChunkyPNG::Chunk::End then ds.end_chunk = chunk
+ else ds.other_chunks << chunk
+ end
end
+ return ds
end
- return ds
+
+ # Verifies that the current stream is a PNG datastream by checking its signature.
+ #
+ # This method reads the PNG signature from the stream, setting the current position
+ # of the stream directly after the signature, where the IHDR chunk should begin.
+ #
+ # @raise [RuntimeError] An exception is raised if the PNG signature is not found at
+ # the beginning of the stream.
+ def verify_signature!(io)
+ signature = io.read(ChunkyPNG::Datastream::SIGNATURE.length)
+ raise "PNG signature not found!" unless signature == ChunkyPNG::Datastream::SIGNATURE
+ end
end
- def self.verify_signature!(io)
- signature = io.read(ChunkyPNG::Datastream::SIGNATURE.length)
- raise "PNG signature not found!" unless signature == ChunkyPNG::Datastream::SIGNATURE
+ #############################################
+ # CHUNKS
+ #############################################
+
+ # Enumerates the chunks in this datastream.
+ #
+ # This will iterate over the chunks using the order in which the chunks
+ # should appear in the PNG file.
+ #
+ # @yield [ChunkyPNG::Chunk::Base] The chunks in this datastrean, one by one.
+ # @see ChunkyPNG::Datastream#chunks
+ def each_chunk
+ yield(header_chunk)
+ other_chunks.each { |chunk| yield(chunk) }
+ yield(palette_chunk) if palette_chunk
+ yield(transparency_chunk) if transparency_chunk
+ data_chunks.each { |chunk| yield(chunk) }
+ yield(end_chunk)
end
-
+
+ # Returns an enumerator instance for this datastream's chunks.
+ # @return [Enumerable::Enumerator] An enumerator for the :each_chunk method.
+ # @see ChunkyPNG::Datastream#each_chunk
def chunks
- cs = [header_chunk]
- cs += other_chunks
- cs << palette_chunk if palette_chunk
- cs << transparency_chunk if transparency_chunk
- cs += data_chunks
- cs << end_chunk
- return cs
+ enum_for(:each_chunk)
end
- def initialize
- @other_chunks = []
- @data_chunks = []
+ # Returns all the textual metadata key/value pairs as hash.
+ def metadata
+ metadata = {}
+ other_chunks.select do |chunk|
+ metadata[chunk.keyword] = chunk.value if chunk.respond_to?(:keyword)
+ end
+ metadata
end
-
+
+ #############################################
+ # WRITING DATASTREAMS
+ #############################################
+
+ # Writes the datastream to the given output stream.
+ # @param [IO] io The output stream to write to.
def write(io)
io << SIGNATURE
- chunks.each { |c| c.write(io) }
+ each_chunk { |c| c.write(io) }
end
-
- def idat_chunks(data)
- streamdata = Zlib::Deflate.deflate(data)
- # TODO: Split long streamdata over multiple chunks
- return [ ChunkyPNG::Chunk::ImageData.new('IDAT', streamdata) ]
+
+ # Saves this datastream as a PNG file.
+ # @param [String] filename The filename to use.
+ def save(filename)
+ File.open(filename, 'wb') { |f| write(f) }
end
-
- def pixel_matrix=(pixel_matrix)
- @pixel_matrix = pixel_matrix
+
+ # Encodes this datastream into a string.
+ # @return [String] The encoded PNG datastream.
+ def to_blob
+ str = StringIO.new
+ write(str)
+ return str.string
end
-
- def pixel_matrix
- @pixel_matrix ||= ChunkyPNG::PixelMatrix.decode(self)
- end
+
+ alias :to_string :to_blob
+ alias :to_s :to_blob
end
end