pax_global_header00006660000000000000000000000064121255632300014511gustar00rootroot0000000000000052 comment=06c0bcc8c9370c58e16b898a7afdf59cb663029a chunky_png-chunky_png-1.2.8/000077500000000000000000000000001212556323000160315ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/.gitignore000066400000000000000000000001341212556323000200170ustar00rootroot00000000000000.DS_Store spec/resources/_*.png /pkg /tmp /doc /.yardoc /.bundle /_site Gemfile.lock test.* chunky_png-chunky_png-1.2.8/.travis.yml000066400000000000000000000003601212556323000201410ustar00rootroot00000000000000language: ruby script: bundle exec rake rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 - ruby-head - ree - rbx-18mode - rbx-19mode - jruby-18mode - jruby-19mode matrix: allow_failures: - rvm: rbx-19mode - rvm: ruby-head chunky_png-chunky_png-1.2.8/.yardopts000066400000000000000000000001561212556323000177010ustar00rootroot00000000000000--title "ChunkyPNG - The pure Ruby PNG library" lib/**/*.rb - BENCHMARKS.rdoc --no-private --hide-void-return chunky_png-chunky_png-1.2.8/BENCHMARKS.rdoc000066400000000000000000000023011212556323000203130ustar00rootroot00000000000000= ChunkyPNG benchmark suite This small benchmark suite is intended to test the speed of PNG decoding and encoding against different ruby interpreters. You can set the number of runs by passing the N environment variable. You can execute them as rake task or as standalone script. rake benchmark:encoding rake benchmark:decoding rake benchmark N=10 # Run all of them, using n=10 Use rvm to simply run the tests against different interpreters. Of course, make sure that the chunky_png is installed for all your interpreters. rvm 1.8.7,1.9.3,rbx rake benchmark N=10 == Results Some of my benchmark (N=50) result on my 2007 iMac can be found in this gist: http://gist.github.com/495323 == Why is this relevant? ChunkyPNG is a pure Ruby library to handle PNG files. Decoding a PNG requires a lot of integer math and bitwise operations, and moderate use of the unpack method to read binary data. Encoding is a good test for Array#pack, and depending on the encoding options, also requires a lot of calculations. Therefore, the library is a good benchmark candidate for these methods and algorithms. It has been used to improve the Array#pack and String#unpack methods in Rubinius. chunky_png-chunky_png-1.2.8/Gemfile000066400000000000000000000001251212556323000173220ustar00rootroot00000000000000source 'https://rubygems.org' gemspec platforms :jruby do gem 'jruby-openssl' end chunky_png-chunky_png-1.2.8/LICENSE000066400000000000000000000020521212556323000170350ustar00rootroot00000000000000Copyright (c) 2010-2013 Willem van Bergen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. chunky_png-chunky_png-1.2.8/README.rdoc000066400000000000000000000065641212556323000176520ustar00rootroot00000000000000= Chunky PNG This library can read and write PNG files. It is written in pure Ruby for maximum portability. Let me rephrase: it does NOT require RMagick or any other memory leaking image library. Source code:: http://github.com/wvanbergen/chunky_png/tree RDoc:: http://rdoc.info/gems/chunky_png/frames Wiki:: http://github.com/wvanbergen/chunky_png/wiki Issue tracker:: http://github.com/wvanbergen/chunky_png/issues == Features * Decodes any image that the PNG standard allows. This includes all standard color modes, all bit depths and all transparency, interlacing and filtering options. * Encodes images supports all color modes (true color, grayscale and indexed) and transparency for all these color modes. The best color mode will be chosen automatically, based on the amount of used colors. * R/W access to the image's pixels. * R/W access to all image metadata that is stored in chunks. * Memory efficient (uses a Fixnum, i.e. 4 or 8 bytes of memory per pixel, depending on the hardware) * Reasonably fast for Ruby standards, by only using integer math and a highly optimized saving routine. * Interoperability with RMagick if you really have to. Also, have a look at OilyPNG at http://github.com/wvanbergen/oily_png. OilyPNG is a mixin module that implements some of the ChunkyPNG algorithms in C, which provides a massive speed boost to encoding and decoding. == Usage require 'chunky_png' # Creating an image from scratch, save as an interlaced PNG png = ChunkyPNG::Image.new(16, 16, ChunkyPNG::Color::TRANSPARENT) png[1,1] = ChunkyPNG::Color.rgba(10, 20, 30, 128) png[2,1] = ChunkyPNG::Color('black @ 0.5') png.save('filename.png', :interlace => true) # Compose images using alpha blending. avatar = ChunkyPNG::Image.from_file('avatar.png') badge = ChunkyPNG::Image.from_file('no_ie_badge.png') avatar.compose!(badge, 10, 10) avatar.save('composited.png', :fast_rgba) # Force the fast saving routine. # Accessing metadata image = ChunkyPNG::Image.from_file('with_metadata.png') puts image.metadata['Title'] image.metadata['Author'] = 'Willem van Bergen' image.save('with_metadata.png') # Overwrite file # Low level access to PNG chunks png_stream = ChunkyPNG::Datastream.from_file('filename.png') png_stream.each_chunk { |chunk| p chunk.type } For more information, see the project wiki on http://github.com/wvanbergen/chunky_png/wiki or the RDOC documentation on http://rdoc.info/gems/chunky_png/frames == Security warning ChunkyPNG is vulnerable to decompression bombs, which means that ChunkyPNG is vulnerable to DOS attacks by running out of memory when loading a specifically crafted PNG file. Because of the pure-Ruby nature of the library it is very hard to fix this problem in the library itself. In order to safely deal with untrusted images, you should make sure to do the image processing using ChunkyPNG in a separate process, e.g. by using fork or a background processing library. == About The library is written by Willem van Bergen for Floorplanner.com, and released under the MIT license (see LICENSE). Please contact me for questions or remarks. Patches are greatly appreciated! Please check out the changelog on https://github.com/wvanbergen/chunky_png/wiki/Changelog to see what changed in all versions. P.S.: The name of this library is intentionally similar to Chunky Bacon and Chunky GIF. Use Google if you want to know _why. :-) chunky_png-chunky_png-1.2.8/Rakefile000066400000000000000000000001501212556323000174720ustar00rootroot00000000000000Dir['tasks/*.rake'].each { |file| load(file) } GithubGem::RakeTasks.new(:gem) task :default => [:spec] chunky_png-chunky_png-1.2.8/benchmarks/000077500000000000000000000000001212556323000201465ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/benchmarks/decoding_benchmark.rb000066400000000000000000000025231212556323000242630ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'benchmark' require 'chunky_png' def image_file(name) File.join(File.dirname(__FILE__), '..', 'spec', 'resources', name) end def image_data(name) data = nil File.open(image_file(name), 'rb') { |f| data = f.read } data end no_filtering_stream = image_data('pixelstream_fast_rgba.png') up_filtering_stream = image_data('pixelstream_reference.png') paeth_filtering_stream = image_data('pixelstream_best_compression.png') rgba_pixelstream = image_data('pixelstream.rgba') rgb_pixelstream = image_data('pixelstream.rgb') n = (ENV['N'] || '5').to_i puts "---------------------------------------------" puts "ChunkyPNG (#{ChunkyPNG::VERSION}) decoding benchmark (n=#{n})" puts "---------------------------------------------" puts Benchmark.bmbm do |x| x.report('PNG - no filtering') { n.times { ChunkyPNG::Image.from_blob(no_filtering_stream) } } x.report('PNG - UP filtering') { n.times { ChunkyPNG::Image.from_blob(up_filtering_stream) } } x.report('PNG - PAETH filtering') { n.times { ChunkyPNG::Image.from_blob(paeth_filtering_stream) } } x.report('From RGBA pixelstream') { n.times { ChunkyPNG::Image.from_rgba_stream(240, 180, rgba_pixelstream) } } x.report('From RGB pixelstream') { n.times { ChunkyPNG::Image.from_rgb_stream(240, 180, rgb_pixelstream) } } end chunky_png-chunky_png-1.2.8/benchmarks/encoding_benchmark.rb000066400000000000000000000031221212556323000242710ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'benchmark' require 'chunky_png' image = ChunkyPNG::Image.new(240, 180, ChunkyPNG::Color::TRANSPARENT) # set some random pixels image[10, 20] = ChunkyPNG::Color.rgba(255, 0, 0, 255) image[50, 87] = ChunkyPNG::Color.rgba( 0, 255, 0, 255) image[33, 99] = ChunkyPNG::Color.rgba( 0, 0, 255, 255) n = (ENV['N'] || '5').to_i puts "---------------------------------------------" puts "ChunkyPNG (#{ChunkyPNG::VERSION}) encoding benchmark (n=#{n})" puts "---------------------------------------------" puts Benchmark.bmbm do |x| x.report('Autodetect (indexed)') { n.times { image.to_blob } } # Presets x.report(':no_compression') { n.times { image.to_blob(:no_compression) } } x.report(':fast_rgba') { n.times { image.to_blob(:fast_rgba) } } x.report(':fast_rgb') { n.times { image.to_blob(:fast_rgb) } } x.report(':good_compression') { n.times { image.to_blob(:good_compression) } } x.report(':best_compression') { n.times { image.to_blob(:best_compression) } } # Some options x.report(':rgb') { n.times { image.to_blob(:color_mode => ChunkyPNG::COLOR_TRUECOLOR) } } x.report(':rgba') { n.times { image.to_blob(:color_mode => ChunkyPNG::COLOR_TRUECOLOR_ALPHA) } } x.report(':indexed') { n.times { image.to_blob(:color_mode => ChunkyPNG::COLOR_INDEXED) } } x.report(':interlaced') { n.times { image.to_blob(:interlaced => true) } } # Exports x.report('to RGBA pixelstream') { n.times { image.to_rgba_stream } } x.report('to RGB pixelstream') { n.times { image.to_rgb_stream } } end chunky_png-chunky_png-1.2.8/benchmarks/filesize_benchmark.rb000066400000000000000000000021331212556323000243160ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'benchmark' require 'chunky_png' files = ['pixelstream_reference.png', 'operations.png', 'clock_stubbed.png'] def encode_png(image, constraints = {}) filesize = nil time = Benchmark.realtime { filesize = image.to_blob(constraints).bytesize } [filesize, time] end files.each do |file| filename = File.join(File.dirname(__FILE__), '..', 'spec', 'resources', file) image = ChunkyPNG::Canvas.from_file(filename) puts "#{file}: #{image.width}x#{image.height} - #{image.palette.size} colors" puts "------------------------------------------------" puts " : %8d bytes in %0.4fs" % encode_png(image) puts ":no_compression : %8d bytes in %0.4fs" % encode_png(image, :no_compression) puts ":fast_rgba : %8d bytes in %0.4fs" % encode_png(image, :fast_rgba) puts ":fast_rgb : %8d bytes in %0.4fs" % encode_png(image, :fast_rgb) puts ":good_compression : %8d bytes in %0.4fs" % encode_png(image, :good_compression) puts ":best_compression : %8d bytes in %0.4fs" % encode_png(image, :best_compression) puts end chunky_png-chunky_png-1.2.8/chunky_png.gemspec000066400000000000000000000333301212556323000215450ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = 'chunky_png' # Do not change the version and date fields by hand. This will be done # automatically by the gem release script. s.version = "1.2.8" s.date = "2013-03-30" s.summary = "Pure ruby library for read/write, chunk-level access to PNG files" s.description = <<-EOT This pure Ruby library can read and write PNG images without depending on an external image library, like RMagick. It tries to be memory efficient and reasonably fast. It supports reading and writing all PNG variants that are defined in the specification, with one limitation: only 8-bit color depth is supported. It supports all transparency, interlacing and filtering options the PNG specifications allows. It can also read and write textual metadata from PNG files. Low-level read/write access to PNG chunks is also possible. This library supports simple drawing on the image canvas and simple operations like alpha composition and cropping. Finally, it can import from and export to RMagick for interoperability. Also, have a look at OilyPNG at http://github.com/wvanbergen/oily_png. OilyPNG is a drop in mixin module that implements some of the ChunkyPNG algorithms in C, which provides a massive speed boost to encoding and decoding. EOT s.authors = ['Willem van Bergen'] s.email = ['willem@railsdoctors.com'] s.homepage = 'http://wiki.github.com/wvanbergen/chunky_png' s.add_development_dependency('rake') s.add_development_dependency('rspec', '~> 2.2') s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source' s.extra_rdoc_files = ['README.rdoc', 'BENCHMARKS.rdoc'] # Do not change the files and test_files fields by hand. This will be done # automatically by the gem release script. s.files = %w(.gitignore .travis.yml .yardopts BENCHMARKS.rdoc Gemfile LICENSE README.rdoc Rakefile benchmarks/decoding_benchmark.rb benchmarks/encoding_benchmark.rb benchmarks/filesize_benchmark.rb chunky_png.gemspec lib/chunky_png.rb lib/chunky_png/canvas.rb lib/chunky_png/canvas/adam7_interlacing.rb lib/chunky_png/canvas/data_url_exporting.rb lib/chunky_png/canvas/data_url_importing.rb lib/chunky_png/canvas/drawing.rb lib/chunky_png/canvas/masking.rb lib/chunky_png/canvas/operations.rb lib/chunky_png/canvas/png_decoding.rb lib/chunky_png/canvas/png_encoding.rb lib/chunky_png/canvas/resampling.rb lib/chunky_png/canvas/stream_exporting.rb lib/chunky_png/canvas/stream_importing.rb lib/chunky_png/chunk.rb lib/chunky_png/color.rb lib/chunky_png/compatibility.rb lib/chunky_png/datastream.rb lib/chunky_png/dimension.rb lib/chunky_png/image.rb lib/chunky_png/palette.rb lib/chunky_png/point.rb lib/chunky_png/rmagick.rb lib/chunky_png/vector.rb spec/chunky_png/canvas/adam7_interlacing_spec.rb spec/chunky_png/canvas/data_url_exporting_spec.rb spec/chunky_png/canvas/data_url_importing_spec.rb spec/chunky_png/canvas/drawing_spec.rb spec/chunky_png/canvas/masking_spec.rb spec/chunky_png/canvas/operations_spec.rb spec/chunky_png/canvas/png_decoding_spec.rb spec/chunky_png/canvas/png_encoding_spec.rb spec/chunky_png/canvas/resampling_spec.rb spec/chunky_png/canvas/stream_exporting_spec.rb spec/chunky_png/canvas/stream_importing_spec.rb spec/chunky_png/canvas_spec.rb spec/chunky_png/color_spec.rb spec/chunky_png/datastream_spec.rb spec/chunky_png/dimension_spec.rb spec/chunky_png/image_spec.rb spec/chunky_png/point_spec.rb spec/chunky_png/rmagick_spec.rb spec/chunky_png/vector_spec.rb spec/chunky_png_spec.rb spec/png_suite/background_chunks/bgai4a08.png spec/png_suite/background_chunks/bgai4a16.png spec/png_suite/background_chunks/bgan6a08.png spec/png_suite/background_chunks/bgan6a16.png spec/png_suite/background_chunks/bgbn4a08.png spec/png_suite/background_chunks/bggn4a16.png spec/png_suite/background_chunks/bgwn6a08.png spec/png_suite/background_chunks/bgyn6a16.png spec/png_suite/basic/basi0g01.png spec/png_suite/basic/basi0g01.rgba spec/png_suite/basic/basi0g02.png spec/png_suite/basic/basi0g02.rgba spec/png_suite/basic/basi0g04.png spec/png_suite/basic/basi0g04.rgba spec/png_suite/basic/basi0g08.png spec/png_suite/basic/basi0g08.rgba spec/png_suite/basic/basi0g16.png spec/png_suite/basic/basi0g16.rgba spec/png_suite/basic/basi2c08.png spec/png_suite/basic/basi2c08.rgba spec/png_suite/basic/basi2c16.png spec/png_suite/basic/basi2c16.rgba spec/png_suite/basic/basi3p01.png spec/png_suite/basic/basi3p01.rgba spec/png_suite/basic/basi3p02.png spec/png_suite/basic/basi3p02.rgba spec/png_suite/basic/basi3p04.png spec/png_suite/basic/basi3p04.rgba spec/png_suite/basic/basi3p08.png spec/png_suite/basic/basi3p08.rgba spec/png_suite/basic/basi4a08.png spec/png_suite/basic/basi4a08.rgba spec/png_suite/basic/basi4a16.png spec/png_suite/basic/basi4a16.rgba spec/png_suite/basic/basi6a08.png spec/png_suite/basic/basi6a08.rgba spec/png_suite/basic/basi6a16.png spec/png_suite/basic/basi6a16.rgba spec/png_suite/basic/basn0g01.png spec/png_suite/basic/basn0g01.rgba spec/png_suite/basic/basn0g02.png spec/png_suite/basic/basn0g02.rgba spec/png_suite/basic/basn0g04.png spec/png_suite/basic/basn0g04.rgba spec/png_suite/basic/basn0g08.png spec/png_suite/basic/basn0g08.rgba spec/png_suite/basic/basn0g16.png spec/png_suite/basic/basn0g16.rgba spec/png_suite/basic/basn2c08.png spec/png_suite/basic/basn2c08.rgba spec/png_suite/basic/basn2c16.png spec/png_suite/basic/basn2c16.rgba spec/png_suite/basic/basn3p01.png spec/png_suite/basic/basn3p01.rgba spec/png_suite/basic/basn3p02.png spec/png_suite/basic/basn3p02.rgba spec/png_suite/basic/basn3p04.png spec/png_suite/basic/basn3p04.rgba spec/png_suite/basic/basn3p08.png spec/png_suite/basic/basn3p08.rgba spec/png_suite/basic/basn4a08.png spec/png_suite/basic/basn4a08.rgba spec/png_suite/basic/basn4a16.png spec/png_suite/basic/basn4a16.rgba spec/png_suite/basic/basn6a08.png spec/png_suite/basic/basn6a08.rgba spec/png_suite/basic/basn6a16.png spec/png_suite/basic/basn6a16.rgba spec/png_suite/broken/x00n0g01.png spec/png_suite/broken/xcrn0g04.png spec/png_suite/broken/xlfn0g04.png spec/png_suite/chunk_ordering/oi1n0g16.png spec/png_suite/chunk_ordering/oi1n2c16.png spec/png_suite/chunk_ordering/oi2n0g16.png spec/png_suite/chunk_ordering/oi2n2c16.png spec/png_suite/chunk_ordering/oi4n0g16.png spec/png_suite/chunk_ordering/oi4n2c16.png spec/png_suite/chunk_ordering/oi9n0g16.png spec/png_suite/chunk_ordering/oi9n2c16.png spec/png_suite/compression_levels/z00n2c08.png spec/png_suite/compression_levels/z03n2c08.png spec/png_suite/compression_levels/z06n2c08.png spec/png_suite/compression_levels/z09n2c08.png spec/png_suite/filtering/f00n0g08.png spec/png_suite/filtering/f00n0g08.rgba spec/png_suite/filtering/f00n0g08_reference.png spec/png_suite/filtering/f00n0g08_reference.rgba spec/png_suite/filtering/f00n2c08.png spec/png_suite/filtering/f00n2c08.rgba spec/png_suite/filtering/f00n2c08_reference.png spec/png_suite/filtering/f00n2c08_reference.rgba spec/png_suite/filtering/f01n0g08.png spec/png_suite/filtering/f01n0g08.rgba spec/png_suite/filtering/f01n0g08_reference.png spec/png_suite/filtering/f01n0g08_reference.rgba spec/png_suite/filtering/f01n2c08.png spec/png_suite/filtering/f01n2c08.rgba spec/png_suite/filtering/f01n2c08_reference.png spec/png_suite/filtering/f01n2c08_reference.rgba spec/png_suite/filtering/f02n0g08.png spec/png_suite/filtering/f02n0g08.rgba spec/png_suite/filtering/f02n0g08_reference.png spec/png_suite/filtering/f02n0g08_reference.rgba spec/png_suite/filtering/f02n2c08.png spec/png_suite/filtering/f02n2c08.rgba spec/png_suite/filtering/f02n2c08_reference.png spec/png_suite/filtering/f02n2c08_reference.rgba spec/png_suite/filtering/f03n0g08.png spec/png_suite/filtering/f03n0g08.rgba spec/png_suite/filtering/f03n0g08_reference.png spec/png_suite/filtering/f03n0g08_reference.rgba spec/png_suite/filtering/f03n2c08.png spec/png_suite/filtering/f03n2c08.rgba spec/png_suite/filtering/f03n2c08_reference.png spec/png_suite/filtering/f03n2c08_reference.rgba spec/png_suite/filtering/f04n0g08.png spec/png_suite/filtering/f04n0g08.rgba spec/png_suite/filtering/f04n0g08_reference.png spec/png_suite/filtering/f04n0g08_reference.rgba spec/png_suite/filtering/f04n2c08.png spec/png_suite/filtering/f04n2c08.rgba spec/png_suite/filtering/f04n2c08_reference.png spec/png_suite/filtering/f04n2c08_reference.rgba spec/png_suite/gamma/g03n0g16.png spec/png_suite/gamma/g03n2c08.png spec/png_suite/gamma/g03n3p04.png spec/png_suite/gamma/g04n0g16.png spec/png_suite/gamma/g04n2c08.png spec/png_suite/gamma/g04n3p04.png spec/png_suite/gamma/g05n0g16.png spec/png_suite/gamma/g05n2c08.png spec/png_suite/gamma/g05n3p04.png spec/png_suite/gamma/g07n0g16.png spec/png_suite/gamma/g07n2c08.png spec/png_suite/gamma/g07n3p04.png spec/png_suite/gamma/g10n0g16.png spec/png_suite/gamma/g10n2c08.png spec/png_suite/gamma/g10n3p04.png spec/png_suite/gamma/g25n0g16.png spec/png_suite/gamma/g25n2c08.png spec/png_suite/gamma/g25n3p04.png spec/png_suite/metadata/cm0n0g04.png spec/png_suite/metadata/cm7n0g04.png spec/png_suite/metadata/cm9n0g04.png spec/png_suite/other/ccwn2c08.png spec/png_suite/other/ccwn3p08.png spec/png_suite/other/cdfn2c08.png spec/png_suite/other/cdhn2c08.png spec/png_suite/other/cdsn2c08.png spec/png_suite/other/cdun2c08.png spec/png_suite/other/ch1n3p04.png spec/png_suite/other/ch2n3p08.png spec/png_suite/other/cs3n2c16.png spec/png_suite/other/cs3n3p08.png spec/png_suite/other/cs5n2c08.png spec/png_suite/other/cs5n3p08.png spec/png_suite/other/cs8n2c08.png spec/png_suite/other/cs8n3p08.png spec/png_suite/other/ct0n0g04.png spec/png_suite/other/ct1n0g04.png spec/png_suite/other/ctzn0g04.png spec/png_suite/other/pp0n2c16.png spec/png_suite/other/pp0n6a08.png spec/png_suite/other/ps1n0g08.png spec/png_suite/other/ps1n2c16.png spec/png_suite/other/ps2n0g08.png spec/png_suite/other/ps2n2c16.png spec/png_suite/sizes/s01i3p01.png spec/png_suite/sizes/s01n3p01.png spec/png_suite/sizes/s02i3p01.png spec/png_suite/sizes/s02n3p01.png spec/png_suite/sizes/s03i3p01.png spec/png_suite/sizes/s03n3p01.png spec/png_suite/sizes/s04i3p01.png spec/png_suite/sizes/s04n3p01.png spec/png_suite/sizes/s05i3p02.png spec/png_suite/sizes/s05n3p02.png spec/png_suite/sizes/s06i3p02.png spec/png_suite/sizes/s06n3p02.png spec/png_suite/sizes/s07i3p02.png spec/png_suite/sizes/s07n3p02.png spec/png_suite/sizes/s08i3p02.png spec/png_suite/sizes/s08n3p02.png spec/png_suite/sizes/s09i3p02.png spec/png_suite/sizes/s09n3p02.png spec/png_suite/sizes/s32i3p04.png spec/png_suite/sizes/s32n3p04.png spec/png_suite/sizes/s33i3p04.png spec/png_suite/sizes/s33n3p04.png spec/png_suite/sizes/s34i3p04.png spec/png_suite/sizes/s34n3p04.png spec/png_suite/sizes/s35i3p04.png spec/png_suite/sizes/s35n3p04.png spec/png_suite/sizes/s36i3p04.png spec/png_suite/sizes/s36n3p04.png spec/png_suite/sizes/s37i3p04.png spec/png_suite/sizes/s37n3p04.png spec/png_suite/sizes/s38i3p04.png spec/png_suite/sizes/s38n3p04.png spec/png_suite/sizes/s39i3p04.png spec/png_suite/sizes/s39n3p04.png spec/png_suite/sizes/s40i3p04.png spec/png_suite/sizes/s40n3p04.png spec/png_suite/transparency/tbbn1g04.png spec/png_suite/transparency/tbbn2c16.png spec/png_suite/transparency/tbbn3p08.png spec/png_suite/transparency/tbgn2c16.png spec/png_suite/transparency/tbgn3p08.png spec/png_suite/transparency/tbrn2c08.png spec/png_suite/transparency/tbwn1g16.png spec/png_suite/transparency/tbwn3p08.png spec/png_suite/transparency/tbyn3p08.png spec/png_suite/transparency/tp0n1g08.png spec/png_suite/transparency/tp0n2c08.png spec/png_suite/transparency/tp0n3p08.png spec/png_suite/transparency/tp1n3p08.png spec/png_suite_spec.rb spec/resources/adam7.png spec/resources/bezier_five_point.png spec/resources/bezier_four_point.png spec/resources/bezier_four_point_flipped.png spec/resources/bezier_four_point_s.png spec/resources/bezier_six_point.png spec/resources/bezier_three_point.png spec/resources/bezier_three_point_flipped.png spec/resources/circles.png spec/resources/clock.png spec/resources/clock_base.png spec/resources/clock_bl_xdown_ydown.png spec/resources/clock_bl_xdown_yup.png spec/resources/clock_bl_xup_yup.png spec/resources/clock_mask.png spec/resources/clock_mask_updated.png spec/resources/clock_nn_xdown_ydown.png spec/resources/clock_nn_xdown_yup.png spec/resources/clock_nn_xup_yup.png spec/resources/clock_updated.png spec/resources/composited.png spec/resources/cropped.png spec/resources/damaged_chunk.png spec/resources/damaged_signature.png spec/resources/lines.png spec/resources/operations.png spec/resources/operations_grayscale.png spec/resources/partial_circles.png spec/resources/pixelstream.rgb spec/resources/pixelstream.rgba spec/resources/pixelstream_best_compression.png spec/resources/pixelstream_fast_rgba.png spec/resources/pixelstream_reference.png spec/resources/polygon_filled_horizontal.png spec/resources/polygon_filled_vertical.png spec/resources/polygon_triangle_filled.png spec/resources/polygon_unfilled.png spec/resources/rect.png spec/resources/replaced.png spec/resources/text_chunk.png spec/resources/ztxt_chunk.png spec/spec_helper.rb tasks/benchmarks.rake tasks/github-gem.rake) s.test_files = %w(spec/chunky_png/canvas/adam7_interlacing_spec.rb spec/chunky_png/canvas/data_url_exporting_spec.rb spec/chunky_png/canvas/data_url_importing_spec.rb spec/chunky_png/canvas/drawing_spec.rb spec/chunky_png/canvas/masking_spec.rb spec/chunky_png/canvas/operations_spec.rb spec/chunky_png/canvas/png_decoding_spec.rb spec/chunky_png/canvas/png_encoding_spec.rb spec/chunky_png/canvas/resampling_spec.rb spec/chunky_png/canvas/stream_exporting_spec.rb spec/chunky_png/canvas/stream_importing_spec.rb spec/chunky_png/canvas_spec.rb spec/chunky_png/color_spec.rb spec/chunky_png/datastream_spec.rb spec/chunky_png/dimension_spec.rb spec/chunky_png/image_spec.rb spec/chunky_png/point_spec.rb spec/chunky_png/rmagick_spec.rb spec/chunky_png/vector_spec.rb spec/chunky_png_spec.rb spec/png_suite_spec.rb) end chunky_png-chunky_png-1.2.8/lib/000077500000000000000000000000001212556323000165775ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/lib/chunky_png.rb000066400000000000000000000117351212556323000213000ustar00rootroot00000000000000# Basic requirements from standard library require 'set' require 'zlib' require 'stringio' require 'enumerator' # ChunkyPNG - the pure ruby library to access PNG files. # # The ChunkyPNG module defines some constants that are used in the # PNG specification, specifies some exception classes, and serves as # a namespace for all the other modules and classes in this library. # # {ChunkyPNG::Image}:: class to represent PNG images, including metadata. # {ChunkyPNG::Canvas}:: class to represent the image's canvas. # {ChunkyPNG::Color}:: module to work with color values. # {ChunkyPNG::Palette}:: represents the palette of colors used on a {ChunkyPNG::Canvas}. # {ChunkyPNG::Datastream}:: represents the internal structure of a PNG {ChunkyPNG::Image}. # {ChunkyPNG::Color}:: represents one chunk of data within a {ChunkyPNG::Datastream}. # {ChunkyPNG::Point}:: geometry helper class representing a 2-dimensional point. # {ChunkyPNG::Dimension}:: geometry helper class representing a dimension (i.e. width x height). # {ChunkyPNG::Vector}:: geometry helper class representing a series of points. # # @author Willem van Bergen module ChunkyPNG # The current version of ChunkyPNG. This value will be updated # automatically by them gem:release rake task. VERSION = "1.2.8" ################################################### # PNG international standard defined constants ################################################### # Indicates that the PNG image uses grayscale colors, i.e. only a # single teint channel. # @private COLOR_GRAYSCALE = 0 # Indicates that the PNG image uses true color, composed of a red # green and blue channel. # @private COLOR_TRUECOLOR = 2 # Indicates that the PNG image uses indexed colors, where the values # point to colors defined on a palette. # @private COLOR_INDEXED = 3 # Indicates that the PNG image uses grayscale colors with opacity, i.e. # a teint channel with an alpha channel. # @private COLOR_GRAYSCALE_ALPHA = 4 # Indicates that the PNG image uses true color with opacity, composed of # a red, green and blue channel, and an alpha value. # @private COLOR_TRUECOLOR_ALPHA = 6 # Indicates that the PNG specification's default compression # method is used (Zlib/Deflate) # @private COMPRESSION_DEFAULT = 0 # Indicates that the image does not use interlacing. # @private INTERLACING_NONE = 0 # Indicates that the image uses Adam7 interlacing. # @private INTERLACING_ADAM7 = 1 ### Filter method constants # Indicates that the PNG specification's default filtering are # being used in the image. # @private FILTERING_DEFAULT = 0 # Indicates that no filtering is used for the scanline. # @private FILTER_NONE = 0 # Indicates that SUB filtering is used for the scanline. # @private FILTER_SUB = 1 # Indicates that UP filtering is used for the scanline. # @private FILTER_UP = 2 # Indicates that AVERAGE filtering is used for the scanline. # @private FILTER_AVERAGE = 3 # Indicates that PAETH filtering is used for the scanline. # @private FILTER_PAETH = 4 ################################################### # ChunkyPNG exception classes ################################################### # Default exception class for ChunkyPNG class Exception < ::StandardError end # Exception that is raised for an unsupported PNG image. class NotSupported < ChunkyPNG::Exception end # Exception that is raised if the PNG signature is not encountered at the # beginning of the file. class SignatureMismatch < ChunkyPNG::Exception end # Exception that is raised if the CRC check for a block fails class CRCMismatch < ChunkyPNG::Exception end # Exception that is raised if an expectation fails. class ExpectationFailed < ChunkyPNG::Exception end # Exception that is raised if an expectation fails. class OutOfBounds < ChunkyPNG::ExpectationFailed end def self.force_binary(str) str.respond_to?(:force_encoding) ? str.force_encoding('BINARY') : str end # Empty byte array. This basically is an empty string, but with the encoding # set correctly to ASCII-8BIT (binary) in Ruby 1.9. # @return [String] An empty string, with encoding set to binary in Ruby 1.9 # @private EMPTY_BYTEARRAY = force_binary("").freeze # Null-byte, with the encoding set correctly to ASCII-8BIT (binary) in Ruby 1.9. # @return [String] A binary string, consisting of one NULL-byte. # @private EXTRA_BYTE = force_binary("\0").freeze end # Ruby 1.8 / 1.9 compatibility require 'chunky_png/compatibility' # PNG file structure require 'chunky_png/datastream' require 'chunky_png/chunk' # Colors require 'chunky_png/palette' require 'chunky_png/color' # Geometry require 'chunky_png/point' require 'chunky_png/vector' require 'chunky_png/dimension' # Canvas / Image classes require 'chunky_png/canvas' require 'chunky_png/image' chunky_png-chunky_png-1.2.8/lib/chunky_png/000077500000000000000000000000001212556323000207445ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas.rb000066400000000000000000000320761212556323000225540ustar00rootroot00000000000000require 'chunky_png/canvas/png_encoding' require 'chunky_png/canvas/png_decoding' require 'chunky_png/canvas/adam7_interlacing' require 'chunky_png/canvas/stream_exporting' require 'chunky_png/canvas/stream_importing' require 'chunky_png/canvas/data_url_exporting' require 'chunky_png/canvas/data_url_importing' require 'chunky_png/canvas/operations' require 'chunky_png/canvas/drawing' require 'chunky_png/canvas/resampling' require 'chunky_png/canvas/masking' module ChunkyPNG # The ChunkyPNG::Canvas class represents a raster image as a matrix of # pixels. # # This class supports loading a Canvas from a PNG datastream, and creating a # {ChunkyPNG::Datastream PNG datastream} based on this matrix. ChunkyPNG # only supports 8-bit color depth, otherwise all of the PNG format's # variations are supported for both reading and writing. # # This class offers per-pixel access to the matrix by using x,y coordinates. # It uses a palette (see {ChunkyPNG::Palette}) to keep track of the # different colors used in this matrix. # # The pixels in the canvas are stored as 4-byte fixnum, representing 32-bit # RGBA colors (8 bit per channel). The module {ChunkyPNG::Color} is provided # to work more easily with these number as color values. # # The module {ChunkyPNG::Canvas::Operations} is imported for operations on # the whole canvas, like cropping and alpha compositing. Simple drawing # functions are imported from the {ChunkyPNG::Canvas::Drawing} module. class Canvas include PNGEncoding extend PNGDecoding extend Adam7Interlacing include StreamExporting extend StreamImporting include DataUrlExporting extend DataUrlImporting include Operations include Drawing include Resampling include Masking # @return [Integer] The number of columns in this canvas attr_reader :width # @return [Integer] The number of rows in this canvas attr_reader :height # @return [Array] The list of pixels in this canvas. # This array always should have +width * height+ elements. attr_reader :pixels ################################################################# # CONSTRUCTORS ################################################################# # Initializes a new Canvas instance. # # @overload initialize(width, height, background_color) # @param [Integer] width The width in pixels of this canvas # @param [Integer] height The height in pixels of this canvas # @param [Integer, ...] background_color The initial background color of this canvas. # This can be a color value or any value that {ChunkyPNG::Color.parse} can handle. # # @overload initialize(width, height, initial) # @param [Integer] width The width in pixels of this canvas # @param [Integer] height The height in pixels of this canvas # @param [Array] initial The initial pizel values. Must be an array with # width * height elements. def initialize(width, height, initial = ChunkyPNG::Color::TRANSPARENT) @width, @height = width, height if initial.kind_of?(Array) raise ArgumentError, "The initial array should have #{width}x#{height} = #{width*height} elements!" unless initial.length == width * height @pixels = initial else @pixels = Array.new(width * height, ChunkyPNG::Color.parse(initial)) end end # Initializes a new Canvas instances when being cloned. # @param [ChunkyPNG::Canvas] other The canvas to duplicate # @return [void] # @private def initialize_copy(other) @width, @height = other.width, other.height @pixels = other.pixels.dup end # Creates a new canvas instance by duplicating another instance. # @param [ChunkyPNG::Canvas] canvas The canvas to duplicate # @return [ChunkyPNG::Canvas] The newly constructed canvas instance. def self.from_canvas(canvas) self.new(canvas.width, canvas.height, canvas.pixels.dup) end ################################################################# # PROPERTIES ################################################################# # Returns the dimension (width x height) for this canvas. # @return [ChunkyPNG::Dimension] A dimension instance with the width and height set for this canvas. def dimension ChunkyPNG::Dimension.new(width, height) end # Returns the area of this canvas in number of pixels. # @return [Integer] The number of pixels in this canvas def area pixels.length end # Replaces a single pixel in this canvas. # @param [Integer] x The x-coordinate of the pixel (column) # @param [Integer] y The y-coordinate of the pixel (row) # @param [Integer] color The new color for the provided coordinates. # @return [Integer] The new color value for this pixel, i.e. color. # @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the image's dimensions. # @see #set_pixel def []=(x, y, color) assert_xy!(x, y) @pixels[y * width + x] = ChunkyPNG::Color.parse(color) end # Replaces a single pixel in this canvas, without bounds checking. # # This method return value and effects are undefined for coordinates # out of bounds of the canvas. # # @param [Integer] x The x-coordinate of the pixel (column) # @param [Integer] y The y-coordinate of the pixel (row) # @param [Integer] pixel The new color for the provided coordinates. # @return [Integer] The new color value for this pixel, i.e. color. def set_pixel(x, y, color) @pixels[y * width + x] = color end # Replaces a single pixel in this canvas, with bounds checking. It will do # noting if the provided coordinates are out of bounds. # # @param [Integer] x The x-coordinate of the pixel (column) # @param [Integer] y The y-coordinate of the pixel (row) # @param [Integer] pixel The new color value for the provided coordinates. # @return [Integer] The new color value for this pixel, i.e. color, or # nil if the coordinates are out of bounds. def set_pixel_if_within_bounds(x, y, color) return unless include_xy?(x, y) @pixels[y * width + x] = color end # Returns a single pixel's color value from this canvas. # @param [Integer] x The x-coordinate of the pixel (column) # @param [Integer] y The y-coordinate of the pixel (row) # @return [Integer] The current color value at the provided coordinates. # @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the image's dimensions. # @see #get_pixel def [](x, y) assert_xy!(x, y) @pixels[y * width + x] end # Returns a single pixel from this canvas, without checking bounds. The return value for # this method is undefined if the coordinates are out of bounds. # @param (see #[]) # @return [Integer] The current pixel at the provided coordinates. def get_pixel(x, y) @pixels[y * width + x] end # Returns an extracted row as vector of pixels # @param [Integer] y The 0-based row index # @return [Array] The vector of pixels in the requested row def row(y) assert_y!(y) pixels.slice(y * width, width) end # Returns an extracted column as vector of pixels. # @param [Integer] x The 0-based column index. # @return [Array] The vector of pixels in the requested column. def column(x) assert_x!(x) (0...height).inject([]) { |pixels, y| pixels << get_pixel(x, y) } end # Replaces a row of pixels on this canvas. # @param [Integer] y The 0-based row index. # @param [Array] vector The vector of pixels to replace the row with. # @return [void] def replace_row!(y, vector) assert_y!(y) && assert_width!(vector.length) pixels[y * width, width] = vector end # Replaces a column of pixels on this canvas. # @param [Integer] x The 0-based column index. # @param [Array] vector The vector of pixels to replace the column with. # @return [void] def replace_column!(x, vector) assert_x!(x) && assert_height!(vector.length) for y in 0...height do set_pixel(x, y, vector[y]) end end # Checks whether the given coordinates are in the range of the canvas # @param [ChunkyPNG::Point, Array, Hash, String] point_like The point to check. # @return [true, false] True if the x and y coordinates of the point are # within the limits of this canvas. # @see ChunkyPNG.Point def include_point?(*point_like) dimension.include?(ChunkyPNG::Point(*point_like)) end alias_method :include?, :include_point? # Checks whether the given x- and y-coordinate are in the range of the canvas # @param [Integer] x The x-coordinate of the pixel (column) # @param [Integer] y The y-coordinate of the pixel (row) # @return [true, false] True if the x- and y-coordinate is in the range of this canvas. def include_xy?(x, y) y >= 0 && y < height && x >= 0 && x < width end # Checks whether the given y-coordinate is in the range of the canvas # @param [Integer] y The y-coordinate of the pixel (row) # @return [true, false] True if the y-coordinate is in the range of this canvas. def include_y?(y) y >= 0 && y < height end # Checks whether the given x-coordinate is in the range of the canvas # @param [Integer] x The y-coordinate of the pixel (column) # @return [true, false] True if the x-coordinate is in the range of this canvas. def include_x?(x) x >= 0 && x < width end # Returns the palette used for this canvas. # @return [ChunkyPNG::Palette] A palette which contains all the colors that are # being used for this image. def palette ChunkyPNG::Palette.from_canvas(self) end # Equality check to compare this canvas with other matrices. # @param other The object to compare this Matrix to. # @return [true, false] True if the size and pixel values of the other canvas # are exactly the same as this canvas's size and pixel values. def eql?(other) other.kind_of?(self.class) && other.pixels == self.pixels && other.width == self.width && other.height == self.height end alias :== :eql? ################################################################# # EXPORTING ################################################################# # Creates an ChunkyPNG::Image object from this canvas. # @return [ChunkyPNG::Image] This canvas wrapped in an Image instance. def to_image ChunkyPNG::Image.from_canvas(self) end # Alternative implementation of the inspect method. # @return [String] A nicely formatted string representation of this canvas. # @private def inspect inspected = "<#{self.class.name} #{width}x#{height} [" for y in 0...height inspected << "\n\t[" << row(y).map { |p| ChunkyPNG::Color.to_hex(p) }.join(' ') << ']' end inspected << "\n]>" end protected # Replaces the image, given a new width, new height, and a new pixel array. def replace_canvas!(new_width, new_height, new_pixels) raise ArgumentError, "The provided pixel array should have #{new_width * new_height} items" unless new_pixels.length == new_width * new_height @width, @height, @pixels = new_width, new_height, new_pixels return self end # Throws an exception if the x-coordinate is out of bounds. def assert_x!(x) raise ChunkyPNG::OutOfBounds, "Column index #{x} out of bounds!" unless include_x?(x) return true end # Throws an exception if the y-coordinate is out of bounds. def assert_y!(y) raise ChunkyPNG::OutOfBounds, "Row index #{y} out of bounds!" unless include_y?(y) return true end # Throws an exception if the x- or y-coordinate is out of bounds. def assert_xy!(x, y) raise ChunkyPNG::OutOfBounds, "Coordinates (#{x},#{y}) out of bounds!" unless include_xy?(x, y) return true end # Throws an exception if the vector_length does not match this canvas' height. def assert_height!(vector_length) raise ChunkyPNG::ExpectationFailed, "The length of the vector (#{vector_length}) does not match the canvas height (#{height})!" if height != vector_length return true end # Throws an exception if the vector_length does not match this canvas' width. def assert_width!(vector_length) raise ChunkyPNG::ExpectationFailed, "The length of the vector (#{vector_length}) does not match the canvas width (#{width})!" if width != vector_length return true end # Throws an exception if the matrix width and height does not match this canvas' dimensions. def assert_size!(matrix_width, matrix_height) raise ChunkyPNG::ExpectationFailed, "The width of the matrix does not match the canvas width!" if width != matrix_width raise ChunkyPNG::ExpectationFailed, "The height of the matrix does not match the canvas height!" if height != matrix_height return true end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/000077500000000000000000000000001212556323000222175ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/adam7_interlacing.rb000066400000000000000000000065161212556323000261240ustar00rootroot00000000000000module ChunkyPNG class Canvas # Methods for decoding and encoding Adam7 interlacing. # # Adam7 interlacing extracts 7 pass images out of a single image, that can be encoded to a # stream separately so the image can be built up progressively. The module is included into # ChunkyPNG canvas and is used to extract the pass images from the original image, or to # reconstruct an original image from separate pass images. module Adam7Interlacing # Returns an array with the x-shift, x-offset, y-shift and y-offset for the requested pass. # @param [Integer] pass The pass number, should be in 0..6. def adam7_multiplier_offset(pass) [3 - (pass >> 1), (pass & 1 == 0) ? 0 : 8 >> ((pass + 1) >> 1), pass == 0 ? 3 : 3 - ((pass - 1) >> 1), (pass == 0 || pass & 1 == 1) ? 0 : 8 >> (pass >> 1)] end # Returns the pixel dimensions of the requested pass. # @param [Integer] pass The pass number, should be in 0..6. # @param [Integer] original_width The width of the original image. # @param [Integer] original_height The height of the original image. def adam7_pass_size(pass, original_width, original_height) x_shift, x_offset, y_shift, y_offset = adam7_multiplier_offset(pass) [ (original_width - x_offset + (1 << x_shift) - 1) >> x_shift, (original_height - y_offset + (1 << y_shift) - 1) >> y_shift] end # Returns an array of the dimension of all the pass images. # @param [Integer] original_width The width of the original image. # @param [Integer] original_height The height of the original image. # @return [Array>] Returns an array with 7 pairs of dimensions. # @see #adam7_pass_size def adam7_pass_sizes(original_width, original_height) (0...7).map { |pass| adam7_pass_size(pass, original_width, original_height) } end # Merges a pass image into a total image that is being constructed. # @param [Integer] pass The pass number, should be in 0..6. # @param [ChunkyPNG::Canvas] canvas The image that is being constructed. # @param [ChunkyPNG::Canvas] subcanvas The pass image that should be merged def adam7_merge_pass(pass, canvas, subcanvas) x_shift, x_offset, y_shift, y_offset = adam7_multiplier_offset(pass) for y in 0...subcanvas.height do for x in 0...subcanvas.width do new_x = (x << x_shift) | x_offset new_y = (y << y_shift) | y_offset canvas[new_x, new_y] = subcanvas[x, y] end end end # Extracts a pass from a complete image # @param [Integer] pass The pass number, should be in 0..6. # @param [ChunkyPNG::Canvas] canvas The image that is being deconstructed. # @return [ChunkyPNG::Canvas] The extracted pass image. def adam7_extract_pass(pass, canvas) x_shift, x_offset, y_shift, y_offset = adam7_multiplier_offset(pass) sm_pixels = [] y_offset.step(canvas.height - 1, 1 << y_shift) do |y| x_offset.step(canvas.width - 1, 1 << x_shift) do |x| sm_pixels << canvas[x, y] end end new_canvas_args = adam7_pass_size(pass, canvas.width, canvas.height) + [sm_pixels] ChunkyPNG::Canvas.new(*new_canvas_args) end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/data_url_exporting.rb000066400000000000000000000006631212556323000264430ustar00rootroot00000000000000module ChunkyPNG class Canvas # Methods to export a canvas to a PNG data URL. module DataUrlExporting # Exports the canvas as a data url (e.g. data:image/png;base64,) that can # easily be used inline in CSS or HTML. # @return [String] The canvas formatted as a data URL string. def to_data_url ['data:image/png;base64,', to_blob].pack('A*m').gsub(/\n/, '') end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/data_url_importing.rb000066400000000000000000000014251212556323000264310ustar00rootroot00000000000000module ChunkyPNG class Canvas # Methods to import a canvas from a PNG data URL. module DataUrlImporting # Imports a canvas from a PNG data URL. # @param [String] string The data URL string to load from. # @return [Canvas] The imported canvas. # @raise ChunkyPNG::SignatureMismatch if the provides string is not a properly # formatted PNG data URL (i.e. it should start with "data:image/png;base64,") def from_data_url(string) if string =~ %r[^data:image/png;base64,((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$] from_blob($1.unpack('m').first) else raise SignatureMismatch, "The string was not a properly formatted data URL for a PNG image." end end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/drawing.rb000066400000000000000000000270511212556323000242040ustar00rootroot00000000000000module ChunkyPNG class Canvas # Module that adds some primitive drawing methods to {ChunkyPNG::Canvas}. # # All of these methods change the current canvas instance and do not create a new one, # even though the method names do not end with a bang. # # @note Drawing operations will not fail when something is drawn outside of the bounds # of the canvas; these pixels will simply be ignored. # @see ChunkyPNG::Canvas module Drawing # Composes a pixel on the canvas by alpha blending a color with its background color. # @param [Integer] x The x-coordinate of the pixel to blend. # @param [Integer] y The y-coordinate of the pixel to blend. # @param [Integer] color The foreground color to blend with # @return [Integer] The composed color. def compose_pixel(x, y, color) return unless include_xy?(x, y) compose_pixel_unsafe(x, y, ChunkyPNG::Color.parse(color)) end # Composes a pixel on the canvas by alpha blending a color with its background color, # without bounds checking. # @param (see #compose_pixel) # @return [Integer] The composed color. def compose_pixel_unsafe(x, y, color) set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y))) end # Draws a Bezier curve # @param [Array, Point] A collection of control points # @return [Chunky:PNG::Canvas] Itself, with the curve drawn def bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK) points = ChunkyPNG::Vector(*points) case points.length when 0, 1; return self when 2; return line(points[0].x, points[0].y, points[1].x, points[1].y, stroke_color) end curve_points = Array.new t = 0 n = points.length - 1 bicof = 0 while t <= 100 cur_p = ChunkyPNG::Point.new(0,0) # Generate a float of t. t_f = t / 100.00 cur_p.x += ((1 - t_f) ** n) * points[0].x cur_p.y += ((1 - t_f) ** n) * points[0].y for i in 1...points.length - 1 bicof = binomial_coefficient(n , i) cur_p.x += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].x cur_p.y += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].y i += 1 end cur_p.x += (t_f ** n) * points[n].x cur_p.y += (t_f ** n) * points[n].y curve_points << cur_p bicof = 0 t += 1 end curve_points.each_cons(2) do |p1, p2| line_xiaolin_wu(p1.x.round, p1.y.round, p2.x.round, p2.y.round, stroke_color) end return self end # Draws an anti-aliased line using Xiaolin Wu's algorithm. # # @param [Integer] x0 The x-coordinate of the first control point. # @param [Integer] y0 The y-coordinate of the first control point. # @param [Integer] x1 The x-coordinate of the second control point. # @param [Integer] y1 The y-coordinate of the second control point. # @param [Integer] stroke_color The color to use for this line. # @param [true, false] inclusive Whether to draw the last pixel. # Set to false when drawing multiple lines in a path. # @return [ChunkyPNG::Canvas] Itself, with the line drawn. def line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true) stroke_color = ChunkyPNG::Color.parse(stroke_color) dx = x1 - x0 sx = dx < 0 ? -1 : 1 dx *= sx dy = y1 - y0 sy = dy < 0 ? -1 : 1 dy *= sy if dy == 0 # vertical line x0.step(inclusive ? x1 : x1 - sx, sx) do |x| compose_pixel(x, y0, stroke_color) end elsif dx == 0 # horizontal line y0.step(inclusive ? y1 : y1 - sy, sy) do |y| compose_pixel(x0, y, stroke_color) end elsif dx == dy # diagonal x0.step(inclusive ? x1 : x1 - sx, sx) do |x| compose_pixel(x, y0, stroke_color) y0 += sy end elsif dy > dx # vertical displacement compose_pixel(x0, y0, stroke_color) e_acc = 0 e = ((dx << 16) / dy.to_f).round (dy - 1).downto(0) do |i| e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff x0 += sx if (e_acc <= e_acc_temp) w = 0xff - (e_acc >> 8) compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w)) compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) if inclusive || i > 0 y0 += sy end compose_pixel(x1, y1, stroke_color) if inclusive else # horizontal displacement compose_pixel(x0, y0, stroke_color) e_acc = 0 e = ((dy << 16) / dx.to_f).round (dx - 1).downto(0) do |i| e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff y0 += sy if (e_acc <= e_acc_temp) w = 0xff - (e_acc >> 8) compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w)) compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) if inclusive || i > 0 x0 += sx end compose_pixel(x1, y1, stroke_color) if inclusive end return self end alias_method :line, :line_xiaolin_wu # Draws a polygon on the canvas using the stroke_color, filled using the fill_color if any. # # @param [Array, String] The control point vector. Accepts everything {ChunkyPNG.Vector} accepts. # @param [Integer] stroke_color The stroke color to use for this polygon. # @param [Integer] fill_color The fill color to use for this polygon. # @return [ChunkyPNG::Canvas] Itself, with the polygon drawn. def polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) vector = ChunkyPNG::Vector(*path) raise ArgumentError, "A polygon requires at least 3 points" if path.length < 3 stroke_color = ChunkyPNG::Color.parse(stroke_color) fill_color = ChunkyPNG::Color.parse(fill_color) # Fill unless fill_color == ChunkyPNG::Color::TRANSPARENT vector.y_range.each do |y| intersections = [] vector.edges.each do |p1, p2| if (p1.y < y && p2.y >= y) || (p2.y < y && p1.y >= y) intersections << (p1.x + (y - p1.y).to_f / (p2.y - p1.y) * (p2.x - p1.x)).round end end intersections.sort! 0.step(intersections.length - 1, 2) do |i| intersections[i].upto(intersections[i + 1]) do |x| compose_pixel(x, y, fill_color) end end end end # Stroke vector.each_edge do |(from_x, from_y), (to_x, to_y)| line(from_x, from_y, to_x, to_y, stroke_color, false) end return self end # Draws a rectangle on the canvas, using two control points. # # @param [Integer] x0 The x-coordinate of the first control point. # @param [Integer] y0 The y-coordinate of the first control point. # @param [Integer] x1 The x-coordinate of the second control point. # @param [Integer] y1 The y-coordinate of the second control point. # @param [Integer] stroke_color The line color to use for this rectangle. # @param [Integer] fill_color The fill color to use for this rectangle. # @return [ChunkyPNG::Canvas] Itself, with the rectangle drawn. def rect(x0, y0, x1, y1, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) stroke_color = ChunkyPNG::Color.parse(stroke_color) fill_color = ChunkyPNG::Color.parse(fill_color) # Fill unless fill_color == ChunkyPNG::Color::TRANSPARENT [x0, x1].min.upto([x0, x1].max) do |x| [y0, y1].min.upto([y0, y1].max) do |y| compose_pixel(x, y, fill_color) end end end # Stroke line(x0, y0, x0, y1, stroke_color, false) line(x0, y1, x1, y1, stroke_color, false) line(x1, y1, x1, y0, stroke_color, false) line(x1, y0, x0, y0, stroke_color, false) return self end # Draws a circle on the canvas. # # @param [Integer] x0 The x-coordinate of the center of the circle. # @param [Integer] y0 The y-coordinate of the center of the circle. # @param [Integer] radius The radius of the circle from the center point. # @param [Integer] stroke_color The color to use for the line. # @param [Integer] fill_color The color to use that fills the circle. # @return [ChunkyPNG::Canvas] Itself, with the circle drawn. def circle(x0, y0, radius, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) stroke_color = ChunkyPNG::Color.parse(stroke_color) fill_color = ChunkyPNG::Color.parse(fill_color) f = 1 - radius ddF_x = 1 ddF_y = -2 * radius x = 0 y = radius compose_pixel(x0, y0 + radius, stroke_color) compose_pixel(x0, y0 - radius, stroke_color) compose_pixel(x0 + radius, y0, stroke_color) compose_pixel(x0 - radius, y0, stroke_color) lines = [radius - 1] unless fill_color == ChunkyPNG::Color::TRANSPARENT while x < y if f >= 0 y -= 1 ddF_y += 2 f += ddF_y end x += 1 ddF_x += 2 f += ddF_x unless fill_color == ChunkyPNG::Color::TRANSPARENT lines[y] = lines[y] ? [lines[y], x - 1].min : x - 1 lines[x] = lines[x] ? [lines[x], y - 1].min : y - 1 end compose_pixel(x0 + x, y0 + y, stroke_color) compose_pixel(x0 - x, y0 + y, stroke_color) compose_pixel(x0 + x, y0 - y, stroke_color) compose_pixel(x0 - x, y0 - y, stroke_color) unless x == y compose_pixel(x0 + y, y0 + x, stroke_color) compose_pixel(x0 - y, y0 + x, stroke_color) compose_pixel(x0 + y, y0 - x, stroke_color) compose_pixel(x0 - y, y0 - x, stroke_color) end end unless fill_color == ChunkyPNG::Color::TRANSPARENT lines.each_with_index do |length, y| line(x0 - length, y0 - y, x0 + length, y0 - y, fill_color) if length > 0 line(x0 - length, y0 + y, x0 + length, y0 + y, fill_color) if length > 0 && y > 0 end end return self end private # Calculates the binomial coefficient for n over k. # # @param [Integer] n first parameter in coeffient (the number on top when looking at the mathematic formula) # @param [Integer] k k-element, second parameter in coeffient (the number on the bottom when looking at the mathematic formula) # @return [Integer] The binomial coeffcient of (n,k) def binomial_coefficient(n, k) return 1 if n == k || k == 0 return n if k == 1 return -1 if n < k # calculate factorials fact_n = (2..n).inject(1) { |carry, i| carry * i } fact_k = (2..k).inject(1) { |carry, i| carry * i } fact_n_sub_k = (2..(n - k)).inject(1) { |carry, i| carry * i } fact_n / (fact_k * fact_n_sub_k) end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/masking.rb000066400000000000000000000113541212556323000242010ustar00rootroot00000000000000module ChunkyPNG class Canvas # The ChunkyPNG::Canvas::Masking module defines methods to perform masking # and theming operations on a {ChunkyPNG::Canvas}. The module is included into the Canvas class so all # these methods are available on every canvas. # # @see ChunkyPNG::Canvas module Masking # Creates a new image, based on the current image but with a new theme color. # # This method will replace one color in an image with another image. This is done by # first extracting the pixels with a color close to the original theme color as a mask # image, changing the color of this mask image and then apply it on the original image. # # Mask extraction works best when the theme colored pixels are clearly distinguishable # from a background color (preferably white). You can set a tolerance level to influence # the extraction process. # # @param [Integer] old_theme_color The original theme color in this image. # @param [Integer] new_theme_color The color to replace the old theme color with. # @param [Integer] bg_color The background color on which the theme colored pixels are placed. # @param [Integer] tolerance The tolerance level to use when extracting the mask image. Five is # the default; increase this if the masked image does not extract all the required pixels, # decrease it if too many pixels get extracted. # @return [ChunkyPNG::Canvas] Returns itself, but with the theme colored pixels changed. # @see #change_theme_color! # @see #change_mask_color! def change_theme_color!(old_theme_color, new_theme_color, bg_color = ChunkyPNG::Color::WHITE, tolerance = 5) base, mask = extract_mask(old_theme_color, bg_color, tolerance) mask.change_mask_color!(new_theme_color) self.replace!(base.compose!(mask)) end # Creates a base image and a mask image from an original image that has a particular theme color. # This can be used to easily change a theme color in an image. # # It will extract all the pixels that look like the theme color (with a tolerance level) and put # these in a mask image. All the other pixels will be stored in a base image. Both images will be # of the exact same size as the original image. The original image will be left untouched. # # The color of the mask image can be changed with {#change_mask_color!}. This new mask image can # then be composed upon the base image to create an image with a new theme color. A call to # {#change_theme_color!} will perform this in one go. # # @param [Integer] mask_color The current theme color. # @param [Integer] bg_color The background color on which the theme colored pixels are applied. # @param [Integer] tolerance The tolerance level to use when extracting the mask image. Five is # the default; increase this if the masked image does not extract all the required pixels, # decrease it if too many pixels get extracted. # @return [Array] An array with the base canvas and the mask # canvas as elements. # @see #change_theme_color! # @see #change_mask_color! def extract_mask(mask_color, bg_color = ChunkyPNG::Color::WHITE, tolerance = 5) base_pixels = [] mask_pixels = [] pixels.each do |pixel| if ChunkyPNG::Color.alpha_decomposable?(pixel, mask_color, bg_color, tolerance) mask_pixels << ChunkyPNG::Color.decompose_color(pixel, mask_color, bg_color, tolerance) base_pixels << bg_color else mask_pixels << (mask_color & 0xffffff00) base_pixels << pixel end end [ self.class.new(width, height, base_pixels), self.class.new(width, height, mask_pixels) ] end # Changes the color of a mask image. # # This method works on a canvas extracted out of another image using the {#extract_mask} method. # It can then be applied on the extracted base image. See {#change_theme_color!} to perform # these operations in one go. # # @param [Integer] new_color The color to replace the original mask color with. # @raise [ChunkyPNG::ExpectationFailed] when this canvas is not a mask image, i.e. its palette # has more than once color, disregarding transparency. # @see #change_theme_color! # @see #extract_mask def change_mask_color!(new_color) raise ChunkyPNG::ExpectationFailed, "This is not a mask image!" if palette.opaque_palette.size != 1 pixels.map! { |pixel| (new_color & 0xffffff00) | ChunkyPNG::Color.a(pixel) } self end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/operations.rb000066400000000000000000000323621212556323000247350ustar00rootroot00000000000000module ChunkyPNG class Canvas # The ChunkyPNG::Canvas::Operations module defines methods to perform operations # on a {ChunkyPNG::Canvas}. The module is included into the Canvas class so all # these methods are available on every canvas. # # Note that some of these operations modify the canvas, while some operations return # a new canvas and leave the original intact. # # @see ChunkyPNG::Canvas module Operations # Converts the canvas to grascale. # # This method will modify the canvas. The obtain a new canvas and leave the # current instance intact, use {#grayscale} instead. # # @return [ChunkyPNG::Canvas] Returns itself, converted to grayscale. # @see {#grayscale} # @see {ChunkyPNG::Color#to_grayscale} def grayscale! pixels.map! { |pixel| ChunkyPNG::Color.to_grayscale(pixel) } return self end # Converts the canvas to grascale, returning a new canvas. # # This method will not modify the canvas. To modift the current canvas, # use {#grayscale!} instead. # # @return [ChunkyPNG::Canvas] A copy of the canvas, converted to grasycale. # @see {#grayscale!} # @see {ChunkyPNG::Color#to_grayscale} def grayscale dup.grayscale! end # Composes another image onto this image using alpha blending. This will modify # the current canvas. # # If you simply want to replace pixels or when the other image does not have # transparency, it is faster to use {#replace!}. # # @param [ChunkyPNG::Canvas] other The foreground canvas to compose on the # current canvas, using alpha compositing. # @param [Integer] offset_x The x-offset to apply the new foreground on. # @param [Integer] offset_y The y-offset to apply the new foreground on. # @return [ChunkyPNG::Canvas] Returns itself, but with the other canvas composed onto it. # @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one, # given the offset and size of the other canvas. # @see #replace! # @see #compose def compose!(other, offset_x = 0, offset_y = 0) check_size_constraints!(other, offset_x, offset_y) for y in 0...other.height do for x in 0...other.width do set_pixel(x + offset_x, y + offset_y, ChunkyPNG::Color.compose(other.get_pixel(x, y), get_pixel(x + offset_x, y + offset_y))) end end self end # Composes another image onto this image using alpha blending. This will return # a new canvas and leave the original intact. # # If you simply want to replace pixels or when the other image does not have # transparency, it is faster to use {#replace}. # # @param (see #compose!) # @return [ChunkyPNG::Canvas] Returns the new canvas, composed of the other 2. # @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one, # given the offset and size of the other canvas. # # @note API changed since 1.0 - This method now no longer is in place, but returns # a new canvas and leaves the original intact. Use {#compose!} if you want to # compose on the canvas in place. # @see #replace def compose(other, offset_x = 0, offset_y = 0) dup.compose!(other, offset_x, offset_y) end # Replaces pixels on this image by pixels from another pixels, on a given offset. # This method will modify the current canvas. # # This will completely replace the pixels of the background image. If you want to blend # them with semi-transparent pixels from the foreground image, see {#compose!}. # # @param [ChunkyPNG::Canvas] other The foreground canvas to get the pixels from. # @param [Integer] offset_x The x-offset to apply the new foreground on. # @param [Integer] offset_y The y-offset to apply the new foreground on. # @return [ChunkyPNG::Canvas] Returns itself, but with the other canvas placed onto it. # @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one, # given the offset and size of the other canvas. # @see #compose! # @see #replace def replace!(other, offset_x = 0, offset_y = 0) check_size_constraints!(other, offset_x, offset_y) for y in 0...other.height do for d in 0...other.width pixels[(y + offset_y) * width + offset_x + d] = other.pixels[y * other.width + d] end end self end # Replaces pixels on this image by pixels from another pixels, on a given offset. # This method will modify the current canvas. # # This will completely replace the pixels of the background image. If you want to blend # them with semi-transparent pixels from the foreground image, see {#compose!}. # # @param (see #replace!) # @return [ChunkyPNG::Canvas] Returns a new, combined canvas. # @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one, # given the offset and size of the other canvas. # # @note API changed since 1.0 - This method now no longer is in place, but returns # a new canvas and leaves the original intact. Use {#replace!} if you want to # replace pixels on the canvas in place. # @see #compose def replace(other, offset_x = 0, offset_y = 0) dup.replace!(other, offset_x, offset_y) end # Crops an image, given the coordinates and size of the image that needs to be cut out. # This will leave the original image intact and return a new, cropped image with pixels # copied from the original image. # # @param [Integer] x The x-coordinate of the top left corner of the image to be cropped. # @param [Integer] y The y-coordinate of the top left corner of the image to be cropped. # @param [Integer] crop_width The width of the image to be cropped. # @param [Integer] crop_height The height of the image to be cropped. # @return [ChunkyPNG::Canvas] Returns the newly created cropped image. # @raise [ChunkyPNG::OutOfBounds] when the crop dimensions plus the given coordinates # are bigger then the original image. def crop(x, y, crop_width, crop_height) dup.crop!(x, y, crop_width, crop_height) end # Crops an image, given the coordinates and size of the image that needs to be cut out. # # This will change the size and content of the current canvas. Use {#crop} if you want to # have a new canvas returned instead, leaving the current canvas intact. # # @param [Integer] x The x-coordinate of the top left corner of the image to be cropped. # @param [Integer] y The y-coordinate of the top left corner of the image to be cropped. # @param [Integer] crop_width The width of the image to be cropped. # @param [Integer] crop_height The height of the image to be cropped. # @return [ChunkyPNG::Canvas] Returns itself, but cropped. # @raise [ChunkyPNG::OutOfBounds] when the crop dimensions plus the given coordinates # are bigger then the original image. def crop!(x, y, crop_width, crop_height) raise ChunkyPNG::OutOfBounds, "Image width is too small!" if crop_width + x > width raise ChunkyPNG::OutOfBounds, "Image width is too small!" if crop_height + y > height new_pixels = [] for cy in 0...crop_height do new_pixels += pixels.slice((cy + y) * width + x, crop_width) end replace_canvas!(crop_width, crop_height, new_pixels) end # Flips the image horizontally, leaving the original intact. # # This will flip the image on its horizontal axis, e.g. pixels on the top will now # be pixels on the bottom. Chaining this method twice will return the original canvas. # This method will leave the original object intact and return a new canvas. # # @return [ChunkyPNG::Canvas] The flipped image # @see #flip_horizontally! def flip_horizontally dup.flip_horizontally! end # Flips the image horizontally in place. # # This will flip the image on its horizontal axis, e.g. pixels on the top will now # be pixels on the bottom. Chaining this method twice will return the original canvas. # This method will leave the original object intact and return a new canvas. # # @return [ChunkyPNG::Canvas] Itself, but flipped # @see #flip_horizontally def flip_horizontally! for y in 0..((height - 1) >> 1) do other_y = height - (y + 1) other_row = row(other_y) replace_row!(other_y, row(y)) replace_row!(y, other_row) end return self end alias_method :flip!, :flip_horizontally! alias_method :flip, :flip_horizontally # Flips the image vertically, leaving the original intact. # # This will flip the image on its vertical axis, e.g. pixels on the left will now # be pixels on the right. Chaining this method twice will return the original canvas. # This method will leave the original object intact and return a new canvas. # # @return [ChunkyPNG::Canvas] The flipped image # @see #flip_vertically! def flip_vertically dup.flip_vertically! end # Flips the image vertically in place. # # This will flip the image on its vertical axis, e.g. pixels on the left will now # be pixels on the right. Chaining this method twice will return the original canvas. # This method will leave the original object intact and return a new canvas. # # @return [ChunkyPNG::Canvas] Itself, but flipped # @see #flip_vertically def flip_vertically! for y in 0...height do replace_row!(y, row(y).reverse) end return self end alias_method :mirror!, :flip_vertically! alias_method :mirror, :flip_vertically # Returns a new canvas instance that is rotated 90 degrees clockwise. # # This method will return a new canvas and leaves the original intact. # See {#rotate_right!} for the in place version. # # @return [ChunkyPNG::Canvas] A clockwise-rotated copy. def rotate_right dup.rotate_right! end # Rotates the image 90 degrees clockwise in place. # # This method will change the current canvas. See {#rotate_right} for # a version that leaves th current canvas intact # # @return [ChunkyPNG::Canvas] Itself, but rotated clockwise. def rotate_right! rotated = self.class.new(height, width) new_pixels = [] 0.upto(width - 1) { |i| new_pixels += column(i).reverse } replace_canvas!(height, width, new_pixels) end alias_method :rotate_clockwise, :rotate_right alias_method :rotate_clockwise!, :rotate_right! # Returns an image that is rotated 90 degrees counter-clockwise. # # This method will leave the original object intact and return a new canvas. # See {#rotate_left!} for the in place version. # # @return [ChunkyPNG::Canvas] A rotated copy of itself. def rotate_left dup.rotate_left! end # Rotates the image 90 degrees counter-clockwise in place. # # This method will change the original canvas. See {#rotate_left} for a # version that leaves the canvas intact and returns a new rotated canvas # instead. # # @return [ChunkyPNG::Canvas] Itself, but rotated. def rotate_left! new_pixels = [] (width - 1).downto(0) { |i| new_pixels += column(i) } replace_canvas!(height, width, new_pixels) end alias_method :rotate_counter_clockwise, :rotate_left alias_method :rotate_counter_clockwise!, :rotate_left! # Rotates the image 180 degrees. # This method will leave the original object intact and return a new canvas. # # @return [ChunkyPNG::Canvas] The rotated image. # @see #rotate_180! def rotate_180 dup.rotate_180! end # Rotates the image 180 degrees in place. # # @return [ChunkyPNG::Canvas] Itself, but rotated 180 degrees. # @see #rotate_180 def rotate_180! pixels.reverse! return self end protected # Checks whether another image has the correct dimension to be used for an operation # on the current image, given an offset coordinate to work with. # @param [ChunkyPNG::Canvas] other The other canvas # @param [Integer] offset_x The x offset on which the other image will be applied. # @param [Integer] offset_y The y offset on which the other image will be applied. # @raise [ChunkyPNG::OutOfBounds] when the other image doesn't fit. def check_size_constraints!(other, offset_x, offset_y) raise ChunkyPNG::OutOfBounds, "Background image width is too small!" if width < other.width + offset_x raise ChunkyPNG::OutOfBounds, "Background image height is too small!" if height < other.height + offset_y end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/png_decoding.rb000066400000000000000000000607761212556323000252040ustar00rootroot00000000000000module ChunkyPNG class Canvas # The PNGDecoding contains methods for decoding PNG datastreams to create a # Canvas object. The datastream can be provided as filename, string or IO # stream. # # Overview of the decoding process: # # * The optional PLTE and tRNS chunk are decoded for the color palette of # the original image. # * The contents of the IDAT chunks is combined, and uncompressed using # Inflate decompression to the image pixelstream. # * Based on the color mode, width and height of the original image, which # is read from the PNG header (IHDR chunk), the amount of bytes # per line is determined. # * For every line of pixels in the encoded image, the original byte values # are restored by unapplying the milter method for that line. # * The read bytes are unfiltered given by the filter function specified by # the first byte of the line. # * The unfiltered pixelstream are is into colored pixels, using the color mode. # * All lines combined to form the original image. # # For interlaced images, the original image was split into 7 subimages. # These images get decoded just like the process above (from step 3), and get # combined to form the original images. # # @see ChunkyPNG::Canvas::PNGEncoding # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification module PNGDecoding # The palette that is used to decode the image, loading from the PLTE and # tRNS chunk from the PNG stream. For RGB(A) images, no palette is required. # @return [ChunkyPNG::Palette] attr_accessor :decoding_palette # The color to be replaced with fully transparent pixels. attr_accessor :transparent_color # Decodes a Canvas from a PNG encoded string. # @param [String] str The string to read from. # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG encoded string. def from_blob(str) from_datastream(ChunkyPNG::Datastream.from_blob(str)) end alias_method :from_string, :from_blob # Decodes a Canvas from a PNG encoded file. # @param [String] filename The file to read from. # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG file. def from_file(filename) from_datastream(ChunkyPNG::Datastream.from_file(filename)) end # Decodes a Canvas from a PNG encoded stream. # @param [IO, #read] io The stream to read from. # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG stream. def from_io(io) from_datastream(ChunkyPNG::Datastream.from_io(io)) end alias_method :from_stream, :from_io # Decodes the Canvas from a PNG datastream instance. # @param [ChunkyPNG::Datastream] ds The datastream to decode. # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG datastream. def from_datastream(ds) width = ds.header_chunk.width height = ds.header_chunk.height color_mode = ds.header_chunk.color interlace = ds.header_chunk.interlace depth = ds.header_chunk.depth if width == 0 || height == 0 raise ExpectationFailed, "Invalid image size, width: #{width}, height: #{height}" end case color_mode when ChunkyPNG::COLOR_INDEXED self.decoding_palette = ChunkyPNG::Palette.from_chunks(ds.palette_chunk, ds.transparency_chunk) when ChunkyPNG::COLOR_TRUECOLOR self.transparent_color = ds.transparency_chunk.truecolor_entry(depth) if ds.transparency_chunk when ChunkyPNG::COLOR_GRAYSCALE self.transparent_color = ds.transparency_chunk.grayscale_entry(depth) if ds.transparency_chunk end decode_png_pixelstream(ds.imagedata, width, height, color_mode, depth, interlace) end # Decodes a canvas from a PNG encoded pixelstream, using a given width, height, # color mode and interlacing mode. # @param [String] stream The pixelstream to read from. # @param [Integer] width The width of the image. # @param [Integer] width The height of the image. # @param [Integer] color_mode The color mode of the encoded pixelstream. # @param [Integer] depth The bit depth of the pixel samples. # @param [Integer] interlace The interlace method of the encoded pixelstream. # @return [ChunkyPNG::Canvas] The decoded Canvas instance. def decode_png_pixelstream(stream, width, height, color_mode, depth, interlace) raise ChunkyPNG::ExpectationFailed, "This palette is not suitable for decoding!" if decoding_palette && !decoding_palette.can_decode? image = case interlace when ChunkyPNG::INTERLACING_NONE; decode_png_without_interlacing(stream, width, height, color_mode, depth) when ChunkyPNG::INTERLACING_ADAM7; decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth) else raise ChunkyPNG::NotSupported, "Don't know how the handle interlacing method #{interlace}!" end image.pixels.map! { |c| c == transparent_color ? ChunkyPNG::Color::TRANSPARENT : c } if transparent_color return image end protected # Decodes a canvas from a non-interlaced PNG encoded pixelstream, using a # given width, height and color mode. # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param depth (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) def decode_png_without_interlacing(stream, width, height, color_mode, depth) decode_png_image_pass(stream, width, height, color_mode, depth, 0) end # Decodes a canvas from a Adam 7 interlaced PNG encoded pixelstream, using a # given width, height and color mode. # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param depth (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) def decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth) canvas = new(width, height) start_pos = 0 for pass in 0...7 sm_width, sm_height = adam7_pass_size(pass, width, height) sm = decode_png_image_pass(stream, sm_width, sm_height, color_mode, depth, start_pos) adam7_merge_pass(pass, canvas, sm) start_pos += ChunkyPNG::Color.pass_bytesize(color_mode, depth, sm_width, sm_height) end canvas end # Extract 4 consecutive bits from a byte. # @param [Integer] byte The byte (0..255) value to extract a 4 bit value from. # @param [Integer] index The index within the byte. This should be either 0 or 2; # the value will be modded by 2 to enforce this. # @return [Integer] The extracted 4bit value (0..15) def decode_png_extract_4bit_value(byte, index) (index & 0x01 == 0) ? ((byte & 0xf0) >> 4) : (byte & 0x0f) end # Extract 2 consecutive bits from a byte. # @param [Integer] byte The byte (0..255) value to extract a 2 bit value from. # @param [Integer] index The index within the byte. This should be either 0, 1, 2, or 3; # the value will be modded by 4 to enforce this. # @return [Integer] The extracted 2 bit value (0..3) def decode_png_extract_2bit_value(byte, index) bitshift = 6 - ((index & 0x03) << 1) (byte & (0x03 << bitshift)) >> bitshift end # Extract a bit from a byte on a given index. # @param [Integer] byte The byte (0..255) value to extract a bit from. # @param [Integer] index The index within the byte. This should be 0..7; # the value will be modded by 8 to enforce this. # @return [Integer] Either 1 or 0. def decode_png_extract_1bit_value(byte, index) bitshift = 7 - (index & 0x07) (byte & (0x01 << bitshift)) >> bitshift end # Resamples a 16 bit value to an 8 bit value. This will discard some color information. # @param [Integer] value The 16 bit value to resample. # @return [Integer] The 8 bit resampled value def decode_png_resample_16bit_value(value) value >> 8 end # No-op - available for completeness sake only # @param [Integer] value The 8 bit value to resample. # @return [Integer] The 8 bit resampled value def decode_png_resample_8bit_value(value) value end # Resamples a 4 bit value to an 8 bit value. # @param [Integer] value The 4 bit value to resample. # @return [Integer] The 8 bit resampled value. def decode_png_resample_4bit_value(value) value << 4 | value end # Resamples a 2 bit value to an 8 bit value. # @param [Integer] value The 2 bit value to resample. # @return [Integer] The 8 bit resampled value. def decode_png_resample_2bit_value(value) value << 6 | value << 4 | value << 2 | value end # Resamples a 1 bit value to an 8 bit value. # @param [Integer] value The 1 bit value to resample. # @return [Integer] The 8 bit resampled value def decode_png_resample_1bit_value(value) value == 0x01 ? 0xff : 0x00 end # Decodes a scanline of a 1-bit, indexed image into a row of pixels. # @param [String] stream The stream to decode from. # @param [Integer] pos The position in the stream on which the scanline starts (including the filter byte). # @param [Integer] width The width in pixels of the scanline. # @return [Array] An array of decoded pixels. def decode_png_pixels_from_scanline_indexed_1bit(stream, pos, width) (0...width).map do |index| palette_pos = decode_png_extract_1bit_value(stream.getbyte(pos + 1 + (index >> 3)), index) decoding_palette[palette_pos] end end # Decodes a scanline of a 2-bit, indexed image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_indexed_2bit(stream, pos, width) (0...width).map do |index| palette_pos = decode_png_extract_2bit_value(stream.getbyte(pos + 1 + (index >> 2)), index) decoding_palette[palette_pos] end end # Decodes a scanline of a 4-bit, indexed image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_indexed_4bit(stream, pos, width) (0...width).map do |index| palette_pos = decode_png_extract_4bit_value(stream.getbyte(pos + 1 + (index >> 1)), index) decoding_palette[palette_pos] end end # Decodes a scanline of a 8-bit, indexed image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_indexed_8bit(stream, pos, width) (1..width).map { |i| decoding_palette[stream.getbyte(pos + i)] } end # Decodes a scanline of an 8-bit, true color image with transparency into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_truecolor_alpha_8bit(stream, pos, width) stream.unpack("@#{pos + 1}N#{width}") end # Decodes a scanline of a 16-bit, true color image with transparency into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_truecolor_alpha_16bit(stream, pos, width) pixels = [] stream.unpack("@#{pos + 1}n#{width * 4}").each_slice(4) do |r, g, b, a| pixels << ChunkyPNG::Color.rgba(decode_png_resample_16bit_value(r), decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(b), decode_png_resample_16bit_value(a)) end return pixels end # Decodes a scanline of an 8-bit, true color image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_truecolor_8bit(stream, pos, width) stream.unpack("@#{pos + 1}" << ('NX' * width)).map { |c| c | 0x000000ff } end # Decodes a scanline of a 16-bit, true color image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_truecolor_16bit(stream, pos, width) pixels = [] stream.unpack("@#{pos + 1}n#{width * 3}").each_slice(3) do |r, g, b| pixels << ChunkyPNG::Color.rgb(decode_png_resample_16bit_value(r), decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(b)) end return pixels end # Decodes a scanline of an 8-bit, grayscale image with transparency into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_alpha_8bit(stream, pos, width) (0...width).map { |i| ChunkyPNG::Color.grayscale_alpha(stream.getbyte(pos + (i * 2) + 1), stream.getbyte(pos + (i * 2) + 2)) } end # Decodes a scanline of a 16-bit, grayscale image with transparency into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_alpha_16bit(stream, pos, width) pixels = [] stream.unpack("@#{pos + 1}n#{width * 2}").each_slice(2) do |g, a| pixels << ChunkyPNG::Color.grayscale_alpha(decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(a)) end return pixels end # Decodes a scanline of a 1-bit, grayscale image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_1bit(stream, pos, width) (0...width).map do |index| value = decode_png_extract_1bit_value(stream.getbyte(pos + 1 + (index >> 3)), index) value == 1 ? ChunkyPNG::Color::WHITE : ChunkyPNG::Color::BLACK end end # Decodes a scanline of a 2-bit, grayscale image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_2bit(stream, pos, width) (0...width).map do |index| value = decode_png_extract_2bit_value(stream.getbyte(pos + 1 + (index >> 2)), index) ChunkyPNG::Color.grayscale(decode_png_resample_2bit_value(value)) end end # Decodes a scanline of a 4-bit, grayscale image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_4bit(stream, pos, width) (0...width).map do |index| value = decode_png_extract_4bit_value(stream.getbyte(pos + 1 + (index >> 1)), index) ChunkyPNG::Color.grayscale(decode_png_resample_4bit_value(value)) end end # Decodes a scanline of an 8-bit, grayscale image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_8bit(stream, pos, width) (1..width).map { |i| ChunkyPNG::Color.grayscale(stream.getbyte(pos + i)) } end # Decodes a scanline of a 16-bit, grayscale image into a row of pixels. # @params (see #decode_png_pixels_from_scanline_indexed_1bit) # @return (see #decode_png_pixels_from_scanline_indexed_1bit) def decode_png_pixels_from_scanline_grayscale_16bit(stream, pos, width) values = stream.unpack("@#{pos + 1}n#{width}") values.map { |value| ChunkyPNG::Color.grayscale(decode_png_resample_16bit_value(value)) } end # Returns the method name to use to decode scanlines into pixels. # @param [Integer] color_mode The color mode of the image. # @param [Integer] depth The bit depth of the image. # @return [Symbol] The method name to use for decoding, to be called on the canvas class. # @raise [ChunkyPNG::NotSupported] when the color_mode and/or bit depth is not supported. def decode_png_pixels_from_scanline_method(color_mode, depth) decoder_method = case color_mode when ChunkyPNG::COLOR_TRUECOLOR; :"decode_png_pixels_from_scanline_truecolor_#{depth}bit" when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; :"decode_png_pixels_from_scanline_truecolor_alpha_#{depth}bit" when ChunkyPNG::COLOR_INDEXED; :"decode_png_pixels_from_scanline_indexed_#{depth}bit" when ChunkyPNG::COLOR_GRAYSCALE; :"decode_png_pixels_from_scanline_grayscale_#{depth}bit" when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; :"decode_png_pixels_from_scanline_grayscale_alpha_#{depth}bit" else nil end raise ChunkyPNG::NotSupported, "No decoder found for color mode #{color_mode} and #{depth}-bit depth!" unless respond_to?(decoder_method, true) decoder_method end # Decodes a single PNG image pass width a given width, height and color # mode, to a Canvas, starting at the given position in the stream. # # A non-interlaced image only consists of one pass, while an Adam7 # image consists of 7 passes that must be combined after decoding. # # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) # @param [Integer] start_pos The position in the pixel stream to start reading. # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) def decode_png_image_pass(stream, width, height, color_mode, depth, start_pos) pixels = [] if width > 0 && height > 0 stream << ChunkyPNG::EXTRA_BYTE if color_mode == ChunkyPNG::COLOR_TRUECOLOR pixel_decoder = decode_png_pixels_from_scanline_method(color_mode, depth) line_length = ChunkyPNG::Color.scanline_bytesize(color_mode, depth, width) pixel_size = ChunkyPNG::Color.pixel_bytesize(color_mode, depth) raise ChunkyPNG::ExpectationFailed, "Invalid stream length!" unless stream.bytesize - start_pos >= ChunkyPNG::Color.pass_bytesize(color_mode, depth, width, height) pos, prev_pos = start_pos, nil for line_no in 0...height do decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size) pixels += send(pixel_decoder, stream, pos, width) prev_pos = pos pos += line_length + 1 end end new(width, height, pixels) end # Decodes a scanline if it was encoded using filtering. # # It will extract the filtering method from the first byte of the scanline, and uses the # method to change the subsequent bytes to unfiltered values. This will modify the pixelstream. # # The bytes of the scanline can then be used to construct pixels, based on the color mode.. # # @param [String] stream The pixelstream to undo the filtering in. # @param [Integer] pos The starting position of the scanline to decode. # @param [Integer, nil] prev_pos The starting position of the previously decoded scanline, or nil # if this is the first scanline of the image. # @param [Integer] line_length The number of bytes in the scanline, discounting the filter method byte. # @param [Integer] pixel_size The number of bytes used per pixel, based on the color mode. # @return [void] def decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size) case stream.getbyte(pos) when ChunkyPNG::FILTER_NONE; # noop when ChunkyPNG::FILTER_SUB; decode_png_str_scanline_sub( stream, pos, prev_pos, line_length, pixel_size) when ChunkyPNG::FILTER_UP; decode_png_str_scanline_up( stream, pos, prev_pos, line_length, pixel_size) when ChunkyPNG::FILTER_AVERAGE; decode_png_str_scanline_average( stream, pos, prev_pos, line_length, pixel_size) when ChunkyPNG::FILTER_PAETH; decode_png_str_scanline_paeth( stream, pos, prev_pos, line_length, pixel_size) else raise ChunkyPNG::NotSupported, "Unknown filter type: #{stream.getbyte(pos)}!" end end # Decodes a scanline that wasn't encoded using filtering. This is a no-op. # @params (see #decode_png_str_scanline) # @return [void] def decode_png_str_scanline_sub_none(stream, pos, prev_pos, line_length, pixel_size) # noop - this method shouldn't get called. end # Decodes a scanline in a pixelstream that was encoded using SUB filtering. # This will change the pixelstream to have unfiltered values. # @params (see #decode_png_str_scanline) # @return [void] def decode_png_str_scanline_sub(stream, pos, prev_pos, line_length, pixel_size) for i in 1..line_length do stream.setbyte(pos + i, (stream.getbyte(pos + i) + (i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0)) & 0xff) end end # Decodes a scanline in a pixelstream that was encoded using UP filtering. # This will change the pixelstream to have unfiltered values. # @params (see #decode_png_str_scanline) # @return [void] def decode_png_str_scanline_up(stream, pos, prev_pos, line_length, pixel_size) for i in 1..line_length do up = prev_pos ? stream.getbyte(prev_pos + i) : 0 stream.setbyte(pos + i, (stream.getbyte(pos + i) + up) & 0xff) end end # Decodes a scanline in a pixelstream that was encoded using AVERAGE filtering. # This will change the pixelstream to have unfiltered values. # @params (see #decode_png_str_scanline) # @return [void] def decode_png_str_scanline_average(stream, pos, prev_pos, line_length, pixel_size) for i in 1..line_length do a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0 b = prev_pos ? stream.getbyte(prev_pos + i) : 0 stream.setbyte(pos + i, (stream.getbyte(pos + i) + ((a + b) >> 1)) & 0xff) end end # Decodes a scanline in a pixelstream that was encoded using PAETH filtering. # This will change the pixelstream to have unfiltered values. # @params (see #decode_png_str_scanline) # @return [void] def decode_png_str_scanline_paeth(stream, pos, prev_pos, line_length, pixel_size) for i in 1..line_length do cur_pos = pos + i a = (i > pixel_size) ? stream.getbyte(cur_pos - pixel_size) : 0 b = prev_pos ? stream.getbyte(prev_pos + i) : 0 c = (prev_pos && i > pixel_size) ? stream.getbyte(prev_pos + i - pixel_size) : 0 p = a + b - c pa = (p - a).abs pb = (p - b).abs pc = (p - c).abs pr = (pa <= pb) ? (pa <= pc ? a : c) : (pb <= pc ? b : c) stream.setbyte(cur_pos, (stream.getbyte(cur_pos) + pr) & 0xff) end end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/png_encoding.rb000066400000000000000000000524641212556323000252110ustar00rootroot00000000000000module ChunkyPNG class Canvas # Methods for encoding a Canvas instance into a PNG datastream. # # Overview of the encoding process: # # * The image is split up in scanlines (i.e. rows of pixels); # * All pixels are encoded as a pixelstream, based on the color mode. # * All the pixel bytes in the pixelstream are adjusted using a filtering # method if one is specified. # * Compress the resulting string using deflate compression. # * Split compressed data over one or more PNG chunks. # * These chunks should be embedded in a datastream with at least a IHDR and # IEND chunk and possibly a PLTE chunk. # # For interlaced images, the initial image is first split into 7 subimages. # These images get encoded exactly as above, and the result gets combined # before the compression step. # # @see ChunkyPNG::Canvas::PNGDecoding # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification module PNGEncoding # The palette used for encoding the image.This is only in used for images # that get encoded using indexed colors. # @return [ChunkyPNG::Palette] attr_accessor :encoding_palette # Writes the canvas to an IO stream, encoded as a PNG image. # @param [IO] io The output stream to write to. # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream) # @return [void] def write(io, constraints = {}) to_datastream(constraints).write(io) end # Writes the canvas to a file, encoded as a PNG image. # @param [String] filename The file to save the PNG image to. # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream) # @return [void] def save(filename, constraints = {}) File.open(filename, 'wb') { |io| write(io, constraints) } end # Encoded the canvas to a PNG formatted string. # @param constraints (see ChunkyPNG::Canvas::PNGEncoding#to_datastream) # @return [String] The PNG encoded canvas as string. def to_blob(constraints = {}) to_datastream(constraints).to_blob end alias_method :to_string, :to_blob alias_method :to_s, :to_blob # Converts this Canvas to a datastream, so that it can be saved as a PNG image. # @param [Hash, Symbol] constraints The constraints to use when encoding the canvas. # This can either be a hash with different constraints, or a symbol which acts as a # preset for some constraints. If no constraints are given, ChunkyPNG will decide # for itself how to best create the PNG datastream. # Supported presets are :fast_rgba for quickly saving images with transparency, # :fast_rgb for quickly saving opaque images, and :best_compression to # obtain the smallest possible filesize. # @option constraints [Fixnum] :color_mode The color mode to use. Use one of the # ChunkyPNG::COLOR_* constants. # @option constraints [true, false] :interlace Whether to use interlacing. # @option constraints [Fixnum] :compression The compression level for Zlib. This can be a # value between 0 and 9, or a Zlib constant like Zlib::BEST_COMPRESSION. # @option constraints [Fixnum] :bit_depth The bit depth to use. This option is only used # for indexed images, in which case it overrides the determined minimal bit depth. For # all the other color modes, a bit depth of 8 is used. # @return [ChunkyPNG::Datastream] The PNG datastream containing the encoded canvas. # @see ChunkyPNG::Canvas::PNGEncoding#determine_png_encoding def to_datastream(constraints = {}) encoding = determine_png_encoding(constraints) ds = Datastream.new ds.header_chunk = Chunk::Header.new(:width => width, :height => height, :color => encoding[:color_mode], :depth => encoding[:bit_depth], :interlace => encoding[:interlace]) if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED ds.palette_chunk = encoding_palette.to_plte_chunk ds.transparency_chunk = encoding_palette.to_trns_chunk unless encoding_palette.opaque? end data = encode_png_pixelstream(encoding[:color_mode], encoding[:bit_depth], encoding[:interlace], encoding[:filtering]) ds.data_chunks = Chunk::ImageData.split_in_chunks(data, encoding[:compression]) ds.end_chunk = Chunk::End.new return ds end protected # Determines the best possible PNG encoding variables for this image, by analyzing # the colors used for the image. # # You can provide constraints for the encoding variables by passing a hash with # encoding variables to this method. # # @param [Hash, Symbol] constraints The constraints for the encoding. This can be a # Hash or a preset symbol. # @return [Hash] A hash with encoding options for {ChunkyPNG::Canvas::PNGEncoding#to_datastream} def determine_png_encoding(constraints = {}) encoding = case constraints when :fast_rgb; { :color_mode => ChunkyPNG::COLOR_TRUECOLOR, :compression => Zlib::BEST_SPEED } when :fast_rgba; { :color_mode => ChunkyPNG::COLOR_TRUECOLOR_ALPHA, :compression => Zlib::BEST_SPEED } when :best_compression; { :compression => Zlib::BEST_COMPRESSION, :filtering => ChunkyPNG::FILTER_PAETH } when :good_compression; { :compression => Zlib::BEST_COMPRESSION, :filtering => ChunkyPNG::FILTER_NONE } when :no_compression; { :compression => Zlib::NO_COMPRESSION } when :black_and_white; { :color_mode => ChunkyPNG::COLOR_GRAYSCALE, :bit_depth => 1 } when Hash; constraints else raise ChunkyPNG::Exception, "Unknown encoding preset: #{constraints.inspect}" end # Do not create a palette when the encoding is given and does not require a palette. if encoding[:color_mode] if encoding[:color_mode] == ChunkyPNG::COLOR_INDEXED self.encoding_palette = self.palette encoding[:bit_depth] ||= self.encoding_palette.determine_bit_depth else encoding[:bit_depth] ||= 8 end else self.encoding_palette = self.palette suggested_color_mode, suggested_bit_depth = encoding_palette.best_color_settings encoding[:color_mode] ||= suggested_color_mode encoding[:bit_depth] ||= suggested_bit_depth end # Use Zlib's default for compression unless otherwise provided. encoding[:compression] ||= Zlib::DEFAULT_COMPRESSION encoding[:interlace] = case encoding[:interlace] when nil, false, ChunkyPNG::INTERLACING_NONE; ChunkyPNG::INTERLACING_NONE when true, ChunkyPNG::INTERLACING_ADAM7; ChunkyPNG::INTERLACING_ADAM7 else encoding[:interlace] end encoding[:filtering] ||= case encoding[:compression] when Zlib::BEST_COMPRESSION; ChunkyPNG::FILTER_PAETH when Zlib::NO_COMPRESSION..Zlib::BEST_SPEED; ChunkyPNG::FILTER_NONE else ChunkyPNG::FILTER_UP end return encoding end # Encodes the canvas according to the PNG format specification with a given color # mode, possibly with interlacing. # @param [Integer] color_mode The color mode to use for encoding. # @param [Integer] bit_depth The bit depth of the image. # @param [Integer] interlace The interlacing method to use. # @return [String] The PNG encoded canvas as string. def encode_png_pixelstream(color_mode = ChunkyPNG::COLOR_TRUECOLOR, bit_depth = 8, interlace = ChunkyPNG::INTERLACING_NONE, filtering = ChunkyPNG::FILTER_NONE) if color_mode == ChunkyPNG::COLOR_INDEXED raise ChunkyPNG::ExpectationFailed, "This palette is not suitable for encoding!" if encoding_palette.nil? || !encoding_palette.can_encode? raise ChunkyPNG::ExpectationFailed, "This palette has too many colors!" if encoding_palette.size > (1 << bit_depth) end case interlace when ChunkyPNG::INTERLACING_NONE; encode_png_image_without_interlacing(color_mode, bit_depth, filtering) when ChunkyPNG::INTERLACING_ADAM7; encode_png_image_with_interlacing(color_mode, bit_depth, filtering) else raise ChunkyPNG::NotSupported, "Unknown interlacing method: #{interlace}!" end end # Encodes the canvas according to the PNG format specification with a given color mode. # @param [Integer] color_mode The color mode to use for encoding. # @param [Integer] bit_depth The bit depth of the image. # @param [Integer] filtering The filtering method to use. # @return [String] The PNG encoded canvas as string. def encode_png_image_without_interlacing(color_mode, bit_depth = 8, filtering = ChunkyPNG::FILTER_NONE) stream = ChunkyPNG::Datastream.empty_bytearray encode_png_image_pass_to_stream(stream, color_mode, bit_depth, filtering) stream end # Encodes the canvas according to the PNG format specification with a given color # mode and Adam7 interlacing. # # This method will split the original canvas in 7 smaller canvases and encode them # one by one, concatenating the resulting strings. # # @param [Integer] color_mode The color mode to use for encoding. # @param [Integer] bit_depth The bit depth of the image. # @param [Integer] filtering The filtering method to use. # @return [String] The PNG encoded canvas as string. def encode_png_image_with_interlacing(color_mode, bit_depth = 8, filtering = ChunkyPNG::FILTER_NONE) stream = ChunkyPNG::Datastream.empty_bytearray 0.upto(6) do |pass| subcanvas = self.class.adam7_extract_pass(pass, self) subcanvas.encoding_palette = encoding_palette subcanvas.encode_png_image_pass_to_stream(stream, color_mode, bit_depth, filtering) end stream end # Encodes the canvas to a stream, in a given color mode. # @param [String] stream The stream to write to. # @param [Integer] color_mode The color mode to use for encoding. # @param [Integer] bit_depth The bit depth of the image. # @param [Integer] filtering The filtering method to use. def encode_png_image_pass_to_stream(stream, color_mode, bit_depth, filtering) start_pos = stream.bytesize pixel_size = Color.pixel_bytesize(color_mode) line_width = Color.scanline_bytesize(color_mode, bit_depth, width) # Determine the filter method encode_method = encode_png_pixels_to_scanline_method(color_mode, bit_depth) filter_method = case filtering when ChunkyPNG::FILTER_SUB; :encode_png_str_scanline_sub when ChunkyPNG::FILTER_UP; :encode_png_str_scanline_up when ChunkyPNG::FILTER_AVERAGE; :encode_png_str_scanline_average when ChunkyPNG::FILTER_PAETH; :encode_png_str_scanline_paeth else nil end 0.upto(height - 1) do |y| stream << send(encode_method, row(y)) end # Now, apply filtering if any if filter_method (height - 1).downto(0) do |y| pos = start_pos + y * (line_width + 1) prev_pos = (y == 0) ? nil : pos - (line_width + 1) send(filter_method, stream, pos, prev_pos, line_width, pixel_size) end end end # Encodes a line of pixels using 8-bit truecolor mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_truecolor_8bit(pixels) pixels.pack('x' + ('NX' * width)) end # Encodes a line of pixels using 8-bit truecolor alpha mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_truecolor_alpha_8bit(pixels) pixels.pack("xN#{width}") end # Encodes a line of pixels using 1-bit indexed mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_indexed_1bit(pixels) chars = [] pixels.each_slice(8) do |p1, p2, p3, p4, p5, p6, p7, p8| chars << ((encoding_palette.index(p1) << 7) | (encoding_palette.index(p2) << 6) | (encoding_palette.index(p3) << 5) | (encoding_palette.index(p4) << 4) | (encoding_palette.index(p5) << 3) | (encoding_palette.index(p6) << 2) | (encoding_palette.index(p7) << 1) | (encoding_palette.index(p8))) end chars.pack('xC*') end # Encodes a line of pixels using 2-bit indexed mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_indexed_2bit(pixels) chars = [] pixels.each_slice(4) do |p1, p2, p3, p4| chars << ((encoding_palette.index(p1) << 6) | (encoding_palette.index(p2) << 4) | (encoding_palette.index(p3) << 2) | (encoding_palette.index(p4))) end chars.pack('xC*') end # Encodes a line of pixels using 4-bit indexed mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_indexed_4bit(pixels) chars = [] pixels.each_slice(2) do |p1, p2| chars << ((encoding_palette.index(p1) << 4) | (encoding_palette.index(p2))) end chars.pack('xC*') end # Encodes a line of pixels using 8-bit indexed mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_indexed_8bit(pixels) pixels.map { |p| encoding_palette.index(p) }.pack("xC#{width}") end # Encodes a line of pixels using 1-bit grayscale mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_grayscale_1bit(pixels) chars = [] pixels.each_slice(8) do |p1, p2, p3, p4, p5, p6, p7, p8| chars << ((p1.nil? ? 0 : (p1 & 0x0000ffff) >> 15 << 7) | (p2.nil? ? 0 : (p2 & 0x0000ffff) >> 15 << 6) | (p3.nil? ? 0 : (p3 & 0x0000ffff) >> 15 << 5) | (p4.nil? ? 0 : (p4 & 0x0000ffff) >> 15 << 4) | (p5.nil? ? 0 : (p5 & 0x0000ffff) >> 15 << 3) | (p6.nil? ? 0 : (p6 & 0x0000ffff) >> 15 << 2) | (p7.nil? ? 0 : (p7 & 0x0000ffff) >> 15 << 1) | (p8.nil? ? 0 : (p8 & 0x0000ffff) >> 15)) end chars.pack('xC*') end # Encodes a line of pixels using 2-bit grayscale mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_grayscale_2bit(pixels) chars = [] pixels.each_slice(4) do |p1, p2, p3, p4| chars << ((p1.nil? ? 0 : (p1 & 0x0000ffff) >> 14 << 6) | (p2.nil? ? 0 : (p2 & 0x0000ffff) >> 14 << 4) | (p3.nil? ? 0 : (p3 & 0x0000ffff) >> 14 << 2) | (p4.nil? ? 0 : (p4 & 0x0000ffff) >> 14)) end chars.pack('xC*') end # Encodes a line of pixels using 2-bit grayscale mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_grayscale_4bit(pixels) chars = [] pixels.each_slice(2) do |p1, p2| chars << ((p1.nil? ? 0 : ((p1 & 0x0000ffff) >> 12) << 4) | (p2.nil? ? 0 : ((p2 & 0x0000ffff) >> 12))) end chars.pack('xC*') end # Encodes a line of pixels using 8-bit grayscale mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_grayscale_8bit(pixels) pixels.map { |p| p >> 8 }.pack("xC#{width}") end # Encodes a line of pixels using 8-bit grayscale alpha mode. # @param [Array] pixels A row of pixels of the original image. # @return [String] The encoded scanline as binary string def encode_png_pixels_to_scanline_grayscale_alpha_8bit(pixels) pixels.pack("xn#{width}") end # Returns the method name to use to decode scanlines into pixels. # @param [Integer] color_mode The color mode of the image. # @param [Integer] depth The bit depth of the image. # @return [Symbol] The method name to use for decoding, to be called on the canvas class. # @raise [ChunkyPNG::NotSupported] when the color_mode and/or bit depth is not supported. def encode_png_pixels_to_scanline_method(color_mode, depth) encoder_method = case color_mode when ChunkyPNG::COLOR_TRUECOLOR; :"encode_png_pixels_to_scanline_truecolor_#{depth}bit" when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; :"encode_png_pixels_to_scanline_truecolor_alpha_#{depth}bit" when ChunkyPNG::COLOR_INDEXED; :"encode_png_pixels_to_scanline_indexed_#{depth}bit" when ChunkyPNG::COLOR_GRAYSCALE; :"encode_png_pixels_to_scanline_grayscale_#{depth}bit" when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; :"encode_png_pixels_to_scanline_grayscale_alpha_#{depth}bit" else nil end raise ChunkyPNG::NotSupported, "No encoder found for color mode #{color_mode} and #{depth}-bit depth!" unless respond_to?(encoder_method, true) encoder_method end # Encodes a scanline of a pixelstream without filtering. This is a no-op. # @param [String] stream The pixelstream to work on. This string will be modified. # @param [Integer] pos The starting position of the scanline. # @param [Integer, nil] prev_pos The starting position of the previous scanline. nil if # this is the first line. # @param [Integer] line_width The number of bytes in this scanline, without counting the filtering # method byte. # @param [Integer] pixel_size The number of bytes used per pixel. # @return [void] def encode_png_str_scanline_none(stream, pos, prev_pos, line_width, pixel_size) # noop - this method shouldn't get called at all. end # Encodes a scanline of a pixelstream using SUB filtering. This will modify the stream. # @param (see #encode_png_str_scanline_none) # @return [void] def encode_png_str_scanline_sub(stream, pos, prev_pos, line_width, pixel_size) line_width.downto(1) do |i| a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0 stream.setbyte(pos + i, (stream.getbyte(pos + i) - a) & 0xff) end stream.setbyte(pos, ChunkyPNG::FILTER_SUB) end # Encodes a scanline of a pixelstream using UP filtering. This will modify the stream. # @param (see #encode_png_str_scanline_none) # @return [void] def encode_png_str_scanline_up(stream, pos, prev_pos, line_width, pixel_size) line_width.downto(1) do |i| b = prev_pos ? stream.getbyte(prev_pos + i) : 0 stream.setbyte(pos + i, (stream.getbyte(pos + i) - b) & 0xff) end stream.setbyte(pos, ChunkyPNG::FILTER_UP) end # Encodes a scanline of a pixelstream using AVERAGE filtering. This will modify the stream. # @param (see #encode_png_str_scanline_none) # @return [void] def encode_png_str_scanline_average(stream, pos, prev_pos, line_width, pixel_size) line_width.downto(1) do |i| a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0 b = prev_pos ? stream.getbyte(prev_pos + i) : 0 stream.setbyte(pos + i, (stream.getbyte(pos + i) - ((a + b) >> 1)) & 0xff) end stream.setbyte(pos, ChunkyPNG::FILTER_AVERAGE) end # Encodes a scanline of a pixelstream using PAETH filtering. This will modify the stream. # @param (see #encode_png_str_scanline_none) # @return [void] def encode_png_str_scanline_paeth(stream, pos, prev_pos, line_width, pixel_size) line_width.downto(1) do |i| a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0 b = (prev_pos) ? stream.getbyte(prev_pos + i) : 0 c = (prev_pos && i > pixel_size) ? stream.getbyte(prev_pos + i - pixel_size) : 0 p = a + b - c pa = (p - a).abs pb = (p - b).abs pc = (p - c).abs pr = (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c) stream.setbyte(pos + i, (stream.getbyte(pos + i) - pr) & 0xff) end stream.setbyte(pos, ChunkyPNG::FILTER_PAETH) end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/resampling.rb000066400000000000000000000116221212556323000247070ustar00rootroot00000000000000 module ChunkyPNG class Canvas # The ChunkyPNG::Canvas::Resampling module defines methods to perform image resampling to # a {ChunkyPNG::Canvas}. # # Currently, only the nearest neighbor algorithm is implemented. Bilinear and cubic # algorithms may be added later on. # # @see ChunkyPNG::Canvas module Resampling # Integer Interpolation between two values # # Used for generating indicies for interpolation (eg, nearest # neighbour). # # @param [Integer] width The width of the source # @param [Integer] new_width The width of the destination # @return [Array] An Array of Integer indicies def steps(width, new_width) indicies, residues = steps_residues(width, new_width) for i in 1..new_width indicies[i-1] = (indicies[i-1] + (residues[i-1] + 127)/255) end return indicies end # Fractional Interpolation between two values # # Used for generating values for interpolation (eg, bilinear). # Produces both the indices and the interpolation factors (residues). # # @param [Integer] width The width of the source # @param [Integer] new_width The width of the destination # @return [Array, Array] Two arrays of indicies and residues def steps_residues(width, new_width) indicies = Array.new(size=new_width, obj=nil) residues = Array.new(size=new_width, obj=nil) # This works by accumulating the fractional error and # overflowing when necessary. # We use mixed number arithmetic with a denominator of # 2 * new_width base_step = width / new_width err_step = (width % new_width) << 1 denominator = (new_width) << 1 # Initial pixel index = (width - new_width) / denominator err = (width - new_width) % denominator for i in 1..new_width indicies[i-1] = index residues[i-1] = (255.0 * err.to_f / denominator.to_f).round index += base_step err += err_step if err >= denominator index += 1 err -= denominator end end return indicies, residues end # Resamples the canvas using nearest neighbor interpolation. # @param [Integer] new_width The width of the resampled canvas. # @param [Integer] new_height The height of the resampled canvas. # @return [ChunkyPNG::Canvas] A new canvas instance with the resampled pixels. def resample_nearest_neighbor!(new_width, new_height) steps_x = steps(width, new_width) steps_y = steps(height, new_height) pixels = Array(size=new_width*new_height) i = 0 for y in steps_y for x in steps_x pixels[i] = get_pixel(x, y) i += 1 end end replace_canvas!(new_width.to_i, new_height.to_i, pixels) end def resample_nearest_neighbor(new_width, new_height) dup.resample_nearest_neighbor!(new_width, new_height) end # Resamples the canvas with bilinear interpolation. # @param [Integer] new_width The width of the resampled canvas. # @param [Integer] new_height The height of the resampled canvas. # @return [ChunkyPNG::Canvas] A new canvas instance with the resampled pixels. def resample_bilinear!(new_width, new_height) index_x, interp_x = steps_residues(width, new_width) index_y, interp_y = steps_residues(height, new_height) pixels = Array(size=new_width*new_height) i = 0 for y in 1..new_height # Clamp the indicies to the edges of the image y1 = [index_y[y-1], 0].max y2 = [index_y[y-1] + 1, height - 1].min y_residue = interp_y[y-1] for x in 1..new_width # Clamp the indicies to the edges of the image x1 = [index_x[x-1], 0].max x2 = [index_x[x-1] + 1, width - 1].min x_residue = interp_x[x-1] pixel_11 = get_pixel(x1, y1) pixel_21 = get_pixel(x2, y1) pixel_12 = get_pixel(x1, y2) pixel_22 = get_pixel(x2, y2) # Interpolate by Row pixel_top = ChunkyPNG::Color.interpolate_quick(pixel_21, pixel_11, x_residue) pixel_bot = ChunkyPNG::Color.interpolate_quick(pixel_22, pixel_12, x_residue) # Interpolate by Column pixels[i] = ChunkyPNG::Color.interpolate_quick(pixel_bot, pixel_top, y_residue) i += 1 end end replace_canvas!(new_width.to_i, new_height.to_i, pixels) end def resample_bilinear(new_width, new_height) dup.resample_bilinear!(new_width, new_height) end alias_method :resample, :resample_nearest_neighbor alias_method :resize, :resample end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/stream_exporting.rb000066400000000000000000000040601212556323000261360ustar00rootroot00000000000000module ChunkyPNG class Canvas # Methods to save load a canvas from to stream, encoded in RGB, RGBA, BGR or ABGR format. module StreamExporting # Creates an RGB-formatted pixelstream with the pixel data from this canvas. # # Note that this format is fast but bloated, because no compression is used # and the internal representation is left intact. To reconstruct the # canvas, the width and height should be known. # # @return [String] The RGBA-formatted pixel data. def to_rgba_stream pixels.pack('N*') end # Creates an RGB-formatted pixelstream with the pixel data from this canvas. # # Note that this format is fast but bloated, because no compression is used # and the internal representation is almost left intact. To reconstruct # the canvas, the width and height should be known. # # @return [String] The RGB-formatted pixel data. def to_rgb_stream pixels.pack('NX' * pixels.length) end # Creates a stream of the alpha channel of this canvas. # # @return [String] The 0-255 alpha values of all pixels packed as string def to_alpha_channel_stream pixels.pack('C*') end # Creates a grayscale stream of this canvas. # # This method assume sthat this image is fully grayscale, i.e. R = G = B for # every pixel. The alpha channel will not be included in the stream. # # @return [String] The 0-255 grayscale values of all pixels packed as string. def to_grayscale_stream pixels.pack('nX' * pixels.length) end # Creates an ABGR-formatted pixelstream with the pixel data from this canvas. # # Note that this format is fast but bloated, because no compression is used # and the internal representation is left intact. To reconstruct the # canvas, the width and height should be known. # # @return [String] The RGBA-formatted pixel data. def to_abgr_stream pixels.pack('V*') end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/canvas/stream_importing.rb000066400000000000000000000076751212556323000261460ustar00rootroot00000000000000module ChunkyPNG class Canvas # Methods to quickly load a canvas from a stream, encoded in RGB, RGBA, BGR or ABGR format. module StreamImporting # Creates a canvas by reading pixels from an RGB formatted stream with a # provided with and height. # # Every pixel should be represented by 3 bytes in the stream, in the correct # RGB order. This format closely resembles the internal representation of a # canvas object, so this kind of stream can be read extremely quickly. # # @param [Integer] width The width of the new canvas. # @param [Integer] height The height of the new canvas. # @param [#read, String] stream The stream to read the pixel data from. # @return [ChunkyPNG::Canvas] The newly constructed canvas instance. def from_rgb_stream(width, height, stream) string = stream.respond_to?(:read) ? stream.read(3 * width * height) : stream.to_s[0, 3 * width * height] string << ChunkyPNG::EXTRA_BYTE # Add a fourth byte to the last RGB triple. unpacker = 'NX' * (width * height) pixels = string.unpack(unpacker).map { |color| color | 0x000000ff } self.new(width, height, pixels) end # Creates a canvas by reading pixels from an RGBA formatted stream with a # provided with and height. # # Every pixel should be represented by 4 bytes in the stream, in the correct # RGBA order. This format is exactly like the internal representation of a # canvas object, so this kind of stream can be read extremely quickly. # # @param [Integer] width The width of the new canvas. # @param [Integer] height The height of the new canvas. # @param [#read, String] stream The stream to read the pixel data from. # @return [ChunkyPNG::Canvas] The newly constructed canvas instance. def from_rgba_stream(width, height, stream) string = stream.respond_to?(:read) ? stream.read(4 * width * height) : stream.to_s[0, 4 * width * height] self.new(width, height, string.unpack("N*")) end # Creates a canvas by reading pixels from an BGR formatted stream with a # provided with and height. # # Every pixel should be represented by 3 bytes in the stream, in the correct # BGR order. This format closely resembles the internal representation of a # canvas object, so this kind of stream can be read extremely quickly. # # @param [Integer] width The width of the new canvas. # @param [Integer] height The height of the new canvas. # @param [#read, String] stream The stream to read the pixel data from. # @return [ChunkyPNG::Canvas] The newly constructed canvas instance. def from_bgr_stream(width, height, stream) string = ChunkyPNG::EXTRA_BYTE # Add a first byte to the first BGR triple. string << stream.respond_to?(:read) ? stream.read(3 * width * height) : stream.to_s[0, 3 * width * height] pixels = string.unpack("@1" << ('XV' * (width * height))).map { |color| color | 0x000000ff } self.new(width, height, pixels) end # Creates a canvas by reading pixels from an ARGB formatted stream with a # provided with and height. # # Every pixel should be represented by 4 bytes in the stream, in the correct # ARGB order. This format is almost like the internal representation of a # canvas object, so this kind of stream can be read extremely quickly. # # @param [Integer] width The width of the new canvas. # @param [Integer] height The height of the new canvas. # @param [#read, String] stream The stream to read the pixel data from. # @return [ChunkyPNG::Canvas] The newly constructed canvas instance. def from_abgr_stream(width, height, stream) string = stream.respond_to?(:read) ? stream.read(4 * width * height) : stream.to_s[0, 4 * width * height] self.new(width, height, string.unpack("V*")) end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/chunk.rb000066400000000000000000000307511212556323000224070ustar00rootroot00000000000000module 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. # # 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. # @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. 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 # 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 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. 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 = {}) 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. 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 ExpectationFailed, 'The IEND chunk should be empty!' if content.bytesize > 0 self.new end # Returns an empty string, because this chunk should always be empty. # @return [""] An empty string. def content ChunkyPNG::Datastream.empty_bytearray 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 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. # # 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). # # @return [Array] 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). # # @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) } 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). # # @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 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 end 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('zTXt') @keyword, @value = keyword, value end def self.read(type, content) 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. # # @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, 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 end chunky_png-chunky_png-1.2.8/lib/chunky_png/color.rb000066400000000000000000000711441212556323000224160ustar00rootroot00000000000000module ChunkyPNG # Factory method to return a color value, based on the arguments given. # # @overload Color(r, g, b, a) # @param (see ChunkyPNG::Color.rgba) # @return [Integer] The rgba color value. # # @overload Color(r, g, b) # @param (see ChunkyPNG::Color.rgb) # @return [Integer] The rgb color value. # # @overload Color(hex_value, opacity = nil) # @param (see ChunkyPNG::Color.from_hex) # @return [Integer] The hex color value, with the opacity applied if one was given. # # @overload Color(color_name, opacity = nil) # @param (see ChunkyPNG::Color.html_color) # @return [Integer] The hex color value, with the opacity applied if one was given. # # @overload Color(color_value, opacity = nil) # @param [Integer, :to_i] The color value. # @return [Integer] The color value, with the opacity applied if one was given. # # @return [Integer] The determined color value as RGBA integer. # @raise [ArgumentError] if the arguments weren't understood as a color. # @see ChunkyPNG::Color # @see ChunkyPNG::Color.parse def self.Color(*args) case args.length when 1; ChunkyPNG::Color.parse(args.first) when 2; (ChunkyPNG::Color.parse(args.first) & 0xffffff00) | args[1].to_i when 3; ChunkyPNG::Color.rgb(*args) when 4; ChunkyPNG::Color.rgba(*args) else raise ArgumentError, "Don't know how to create a color from #{args.inspect}!" end end # The Color module defines methods for handling colors. Within the ChunkyPNG # library, the concepts of pixels and colors are both used, and they are # both represented by a Integer. # # Pixels/colors are represented in RGBA components. Each of the four # components is stored with a depth of 8 bits (maximum value = 255 = # {ChunkyPNG::Color::MAX}). Together, these components are stored in a 4-byte # Integer. # # A color will always be represented using these 4 components in memory. # When the image is encoded, a more suitable representation can be used # (e.g. rgb, grayscale, palette-based), for which several conversion methods # are provided in this module. module Color extend self # @return [Integer] The maximum value of each color component. MAX = 0xff # @private # @return [Regexp] The regexp to parse hex color values. HEX_COLOR_REGEXP = /^(?:#|0x)?([0-9a-f]{6})([0-9a-f]{2})?$/i # @private # @return [Regexp] The regexp to parse named color values. HTML_COLOR_REGEXP = /^([a-z][a-z_ ]+[a-z])(?:\ ?\@\ ?(1\.0|0\.\d+))?$/i #################################################################### # CONSTRUCTING COLOR VALUES #################################################################### # Parses a color value given a numeric or string argument. # # It supports color numbers, colors in hex notation and named HTML colors. # # @param [Integer, String] The color value. # @return [Integer] The color value, with the opacity applied if one was given. def parse(source) return source if source.kind_of?(Integer) case source.to_s when /^\d+$/; source.to_s.to_i when ChunkyPNG::Color::HEX_COLOR_REGEXP; ChunkyPNG::Color.from_hex(source.to_s) when ChunkyPNG::Color::HTML_COLOR_REGEXP; ChunkyPNG::Color.html_color(source.to_s) else raise ArgumentError, "Don't know how to create a color from #{source.inspect}!" end end # Creates a new color using an r, g, b triple and an alpha value. # @param [Integer] r The r-component (0-255) # @param [Integer] g The g-component (0-255) # @param [Integer] b The b-component (0-255) # @param [Integer] a The opacity (0-255) # @return [Integer] The newly constructed color value. def rgba(r, g, b, a) r << 24 | g << 16 | b << 8 | a end # Creates a new color using an r, g, b triple. # @param [Integer] r The r-component (0-255) # @param [Integer] g The g-component (0-255) # @param [Integer] b The b-component (0-255) # @return [Integer] The newly constructed color value. def rgb(r, g, b) r << 24 | g << 16 | b << 8 | 0xff end # Creates a new color using a grayscale teint. # @param [Integer] teint The grayscale teint (0-255), will be used as r, g, and b value. # @return [Integer] The newly constructed color value. def grayscale(teint) teint << 24 | teint << 16 | teint << 8 | 0xff end # Creates a new color using a grayscale teint and alpha value. # @param [Integer] teint The grayscale teint (0-255), will be used as r, g, and b value. # @param [Integer] a The opacity (0-255) # @return [Integer] The newly constructed color value. def grayscale_alpha(teint, a) teint << 24 | teint << 16 | teint << 8 | a end #################################################################### # COLOR IMPORTING #################################################################### # Creates a color by unpacking an rgb triple from a string. # # @param [String] stream The string to load the color from. It should be # at least 3 + pos bytes long. # @param [Integer] pos The position in the string to load the triple from. # @return [Integer] The newly constructed color value. def from_rgb_stream(stream, pos = 0) rgb(*stream.unpack("@#{pos}C3")) end # Creates a color by unpacking an rgba triple from a string # # @param [String] stream The string to load the color from. It should be # at least 4 + pos bytes long. # @param [Integer] pos The position in the string to load the triple from. # @return [Integer] The newly constructed color value. def from_rgba_stream(stream, pos = 0) rgba(*stream.unpack("@#{pos}C4")) end # Creates a color by converting it from a string in hex notation. # # It supports colors with (#rrggbbaa) or without (#rrggbb) alpha channel. # Color strings may include the prefix "0x" or "#". # # @param [String] str The color in hex notation. @return [Integer] The # converted color value. # @param [Integer] opacity The opacity value for the color. Overrides any # opacity value given in the hex value if given. # @return [Integer] The color value. # @raise [ArgumentError] if the value given is not a hex color notation. def from_hex(hex_value, opacity = nil) if HEX_COLOR_REGEXP =~ hex_value base_color = $1.hex << 8 opacity ||= $2 ? $2.hex : 0xff base_color | opacity else raise ArgumentError, "Not a valid hex color notation: #{hex_value.inspect}!" end end #################################################################### # PROPERTIES #################################################################### # Returns the red-component from the color value. # # @param [Integer] value The color value. # @return [Integer] A value between 0 and MAX. def r(value) (value & 0xff000000) >> 24 end # Returns the green-component from the color value. # # @param [Integer] value The color value. # @return [Integer] A value between 0 and MAX. def g(value) (value & 0x00ff0000) >> 16 end # Returns the blue-component from the color value. # # @param [Integer] value The color value. # @return [Integer] A value between 0 and MAX. def b(value) (value & 0x0000ff00) >> 8 end # Returns the alpha channel value for the color value. # # @param [Integer] value The color value. # @return [Integer] A value between 0 and MAX. def a(value) value & 0x000000ff end # Returns true if this color is fully opaque. # # @param [Integer] value The color to test. # @return [true, false] True if the alpha channel equals MAX. def opaque?(value) a(value) == 0x000000ff end # Returns the opaque value of this color by removing the alpha channel. # @param [Integer] value The color to transform. # @return [Integer] The opaque color def opaque!(value) value | 0x000000ff end # Returns true if this color is fully transparent. # # @param [Integer] value The color to test. # @return [true, false] True if the r, g and b component are equal. def grayscale?(value) r(value) == b(value) && b(value) == g(value) end # Returns true if this color is fully transparent. # # @param [Integer] value The color to test. # @return [true, false] True if the alpha channel equals 0. def fully_transparent?(value) a(value) == 0x00000000 end #################################################################### # ALPHA COMPOSITION #################################################################### # Multiplies two fractions using integer math, where the fractions are stored using an # integer between 0 and 255. This method is used as a helper method for compositing # colors using integer math. # # This is a quicker implementation of ((a * b) / 255.0).round. # # @param [Integer] a The first fraction. # @param [Integer] b The second fraction. # @return [Integer] The result of the multiplication. def int8_mult(a, b) t = a * b + 0x80 ((t >> 8) + t) >> 8 end # Composes two colors with an alpha channel using integer math. # # This version is faster than the version based on floating point math, so this # compositing function is used by default. # # @param [Integer] fg The foreground color. # @param [Integer] bg The foreground color. # @return [Integer] The composited color. # @see ChunkyPNG::Color#compose_precise def compose_quick(fg, bg) return fg if opaque?(fg) || fully_transparent?(bg) return bg if fully_transparent?(fg) a_com = int8_mult(0xff - a(fg), a(bg)) new_r = int8_mult(a(fg), r(fg)) + int8_mult(a_com, r(bg)) new_g = int8_mult(a(fg), g(fg)) + int8_mult(a_com, g(bg)) new_b = int8_mult(a(fg), b(fg)) + int8_mult(a_com, b(bg)) new_a = a(fg) + a_com rgba(new_r, new_g, new_b, new_a) end # Composes two colors with an alpha channel using floating point math. # # This method uses more precise floating point math, but this precision is lost # when the result is converted back to an integer. Because it is slower than # the version based on integer math, that version is preferred. # # @param [Integer] fg The foreground color. # @param [Integer] bg The foreground color. # @return [Integer] The composited color. # @see ChunkyPNG::Color#compose_quick def compose_precise(fg, bg) return fg if opaque?(fg) || fully_transparent?(bg) return bg if fully_transparent?(fg) fg_a = a(fg).to_f / MAX bg_a = a(bg).to_f / MAX a_com = (1.0 - fg_a) * bg_a new_r = (fg_a * r(fg) + a_com * r(bg)).round new_g = (fg_a * g(fg) + a_com * g(bg)).round new_b = (fg_a * b(fg) + a_com * b(bg)).round new_a = ((fg_a + a_com) * MAX).round rgba(new_r, new_g, new_b, new_a) end alias :compose :compose_quick # Blends the foreground and background color by taking the average of # the components. # # @param [Integer] fg The foreground color. # @param [Integer] bg The foreground color. # @return [Integer] The blended color. def blend(fg, bg) (fg + bg) >> 1 end # Interpolates the foreground and background colors by the given alpha value. # This also blends the alpha channels themselves. # # A blending factor of 255 will give entirely the foreground, # while a blending factor of 0 will give the background. # # @param [Integer] fg The foreground color. # @param [Integer] bg The background color. # @param [Integer] alpha The blending factor (fixed 8bit) # @param [Integer] The interpolated color. def interpolate_quick(fg, bg, alpha) return fg if alpha >= 255 return bg if alpha <= 0 alpha_com = 255 - alpha new_r = int8_mult(alpha, r(fg)) + int8_mult(alpha_com, r(bg)) new_g = int8_mult(alpha, g(fg)) + int8_mult(alpha_com, g(bg)) new_b = int8_mult(alpha, b(fg)) + int8_mult(alpha_com, b(bg)) new_a = int8_mult(alpha, a(fg)) + int8_mult(alpha_com, a(bg)) return rgba(new_r, new_g, new_b, new_a) end # Calculates the grayscale teint of an RGB color. # # @param [Integer] color The color to convert. # @return [Integer] The grayscale teint of the input color, 0-255. def grayscale_teint(color) (r(color) * 0.3 + g(color) * 0.59 + b(color) * 0.11).round end # Converts a color to a fiting grayscale value. It will conserve the alpha # channel. # # This method will return a full color value, with the R, G, and B value set # to the grayscale teint calcuated from the input color's R, G and B values. # # @param [Integer] color The color to convert. # @return [Integer] The input color, converted to the best fitting grayscale. # @see #grayscale_teint def to_grayscale(color) grayscale_alpha(grayscale_teint(color), a(color)) end # Lowers the intensity of a color, by lowering its alpha by a given factor. # @param [Integer] color The color to adjust. # @param [Integer] factor Fade factor as an integer between 0 and 255. # @return [Integer] The faded color. def fade(color, factor) new_alpha = int8_mult(a(color), factor) (color & 0xffffff00) | new_alpha end # Decomposes a color, given a color, a mask color and a background color. # The returned color will be a variant of the mask color, with the alpha # channel set to the best fitting value. This basically is the reverse # operation if alpha composition. # # If the color cannot be decomposed, this method will return the fully # transparent variant of the mask color. # # @param [Integer] color The color that was the result of compositing. # @param [Integer] mask The opaque variant of the color that was being composed # @param [Integer] bg The background color on which the color was composed. # @param [Integer] tolerance The decomposition tolerance level, a value between 0 and 255. # @return [Integer] The decomposed color,a variant of the masked color with the # alpha channel set to an appropriate value. def decompose_color(color, mask, bg, tolerance = 1) if alpha_decomposable?(color, mask, bg, tolerance) mask & 0xffffff00 | decompose_alpha(color, mask, bg) else mask & 0xffffff00 end end # Checks whether an alpha channel value can successfully be composed # given the resulting color, the mask color and a background color, # all of which should be opaque. # # @param [Integer] color The color that was the result of compositing. # @param [Integer] mask The opaque variant of the color that was being composed # @param [Integer] bg The background color on which the color was composed. # @param [Integer] tolerance The decomposition tolerance level, a value between 0 and 255. # @return [Boolean] True if the alpha component can be decomposed successfully. # @see #decompose_alpha def alpha_decomposable?(color, mask, bg, tolerance = 1) components = decompose_alpha_components(color, mask, bg) sum = components.inject(0) { |a,b| a + b } max = components.max * 3 return components.max <= 255 && components.min >= 0 && (sum + tolerance * 3) >= max end # Decomposes the alpha channel value given the resulting color, the mask color # and a background color, all of which should be opaque. # # Make sure to call {#alpha_decomposable?} first to see if the alpha channel # value can successfully decomposed with a given tolerance, otherwise the return # value of this method is undefined. # # @param [Integer] color The color that was the result of compositing. # @param [Integer] mask The opaque variant of the color that was being composed # @param [Integer] bg The background color on which the color was composed. # @return [Integer] The best fitting alpha channel, a value between 0 and 255. # @see #alpha_decomposable? def decompose_alpha(color, mask, bg) components = decompose_alpha_components(color, mask, bg) (components.inject(0) { |a,b| a + b } / 3.0).round end # Decomposes an alpha channel for either the r, g or b color channel. # @param [:r, :g, :b] channel The channel to decompose the alpha channel from. # @param [Integer] color The color that was the result of compositing. # @param [Integer] mask The opaque variant of the color that was being composed # @param [Integer] bg The background color on which the color was composed. # @return [Integer] The decomposed alpha value for the channel. def decompose_alpha_component(channel, color, mask, bg) cc, mc, bc = send(channel, color), send(channel, mask), send(channel, bg) return 0x00 if bc == cc return 0xff if bc == mc return 0xff if cc == mc (((bc - cc).to_f / (bc - mc).to_f) * MAX).round end # Decomposes the alpha channels for the r, g and b color channel. # @param [Integer] color The color that was the result of compositing. # @param [Integer] mask The opaque variant of the color that was being composed # @param [Integer] bg The background color on which the color was composed. # @return [Array] The decomposed alpha values for the r, g and b channels. def decompose_alpha_components(color, mask, bg) [ decompose_alpha_component(:r, color, mask, bg), decompose_alpha_component(:g, color, mask, bg), decompose_alpha_component(:b, color, mask, bg) ] end #################################################################### # CONVERSIONS #################################################################### # Returns a string representing this color using hex notation (i.e. #rrggbbaa). # # @param [Integer] value The color to convert. # @return [String] The color in hex notation, starting with a pound sign. def to_hex(color, include_alpha = true) include_alpha ? ('#%08x' % color) : ('#%06x' % [color >> 8]) end # Returns an array with the separate RGBA values for this color. # # @param [Integer] color The color to convert. # @return [Array] An array with 4 Integer elements. def to_truecolor_alpha_bytes(color) [r(color), g(color), b(color), a(color)] end # Returns an array with the separate RGB values for this color. # The alpha channel will be discarded. # # @param [Integer] color The color to convert. # @return [Array] An array with 3 Integer elements. def to_truecolor_bytes(color) [r(color), g(color), b(color)] end # Returns an array with the grayscale teint value for this color. # # This method expects the r,g and b value to be equal, and the alpha # channel will be discarded. # # @param [Integer] color The grayscale color to convert. # @return [Array] An array with 1 Integer element. def to_grayscale_bytes(color) [b(color)] # assumption r == g == b end # Returns an array with the grayscale teint and alpha channel values # for this color. # # This method expects the color to be grayscale, i.e. r,g and b value # to be equal and uses only the B channel. If you need to convert a # color to grayscale first, see {#to_grayscale}. # # @param [Integer] color The grayscale color to convert. # @return [Array] An array with 2 Integer elements. # @see #to_grascale def to_grayscale_alpha_bytes(color) [b(color), a(color)] # assumption r == g == b end #################################################################### # COLOR CONSTANTS #################################################################### # @return [Hash] All the predefined color names in HTML. PREDEFINED_COLORS = { :aliceblue => 0xf0f8ff00, :antiquewhite => 0xfaebd700, :aqua => 0x00ffff00, :aquamarine => 0x7fffd400, :azure => 0xf0ffff00, :beige => 0xf5f5dc00, :bisque => 0xffe4c400, :black => 0x00000000, :blanchedalmond => 0xffebcd00, :blue => 0x0000ff00, :blueviolet => 0x8a2be200, :brown => 0xa52a2a00, :burlywood => 0xdeb88700, :cadetblue => 0x5f9ea000, :chartreuse => 0x7fff0000, :chocolate => 0xd2691e00, :coral => 0xff7f5000, :cornflowerblue => 0x6495ed00, :cornsilk => 0xfff8dc00, :crimson => 0xdc143c00, :cyan => 0x00ffff00, :darkblue => 0x00008b00, :darkcyan => 0x008b8b00, :darkgoldenrod => 0xb8860b00, :darkgray => 0xa9a9a900, :darkgrey => 0xa9a9a900, :darkgreen => 0x00640000, :darkkhaki => 0xbdb76b00, :darkmagenta => 0x8b008b00, :darkolivegreen => 0x556b2f00, :darkorange => 0xff8c0000, :darkorchid => 0x9932cc00, :darkred => 0x8b000000, :darksalmon => 0xe9967a00, :darkseagreen => 0x8fbc8f00, :darkslateblue => 0x483d8b00, :darkslategray => 0x2f4f4f00, :darkslategrey => 0x2f4f4f00, :darkturquoise => 0x00ced100, :darkviolet => 0x9400d300, :deeppink => 0xff149300, :deepskyblue => 0x00bfff00, :dimgray => 0x69696900, :dimgrey => 0x69696900, :dodgerblue => 0x1e90ff00, :firebrick => 0xb2222200, :floralwhite => 0xfffaf000, :forestgreen => 0x228b2200, :fuchsia => 0xff00ff00, :gainsboro => 0xdcdcdc00, :ghostwhite => 0xf8f8ff00, :gold => 0xffd70000, :goldenrod => 0xdaa52000, :gray => 0x80808000, :grey => 0x80808000, :green => 0x00800000, :greenyellow => 0xadff2f00, :honeydew => 0xf0fff000, :hotpink => 0xff69b400, :indianred => 0xcd5c5c00, :indigo => 0x4b008200, :ivory => 0xfffff000, :khaki => 0xf0e68c00, :lavender => 0xe6e6fa00, :lavenderblush => 0xfff0f500, :lawngreen => 0x7cfc0000, :lemonchiffon => 0xfffacd00, :lightblue => 0xadd8e600, :lightcoral => 0xf0808000, :lightcyan => 0xe0ffff00, :lightgoldenrodyellow => 0xfafad200, :lightgray => 0xd3d3d300, :lightgrey => 0xd3d3d300, :lightgreen => 0x90ee9000, :lightpink => 0xffb6c100, :lightsalmon => 0xffa07a00, :lightseagreen => 0x20b2aa00, :lightskyblue => 0x87cefa00, :lightslategray => 0x77889900, :lightslategrey => 0x77889900, :lightsteelblue => 0xb0c4de00, :lightyellow => 0xffffe000, :lime => 0x00ff0000, :limegreen => 0x32cd3200, :linen => 0xfaf0e600, :magenta => 0xff00ff00, :maroon => 0x80000000, :mediumaquamarine => 0x66cdaa00, :mediumblue => 0x0000cd00, :mediumorchid => 0xba55d300, :mediumpurple => 0x9370d800, :mediumseagreen => 0x3cb37100, :mediumslateblue => 0x7b68ee00, :mediumspringgreen => 0x00fa9a00, :mediumturquoise => 0x48d1cc00, :mediumvioletred => 0xc7158500, :midnightblue => 0x19197000, :mintcream => 0xf5fffa00, :mistyrose => 0xffe4e100, :moccasin => 0xffe4b500, :navajowhite => 0xffdead00, :navy => 0x00008000, :oldlace => 0xfdf5e600, :olive => 0x80800000, :olivedrab => 0x6b8e2300, :orange => 0xffa50000, :orangered => 0xff450000, :orchid => 0xda70d600, :palegoldenrod => 0xeee8aa00, :palegreen => 0x98fb9800, :paleturquoise => 0xafeeee00, :palevioletred => 0xd8709300, :papayawhip => 0xffefd500, :peachpuff => 0xffdab900, :peru => 0xcd853f00, :pink => 0xffc0cb00, :plum => 0xdda0dd00, :powderblue => 0xb0e0e600, :purple => 0x80008000, :red => 0xff000000, :rosybrown => 0xbc8f8f00, :royalblue => 0x4169e100, :saddlebrown => 0x8b451300, :salmon => 0xfa807200, :sandybrown => 0xf4a46000, :seagreen => 0x2e8b5700, :seashell => 0xfff5ee00, :sienna => 0xa0522d00, :silver => 0xc0c0c000, :skyblue => 0x87ceeb00, :slateblue => 0x6a5acd00, :slategray => 0x70809000, :slategrey => 0x70809000, :snow => 0xfffafa00, :springgreen => 0x00ff7f00, :steelblue => 0x4682b400, :tan => 0xd2b48c00, :teal => 0x00808000, :thistle => 0xd8bfd800, :tomato => 0xff634700, :turquoise => 0x40e0d000, :violet => 0xee82ee00, :wheat => 0xf5deb300, :white => 0xffffff00, :whitesmoke => 0xf5f5f500, :yellow => 0xffff0000, :yellowgreen => 0x9acd3200 } # Gets a color value based on a HTML color name. # # The color name is flexible. E.g. 'yellowgreen', 'Yellow green', # 'YellowGreen', 'YELLOW_GREEN' and :yellow_green will # all return the same color value. # # You can include a opacity level in the color name (e.g. 'red @ 0.5') or give # an explicit opacity value as second argument. If no opacity value is given, the color # will be fully opaque. # # @param [Symbol, String] color_name The color name. It may include an opacity specifier # like @ 0.8 to set the color's opacity. # @param [Integer] opacity The opacity value for the color between 0 and 255. Overrides # any opacity value given in the color name. # @return [Integer] The color value. # @raise [ChunkyPNG::Exception] If the color name was not recognized. def html_color(color_name, opacity = nil) if color_name.to_s =~ HTML_COLOR_REGEXP opacity ||= $2 ? ($2.to_f * 255.0).round : 0xff base_color_name = $1.gsub(/[^a-z]+/i, '').downcase.to_sym return PREDEFINED_COLORS[base_color_name] | opacity if PREDEFINED_COLORS.has_key?(base_color_name) end raise ArgumentError, "Unknown color name #{color_name}!" end # @return [Integer] Black pixel/color BLACK = rgb( 0, 0, 0) # @return [Integer] White pixel/color WHITE = rgb(255, 255, 255) # @return [Integer] Fully transparent pixel/color TRANSPARENT = rgba(0, 0, 0, 0) #################################################################### # STATIC UTILITY METHODS #################################################################### # Returns the number of sample values per pixel. # @param [Integer] color_mode The color mode being used. # @return [Integer] The number of sample values per pixel. def samples_per_pixel(color_mode) case color_mode when ChunkyPNG::COLOR_INDEXED; 1 when ChunkyPNG::COLOR_TRUECOLOR; 3 when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; 4 when ChunkyPNG::COLOR_GRAYSCALE; 1 when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; 2 else raise ChunkyPNG::NotSupported, "Don't know the numer of samples for this colormode: #{color_mode}!" end end # Returns the size in bytes of a pixel when it is stored using a given color mode. # @param [Integer] color_mode The color mode in which the pixels are stored. # @return [Integer] The number of bytes used per pixel in a datastream. def pixel_bytesize(color_mode, depth = 8) return 1 if depth < 8 (pixel_bitsize(color_mode, depth) + 7) >> 3 end # Returns the size in bits of a pixel when it is stored using a given color mode. # @param [Integer] color_mode The color mode in which the pixels are stored. # @param [Integer] depth The color depth of the pixels. # @return [Integer] The number of bytes used per pixel in a datastream. def pixel_bitsize(color_mode, depth = 8) samples_per_pixel(color_mode) * depth end # Returns the number of bytes used per scanline. # @param [Integer] color_mode The color mode in which the pixels are stored. # @param [Integer] depth The color depth of the pixels. # @param [Integer] width The number of pixels per scanline. # @return [Integer] The number of bytes used per scanline in a datastream. def scanline_bytesize(color_mode, depth, width) ((pixel_bitsize(color_mode, depth) * width) + 7) >> 3 end # Returns the number of bytes used for an image pass # @param [Integer] color_mode The color mode in which the pixels are stored. # @param [Integer] depth The color depth of the pixels. # @param [Integer] width The width of the image pass. # @param [Integer] width The height of the image pass. # @return [Integer] The number of bytes used per scanline in a datastream. def pass_bytesize(color_mode, depth, width, height) return 0 if width == 0 || height == 0 (scanline_bytesize(color_mode, depth, width) + 1) * height end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/compatibility.rb000066400000000000000000000006071212556323000241450ustar00rootroot00000000000000# Define the byte-operators on a string if they're not defined (Ruby 1.8) class String alias_method :getbyte, :[] unless method_defined?(:getbyte) alias_method :setbyte, :[]= unless method_defined?(:setbyte) alias_method :bytesize, :size unless method_defined?(:bytesize) end module Enumerable unless method_defined?(:minmax) def minmax [min, max] end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/datastream.rb000066400000000000000000000147361212556323000234310ustar00rootroot00000000000000module ChunkyPNG # The Datastream class represents a PNG formatted datastream. It supports # both reading from and writing to strings, streams 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 = ChunkyPNG.force_binary([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] 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] attr_accessor :data_chunks # The empty chunk that signals the end of this datastream # @return [ChunkyPNG::Chunk::Header] attr_accessor :end_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; ds.header_chunk = chunk when ChunkyPNG::Chunk::Palette; ds.palette_chunk = chunk when ChunkyPNG::Chunk::Transparency; ds.transparency_chunk = chunk when ChunkyPNG::Chunk::ImageData; ds.data_chunks << chunk when ChunkyPNG::Chunk::End; ds.end_chunk = chunk else ds.other_chunks << chunk end end return ds end # 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. # # @param [IO] io The stream to read the PNG signature from. # @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) unless ChunkyPNG.force_binary(signature) == ChunkyPNG::Datastream::SIGNATURE raise ChunkyPNG::SignatureMismatch, "PNG signature not found, found #{signature.inspect} instead of #{ChunkyPNG::Datastream::SIGNATURE.inspect}!" end end end ################################################################################## # 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 [chunk] Yields the chunks in this datastream, one by one in the correct order. # @yieldparam [ChunkyPNG::Chunk::Base] chunk A chunk in this datastream. # @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 enum_for(:each_chunk) end # Returns all the textual metadata key/value pairs as hash. # @return [Hash] A hash containing metadata fields and their values. def metadata metadata = {} other_chunks.select do |chunk| metadata[chunk.keyword] = chunk.value if chunk.respond_to?(:keyword) end metadata end # Returns the uncompressed image data, combined from all the IDAT chunks # @return [String] The uncompressed image data for this datastream def imagedata ChunkyPNG::Chunk::ImageData.combine_chunks(data_chunks) end ################################################################################## # WRITING DATASTREAMS ################################################################################## # Returns an empty stream using binary encoding that can be used as stream to encode to. # @return [String] An empty, binary string. def self.empty_bytearray ChunkyPNG::EMPTY_BYTEARRAY.dup end # Writes the datastream to the given output stream. # @param [IO] io The output stream to write to. def write(io) io << SIGNATURE each_chunk { |c| c.write(io) } end # 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 # Encodes this datastream into a string. # @return [String] The encoded PNG datastream. def to_blob str = StringIO.new write(str) return str.string end alias :to_string :to_blob alias :to_s :to_blob end end chunky_png-chunky_png-1.2.8/lib/chunky_png/dimension.rb000066400000000000000000000106141212556323000232600ustar00rootroot00000000000000module ChunkyPNG # Creates a {ChunkyPNG::Dimension} instance using arguments that can be interpreted # as width and height. # # @overload Dimension(width, height) # @param [Integer] width The width-component of the dimension. # @param [Integer] height The height-component of the dimension. # @return [ChunkyPNG::Dimension] The instantiated dimension. # # @overload Dimension(string) # @param [String] string A string from which a width and height value can be parsed, e.g. # '10x20' or '[10, 20]'. # @return [ChunkyPNG::Dimension] The instantiated dimension. # # @overload Dimension(ary) # @param [Array] ary An array with the desired width as first element and the # desired height as second element, e.g. [10, 20]. # @return [ChunkyPNG::Dimension] The instantiated dimension. # # @overload Dimension(hash) # @param [Hash] hash An hash with a 'height' or :height key for the # desired height and with a 'width' or :width key for the desired # width. # @return [ChunkyPNG::Dimension] The instantiated dimension. # # @return [ChunkyPNG::Dimension] The dimension created by this factory method. # @raise [ArgumentError] If the argument(s) given where not understood as a dimension. # @see ChunkyPNG::Dimension def self.Dimension(*args) case args.length when 2; ChunkyPNG::Dimension.new(*args) when 1; case source = args.first when ChunkyPNG::Dimension; source when ChunkyPNG::Point; ChunkyPNG::Dimension.new(source.x, source.y) when Array; ChunkyPNG::Dimension.new(source[0], source[1]) when Hash; ChunkyPNG::Dimension.new(source[:width] || source['width'], source[:height] || source['height']) when ChunkyPNG::Dimension::DIMENSION_REGEXP; ChunkyPNG::Dimension.new($1, $2) else if source.respond_to?(:width) && source.respond_to?(:height) ChunkyPNG::Dimension.new(source.width, source.height) else raise ArgumentError, "Don't know how to construct a point from #{source.inspect}!" end end else raise ArgumentError, "Don't know how to construct a point from #{args.inspect}!" end end # Class that represents the dimension of something, e.g. a {ChunkyPNG::Canvas}. # # This class contains some methods to simplify performing dimension related checks. class Dimension # @return [Regexp] The regexp to parse dimensions from a string. # @private DIMENSION_REGEXP = /^[\(\[\{]?(\d+)\s*[x,]?\s*(\d+)[\)\]\}]?$/ # @return [Integer] The width-component of this dimension. attr_accessor :width # @return [Integer] The height-component of this dimension. attr_accessor :height # Initializes a new dimension instance. # @param [Integer] width The width-component of the new dimension. # @param [Integer] height The height-component of the new dimension. def initialize(width, height) @width, @height = width.to_i, height.to_i end # Returns the area of this dimension. # @return [Integer] The area in number of pixels. def area width * height end # Checks whether a point is within bounds of this dimension. # @param [ChunkyPNG::Point, ...] A point-like to bounds-check. # @return [true, false] True iff the x and y coordinate fall in this dimension. # @see ChunkyPNG.Point def include?(*point_like) point = ChunkyPNG::Point(*point_like) point.x >= 0 && point.x < width && point.y >= 0 && point.y < height end # Checks whether 2 dimensions are identical. # @param [ChunkyPNG::Dimension] The dimension to compare with. # @return [true, false] true iff width and height match. def eql?(other) other.width == width && other.height == height end alias_method :==, :eql? # Compares the size of 2 dimensions. # @param [ChunkyPNG::Dimension] The dimension to compare with. # @return [-1, 0, 1] -1 if the other dimension has a larger area, 1 of this # dimension is larger, 0 if both are identical in size. def <=>(other) other.area <=> area end # Casts this dimension into an array. # @return [Array] [width, height] for this dimension. def to_a [width, height] end alias_method :to_ary, :to_a end end chunky_png-chunky_png-1.2.8/lib/chunky_png/image.rb000066400000000000000000000054071212556323000223610ustar00rootroot00000000000000module ChunkyPNG # ChunkyPNG::Image is an extension of the {ChunkyPNG::Canvas} class, that # also includes support for metadata. # # @see ChunkyPNG::Canvas class Image < Canvas # The minimum size of bytes the value of a metadata field should be before compression # is enabled for the chunk. METADATA_COMPRESSION_TRESHOLD = 300 # @return [Hash] The hash of metadata fields for this PNG image. attr_accessor :metadata # Initializes a new ChunkyPNG::Image instance. # @param [Integer] width The width of the new image. # @param [Integer] height The height of the new image. # @param [Integer] bg_color The background color of the new image. # @param [Hash] metadata A hash of metadata fields and values for this image. # @see ChunkyPNG::Canvas#initialize def initialize(width, height, bg_color = ChunkyPNG::Color::TRANSPARENT, metadata = {}) super(width, height, bg_color) @metadata = metadata end # Initializes a copy of another ChunkyPNG::Image instance. # # @param [ChunkyPNG::Image] other The other image to copy. def initialize_copy(other) super(other) @metadata = other.metadata end # Returns the metadata for this image as PNG chunks. # # Chunks will either be of the {ChunkyPNG::Chunk::Text} type for small # values (in bytes), or of the {ChunkyPNG::Chunk::CompressedText} type # for values that are larger in size. # # @return [Array] An array of metadata chunks. # @see ChunkyPNG::Image::METADATA_COMPRESSION_TRESHOLD def metadata_chunks metadata.map do |key, value| if value.length >= METADATA_COMPRESSION_TRESHOLD ChunkyPNG::Chunk::CompressedText.new(key, value) else ChunkyPNG::Chunk::Text.new(key, value) end end end # Encodes the image to a PNG datastream for saving to disk or writing to an IO stream. # # Besides encoding the canvas, it will also encode the metadata fields to text chunks. # # @param [Hash] constraints The constraints to use when encoding the canvas. # @return [ChunkyPNG::Datastream] The datastream that contains this image. # @see ChunkyPNG::Canvas::PNGEncoding#to_datastream # @see #metadata_chunks def to_datastream(constraints = {}) ds = super(constraints) ds.other_chunks += metadata_chunks return ds end # Reads a ChunkyPNG::Image instance from a data stream. # # Besides decoding the canvas, this will also read the metadata fields # from the datastream. # # @param [ChunkyPNG::Datastream] The datastream to read from. def self.from_datastream(ds) image = super(ds) image.metadata = ds.metadata return image end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/palette.rb000066400000000000000000000170071212556323000227340ustar00rootroot00000000000000module ChunkyPNG # 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 ChunkyPNG::Color class Palette < SortedSet # Builds a new palette given a set (Enumerable instance) of colors. # # @param [Enumerable] enum The set of colors to include in this palette. # This Enumerable can contains duplicates. # @param [Array] decoding_map An array of colors in the exact order at which # they appeared in the palette chunk, so that this array can be used for decoding. def initialize(enum, decoding_map = nil) super(enum) @decoding_map = decoding_map if decoding_map end # 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. # # @param [ChunkyPNG::Chunk::Palette] The palette chunk to load from # @param [ChunkyPNG::Chunk::Transparency, nil] The optional transparency chunk. # @return [ChunkyPNG::Palette] The loaded palette instance. # @see ChunkyPNG::Palette#can_decode? 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 self.new(decoding_map, decoding_map) end # Builds a palette instance from a given canvas. # @param [ChunkyPNG::Canvas] canvas The canvas to create a palette for. # @return [ChunkyPNG::Palette] The palette instance. def self.from_canvas(canvas) self.new(canvas.pixels) end # Builds a palette instance from a given set of pixels. # @param [Enumerable] pixels An enumeration of pixels to create a palette for # @return [ChunkyPNG::Palette] The palette instance. def self.from_pixels(pixels) self.new(pixels) end # Checks whether the size of this palette is suitable for indexed storage. # @return [true, false] True if the number of colors in this palette is at most 256. def indexable? size <= 256 end # Check whether this palette only contains opaque colors. # @return [true, false] True if all colors in this palette are opaque. # @see ChunkyPNG::Color#opaque? def opaque? all? { |color| Color.opaque?(color) } end # Check whether this palette only contains grayscale colors. # @return [true, false] True if all colors in this palette are grayscale teints. # @see ChunkyPNG::Color#grayscale?? def grayscale? all? { |color| Color.grayscale?(color) } end # Check whether this palette only contains bacl and white. # @return [true, false] True if all colors in this palette are grayscale teints. # @see ChunkyPNG::Color#grayscale?? def black_and_white? entries == [ChunkyPNG::Color::BLACK, ChunkyPNG::Color::WHITE] end # Returns a palette with all the opaque variants of the colors in this palette. # @return [ChunkyPNG::Palette] A new Palette instance with only opaque colors. # @see ChunkyPNG::Color#opaque! def opaque_palette self.class.new(map { |c| ChunkyPNG::Color.opaque!(c) }) end # 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. # # @return [true, false] True if a decoding map was built when this palette was loaded. def can_decode? !@decoding_map.nil? end # 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. # # @return [true, false] True if a encoding map was built when this palette was loaded. def can_encode? !@encoding_map.nil? end # Returns a color, given the position in the original palette chunk. # @param [Integer] index The 0-based position of the color in the palette. # @return [ChunkyPNG::Color] The color that is stored in the palette under the given index # @see ChunkyPNG::Palette#can_decode? def [](index) @decoding_map[index] end # Returns the position of a color in the palette # @param [ChunkyPNG::Color] color The color for which to look up the index. # @return [Integer] The 0-based position of the color in the palette. # @see ChunkyPNG::Palette#can_encode? def index(color) color.nil? ? 0 : @encoding_map[color] end # 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. # # @return [ChunkyPNG::Chunk::Transparency] The tRNS chunk. def to_trns_chunk ChunkyPNG::Chunk::Transparency.new('tRNS', map { |c| ChunkyPNG::Color.a(c) }.pack('C*')) end # Creates a PLTE chunk that corresponds with this palette to store the # r, g and b channels of all colors. # # Note that 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. # # @return [ChunkyPNG::Chunk::Palette] The PLTE chunk. # @see ChunkyPNG::Palette#can_encode? 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 # Determines the most suitable colormode for this palette. # @return [Integer] The colormode which would create the smallest possible # file for images that use this exact palette. 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 # Determines the minimal bit depth required for an indexed image # @return [Integer] Number of bits per pixel, i.e. 1, 2, 4 or 8, or nil if this # image cannot be saved as an indexed image. def determine_bit_depth case size when 1..2; 1 when 3..4; 2 when 5..16; 4 when 17..256; 8 else nil end end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/point.rb000066400000000000000000000106271212556323000224300ustar00rootroot00000000000000module ChunkyPNG # Factory method to create {ChunkyPNG::Point} instances. # # This method tries to be as flexible as possible with regards to the given input: besides # explicit coordinates, this method also accepts arrays, hashes, strings, {ChunkyPNG::Dimension} # instances and anything that responds to :x and :y. # # @overload Point(x, y) # @param [Integer, :to_i] x The x-coordinate # @param [Integer, :to_i] y The y-coordinate # @return [ChunkyPNG::Point] The instantiated point. # # @overload Point(array) # @param [Array] array A two element array which represent the x- and y-coordinate. # @return [ChunkyPNG::Point] The instantiated point. # # @overload Point(hash) # @param [Hash] array A hash with the :x or 'x' and :y or # 'y' keys set, which will be used as coordinates. # @return [ChunkyPNG::Point] The instantiated point. # # @overload Point(string) # @param [String] string A string that contains the coordinates, e.g. '0, 4', # '(0 4)', [0,4}', etc. # @return [ChunkyPNG::Point] The instantiated point. # # @return [ChunkyPNG::Point] # @raise [ArgumentError] if the arguments weren't understood. # @see ChunkyPNG::Point def self.Point(*args) case args.length when 2; ChunkyPNG::Point.new(*args) when 1; case source = args.first when ChunkyPNG::Point; source when ChunkyPNG::Dimension; ChunkyPNG::Point.new(source.width, source.height) when Array; ChunkyPNG::Point.new(source[0], source[1]) when Hash; ChunkyPNG::Point.new(source[:x] || source['x'], source[:y] || source['y']) when ChunkyPNG::Point::POINT_REGEXP; ChunkyPNG::Point.new($1.to_i, $2.to_i) else if source.respond_to?(:x) && source.respond_to?(:y) ChunkyPNG::Point.new(source.x, source.y) else raise ArgumentError, "Don't know how to construct a point from #{source.inspect}!" end end else raise ArgumentError, "Don't know how to construct a point from #{args.inspect}!" end end # Simple class that represents a point on a canvas using an x and y coordinate. # # This class implements some basic methods to handle comparison, the splat operator and # bounds checking that make it easier to work with coordinates. # # @see ChunkyPNG.Point class Point # @return [Regexp] The regexp to parse points from a string. # @private POINT_REGEXP = /^[\(\[\{]?(\d+)\s*[,]?\s*(\d+)[\)\]\}]?$/ # @return [Integer] The x-coordinate of the point. attr_accessor :x # @return [Integer] The y-coordinate of the point. attr_accessor :y # Initializes a new point instance. # @param [Integer, :to_i] x The x-coordinate. # @param [Integer, :to_i] y The y-coordinate. def initialize(x, y) @x, @y = x.to_i, y.to_i end # Checks whether 2 points are identical. # @return [true, false] true iff the x and y coordinates match def eql?(other) other.x == x && other.y == y end alias_method :==, :eql? # Compares 2 points. # # It will first compare the y coordinate, and it only takes the x-coordinate into # account if the y-coordinates of the points are identical. This way, an array of # points will be sorted into the order in which they would occur in the pixels # array returned by {ChunkyPNG::Canvas#pixels}. # # @param [ChunkyPNG::Point] other The point to compare this point with. # @return [-1, 0, 1] -1 If this point comes before the other one, 1 # if after, and 0 if the points are identical. def <=>(other) ((y <=> other.y) == 0) ? x <=> other.x : y <=> other.y end # Converts the point instance to an array. # @return [Array] A 2-element array, i.e. [x, y]. def to_a [x, y] end alias_method :to_ary, :to_a # Checks whether the point falls into a dimension # @param [ChunkyPNG::Dimension, ...] dimension_like The dimension of which the bounds # should be taken for the check. # @return [true, false] true iff the x and y coordinate fall width the width # and height of the dimension. def within_bounds?(*dimension_like) ChunkyPNG::Dimension(*dimension_like).include?(self) end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/rmagick.rb000066400000000000000000000025751212556323000227170ustar00rootroot00000000000000require 'RMagick' module ChunkyPNG # Methods for importing and exporting RMagick image objects. # # By default, this module is disabled because of the dependency on RMagick. # You need to include this module yourself if you want to use it. # # @example # # require 'rmagick' # require 'chunky_png/rmagick' # # canvas = ChunkyPNG::Canvas.from_file('filename.png') # image = ChunkyPNG::RMagick.export(canvas) # # # do something with the image using RMagick # # updated_canvas = ChunkyPNG::RMagick.import(image) # module RMagick extend self # Imports an RMagick image as Canvas object. # @param [Magick::Image] image The image to import # @return [ChunkyPNG::Canvas] The canvas, constructed from the RMagick image. def import(image) pixels = image.export_pixels_to_str(0, 0, image.columns, image.rows, 'RGBA') ChunkyPNG::Canvas.from_rgba_stream(image.columns, image.rows, pixels) end # Exports a Canvas as RMagick image instance. # @param [ChunkyPNG::Canvas] canvas The canvas to export. # @return [Magick::Image] The RMagick image constructed from the Canvas instance. def export(canvas) image = Magick::Image.new(canvas.width, canvas.height) image.import_pixels(0,0, canvas.width, canvas.height, 'RGBA', canvas.pixels.pack('N*')) image end end end chunky_png-chunky_png-1.2.8/lib/chunky_png/vector.rb000066400000000000000000000154631212556323000226040ustar00rootroot00000000000000module ChunkyPNG # Factory method for {ChunkyPNG::Vector} instances. # # @overload Vector(x0, y0, x1, y1, x2, y2, ...) # Creates a vector by parsing two subsequent values in the argument list # as x- and y-coordinate of a point. # @return [ChunkyPNG::Vector] The instantiated vector. # @overload Vector(string) # Creates a vector by parsing coordinates from the input string. # @return [ChunkyPNG::Vector] The instantiated vector. # @overload Vector(pointlike, pointlike, pointlike, ...) # Creates a vector by converting every argument to a point using {ChunkyPNG.Point}. # @return [ChunkyPNG::Vector] The instantiated vector. # # @return [ChunkyPNG::Vector] The vector created by this factory method. # @raise [ArgumentError] If the given arguments could not be understood as a vector. # @see ChunkyPNG::Vector def self.Vector(*args) return args.first if args.length == 1 && args.first.kind_of?(ChunkyPNG::Vector) if args.length == 1 && args.first.respond_to?(:scan) ChunkyPNG::Vector.new(ChunkyPNG::Vector.multiple_from_string(args.first)) # e.g. ['1,1 2,2 3,3'] else ChunkyPNG::Vector.new(ChunkyPNG::Vector.multiple_from_array(args)) # e.g. [[1,1], [2,2], [3,3]] or [1,1,2,2,3,3] end end # Class that represents a vector of points, i.e. a list of {ChunkyPNG::Point} instances. # # Vectors can be created quite flexibly. See the {ChunkyPNG.Vector} factory methods for # more information on how to construct vectors. class Vector include Enumerable # @return [Array] The array that holds all the points in this vector. attr_reader :points # Initializes a vector based on a list of Point instances. # # You usually do not want to use this method directly, but call {ChunkyPNG.Vector} instead. # # @param [Array] points # @see ChunkyPNG.Vector def initialize(points = []) @points = points end # Iterates over all the edges in this vector. # # An edge is a combination of two subsequent points in the vector. Together, they will form # a path from the first point to the last point # # @param [true, false] close Whether to close the path, i.e. return an edge that connects the last # point in the vector back to the first point. # @return [void] # @raise [ChunkyPNG::ExpectationFailed] if the vector contains less than two points. # @see #edges def each_edge(close = true) raise ChunkyPNG::ExpectationFailed, "Not enough points in this path to draw an edge!" if length < 2 points.each_cons(2) { |a, b| yield(a, b) } yield(points.last, points.first) if close end # Returns the point with the given indexof this vector. # @param [Integer] index The 0-based index of the point in this vector. # @param [ChunkyPNG::Point] The point instance. def [](index) points[index] end # Returns an enumerator that will iterate over all the edges in this vector. # @param (see #each_edge) # @return [Enumerator] The enumerator that iterates over the edges. # @raise [ChunkyPNG::ExpectationFailed] if the vector contains less than two points. # @see #each_edge def edges(close = true) to_enum(:each_edge, close) end # Returns the number of points in this vector. # @return [Integer] The length of the points array. def length points.length end # Iterates over all the points in this vector # @yield [ChunkyPNG::Point] The points in the correct order. # @return [void] def each(&block) points.each(&block) end # Comparison between two vectors for quality. # @param [ChunkyPNG::Vector] other The vector to compare with. # @return [true, false] true if the list of points are identical def eql?(other) other.points == points end alias_method :==, :eql? # Returns the range in x-coordinates for all the points in this vector. # @return [Range] The (inclusive) range of x-coordinates. def x_range Range.new(*points.map { |p| p.x }.minmax) end # Returns the range in y-coordinates for all the points in this vector. # @return [Range] The (inclusive) range of y-coordinates. def y_range Range.new(*points.map { |p| p.y }.minmax) end # Finds the lowest x-coordinate in this vector. # @return [Integer] The lowest x-coordinate of all the points in the vector. def min_x x_range.first end # Finds the highest x-coordinate in this vector. # @return [Integer] The highest x-coordinate of all the points in the vector. def max_x x_range.last end # Finds the lowest y-coordinate in this vector. # @return [Integer] The lowest y-coordinate of all the points in the vector. def min_y y_range.first end # Finds the highest y-coordinate in this vector. # @return [Integer] The highest y-coordinate of all the points in the vector. def max_y y_range.last end # Returns the offset from (0,0) of the minimal bounding box of all the # points in this vector # @return [ChunkyPNG::Point] A point that describes the top left corner if a # minimal bounding box would be drawn around all the points in the vector. def offset ChunkyPNG::Point.new(min_x, min_y) end # Returns the width of the minimal bounding box of all the points in this vector. # @return [Integer] The x-distance between the points that are farthest from each other. def width 1 + (max_x - min_x) end # Returns the height of the minimal bounding box of all the points in this vector. # @return [Integer] The y-distance between the points that are farthest from each other. def height 1 + (max_y - min_y) end # Returns the dimension of the minimal bounding rectangle of the points in this vector. # @return [ChunkyPNG::Dimension] The dimension instance with the width and height def dimension ChunkyPNG::Dimension.new(width, height) end # @return [Array] The list of points interpreted from the input array. def self.multiple_from_array(source) return [] if source.empty? if source.first.kind_of?(Numeric) || source.first =~ /^\d+$/ raise ArgumentError, "The points array is expected to have an even number of items!" if source.length % 2 != 0 points = [] source.each_slice(2) { |x, y| points << ChunkyPNG::Point.new(x, y) } return points else source.map { |p| ChunkyPNG::Point(p) } end end # @return [Array] The list of points parsed from the string. def self.multiple_from_string(source_str) multiple_from_array(source_str.scan(/[\(\[\{]?(\d+)\s*[,x]?\s*(\d+)[\)\]\}]?/)) end end end chunky_png-chunky_png-1.2.8/spec/000077500000000000000000000000001212556323000167635ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/chunky_png/000077500000000000000000000000001212556323000211305ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/000077500000000000000000000000001212556323000224035ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/adam7_interlacing_spec.rb000066400000000000000000000070211212556323000273120ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::Adam7Interlacing do include ChunkyPNG::Canvas::Adam7Interlacing describe '#adam7_pass_sizes' do it "should get the pass sizes for a 8x8 image correctly" do adam7_pass_sizes(8, 8).should == [ [1, 1], [1, 1], [2, 1], [2, 2], [4, 2], [4, 4], [8, 4] ] end it "should get the pass sizes for a 12x12 image correctly" do adam7_pass_sizes(12, 12).should == [ [2, 2], [1, 2], [3, 1], [3, 3], [6, 3], [6, 6], [12, 6] ] end it "should get the pass sizes for a 33x47 image correctly" do adam7_pass_sizes(33, 47).should == [ [5, 6], [4, 6], [9, 6], [8, 12], [17, 12], [16, 24], [33, 23] ] end it "should get the pass sizes for a 1x1 image correctly" do adam7_pass_sizes(1, 1).should == [ [1, 1], [0, 1], [1, 0], [0, 1], [1, 0], [0, 1], [1, 0] ] end it "should get the pass sizes for a 0x0 image correctly" do adam7_pass_sizes(0, 0).should == [ [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0] ] end it "should always maintain the same amount of pixels in total" do [[8, 8], [12, 12], [33, 47], [1, 1], [0, 0]].each do |(width, height)| pass_sizes = adam7_pass_sizes(width, height) pass_sizes.inject(0) { |sum, (w, h)| sum + (w*h) }.should == width * height end end end describe '#adam7_multiplier_offset' do it "should get the multiplier and offset values for pass 1 correctly" do adam7_multiplier_offset(0).should == [3, 0, 3, 0] end it "should get the multiplier and offset values for pass 2 correctly" do adam7_multiplier_offset(1).should == [3, 4, 3, 0] end it "should get the multiplier and offset values for pass 3 correctly" do adam7_multiplier_offset(2).should == [2, 0, 3, 4] end it "should get the multiplier and offset values for pass 4 correctly" do adam7_multiplier_offset(3).should == [2, 2, 2, 0] end it "should get the multiplier and offset values for pass 5 correctly" do adam7_multiplier_offset(4).should == [1, 0, 2, 2] end it "should get the multiplier and offset values for pass 6 correctly" do adam7_multiplier_offset(5).should == [1, 1, 1, 0] end it "should get the multiplier and offset values for pass 7 correctly" do adam7_multiplier_offset(6).should == [0, 0, 1, 1] end end describe '#adam7_merge_pass' do it "should merge the submatrices correctly" do submatrices = [ ChunkyPNG::Canvas.new(1, 1, 168430335), # r = 10 ChunkyPNG::Canvas.new(1, 1, 336860415), # r = 20 ChunkyPNG::Canvas.new(2, 1, 505290495), # r = 30 ChunkyPNG::Canvas.new(2, 2, 677668095), # r = 40 ChunkyPNG::Canvas.new(4, 2, 838912255), # r = 50 ChunkyPNG::Canvas.new(4, 4, 1023344895), # r = 60 ChunkyPNG::Canvas.new(8, 4, 1175063295), # r = 70 ] canvas = ChunkyPNG::Image.new(8,8) submatrices.each_with_index { |m, pass| adam7_merge_pass(pass, canvas, m) } canvas.should == reference_image('adam7') end end describe '#adam7_extract_pass' do before(:each) { @canvas = reference_canvas('adam7') } 1.upto(7) do |pass| it "should extract pass #{pass} correctly" do sm = adam7_extract_pass(pass - 1, @canvas) sm.pixels.length.should == sm.width * sm.height sm.pixels.uniq.length.should == 1 ChunkyPNG::Color.r(sm[0,0]).should == pass * 10 end end end endchunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/data_url_exporting_spec.rb000066400000000000000000000011271212556323000276350ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas do describe '#to_data_url' do it "should export a sample canvas to an RGBA stream correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [ChunkyPNG::Color.rgba(1,2,3,4), ChunkyPNG::Color.rgba(5,6,7,8), ChunkyPNG::Color.rgba(4,3,2,1), ChunkyPNG::Color.rgba(8,7,6,5)]) canvas.to_data_url.should == "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEUBAgMEAwIFBgcIBwazgAAdAAAABHRSTlMEAQgFhYDlfQAAAAxJREFUeJxjUmAKAAAAwAB1GNhIEwAAAABJRU5ErkJggg==" end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/data_url_importing_spec.rb000066400000000000000000000007711212556323000276320ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas do describe '.from_data_url' do it "should import an image from a data URL" do data_url = reference_canvas('operations').to_data_url ChunkyPNG::Canvas.from_data_url(data_url).should == reference_canvas('operations') end it "should raise an exception if the string is not a proper data URL" do lambda { ChunkyPNG::Canvas.from_data_url('whatever') }.should raise_error(ChunkyPNG::SignatureMismatch) end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/drawing_spec.rb000066400000000000000000000154231212556323000254020ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::Drawing do describe '#compose_pixel' do subject { ChunkyPNG::Canvas.new(1, 1, ChunkyPNG::Color.rgb(200, 150, 100)) } it "should compose colors correctly" do subject.compose_pixel(0,0, ChunkyPNG::Color(100, 150, 200, 128)) subject[0, 0].should == ChunkyPNG::Color(150, 150, 150) end it "should return the composed color" do subject.compose_pixel(0, 0, ChunkyPNG::Color.rgba(100, 150, 200, 128)).should == ChunkyPNG::Color.rgb(150, 150, 150) end it "should do nothing when the coordinates are out of bounds" do subject.compose_pixel(1, -1, :black).should be_nil lambda { subject.compose_pixel(1, -1, :black) }.should_not change { subject[0, 0] } end end describe '#line' do it "should draw lines correctly with anti-aliasing" do canvas = ChunkyPNG::Canvas.new(31, 31, ChunkyPNG::Color::WHITE) canvas.line( 0, 0, 30, 30, ChunkyPNG::Color::BLACK) canvas.line( 0, 30, 30, 0, ChunkyPNG::Color::BLACK) canvas.line(15, 30, 15, 0, ChunkyPNG::Color.rgba(200, 0, 0, 128)) canvas.line( 0, 15, 30, 15, ChunkyPNG::Color.rgba(200, 0, 0, 128)) canvas.line(30, 30, 0, 15, ChunkyPNG::Color.rgba( 0, 200, 0, 128), false) canvas.line( 0, 15, 30, 0, ChunkyPNG::Color.rgba( 0, 200, 0, 128)) canvas.line( 0, 30, 15, 0, ChunkyPNG::Color.rgba( 0, 0, 200, 128), false) canvas.line(15, 0, 30, 30, ChunkyPNG::Color.rgba( 0, 0, 200, 128)) canvas.should == reference_canvas('lines') end it "should draw partial lines if the coordinates are partially out of bounds" do canvas = ChunkyPNG::Canvas.new(1, 2, ChunkyPNG::Color::WHITE) canvas.line(-5, -5, 0, 0, '#000000') canvas.pixels.should == [ChunkyPNG::Color::BLACK, ChunkyPNG::Color::WHITE] end it "should return itself to allow chaining" do canvas = ChunkyPNG::Canvas.new(16, 16, ChunkyPNG::Color::WHITE) canvas.line(1, 1, 10, 10, :black).should equal(canvas) end end describe '#rect' do subject { ChunkyPNG::Canvas.new(16, 16, '#ffffff') } it "should draw a rectangle with the correct colors" do subject.rect(1, 1, 10, 10, ChunkyPNG::Color.rgba(0, 255, 0, 80), ChunkyPNG::Color.rgba(255, 0, 0, 100)) subject.rect(5, 5, 14, 14, ChunkyPNG::Color.rgba(0, 0, 255, 160), ChunkyPNG::Color.rgba(255, 255, 0, 100)) subject.should == reference_canvas('rect') end it "should return itself to allow chaining" do subject.rect(1, 1, 10, 10).should equal(subject) end it "should draw partial rectangles if the coordinates are partially out of bounds" do subject.rect(0, 0, 20, 20, :black, :white) subject[0, 0].should == ChunkyPNG::Color::BLACK end it "should draw the rectangle fill only if the coordinates are fully out of bounds" do subject.rect(-1, -1, 20, 20, :black, :white) subject[0, 0].should == ChunkyPNG::Color::WHITE end end describe '#circle' do subject { ChunkyPNG::Canvas.new(32, 32, ChunkyPNG::Color.rgba(0, 0, 255, 128)) } it "should draw circles" do subject.circle(11, 11, 10, ChunkyPNG::Color('red @ 0.5'), ChunkyPNG::Color('white @ 0.2')) subject.circle(21, 21, 10, ChunkyPNG::Color('green @ 0.5')) subject.should == reference_canvas('circles') end it "should draw partial circles when going of the canvas bounds" do subject.circle(0, 0, 10, ChunkyPNG::Color(:red)) subject.circle(31, 16, 10, ChunkyPNG::Color(:black), ChunkyPNG::Color(:white, 0xaa)) subject.should == reference_canvas('partial_circles') end it "should return itself to allow chaining" do subject.circle(10, 10, 5, :red).should equal(subject) end end describe '#polygon' do subject { ChunkyPNG::Canvas.new(22, 22) } it "should draw an filled triangle when using 3 control points" do subject.polygon('(2,2) (20,5) (5,20)', ChunkyPNG::Color(:black, 0xaa), ChunkyPNG::Color(:red, 0x44)) subject.should == reference_canvas('polygon_triangle_filled') end it "should draw a unfilled polygon with 6 control points" do subject.polygon('(2,2) (12, 1) (20,5) (18,18) (5,20) (1,12)', ChunkyPNG::Color(:black)) subject.should == reference_canvas('polygon_unfilled') end it "should draw a vertically crossed filled polygon with 4 control points" do subject.polygon('(2,2) (21,2) (2,21) (21,21)', ChunkyPNG::Color(:black), ChunkyPNG::Color(:red)) subject.should == reference_canvas('polygon_filled_vertical') end it "should draw a vertically crossed filled polygon with 4 control points" do subject.polygon('(2,2) (2,21) (21,2) (21,21)', ChunkyPNG::Color(:black), ChunkyPNG::Color(:red)) subject.should == reference_canvas('polygon_filled_horizontal') end it "should return itself to allow chaining" do subject.polygon('(2,2) (20,5) (5,20)').should equal(subject) end end describe '#bezier_curve' do subject { ChunkyPNG::Canvas.new(24, 24, ChunkyPNG::Color::WHITE) } it "should draw a bezier curve starting at the first point" do subject.bezier_curve('3,20 10,10, 20,20') subject[3, 20].should == ChunkyPNG::Color::BLACK end it "should draw a bezier curve ending at the last point" do subject.bezier_curve('3,20 10,10, 20,20') subject[20, 20].should == ChunkyPNG::Color::BLACK end it "should draw a bezier curve with a color of green" do subject.bezier_curve('3,20 10,10, 20,20', :green) subject[3, 20].should == ChunkyPNG::Color(:green) end it "should draw a three point bezier curve" do subject.bezier_curve('1,23 12,10 23,23').should == reference_canvas('bezier_three_point') end it "should draw a three point bezier curve flipped" do subject.bezier_curve('1,1 12,15 23,1').should == reference_canvas('bezier_three_point_flipped') end it "should draw a four point bezier curve" do subject.bezier_curve('1,23 1,5 22,5 22,23').should == reference_canvas('bezier_four_point') end it "should draw a four point bezier curve flipped" do subject.bezier_curve('1,1 1,19 22,19 22,1').should == reference_canvas('bezier_four_point_flipped') end it "should draw a four point bezier curve with a shape of an s" do subject.bezier_curve('1,23 1,5 22,23 22,5').should == reference_canvas('bezier_four_point_s') end it "should draw a five point bezier curve" do subject.bezier_curve('10,23 1,10 12,5 23,10 14,23').should == reference_canvas('bezier_five_point') end it "should draw a six point bezier curve" do subject.bezier_curve('1,23 4,15 8,20 2,2 23,15 23,1').should == reference_canvas('bezier_six_point') end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/masking_spec.rb000066400000000000000000000032171212556323000253760ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::Masking do subject { reference_canvas('clock') } before(:all) do @theme_color = ChunkyPNG::Color('#e10f7a') @new_color = ChunkyPNG::Color('#ff0000') @background_color = ChunkyPNG::Color('white') end describe '#change_theme_color!' do it "should change the theme color correctly" do subject.change_theme_color!(@theme_color, @new_color) subject.should == reference_canvas('clock_updated') end end describe '#extract_mask' do it "should create the correct base and mask image" do base, mask = subject.extract_mask(@theme_color, @background_color) base.should == reference_canvas('clock_base') mask.should == reference_canvas('clock_mask') end it "should create a mask image with only one opaque color" do base, mask = subject.extract_mask(@theme_color, @background_color) mask.palette.opaque_palette.size.should == 1 end end describe '#change_mask_color!' do before { @mask = reference_canvas('clock_mask') } it "should replace the mask color correctly" do @mask.change_mask_color!(@new_color) @mask.should == reference_canvas('clock_mask_updated') end it "should still only have one opaque color" do @mask.change_mask_color!(@new_color) @mask.palette.opaque_palette.size.should == 1 end it "should raise an exception when the mask image has more than once color" do not_a_mask = reference_canvas('operations') lambda { not_a_mask.change_mask_color!(@new_color) }.should raise_error(ChunkyPNG::ExpectationFailed) end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/operations_spec.rb000066400000000000000000000236071212556323000261350ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::Operations do subject { reference_canvas('operations') } describe '#grayscale' do it "should not return itself" do subject.grayscale.should_not equal(subject) end it "should convert the image correctly" do subject.grayscale.should == reference_canvas('operations_grayscale') end it "should not adjust the current image" do lambda { subject.crop(10, 5, 4, 8) }.should_not change(subject, :pixels) end end describe '#grayscale!' do it "should return itself" do subject.grayscale!.should equal(subject) end it "should convert the image correctly" do subject.grayscale! subject.should == reference_canvas('operations_grayscale') end end describe '#crop' do it "should crop the right pixels from the original canvas" do subject.crop(10, 5, 4, 8).should == reference_canvas('cropped') end it "should not return itself" do subject.crop(10, 5, 4, 8).should_not equal(subject) end it "should not adjust the current image" do lambda { subject.crop(10, 5, 4, 8) }.should_not change(subject, :pixels) end it "should raise an exception when the cropped image falls outside the oiginal image" do lambda { subject.crop(16, 16, 2, 2) }.should raise_error(ChunkyPNG::OutOfBounds) end end describe '#crop!' do it "should crop the right pixels from the original canvas" do subject.crop!(10, 5, 4, 8) subject.should == reference_canvas('cropped') end it "should have a new width and height" do lambda { subject.crop!(10, 5, 4, 8) }.should change(subject, :dimension). from(ChunkyPNG::Dimension('16x16')). to(ChunkyPNG::Dimension('4x8')) end it "should raise an exception when the cropped image falls outside the oiginal image" do lambda { subject.crop!(16, 16, 2, 2) }.should raise_error(ChunkyPNG::OutOfBounds) end it "should return itself" do subject.crop!(10, 5, 4, 8).should equal(subject) end end describe '#compose' do it "should compose pixels correctly" do subcanvas = ChunkyPNG::Canvas.new(4, 8, ChunkyPNG::Color.rgba(0, 0, 0, 75)) subject.compose(subcanvas, 8, 4).should == reference_canvas('composited') end it "should leave the original intact" do subject.compose(ChunkyPNG::Canvas.new(1,1)) subject.should == reference_canvas('operations') end it "should not return itself" do subject.compose(ChunkyPNG::Canvas.new(1,1)).should_not equal(subject) end it "should raise an exception when the pixels to compose fall outside the image" do lambda { subject.compose(ChunkyPNG::Canvas.new(1,1), 16, 16) }.should raise_error(ChunkyPNG::OutOfBounds) end end describe '#compose!' do it "should compose pixels correctly" do subcanvas = ChunkyPNG::Canvas.new(4, 8, ChunkyPNG::Color.rgba(0, 0, 0, 75)) subject.compose!(subcanvas, 8, 4) subject.should == reference_canvas('composited') end it "should return itself" do subject.compose!(ChunkyPNG::Canvas.new(1,1)).should equal(subject) end it "should compose a base image and mask correctly" do base = reference_canvas('clock_base') mask = reference_canvas('clock_mask_updated') base.compose!(mask) base.should == reference_canvas('clock_updated') end it "should raise an exception when the pixels to compose fall outside the image" do lambda { subject.compose!(ChunkyPNG::Canvas.new(1,1), 16, 16) }.should raise_error(ChunkyPNG::OutOfBounds) end end describe '#replace' do it "should replace the correct pixels" do subcanvas = ChunkyPNG::Canvas.new(3, 2, ChunkyPNG::Color.rgb(200, 255, 0)) subject.replace(subcanvas, 5, 4).should == reference_canvas('replaced') end it "should not return itself" do subject.replace(ChunkyPNG::Canvas.new(1,1)).should_not equal(subject) end it "should leave the original intact" do subject.replace(ChunkyPNG::Canvas.new(1,1)) subject.should == reference_canvas('operations') end it "should raise an exception when the pixels to replace fall outside the image" do lambda { subject.replace(ChunkyPNG::Canvas.new(1,1), 16, 16) }.should raise_error(ChunkyPNG::OutOfBounds) end end describe '#replace!' do it "should replace the correct pixels" do subcanvas = ChunkyPNG::Canvas.new(3, 2, ChunkyPNG::Color.rgb(200, 255, 0)) subject.replace!(subcanvas, 5, 4) subject.should == reference_canvas('replaced') end it "should return itself" do subject.replace!(ChunkyPNG::Canvas.new(1,1)).should equal(subject) end it "should raise an exception when the pixels to replace fall outside the image" do lambda { subject.replace!(ChunkyPNG::Canvas.new(1,1), 16, 16) }.should raise_error(ChunkyPNG::OutOfBounds) end end end describe ChunkyPNG::Canvas::Operations do subject { ChunkyPNG::Canvas.new(2, 3, [1, 2, 3, 4, 5, 6]) } describe '#flip_horizontally!' do it "should flip the pixels horizontally in place" do subject.flip_horizontally! subject.should == ChunkyPNG::Canvas.new(2, 3, [5, 6, 3, 4, 1, 2]) end it "should return itself" do subject.flip_horizontally!.should equal(subject) end end describe '#flip_horizontally' do it "should flip the pixels horizontally" do subject.flip_horizontally.should == ChunkyPNG::Canvas.new(2, 3, [5, 6, 3, 4, 1, 2]) end it "should not return itself" do subject.flip_horizontally.should_not equal(subject) end it "should return a copy of itself when applied twice" do subject.flip_horizontally.flip_horizontally.should == subject end end describe '#flip_vertically!' do it "should flip the pixels vertically" do subject.flip_vertically! subject.should == ChunkyPNG::Canvas.new(2, 3, [2, 1, 4, 3, 6, 5]) end it "should return itself" do subject.flip_horizontally!.should equal(subject) end end describe '#flip_vertically' do it "should flip the pixels vertically" do subject.flip_vertically.should == ChunkyPNG::Canvas.new(2, 3, [2, 1, 4, 3, 6, 5]) end it "should not return itself" do subject.flip_horizontally.should_not equal(subject) end it "should return a copy of itself when applied twice" do subject.flip_vertically.flip_vertically.should == subject end end describe '#rotate_left' do it "should rotate the pixels 90 degrees counter-clockwise" do subject.rotate_left.should == ChunkyPNG::Canvas.new(3, 2, [2, 4, 6, 1, 3, 5] ) end it "should not return itself" do subject.rotate_left.should_not equal(subject) end it "should not change the image dimensions" do lambda { subject.rotate_left }.should_not change(subject, :dimension) end it "it should rotate 180 degrees when applied twice" do subject.rotate_left.rotate_left.should == subject.rotate_180 end it "it should rotate right when applied three times" do subject.rotate_left.rotate_left.rotate_left.should == subject.rotate_right end it "should return itself when applied four times" do subject.rotate_left.rotate_left.rotate_left.rotate_left.should == subject end end describe '#rotate_left!' do it "should rotate the pixels 90 degrees clockwise" do subject.rotate_left! subject.should == ChunkyPNG::Canvas.new(3, 2, [2, 4, 6, 1, 3, 5] ) end it "should return itself" do subject.rotate_left!.should equal(subject) end it "should change the image dimensions" do lambda { subject.rotate_left! }.should change(subject, :dimension).from(ChunkyPNG::Dimension('2x3')).to(ChunkyPNG::Dimension('3x2')) end end describe '#rotate_right' do it "should rotate the pixels 90 degrees clockwise" do subject.rotate_right.should == ChunkyPNG::Canvas.new(3, 2, [5, 3, 1, 6, 4, 2] ) end it "should not return itself" do subject.rotate_right.should_not equal(subject) end it "should not change the image dimensions" do lambda { subject.rotate_right }.should_not change(subject, :dimension) end it "it should rotate 180 degrees when applied twice" do subject.rotate_right.rotate_right.should == subject.rotate_180 end it "it should rotate left when applied three times" do subject.rotate_right.rotate_right.rotate_right.should == subject.rotate_left end it "should return itself when applied four times" do subject.rotate_right.rotate_right.rotate_right.rotate_right.should == subject end end describe '#rotate_right!' do it "should rotate the pixels 90 degrees clockwise" do subject.rotate_right! subject.should == ChunkyPNG::Canvas.new(3, 2, [5, 3, 1, 6, 4, 2] ) end it "should return itself" do subject.rotate_right!.should equal(subject) end it "should change the image dimensions" do lambda { subject.rotate_right! }.should change(subject, :dimension).from(ChunkyPNG::Dimension('2x3')).to(ChunkyPNG::Dimension('3x2')) end end describe '#rotate_180' do it "should rotate the pixels 180 degrees" do subject.rotate_180.should == ChunkyPNG::Canvas.new(2, 3, [6, 5, 4, 3, 2, 1]) end it "should return not itself" do subject.rotate_180.should_not equal(subject) end it "should return a copy of itself when applied twice" do subject.rotate_180.rotate_180.should == subject end end describe '#rotate_180!' do it "should rotate the pixels 180 degrees" do subject.rotate_180! subject.should == ChunkyPNG::Canvas.new(2, 3, [6, 5, 4, 3, 2, 1]) end it "should return itself" do subject.rotate_180!.should equal(subject) end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/png_decoding_spec.rb000066400000000000000000000103041212556323000263600ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::PNGDecoding do include ChunkyPNG::Canvas::PNGDecoding describe '#decode_png_scanline' do it "should decode a line without filtering as is" do stream = [ChunkyPNG::FILTER_NONE, 255, 255, 255, 255, 255, 255, 255, 255, 255].pack('C*') decode_png_str_scanline(stream, 0, nil, 9, 3) stream.unpack('@1C*').should == [255, 255, 255, 255, 255, 255, 255, 255, 255] end it "should decode a line with sub filtering correctly" do # all white pixels stream = [ChunkyPNG::FILTER_SUB, 255, 255, 255, 0, 0, 0, 0, 0, 0].pack('C*') decode_png_str_scanline(stream, 0, nil, 9, 3) stream.unpack('@1C*').should == [255, 255, 255, 255, 255, 255, 255, 255, 255] # all black pixels stream = [ChunkyPNG::FILTER_SUB, 0, 0, 0, 0, 0, 0, 0, 0, 0].pack('C*') decode_png_str_scanline(stream, 0, nil, 9, 3) stream.unpack('@1C*').should == [0, 0, 0, 0, 0, 0, 0, 0, 0] # various colors stream = [ChunkyPNG::FILTER_SUB, 255, 0, 45, 0, 255, 0, 112, 200, 178].pack('C*') decode_png_str_scanline(stream, 0, nil, 9, 3) stream.unpack('@1C*').should == [255, 0, 45, 255, 255, 45, 111, 199, 223] end it "should decode a line with up filtering correctly" do # previous line has various pixels previous = [ChunkyPNG::FILTER_UP, 255, 255, 255, 127, 127, 127, 0, 0, 0] current = [ChunkyPNG::FILTER_UP, 0, 127, 255, 0, 127, 255, 0, 127, 255] stream = (previous + current).pack('C*') decode_png_str_scanline(stream, 10, 0, 9, 3) stream.unpack('@11C9').should == [255, 126, 254, 127, 254, 126, 0, 127, 255] end it "should decode a line with average filtering correctly" do previous = [ChunkyPNG::FILTER_AVERAGE, 10, 20, 30, 40, 50, 60, 70, 80, 80, 100, 110, 120] current = [ChunkyPNG::FILTER_AVERAGE, 0, 0, 10, 23, 15, 13, 23, 63, 38, 60, 253, 53] stream = (previous + current).pack('C*') decode_png_str_scanline(stream, 13, 0, 12, 3) stream.unpack('@14C12').should == [5, 10, 25, 45, 45, 55, 80, 125, 105, 150, 114, 165] end it "should decode a line with paeth filtering correctly" do previous = [ChunkyPNG::FILTER_PAETH, 10, 20, 30, 40, 50, 60, 70, 80, 80, 100, 110, 120] current = [ChunkyPNG::FILTER_PAETH, 0, 0, 10, 20, 10, 0, 0, 40, 10, 20, 190, 0] stream = (previous + current).pack('C*') decode_png_str_scanline(stream, 13, 0, 12, 3) stream.unpack('@14C12').should == [10, 20, 40, 60, 60, 60, 70, 120, 90, 120, 54, 120] end end describe '#decode_png_extract_4bit_value' do it "should extract the high bits successfully" do decode_png_extract_4bit_value('10010110'.to_i(2), 0).should == '1001'.to_i(2) end it "should extract the low bits successfully" do decode_png_extract_4bit_value('10010110'.to_i(2), 17).should == '0110'.to_i(2) end end describe '#decode_png_extract_2bit_value' do it "should extract the first 2 bits successfully" do decode_png_extract_2bit_value('10010110'.to_i(2), 0).should == '10'.to_i(2) end it "should extract the second 2 bits successfully" do decode_png_extract_2bit_value('10010110'.to_i(2), 5).should == '01'.to_i(2) end it "should extract the third 2 bits successfully" do decode_png_extract_2bit_value('10010110'.to_i(2), 2).should == '01'.to_i(2) end it "should extract the low two bits successfully" do decode_png_extract_2bit_value('10010110'.to_i(2), 7).should == '10'.to_i(2) end end describe '#decode_png_extract_1bit_value' do it "should extract all separate bits correctly" do decode_png_extract_1bit_value('10010110'.to_i(2), 0).should == 1 decode_png_extract_1bit_value('10010110'.to_i(2), 1).should == 0 decode_png_extract_1bit_value('10010110'.to_i(2), 2).should == 0 decode_png_extract_1bit_value('10010110'.to_i(2), 3).should == 1 decode_png_extract_1bit_value('10010110'.to_i(2), 4).should == 0 decode_png_extract_1bit_value('10010110'.to_i(2), 5).should == 1 decode_png_extract_1bit_value('10010110'.to_i(2), 6).should == 1 decode_png_extract_1bit_value('10010110'.to_i(2), 7).should == 0 end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/png_encoding_spec.rb000066400000000000000000000272371212556323000264070ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::PNGEncoding do include ChunkyPNG::Canvas::PNGEncoding context 'determining encoding options' do [:indexed, :grayscale, :grayscale_alpha, :truecolor, :truecolor_alpha].each do |color_mode_name| it "should encode an image with color mode #{color_mode_name} correctly" do canvas = ChunkyPNG::Canvas.new(10, 10, ChunkyPNG::Color.rgb(100, 100, 100)) color_mode = ChunkyPNG.const_get("COLOR_#{color_mode_name.to_s.upcase}") blob = canvas.to_blob(:color_mode => color_mode) ds = ChunkyPNG::Datastream.from_blob(blob) ds.header_chunk.color.should == color_mode ChunkyPNG::Canvas.from_datastream(ds).should == ChunkyPNG::Canvas.new(10, 10, ChunkyPNG::Color.rgb(100, 100, 100)) end end it "should encode an image with 2 colors using 1-bit indexed color mode" do @canvas = ChunkyPNG::Canvas.from_file(png_suite_file('basic', 'basn3p01.png')) ds = ChunkyPNG::Datastream.from_blob(@canvas.to_blob) ds.header_chunk.color.should == ChunkyPNG::COLOR_INDEXED ds.header_chunk.depth.should == 1 @canvas.should == ChunkyPNG::Canvas.from_datastream(ds) end it "should encode an image with 4 colors using 2-bit indexed color mode" do @canvas = ChunkyPNG::Canvas.from_file(png_suite_file('basic', 'basn3p02.png')) ds = ChunkyPNG::Datastream.from_blob(@canvas.to_blob) ds.header_chunk.color.should == ChunkyPNG::COLOR_INDEXED ds.header_chunk.depth.should == 2 @canvas.should == ChunkyPNG::Canvas.from_datastream(ds) end it "should encode an image with 16 colors using 4-bit indexed color mode" do @canvas = ChunkyPNG::Canvas.from_file(png_suite_file('basic', 'basn3p04.png')) ds = ChunkyPNG::Datastream.from_blob(@canvas.to_blob) ds.header_chunk.color.should == ChunkyPNG::COLOR_INDEXED ds.header_chunk.depth.should == 4 @canvas.should == ChunkyPNG::Canvas.from_datastream(ds) end it "should encode an image with 256 colors using 8-bit indexed color mode" do @canvas = ChunkyPNG::Canvas.from_file(png_suite_file('basic', 'basn3p08.png')) ds = ChunkyPNG::Datastream.from_blob(@canvas.to_blob) ds.header_chunk.color.should == ChunkyPNG::COLOR_INDEXED ds.header_chunk.depth.should == 8 @canvas.should == ChunkyPNG::Canvas.from_datastream(ds) end it "should use a higher bit depth than necessary if requested" do @canvas = ChunkyPNG::Canvas.from_file(png_suite_file('basic', 'basn3p01.png')) ds = ChunkyPNG::Datastream.from_blob(@canvas.to_blob(:bit_depth => 4)) ds.header_chunk.color.should == ChunkyPNG::COLOR_INDEXED ds.header_chunk.depth.should == 4 @canvas.should == ChunkyPNG::Canvas.from_datastream(ds) end it "should encode an image with interlacing correctly" do input_canvas = ChunkyPNG::Canvas.from_file(resource_file('operations.png')) blob = input_canvas.to_blob(:interlace => true) ds = ChunkyPNG::Datastream.from_blob(blob) ds.header_chunk.interlace.should == ChunkyPNG::INTERLACING_ADAM7 ChunkyPNG::Canvas.from_datastream(ds).should == input_canvas end it "should save an image using the normal routine correctly" do canvas = reference_canvas('operations') Zlib::Deflate.should_receive(:deflate).with(anything, Zlib::DEFAULT_COMPRESSION).and_return('') canvas.to_blob end it "should save an image using the :fast_rgba routine correctly" do canvas = reference_canvas('operations') canvas.should_not_receive(:encode_png_str_scanline_none) canvas.should_not_receive(:encode_png_str_scanline_sub) canvas.should_not_receive(:encode_png_str_scanline_up) canvas.should_not_receive(:encode_png_str_scanline_average) canvas.should_not_receive(:encode_png_str_scanline_paeth) Zlib::Deflate.should_receive(:deflate).with(anything, Zlib::BEST_SPEED).and_return('') canvas.to_blob(:fast_rgba) end it "should save an image using the :good_compression routine correctly" do canvas = reference_canvas('operations') canvas.should_not_receive(:encode_png_str_scanline_none) canvas.should_not_receive(:encode_png_str_scanline_sub) canvas.should_not_receive(:encode_png_str_scanline_up) canvas.should_not_receive(:encode_png_str_scanline_average) canvas.should_not_receive(:encode_png_str_scanline_paeth) Zlib::Deflate.should_receive(:deflate).with(anything, Zlib::BEST_COMPRESSION).and_return('') canvas.to_blob(:good_compression) end it "should save an image using the :best_compression routine correctly" do canvas = reference_canvas('operations') canvas.should_receive(:encode_png_str_scanline_paeth).exactly(canvas.height).times Zlib::Deflate.should_receive(:deflate).with(anything, Zlib::BEST_COMPRESSION).and_return('') canvas.to_blob(:best_compression) end it "should save an image with black and white only if requested" do ds = ChunkyPNG::Datastream.from_blob(reference_canvas('lines').to_blob(:black_and_white)) ds.header_chunk.color.should == ChunkyPNG::COLOR_GRAYSCALE ds.header_chunk.depth.should == 1 end end describe 'different color modes and bit depths' do before do @canvas = ChunkyPNG::Canvas.new(2, 2) @canvas[0, 0] = ChunkyPNG::Color.rgba( 1, 2, 3, 4) @canvas[1, 0] = ChunkyPNG::Color.rgba(252, 253, 254, 255) @canvas[0, 1] = ChunkyPNG::Color.rgba(255, 254, 253, 252) @canvas[1, 1] = ChunkyPNG::Color.rgba( 4, 3, 2, 1) @canvas.encoding_palette = @canvas.palette @canvas.encoding_palette.to_plte_chunk end it "should encode using 8-bit RGBA mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 8, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x01\x02\x03\x04\xFC\xFD\xFE\xFF\0\xFF\xFE\xFD\xFC\x04\x03\x02\x01") end it "should encode using 8 bit RGB mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x01\x02\x03\xFC\xFD\xFE\0\xFF\xFE\xFD\x04\x03\x02") end it "should encode using 1-bit grayscale mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_GRAYSCALE, 1, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x40\0\x80") # Using the B byte of the pixel == 3, assuming R == G == B for grayscale images end it "should encode using 2-bit grayscale mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_GRAYSCALE, 2, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x30\0\xC0") # Using the B byte of the pixel == 3, assuming R == G == B for grayscale images end it "should encode using 4-bit grayscale mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_GRAYSCALE, 4, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x0F\0\xF0") # Using the B byte of the pixel == 3, assuming R == G == B for grayscale images end it "should encode using 8-bit grayscale mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_GRAYSCALE, 8, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x03\xFE\0\xFD\x02") # Using the B byte of the pixel == 3, assuming R == G == B for grayscale images end it "should not encode using 1-bit indexed mode because the image has too many colors" do lambda { @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_INDEXED, 1, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) }.should raise_error(ChunkyPNG::ExpectationFailed) end it "should encode using 2-bit indexed mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_INDEXED, 2, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x20\0\xD0") end it "should encode using 4-bit indexed mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_INDEXED, 4, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x02\0\x31") end it "should encode using 8-bit indexed mode correctly" do stream = @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_INDEXED, 8, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) stream.should == ChunkyPNG.force_binary("\0\x00\x02\0\x03\x01") end end describe 'different filter methods' do it "should encode a scanline without filtering correctly" do stream = [ChunkyPNG::FILTER_NONE, 0, 0, 0, 1, 1, 1, 2, 2, 2].pack('C*') encode_png_str_scanline_none(stream, 0, nil, 9, 3) stream.unpack('C*').should == [ChunkyPNG::FILTER_NONE, 0, 0, 0, 1, 1, 1, 2, 2, 2] end it "should encode a scanline with sub filtering correctly" do stream = [ChunkyPNG::FILTER_NONE, 255, 255, 255, 255, 255, 255, 255, 255, 255, ChunkyPNG::FILTER_NONE, 255, 255, 255, 255, 255, 255, 255, 255, 255].pack('C*') # Check line with previous line encode_png_str_scanline_sub(stream, 10, 0, 9, 3) stream.unpack('@10C10').should == [ChunkyPNG::FILTER_SUB, 255, 255, 255, 0, 0, 0, 0, 0, 0] # Check line without previous line encode_png_str_scanline_sub(stream, 0, nil, 9, 3) stream.unpack('@0C10').should == [ChunkyPNG::FILTER_SUB, 255, 255, 255, 0, 0, 0, 0, 0, 0] end it "should encode a scanline with up filtering correctly" do stream = [ChunkyPNG::FILTER_NONE, 255, 255, 255, 255, 255, 255, 255, 255, 255, ChunkyPNG::FILTER_NONE, 255, 255, 255, 255, 255, 255, 255, 255, 255].pack('C*') # Check line with previous line encode_png_str_scanline_up(stream, 10, 0, 9, 3) stream.unpack('@10C10').should == [ChunkyPNG::FILTER_UP, 0, 0, 0, 0, 0, 0, 0, 0, 0] # Check line without previous line encode_png_str_scanline_up(stream, 0, nil, 9, 3) stream.unpack('@0C10').should == [ChunkyPNG::FILTER_UP, 255, 255, 255, 255, 255, 255, 255, 255, 255] end it "should encode a scanline with average filtering correctly" do stream = [ChunkyPNG::FILTER_NONE, 10, 20, 30, 40, 50, 60, 70, 80, 80, 100, 110, 120, ChunkyPNG::FILTER_NONE, 5, 10, 25, 45, 45, 55, 80, 125, 105, 150, 114, 165].pack('C*') # Check line with previous line encode_png_str_scanline_average(stream, 13, 0, 12, 3) stream.unpack('@13C13').should == [ChunkyPNG::FILTER_AVERAGE, 0, 0, 10, 23, 15, 13, 23, 63, 38, 60, 253, 53] # Check line without previous line encode_png_str_scanline_average(stream, 0, nil, 12, 3) stream.unpack('@0C13').should == [ChunkyPNG::FILTER_AVERAGE, 10, 20, 30, 35, 40, 45, 50, 55, 50, 65, 70, 80] end it "should encode a scanline with paeth filtering correctly" do stream = [ChunkyPNG::FILTER_NONE, 10, 20, 30, 40, 50, 60, 70, 80, 80, 100, 110, 120, ChunkyPNG::FILTER_NONE, 10, 20, 40, 60, 60, 60, 70, 120, 90, 120, 54, 120].pack('C*') # Check line with previous line encode_png_str_scanline_paeth(stream, 13, 0, 12, 3) stream.unpack('@13C13').should == [ChunkyPNG::FILTER_PAETH, 0, 0, 10, 20, 10, 0, 0, 40, 10, 20, 190, 0] # Check line without previous line encode_png_str_scanline_paeth(stream, 0, nil, 12, 3) stream.unpack('@0C13').should == [ChunkyPNG::FILTER_PAETH, 10, 20, 30, 30, 30, 30, 30, 30, 20, 30, 30, 40] end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/resampling_spec.rb000066400000000000000000000105541212556323000261100ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas::Resampling do subject { reference_canvas('clock') } describe '#resample_nearest_neighbor' do it "should downscale from 2x2 to 1x1 correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [1, 2, 3, 4]) canvas.resample_nearest_neighbor(1, 1).should == ChunkyPNG::Canvas.new(1, 1, [4]) end it "should upscale from 2x2 to 4x4 correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [1, 2, 3, 4]) canvas.resample_nearest_neighbor(4, 4).should == ChunkyPNG::Canvas.new(4, 4, [1, 1, 2, 2, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4]) end it "should upscale both axis of the image" do subject.resample_nearest_neighbor(45, 45).should == reference_canvas('clock_nn_xup_yup') end it "should downscale both axis of the image" do subject.resample_nearest_neighbor(12, 12).should == reference_canvas('clock_nn_xdown_ydown') end it "should downscale the x-axis and upscale the y-axis of the image" do subject.resample_nearest_neighbor(20, 50).should == reference_canvas('clock_nn_xdown_yup') end it "should not return itself" do subject.resample_nearest_neighbor(1, 1).should_not equal(subject) end it "should not change the original image's dimensions" do lambda { subject.resample_nearest_neighbor(1, 1) }.should_not change(subject, :dimension) end end describe '#resample_nearest_neighbor!' do it "should upscale both axis of the image" do subject.resample_nearest_neighbor!(45, 45) subject.should == reference_canvas('clock_nn_xup_yup') end it "should downscale both axis of the image" do subject.resample_nearest_neighbor!(12, 12) subject.should == reference_canvas('clock_nn_xdown_ydown') end it "should downscale the x-axis and upscale the y-axis of the image" do subject.resample_nearest_neighbor!(20, 50) subject.should == reference_canvas('clock_nn_xdown_yup') end it "should return itself" do subject.resample_nearest_neighbor!(1, 1).should equal(subject) end it "should change the original image's dimensions" do lambda { subject.resample_nearest_neighbor!(1, 1) }.should change(subject, :dimension).to(ChunkyPNG::Dimension('1x1')) end end describe "#resample_bilinear" do it "should downscale from 2x2 to 1x1 correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [1, 2, 3, 4]) canvas.resample_bilinear(1, 1).should == ChunkyPNG::Canvas.new(1, 1, [2]) end it "should upscale from 2x2 to 4x4 correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [1, 2, 3, 4]) canvas.resample_bilinear(4, 4).should == ChunkyPNG::Canvas.new(4, 4, [1, 2, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 3, 3, 4, 4]) end it "should upscale both axis of the image" do subject.resample_bilinear(45, 45).should == reference_canvas('clock_bl_xup_yup') end it "should downscale both axis of the image" do subject.resample_bilinear(12, 12).should == reference_canvas('clock_bl_xdown_ydown') end it "should downscale the x-axis and upscale the y-axis of the image" do subject.resample_bilinear(20, 50).should == reference_canvas('clock_bl_xdown_yup') end it "should not return itself" do subject.resample_bilinear(1, 1).should_not equal(subject) end it "should not change the original image's dimensions" do lambda { subject.resample_bilinear(1, 1) }.should_not change(subject, :dimension) end end describe '#resample_bilinear!' do it "should upscale both axis of the image" do subject.resample_bilinear!(45, 45) subject.should == reference_canvas('clock_bl_xup_yup') end it "should downscale both axis of the image" do subject.resample_bilinear!(12, 12) subject.should == reference_canvas('clock_bl_xdown_ydown') end it "should downscale the x-axis and upscale the y-axis of the image" do subject.resample_bilinear!(20, 50) subject.should == reference_canvas('clock_bl_xdown_yup') end it "should return itself" do subject.resample_bilinear!(1, 1).should equal(subject) end it "should change the original image's dimensions" do lambda { subject.resample_bilinear!(1, 1) }.should change(subject, :dimension).to(ChunkyPNG::Dimension('1x1')) end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/stream_exporting_spec.rb000066400000000000000000000053571212556323000273460ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas do describe '#to_rgba_stream' do it "should export a sample canvas to an RGBA stream correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [ChunkyPNG::Color.rgba(1,2,3,4), ChunkyPNG::Color.rgba(5,6,7,8), ChunkyPNG::Color.rgba(4,3,2,1), ChunkyPNG::Color.rgba(8,7,6,5)]) canvas.to_rgba_stream.should == [1,2,3,4,5,6,7,8,4,3,2,1,8,7,6,5].pack('C16') end it "should export an image to an RGBA datastream correctly" do reference_canvas('pixelstream_reference').to_rgba_stream.should == resource_data('pixelstream.rgba') end end describe '#to_rgb_stream' do it "should export a sample canvas to an RGBA stream correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [ChunkyPNG::Color.rgba(1,2,3,4), ChunkyPNG::Color.rgba(5,6,7,8), ChunkyPNG::Color.rgba(4,3,2,1), ChunkyPNG::Color.rgba(8,7,6,5)]) canvas.to_rgb_stream.should == [1,2,3,5,6,7,4,3,2,8,7,6].pack('C12') end it "should export an image to an RGB datastream correctly" do reference_canvas('pixelstream_reference').to_rgb_stream.should == resource_data('pixelstream.rgb') end end describe '#to_grayscale_stream' do it "should export a grayscale image to a grayscale datastream correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [ChunkyPNG::Color.grayscale(1), ChunkyPNG::Color.grayscale(2), ChunkyPNG::Color.grayscale(3), ChunkyPNG::Color.grayscale(4)]) canvas.to_grayscale_stream.should == [1,2,3,4].pack('C4') end it "should export a color image to a grayscale datastream, using B values" do canvas = ChunkyPNG::Canvas.new(2, 2, [ChunkyPNG::Color.rgba(1,2,3,4), ChunkyPNG::Color.rgba(5,6,7,8), ChunkyPNG::Color.rgba(4,3,2,1), ChunkyPNG::Color.rgba(8,7,6,5)]) canvas.to_grayscale_stream.should == [3,7,2,6].pack('C4') end end describe '#to_alpha_channel_stream' do it "should export an opaque image to an alpha channel datastream correctly" do grayscale_array = Array.new(reference_canvas('pixelstream_reference').pixels.length, 255) reference_canvas('pixelstream_reference').to_alpha_channel_stream.should == grayscale_array.pack('C*') end it "should export a transparent image to an alpha channel datastream correctly" do canvas = ChunkyPNG::Canvas.new(2, 2, [ChunkyPNG::Color.rgba(1,2,3,4), ChunkyPNG::Color.rgba(5,6,7,8), ChunkyPNG::Color.rgba(4,3,2,1), ChunkyPNG::Color.rgba(8,7,6,5)]) canvas.to_alpha_channel_stream.should == [4,8,1,5].pack('C4') end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas/stream_importing_spec.rb000066400000000000000000000012661212556323000273320ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas do describe '.from_rgb_stream' do it "should load an image correctly from a datastream" do File.open(resource_file('pixelstream.rgb')) do |stream| matrix = ChunkyPNG::Canvas.from_rgb_stream(240, 180, stream) matrix.should == reference_canvas('pixelstream_reference') end end end describe '.from_rgba_stream' do it "should load an image correctly from a datastream" do File.open(resource_file('pixelstream.rgba')) do |stream| matrix = ChunkyPNG::Canvas.from_rgba_stream(240, 180, stream) matrix.should == reference_canvas('pixelstream_reference') end end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/canvas_spec.rb000066400000000000000000000205431212556323000237460ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Canvas do subject { ChunkyPNG::Canvas.new(1, 1, ChunkyPNG::Color::WHITE) } it { should respond_to(:width) } it { should respond_to(:height) } it { should respond_to(:pixels) } describe '#initialize' do it "should accept a single color value as background color" do canvas = ChunkyPNG::Canvas.new(2, 2, 'red @ 0.8') canvas[1, 0].should == ChunkyPNG::Color.parse('red @ 0.8') end it "should raise an error if the color value is not understood" do lambda { ChunkyPNG::Canvas.new(2, 2, :nonsense) }.should raise_error(ArgumentError) end it "should accept an array as initial pixel values" do canvas = ChunkyPNG::Canvas.new(2, 2, [1,2,3,4]) canvas[0, 0].should == 1 canvas[1, 0].should == 2 canvas[0, 1].should == 3 canvas[1, 1].should == 4 end it "should raise an ArgumentError if the initial array does not have the correct number of elements" do lambda { ChunkyPNG::Canvas.new(2, 2, [1,2,3]) }.should raise_error(ArgumentError) lambda { ChunkyPNG::Canvas.new(2, 2, [1,2,3,4,5]) }.should raise_error(ArgumentError) end it "should use a transparent background by default" do canvas = ChunkyPNG::Canvas.new(1, 1) canvas[0,0].should == ChunkyPNG::Color::TRANSPARENT end end describe '#dimension' do it "should return the dimensions as a Dimension instance" do subject.dimension.should == ChunkyPNG::Dimension('1x1') end end describe '#area' do it "should return the dimensions as two-item array" do subject.area.should == ChunkyPNG::Dimension('1x1').area end end describe '#include?' do it "should return true if the coordinates are within bounds, false otherwise" do subject.include_xy?( 0, 0).should be_true subject.include_xy?(-1, 0).should be_false subject.include_xy?( 1, 0).should be_false subject.include_xy?( 0, -1).should be_false subject.include_xy?( 0, 1).should be_false subject.include_xy?(-1, -1).should be_false subject.include_xy?(-1, 1).should be_false subject.include_xy?( 1, -1).should be_false subject.include_xy?( 1, 1).should be_false end it "should accept strings, arrays, hashes and points as well" do subject.should include('0, 0') subject.should_not include('0, 1') subject.should include([0, 0]) subject.should_not include([0, 1]) subject.should include(:y => 0, :x => 0) subject.should_not include(:y => 1, :x => 0) subject.should include(ChunkyPNG::Point.new(0, 0)) subject.should_not include(ChunkyPNG::Point.new(0, 1)) end end describe '#include_x?' do it "should return true if the x-coordinate is within bounds, false otherwise" do subject.include_x?( 0).should be_true subject.include_x?(-1).should be_false subject.include_x?( 1).should be_false end end describe '#include_y?' do it "should return true if the y-coordinate is within bounds, false otherwise" do subject.include_y?( 0).should be_true subject.include_y?(-1).should be_false subject.include_y?( 1).should be_false end end describe '#assert_xy!' do it "should not raise an exception if the coordinates are within bounds" do subject.should_receive(:include_xy?).with(0, 0).and_return(true) lambda { subject.send(:assert_xy!, 0, 0) }.should_not raise_error end it "should raise an exception if the coordinates are out of bounds bounds" do subject.should_receive(:include_xy?).with(0, -1).and_return(false) lambda { subject.send(:assert_xy!, 0, -1) }.should raise_error(ChunkyPNG::OutOfBounds) end end describe '#assert_x!' do it "should not raise an exception if the x-coordinate is within bounds" do subject.should_receive(:include_x?).with(0).and_return(true) lambda { subject.send(:assert_x!, 0) }.should_not raise_error end it "should raise an exception if the x-coordinate is out of bounds bounds" do subject.should_receive(:include_y?).with(-1).and_return(false) lambda { subject.send(:assert_y!, -1) }.should raise_error(ChunkyPNG::OutOfBounds) end end describe '#[]' do it "should return the pixel value if the coordinates are within bounds" do subject[0, 0].should == ChunkyPNG::Color::WHITE end it "should assert the coordinates to be within bounds" do subject.should_receive(:assert_xy!).with(0, 0) subject[0, 0] end end describe '#get_pixel' do it "should return the pixel value if the coordinates are within bounds" do subject.get_pixel(0, 0).should == ChunkyPNG::Color::WHITE end it "should not assert nor check the coordinates" do subject.should_not_receive(:assert_xy!) subject.should_not_receive(:include_xy?) subject.get_pixel(0, 0) end end describe '#[]=' do it "should change the pixel's color value" do lambda { subject[0, 0] = ChunkyPNG::Color::BLACK }.should change { subject[0, 0] }.from(ChunkyPNG::Color::WHITE).to(ChunkyPNG::Color::BLACK) end it "should assert the bounds of the image" do subject.should_receive(:assert_xy!).with(0, 0) subject[0, 0] = ChunkyPNG::Color::BLACK end end describe 'set_pixel' do it "should change the pixel's color value" do lambda { subject.set_pixel(0, 0, ChunkyPNG::Color::BLACK) }.should change { subject[0, 0] }.from(ChunkyPNG::Color::WHITE).to(ChunkyPNG::Color::BLACK) end it "should not assert or check the bounds of the image" do subject.should_not_receive(:assert_xy!) subject.should_not_receive(:include_xy?) subject.set_pixel(0, 0, ChunkyPNG::Color::BLACK) end end describe '#set_pixel_if_within_bounds' do it "should change the pixel's color value" do lambda { subject.set_pixel_if_within_bounds(0, 0, ChunkyPNG::Color::BLACK) }.should change { subject[0, 0] }.from(ChunkyPNG::Color::WHITE).to(ChunkyPNG::Color::BLACK) end it "should not assert, but only check the coordinates" do subject.should_not_receive(:assert_xy!) subject.should_receive(:include_xy?).with(0, 0) subject.set_pixel_if_within_bounds(0, 0, ChunkyPNG::Color::BLACK) end it "should do nothing if the coordinates are out of bounds" do subject.set_pixel_if_within_bounds(-1, 1, ChunkyPNG::Color::BLACK).should be_nil subject[0, 0].should == ChunkyPNG::Color::WHITE end end describe '#row' do before { @canvas = reference_canvas('operations') } it "should give an out of bounds exception when y-coordinate is out of bounds" do lambda { @canvas.row(-1) }.should raise_error(ChunkyPNG::OutOfBounds) lambda { @canvas.row(16) }.should raise_error(ChunkyPNG::OutOfBounds) end it "should return the correct pixels" do data = @canvas.row(0) data.should have(@canvas.width).items data.should == [65535, 268500991, 536936447, 805371903, 1073807359, 1342242815, 1610678271, 1879113727, 2147549183, 2415984639, 2684420095, 2952855551, 3221291007, 3489726463, 3758161919, 4026597375] end end describe '#column' do before { @canvas = reference_canvas('operations') } it "should give an out of bounds exception when x-coordinate is out of bounds" do lambda { @canvas.column(-1) }.should raise_error(ChunkyPNG::OutOfBounds) lambda { @canvas.column(16) }.should raise_error(ChunkyPNG::OutOfBounds) end it "should return the correct pixels" do data = @canvas.column(0) data.should have(@canvas.height).items data.should == [65535, 1114111, 2162687, 3211263, 4259839, 5308415, 6356991, 7405567, 8454143, 9502719, 10551295, 11599871, 12648447, 13697023, 14745599, 15794175] end end describe '#replace_canvas' do it "should change the dimension of the canvas" do lambda { subject.send(:replace_canvas!, 2, 2, [1,2,3,4]) }.should change(subject, :dimension). from(ChunkyPNG::Dimension('1x1')).to(ChunkyPNG::Dimension('2x2')) end it "should change the pixel array" do lambda { subject.send(:replace_canvas!, 2, 2, [1,2,3,4]) }.should change(subject, :pixels). from([ChunkyPNG::Color('white')]).to([1,2,3,4]) end it "should return itself" do subject.send(:replace_canvas!, 2, 2, [1,2,3,4]).should equal(subject) end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/color_spec.rb000066400000000000000000000205621212556323000236120ustar00rootroot00000000000000require 'spec_helper' describe 'ChunyPNG.Color' do it "should interpret 4 arguments as RGBA values" do ChunkyPNG::Color(1, 2, 3, 4).should == ChunkyPNG::Color.rgba(1, 2, 3, 4) end it "should interpret 3 arguments as RGBA values" do ChunkyPNG::Color(1, 2, 3).should == ChunkyPNG::Color.rgb(1, 2, 3) end it "should interpret 2 arguments as a color to parse and an opacity value" do ChunkyPNG::Color('0x0a649664', 0xaa).should == 0x0a6496aa ChunkyPNG::Color('spring green @ 0.6666', 0xff).should == 0x00ff7fff end it "should interpret 1 argument as a color to parse" do ChunkyPNG::Color.should_receive(:parse).with('0x0a649664') ChunkyPNG::Color('0x0a649664') end end describe ChunkyPNG::Color do include ChunkyPNG::Color before(:each) do @white = 0xffffffff @black = 0x000000ff @opaque = 0x0a6496ff @non_opaque = 0x0a649664 @fully_transparent = 0x0a649600 end describe '#parse' do it "should interpret a hex string correctly" do parse('0x0a649664').should == ChunkyPNG::Color.from_hex('#0a649664') end it "should interpret a color name correctly" do parse(:spring_green).should == 0x00ff7fff parse('spring green').should == 0x00ff7fff parse('spring green @ 0.6666').should == 0x00ff7faa end it "should return numbers as is" do parse('12345').should == 12345 parse(12345).should == 12345 end end describe '#pixel_bytesize' do it "should return the normal amount of bytes with a bit depth of 8" do pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8).should == 3 end it "should return a multiple of the normal amount of bytes with a bit depth greater than 8" do pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 16).should == 6 pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 16).should == 8 pixel_bytesize(ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 16).should == 4 end it "should return 1 with a bit depth lower than 0" do pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 4).should == 1 pixel_bytesize(ChunkyPNG::COLOR_INDEXED, 2).should == 1 pixel_bytesize(ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 1).should == 1 end end describe '#pass_bytesize' do it "should calculate a pass size correctly" do pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 10, 10).should == 310 end it "should return 0 if one of the dimensions is zero" do pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 0, 10).should == 0 pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 10, 0).should == 0 end end describe '#rgba' do it "should represent pixels as the correct number" do rgba(255, 255, 255, 255).should == @white rgba( 0, 0, 0, 255).should == @black rgba( 10, 100, 150, 255).should == @opaque rgba( 10, 100, 150, 100).should == @non_opaque rgba( 10, 100, 150, 0).should == @fully_transparent end end describe '#from_hex' do it "should load colors correctlt from hex notation" do from_hex('0a649664').should == @non_opaque from_hex('#0a649664').should == @non_opaque from_hex('0x0a649664').should == @non_opaque from_hex('0a6496').should == @opaque from_hex('#0a6496').should == @opaque from_hex('0x0a6496').should == @opaque end it "should allow setting opacity explicitely" do from_hex('0x0a6496', 0x64).should == @non_opaque from_hex('#0a6496', 0x64).should == @non_opaque end end describe '#html_color' do it "should find the correct color value" do html_color(:springgreen).should == 0x00ff7fff html_color(:spring_green).should == 0x00ff7fff html_color('springgreen').should == 0x00ff7fff html_color('spring green').should == 0x00ff7fff html_color('SpringGreen').should == 0x00ff7fff html_color('SPRING_GREEN').should == 0x00ff7fff end it "should set the opacity level explicitely" do html_color(:springgreen, 0xff).should == 0x00ff7fff html_color(:springgreen, 0xaa).should == 0x00ff7faa html_color(:springgreen, 0x00).should == 0x00ff7f00 end it "should set opacity levels from the color name" do html_color('Spring green @ 1.0').should == 0x00ff7fff html_color('Spring green @ 0.666').should == 0x00ff7faa html_color('Spring green @ 0.0').should == 0x00ff7f00 end it "should raise for an unkown color name" do lambda { html_color(:nonsense) }.should raise_error(ArgumentError) end end describe '#opaque?' do it "should correctly check for opaqueness" do opaque?(@white).should be_true opaque?(@black).should be_true opaque?(@opaque).should be_true opaque?(@non_opaque).should be_false opaque?(@fully_transparent).should be_false end end describe 'extractiion of separate color channels' do it "should extract components from a color correctly" do r(@opaque).should == 10 g(@opaque).should == 100 b(@opaque).should == 150 a(@opaque).should == 255 end end describe '#grayscale_teint' do it "should calculate the correct grayscale teint" do grayscale_teint(@opaque).should == 79 grayscale_teint(@non_opaque).should == 79 end end describe '#to_grayscale' do it "should use the grayscale teint for r, g and b" do gs = to_grayscale(@non_opaque) r(gs).should == grayscale_teint(@non_opaque) g(gs).should == grayscale_teint(@non_opaque) b(gs).should == grayscale_teint(@non_opaque) end it "should preserve the alpha channel" do a(to_grayscale(@non_opaque)).should == a(@non_opaque) a(to_grayscale(@opaque)).should == ChunkyPNG::Color::MAX end end describe '#to_hex' do it "should represent colors correcly using hex notation" do to_hex(@white).should == '#ffffffff' to_hex(@black).should == '#000000ff' to_hex(@opaque).should == '#0a6496ff' to_hex(@non_opaque).should == '#0a649664' to_hex(@fully_transparent).should == '#0a649600' end it "should represent colors correcly using hex notation without alpha channel" do to_hex(@white, false).should == '#ffffff' to_hex(@black, false).should == '#000000' to_hex(@opaque, false).should == '#0a6496' to_hex(@non_opaque, false).should == '#0a6496' to_hex(@fully_transparent, false).should == '#0a6496' end end describe 'conversion to other formats' do it "should convert the individual color values back correctly" do to_truecolor_bytes(@opaque).should == [10, 100, 150] to_truecolor_alpha_bytes(@non_opaque).should == [10, 100, 150, 100] end end describe '#compose' do it "should use the foregorund color as is when the background color is fully transparent" do compose(@non_opaque, @fully_transparent).should == @non_opaque end it "should use the foregorund color as is when an opaque color is given as foreground color" do compose(@opaque, @white).should == @opaque end it "should use the background color as is when a fully transparent pixel is given as foreground color" do compose(@fully_transparent, @white).should == @white end it "should compose pixels correctly with both algorithms" do compose_quick(@non_opaque, @white).should == 0x9fc2d6ff compose_precise(@non_opaque, @white).should == 0x9fc2d6ff end end describe '#decompose_alpha' do it "should decompose the alpha channel correctly" do decompose_alpha(0x9fc2d6ff, @opaque, @white).should == 0x00000064 end it "should return fully transparent if the background channel matches the resulting color" do decompose_alpha(0xabcdefff, 0xff000000, 0xabcdefff).should == 0x00 end it "should return fully opaque if the background channel matches the mask color" do decompose_alpha(0xff000000, 0xabcdefff, 0xabcdefff).should == 0xff end it "should return fully opaque if the resulting color matches the mask color" do decompose_alpha(0xabcdefff, 0xabcdefff, 0xffffffff).should == 255 end end describe '#blend' do it "should blend colors correctly" do blend(@opaque, @black).should == 0x05324bff end it "should not matter what color is used as foreground, and what as background" do blend(@opaque, @black).should == blend(@black, @opaque) end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/datastream_spec.rb000066400000000000000000000021641212556323000246170ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Datastream do describe '.from_io'do it "should raise an error when loading a file with a bad signature" do filename = resource_file('damaged_signature.png') lambda { ChunkyPNG::Datastream.from_file(filename) }.should raise_error end it "should raise an error if the CRC of a chunk is incorrect" do filename = resource_file('damaged_chunk.png') lambda { ChunkyPNG::Datastream.from_file(filename) }.should raise_error end end describe '#metadata' do it "should load uncompressed tXTt chunks correctly" do filename = resource_file('text_chunk.png') ds = ChunkyPNG::Datastream.from_file(filename) ds.metadata['Title'].should == 'My amazing icon!' ds.metadata['Author'].should == "Willem van Bergen" end it "should load compressed zTXt chunks correctly" do filename = resource_file('ztxt_chunk.png') ds = ChunkyPNG::Datastream.from_file(filename) ds.metadata['Title'].should == 'PngSuite' ds.metadata['Copyright'].should == "Copyright Willem van Schaik, Singapore 1995-96" end end end chunky_png-chunky_png-1.2.8/spec/chunky_png/dimension_spec.rb000066400000000000000000000030271212556323000244560ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Dimension do subject { ChunkyPNG::Dimension.new(2, 3) } it { should respond_to(:width) } it { should respond_to(:height) } describe '#area' do it "should calculate the area correctly" do subject.area.should == 6 end end end describe 'ChunkyPNG.Dimension' do subject { ChunkyPNG::Dimension.new(1, 2) } it "should create a dimension from a 2-item array" do ChunkyPNG::Dimension([1, 2]).should == subject ChunkyPNG::Dimension(['1', '2']).should == subject end it "should create a dimension from a hash with x and y keys" do ChunkyPNG::Dimension(:width => 1, :height => 2).should == subject ChunkyPNG::Dimension('width' => '1', 'height' => '2').should == subject end it "should create a dimension from a point-like string" do [ ChunkyPNG::Dimension('1,2'), ChunkyPNG::Dimension('1 2'), ChunkyPNG::Dimension('(1 , 2)'), ChunkyPNG::Dimension("{1x2}"), ChunkyPNG::Dimension("[1\t2}"), ].all? { |point| point == subject } end it "should create a dimension from an object that responds to width and height" do mock_object = mock('Some object with width and height', :width => 1, :height => 2) ChunkyPNG::Dimension(mock_object).should == subject end it "should raise an exception if the input is not understood" do lambda { ChunkyPNG::Dimension(Object.new) }.should raise_error(ArgumentError) lambda { ChunkyPNG::Dimension(1, 2, 3) }.should raise_error(ArgumentError) end end chunky_png-chunky_png-1.2.8/spec/chunky_png/image_spec.rb000066400000000000000000000015011212556323000235460ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Image do describe '#metadata' do it "should load metadata from an existing file" do image = ChunkyPNG::Image.from_file(resource_file('text_chunk.png')) image.metadata['Title'].should == 'My amazing icon!' image.metadata['Author'].should == 'Willem van Bergen' end it "should write metadata to the file correctly" do filename = resource_file('_metadata.png') image = ChunkyPNG::Image.new(10, 10) image.metadata['Title'] = 'My amazing icon!' image.metadata['Author'] = 'Willem van Bergen' image.save(filename) metadata = ChunkyPNG::Datastream.from_file(filename).metadata metadata['Title'].should == 'My amazing icon!' metadata['Author'].should == 'Willem van Bergen' end end endchunky_png-chunky_png-1.2.8/spec/chunky_png/point_spec.rb000066400000000000000000000050411212556323000236200ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Point do subject { ChunkyPNG::Point.new(1, 2) } it { should respond_to(:x) } it { should respond_to(:y) } describe '#within_bounds?' do it { should be_within_bounds(2, 3) } it { should_not be_within_bounds('1x3') } it { should_not be_within_bounds(2, 2) } it { should_not be_within_bounds('[1 2]') } end describe '#<=>' do it "should return 0 if the coordinates are identical" do (subject <=> ChunkyPNG::Point.new(1, 2)).should == 0 end it "should return -1 if the y coordinate is smaller than the other one" do (subject <=> ChunkyPNG::Point.new(1, 3)).should == -1 (subject <=> ChunkyPNG::Point.new(0, 3)).should == -1 # x doesn't matter (subject <=> ChunkyPNG::Point.new(2, 3)).should == -1 # x doesn't matter end it "should return 1 if the y coordinate is larger than the other one" do (subject <=> ChunkyPNG::Point.new(1, 0)).should == 1 (subject <=> ChunkyPNG::Point.new(0, 0)).should == 1 # x doesn't matter (subject <=> ChunkyPNG::Point.new(2, 0)).should == 1 # x doesn't matter end it "should return -1 if the x coordinate is smaller and y is the same" do (subject <=> ChunkyPNG::Point.new(2, 2)).should == -1 end it "should return 1 if the x coordinate is larger and y is the same" do (subject <=> ChunkyPNG::Point.new(0, 2)).should == 1 end end end describe 'ChunkyPNG.Point' do subject { ChunkyPNG::Point.new(1, 2) } it "should create a point from a 2-item array" do ChunkyPNG::Point([1, 2]).should == subject ChunkyPNG::Point(['1', '2']).should == subject end it "should create a point from a hash with x and y keys" do ChunkyPNG::Point(:x => 1, :y => 2).should == subject ChunkyPNG::Point('x' => '1', 'y' => '2').should == subject end it "should create a point from a point-like string" do [ ChunkyPNG::Point('1,2'), ChunkyPNG::Point('1 2'), ChunkyPNG::Point('(1 , 2)'), ChunkyPNG::Point("{1,\t2}"), ChunkyPNG::Point("[1 2}"), ].all? { |point| point == subject } end it "should create a point from an object that responds to x and y" do mock_object = mock('Some object with x and y', :x => 1, :y => 2) ChunkyPNG::Point(mock_object).should == subject end it "should raise an exception if the input is not understood" do lambda { ChunkyPNG::Point(Object.new) }.should raise_error(ArgumentError) lambda { ChunkyPNG::Point(1, 2, 3) }.should raise_error(ArgumentError) end end chunky_png-chunky_png-1.2.8/spec/chunky_png/rmagick_spec.rb000066400000000000000000000012151212556323000241030ustar00rootroot00000000000000require 'spec_helper' begin require 'chunky_png/rmagick' describe ChunkyPNG::RMagick do it "should import an image from RMagick correctly" do image = Magick::Image.read(resource_file('composited.png')).first canvas = ChunkyPNG::RMagick.import(image) canvas.should == reference_canvas('composited') end it "should export an image to RMagick correctly" do canvas = reference_canvas('composited') image = ChunkyPNG::RMagick.export(canvas) image.format = 'PNG32' canvas.should == ChunkyPNG::Canvas.from_blob(image.to_blob) end end rescue LoadError => e # skipping RMagick tests end chunky_png-chunky_png-1.2.8/spec/chunky_png/vector_spec.rb000066400000000000000000000063441212556323000240000ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG::Vector do subject { ChunkyPNG::Vector.new([ChunkyPNG::Point.new(2, 5), ChunkyPNG::Point.new(1, 3), ChunkyPNG::Point.new(4, 6)]) } it { should respond_to(:points) } it { should have(3).items } describe '#x_range' do it "should get the right range of x values" do subject.x_range.should == (1..4) end it "should find the minimum x-coordinate" do subject.min_x.should == 1 end it "should find the maximum x-coordinate" do subject.max_x.should == 4 end it "should calculate the width correctly" do subject.width.should == 4 end end describe '#y_range' do it "should get the right range of y values" do subject.y_range.should == (3..6) end it "should find the minimum x-coordinate" do subject.min_y.should == 3 end it "should find the maximum x-coordinate" do subject.max_y.should == 6 end it "should calculate the height correctly" do subject.height.should == 4 end end describe '#offset' do it "should return a ChunkyPNG::Point" do subject.offset.should be_kind_of(ChunkyPNG::Point) end it "should use the mininum x and y coordinates as values for the point" do subject.offset.x.should == subject.min_x subject.offset.y.should == subject.min_y end end describe '#dimension' do it "should return a ChunkyPNG::Dimension" do subject.dimension.should be_kind_of(ChunkyPNG::Dimension) end it "should use the width and height of the vector for the dimension" do subject.dimension.width.should == subject.width subject.dimension.height.should == subject.height end end describe '#edges' do it "should get three edges when closing the path" do subject.edges(true).to_a.should == [[ChunkyPNG::Point.new(2, 5), ChunkyPNG::Point.new(1, 3)], [ChunkyPNG::Point.new(1, 3), ChunkyPNG::Point.new(4, 6)], [ChunkyPNG::Point.new(4, 6), ChunkyPNG::Point.new(2, 5)]] end it "should get two edges when not closing the path" do subject.edges(false).to_a.should == [[ChunkyPNG::Point.new(2, 5), ChunkyPNG::Point.new(1, 3)], [ChunkyPNG::Point.new(1, 3), ChunkyPNG::Point.new(4, 6)]] end end end describe 'ChunkyPNG.Vector' do let(:example) { ChunkyPNG::Vector.new([ChunkyPNG::Point.new(2, 4), ChunkyPNG::Point.new(1, 2), ChunkyPNG::Point.new(3, 6)]) } it "should return an empty vector when given an empty array" do ChunkyPNG::Vector().should == ChunkyPNG::Vector.new([]) ChunkyPNG::Vector(*[]).should == ChunkyPNG::Vector.new([]) end it "should raise an error when an odd number of numerics is given" do lambda { ChunkyPNG::Vector(1, 2, 3) }.should raise_error(ArgumentError) end it "should create a vector from a string" do ChunkyPNG::Vector('(2,4) (1,2) (3,6)').should == example end it "should create a vector from a flat array" do ChunkyPNG::Vector(2,4,1,2,3,6).should == example end it "should create a vector from a nested array" do ChunkyPNG::Vector('(2,4)', [1, 2], :x => 3, :y => 6).should == example end end chunky_png-chunky_png-1.2.8/spec/chunky_png_spec.rb000066400000000000000000000002321212556323000224640ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG do it "should have a VERSION constant" do ChunkyPNG.const_defined?('VERSION').should be_true end end chunky_png-chunky_png-1.2.8/spec/png_suite/000077500000000000000000000000001212556323000207605ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/000077500000000000000000000000001212556323000244525ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/bgai4a08.png000077500000000000000000000003261212556323000264630ustar00rootroot00000000000000PNG  IHDR tgAMA1_IDATx 0 ֽA((sOAVG":ݙbH$ @ɾzM2x<7U[0t<n!Y~.,>RfqXAh٪wϤ50o0N N6 O= YU]IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/bgai4a16.png000077500000000000000000000054471212556323000264730ustar00rootroot00000000000000PNG  IHDR ^gAMA1_ IDATxpTu?Z=IowB2O߄5f pњf4b8O5vH\ Cc7Ԯ#-+06P ЂzW+tOL2m%K,: 뻇oތ! ~>6K2m4?)u7,㍝4ueqCYcgg' i!lB4M\x2s&߆OiҮC-hDýgt<Kzz)]}?30.W\ZQKiپnp7Bn(A ;`n~"OÎ, :X@apy(b.7 9-#X1s;WhPfS!6ئrͬ’(T(o+ܟ| KWcf޶܀ fC>A y(kfPEMpx4t! ٟ%kx370%8$NA-!xzX!jl5+ .S3;-[W~u>Gtu,_WՁS7_ի:aTnZM߳0ctµvx,jq(VAyOjoBe-oA>k+?3V̮ū!xS [3O01 'wj칷d/SU槜wpx6kfi]^.#jkFS dF0,~Fef[7}^X݆@-xOC^#AQA ̋ɇ2B*Kik * \,koBZJ<w||&f(o@)Lirm^[Я.OFJ[_ye_uJȿ:)d!?eC;2zFW^'d2)d2#z3\8MoU(P̮Yӗ,Q lȞuN]s#ǯ\++rB|\H!e7n5]߷#BR r ,9gG$  ; &(\ ?k%{M+0lrow[O2Ip An;nC[0c5 {r Fچl늺fVXH] Y6_{6 Y-%B !?2uHM̋B45VL? cp>f֖ܷfEW!+7oB`30~&k74z<©̋uC_?0/MoB3 cV2|*( aHA$aAy٫.}fs]yu]uN΁Uh = =Ms>=ㅢ0%-C/ SRRaHv {a`z>C0EPs ʜGB't:p4t@IBK,# )ȏ7 _MHoFH,k*nvR3΅!nw!WIЅQ2 o? 7O0`7ȓ0eHTlͷ5 ƺ ?@w.3`>aoW phJς~ CoA&A>cQHoR): ӡ\^7_cbtƮSQr '!<PoA^r]P+a"0$L0d2&ȝ0`pttͥ4Wce_׋ȺYqa@f3dR 0\N~ UTcZ$XCϙ!C(9q>3Z_3=g@ BޛW;I`RU}]N;ծTC.(σ@3;`|ۡh thA ϙHZ7A4۞$lZHxjks# !(M@LO@ ,{022 ' }g|P\_{@MLG25 >i!'505-0e 䅜Bk)F[a"J+[4Bo+PA(x|%EqXk@G>3c٤ehu![HPSB 9!! 9uyBROH)!}B?+Bz]H6!f9z@ȱ+*!'~ $_B*T2gys >!H{I9lIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/bgan6a08.png000077500000000000000000000002701212556323000264700ustar00rootroot00000000000000PNG  IHDR szzgAMA1_oIDATx1 0 F'dhO?U!ExRP(M(ي0^{~3uG XN5"}\TB\.y 6{@<P6@R LIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/bgan6a16.png000077500000000000000000000065531212556323000265010ustar00rootroot00000000000000PNG  IHDR #ꦷgAMA1_ "IDATxݙ_luKrM.ZKʴRM Y.R2Z[Ɋ^qb,ZɊ (0PrB&\)(D~D4KK/9wUU4AΝ;ww|猕i |ؘ}}+M!3߀ޱ&}߯ZfF5[,@f.3rZtL017qHÿto Z?TR4٢:O~-{D`kEU4")sZHfn2@6RtXO)ޯ;voR'zֽO۴=5ߣDOqD Oc3`co47e k$9SQ aȗ~U5`:OE5CJ :u'|VMt?\3%'*JS_7W&; 42@THtZ]m'Ի6tmrwK'Z׉^щ~QCbV b^NJ^Jr宁Z S _XfxNEOfͅz-/?ջu5Dp^vGVˏGW5| %˿\5ėf%XYhNf uSSn6Ys;;:p-'AW:UGpxqB?7GsZ g`/f@%6mW#r?zf7k.{zzz _NF٪ At5Huta>4u-⋋=wTK\*< e7 M@x޶"&nܛ9f5|>IEtSqEUvvhƫ ^R\TjVz=XtodɎap~~ p}c ׍{3lvB}ކ|9:^TyJz5%WkFƫ%X go|h7#aL$7u)7ݬP?*>WU4^Y+/WZO?{1+^ |{ w u'0oh5HM@3~ݸ7sf7k.7e-/NK?RxZ s'@/eAY@ [?p| HQtu yfKϼQ-ѪHM@3~ݸ7sf7k.ԇ _|IV?4ħځN,'[3kXf|>o$_NY%an }MEFj0&fqo攛n\~|[~|!~ݻj%VyN&#āHFq@ ҊU;2t`@2zKoT۵3=-nj7#aL$7Vlvkeݻμ K@pR (#8grRKX4L;V݌14׍{3\67aZi.3o~`i޻ {pi 3 =)opQqdv%)3{>5XaXiH4ь̤+[h7#aL$׀fiZV!_ Cɽ?x~;w1[o zDE3ؓI~]Rl~%yZ+1S5ISZj݈VF˜H.~xVv: N]mx⁍/43}Ȉ c'ݛ l/Ib*aS1Q&1%?3j݈VD\9߭;wR'3j=K3Y ҕx{vi 1\[SΦx>lOcJF ޒ4ŨuWGjO9hn$83*}}§Rw~;@4k7syv|aE%aL>  j0&3gƫˁȽ_BAqޓ=vA&folnon4|6jd.dndoGq?eí۵IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/bgbn4a08.png000077500000000000000000000002141212556323000264650ustar00rootroot00000000000000PNG  IHDR sgAMA1_bKGD#25IDATxch41",(,?a0T1`4GÀ*hP* }IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/background_chunks/bggn4a16.png000077500000000000000000000042541212556323000265010ustar00rootroot00000000000000PNG  IHDR n<gAMA1_bKGD )UIDATxŗ_h[cHuRGT(8B-P.%4BiKaw]HɐXf%$zmiJWGc9w~t5O>>`eaX=`Ԃ±8G?]e _^X.wGO3wd3 gZú|'fef[Z!p&ކuh`c:oȇtri^g)X]UVZ l-5C`T$u!Z??p߅Cgj@dƱt-YY˅ae}';Ȁ~B@3 x'͌B ^SA#nr2r:QfGR`fAdv <@_"AÃևðqXZ3S:܀{Ճ< ';B 1/s,ZYߣ` `pg=U<@#뿃נ/`6,њ̀U]轏@`7&kc{ ? %:4o6JވaKGaɄ ? @= !e/sVVCpA5AhΠ.Ak%,?%!V[?hv@[/  !~Q`ZJHV@ס, \Z-tVKuVai *(a0ס  =i0Vކ.a{hQf\5 |ee8 l9QF haeXC9wnA D`6_Bh^SygLgv]~dA_ lwSR; I[VF&;5l :F$g 8h=ox yot| 'Q2 ,44c94O!1DӲͷ+3߲M:$m+?=*|~5{Wb zC`W݄辥< Cu㰈"j Q}8p|$;>.;/؏a 4/@ н[/E})x6훏m^=t)y#!ËGT/So SMO]]#7*gǏl!qXkdQa59G2 5e5$l (+畸2ۥ_$`Wb?@0 9E{Bk80/ƲVRZYxYf\ Nv{k By_@SaJcKjT𔥞cVE^>[v]7R>܉\i@)]SBBy$Ғh('eϭia;cҹ; МkX]HM%I%ŎUz]„Xj"22CO+h ɴ )oF\ܼ27 h+' wCQRY\-4-dA.Hax4,@ᜲ$o{ dJ;v,& C˧T$3ӔgVWsϓ<yhB# FP!v Vޔl X$p]y]\._s=,l7Gd){&TLK(}Z)h+V %@{gd"iiqૢ]Z^<y wPg+ЮB;  s%/ڟ gU<=DPw?({!l7d̀t:9-xIb z 4thFk2*<#~WQ\ 4eύpNf.J,OAdžN 6lf-ȌcGR3*%dE((\0){n$KJ3`38`ek+-w53&7yiIq4u-⋋=wTK\*< e7 M@x޶"&nܛ9f5|>IEtSqEUvvhƫ ^R\TjVz=XtodɎap~~ p}c ׍{3lvB}ކ|9:^TyJz5%WkFƫ%X go|h7#aL$7u)7ݬP?*>WU4^Y+/WZO?{1+^ |{ w u'0oh5HM@3~ݸ7sf7k.7e-/NK?RxZ s'@/eAY@ [?p| HQtu yfKϼQ-ѪHM@3~ݸ7sf7k.ԇ _|IV?4ħځN,'[3kXf|>o$_NY%an }MEFj0&fqo攛n\~|[~|!~ݻj%VyN&#āHFq@ ҊU;2t`@2zKoT۵3=-nj7#aL$7Vlvkeݻμ K@pR (#8grRKX4L;V݌14׍{3\67aZi.3o~`i޻ {pi 3 =)opQqdv%)3{>5XaXiH4ь̤+[h7#aL$׀fiZV!_ Cɽ?x~;w1[o zDE3ؓI~]Rl~%yZ+1S5ISZj݈VF˜H.~xVv: N]mx⁍/43}Ȉ c'ݛ l/Ib*aS1Q&1%?3j݈VD\9߭;wR'3j=K3Y ҕx{vi 1\[SΦx>lOcJF ޒ4ŨuWGjO9hn$83*}}§Rw~;@4k7syv|aE%aL>  j0&3gƫˁȽ_BAqޓ=vA&folnon4|6jd.dndoGq?eí۵IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/000077500000000000000000000000001212556323000220415ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g01.png000077500000000000000000000003311212556323000240550ustar00rootroot00000000000000PNG  IHDR ,wgAMA1_IDATx-10 EƂz.z' V9cX,e5|KxOp笹pi\Yc*L'Dd[6癹+MKOKzҍ2-czpEHp-/zQ!塌Qf"IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g01.rgba000066400000000000000000000100001212556323000241730ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g02.png000077500000000000000000000002321212556323000240560ustar00rootroot00000000000000PNG  IHDR k gAMA1_QIDATxcPb`r`p16nDgg]!dDCH dJHR? 0A 9RM}IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g02.rgba000066400000000000000000000100001212556323000241740ustar00rootroot00000000000000UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUchunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g04.png000077500000000000000000000003671212556323000240710ustar00rootroot00000000000000PNG  IHDR gAMA1_IDATxeQ0D:(u AAAAA$TI~ò¶EuEĺCsGjw<# ^bs8Al.iGZ'(CYd:"k@i2Gpr:1(Kkce s {ig 826N'MIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g04.rgba000066400000000000000000000100001212556323000241760ustar00rootroot00000000000000""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwchunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g08.png000077500000000000000000000003761212556323000240750ustar00rootroot00000000000000PNG  IHDR !gAMA1_IDATx] 0B{C%>x!Kܦ$]!2,$UIBH"*V$$ŏ Jl, lܲ 9:eSW}q@Şp쿣;_|?/ >>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!    !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!  chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g16.png000077500000000000000000000004531212556323000240700ustar00rootroot00000000000000PNG  IHDR qgAMA1_IDATx;0DI ]0S ,D @C#IVhk& \lX5R 2'+d~G @4 @4߁@\\y9۩ܵEkQט-} Q FAQ wbk@|DVIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi0g16.rgba000066400000000000000000000100001212556323000242010ustar00rootroot00000000000000 $$$---666???HHHQQQZZZcccllluuu~~~ &&&///888AAAJJJSSS\\\eeennnwww (((111:::CCCLLLUUU^^^gggpppyyy!!!***333<<>>GGGPPPYYYbbbkkkttt}}} %%%...777@@@IIIRRR[[[dddmmmvvv '''000999BBBKKKTTT]]]fffoooxxx )))222;;;DDDMMMVVV___hhhqqqzzz"""+++444===FFFOOOXXXaaajjjsss|||$$$---666???HHHQQQZZZcccllluuu~~~&&&///888AAAJJJSSS\\\eeennnwww~~~(((111:::CCCLLLUUU^^^gggpppyyyxxx!!!***333<<>>GGGPPPYYYbbbkkkttt}}}lll%%%...777@@@IIIRRR[[[dddmmmvvvfff'''000999BBBKKKTTT]]]fffoooxxx{{{``` )))222;;;DDDMMMVVV___hhhqqqzzzuuuZZZ"""+++444===FFFOOOXXXaaajjjsss|||oooTTT$$$---666???HHHQQQZZZcccllluuu~~~iiiNNN&&&///888AAAJJJSSS\\\eeennnwww~~~cccHHH(((111:::CCCLLLUUU^^^gggpppyyyxxx]]]BBB***333<<>>GGGPPPYYYbbbkkkttt}}}lllQQQ666...777@@@IIIRRR[[[dddmmmvvvfffKKK000000999BBBKKKTTT]]]fffoooxxx{{{```EEE***222;;;DDDMMMVVV___hhhqqqzzzuuuZZZ???$$$444===FFFOOOXXXaaajjjsss|||oooTTT999666???HHHQQQZZZcccllluuu~~~iiiNNN333888AAAJJJSSS\\\eeennnwww~~~cccHHH---:::CCCLLLUUU^^^gggpppyyyxxx]]]BBB''' <<>>GGGPPPYYYbbbkkkttt}}}lllQQQ666chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi2c08.png000077500000000000000000000004731212556323000240710ustar00rootroot00000000000000PNG  IHDR 5gAMA1_IDATxՓA! D{x=Ys3hhf gZYd1b|V%}֠Ɯ7~gv>^/-Jcm smXTmcm @=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!  chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi2c16.png000077500000000000000000000011231212556323000240610ustar00rootroot00000000000000PNG  IHDR ۏvgAMA1_ IDATxՖ!s0?8a>Vx?0.;Xx0p0 SW+c Y}+EI$ ottЅ Awj88:A?/ĠnqsT`z!gAMA1_PLTE""fl&)IDATxc`P CѰ1Ù3IH6?@@!( B-vÞIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p01.rgba000066400000000000000000000100001212556323000242070ustar00rootroot00000000000000"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f""""chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p02.png000077500000000000000000000003011212556323000240670ustar00rootroot00000000000000PNG  IHDR ygAMA1_sBIT|.w PLTEe?+QIDATxcxǰ]̌!9J޸b.??dCH dJHR?t#B,*9Z}uIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p02.rgba000066400000000000000000000100001212556323000242100ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p04.png000077500000000000000000000005071212556323000241010ustar00rootroot00000000000000PNG  IHDR SWQgAMA1_sBITw-PLTE""fwDDҰIIDATxcc8N%(KS,1,ľ @ _B$ ,qP=L(-]]tލ0@L1 RL1pOGh3V{7s&*WPS)ʽ{Sˀ)UR@妥a rwvh t_IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p04.rgba000066400000000000000000000100001212556323000242120ustar00rootroot00000000000000ffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDwwww""""DDDDwwww""""DDDDwwww""""DDDDwwww""""DDDDwwww""""DDDDDDDDwwww""""DDDDDDDDwwww""""DDDDDDDDwwww""""DDDDDDDDwwww""""DDDDDDDD""""wwww""""DDDDDDDD""""wwww""""DDDDDDDD""""wwww""""DDDDDDDD""""""""DDDDDDDD""""""""DDDDDDDD""""""""DDDDDDDD""""""""DDDDDDDD""""DDDDDDDD""""DDDDDDDD""""DDDDDDDD""""DDDDDDDD""""DDDD""""DDDD""""DDDD""""DDDD""""chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p08.png000077500000000000000000000027671212556323000241170ustar00rootroot00000000000000PNG  IHDR 3PgAMA1_PLTE"Dww :w""""Uffff"DDUU"DDUU3DDff3D"ffD33U*D˺[""f2Ucw:DDkfBkܺ33sJw{"w332fDwJf""UUDff3UwwDwffD"w"3333cU3{UUUUf܃wwUUww""DD3ԪU*U˴f3BSD̙"Sww333Ĉwff""UU"DD[wfwws33wD""U"f 3bIDATxei\ 7JMc))IDXQaѡ29s%"B!W*M%:9Z<}RE4XL1M.5#\HmP!BzԞ]Be&hAViڍ5Fױ{Y.L؛# 0Mun`7%RYH5!C33;;׷522%JK *+}|ظյ()JJHY'00/OI),LW7< sr::mm)>>&&*JNBKK_22x< 0: fiAޞ]\"rqa2|ۛdkj&&zz֦v 05MMuvnh`0TTd9:ƆFDȶmn%53 ke(/^S#Av/#,0 '0N5X S vc0jXw`  p>U0 N x!<۰ .^ nZs` z\?0! oa<~Np&023 0 tx C8, `#G0srIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi3p08.rgba000066400000000000000000000100001212556323000242160ustar00rootroot00000000000000        """"""""""""""""""""""""""""""""""""""""""""33333333333333333333333333333333333333333333DDDDD"D"D"D"DDDDDDDD"D"D"D"DDDDDDDDDDDDDDDDDDDDDDDDDUUUUU*U*U*U*UUUUUUUU*U*U*U*UUUUUUUUUUUUUUUUUUUUUUUUUfffff2f2f2f2ffffffff2f2f2f2fffffffffffffffffffffffffwwwww:w:w:w:wwwwwwww:w:w:w:wwwwwwwwwwwwwwwwwwwwwwwwwBBBBBBBBJJJJJJJJSSSSSSSS[[[[[[[[cccccccckkkkkkkkssssssss{{{{{{{{""""""""""""""""""""""""""""""""""""""""""""33333333333333333333333333333333333333333333DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUffffffffffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwĈĈĈĈ̙̙̙̙ԪԪԪԪܺܺܺܺchunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi4a08.png000077500000000000000000000003261212556323000240660ustar00rootroot00000000000000PNG  IHDR tgAMA1_IDATx 0 ֽA((sOAVG":ݙbH$ @ɾzM2x<7U[0t<n!Y~.,>RfqXAh٪wϤ50o0N N6 O= YU]IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi4a08.rgba000066400000000000000000000100001212556323000242000ustar00rootroot00000000000000 )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ރދޔޜޤެ޴޽ )19AJRZbjs{ՃՋՔ՜դլմս )19AJRZbjs{͔̓͋ͤͬ͜ʹͽ )19AJRZbjs{ŃŋŔŜŤŬŴŽ )19AJRZbjs{Žͽս޽潽 )19AJRZbjs{Ŵʹմ޴洴 )19AJRZbjs{Ŭͬլެ欬 )19AJRZbjs{Ťͤդޤ椤 )19AJRZbjs{Ŝ͜՜ޜ朜 )19AJRZbjs{Ŕ͔Քޔ攔 )19AJRZbjs{ŋ͋Ջދ拋 )19AJRZbjs{Ń̓Ճރ惃{{{{{{{{{{{{{{{ {{{){{{1{{{9{{{A{{{J{{{R{{{Z{{{b{{{j{{{s{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{sssssssssssssss sss)sss1sss9sssAsssJsssRsssZsssbsssjsssssss{ssssssssssssssssssssssssssssssssssssssssssssssssjjjjjjjjjjjjjjj jjj)jjj1jjj9jjjAjjjJjjjRjjjZjjjbjjjjjjjsjjj{jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjbbbbbbbbbbbbbbb bbb)bbb1bbb9bbbAbbbJbbbRbbbZbbbbbbbjbbbsbbb{bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbZZZZZZZZZZZZZZZ ZZZ)ZZZ1ZZZ9ZZZAZZZJZZZRZZZZZZZbZZZjZZZsZZZ{ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZRRRRRRRRRRRRRRR RRR)RRR1RRR9RRRARRRJRRRRRRRZRRRbRRRjRRRsRRR{RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRJJJJJJJJJJJJJJJ JJJ)JJJ1JJJ9JJJAJJJJJJJRJJJZJJJbJJJjJJJsJJJ{JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJAAAAAAAAAAAAAAA AAA)AAA1AAA9AAAAAAAJAAARAAAZAAAbAAAjAAAsAAA{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA999999999999999 999)99919999999A999J999R999Z999b999j999s999{999999999999999999999999999999999999999999999999111111111111111 111)11111119111A111J111R111Z111b111j111s111{111111111111111111111111111111111111111111111111))))))))))))))) )))))))1)))9)))A)))J)))R)))Z)))b)))j)))s))){))))))))))))))))))))))))))))))))))))))))))))))))    ) 1 9 A J R Z b j s {  )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi4a16.png000077500000000000000000000054471212556323000240760ustar00rootroot00000000000000PNG  IHDR ^gAMA1_ IDATxpTu?Z=IowB2O߄5f pњf4b8O5vH\ Cc7Ԯ#-+06P ЂzW+tOL2m%K,: 뻇oތ! ~>6K2m4?)u7,㍝4ueqCYcgg' i!lB4M\x2s&߆OiҮC-hDýgt<Kzz)]}?30.W\ZQKiپnp7Bn(A ;`n~"OÎ, :X@apy(b.7 9-#X1s;WhPfS!6ئrͬ’(T(o+ܟ| KWcf޶܀ fC>A y(kfPEMpx4t! ٟ%kx370%8$NA-!xzX!jl5+ .S3;-[W~u>Gtu,_WՁS7_ի:aTnZM߳0ctµvx,jq(VAyOjoBe-oA>k+?3V̮ū!xS [3O01 'wj칷d/SU槜wpx6kfi]^.#jkFS dF0,~Fef[7}^X݆@-xOC^#AQA ̋ɇ2B*Kik * \,koBZJ<w||&f(o@)Lirm^[Я.OFJ[_ye_uJȿ:)d!?eC;2zFW^'d2)d2#z3\8MoU(P̮Yӗ,Q lȞuN]s#ǯ\++rB|\H!e7n5]߷#BR r ,9gG$  ; &(\ ?k%{M+0lrow[O2Ip An;nC[0c5 {r Fچl늺fVXH] Y6_{6 Y-%B !?2uHM̋B45VL? cp>f֖ܷfEW!+7oB`30~&k74z<©̋uC_?0/MoB3 cV2|*( aHA$aAy٫.}fs]yu]uN΁Uh = =Ms>=ㅢ0%-C/ SRRaHv {a`z>C0EPs ʜGB't:p4t@IBK,# )ȏ7 _MHoFH,k*nvR3΅!nw!WIЅQ2 o? 7O0`7ȓ0eHTlͷ5 ƺ ?@w.3`>aoW phJς~ CoA&A>cQHoR): ӡ\^7_cbtƮSQr '!<PoA^r]P+a"0$L0d2&ȝ0`pttͥ4Wce_׋ȺYqa@f3dR 0\N~ UTcZ$XCϙ!C(9q>3Z_3=g@ BޛW;I`RU}]N;ծTC.(σ@3;`|ۡh thA ϙHZ7A4۞$lZHxjks# !(M@LO@ ,{022 ' }g|P\_{@MLG25 >i!'505-0e 䅜Bk)F[a"J+[4Bo+PA(x|%EqXk@G>3c٤ehu![HPSB 9!! 9uyBROH)!}B?+Bz]H6!f9z@ȱ+*!'~ $_B*T2gys >!H{I9lIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basi4a16.rgba000066400000000000000000000100001212556323000241770ustar00rootroot00000000000000!!!111BBBRRRcccsssssscccRRRBBB111!!!###444FFFXXXiii{{{{{{iiiXXXFFF444###!!!!!%%%!888!KKK!^^^!qqq!!!!!!!!!!!!!!!qqq!^^^!KKK!888!%%%!!!!!!111###!11(((1===1QQQ1fff1zzz1111111111111zzz1fff1QQQ1===1(((111!###111BBB444%%%!1BB,,,BBBBBYYYBoooBBBBBBBBBBBBBoooBYYYBBBBB,,,BBB1%%%!444BBBRRRFFF888!(((1BRR000RIIIRaaaRyyyRRRRRRRRRRRyyyRaaaRIIIR000RRRB(((1888!FFFRRRcccXXXKKK!===1,,,BRcc555cPPPckkkccccccccccckkkcPPPc555cccR,,,B===1KKK!XXXcccsssiii^^^!QQQ1BBBB000Rcss<<SͭVUgTYъ2sAw厊D|Ɏ$d:|sGW#ߘ,  PU*Ԇ͒4C=3Q86sS-C?"SWkK/:[l; ~-&g]۔Bj+SwݗTQ@id'D|_0$[]d)u]3/U'NJ&:=V!``wS W uTBWAUD|UjՒ9U>z1ulh0,9h H=0HF-_0M-R_V[[TSϪ C7dg$@~s9%@Vm֋㔷ޓKN]]35 _#igT}ޙ3c\s  {$)cng*c4&'[9X`}'p 1g>"@BlOjbc%Q4Ի`RVUuH3 eMл@,Lr%е̬i(?XQ-7z(m626 ~Pt2o@ċPgCƁy.`$Jg]q0pUzOs$ɒ%}䦿^)cdmXrSd8j -7!ܘNSו qpH@8fʹn]Ʉ*yU.O.~UZ_4ö*p2Ib늦-D/.l_YY0E%; )7eBkGD!-7KJ@&ڕI:Mi㊽FaPŀ!oyF(Qp\rs@I{5w_8*-#yfvC۫"ʒ*%&%iO:6@8-'*S,dHNn-L] 8tJo26VA8:jgM hw*OogY=|=@j>@3mq2N)@U̿)*K KGk;aR<\/| bp:C=&R2}F2\r~_Wڮێ5e:DV[:850b-{l4K uAzPY/V.yA p H%L;k*h gZ1=LlС \m$M=_/ ,50!2[s!B}n(ӽϽԡ}" RP\WzHlA+50$!8oo E3@Et6BشpdGC[ PHN> K.`C +P!ҿo!_aCA-CLnQhr ->u157"v0  a{6reDCB%[>C> }"03a8:LR ,0D=4C܆">$D@n1v]f=g3b1/BPv@͐!Xڌ.&Aö g>7jYJ} ^k @jm&+ceVUhlMTB+ o5@*ot'|զޱVL]vQP_ Z40+XhV{#I+f3*#-Y;;ɗi@^rBXbVQAD{0b_l $@ p3,n<(nPPޑ(-Z4%~eE"RR`2JY]Z1 _0ߗ(([Zc2uH.t w%@fg@fCҧE @II(ZD 흧U) E@ZBR;ijaKX,2uHtbLֻ~{ p*_|Vp; 2_:>(ux h2  R[ }d}#VL;.*>m[>:c,gL7piR )qv{.@zW3@aCVS%d6UVpޠTPuu J%z#?&|tE+QOl5jvyԑN-Cϛo7uS!9Cd2Rl{ ŵ 3`/vot.PpSGUY)UV12 P^4Т55 A8Z^۹NK4S)8Y܃rrMۖc;5vCơ R{cҳt/{VS2|WμdvdWBB\>dQ.w- k`"mm#M޷J<K9ʖ\R|+sK~ 8tqm ȫGUV)E*aeWTiRJјuJv8=g韮YtmQKa znޙh;:~+P򶎷JW=)gONzg}f%;in<9_UjCV@N¬ jYehɋ*PH$$q{/ϵP^gѽgCã_n>=Ő XUн:}Lzl4%?  J |!nI/;) ۟M\٫X+NާW4-4A,2z w 9\* KR"2@Lw+tJ()g`qb %!RϞ|O}S+ckY1 V|jT {;cޙPv W"6"!ׁCC8?u2 BcM_np5X' Qvzgn "CgqEL9j[^.V^ck;`m+ jtKOX$F@$Cp#$Ap$*!r Gb; .ਖ?0̀@ r"`s{#dGR-0# #)O&_hPT4y`}~(M C/BmPt!7C]CLksk\c{"8q쉆ppiCxCCd)w!q!6Ca kп0w4 }~C,)gi(>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!    !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!  chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn0g16.png000077500000000000000000000002471212556323000240760ustar00rootroot00000000000000PNG  IHDR kgAMA1_^IDATx1 0 CQ9[ܠ({2*ُ?8Wc:`݂@B&@=2 -hL`?oO8K_+IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn0g16.rgba000066400000000000000000000100001212556323000242060ustar00rootroot00000000000000 $$$---666???HHHQQQZZZcccllluuu~~~ &&&///888AAAJJJSSS\\\eeennnwww (((111:::CCCLLLUUU^^^gggpppyyy!!!***333<<>>GGGPPPYYYbbbkkkttt}}} %%%...777@@@IIIRRR[[[dddmmmvvv '''000999BBBKKKTTT]]]fffoooxxx )))222;;;DDDMMMVVV___hhhqqqzzz"""+++444===FFFOOOXXXaaajjjsss|||$$$---666???HHHQQQZZZcccllluuu~~~&&&///888AAAJJJSSS\\\eeennnwww~~~(((111:::CCCLLLUUU^^^gggpppyyyxxx!!!***333<<>>GGGPPPYYYbbbkkkttt}}}lll%%%...777@@@IIIRRR[[[dddmmmvvvfff'''000999BBBKKKTTT]]]fffoooxxx{{{``` )))222;;;DDDMMMVVV___hhhqqqzzzuuuZZZ"""+++444===FFFOOOXXXaaajjjsss|||oooTTT$$$---666???HHHQQQZZZcccllluuu~~~iiiNNN&&&///888AAAJJJSSS\\\eeennnwww~~~cccHHH(((111:::CCCLLLUUU^^^gggpppyyyxxx]]]BBB***333<<>>GGGPPPYYYbbbkkkttt}}}lllQQQ666...777@@@IIIRRR[[[dddmmmvvvfffKKK000000999BBBKKKTTT]]]fffoooxxx{{{```EEE***222;;;DDDMMMVVV___hhhqqqzzzuuuZZZ???$$$444===FFFOOOXXXaaajjjsss|||oooTTT999666???HHHQQQZZZcccllluuu~~~iiiNNN333888AAAJJJSSS\\\eeennnwww~~~cccHHH---:::CCCLLLUUU^^^gggpppyyyxxx]]]BBB''' <<>>GGGPPPYYYbbbkkkttt}}}lllQQQ666chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn2c08.png000077500000000000000000000002211212556323000240650ustar00rootroot00000000000000PNG  IHDR gAMA1_HIDATx 0 @r;D++ ; }Lx@J„(t8#@pw^@KIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn2c08.rgba000066400000000000000000000100001212556323000242050ustar00rootroot00000000000000~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!  chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn2c16.png000077500000000000000000000004561212556323000240760ustar00rootroot00000000000000PNG  IHDR 1gAMA1_IDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn2c16.rgba000066400000000000000000000100001212556323000242040ustar00rootroot00000000000000{skcZRJB91)!{skcZRJB91)!{skcZRJB91)!{skcZRJB91)!{skcZRJB91)!!{skcZRJB91)!!){skcZRJB91)!!)1{skcZRJB91)!!)19޽ֽνƽ{skcZRJB91)!!)19B޵ֵεƵ{skcZRJB91)!!)19BJޭ֭έƭ{skcZRJB91!))!19BJRޥ֥Υƥ{skcZRJB9!1))1!9BJRZޜ֜ΜƜ{skcZRJB!9)11)9!BJRZcޔ֔ΔƔ{skcZRJ!B)9119)B!JRZckތ֌Όƌ{skcZR!J)B1991B)J!RZcksބք΄Ƅ{skcZ!R)J1B99B1J)R!Zcks{{{{{{{{{{{{{{{{{{{s{k{c{!Z{)R{1J{9B{B9{J1{R){Z!{c{k{s{{{ssssssssssssssss{sssks!cs)Zs1Rs9JsBBsJ9sR1sZ)sc!sksss{sskkkkkkkkkkkkkkkk{ksk!kk)ck1Zk9RkBJkJBkR9kZ1kc)kk!ksk{kkkcccccccccccccccc{c!sc)kc1cc9ZcBRcJJcRBcZ9cc1ck)cs!c{ccccZZZZZZZZZZZZZZZZ!{Z)sZ1kZ9cZBZZJRZRJZZBZc9Zk1Zs)Z{!ZZZZZRRRRRRRRRRRRRRR!R){R1sR9kRBcRJZRRRRZJRcBRk9Rs1R{)R!RRRRRJJJJJJJJJJJJJJ!J)J1{J9sJBkJJcJRZJZRJcJJkBJs9J{1J)J!JJJJJBBBBBBBBBBBBB!B)B1B9{BBsBJkBRcBZZBcRBkJBsBB{9B1B)B!BBBBB999999999999!9)91999B{9Js9Rk9Zc9cZ9kR9sJ9{B99919)9!9999911111111111!1)11191B1J{1Rs1Zk1cc1kZ1sR1{J1B19111)1!11111))))))))))!)))1)9)B)J)R{)Zs)ck)kc)sZ){R)J)B)9)1)))!)))))!!!!!!!!!!!)!1!9!B!J!R!Z{!cs!kk!sc!{Z!R!J!B!9!1!)!!!!!!!!)19BJRZc{kssk{cZRJB91)!!)19BJRZck{ss{kcZRJB91)!!)19BJRZcks{{skcZRJB91)!!)19BJRZcks{{skcZRJB91)!chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p01.png000077500000000000000000000001601212556323000240760ustar00rootroot00000000000000PNG  IHDR IgAMA1_PLTE""fl&IDATxc4?fYIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p01.rgba000066400000000000000000000100001212556323000242140ustar00rootroot00000000000000"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f""""chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p02.png000077500000000000000000000002221212556323000240760ustar00rootroot00000000000000PNG  IHDR ggAMA1_sBIT|.w PLTEe?+"IDATxc0,| =IꉎIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p02.rgba000066400000000000000000000100001212556323000242150ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p04.png000077500000000000000000000003301212556323000241000ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw-PLTE""fwDDҰIGIDATxc =sfժrcwfd C+(H*ŅTݻq@*)#MK#G{7}IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p04.rgba000066400000000000000000000100001212556323000242170ustar00rootroot00000000000000ffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDffffwwww""""DDDDwwww""""DDDDwwww""""DDDDwwww""""DDDDwwww""""DDDDwwww""""DDDDDDDDwwww""""DDDDDDDDwwww""""DDDDDDDDwwww""""DDDDDDDDwwww""""DDDDDDDD""""wwww""""DDDDDDDD""""wwww""""DDDDDDDD""""wwww""""DDDDDDDD""""""""DDDDDDDD""""""""DDDDDDDD""""""""DDDDDDDD""""""""DDDDDDDD""""DDDDDDDD""""DDDDDDDD""""DDDDDDDD""""DDDDDDDD""""DDDD""""DDDD""""DDDD""""DDDD""""chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p08.png000077500000000000000000000024061212556323000241120ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE"Dww :w""""Uffff"DDUU"DDUU3DDff3D"ffD33U*D˺[""f2Ucw:DDkfBkܺ33sJw{"w332fDwJf""UUDff3UwwDwffD"w"3333cU3{UUUUf܃wwUUww""DD3ԪU*U˴f3BSD̙"Sww333Ĉwff""UU"DD[wfwws33wD""U"f 3bIDATx GHd+;3 ekXegа**4h lޣY2W%syiHCL*;8"KE6sx'HK?Y9sģ>1~!'B 0IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn3p08.rgba000066400000000000000000000100001212556323000242230ustar00rootroot00000000000000        """"""""""""""""""""""""""""""""""""""""""""33333333333333333333333333333333333333333333DDDDD"D"D"D"DDDDDDDD"D"D"D"DDDDDDDDDDDDDDDDDDDDDDDDDUUUUU*U*U*U*UUUUUUUU*U*U*U*UUUUUUUUUUUUUUUUUUUUUUUUUfffff2f2f2f2ffffffff2f2f2f2fffffffffffffffffffffffffwwwww:w:w:w:wwwwwwww:w:w:w:wwwwwwwwwwwwwwwwwwwwwwwwwBBBBBBBBJJJJJJJJSSSSSSSS[[[[[[[[cccccccckkkkkkkkssssssss{{{{{{{{""""""""""""""""""""""""""""""""""""""""""""33333333333333333333333333333333333333333333DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUffffffffffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwĈĈĈĈ̙̙̙̙ԪԪԪԪܺܺܺܺchunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn4a08.png000077500000000000000000000001761212556323000240760ustar00rootroot00000000000000PNG  IHDR sgAMA1_5IDATxch41",(,?a0T1`4GÀ*hP* }IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn4a08.rgba000066400000000000000000000100001212556323000242050ustar00rootroot00000000000000 )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ރދޔޜޤެ޴޽ )19AJRZbjs{ՃՋՔ՜դլմս )19AJRZbjs{͔̓͋ͤͬ͜ʹͽ )19AJRZbjs{ŃŋŔŜŤŬŴŽ )19AJRZbjs{Žͽս޽潽 )19AJRZbjs{Ŵʹմ޴洴 )19AJRZbjs{Ŭͬլެ欬 )19AJRZbjs{Ťͤդޤ椤 )19AJRZbjs{Ŝ͜՜ޜ朜 )19AJRZbjs{Ŕ͔Քޔ攔 )19AJRZbjs{ŋ͋Ջދ拋 )19AJRZbjs{Ń̓Ճރ惃{{{{{{{{{{{{{{{ {{{){{{1{{{9{{{A{{{J{{{R{{{Z{{{b{{{j{{{s{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{sssssssssssssss sss)sss1sss9sssAsssJsssRsssZsssbsssjsssssss{ssssssssssssssssssssssssssssssssssssssssssssssssjjjjjjjjjjjjjjj jjj)jjj1jjj9jjjAjjjJjjjRjjjZjjjbjjjjjjjsjjj{jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjbbbbbbbbbbbbbbb bbb)bbb1bbb9bbbAbbbJbbbRbbbZbbbbbbbjbbbsbbb{bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbZZZZZZZZZZZZZZZ ZZZ)ZZZ1ZZZ9ZZZAZZZJZZZRZZZZZZZbZZZjZZZsZZZ{ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZRRRRRRRRRRRRRRR RRR)RRR1RRR9RRRARRRJRRRRRRRZRRRbRRRjRRRsRRR{RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRJJJJJJJJJJJJJJJ JJJ)JJJ1JJJ9JJJAJJJJJJJRJJJZJJJbJJJjJJJsJJJ{JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJAAAAAAAAAAAAAAA AAA)AAA1AAA9AAAAAAAJAAARAAAZAAAbAAAjAAAsAAA{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA999999999999999 999)99919999999A999J999R999Z999b999j999s999{999999999999999999999999999999999999999999999999111111111111111 111)11111119111A111J111R111Z111b111j111s111{111111111111111111111111111111111111111111111111))))))))))))))) )))))))1)))9)))A)))J)))R)))Z)))b)))j)))s))){))))))))))))))))))))))))))))))))))))))))))))))))    ) 1 9 A J R Z b j s {  )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{ )19AJRZbjs{chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn4a16.png000077500000000000000000000042361212556323000240760ustar00rootroot00000000000000PNG  IHDR n<gAMA1_UIDATxŗ_h[cHuRGT(8B-P.%4BiKaw]HɐXf%$zmiJWGc9w~t5O>>`eaX=`Ԃ±8G?]e _^X.wGO3wd3 gZú|'fef[Z!p&ކuh`c:oȇtri^g)X]UVZ l-5C`T$u!Z??p߅Cgj@dƱt-YY˅ae}';Ȁ~B@3 x'͌B ^SA#nr2r:QfGR`fAdv <@_"AÃևðqXZ3S:܀{Ճ< ';B 1/s,ZYߣ` `pg=U<@#뿃נ/`6,њ̀U]轏@`7&kc{ ? %:4o6JވaKGaɄ ? @= !e/sVVCpA5AhΠ.Ak%,?%!V[?hv@[/  !~Q`ZJHV@ס, \Z-tVKuVai *(a0ס  =i0Vކ.a{hQf\5 |ee8 l9QF haeXC9wnA D`6_Bh^SygLgv]~dA_ lwSR; I[VF&;5l :F$g 8h=ox yot| 'Q2 ,44c94O!1DӲͷ+3߲M:$m+?=*|~5{Wb zC`W݄辥< Cu㰈"j Q}8p|$;>.;/؏a 4/@ н[/E})x6훏m^=t)y#!ËGT/So SMO]]#7*gǏl!qXkdQa59G2 5e5$l (+畸2ۥ_$`Wb?@0 9E{Bk80/ƲVRZYxYf\ Nv{k By_@SaJcKjT𔥞cVE^>[v]7R>܉\i@)]SBBy$Ғh('eϭia;cҹ; МkX]HM%I%ŎUz]„Xj"22CO+h ɴ )oF\ܼ27 h+' wCQRY\-4-dA.Hax4,@ᜲ$o{ dJ;v,& C˧T$3ӔgVWsϓ<yhB# FP!v Vޔl X$p]y]\._s=,l7Gd){&TLK(}Z)h+V %@{gd"iiqૢ]Z^<y wPg+ЮB;  s%/ڟ gU<=DPw?({!l7d̀t:9-xIb z 4thFk2*<#~WQ\ 4eύpNf.J,OAdžN 6lf-ȌcGR3*%dE((\0){n$KJ3`38`ek+-w53&7yiIq4u-⋋=wTK\*< e7 M@x޶"&nܛ9f5|>IEtSqEUvvhƫ ^R\TjVz=XtodɎap~~ p}c ׍{3lvB}ކ|9:^TyJz5%WkFƫ%X go|h7#aL$7u)7ݬP?*>WU4^Y+/WZO?{1+^ |{ w u'0oh5HM@3~ݸ7sf7k.7e-/NK?RxZ s'@/eAY@ [?p| HQtu yfKϼQ-ѪHM@3~ݸ7sf7k.ԇ _|IV?4ħځN,'[3kXf|>o$_NY%an }MEFj0&fqo攛n\~|[~|!~ݻj%VyN&#āHFq@ ҊU;2t`@2zKoT۵3=-nj7#aL$7Vlvkeݻμ K@pR (#8grRKX4L;V݌14׍{3\67aZi.3o~`i޻ {pi 3 =)opQqdv%)3{>5XaXiH4ь̤+[h7#aL$׀fiZV!_ Cɽ?x~;w1[o zDE3ؓI~]Rl~%yZ+1S5ISZj݈VF˜H.~xVv: N]mx⁍/43}Ȉ c'ݛ l/Ib*aS1Q&1%?3j݈VD\9߭;wR'3j=K3Y ҕx{vi 1\[SΦx>lOcJF ޒ4ŨuWGjO9hn$83*}}§Rw~;@4k7syv|aE%aL>  j0&3gƫˁȽ_BAqޓ=vA&folnon4|6jd.dndoGq?eí۵IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/basic/basn6a16.rgba000066400000000000000000000100001212556323000242060ustar00rootroot00000000000000{skcZRJB91)!{riaXOF=4,#!!!!!!!!!!!!!!{!q!h!^!U!K!B!8!/!%!!! !!!1111111111111z1p1f1\1Q1G1=131(111 11 !!1BBBBBBBBBBBBzBoBdBYBMBBB7B,B!BB BB 1!!!1BRRRRRRRRRRRyRmRaRURIR<R0R$RR RR B1!#)!1BRccccccccccyckc^cPcCc5c(cc cc RB1%!,1!1BRcsssssssssxsisZsKs<s-ssss cR!B(1/!49!1BRcswfUD3"sc$R,B318!=B!1BRcsvbN;'s(c0R7B=1B!FJ!1BRcst]E."-s5c/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/chunk_ordering/oi2n0g16.png000077500000000000000000000002631212556323000257400ustar00rootroot00000000000000PNG  IHDR kgAMA1_@IDATx1 0 CQ9[ܠ({2*ُ?8Wc:`݂@B&@=2:3IDAT -hL`?oO8K_z_}IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/chunk_ordering/oi2n2c16.png000077500000000000000000000004721212556323000257400ustar00rootroot00000000000000PNG  IHDR 1gAMA1_IDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@gseIDAT.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^MwbIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/chunk_ordering/oi4n0g16.png000077500000000000000000000003131212556323000257360ustar00rootroot00000000000000PNG  IHDR kgAMA1_IDATx1 0 CQ9[ܠ({2*ُ?8mIDATWc:`݂@B&@=ΣIDAT2mf IDAT -hL`?oO8K_z_}IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/chunk_ordering/oi4n2c16.png000077500000000000000000000005221212556323000257360ustar00rootroot00000000000000PNG  IHDR 1gAMA1_cIDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(CnTZXIDAT\l52L̩g I@gecIDAT.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B )IDAT^z zIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/chunk_ordering/oi9n0g16.png000077500000000000000000000024031212556323000257450ustar00rootroot00000000000000PNG  IHDR kgAMA1_IDATxvIDATсIDATށ[IDAT@IDAT1y}IDAT IDAT\XIDAT0MDIDAT !1IDATCmIDATQ4TIDAT9w=IDAT[ԁIDATIDATEIDATZ?mIDATܧ]IDATIDATA>IDAT\XIDAT(IDAT{\IDAT2,hIDATށ[IDAT*>IDAT/UIDATg珺IDAT7IDATU?YIDAT?^PIDAT8:vIDATŀIDAT`KIDATFVIDATW7IDAT<6IDAT<6IDATcM IDATIDAT:4ZIDAT+IDAT`eIDATK>1IDAT&IDATVIDAT(8}IDATZӁIDAT+IDAT|*IDAT@^IDAT̺sIDATzIDAT;TIDAT,[IDATE.IDAT_[IDAT=pP1IDATG IDATIDATU?YIDAT5mIDATB]TIDAT(8}IDAT>Y`CIDAT/@IDAT2IDAT8IDATIDATdbةIDATr3mIDATs4]nIDAT~!IDAT;TIDAT=pP1IDAT'2ȃIDAT3IDAT_1IDATT_IDATf<IDAT NIDATZIDATwYwIDAT}=[IDAT6IDATFIDATT_IDATtPIDAT(IDATpx= IDAT,IDAT3IDATFS{IDAT NIDAT-IDAT{IDATpx= IDAT]=PIDAT쟪IDAT_?M~IDAT`eIDAT NIDAT WHIDAT^IDAT_1IDATIDAT쁄ӻIDAT{IDAT6IDATypIDATtPIDAT{5IDATYdIDAT\XIDAT?^PIDATtPIDAT{5IDAT_[IDATLS(IDAT(IDATCmIDATV 3IDAT\J`7IDATll1IDAT(8}IDAT :kQIDATgIDATV 3IDAT VUIDATKIDATDYaIDATgIDATIDAT&5IDATЮ<IDAT0pIDATIDATO7$IDAT\XIDAT(IDATKIDAT(IDAT`eIDAT WIDATK>1IDAT;TIDATRR'@IDATT_IDATWV|IDAT1uIDAT6qIDAT0MDIDAT1,RIDATIDATIDATY:IDAT`eIDAT7@IDATzIDATzPIDAT\XIDATllIDATzIDAT2⩕IDATȽ7jIDATH=IDAT_?M~IDATm;` IDATtPIDATT_IDAT3IDAT]IDAT𕅏IDATŀIDATT_IDAT?ëIDAT&IDAT^IDATA) IDATO[!IDAT/UIDATOIDATIDAT3IDATsIDATaQ:IDATB]TIDATr=IDAT^IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/compression_levels/000077500000000000000000000000001212556323000246735ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/compression_levels/z00n2c08.png000077500000000000000000000061441212556323000265750ustar00rootroot00000000000000PNG  IHDR  +IDATx                                                                                                                 UNnIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/compression_levels/z03n2c08.png000077500000000000000000000003501212556323000265710ustar00rootroot00000000000000PNG  IHDR IDATx^@0 J\l" [ןED=SJSf^տ> xЀ(9CNщ֠ .Gtx_AC:$%tgcݳ"n]ZWXtyF]؁]ȥ:Й1zd\ROx DWUNnCIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/compression_levels/z06n2c08.png000077500000000000000000000003401212556323000265730ustar00rootroot00000000000000PNG  IHDR IDATxK Кv1~a>W$n(8yKr-"ˡ %PzPE:wNh~uJLН#uOE" m(t:][ZWXtF]]Tҗ^}@'#si" |A~UUNn1R,IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/compression_levels/z09n2c08.png000077500000000000000000000003401212556323000265760ustar00rootroot00000000000000PNG  IHDR IDATxڵK Кv1~a>W$n(8yKr-"ˡ %PzPE:wNh~uJLН#uOE" m(t:][ZWXtF]]Tҗ^}@'#si" |A~UUNnIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/000077500000000000000000000000001212556323000227435ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n0g08.png000077500000000000000000000004771212556323000246260ustar00rootroot00000000000000PNG  IHDR V%(IDATx/LBQsc77bXHfiT#Hl&bX 7s vsd:-ǧ׷fޗcC02CCpe. \B JPd"ܖ " sTRz"'!]8Rݖ9pg>7[m0EuʩPs"'}H '8JEXiVE?h$"ˋ?CCP75Cp`,%/}XPζIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n0g08.rgba000066400000000000000000000100001212556323000247310ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooo***///444:::???EEELLLRRRYYY```ggg&&&***///444:::???EEELLLRRRYYY```vvv!!!&&&***///444:::???EEELLLRRRYYYooovvv!!!&&&***///444:::???EEELLLRRRggg!!!&&&***///444:::???EEELLLvvv!!!&&&***///444:::???EEEgggooovvv!!!&&&***///444:::???YYY```gggooovvv!!!&&&***///444:::LLLRRRYYY```gggooo !!!&&&***///444LLLRRRYYY``` !!!&&&***///444ooovvv !!!&&&***///444```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n0g08_reference.png000066400000000000000000000007741212556323000266410ustar00rootroot00000000000000PNG  IHDR szzIDATx-1D `,&fmVh]L&b1X `;ن؞O}CF#cdtl|brjzfvn~>z]"vNCmDj%f.h4t@^)r Ph EZR!JB]ݤ(EU10)&Ju" X`Ln"u"@.&WBg;a2'穋qȝ& \vdb"R &cL9fNHE9@*.Bށ+<2#LWH$A|E!X,FDQD %D@\!W[`ŏFIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n0g08_reference.rgba000066400000000000000000000100001212556323000267470ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooo***///444:::???EEELLLRRRYYY```ggg&&&***///444:::???EEELLLRRRYYY```vvv!!!&&&***///444:::???EEELLLRRRYYYooovvv!!!&&&***///444:::???EEELLLRRRggg!!!&&&***///444:::???EEELLLvvv!!!&&&***///444:::???EEEgggooovvv!!!&&&***///444:::???YYY```gggooovvv!!!&&&***///444:::LLLRRRYYY```gggooo !!!&&&***///444LLLRRRYYY``` !!!&&&***///444ooovvv !!!&&&***///444```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n2c08.png000077500000000000000000000046531212556323000246240ustar00rootroot00000000000000PNG  IHDR  rIDATxuopTƟM6Ɇ ,I hE@v Fؖb k"d; MHCaVe(RzCH/||L(8ڞOwD2h\:9j<-B>YY󸠄X@WWs]-7sK#lfi<<p_hNKzNsL,sɧwKsi\U5󸾄n`cߪZg[#ۛxRϮvw|/eEiywn!>Ǥ-L%QI4be.WsxpC!7y|M wVpw[yi̓x?]9//[סFʤ16X-lql*BS=sV} XCS5߯Z ɛgx" :1è&v&QM,eW>O."%b/U=m&5hꝼ}w !iP㇍17QKbOd7Wym,`_!7~Sg5Zުwm k;}'D8,G"r4*bU5M3꺑4d&3҉<$א/udE r]Dߎ#qqxN7t)?7&+uŗo6p-[ca!I#’3"ccUyt4g9OWsN Xsvwf7؀d0D_ T% RRFD2cR?([stw2^qz},bpFf_MA!DRRRR>Q?z vqN|Y\P|q%A:m'Ix,)B E0U It)2Gw"9{nΚӹDzbV\D O*V M(Y"`l?KIV a?%cX:K= L*snXͿb#L w</[B!"8F?ǞԙR|-E \=k' ӹ׊6-Ng\p'<<4>Ẉ {C̆+n'<.x'7iPBkA)T'k4OMv{yv6TG6yp=7φ%B;&D-=x:&KX,Zan.nx pOhF)VXl1؁,']p1҃^LaS aQJˣXK*^#-]C¡{/! { 3L$[bNd`w#ǃ^8}O&XB/Q,XZ!72}yo]@ adE f;:Յ 72=E/a xDA~ &cJODaqkxNOHr9_ FLv$;`vb 7R=H& 6?(#a8#&p -a |4/ '.H$ fR@jȈ`p1 Qa' 9:o&Z$IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n2c08.rgba000066400000000000000000000100001212556323000247270ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnw&^fnv~p{^fnv~Xu`is|&.6>^fnv~BjIuPX_hpy%-5=U]emu}0_5j:u@FLSZahp %-MU]emu} U$_'j,u049>CHN`e joEMU]emu}LU_ju #&)-0;@F k=qEwM}U]emu}C L U _ ju5K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n2c08_reference.png000066400000000000000000000046211212556323000266320ustar00rootroot00000000000000PNG  IHDR szz XIDATxڅ{PTsvDEu!nc #:6BA͈ ˰HG&T륵1f-jeژET.VSv&(20>CFMJ-%s_&NϒHoIbtrTVڴE.%5NK/I_[R]zH#E "0N.ALDE @X [u 8 q- #]rxK &b p(D9Jk@V @a~@42f @o  @" Xjb  vhAqYA\ & oF(P`qB#wTX Z%! v9 /~'>Nhws h9I _ W @lD-/: b/ 8 ? N8D_ + hqDK26VXb+] 8  N8 ,FA4"K .; AmwA9]D= hq D n -Aq7 @DD;{ > /A<:)㱪**ߦ@`0BP;^tԐ~JIbJ>$~C$>QRa2+A D= h ߗD6! > CF~E (Qb j@ԁ ē>guNc;}%+_hVAas[ W+,XTPm%>[.OЦ![GzAGôOt@?Sɯ::Q .*ddb A䂘bA,Q| X8&OV`&Aqğ@qi { 2@d b,   5zD5m D} 1Fb$ R@"`@d.1Ϙ_Xb9Jk@!ޭQˆr"DT|A$鵨ґ 5hUZ)(v$yOV4Je9'[LaxQ1itDQ@2C CTɬW(TBߙ 9^0ts  2;^܎MWh1  zAę0]."D=A b! @|A WA(D A b3 ~O=0&FXA@D"DD@HD:y 1 r@ b9 ^D % *@' 0pd , l "AD "DD@n(d "DlcA@L1LCLyvBO LV6 "ADDx]ᆂ"D 4v@ b,?b| |@ LV66 "Atq ` b@Ă"Dw z"D Ti ҿZIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f00n2c08_reference.rgba000066400000000000000000000100001212556323000267450ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnw&^fnv~p{^fnv~Xu`is|&.6>^fnv~BjIuPX_hpy%-5=U]emu}0_5j:u@FLSZahp %-MU]emu} U$_'j,u049>CHN`e joEMU]emu}LU_ju #&)-0;@F k=qEwM}U]emu}C L U _ ju5K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n0g08.png000077500000000000000000000005011212556323000246130ustar00rootroot00000000000000PNG  IHDR V%(IDATx!KAvvv1`1X,&f 6`.`8"\ A0' ٰǾq-TUJ !$QGX[:wĥuąuęuij ǩWGZ14)9x+1fcI+$½8l)U'0Ntb{1nG'-qU)Nj7'qMW0A.A Qt)C[P l mAtDGQ, IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n0g08.rgba000066400000000000000000000100001212556323000247320ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooo***///444:::???EEELLLRRRYYY```ggg&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooo!!!&&&***///444:::???EEELLLRRRYYY```ggg!!!&&&***///444:::???EEELLLRRRYYY```vvv!!!&&&***///444:::???EEELLLRRRYYYooovvv!!!&&&***///444:::???EEELLLRRRgggooovvv !!!&&&***///444:::???EEELLL```gggooovvv !!!&&&***///444ooovvv !!!&&&***///gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n0g08_reference.png000066400000000000000000000007271212556323000266400ustar00rootroot00000000000000PNG  IHDR szzIDATx+AUd `,&fmV6mf2,` "뿬0xpNt>y>}p8h4xbrjzfvn~pxt|rzv~|ERNCI6%hZDlRFAI:%j4Ex j&<@R."<r\.S<@T⯈pK4&"Ef:P(TE>)"\SrM `fix 2 M `NiPہT*ES0$I""HPiCuEnob1J"F)8D Ps%P#@x%K>IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n0g08_reference.rgba000066400000000000000000000100001212556323000267500ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooo***///444:::???EEELLLRRRYYY```ggg&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooo!!!&&&***///444:::???EEELLLRRRYYY```ggg!!!&&&***///444:::???EEELLLRRRYYY```vvv!!!&&&***///444:::???EEELLLRRRYYYooovvv!!!&&&***///444:::???EEELLLRRRgggooovvv !!!&&&***///444:::???EEELLL```gggooovvv !!!&&&***///444ooovvv !!!&&&***///gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n2c08.png000077500000000000000000000022341212556323000246160ustar00rootroot00000000000000PNG  IHDR cIDATx?%UΜ34"XBc+АPF M(,hH((,10V5RB %,bܝ토x7}ost°ٙbW>y~Lk鯝U(3kAy=uesp+ȍ2:SAE7woyU~wz:`{('^TU*qVNzS)S[I[DY\O*_ ?+t|' E3ǧpդ$LTPMR54: @|6ۿ|a3Ƞ@ *TTA:0`pIvz (Ȓ"Dթhbvt;}@8}*(fDv8䨞ꨞi٫ُ3Zg { IdyW LδH Q=~w'%hr')<Gh9^zp70FR(;{ ^xڹ ,#)Q%1%j&2N? p(QT; d 5'ER E,%Δ̔G̨Zf; v:1#i%aL11oꂛctƬeQ !y{b$FR:cF^B)LRj56 1,ڠxOHLĵΗ%+XLJL@<4 CGb"5FȕRN l]B.Ot'R(60 P]~rpg߅8+G 䡲\ V.: pi!„& A뾝;:QDZc(? @&K炭FNV^fnv~BjIuPX_hpy5=EMU]emu}0_5j:u@FLSZahp-5=EMU]emu} U$_'j,u049>CHN`e t%y-~5=EMU]emu}LU_ju #&)-048;@RX%^-d5k=qEwM}U]emu}C L U _ ju.5<$C,K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n2c08_reference.png000066400000000000000000000047111212556323000266330ustar00rootroot00000000000000PNG  IHDR szz IDATxڅ{P U!6HhT@2t* e 1"Z*'TXVRc6f/T|]g̴3ea=52aRX)&AJ+9ҤägH/,M"M%Η,<ҲRbyTCڵ[D:pX:zL:yZj ],}nI#L D* N.nALD! J@XFu >qQ'AEWAGduSb q &b9 (Q D9u փbu 8/ A\qD36 w^ < Xb!bK@,Qb-AlD5Aԃ@xAq%WA4 .[2ނ C_@iH95 *@i b'][ BS8tD#Q|/wSy ~EvyxD vQ   8  ΂hq D3 @Lm] A|b 8 8 N8F@4 ;k Ah6 A@qL{vnQ b hqD  ZA  ; "=q߃ ʦZ@xCQ4M~@R HƘǑ@uR6YM@#kB tIhM@v{@qDfAs[dv  :@q}@<= L^U (Qb Aԁh/Aѵx0| +_ -f- ZA .3c+ *Ҫ*(jiצwATkBx05B>8}W\y5AQ^'A8r-" D:LNY ^1d@31OQR AM @lAԃ0W|X"D@ "D&'̄ӕ< ~b1 A!@Tu Lۢp "ADy,.(DO j`U"$M~RNӊlFkJ$9AkzQfjRAQMz.#\O<ȺT*RKl^#'!Rab $Bp@ Y^0t! 2;Ce:@D$h=@ Dx t "D'7&"DB A(*ƧwqFq"DdYV,Ud!tY:րK6Qw=x4KC)," v\vϴN3;ˡhi  b$A}"\ @,Q b A> re5=dR)Nq']) H1=.b q r@LX  ր&[Al.u ;@$x  C@P7~mAp b@ĂH 4 "Sy >>]V6v! BADh='D"$)  H1DLN#A@d 0 DP\ @DD,8A$H "C75#&xIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f01n2c08_reference.rgba000066400000000000000000000100001212556323000267460ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnw&.6NV^fnv~p{&FNV^fnv~Xu`is|>FNV^fnv~BjIuPX_hpy5=EMU]emu}0_5j:u@FLSZahp-5=EMU]emu} U$_'j,u049>CHN`e t%y-~5=EMU]emu}LU_ju #&)-048;@RX%^-d5k=qEwM}U]emu}C L U _ ju.5<$C,K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n0g08.png000077500000000000000000000005431212556323000246220ustar00rootroot00000000000000PNG  IHDR V%(*IDATx/KQ8 A M,XI0 V `M`b,AA}_æ½pp:<&1"iDD1Zw{B1FDJA)JAP R4A`w{s=4Jtm}L\$ EDj@M'TR$z.BĢ ..k>PZdOb 31z]贘G1(q tڷOr|1:S! ?C+k[;{CAE  䗿IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n0g08.rgba000066400000000000000000000100001212556323000247330ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooo***///444:::???EEELLLRRRYYY```gggooovvv&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooo!!!&&&***///444:::???EEELLLRRRYYY```!!!&&&***///444:::???EEELLLRRRooovvv!!!&&&***///444:::???EEE```gggooovvv !!!&&&***///444:::RRRYYY```gggooovvv !!!&&&***///vvv !!!&&&***ooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n0g08_reference.png000066400000000000000000000007371212556323000266420ustar00rootroot00000000000000PNG  IHDR szzIDATx+A+"`,d3l6jm6d2Y,"AD׹xad\ugv_4|mv:v]z=}CF#cN&NSf3s. .KV+kn6n[v;{GN'g^.^Wn7w>j(@٤$FuJ"P(@Z$Je"{l(J4A EX,-B;l( tPjBݛ<"TT&\.GħP@6_0iQd2ЋdN N+Sw|C J BH&EcD6=@<)BhآFt1A --%iklX($P`0HI%xV$uW_IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n0g08_reference.rgba000066400000000000000000000100001212556323000267510ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooo***///444:::???EEELLLRRRYYY```gggooovvv&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooo!!!&&&***///444:::???EEELLLRRRYYY```!!!&&&***///444:::???EEELLLRRRooovvv!!!&&&***///444:::???EEE```gggooovvv !!!&&&***///444:::RRRYYY```gggooovvv !!!&&&***///vvv !!!&&&***ooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n2c08.png000077500000000000000000000033011212556323000246130ustar00rootroot00000000000000PNG  IHDR IDATxmI$yoD-YKWee xƓ s@#xƃ0" (*"҈=ݵuUU&eAjҞPRd91aTI)5aXL+Fxx\7enhƸ}Lʢ62+/PDa\KAe@(8sIs qU%2Wm:!UQeD))C&5k,xs[T/&\K\{pN`Wσ1+l)LxƄQaDÝ뱭w,;{q7Mt FO|Azz:ԡ-4AԣvZlĶeǛmWfy>ohy2DR:AO+16=MlemĶ쁳EEyOlnB79D#9FW-4z6Gj6cϑpײ*3%%sp&aKpmϱ(pвݘ-gV~jv݊)&Rz!6So'ގ%4[^l;ζm:[ ,X)HlW#PTL|o-o.)8iGqь{uHGg6[m~.~?7i7iæ5d=_0?IR'iК&x[]{ r>@Btttؤf2gLX2iɔX!.y,'͑ Jqie<z<}B ,;{3:rwBnNN,' -KQXq©pzv[llĬ98;bEAQyNfV|JK'DmN~ TªcrԿC(*ۡӥWfvJ'~ TºcHV5d +Kѡۥ2Q$+^B T+*abts:9yF^VӒ1{1;-Ǧc-C>We==IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n2c08.rgba000066400000000000000000000100001212556323000247310ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnw&^fnv~p{^fnv~Xu`is|&.6>^fnv~BjIuPX_hpy%-5=U]emu}0_5j:u@FLSZahp %-5MU]emu} U$_'j,u049>CHNTY`e jot%y-EMU]emu}LU_ju #&)-048;@F LRX%k=qEwM}U]emu}C L U _ ju ' .K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f02n2c08_reference.png000066400000000000000000000046611212556323000266400ustar00rootroot00000000000000PNG  IHDR szz xIDATxڍ{PTw/  b$fmMd1&^ѩd4Q:/#V{#VK݈52Xm"*^BL;-3?a9{#%EuRԞ#K?Xzi(WqKI3 %|tdTRZ#m,nvHIGJ'NI祋R-).=| etz1 8@(Q b2 į@D== 8/@\qD ; xH&B'\ F b^Aq~AqD# qW A\ 6dvVX b 8  N8 ,FA4"K . AmwA9Q b hq D n -Aq7 @DD;{ > OA<:)S]mSMM^M>]]`BT Sߣ2e%z/}H!:H}*SRa+@.A5- [D6! > C̪"sA(: A@|3zV_:>_A\A4hqD+ 0xz VT[UXc%>mzDuЮM!]Pii^zʗ_3u\uJ-S _@\qUdr - b\c@䁘b7AQ7` D5 6zq A40| "p#AA1T"Ep8y@sA<h"&h[9N 80y%H"D "0. ƀ1Dǀ0?R VX CD=(D Xq,JD"$U}VH ؔ)#dCve)aԋ2Zy*TFj&q60e z]F7GS*3VPT |F6?T@0A a x BX9N/-( b@t "D]At"D  :H'\ 5$ @ D9s^vgg,xwL^7 R@H,սe' en(ufgiHbZùgLN|> @ 10/u*Qb (R Vz9]d2 &I+D/  Cp@/5P b> D5 6 b v5Ҹ{83strAxP.!%GwATb:^ >^Aq pH;sebL~L ,OVU ^7S{_o6CtD#K<X@0@ \ 1A hq 7A'4qq2 Br24LA&#8g$ e<2DkYtxede @A@@@@ LTLg H,@8A@A1Lo %2+jdyedvedt6diI][ץ^7xv6N AĂH"D:^ @dxp Ƃxn(d@Q J@Ԃw >gAę0fFD"D$) A8@1D ^, 72xDBs@X = 6 zXaa " D 8 At"Do  0 F1D) fx% *aA "Dh A$HDyB/, r@8A 1XAL1=Ĕ>^fnv~BjIuPX_hpy%-5=U]emu}0_5j:u@FLSZahp %-5MU]emu} U$_'j,u049>CHNTY`e jot%y-EMU]emu}LU_ju #&)-048;@F LRX%k=qEwM}U]emu}C L U _ ju ' .K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n0g08.png000077500000000000000000000006051212556323000246220ustar00rootroot00000000000000PNG  IHDR V%(LIDATx}ұKBQDQ !EP4.A!"!H_BA 84854 Ay{wpwu2ݽã\x~qyu}S*o+׭UM ۆJnJ -4IBFv&u0 C V1ň;୑BguNJ=:'N{1qO0">1qv7/M1 i"ޡE*plCǰby% &'0B{C* T L @cg\B 9IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n0g08.rgba000066400000000000000000000100001212556323000247340ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooovvv***///444:::???EEELLLRRRYYY```gggooovvv&&&***///444:::???EEELLLRRRYYY```gggooo!!!&&&***///444:::???EEELLLRRRYYY```ggg!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::LLLRRRYYY```gggooo !!!&&&***///444LLLRRRYYY``` !!!&&&***///444ooovvv !!!&&&***///444```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n0g08_reference.png000066400000000000000000000007311212556323000266350ustar00rootroot00000000000000PNG  IHDR szzIDATx-Q"`XLdٌ6f٦L&b,AAD; ݝ7M8}wv9 8.G1' )g9%W57 -w=#O3/ +o;wt:j"n@բ&f4 j"Pש@V&j~|JBɗ@\8pqT*n(Eᶍ qh0? J5 |Rn:r9 Q lA#$=L&à@rm#N3(R)ExMd!L/ld"H񮤫xA!l'hbFD Pp8LMB5xu&i!`α (t6enIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n0g08_reference.rgba000066400000000000000000000100001212556323000267520ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooovvv***///444:::???EEELLLRRRYYY```gggooovvv&&&***///444:::???EEELLLRRRYYY```gggooo!!!&&&***///444:::???EEELLLRRRYYY```ggg!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::???EEELLLRRRYYY```gggooovvv!!!&&&***///444:::LLLRRRYYY```gggooo !!!&&&***///444LLLRRRYYY``` !!!&&&***///444ooovvv !!!&&&***///444```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n2c08.png000077500000000000000000000024131212556323000246170ustar00rootroot00000000000000PNG  IHDR IDATx}]TeϼggZ,(BB2B*M  zxc~PwUwMW{m "^XvabE|}893a?Qr6WŊG7٧7m/ڗ^wډ7I-]!{S{=qž~S̱u/EP2E(B ƚ%Eȃ/)¨2a&$&c-ˈZFGawK$qIJ 3<{=<{xjxJ=Z5fLP9 0U~4 ߄-|fm@+uCHNTY`t%y-~5=EMU]emu}LU_ju #&)-048^-d5k=qEwM}U]emu}C L U _ ju ' C,K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n2c08_reference.png000066400000000000000000000046471212556323000266450ustar00rootroot00000000000000PNG  IHDR szz nIDATxڍ{PT."uAUD*nQxɘ:5#64/Qc$#VK„*^Rk6ޢ1Vjfg}>#&b⥄3U:Bz|9^zYiLiqmf-&nem'W@2MEyJDz b} 80/c N8 9߀[ hq djZ [A^GAq)g@48 9A\q?A\ 5 n  }%SCu@ԀhQ>~ hq5A&[ n; "]q}߃/ ڮ:@xCpQ4GƘt0=? B۵j@lc Z@UP   &Ԧ<K@(5 A4 QPN1}_tI~ooA|Dk AqX`s /)ʦj(n5]o;9EqhF cM'NaB~TZ_|ȵ48@Lb ^D1e Aģɫ5Z5z$Q/ o 8fƙmi0Ti 2@@dx 3@1DGJt =Z ցx&  D# S&6R A8A b(t \ Ƃ{"[@Qbw@ւaϻ,Vk@TXb- b'zO읽Aă 3 ɳGpإѬ\$n*gvmQ D=A>Aqq3DEC@)=A&Y+(}*o 0:=11@\/T @Z*t.n@'6qD++ @\q u MTL UcoOL%HiT  + D6\24f9Yrjj!!!|.0a2AD"D RAppb. ^- 2+dVWl$99*_zI}JCllD@"D$@H1 D& Y(d@,Q @ԀN >9c |t`c3 "B@"Do @8A 1 D 2ρxD\Ax  @l 6h ,~@A8@" D X}@$H1D  q & "  @b1b@Xb- X2X l  BA "D X}@(dH" D&@x@L1\\2_y@zap " D8 @D A @$H1D Adp bɝG&8p@X l l   B@@D"D bADDI A8i w-IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f03n2c08_reference.rgba000066400000000000000000000100001212556323000267500ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnwnv~p{fnv~Xu`is|&.6V^fnv~BjIuPX_hpy %EMU]emu}0_5j:u@FLSZahpw 5=EMU]emu} U$_'j,u049>CHNTY`t%y-~5=EMU]emu}LU_ju #&)-048^-d5k=qEwM}U]emu}C L U _ ju ' C,K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n0g08.png000077500000000000000000000004151212556323000246220ustar00rootroot00000000000000PNG  IHDR V%(IDATxm0Lb )nK'-X B!9Dzѧي|o&C{/މsYk1#gZL B.mz#\@* // _,LPߪZQ[6@]8_fX`ⷹ=L[+/C qfS(RR| D_H/$@&L!`IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n0g08.rgba000066400000000000000000000100001212556323000247350ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooovvv***///444:::???EEELLLRRRYYY```gggooovvv&&&***///444:::???EEELLLRRRYYY```ggg!!!&&&***///444:::???EEELLLRRRYYY```vvv!!!&&&***///444:::???EEELLLRRRooovvv!!!&&&***///444:::???EEELLL```gggooovvv!!!&&&***///444:::???EEEYYY```gggooo!!!&&&***///444:::???!!!&&&***///444::: !!!&&&***///444:::???EEELLLRRRYYYooovvv !!!&&&***///444:::???EEELLLRRRgggooovvv !!!&&&***///444:::???EEELLL```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n0g08_reference.png000066400000000000000000000007301212556323000266350ustar00rootroot00000000000000PNG  IHDR szzIDATx+A"`,d3l6jml6dX,`A_Ea`x/X8 'of3z<ᐣшɄ锳ٌł咫Պ͆햻ݎÁ㑧Ӊ˅땷ۍǃ瓯Snt:J"n)@բ$f4 J"P)@V-\VE(NR8P.iP]3RDSDX)B5b:P(h>E> B5@..¿oA R d2"Tt4u#LHRELvd&o-$ ")G<+A 1O (%D"D SP(DID@+TtIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n0g08_reference.rgba000066400000000000000000000100001212556323000267530ustar00rootroot00000000000000vvvooovvvgggooovvv```gggooovvvYYY```gggooovvvRRRYYY```gggooovvvLLLRRRYYY```gggooovvvEEELLLRRRYYY```gggooovvv???EEELLLRRRYYY```gggooovvv:::???EEELLLRRRYYY```gggooovvv444:::???EEELLLRRRYYY```gggooovvv///444:::???EEELLLRRRYYY```gggooovvv***///444:::???EEELLLRRRYYY```gggooovvv&&&***///444:::???EEELLLRRRYYY```ggg!!!&&&***///444:::???EEELLLRRRYYY```vvv!!!&&&***///444:::???EEELLLRRRooovvv!!!&&&***///444:::???EEELLL```gggooovvv!!!&&&***///444:::???EEEYYY```gggooo!!!&&&***///444:::???!!!&&&***///444::: !!!&&&***///444:::???EEELLLRRRYYYooovvv !!!&&&***///444:::???EEELLLRRRgggooovvv !!!&&&***///444:::???EEELLL```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvv !!!&&&***///444:::???EEELLLRRRYYY```gggooovvvchunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n2c08.png000077500000000000000000000017311212556323000246220ustar00rootroot00000000000000PNG  IHDR IDATx͎E[Uv۔("  ${Y[g*BQ,<3c/n=c ZG֭v[N+:ۭv=WoTQa8`HB[ n zeH4;؂->VJP_Ӻrό9q'JB i9 "Ok-E4Ҹ2mr n:(UB 칻ZLwW)40)t\+ T_?G٪{wGN)t йj^0~_j 4hL| sIE@ ͐mʢv;̣8R^j" RC6>|+WAp )O'-a_[>w(e5H5:϶yz8㖫$CykU樏G.% )xr|Ok5 }MR9?Hұ5`_€Q .i P7|Hrel$Л\$XT)3T%?qoٿ*V%J(Kq Oq/nB]g_`GR|%mʹbZ2w̛@{?X&^~ MTPIF$->Ү|#0M&ؑd'Q($4 0Scs:yꆼU]Mj 0WGɱ٦o l` k;4 Xͽ {vG!@,'$iKqOaHOv}8Xg DBs96)j2"4~, *ڧ30ClZ(cՂU@ \Ҍivݙ!)̓r+LV71 jIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n2c08.rgba000066400000000000000000000100001212556323000247330ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnw&.6NV^fnv~p{&.FNV^fnv~Xu`is|>FNV^fnv~BjIuPX_hpy -5=EMU]emu}0_5j:u@FLSZahpw%-5=EMU]emu} U$_'j,u049>CHNTYjot%y-~5=EMU]emu}LU_ju #&)-04F LRX%^-d5k=qEwM}U]emu}C L U _ ju ' .5<$C,K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n2c08_reference.png000066400000000000000000000047111212556323000266360ustar00rootroot00000000000000PNG  IHDR szz IDATxڍyP (u`TDZ\kL61F7HFEi w#VKjĚWբKژED@( D ~ @q'@ W@ .e|C;Nhf h9 iog߀65 jAԁn{AqaG@x@q  . e_hqD At\-a b < 8 ΂hqD .2 D Vm n]T-vj@q D3k Z@\ 67Aq߂h/~ 7{ @<LeMUU6ck$?;H<I% #cE7lB ;dA2Xb v88& hqD+V4߂h]wAq}߁xXb.b VXDq'A<.9A| +q  D+6@a^,Uʬʭ*ʪ9 lZ=>6ڮ }H.{R|2OCt@?'zY5&6G;KΨDZ&_ |̳ha F"dA b" AxvkWATu b/  Nha9"D&',#@A1@<.^LW~b1w@X @T N 801ŁH"Do oXl@xL?  A,Qb5 >Qu LۢPa "@DZUbQ4. b@Z`U"S69dW"b)#5VNP5M/)b0S3؎(-\F7&9$#k$U|o6O%+B?AȂ1&A-!,:g ?wPa ADD]$;'cYdJ Oc@L "Dy J@ץ8#@Dɸe" D,e=d!]V@Ye }r 6|GgTSIqS "DA8A J րb3- D='1zG,A8@spxhfb1U~!!!t*" DW A" XS@1$P,YQ%Vfk.Aq&,R5KԳw2- "Dh1 A$ " D: @1@C!b.b VXD]9/@A; VDa "AD"D_Adbx^y @Qb1@Tb;@Еu>Xaa "DH b@ăHTr @1 D6 xE ( *A "DP @D@C!Dt @18S@Ld} p = "D(p @tq  @p "D? 2A8A HA@ "D0N@C D @D"D,x @$H 7YIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/filtering/f04n2c08_reference.rgba000066400000000000000000000100001212556323000267510ustar00rootroot00000000000000 ')/179?AGJORWZ_bgjosw{&-4;'C/J7Q?XG`OgWn_ug|owſ7;?EKQW']/d7j?pGvO|W_gowǷͿMSX_dins'x/}7?GOW_gowƧ˯зտ`gov'.7?GNW_gnwŎɗ͟ѦծٷݿoxЁߋ'.7?GNW_gnwЇӎٟ֗ܦ|Лߧ'.7?GNW_gnwе'.7?GNW_gnw&.6NV^fnv~p{&.FNV^fnv~Xu`is|>FNV^fnv~BjIuPX_hpy -5=EMU]emu}0_5j:u@FLSZahpw%-5=EMU]emu} U$_'j,u049>CHNTYjot%y-~5=EMU]emu}LU_ju #&)-04F LRX%^-d5k=qEwM}U]emu}C L U _ ju ' .5<$C,K4RCHNTY`ej   & , 4;CLU_ju #&)-048;@F   &,4;C L U _ ju chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/000077500000000000000000000000001212556323000220425ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g03n0g16.png000077500000000000000000000005311212556323000237170ustar00rootroot00000000000000PNG  IHDR kgAMAIIDATxM 0WZEP(]S4^"\"_mu!A3Ct7Hi|k,P1 Q`8  M$ z NA)]Wqm)*N ׫_ׂ X.TyecyZBJu2y"8MN7E@WP Mw; N'`2}&8MUeem`<u+DݷIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g03n2c08.png000077500000000000000000000005621212556323000237220ustar00rootroot00000000000000PNG  IHDR gAMAI)IDATxM@F1Dw.x!.2dFgFˆ$øVJ_.b+gƻGq98`>//߀шVa" A+P :Π P Z-@TfSA2~4@]5Ȁ_>tl̮1333$RdV8$lB'4I^7m6MV78YmtC|wcn'[|(% jПA Ϻd@ ZY#[jIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g03n3p04.png000077500000000000000000000003261212556323000237320ustar00rootroot00000000000000PNG  IHDR TggAMAIPLTEȭ1 cIDATxc(% 0A `@`@W,@G.@p`06`FRalb\%U@ l@-H*@ SRpIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g04n0g16.png000077500000000000000000000005531212556323000237240ustar00rootroot00000000000000PNG  IHDR kgAMA7"IDATx90E'",$4)RpNOp  ea Ed1YƅRm hn簮q0@q~`4X. (`p4xp0{Dfө@0Wdi  ?xnG/P  (. l6I2+3JimvѠ=}?̀wsw[ H˅n1(Kt1@dRZ+ fpYk<_bFa32O @ >)4(cIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g04n2c08.png000077500000000000000000000005711212556323000237230ustar00rootroot00000000000000PNG  IHDR gAMA70IDATxՖ=n@F̟D-}H(R"~_Oq)xw޼!Su4T矪'3p0R%F``\~ql\^6 >`k\Π1d@Wnw G/Ȼ15+:@ l`6 rD^_](iX."d@3V1yod@o3LzdR f8l6?VFpaHnnrenuݮ|4-AA U As GA/@ Q#99͹~ͅEX@p:)q)8WSp80/)n#vW <8`2C~8\x8ah`,uA׭ nV @_`+(W]X&*&5-~> ϧR#лtpDPGXDzЂd ?_fAIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g05n2c08.png000077500000000000000000000005361212556323000237250ustar00rootroot00000000000000PNG  IHDR gAMAOX2IDATx90ERBDAGChr8hnB @B"B$pT/ϱܪd?nGg 9 vi%(.tDj$" n Ejj@ͦ  2@5Eۢ`W f@ՠV *2 @StA鴸̲/klyС(BK@`n\"2z˷^dx!E" Vf@ˡE7j9M,WIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g05n3p04.png000077500000000000000000000003161212556323000237330ustar00rootroot00000000000000PNG  IHDR TggAMAOX2PLTEs$[IDATxcP pA `@`P@W,@Gi@` p00vq`AV`CU ``gjARQ^w*x;JUIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g07n0g16.png000077500000000000000000000005011212556323000237200ustar00rootroot00000000000000PNG  IHDR kgAMAp;XVIDATx͕ 0 B%hXI\1-Dl IL9P8j-Z׵Z{ۦ>2x n@i$qliE: - tA LZ߷4 di`Y8 }SU1@ͣAYhgphk \";9@39BuxC]] @ Ҡ >š,s,IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g07n2c08.png000077500000000000000000000005241212556323000237240ustar00rootroot00000000000000PNG  IHDR gAMAp;XV IDATxՖA 0E'"+/Ir&!ݖm)E+U:34`;"!WPMė"p)~~ ʒ~\( !C@@I LR4b2fI" Ā30FEow0R2~hp2@Z  6fMW^uP릪R|y?i ` DlknkYuKc,5U(s(28 3Z? IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g07n3p04.png000077500000000000000000000003171212556323000237360ustar00rootroot00000000000000PNG  IHDR TggAMAp;XVPLTEvvvNTl\IDATxc4 PA `@`0 @P p0S EX (//(Wbb 00 ``gRBQQ^w*#kr=IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g10n0g16.png000077500000000000000000000004061212556323000237160ustar00rootroot00000000000000PNG  IHDR kgAMA1_IDATx1 E? ,=\!^KXHbřDdcfy΁HkMqye--HwZ_< JMGi =ضB;݃al>hly,kdǔ41{mi!,RIϴϫ IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g10n2c08.png000077500000000000000000000004351212556323000237170ustar00rootroot00000000000000PNG  IHDR gAMA1_IDATx1 0D }ǰ Bp 3H*M&S-g O 7C @T PĠ("kp3p _ (d Pu )-wzCDD2O5 H3oiۺvtv|GSMd"*?l kblt RUԭMTߏIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g10n3p04.png000077500000000000000000000003261212556323000237300ustar00rootroot00000000000000PNG  IHDR TggAMA1_PLTETTUZcIDATxcP H% `@`@W,@G.@` p00NK`CV`Ɩ *@-H*ـ"(/YLFə:IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g25n0g16.png000077500000000000000000000005771212556323000237350ustar00rootroot00000000000000PNG  IHDR kgAMAАTO6IDATx 0 @@|"gnā:]5 8DQ-YPVVo@EN\o6VEVj4ef 0O c@ `ӟ3i(ͦzZB 7me\ tf|4t~3Cl^f3{oLkeU>b0]β  o1z0}we}&%ϧS<ے$m 664p_4f@B`2|? 8IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g25n2c08.png000077500000000000000000000006251212556323000237260ustar00rootroot00000000000000PNG  IHDR gAMAАTOLIDATxm1@Nj #؎8Pu-` @ T@|HsZOH0gi,a&^ݥݧSȀY@'%  @V T2H o2 @'J\^_^|AU ov2`4"236"yA瓲4 x pϋ< HHQ‰ b_`8,ߢ^-v@'N=yLӅw@["^] s2 pd@@?+ZD _-a2wIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/gamma/g25n3p04.png000077500000000000000000000003271212556323000237370ustar00rootroot00000000000000PNG  IHDR TggAMAАTOPLTE--\\\RQdIDATxα 0 DQ[J+ ^msQ$s5HІ:P 5B) oe p/)FqjS!*$BB?x(:5tf;PIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/metadata/000077500000000000000000000000001212556323000225405ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/metadata/cm0n0g04.png000077500000000000000000000004441212556323000245030ustar00rootroot00000000000000PNG  IHDR )gAMA1_tIME "8ݜIDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/metadata/cm7n0g04.png000077500000000000000000000004441212556323000245120ustar00rootroot00000000000000PNG  IHDR )gAMA1_tIME V IDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/metadata/cm9n0g04.png000077500000000000000000000004441212556323000245140ustar00rootroot00000000000000PNG  IHDR )gAMA1_tIME ;;u0IDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/000077500000000000000000000000001212556323000221015ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/other/ccwn2c08.png000077500000000000000000000027521212556323000241470ustar00rootroot00000000000000PNG  IHDR gAMA1_ cHRMz&u0`:pQ<uIDATxOg3g̜sfnbrBiFS06"ئwAE7q.Dq)R[PP1r9y'kJH^^f3<̫D;M }#x=_+s8Rr˰#O3#o!AK!EupKOFb=0Y1\d{9fҞߣ31AL|~}v@v {t{t#:aD'"rԌo·>6*@Hp4R1 Ng V㬞d<t:XAդ>#88z~ N{nCT8^@7f{0>c&`[M4.!E4&AKgڧ藘MtP]k):b$B`نDua֎ś1pi{nLO .Q*F搸 kº0求xIv |it(=7b65aC #a$ SANEG{G8gCa, 7^^=I]BЭ-Cʅ;8N{A$6$*0Ԍ>qل"a:&$2u>Of[9E fskTZ$ A27CRu,_9s_~G5b .07ɛmK9.r<};,%"o㞩6",5f}<ܞo@JLWW/FJ*y]^'; D(IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ccwn3p08.png000077500000000000000000000030221212556323000241540ustar00rootroot00000000000000PNG  IHDR DgAMA1_ cHRMz&u0`:pQ<PLTEމu k51I&442tQ#l-^>bOw3X .c$s=;Y\z<HA(-I`%{KR~o|dqj`+_p5]g+=dEcC=K&037c, G6ǫfYLqLَJ{AL;[@]~T<e9O)f ˝4foX㌖Ae=6&^?)gKT!i¿kK{|SiC/5^eMu@~rs>oݛ?@3'0|{O%?|{S 9OU>/WLbz Y+к+JIDATxc8B0m\ 4RR¥ #* \kjZ]OhM$]un6n SUrn: a(`Ywhsh \7Oڿ{ѣ[BCWǣ*Uy~55T]E$Wvwk^a\",:Ad9IW 45dVUO8\YI$.lkzÂzz $\\cz)nh4Ck']pn.OM^ȱr-e8q́_jխ[ Nl)s6SE^~YPtKQQ ۇNX&Ѷ]I{7(31s+H׀ ]jSRokV֩e+O>{bk'ܙt>/_N-[&$T"+a)P^Me#ΗS>BϜ93or S}}TT/6ީ~k\hIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cdsn2c08.png000077500000000000000000000003501212556323000241340ustar00rootroot00000000000000PNG  IHDRKm)gAMA1_sBITw pHYsO%{IDATxEQ @DSga-B+ X JV++|4L2]C0بp|̡b?Liy+[}5kIR0_66T$'GjD>Pe}P] aO_w |*/^֮IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cdun2c08.png000077500000000000000000000013241212556323000241400ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw pHYs{RkgIDATx qjhWK %ܵB q @ Fx7 zO/̃/[ü ̍ӑm@`]]6y~KXa;e" ṯ[I=du5V3>&?wVӶc[lJ6YՖ` ⤉)?euSX,QOyKN]J:#< CL=v%$ǃ'JwMk=b+\u;zwP^<'pbS d(@J,=9ּnH])rt`oIAtUۛDVbmB5˙(YkTns=xBd"\`"[ fRhz0'mehCMݨ3  Ldٯ?`~+Іdl*#p]q2^ɩ8|_ rj:2Ckiʙ9ff |ADJi?c~++ 範t>rl5yM.f P6@.lIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ch1n3p04.png000077500000000000000000000004021212556323000240460ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw-PLTE""fwDDҰIhIST@p0`` P@0PpHYAGIDATxc =sfժrcwfd C+(H*ŅTݻq@*)#MK#G{7}IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ch2n3p08.png000077500000000000000000000034221212556323000240600ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE"Dww :w""""Uffff"DDUU"DDUU3DDff3D"ffD33U*D˺[""f2Ucw:DDkfBkܺ33sJw{"w332fDwJf""UUDff3UwwDwffD"w"3333cU3{UUUUf܃wwUUww""DD3ԪU*U˴f3BSD̙"Sww333Ĉwff""UU"DD[wfwws33wD""U"f 3bhISTMIDATx GHd+;3 ekXegа**4h lޣY2W%syiHCL*;8"KE6sx'HK?Y9sģ>1~!'B 0IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cs3n2c16.png000077500000000000000000000003261212556323000240550ustar00rootroot00000000000000PNG  IHDR 1gAMA1_sBIT 7~IDATx @ H*)^ < 4\u wClPgXPR3ޛߟ:Ž~!N$#|G}LCli(?c"B@aEDG@0S&i\vgIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cs3n3p08.png000077500000000000000000000004031212556323000240700ustar00rootroot00000000000000PNG  IHDR DgAMA1_sBITBTPLTEmmI$$II$m$Imp+KIDATxbWT#$tNzC{h t. mtn:B"@3w&IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cs5n2c08.png000077500000000000000000000002721212556323000240600ustar00rootroot00000000000000PNG  IHDR gAMA1_sBIT&CbIDATx핱@ Ô;/Awl@*QEDqm\a .[p=jE'-X@O0 WyH0o y<Aa=^ohIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cs5n3p08.png000077500000000000000000000004171212556323000240770ustar00rootroot00000000000000PNG  IHDR DgAMA1_sBIT&C`PLTEB{c!Z::cZ!B{G/KIDATx0[>@Vj%(`R A XtY VA,`d\p *-hq>IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cs8n2c08.png000077500000000000000000000002251212556323000240610ustar00rootroot00000000000000PNG  IHDR gAMA1_LIDATxA 0 BQcjS҉hi Dk`j}0MA_uoFIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/cs8n3p08.png000077500000000000000000000004001212556323000240720ustar00rootroot00000000000000PNG  IHDR DgAMA1_`PLTE ߠ?_`@`@_ ?cKIDATx0[> |Gp "lX *A/P $BP Z.Y N-x)>SIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ct0n0g04.png000077500000000000000000000004211212556323000240460ustar00rootroot00000000000000PNG  IHDR )gAMA1_IDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ct1n0g04.png000077500000000000000000000014301212556323000240500ustar00rootroot00000000000000PNG  IHDR )gAMA1_tEXtTitlePngSuiteOUL1tEXtAuthorWillem A.J. van Schaik (willem@schaik.com)G8tEXtCopyrightCopyright Willem van Schaik, Singapore 1995-96P8tEXtDescriptionA compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.M k9tEXtSoftwareCreated on a NeXTstation color using "pnmtopng".jdytEXtDisclaimerFreeware._,JIDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ctzn0g04.png000077500000000000000000000013611212556323000241640ustar00rootroot00000000000000PNG  IHDR )gAMA1_tEXtTitlePngSuiteOUL1tEXtAuthorWillem A.J. van Schaik (willem@schaik.com)GAzTXtCopyrightxs/,L(QIU(KSNHQKO,/JU04յ4aRZzTXtDescriptionx-0 D~MLX`\C"8J UWY{wwRg+]|?BO&2 .IlL} "$ܮg<-.=KMk.QI#|Ě=&'*EQ5 o>ᤊ{,SPm{͹<}mUxoGmҥVbz@zTXtSoftwarexs.JM,IMQSHTK).I,sJ3 rK ҕBQzTXtDisclaimerxs+JM-O,JU`ԮIDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/pp0n2c16.png000077500000000000000000000017021212556323000240630ustar00rootroot00000000000000PNG  IHDR 1gAMA1_PLTE3f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3fcQIDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/pp0n6a08.png000077500000000000000000000014621212556323000240710ustar00rootroot00000000000000PNG  IHDR szzgAMA1_PLTE3f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3fcQUIDATx1 0 CQ<$o ԥKA4 z-qKjI`~Ux\Ku7UIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ps1n0g08.png000077500000000000000000000027051212556323000240760ustar00rootroot00000000000000PNG  IHDR V%(gAMA1_/spALsix-cubePNG group 1996-10-223f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f3333f333ff3fffff3f3f̙3f3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3f@AIDATxcd`$ȳ )?`y00gdy\ q10edPq5YIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ps1n2c16.png000077500000000000000000000031511212556323000240670ustar00rootroot00000000000000PNG  IHDR 1gAMA1_/spALsix-cubePNG group 1996-10-223f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f3333f333ff3fffff3f3f̙3f3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3f@IDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ps2n0g08.png000077500000000000000000000044451212556323000241020ustar00rootroot00000000000000PNG  IHDR V%(gAMA1_spALsix-cubePNG group 1996-10-223f3333f333ff3fffff3f3f3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fffffff3fffff3f3333f333ff3fffff3f3f3f3f3333f333ff3fffff3f3f3f3f3333f333ff3fffff3f3f3f AIDATxcd`$ȳ )?`y00gdy\ q10edPq5YIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/other/ps2n2c16.png000077500000000000000000000047111212556323000240730ustar00rootroot00000000000000PNG  IHDR 1gAMA1_spALsix-cubePNG group 1996-10-223f3333f333ff3fffff3f3f3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fffffff3fffff3f3333f333ff3fffff3f3f3f3f3333f333ff3fffff3f3f3f3f3333f333ff3fffff3f3f3f IDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/000077500000000000000000000000001212556323000221155ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s01i3p01.png000077500000000000000000000001611212556323000240040ustar00rootroot00000000000000PNG  IHDRRf\gAMA1_sBITwPLTExW IDATxc`HqIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s01n3p01.png000077500000000000000000000001611212556323000240110ustar00rootroot00000000000000PNG  IHDR%VgAMA1_sBITwPLTExW IDATxc`HqIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s02i3p01.png000077500000000000000000000001621212556323000240060ustar00rootroot00000000000000PNG  IHDR?gAMA1_sBITwPLTE\/% IDATxc`gIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s02n3p01.png000077500000000000000000000001631212556323000240140ustar00rootroot00000000000000PNG  IHDRHxggAMA1_sBITwPLTE\/% IDATxc```8UIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s03i3p01.png000077500000000000000000000001661212556323000240130ustar00rootroot00000000000000PNG  IHDRjgAMA1_sBITwPLTEw奟 IDATxc`LAIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s03n3p01.png000077500000000000000000000001701212556323000240130ustar00rootroot00000000000000PNG  IHDRl'gAMA1_sBITwPLTEw奟IDATxc``p``A91 KIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s04i3p01.png000077500000000000000000000001761212556323000240150ustar00rootroot00000000000000PNG  IHDR8<gAMA1_sBITwPLTEwCIDATxch`  >7IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s04n3p01.png000077500000000000000000000001711212556323000240150ustar00rootroot00000000000000PNG  IHDR? =gAMA1_sBITwPLTEwCIDATxc0?  IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s05i3p02.png000077500000000000000000000002061212556323000240110ustar00rootroot00000000000000PNG  IHDRgAMA1_sBITw PLTEwAsIDATxch`h`X";tOm"Mp$FEIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s05n3p02.png000077500000000000000000000002011212556323000240110ustar00rootroot00000000000000PNG  IHDRvgAMA1_sBITw PLTEwAsIDATxcX0a"\*JIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s06i3p02.png000077500000000000000000000002171212556323000240140ustar00rootroot00000000000000PNG  IHDR7MgAMA1_sBITw PLTEwEh"IDATxch`h` - +<"&0LH`XYLe$IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s06n3p02.png000077500000000000000000000002031212556323000240140ustar00rootroot00000000000000PNG  IHDRgAMA1_sBITw PLTEwEhIDATxcXaj02V-9_)pIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s07i3p02.png000077500000000000000000000002251212556323000240140ustar00rootroot00000000000000PNG  IHDR;gAMA1_sBITw PLTEwwC%IDATxc8p 3aH? Wcn UIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s07n3p02.png000077500000000000000000000002121212556323000240150ustar00rootroot00000000000000PNG  IHDR<@gAMA1_sBITw PLTEwwCIDATxcj í9 7H qy ]mIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s08i3p02.png000077500000000000000000000002251212556323000240150ustar00rootroot00000000000000PNG  IHDRffgAMA1_sBITw PLTEwwY%IDATxc` ~`Ǡ0aXAAPPpJIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s08n3p02.png000077500000000000000000000002131212556323000240170ustar00rootroot00000000000000PNG  IHDRaVgAMA1_sBITw PLTEwwYIDATxc```ZA =V=LIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s09i3p02.png000077500000000000000000000002231212556323000240140ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw PLTEwwVd#IDATxc`@ X ^3\b  duFIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s09n3p02.png000077500000000000000000000002171212556323000240240ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw PLTEwwVdIDATxc` V,XB q iejC#IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s32i3p04.png000077500000000000000000000005431212556323000240170ustar00rootroot00000000000000PNG  IHDR SWQgAMA1_sBITw'PLTEwwwwww;IDATx}0F?E~S׆$F:5q,섁3$XV)16͗ù6H^|GO6vjPhq SXL!0Z(-`FUʂ $Lz*>=& r%@=E"t6;s2 q2wzE¥0l%=!޲Z;7R҄#IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s32n3p04.png000077500000000000000000000004071212556323000240230ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw'PLTEwwwwww;|IDATxѽ Wq zZ6w +'a0MHR\GrvX41$TxAsŖ?hnsof]A[ $ \V~01YB6ipaHz&.IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s33i3p04.png000077500000000000000000000006011212556323000240130ustar00rootroot00000000000000PNG  IHDR!!gAMA1_sBITw'PLTEwwwwww;IDATx]1r0E?0(xFHܸSKε+4>y˒Vo 4Ĺp:4a`5$9k L~FQZ #J3^K?Z]T4Z?&ikEJ 3) ]aVYEh-oĻ$`*1sPxKE= "aXIDo-MBC:e֖)Zir450NRk[]IldnaȕGR[_{Q_E6bp&],P]I4?t3P]jP33/2Z߈)IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s34n3p04.png000077500000000000000000000003701212556323000240240ustar00rootroot00000000000000PNG  IHDR""igAMA1_sBITw'PLTEwwwwww;mIDATxϱ _ [2г0&G+.%:r*Hv4,$ĂY49n2=xNNm'c)*+ TxVR,v5H\-Y~H:+NIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s35i3p04.png000077500000000000000000000006171212556323000240240ustar00rootroot00000000000000PNG  IHDR##gAMA1_sBITw'PLTEwwwwww;IDATxe=PQv@Xـ  zs;&}!rBї 8ycec:1 4;0'9]L(PFڄ唪*1j1-FtH?׹HD"nƮMM;-ww+T0q=މ^fBu/ep cA%q)uMa8VFL`?Ha; L[ΉqЊiIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s35n3p04.png000077500000000000000000000005221212556323000240240ustar00rootroot00000000000000PNG  IHDR##jgAMA1_sBITw'PLTEwwwwww;IDATxe!@״p иJ,{>a3Fު`Y\Ռ:T4b2(h%sn݆oEd=d2ܙBcF5μWhQd *F&Ny7zAwe\/x7rL#"ckLiq6>G΢HIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s36i3p04.png000077500000000000000000000005441212556323000240240ustar00rootroot00000000000000PNG  IHDR$$d)=gAMA1_sBITw'PLTEwwwwww;IDATx 0F?ӷ"]IQv iwqpvrTjbMI xA(;ވVLIFqxfSB0'zWs%Ñm rtȁ \Ap9*e} u} GT_`-U$ڪz3Q?, 0h+8郱oe(8Eb_zةj ,a*muuZ9?.IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s36n3p04.png000077500000000000000000000004021212556323000240220ustar00rootroot00000000000000PNG  IHDR$$.gAMA1_sBITw'PLTEwwwwww;wIDATxc`eG$! .dp]]h*t !,[*`:B,!,Bl7݅,NEPWB BB Є$BB62`XoIӑIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s37i3p04.png000077500000000000000000000006111212556323000240200ustar00rootroot00000000000000PNG  IHDR%%@ gAMA1_sBITw'PLTEwwwwww;IDATxe;0Q NL.`h쭨hSRڥr(70̲˷`FCj} |n(`,,䜖jK)#)(8n P M$0 6!a?ؑ\Zs!*V}6nJ_Dؐ w9)/ggL x q%x \r/Rx [JsFNk-8F߮Rz'#?IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s37n3p04.png000077500000000000000000000005201212556323000240240ustar00rootroot00000000000000PNG  IHDR%%7=0gAMA1_sBITw'PLTEwwwwww;IDATxe-@/M' '؄ z Vp)3x3@|Iz#S<#u*dUa02qEKϾ,27Fxlj<ɹ)Pz?KwWs:kPѤW2 -W]s' jeG KޭۯG|QV IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s38i3p04.png000077500000000000000000000005451212556323000240270ustar00rootroot00000000000000PNG  IHDR&&- gAMA1_sBITw'PLTEwwwwww;IDATx? @XA_EZF6i4475 :E@Hީn#9SЎOQbdTw)q!o;s+;qZIc|jb[P4i|aljƕAn6 S4zg4Xn8m uM/$yC;+|<ױ;^y}kvob땗){zB|DyEIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s38n3p04.png000077500000000000000000000003651212556323000240340ustar00rootroot00000000000000PNG  IHDR&&ZgAMA1_sBITw'PLTEwwwwww;jIDATxc`eG`$61 !fpCCl* L1lvX*`q*b 0h-܌81 bhb)`@da1-i+άDIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s39i3p04.png000077500000000000000000000006441212556323000240300ustar00rootroot00000000000000PNG  IHDR'' |gAMA1_sBITw'PLTEwwwwww;IDATxm1r0EǓ N."pʵ;ZJnQS:TV1/{f3T` Di@QhjZjvJJ85=P*0 zS`9!T5k=- K#<͚_.ܥ?Nubo&8:/G„"L,B MǔvkNhy>xxppWpX9çe(wx:Ι4e5߹?N|.@ny 5 IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s39n3p04.png000077500000000000000000000005401212556323000240300ustar00rootroot00000000000000PNG  IHDR''~LgAMA1_sBITw'PLTEwwwwww;IDATxm!@GHzp 8,šW1CufICgf`2(6 'Zж =E'̪ 'QuEyJڟd0`7~mdUg/C>d(U>ѱb'LWtec+`:4nh7zob/ Ǫ O/5+IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s40i3p04.png000077500000000000000000000005451212556323000240200ustar00rootroot00000000000000PNG  IHDR(( וgAMA1_sBITw'PLTEwwwwww;IDATx 0EZk]dRPN7upqg'B YP1BCᐼ<DZrQeA|$`O sc#WLYDOLILȾ{aWvD( cNٮaXDCapl5pQDV @"$֓I#U&!AHt xnR=͈ki^ jNp{xIENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/sizes/s40n3p04.png000077500000000000000000000004001212556323000240130ustar00rootroot00000000000000PNG  IHDR((~Х^gAMA1_sBITw'PLTEwwwwww;uIDATxб 0 DC,!alE6H%2EHVNRbG@!FeQk7^ Bؚp877*,mWBMwG)Rc!wu'gWpH])IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/000077500000000000000000000000001212556323000234715ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbbn1g04.png000077500000000000000000000006431212556323000255260ustar00rootroot00000000000000PNG  IHDR )gAMA1_tRNSXbKGD#2>IDATxUKAd[jH-5(5(O 'W:[":EEt-jv.<3`vBwqg&W? -³[#Τ}O$CptjL:dt}Kߩz}\}|x,ryT$eѐY\D5Z$/d<$t:  B>p|||||lv{~UHo0Ғr\<< BQje3 B@M,S&JpKs:N\O,r)ټw·d^ '@  UnL&dxa>|8J% )BBx灭ݻwͲ, l6˗jj5wt~ ~}6jZ7 J΍hThrXxe#T*`zeOY~[l6*W`͛7oy<dz loBP(DU@>:::::v:Y@oI*y`xaB*&&vp:GF8Nux~o'ӧ'O'OVWb.rnx5 L&IzFh48&(x8< 4T&@0H `b"HDfl~xD"H7i@6f, RT*B(2ٗy[Z[|>EzI |x|JPЅV{gg$ˊ}y,b\kE89= LOWUUUt:N~g2L&)|P( 3i). P Drz)Td`0 Y V֖N3PyF?F 03,+d2YG ..({U`'6{vvvvv033 kk@#|| ={<{q:iZVKAL  X,SjhцE_?Qt:Nh4Fp8ٮ.1MD[ *>er\.cYeY奞Aޠ@Vj4< 0࠘ qnjFH>Na::`"Ijsn>7ҍ7*1yT@HI$@$2ܬ--bpohhhhh ޳Dzz^r\NHw0D|a2r&˦,KX^+Aoooooo%w8RAZ܌n:*WUd U~hfl6奻^uL<:::::Z>.H$I%+t NOOOOOwKwgѴ[b_XXXXXX _-Jw7JMGP2F2Dڇ(*{^-w)SRRդ߿J uZ*|+n}}}}}}''''''-/ ᵅ IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbbn3p08.png000077500000000000000000000021501212556323000255400ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE""ssQ DDb ffs sssoookkkgggUUUggQQQMMM88 V 333////+/++w||+'s+++o'''x ###UMMM < oo/8' UUM M  bb UU3لww<<D ||| bbb"^^^ZZZ''xxxxssooDDD@@@ff<<< UU444MM|EE<<"""<33"D''<  // m4#tRNS@fbKGD \IDATxc`XW_!^.aaҡ^KE],C:OWF\` T%ڭg➗#~aξNlEdDU4.-S s D-JjA*t2g#3)"gįp(0I5{8 H\ _ $a}Fc7@y1`vY/rU&K­PqL%B}?aaDL*_*h>Md'R@C r+ ³L bXZ%J!F8N7) ( .&  $v̆Kwq10i^̼0>m8 jQ9/KxvYžL8xqI+s'IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbgn2c16.png000077500000000000000000000037121212556323000255330ustar00rootroot00000000000000PNG  IHDR 1gAMA1_tRNSYbKGDމc]IDATxX_HbiD  I  ƅ 6.Bq`*(`‹6 jYBȅrIHA!9CFxSgwWs<{djjjjj ywwwww!ICJT*J\[[[[[[cccccC WVVVVV`0H$DcX,_=FfvrH$----Noǣh4bx `0:N\. d2ᅮt*JR?ŋFc>/d<$t:  B>p|||||lv{~UHo0Ғr\<< BQje3 B@M,S&JpKs:N\O,r)ټw·d^ '@  UnL&dxa>|8J% )BBx灭ݻwͲ, l6˗jj5wt~ ~}6jZ7 J΍hThrXxe#T*`zeOY~[l6*W`͛7oy<dz loBP(DU@>:::::v:Y@oI*y`xaB*&&vp:GF8Nux~o'ӧ'O'OVWb.rnx5 L&IzFh48&(x8< 4T&@0H `b"HDfl~xD"H7i@6f, RT*B(2ٗy[Z[|>EzI |x|JPЅV{gg$ˊ}y,b\kE89= LOWUUUt:N~g2L&)|P( 3i). P Drz)Td`0 Y V֖N3PyF?F 03,+d2YG ..({U`'6{vvvvv033 kk@#|| ={<{q:iZVKAL  X,SjhцE_?Qt:Nh4Fp8ٮ.1MD[ *>er\.cYeY奞Aޠ@Vj4< 0࠘ qnjFH>Na::`"Ijsn>7ҍ7*1yT@HI$@$2ܬ--bpohhhhh ޳Dzz^r\NHw0D|a2r&˦,KX^+Aoooooo%w8RAZ܌n:*WUd U~hfl6奻^uL<:::::Z>.H$I%+t NOOOOOwKwgѴ[b_XXXXXX _-Jw7JMGP2F2Dڇ(*{^-w)SRRդ߿J uZ*|+n}}}}}}''''''-/ ᵅ IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbgn3p08.png000077500000000000000000000021501212556323000255450ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE""ssQ DDb ffs sssoookkkgggUUUggQQQMMM88 V 333////+/++w||+'s+++o'''x ###UMMM < oo/8' UUM M  bb UU3لww<<D ||| bbb"^^^ZZZ''xxxxssooDDD@@@ff<<< UU444MM|EE<<"""<33"D''<  // m4#tRNS@fbKGDf |dIDATxc`XW_!^.aaҡ^KE],C:OWF\` T%ڭg➗#~aξNlEdDU4.-S s D-JjA*t2g#3)"gįp(0I5{8 H\ _ $a}Fc7@y1`vY/rU&K­PqL%B}?aaDL*_*h>Md'R@C r+ ³L bXZ%J!F8N7) ( .&  $v̆Kwq10i^̼0>m8 jQ9/KxvYžL8xqI+s'IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbrn2c08.png000077500000000000000000000025031212556323000255440ustar00rootroot00000000000000PNG  IHDR gAMA1_tRNS33ObKGD3'|IDATx_hSw?@#^i/4@of4Z`UVf )X1`}Hp` CO v,fP; -Hcb$=ddf]w=99k}6G^|:x(޽{&E1& @Q'O_>nO&#HooaqqC8W^]z5 x8D",/fۏNp8vm~~~`d21;KF\ZXX8"۷o'''3ѹ߅<002V+Ϟ01nϟ?~Dt]O,!˸,,`2q@4Lm3ĉPa0~dt^o8xK竵3/، d˔A-W:k;tuu5b\`0(IA 7յ`hp8𡅾;`hhHzoo(WhhM C8$)y@`0xIkJ:\.i4UUX,@N cǎEўZFUр'O={ӧO;;;Gz IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbwn1g16.png000077500000000000000000000021721212556323000255550ustar00rootroot00000000000000PNG  IHDR kgAMA1_tRNSĬbKGD1IDATxUMLm–ʊ݂-kjh@D$!1!&z01WB"(k' h$h kXi!ǵJ~G3<;;3؂$l%8ݻg047/ ۧ<Ǐfjc߿GwS'F08xd:}Գgcc>ZEfiihsJGFr9Z\-}$Þ=w>z41qBW\_pW\gu$ s<929yVw7X>e2SSK1AAfaѺ:$DBFE?bNv`^NS2@mm}}Up"""cm lVy(*`cc:t V(OJ&ҥK SLddڅa~KQ> JKL|YxJ$VfVVʷSDWF\` T%ڭg➗#~aξNlEdDU4.-S s D-JjA*t2g#3)"gįp(0I5{8 H\ _ $a}Fc7@y1`vY/rU&K­PqL%B}?aaDL*_*h>Md'R@C r+ ³L bXZ%J!F8N7) ( .&  $v̆Kwq10i^̼0>m8 jQ9/KxvYžL8xqI+s'IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tbyn3p08.png000077500000000000000000000021531212556323000255720ustar00rootroot00000000000000PNG  IHDR DgAMA1_ PLTE""ssQ DDb ffs sssoookkkgggUUUggQQQMMM88 V 333////+/++w||+'s+++o'''x ###UMMM < oo/8' UUM M  bb UU3لww<<D ||| bbb"^^^ZZZ''xxxxssooDDD@@@ff<<< UU444MM|EE<<"""<33"D''<  // ftRNS@fbKGD bIDATxc`XW_!^.aaҡ^KE],C:OWF\` T%ڭg➗#~aξNlEdDU4.-S s D-JjA*t2g#3)"gįp(0I5{8 H\ _ $a}Fc7@y1`vY/rU&K­PqL%B}?aaDL*_*h>Md'R@C r+ ³L bXZ%J!F8N7) ( .&  $v̆Kwq10i^̼0>m8 jQ9/KxvYžL8xqI+s'IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tp0n1g08.png000077500000000000000000000012611212556323000254630ustar00rootroot00000000000000PNG  IHDR V%(gAMA1_hIDATx}]HqM2?rY65K]hddaQQ؅ldHPAt}t+BRUaH E-6kL9[_du{\{9Gr-_[qŃ=CX=. .Zmm9sn'%.o=?Ud^g#!{}]DЯvu1ޤGb];;0~Y4$pϐKd[/{j:Ru>Gcèۚp!]v4[>ˎξu#~ENŒ].?+FSD^RSe:^z52E$8 &*az$"a lUEy9Z*XWr=+pȶSM l;h ϫt;\rlnn 0(RVۂP%>rB-)ԙnQIHL0˧2ky݄:Ubrd,)gk;(~V٬uGU@Y޾ vK$j 8P=MIƻ9WR'uf5]K.ݣ6< ,=ۂ?)͚IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tp0n2c08.png000077500000000000000000000024371212556323000254660ustar00rootroot00000000000000PNG  IHDR gAMA1_IDATx_hSw?@#^i/4@of4Z`UVf )X1`}Hp` CO v,fP; -Hcb$=ddf]w=99k}6G^|:x(޽{&E1& @Q'O_>nO&#HooaqqC8W^]z5 x8D",/fۏNp8vm~~~`d21;KF\ZXX8"۷o'''3ѹ߅<002V+Ϟ01nϟ?~Dt]O,!˸,,`2q@4Lm3ĉPa0~dt^o8xK竵3/، d˔A-W:k;tuu5b\`0(IA 7յ`hp8𡅾;`hhHzoo(WhhM C8$)y@`0xIkJ:\.i4UUX,@N cǎEўZFUр'O={ӧO;;;Gz IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tp0n3p08.png000077500000000000000000000021401212556323000254730ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE""ssQ DDb ffs sssoookkkgggUUUggQQQMMM88 V 333////+/++w||+'s+++o'''x ###UMMM < oo/8' UUM M  bb UU3لww<<D ||| bbb"^^^ZZZ''xxxxssooDDD@@@ff<<< UU444MM|EE<<"""<33"D''<  // 2lIDATx}_ QeBeF"[2YHȣ6%VVk }^( e ݋wѳO},g V1Mrnƾ@2 wfCx>+cKQ–[-ȱ䈻#0Dfݦ%#v3nwd@\ qjɲ TYs%IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite/transparency/tp1n3p08.png000077500000000000000000000021331212556323000254760ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE""ssQ DDb ffs sssoookkkgggUUUggQQQMMM88 V 333////+/++w||+'s+++o'''x ###UMMM < oo/8' UUM M  bb UU3لww<<D ||| bbb"^^^ZZZ''xxxxssooDDD@@@ff<<< UU444MM|EE<<"""<33"D''<  // m4#tRNS@fIDATxc`XW_!^.aaҡ^KE],C:OWF\` T%ڭg➗#~aξNlEdDU4.-S s D-JjA*t2g#3)"gįp(0I5{8 H\ _ $a}Fc7@y1`vY/rU&K­PqL%B}?aaDL*_*h>Md'R@C r+ ³L bXZ%J!F8N7) ( .&  $v̆Kwq10i^̼0>m8 jQ9/KxvYžL8xqI+s'IENDB`chunky_png-chunky_png-1.2.8/spec/png_suite_spec.rb000066400000000000000000000127311212556323000223230ustar00rootroot00000000000000require 'spec_helper' describe 'PNG testuite' do context 'Decoding broken images' do png_suite_files(:broken).each do |file| it "should report #{File.basename(file)} as broken" do lambda { ChunkyPNG::Image.from_file(file) }.should raise_error(ChunkyPNG::Exception) end it "should not report #{File.basename(file)} as unsupported" do lambda { ChunkyPNG::Image.from_file(file) }.should_not raise_error(ChunkyPNG::NotSupported) end end end context 'Decoding supported images' do png_suite_files(:basic, '*.png').each do |file| reference = file.sub(/\.png$/, '.rgba') color_mode = file.match(/[in](\d)[apgc](\d\d)\.png$/)[1].to_i bit_depth = file.match(/[in](\d)[apgc](\d\d)\.png$/)[2].to_i it "should decode #{File.basename(file)} (color mode: #{color_mode}, bit depth: #{bit_depth}) exactly the same as the reference image" do decoded = ChunkyPNG::Canvas.from_file(file) File.open(reference, 'rb') { |f| decoded.to_rgba_stream.should == f.read } end end end context 'Decoding text chunks' do it "should not find metadata in a file without text chunks" do image = ChunkyPNG::Image.from_file(png_suite_file(:metadata, 'cm0n0g04.png')) image.metadata.should be_empty end # it "should find metadata in a file with uncompressed text chunks" do # image = ChunkyPNG::Image.from_file(png_suite_file(:metadata, 'cm7n0g04.png')) # image.metadata.should_not be_empty # end # # it "should find metadata in a file with compressed text chunks" do # image = ChunkyPNG::Image.from_file(png_suite_file(:metadata, 'cm9n0g04.png')) # image.metadata.should_not be_empty # end end context 'Decoding filter methods' do png_suite_files(:filtering, '*_reference.png').each do |reference_file| file = reference_file.sub(/_reference\.png$/, '.png') filter_method = file.match(/f(\d\d)[a-z0-9]+\.png/)[1].to_i it "should decode #{File.basename(file)} (filter method: #{filter_method}) exactly the same as the reference image" do decoded = ChunkyPNG::Canvas.from_file(file) reference = ChunkyPNG::Canvas.from_file(reference_file) decoded.should == reference end end end context 'Decoding different chunk splits' do it "should decode grayscale images successfully regardless of the data chunk ordering and splitting" do reference = ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi1n0g16.png')).imagedata ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi2n0g16.png')).imagedata.should == reference ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi4n0g16.png')).imagedata.should == reference ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi9n0g16.png')).imagedata.should == reference end it "should decode color images successfully regardless of the data chunk ordering and splitting" do reference = ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi1n2c16.png')).imagedata ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi2n2c16.png')).imagedata.should == reference ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi4n2c16.png')).imagedata.should == reference ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi9n2c16.png')).imagedata.should == reference end end context 'Decoding different compression levels' do it "should decode the image successfully regardless of the compression level" do reference = ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z00n2c08.png')).imagedata ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z03n2c08.png')).imagedata.should == reference ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z06n2c08.png')).imagedata.should == reference ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z09n2c08.png')).imagedata.should == reference end end context 'Decoding transparency' do png_suite_files(:transparency, 'tp0*.png').each do |file| it "should not have transparency in #{File.basename(file)}" do ChunkyPNG::Color.a(ChunkyPNG::Image.from_file(file)[0,0]).should == 255 end end png_suite_files(:transparency, 'tp1*.png').each do |file| it "should have transparency in #{File.basename(file)}" do ChunkyPNG::Color.a(ChunkyPNG::Image.from_file(file)[0,0]).should == 0 end end png_suite_files(:transparency, 'tb*.png').each do |file| it "should have transparency in #{File.basename(file)}" do ChunkyPNG::Color.a(ChunkyPNG::Image.from_file(file)[0,0]).should == 0 end end end context 'Decoding different sizes' do png_suite_files(:sizes, '*n*.png').each do |file| dimension = file.match(/s(\d\d)n\dp\d\d/)[1].to_i it "should create a canvas with a #{dimension}x#{dimension} size" do canvas = ChunkyPNG::Image.from_file(file) canvas.width.should == dimension canvas.height.should == dimension end it "should decode the #{dimension}x#{dimension} interlaced image exactly the same the non-interlaced version" do interlaced_file = file.sub(/n3p(\d\d)\.png$/, 'i3p\\1.png') ChunkyPNG::Image.from_file(interlaced_file).should == ChunkyPNG::Image.from_file(file) end end end end chunky_png-chunky_png-1.2.8/spec/resources/000077500000000000000000000000001212556323000207755ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/spec/resources/adam7.png000066400000000000000000000002611212556323000224730ustar00rootroot00000000000000PNG  IHDR~/tEXtSoftwareAdobe ImageReadyqe<PLTEF <2(dd ܣ/IDATxb` Vb`a ff  & h@1@;SdIENDB`chunky_png-chunky_png-1.2.8/spec/resources/bezier_five_point.png000066400000000000000000000001461212556323000252060ustar00rootroot00000000000000PNG  IHDR U-IDATxc?a78`74012@qX- cIENDB`chunky_png-chunky_png-1.2.8/spec/resources/bezier_four_point.png000066400000000000000000000001561212556323000252310ustar00rootroot00000000000000PNG  IHDR U5IDATxc?AL~10}a```W ]~ :vIENDB`chunky_png-chunky_png-1.2.8/spec/resources/bezier_four_point_flipped.png000066400000000000000000000001661212556323000267350ustar00rootroot00000000000000PNG  IHDR U=IDATxc?L (XILL< /.013,`bbt`b Z' .6lIENDB`chunky_png-chunky_png-1.2.8/spec/resources/bezier_four_point_s.png000066400000000000000000000001611212556323000255470ustar00rootroot00000000000000PNG  IHDR U8IDATxc?L@ ? (+ NIENDB`chunky_png-chunky_png-1.2.8/spec/resources/bezier_six_point.png000066400000000000000000000002041212556323000250530ustar00rootroot00000000000000PNG  IHDR UKIDATx-ʱ 0CQbF`F8(tt(%R;ޭH+0 %Z}}Jcc'=33#277ח퍫s/qm~D~{ݰDÛ"x~)|RjQND(mkH)0Q\KKtuuy]nz7.yQ>`|~rKUHi|x3I I_kGGG_.+~ ~W֘˝;dK_{EJ/=kdttԶzݞmLf|;Y~۔`tmEɈ_C7\X4ת#tkY{_{ֹt | Ys &i7MVSG.8#Ujτ4r(% NZ@uʄa&] NdoUw. (7jJtg-҅u._P.Є6I !1z @5`s'?`7S56`SAT[ T s#aB&ۦ4l׶) NV(..6ft%b6~< E-m7تSOv:r|9f,3zK]T ΔJ $sLYSi [v"]8jPB/ 6ܘNo8| _aD!hzV\0}Р@Gj DGbVdof$鬜:y @ "R9UhHd_7F$\TRC@+\pkz&YLƯ 5&qJ$ F.تtEV8t' N7gVMF g%d!X䄀 ǡь|GT. PiC؅I5y+u9Ym:p D5g% jVPubƑd|cNF7G6@:s6TH7\~>m6~6T`BCG.Ux'h5"7Y3Oß]2?lf^* w">C̉:6 M:ڄ7<~ëX΃)%xwp/ʇ4Hg}$xӥ4H0]AIG!,' d; ,xHX~AKv=U<32p.){}N6vnnɖ?ëxx5 \:8%yjmv4 _V7$/5\֕jBX}ظe}::;Ba4'ݦs# L c}Z~ f!73ҙY(;oqT]£?CIENDB`chunky_png-chunky_png-1.2.8/spec/resources/clock_bl_xdown_ydown.png000066400000000000000000000004701212556323000257130ustar00rootroot00000000000000PNG  IHDR alPLTEz~$$#$pqrqrcIDATx 0ԟ@P}W^61b]wI¾3DXM7 @ ueeaz'ÅDsUxq$=*%i|IƔ* j{ȠnY[Zy6B?%+ IENDB`chunky_png-chunky_png-1.2.8/spec/resources/clock_bl_xdown_yup.png000066400000000000000000000025611212556323000253730ustar00rootroot00000000000000PNG  IHDR2k CPLTEzz{}~}~~!))*--112255@EBEOOOUVQUVV]]\]^^_ffggooyyۍIDATxUKOQ;LP;-tJJy؄bV4.Av.LaAbLXd! 3%H%<Lߴ:vZe9{̹ V V-ȱ5Cc[岌J0](+\i hrK5~Hû^*.\@ 63i*VncK_DŹ~T;uɥ2 ۡެAڑ1qFbszEø[ĻƯ %4V [Jc8ε>oTfc4"n 0!|@؂ĽH#F\DjG3GYuDlD1"QKP/#OHH(*PB4%A0 %TT8P6!m%IW85 E.i?jb{@ʛ*s)%UK?R%)bKndId ܊2 Z˭jMQxf`֊:D VWzjt6@gGTX_X 3-]WU >-I.0x ݈u6rh8Mŗ_Ps4F'.A_KO@?UEN/^D󔝭"좄8 dk!ܬ 7?Ώ&|=gN"K;IENDB`chunky_png-chunky_png-1.2.8/spec/resources/clock_bl_xup_yup.png000066400000000000000000000040361212556323000250470ustar00rootroot00000000000000PNG  IHDR-- PLTEzz|}~{||}}~~,-74477899::;<>79;>AADHADFGOPZYYZZ__]^^ccdfg}~VE.KIDATxkLYOgtn@u+>+ .GE] >hHL$D cBW$ ]6 Ȇڵ#P;>LWWݓ;sen9]iקHIq,3w.B~N·"ah7|fʚA(Y13+sc6Y N:ʘxZf{;Ñ*g=/ :ͼYWҖ36'Wue7J4ԯ~V4,ө|i;cYN[ǽ N״ v<{ywF,xe6@z-g;Ozi1>km;*xufmH6VhbZ^]EyAoɽ9l87atmы*J8g@i0xLbjrT P 1=+]ycBχGԼ ~M3~xY~J>P>2yXNr>3@Xh}F?m& &)>J<,迍]bA >ʆm,HH(I= %ҘsIdaL[h.zB _olq c[!)rNpH^];園 g$h5iK_ Anq4.!虧svnv807hĽqt8q0;X7{ط&~{1e"[#7bQppV!{`ff߱H-N#@1W8 }%THLL2?spA$rB)e48ueܝ#,CxxM)hO>Hf)&jDH{z9^іS!)$ۤ=yBXRד54̍AHxk T1q*H06p{Di4 yF(vl6*]V>Z9{sP 4Q(/w,.IqTQbSMQW*DjfhVQAO6fK"2Yx, 6Y=*Q{ 1ͺdnRoG?KhS$TwNΛ '! Mz _/~Æ"*\spv'n%ꥉ*09IENDB`chunky_png-chunky_png-1.2.8/spec/resources/clock_nn_xup_yup.png000066400000000000000000000017601212556323000250660ustar00rootroot00000000000000PNG  IHDR-- PLTEz|"#CcsxO$IDATx SPA\R[qu5P=M̭3(癗sz2I㓙梳Mc4GOGN'1x``vѭNt/__]OGG*ԫ p\&^+)j|&P..4ϏOOϯ} EI0MH˝%BrZ/@t oh@XrV;fxG]2Ɗny^d yCqZ=rC}1f^`Ws CxP3 `&!Ҙ0g4C!Tp@!{X")5'|Im4W`G=o9?\c!qs:kᄑp1pbܺpǩ?Fc  \(H ́ox'Dmh ԏ UE0d4dI) nJCL#.H;wK}"%'J].Kp"'5qP]$=TPTP,3}|,!WհMӗkeOUI~~zei|4QsnޚN ? '9[ uؤAN&vAI:l_5]; aWq~dim GzV -Nf]X˫R)u<g( IENDB`chunky_png-chunky_png-1.2.8/spec/resources/clock_updated.png000066400000000000000000000016171212556323000243110ustar00rootroot00000000000000PNG  IHDR(( H_PLTE 66WWhhoozz3,(IDATx} O@ۖR(PPE#!"B,wWql;OٙMiDED+=>Xw^̹"y(\[C=r1^?)虔;j9dQ8UMbi7eߏiS&+A>Q*W'gXK-,͇VZmMWȴeQۤ H#Uڶ=9#@}ޏ@ yd.¨|A*BDd9< ˨ 4n@9$cRYQ>*ȋ\DIK?<r0 8(HF4R8> @AFl|(1*Cbt4Aj5 zľ&_[seL60<(6#02@qRJ ] 1R&'qfA}%3pb"5CP L Dx @|K^_M$,Zt2%u)L;DSjiy5`,*N]kj: ~u% ::Aj !{)Õ$+cɠ^8봠x ymڡ* ^_:ͨ:TcgSsshNg>?|A*s !^C!$>n/cODx2IENDB`chunky_png-chunky_png-1.2.8/spec/resources/composited.png000066400000000000000000000002541212556323000236520ustar00rootroot00000000000000PNG  IHDRh6sIDATxcb`/_ 0o`?`310Pí[z3g7"J Ij.Z..&.޴iir2 IENDB`chunky_png-chunky_png-1.2.8/spec/resources/cropped.png000066400000000000000000000002671212556323000231440ustar00rootroot00000000000000PNG  IHDR7`PLTEP`pP`pP`pP`pЀАРаBIDATxcb`b Z]1CIENDB`chunky_png-chunky_png-1.2.8/spec/resources/damaged_chunk.png000066400000000000000000000001071212556323000242530ustar00rootroot00000000000000PNG  IHDR 09IDATxcb&3(09IEND09chunky_png-chunky_png-1.2.8/spec/resources/damaged_signature.png000066400000000000000000000003601212556323000251450ustar00rootroot00000000000000PNG  IHDR )IDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/resources/lines.png000066400000000000000000000006741212556323000226240ustar00rootroot00000000000000PNG  IHDR(p 9PLTE2}%22>dd}#RU/U/____>IDATxur E %tL}K (mQ̜]"ܬC4:h?9"4Bl<B#lLqN=U WaK3,< tV$X 鸏W9 _cAƅ.uPp0= O=ye& NmeMڝ\̞i%0?7Y8 1& Sw8LFWyMtWRU2Dyt]T}y*HqO*·w%Ы۽PBUp1@H$X#Vc^RBǟ&eOGIENDB`chunky_png-chunky_png-1.2.8/spec/resources/operations.png000066400000000000000000000002011212556323000236570ustar00rootroot00000000000000PNG  IHDRh6HIDATxcb`/_ 0o`?`310F5j:&ҫIENDB`chunky_png-chunky_png-1.2.8/spec/resources/operations_grayscale.png000066400000000000000000000001721212556323000257200ustar00rootroot00000000000000PNG  IHDR:AIDATxcQT7sr OaB"@,$ T!&4@hz8LŪEHkM%a. SIENDB`chunky_png-chunky_png-1.2.8/spec/resources/partial_circles.png000066400000000000000000000002731212556323000246450ustar00rootroot00000000000000PNG  IHDR g PLTEݵ_tRNS]<ZIDATxc &(ʐIѫUL l _@ U %@0L.""L0cLJ`1X`x%߹TIENDB`chunky_png-chunky_png-1.2.8/spec/resources/pixelstream.rgb000066400000000000000000003751001212556323000240340ustar00rootroot00000000000000Ȧoookkkggg'%# #" $" (&$*'%*(&+(&,)'*(')'% &$#(%#*'%+(&+(&,)' //////........./// +(&+)')'&)&%)&$'$"&#!$" %"!#!#! # $" %"!&$"(%#)'%*(&,)(-*(-*(-*(-+)-+).+),*(*)'*(&$" (&$(&$*(&+)&-*(-+),*(-+)-*(,*(+(&)&%'&$'$#(&$*(&+(&,)'-+)-+).+)-*(,)',)',*(+(')'%'%# dddJA:nM3ΣVVV T6Z:!0jG,qM0[=|U6d<f?!`;8)08)08)08*18*18*18*18*18*18*18*18*18*18*1=/5mK1wT8uQ4xR4`AqJ+///,)'pH)|U8Z~V5_>tK+mD$nE%X8[;tL-a:3! nK0jF)hC%sK-Z:`@X8h?pG'kD#nG(qL/uR7V4]:`;pI*xO/^>gGZ9|T4]>oH)qK,kE)uR6mJ/%#!cccH>7iG.ڒZZZ%%%''' bF-\<#1 fC(^AhIiIeE|T5wS55'.5'.5'.5'.5'.5'.5'.5'.6(/6(/6(/6(/6(/6(/:-3jI/vR7wR5zT6uM.mD$000.+)tL,~W8zT7\@WC3T8#gD(sN2{U6Y8~T2[9|R0[9cAmLnLiIsL-;( mJ/iE(qJ-yQ1kKxVmKcA[:[:tL,oI,uR6tS9qL0{U7sJ+uJ(a?gE_>\]=dB{R12/-\9^"fA#sK*__>zQ1"'c@%cA'$#!dddE<5a="lll 444<<lB ]8W1xK(sI)6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/ EEE41.xI$wI&pD#oF' ^9nF&qF#S.U/xFR+V/V/Q*T-xJ%rF$6!`;lD%wL+|M)R,O(e>\5R+W1xK&qF$g?!gB&kC$xM*~O*P)O'Q*P,{N+tI'C:+dgPV3`>$&#!nnn^YThE(666***>>>BBBGGGKKKOOOSSSWWWOOO666^A*iJ24$ nJ.aBkHmHqJmGfC8)08)08)08)06(/7(/8)08)07)07)08)08*19*1=/5yT6[:T2d@nHjD742e>hDb?{Q1>+wR4a@d@^7mE|Sg>b9yPWyPkEeAB- }X:|T4Z7boMgBmFvPnJ }U5dBjF_;rLtM963kD`:iEcBmH,Y:~T2mIvOb:}TsJwN}TZ[e>kGF1  ~X:dCgCpJW]sJuL~UxOzTc>fDZ:kIqL~WnEtKtLmH^;|r]޽Ȭ бtP4666@@@ZZZ@@@%%% RRReI1gH16& zW;\=gDuP|UqJmIlJbB]>qL0sP4wU:wU:;):(sQ6rP5xU;{Y>rP54$dG1aAiG`=iDxS{T{U963wP{Ud@`>cD1"aAoLmHqJV\~UZY~UV|UvRG1 "gGhFzV}WXW\ZyPW|UvQmJmKjGqK~W~UViBkGlJ__SӶȭ бsN3666 BBB{{{P2Y9!. kI-jD%vL,Y6[7\8yN,oF':'#tO3uP3rK.vP2yS5yS5jD&jD&tN0nG)sL.Y9 2 X6[8fAiC]8d>pI631b;cyQ0O6!*U3Z6kFoHmDvMpGvMf=Y0b9]6jDvN/Z9Z;#T7 V;%T:%Q8#S:%P6"T;&L3D*J1H/O5!M4F-I/K1R7"[?(hG-\:^;lGf?Y1c:eDDD???zzz]>$pN57' M4!O4 [>'N7%K6&,3~Z=]>`@{R1}S2V5~T3V5fEmLjIxM+)`>lGsMwPrL`9f?741lEuNkEnKdCtL- H0cB(pO4}[?cEV6}Q0fCnIrKWyPg>kBh?h?h?vM~W[jFW3fD`@cD`B|W9vP2rL.oJ,qK-lG){U7]?fH~X:oJ,[=~X:dEgGeDdA]9`:jCc:vMWcW_6vNqJvQtPlIeC{Q0~U4X8V4_=oK{WwRW0nFuLyQySnJaA]XJ޽Ÿ бoJ,  [9]95# +,, - NYy lI-~W7W4fBb>iEgClHpLlHa=~O+f?!0a;f?f>g@qJgA631O(e>be>f>kBxOqHsJwN{RxOzRoHR,R-vH$[8W5X6X8\a@`?jI`@pF%oE$nD$|R1Y7\:c?`;e?mFmFuLyP\W\3V-X0_8iBgAe?[6b>`=c?gCmHuPvP`9S+W/\3jAe>b?|S2^TEҫҫyбpJ,oI+zT5zT5sN2eD)_@(V9!Q4Q4L1U:%M2P6 N4M3O4W;&Y<'Y='X;$X;#Z<$[;#\: fB%sM0oH*dB&W:#W:#V9"U:$Q8#S9%L3L2N4 K2H.I/M4 H.I/J1M4 K36$* ,,76:954,,##kH,}S1gAnFnFhAce`=`=[7Y5[7U2|P,T0]9U1U1Z5]8_:e?iCqJd=f=kBg>_6`7kB_6`8b:jBhAf?kDqKcg@d=]5_6c;\3h?c;c$mK2sR7uS8xU:rO3sO3rM0sK,cCcC`?a?W6}T4sK,}W9]?^A[>}X:}W9sN0pK-bD~X;}X:Y;tN0oL/a?%('76DDCC98--"" qM1V4^7rIYZV{R~UlCoFV}T|SwS7 kG+`9g@tM852tNqKmFb;a;Z7c@I4#4$]<]c=tMuOpJhCd?d?Z5kFsNsNuPkFiD`;Z5tOqLmHnIe@c<\6pJxQ{TyRuNvNiAf>_zQyPzQnElCc:uM~VYW{R}TkBmD\yPxOvMkEY5xO.PH7~gб\=Y:`?iFb@\}X;wQ4Y<\=^?}W8]>`@_?_?]:a?lHgDlJnK];dCbAdC^>a@eDbA]<|R2bBbB`?dCjJ[;xT6+*:9EE??54)) [?a?lEpGsJzQ}TV[WmDYvMyPxOlK (aB*wR5631qKvO{UrKmFiCiEa@ gFmJuQvOrJ}T~UZ{RuLyPWzQxOX~VzR{TsLjEtOnIsNpLe@oIqKrMiDpJrMlHjFc>iDsMnItOvPd?oIlGnJjFnIuOtMpId<{SzQxO~U^wNvMtKuLyP|SWWzQf=bxOxOyQXgCZ:`YEǤҫҫ б\kGpLpLeBhH328743,+"!yV:fDZYvMW˗n_{RWwNlCnEwNxO|S{S|Y>8( GGG631kEuNtMtM|U[jFjF@/"U<(pM]9xRxPpH|S{RxOVavM{RÏfa}UyQd>}XkG_nFnF¶ȻĶ·µǺɽǻĹ÷ l xT8V4[4b9O&\3mDrI`7P'Z1Z1_6eQ*y]GfPjTx^HoU@tZE# iBvOoHevMkB̭  l vR5Y6rJtLeByRwPsKjAuLWg>oFmDjAwNh?sKqItMzS{TqJ`;iDnIbjAuLwNΫz xS7fD`:hA;'|O+R,b:Z1_6W.jAh?vM~UzQh?sKV.b:V.Q*X2S.X3rKwPxQmDuMW0V0}O+zM* hD'vI%b=`9lDxO|SrIoFf=Z1a8Y2O)V2tJ);$  G1lG*rN0iD'Q5 nI+qL/{U8wQ4 S6oH)pF$Q-a;c;lCzQsJ{Rf=h?_6h?X/^5d;Z1]4^5f=jAyPvNtM_9d?zN*V4oF&e?!jE)```бtK*~R0}N)Q)R)Q(N%P'X/\3]4a8^5_6U,^5Ϋb>"yN,Y4\:D(rE"S,Z2\3_6]4g>N%Y0\3_6Y1O'~L$L$M%O(T.S-}L&U.X1_6X/|I"yI"vG#sF"@'.~R/|O+U0T,U-_6c:W.U,R*N&yH"tF"qG&iC%*䎎& U6X8W6$ ]e=e?!000бzP0W5gBf>a8uLYyPyPvM~UyPoFmDuLf=̫W;$xN.d@  ^AlHvOzRWsJg>mDoFb9sJxOi@]4\VyQxQpIf?kE`8oFvMd<\5uPqMdC gFhE_:g@pHe}TwNȾɿά |u|,J4!zT53!  `BgCyR|S[kB|SyPrIa8wNuLWWW[oHhFmFqJoGb9nErIsKsMpKsP8'L6$nKc?a;uMrJe<?35$I2  +_?wSnGXxOqHnEsJyP|SX}T^|SuL^:tMiCsK|SyPyP}UYrMiEzS4 lIgClGlE{SxOxO~U|VxSx\x\x\x\x\x\v[tYtYtYtYv[v[tYtYsXtYtYtYtYuZv[v[w[v[v[v[w[x\x\x\x\x\]=lInIlEqIsJqHyP\yPV|S}T[\sJZyPqHoFrIrI|SXzQxRjG_> бuK+wK*S/Q+S,xH#{M(W3R-T0|M(~N(S-\4^5_6ûȾк-' ,-,*( +7"H.N4 %#"$""$ e>pAT-X0a8d;b9V-Z1`7Z1T+V-`8a>#`9`9^7W.\3Y0W.P(Q+W3T1 H,yL)W3\6]6\3`7V.T-tF"oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@a:rH&}O+W1a9]4[2\3^5^5R)]4c:^5W.N%Y0[2`7c:`7Y0Z1Y0\4~M'yL)yO.PPPбzR3xN-T2Z7hD^;U3mM4H2"aAY6\8Z5xRwOkCƺvW=tP4|U7qK-cD^?Z<~W8kD%~V8[=cE- --,10/+*)"!   jC%fBsLzQzQlCg>c:zQxOmDzQ[xQ~WoHg?e"X5_:_8iAkBxPnIcAoS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@eB'b;\:]8cg>qHjAuLpGnEgAiF`?OOOбrM0sN0X9R:&  }S2U2\7e>º׿pHtMcJ0 $""654CBBA@@554)(' [<"U2e?h@yPyPqHjA`7lCh?d=b;Z3b;jBqHyPuLoFb9jBd=^9Y5M4 1 T1Y5b=f?tKuMgBW5rXtYv[v[uZrXrXrXrXrXrXrXrXtYtYtYtYtYtYtYtYtYv[v[x\x\x\x\x\x\x\x\x\hF+oI*xM,Z6mFmDuLtKxOi@lCmDe+^:nGrJ|SW{RzQY{RmEe>g@hBlDtK~U{RwNyP~VvOhB[7U4 U4]:b=pJzRpHjEcAoS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@rP5~W9Z8kFhAtKyP{R}T~U|SWxOmDmDxOxOnEtKlCrIwNzQX{RrLpMbA+++ ___б8' """### cCfDpKǹø|S~U~U{RVVZnEX^<1"10/=<;;:90/.$"" gFjDnEtKvMX|StKYyRrKwPwPpHrItKxO~U|SqH}VwQmInK4#M7%kHiEkFpIxQqLdCtYtYtYtYtYtYtYtYtYtYtYuZv[v[v[v[v[v[v[v[tYtYtYsXtYtYuZv[v[x\x\x\w[tQ6zT6eCmIYyPoF{R}T~U|S~UWYoF]sJyP}TZvMoFvMzQ~UsMgDjIWWW@@@)))а[@+ ###### [?)a@jGȻ÷ȿ_őhVŽe͙puL]lCpHrMeC 0/.0/.('&V:$dC^}T^őh]]˗n\xQgAiCuNYZ^`^_d^qJhCnJZ; iFlI\|VZzUrPoS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@mK0^?cBiEuMőhyP^őhVÏf͙pvM\mDtK}T~UqH{RȔk}T^ÏfXYafEvvv^^^а  ###############$eFmLȺø[^_`\pGdvMVlD_;bA0# $""  N5 Y;oL{U^dZZŽeÏfwNuN}VnG{U][_[YƒiŽe}TpI}W[6iE$cG1wSzU|V|WqMvUuZv[tYtYsYrXrXrXrXsYv[v[v[uZtYtYuZtYrXsYsXrXrXrXsYuZuZtYtYtYtYtYuZuS8vP2X7oKZ^^[^^`]pGbwNXqHkB{R]Zad^YZxU^=@@@а!!!###"""###############bB(W6ɾȻølCi@f=ejAkBa8V.[4Y3iCjDnFc:qHb9oFjAh?U,W/Y3S.T1\<$#dAW3d>d>]:~S2oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@a>#mG)X7]8b;kBf=kBh?eƻɹƺ`|Sf=g>}TYc]oFmDkBoHmHhGP9'$eE+wQ2V4iCqI|S~U]Wb9b9zQ}TxQwPe>oH|TpGZbd;\3{R|SWwOlFc?]9 ~Y;b>vQ}W[7rG%sXtYtYtYtYrXrXrXrXrXrXrXrXrXrXrXrXrXrXrXsYtYtYv[v[v[v[v[v[v[v[v[v[hE*vP1X7iEvN~UV`|Sf=g>|SYa\oFmDkBtKxOWX_|Sa9]7gDgFpppа2"########################iG-øο˽b9\3a8Z1e"pJ,U4c>b:sJc:c:[2a8Y0d;jAkB`7[2c:jAqHnErI^5i@V-]5T.}P-V5000б]?'### !!!################## -μ˿õ¸f=vMwNc:lCg>\3b9i@sJVtKtKnGlFY7K3!V8!nG)V3f?~UkBzQkBuLpGuLnEkBh?]5`9\5e>tMf>tKqHpGlCnEh?^5g>`8c<\6lF^: 9&kEgAc>`lC^zQxOyPWpId=f@jDd>blFV2W5oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@vS7vO0Y7fAjBpGwN}Tb9euMrK]6d=`uLqHyP\c[xO|S|S[uLyPkBsLa:hBiCc>pMjJcEPPPСuR5iG,( """""" 2~@@% PJFȹʺĵöʼϾŻɼxZyP~UWtKwN_~U}TwNpG~UpGc:qH|TsNjGfE`=d?YVyPzQi@zQnEkBsJVX\YtKjBhBpIlDpGpGyP~U]_}TqHW^wO}UiAuMuMh@kDtMsMvR`AgFeAmG}UsJd;pGZzQ~UVsJvM_~U|SwNnEzRjC\6fAoLkHeD]>uP3qqqa]YX2Y5N."""2/x[BWZZVV00776ξźĺùøùø¶øȼʾɼgR)P'{GL#L#N%vBxDK"xD}I N%K"P'L#|H~L%xH"uEwG!P)s@uAN%xDyEM$M$M$L#N%L#O&L#}I!\:{J#yH"|I!~J!N%zF~J!~J!S*L#W.xDs?|HxDP'{HzGL#N%s?vEm>oC b9X3J*?%=#?%:">$:! <"D(Q0S-Z1j>n?uD}J"{GP'M$P'P'{GL#M$N%uAxDL#xD{G|J"vFvG" %)]gK%)2+P/СyR5vP3wQ3, !!!\X4>;$31a=LH%>NN>>66DDffĺɻz~UpGjA}TuLwNnEb9g>xOkByPzQtKpG|SnEoFwOqHwNrIf=d;tKuLzQyPpGqHyPpGtKtKqH}X:rKpIrJoFrIuLxOqHwNvM}TnElCqHi@uLtKtKqHqHpHyRe?iE`?[=fD)X<&Z>(^C- N4U:$Q6  [?)eF-pL1yQ2U3]9mG`9sJ{RvMsJ~UpGjA}TuLwNoFa8g>xOkBuMoJc@8=-pxxd]]]бuN/xQ3vO0nK1  pkf7+)TJ<MMJJ55--;;W,,40.ƻȻ{tKsJtKuL{RyPrIkBU,jAkBg>kBqHtKtKmDqHuLsJXtKkBV-g>nEnElCoFtKqHmDsJqHpGxQxS6iCjDf>oFmDwNrIuLi@vMxOuLoFef>c:kBrIuLtKrIuLuLzQzQsJjAU,jAjAc<`;^;G@-yaxx[aFбvO0{S5Z;`@[Z1c:nEi@zQ|SwNmDepGyPvMuL^6lClCh?h?pGY1\4a9oFi@oFvM~U{Re>d=Z5V3pJ+C-hH.oN5nN4_@'5!_>%a?%eC(_@'G/ F/N5 nI+wN._dDiJeEaB\?5% 6$U:%flg775!hZ=ic0 q22DD8s_M}gy~yzi`X}TZ}TpGrI~UpGVV{RW`Y_Z~U]WqHqHwNg>XzQX][[\YY_XmFg@tMySyS|U]|T}VXZXrJe>nGdhByRuL~U[zQ~UcZ`X}T[~UpGrI}TnEV|StMoLlKFA.ixxckNб~W8}V7X8V5}U5pO5 1 KZ#  iys> ?+tQ2hArIWvM{RrIf=uLwN}T|SyP{RxOyPoFvMXxO|S}Th?vMwNVVxO|S|S{RsJyPW{R{R}TkBtKsJ{R}TuL}TuLuLnEwNV}T{RyPmDrIh?uMzRpHzSpJgAmHpJkFkClEe>kEvOmGwQuOf@kElFvOtMsLqJnGoGkBpGzQtKwPtNjH $ Z9]=F1 ;)hF`>G2! >+jFnHsLlCuLXxO{RVi@vMwN~UVwN|S{RzQsJyPWyPsLkG~T4LH2pxxv\ а_AfGjJ 8 E D0 fb=OJ(><"!  FB(<:852/+(E0zU7rL{TxQ]zSdYZW^~V\YsKpHyQWxPczRcW\Z^Y\XuMoFyP|TzQcxOőh|S_Zd]_`|StKrIzQ~UbxOőhvM^^aXZ]lFoJyToI|VqKY|VY{VtO|WgBvQ^9pJzT{UxQ{T\]^^đhwNZsLsNH2 D1"pNpMxVhFlKgG]=\W3X6[:oNmKtRpMiFmI~ZsOjG\:fDeBc@`=]9vQtQqMnJdBqOsPwTpLiCpIwPWsJd;W^`X|S[VȔkYrItK~UZxOa8zR{UwTfEFA.yaxx`eIϯyT7sN0@,0 %<|niAU- Wz:Op"3X?\(K_=P`+/~77s11WId!!&" \>$kE&vM+}S1_=[:\;];bAcA_=T1|Q.yN+\:X5|P-T1W4`=a>]:dBc@eB^;S0|P-{O,\9|P-{N+V2[6d?kEe>qIkCxOkB^5^5^5g>pGW.b9d;lCqHjArIf>vNkDngblfalf`jd_ga]f`[20. {S3fB`=gC`;lGc?^:T1 @*W5{O,vJ'V3[7iDe@nHe?pIrJiA_7c:]5b;]6V0X4X4eB_=dA`qHsJnExOtKxOnE`7]4]4mDa8Z1a8f=nEqHc\3_6|S~Uőh]jAi@e<^5]ZuLnGc^9\7`;uPrMe@mG]7Z2a;uM\XlEb;Z3b;g@~WyQlEvOa:Z3`9tM[]zRkC]4i@kBZ~UqHwNh?^5c:|SZdZoFf=g>d;ZxReA\<80!ixxZ`Fϭ35!.+Lw'TgTiY"WYb9a8a8kBi@e=xL)R6  GGG2/,zL)V3X5Y6zO+hD(2\:4"!0 uN1gE* I4xL)V3Y5_;W2\6[4T,`7jAeb:U,lC^5a8_6]4lCa9^6\4g>nEf=lCd;Y0pGa8f=^5f=c:e<^5]4ejAoFpGwNrIfD+ I4"Z>)30-`=b?iFa?lJxU: Hk?  V=)kGsMwQoHnGjBjAc:nEi@nEoFpGuMyQ{S|S}T}TvMkBjAh?lCg>pGnEqHyP{R{R|S|SxOxOnEf=kBh?b9mDvMqHwNzQzQX}TvMvMpGc:jA *S8$iEvMvOsOkH8=-ixx|^ ά2! 4D fzU'TymBwd-E{nRN0NJpRR%BkkJcx=;  M3???vO/~R.^:qN31!(gF,ZvMuLxOwN{Rc:gF, Y9 jBnGiC31/V3^;X6/"# 4M(Bc5   1J&  Z6Z5oIiC_8^5xP{SmFnGvOmFf>g?h@e=X1sLmFg?c;jB|TsLoHnGsLjBnGiAc;Y2pItMqJa9a9YuNpIjBuNoHlEjBc<`8nFyPkCf=kB_uLrIiElL2&2"qK-V1^; }CI5 ]@)653ά~zw "@En3}2iFSyAA|f.tLs)wYNk') ]A*???~V6nLiDb;i@W2\B-0#wP}T{RyPyPdB(e?YnF_8a:30.fB`=V4.!;W3Hk@ *- 2K+%'?'jdDg=S8#iFiE^:iEb=jEuPZtOnJd@iEeAgBaZ6mItP{WoJpKiDgBiDiE_:lHqLd@]9Y4lHsOzVrNqLlHhCmImH\7jEzTkEclE}TX~UlH6%bAZ|SkG,iBVjAnFpIuN41/mJeBdB~T3]A+Fk9;X2 cWOuF  fD[8b>fBgB[`>dBiGeCjHbA][9fEfDfDiGfD^=kJ]lIjHbBZ=uQ4)'$ά~* 9V-Ci 2\v3gW(JB}l:b-BxtQv1]~/u{XDK 2!???oJ,zP0c@e>c;c:c:jAb<)`:\3:&sH'\3i@f>lDg@g@30-~P-zN,uM-?)4M'&9Pw=  %7#MrD *wM+~R/Z7^:|O,`=^:d>^7[3jAd;a8^5d;X/]4f>c;vQ47%\7W3}R0xP1rL.*'&nnn YURkJ0`C, "D}yRŐd4:tLaњxj.N .'!ZA.R6 ???zU8^>jGjD{R`7i@esJi@S*e];_?xR4aAgDc>mFoFmDmD`7b9Z1i@i@h?uL{RpJ(d>_<V5.+)cccE<5b?#X9!Ug.%> B-PaQ:pKhyESg<%NpföXrM'%%' ]<"OOO!!!???YYYiD'vL*Y4]6c:b9_6^5`7R)L#, [2\3b9`8[4]7^80-,{N*T2|Q0J/    Ch>'"6 E,zO-|Q.}R.|R1vO/iF)rK,V4V1\4^5T+N%W.{G[2]4`7\3d;a8d;I0U7}P-wN-,*(bbbI@9nL1sO4$ R t_FĴ?/ gH/fE+lK0iH.hG-8''...???fff///zT5a?];b>mFnGpItMxQqJlE EEE2/-hF^=^= /sM1 7&'?\9I1%*bA\;a?|S4vP3fE+uP3[;b?tNvOtMqJgAtMsLuNjDrKgAgAlEX9/ dBbB-+)dddJB;]B|Y=_CY@,@. V?-*079S E@ Sf$#0 iL6tUxV;|[?~\BvU:[A+??????kM6aCjIiGyUuP\wRlGvQnIe@Z5?-|X={VsNyUkGwT^B0.+fDmMaB `@gHaCqS;_B\A- zU9oI,**eEpOjIhJ]ArR8\@fGrQoKiDjEhC\7zUzUxSuPyTrM~YwR[ ^>cD,)'fffPF>}Y<~Y=~Y<}X:|X;vS7M5# (? #6$$R;)kL2vU:wT8uQ6zW:wS7uR6xT9L5#/"  1$         2#cB(aAtRkIgDpKiD{WxStPsOrMd@S<)<'pKtP{WkHjGmJ4441.,yO.^>bE/$^>`A]>]@[=lN91#{U8pI,^?rJ+\?fFfF`@~Y;vT:vR4gG];tPfGkGrMsOpK[7qLrMZrMjFqLhD)gI3dE.+)kkk.,,VQLUOKVQNVQMTOKPLHOJG @=:EB>1.+-*(IDBLGDNIFPLHRMIROJSOKTOKSNIOKHMIEKFCIFBIEAIEAIEAIEAIDBIDBIEAHDAHEAJFBIEAIDAHDAHCAHDAIDBIEBJFBIEBJFBJFBJFCIEBJFBJFCJFBJFBIFBLGDNJFRMITOKga\f`\c^Ye_[d^Yiiiiiigggfffffffff_ZV^XT_YU]XTZUQVRM PKGUPMXTO_YUd_[\VRoooÿѺի֬Ĭ»ṹñ¼chunky_png-chunky_png-1.2.8/spec/resources/pixelstream.rgba000066400000000000000000005214001212556323000241710ustar00rootroot00000000000000oookkkggg'%# #" $" (&$*'%*(&+(&,)'*(')'% &$#(%#*'%+(&+(&,)' //////........./// +(&+)')'&)&%)&$'$"&#!$" %"!#!#! # $" %"!&$"(%#)'%*(&,)(-*(-*(-*(-+)-+).+),*(*)'*(&$" (&$(&$*(&+)&-*(-+),*(-+)-*(,*(+(&)&%'&$'$#(&$*(&+(&,)'-+)-+).+)-*(,)',)',*(+(')'%'%# dddJA:nM3VVV T6Z:!0jG,qM0[=|U6d<f?!`;8)08)08)08*18*18*18*18*18*18*18*18*18*18*1=/5mK1wT8uQ4xR4`AqJ+///,)'pH)|U8Z~V5_>tK+mD$nE%X8[;tL-a:3! nK0jF)hC%sK-Z:`@X8h?pG'kD#nG(qL/uR7V4]:`;pI*xO/^>gGZ9|T4]>oH)qK,kE)uR6mJ/%#!cccH>7iG.ZZZ%%%''' bF-\<#1 fC(^AhIiIeE|T5wS55'.5'.5'.5'.5'.5'.5'.5'.6(/6(/6(/6(/6(/6(/:-3jI/vR7wR5zT6uM.mD$000.+)tL,~W8zT7\@WC3T8#gD(sN2{U6Y8~T2[9|R0[9cAmLnLiIsL-;( mJ/iE(qJ-yQ1kKxVmKcA[:[:tL,oI,uR6tS9qL0{U7sJ+uJ(a?gE_>\]=dB{R12/-\9^"fA#sK*__>zQ1"'c@%cA'$#!dddE<5a="lll 444<<lB ]8W1xK(sI)6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/6(/ EEE41.xI$wI&pD#oF' ^9nF&qF#S.U/xFR+V/V/Q*T-xJ%rF$6!`;lD%wL+|M)R,O(e>\5R+W1xK&qF$g?!gB&kC$xM*~O*P)O'Q*P,{N+tI'C:+dgPV3`>$&#!nnn^YThE(666***>>>BBBGGGKKKOOOSSSWWWOOO666^A*iJ24$ nJ.aBkHmHqJmGfC8)08)08)08)06(/7(/8)08)07)07)08)08*19*1=/5yT6[:T2d@nHjD742e>hDb?{Q1>+wR4a@d@^7mE|Sg>b9yPWyPkEeAB- }X:|T4Z7boMgBmFvPnJ }U5dBjF_;rLtM963kD`:iEcBmH,Y:~T2mIvOb:}TsJwN}TZ[e>kGF1  ~X:dCgCpJW]sJuL~UxOzTc>fDZ:kIqL~WnEtKtLmH^;|r]޽Ȭ tP4666@@@ZZZ@@@%%% RRReI1gH16& zW;\=gDuP|UqJmIlJbB]>qL0sP4wU:wU:;):(sQ6rP5xU;{Y>rP54$dG1aAiG`=iDxS{T{U963wP{Ud@`>cD1"aAoLmHqJV\~UZY~UV|UvRG1 "gGhFzV}WXW\ZyPW|UvQmJmKjGqK~W~UViBkGlJ__SӶȭ sN3666 BBB{{{P2Y9!. kI-jD%vL,Y6[7\8yN,oF':'#tO3uP3rK.vP2yS5yS5jD&jD&tN0nG)sL.Y9 2 X6[8fAiC]8d>pI631b;cyQ0O6!*U3Z6kFoHmDvMpGvMf=Y0b9]6jDvN/Z9Z;#T7 V;%T:%Q8#S:%P6"T;&L3D*J1H/O5!M4F-I/K1R7"[?(hG-\:^;lGf?Y1c:eDDD???zzz]>$pN57' M4!O4 [>'N7%K6&,3~Z=]>`@{R1}S2V5~T3V5fEmLjIxM+)`>lGsMwPrL`9f?741lEuNkEnKdCtL- H0cB(pO4}[?cEV6}Q0fCnIrKWyPg>kBh?h?h?vM~W[jFW3fD`@cD`B|W9vP2rL.oJ,qK-lG){U7]?fH~X:oJ,[=~X:dEgGeDdA]9`:jCc:vMWcW_6vNqJvQtPlIeC{Q0~U4X8V4_=oK{WwRW0nFuLyQySnJaA]XJ޽ oJ,  [9]95# +,, - NYy lI-~W7W4fBb>iEgClHpLlHa=~O+f?!0a;f?f>g@qJgA631O(e>be>f>kBxOqHsJwN{RxOzRoHR,R-vH$[8W5X6X8\a@`?jI`@pF%oE$nD$|R1Y7\:c?`;e?mFmFuLyP\W\3V-X0_8iBgAe?[6b>`=c?gCmHuPvP`9S+W/\3jAe>b?|S2^TEҫҫypJ,oI+zT5zT5sN2eD)_@(V9!Q4Q4L1U:%M2P6 N4M3O4W;&Y<'Y='X;$X;#Z<$[;#\: fB%sM0oH*dB&W:#W:#V9"U:$Q8#S9%L3L2N4 K2H.I/M4 H.I/J1M4 K36$* ,,76:954,,##kH,}S1gAnFnFhAce`=`=[7Y5[7U2|P,T0]9U1U1Z5]8_:e?iCqJd=f=kBg>_6`7kB_6`8b:jBhAf?kDqKcg@d=]5_6c;\3h?c;c$mK2sR7uS8xU:rO3sO3rM0sK,cCcC`?a?W6}T4sK,}W9]?^A[>}X:}W9sN0pK-bD~X;}X:Y;tN0oL/a?%('76DDCC98--"" qM1V4^7rIYZV{R~UlCoFV}T|SwS7 kG+`9g@tM852tNqKmFb;a;Z7c@I4#4$]<]c=tMuOpJhCd?d?Z5kFsNsNuPkFiD`;Z5tOqLmHnIe@c<\6pJxQ{TyRuNvNiAf>_zQyPzQnElCc:uM~VYW{R}TkBmD\yPxOvMkEY5xO.PH7~g\=Y:`?iFb@\}X;wQ4Y<\=^?}W8]>`@_?_?]:a?lHgDlJnK];dCbAdC^>a@eDbA]<|R2bBbB`?dCjJ[;xT6+*:9EE??54)) [?a?lEpGsJzQ}TV[WmDYvMyPxOlK (aB*wR5631qKvO{UrKmFiCiEa@ gFmJuQvOrJ}T~UZ{RuLyPWzQxOX~VzR{TsLjEtOnIsNpLe@oIqKrMiDpJrMlHjFc>iDsMnItOvPd?oIlGnJjFnIuOtMpId<{SzQxO~U^wNvMtKuLyP|SWWzQf=bxOxOyQXgCZ:`YEǤҫҫ \kGpLpLeBhH328743,+"!yV:fDZYvMW˗n_{RWwNlCnEwNxO|S{S|Y>8( GGG631kEuNtMtM|U[jFjF@/"U<(pM]9xRxPpH|S{RxOVavM{RÏfa}UyQd>}XkG_nFnF¶ȻĶ·µǺɽǻĹ÷ l xT8V4[4b9O&\3mDrI`7P'Z1Z1_6eQ*y]GfPjTx^HoU@tZE# iBvOoHevMkB  l vR5Y6rJtLeByRwPsKjAuLWg>oFmDjAwNh?sKqItMzS{TqJ`;iDnIbjAuLwNz xS7fD`:hA;'|O+R,b:Z1_6W.jAh?vM~UzQh?sKV.b:V.Q*X2S.X3rKwPxQmDuMW0V0}O+zM* hD'vI%b=`9lDxO|SrIoFf=Z1a8Y2O)V2tJ);$  G1lG*rN0iD'Q5 nI+qL/{U8wQ4 S6oH)pF$Q-a;c;lCzQsJ{Rf=h?_6h?X/^5d;Z1]4^5f=jAyPvNtM_9d?zN*V4oF&e?!jE)```tK*~R0}N)Q)R)Q(N%P'X/\3]4a8^5_6U,^5b>"yN,Y4\:D(rE"S,Z2\3_6]4g>N%Y0\3_6Y1O'~L$L$M%O(T.S-}L&U.X1_6X/|I"yI"vG#sF"@'.~R/|O+U0T,U-_6c:W.U,R*N&yH"tF"qG&iC%*& U6X8W6$ ]e=e?!000zP0W5gBf>a8uLYyPyPvM~UyPoFmDuLf=W;$xN.d@  ^AlHvOzRWsJg>mDoFb9sJxOi@]4\VyQxQpIf?kE`8oFvMd<\5uPqMdC gFhE_:g@pHe}TwNȾɿ |u|,J4!zT53!  `BgCyR|S[kB|SyPrIa8wNuLWWW[oHhFmFqJoGb9nErIsKsMpKsP8'L6$nKc?a;uMrJe<?35$I2  +_?wSnGXxOqHnEsJyP|SX}T^|SuL^:tMiCsK|SyPyP}UYrMiEzS4 lIgClGlE{SxOxO~U|VxSx\x\x\x\x\x\v[tYtYtYtYv[v[tYtYsXtYtYtYtYuZv[v[w[v[v[v[w[x\x\x\x\x\]=lInIlEqIsJqHyP\yPV|S}T[\sJZyPqHoFrIrI|SXzQxRjG_> uK+wK*S/Q+S,xH#{M(W3R-T0|M(~N(S-\4^5_6ûȾ-' ,-,*( +7"H.N4 %#"$""$ e>pAT-X0a8d;b9V-Z1`7Z1T+V-`8a>#`9`9^7W.\3Y0W.P(Q+W3T1 H,yL)W3\6]6\3`7V.T-tF"oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@a:rH&}O+W1a9]4[2\3^5^5R)]4c:^5W.N%Y0[2`7c:`7Y0Z1Y0\4~M'yL)yO.PPPzR3xN-T2Z7hD^;U3mM4H2"aAY6\8Z5xRwOkCƺvW=tP4|U7qK-cD^?Z<~W8kD%~V8[=cE- --,10/+*)"!   jC%fBsLzQzQlCg>c:zQxOmDzQ[xQ~WoHg?e"X5_:_8iAkBxPnIcAoS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@eB'b;\:]8cg>qHjAuLpGnEgAiF`?OOOrM0sN0X9R:&  }S2U2\7e>ºpHtMcJ0 $""654CBBA@@554)(' [<"U2e?h@yPyPqHjA`7lCh?d=b;Z3b;jBqHyPuLoFb9jBd=^9Y5M4 1 T1Y5b=f?tKuMgBW5rXtYv[v[uZrXrXrXrXrXrXrXrXtYtYtYtYtYtYtYtYtYv[v[x\x\x\x\x\x\x\x\x\hF+oI*xM,Z6mFmDuLtKxOi@lCmDe+^:nGrJ|SW{RzQY{RmEe>g@hBlDtK~U{RwNyP~VvOhB[7U4 U4]:b=pJzRpHjEcAoS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@rP5~W9Z8kFhAtKyP{R}T~U|SWxOmDmDxOxOnEtKlCrIwNzQX{RrLpMbA+++ ___8' """### cCfDpKǹø|S~U~U{RVVZnEX^<1"10/=<;;:90/.$"" gFjDnEtKvMX|StKYyRrKwPwPpHrItKxO~U|SqH}VwQmInK4#M7%kHiEkFpIxQqLdCtYtYtYtYtYtYtYtYtYtYtYuZv[v[v[v[v[v[v[v[tYtYtYsXtYtYuZv[v[x\x\x\w[tQ6zT6eCmIYyPoF{R}T~U|S~UWYoF]sJyP}TZvMoFvMzQ~UsMgDjIWWW@@@)))[@+ ###### [?)a@jGȻ÷ȿ_őhVŽe͙puL]lCpHrMeC 0/.0/.('&V:$dC^}T^őh]]˗n\xQgAiCuNYZ^`^_d^qJhCnJZ; iFlI\|VZzUrPoS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@mK0^?cBiEuMőhyP^őhVÏf͙pvM\mDtK}T~UqH{RȔk}T^ÏfXYafEvvv^^^  ###############$eFmLȺø[^_`\pGdvMVlD_;bA0# $""  N5 Y;oL{U^dZZŽeÏfwNuN}VnG{U][_[YƒiŽe}TpI}W[6iE$cG1wSzU|V|WqMvUuZv[tYtYsYrXrXrXrXsYv[v[v[uZtYtYuZtYrXsYsXrXrXrXsYuZuZtYtYtYtYtYuZuS8vP2X7oKZ^^[^^`]pGbwNXqHkB{R]Zad^YZxU^=@@@!!!###"""###############bB(W6ɾȻølCi@f=ejAkBa8V.[4Y3iCjDnFc:qHb9oFjAh?U,W/Y3S.T1\<$#dAW3d>d>]:~S2oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@a>#mG)X7]8b;kBf=kBh?eƻɹƺ`|Sf=g>}TYc]oFmDkBoHmHhGP9'$eE+wQ2V4iCqI|S~U]Wb9b9zQ}TxQwPe>oH|TpGZbd;\3{R|SWwOlFc?]9 ~Y;b>vQ}W[7rG%sXtYtYtYtYrXrXrXrXrXrXrXrXrXrXrXrXrXrXrXsYtYtYv[v[v[v[v[v[v[v[v[v[hE*vP1X7iEvN~UV`|Sf=g>|SYa\oFmDkBtKxOWX_|Sa9]7gDgFppp2"########################iG-øο˽b9\3a8Z1e"pJ,U4c>b:sJc:c:[2a8Y0d;jAkB`7[2c:jAqHnErI^5i@V-]5T.}P-V5000]?'### !!!################## -μ˿õ¸f=vMwNc:lCg>\3b9i@sJVtKtKnGlFY7K3!V8!nG)V3f?~UkBzQkBuLpGuLnEkBh?]5`9\5e>tMf>tKqHpGlCnEh?^5g>`8c<\6lF^: 9&kEgAc>`lC^zQxOyPWpId=f@jDd>blFV2W5oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@oS@vS7vO0Y7fAjBpGwN}Tb9euMrK]6d=`uLqHyP\c[xO|S|S[uLyPkBsLa:hBiCc>pMjJcEPPPuR5iG,( """""" 2~@@% PJFȹʺĵöʼϾŻɼxZyP~UWtKwN_~U}TwNpG~UpGc:qH|TsNjGfE`=d?YVyPzQi@zQnEkBsJVX\YtKjBhBpIlDpGpGyP~U]_}TqHW^wO}UiAuMuMh@kDtMsMvR`AgFeAmG}UsJd;pGZzQ~UVsJvM_~U|SwNnEzRjC\6fAoLkHeD]>uP3qqqa]YX2Y5N."""2/x[BWZZVV00776ξźĺùøùø¶øȼʾɼgR)P'{GL#L#N%vBxDK"xD}I N%K"P'L#|H~L%xH"uEwG!P)s@uAN%xDyEM$M$M$L#N%L#O&L#}I!\:{J#yH"|I!~J!N%zF~J!~J!S*L#W.xDs?|HxDP'{HzGL#N%s?vEm>oC b9X3J*?%=#?%:">$:! <"D(Q0S-Z1j>n?uD}J"{GP'M$P'P'{GL#M$N%uAxDL#xD{G|J"vFvG" %)]gK%)2+P/yR5vP3wQ3, !!!\X4>;$31a=LH%>NN>>66DDffĺɻz~UpGjA}TuLwNnEb9g>xOkByPzQtKpG|SnEoFwOqHwNrIf=d;tKuLzQyPpGqHyPpGtKtKqH}X:rKpIrJoFrIuLxOqHwNvM}TnElCqHi@uLtKtKqHqHpHyRe?iE`?[=fD)X<&Z>(^C- N4U:$Q6  [?)eF-pL1yQ2U3]9mG`9sJ{RvMsJ~UpGjA}TuLwNoFa8g>xOkBuMoJc@8=-pxxd]]]uN/xQ3vO0nK1  pkf7+)TJ<MMJJ55--;;W,,40.ƻȻ{tKsJtKuL{RyPrIkBU,jAkBg>kBqHtKtKmDqHuLsJXtKkBV-g>nEnElCoFtKqHmDsJqHpGxQxS6iCjDf>oFmDwNrIuLi@vMxOuLoFef>c:kBrIuLtKrIuLuLzQzQsJjAU,jAjAc<`;^;G@-yaxx[aFvO0{S5Z;`@[Z1c:nEi@zQ|SwNmDepGyPvMuL^6lClCh?h?pGY1\4a9oFi@oFvM~U{Re>d=Z5V3pJ+C-hH.oN5nN4_@'5!_>%a?%eC(_@'G/ F/N5 nI+wN._dDiJeEaB\?5% 6$U:%flg775!hZ=ic0 q22DD8s_M}gy~yzi`X}TZ}TpGrI~UpGVV{RW`Y_Z~U]WqHqHwNg>XzQX][[\YY_XmFg@tMySyS|U]|T}VXZXrJe>nGdhByRuL~U[zQ~UcZ`X}T[~UpGrI}TnEV|StMoLlKFA.ixxckN~W8}V7X8V5}U5pO5 1 KZ#  iys> ?+tQ2hArIWvM{RrIf=uLwN}T|SyP{RxOyPoFvMXxO|S}Th?vMwNVVxO|S|S{RsJyPW{R{R}TkBtKsJ{R}TuL}TuLuLnEwNV}T{RyPmDrIh?uMzRpHzSpJgAmHpJkFkClEe>kEvOmGwQuOf@kElFvOtMsLqJnGoGkBpGzQtKwPtNjH $ Z9]=F1 ;)hF`>G2! >+jFnHsLlCuLXxO{RVi@vMwN~UVwN|S{RzQsJyPWyPsLkG~T4LH2pxxv\ _AfGjJ 8 E D0 fb=OJ(><"!  FB(<:852/+(E0zU7rL{TxQ]zSdYZW^~V\YsKpHyQWxPczRcW\Z^Y\XuMoFyP|TzQcxOőh|S_Zd]_`|StKrIzQ~UbxOőhvM^^aXZ]lFoJyToI|VqKY|VY{VtO|WgBvQ^9pJzT{UxQ{T\]^^đhwNZsLsNH2 D1"pNpMxVhFlKgG]=\W3X6[:oNmKtRpMiFmI~ZsOjG\:fDeBc@`=]9vQtQqMnJdBqOsPwTpLiCpIwPWsJd;W^`X|S[VȔkYrItK~UZxOa8zR{UwTfEFA.yaxx`eIyT7sN0@,0 %<|niAU- Wz:Op"3X?\(K_=P`+/~77s11WId!!&" \>$kE&vM+}S1_=[:\;];bAcA_=T1|Q.yN+\:X5|P-T1W4`=a>]:dBc@eB^;S0|P-{O,\9|P-{N+V2[6d?kEe>qIkCxOkB^5^5^5g>pGW.b9d;lCqHjArIf>vNkDngblfalf`jd_ga]f`[20. {S3fB`=gC`;lGc?^:T1 @*W5{O,vJ'V3[7iDe@nHe?pIrJiA_7c:]5b;]6V0X4X4eB_=dA`qHsJnExOtKxOnE`7]4]4mDa8Z1a8f=nEqHc\3_6|S~Uőh]jAi@e<^5]ZuLnGc^9\7`;uPrMe@mG]7Z2a;uM\XlEb;Z3b;g@~WyQlEvOa:Z3`9tM[]zRkC]4i@kBZ~UqHwNh?^5c:|SZdZoFf=g>d;ZxReA\<80!ixxZ`F35!.+Lw'TgTiY"WYb9a8a8kBi@e=xL)R6  GGG2/,zL)V3X5Y6zO+hD(2\:4"!0 uN1gE* I4xL)V3Y5_;W2\6[4T,`7jAeb:U,lC^5a8_6]4lCa9^6\4g>nEf=lCd;Y0pGa8f=^5f=c:e<^5]4ejAoFpGwNrIfD+ I4"Z>)30-`=b?iFa?lJxU: Hk?  V=)kGsMwQoHnGjBjAc:nEi@nEoFpGuMyQ{S|S}T}TvMkBjAh?lCg>pGnEqHyP{R{R|S|SxOxOnEf=kBh?b9mDvMqHwNzQzQX}TvMvMpGc:jA *S8$iEvMvOsOkH8=-ixx|^ 2! 4D fzU'TymBwd-E{nRN0NJpRR%BkkJcx=;  M3???vO/~R.^:qN31!(gF,ZvMuLxOwN{Rc:gF, Y9 jBnGiC31/V3^;X6/"# 4M(Bc5   1J&  Z6Z5oIiC_8^5xP{SmFnGvOmFf>g?h@e=X1sLmFg?c;jB|TsLoHnGsLjBnGiAc;Y2pItMqJa9a9YuNpIjBuNoHlEjBc<`8nFyPkCf=kB_uLrIiElL2&2"qK-V1^; }CI5 ]@)653~zw "@En3}2iFSyAA|f.tLs)wYNk') ]A*???~V6nLiDb;i@W2\B-0#wP}T{RyPyPdB(e?YnF_8a:30.fB`=V4.!;W3Hk@ *- 2K+%'?'jdDg=S8#iFiE^:iEb=jEuPZtOnJd@iEeAgBaZ6mItP{WoJpKiDgBiDiE_:lHqLd@]9Y4lHsOzVrNqLlHhCmImH\7jEzTkEclE}TX~UlH6%bAZ|SkG,iBVjAnFpIuN41/mJeBdB~T3]A+Fk9;X2 cWOuF  fD[8b>fBgB[`>dBiGeCjHbA][9fEfDfDiGfD^=kJ]lIjHbBZ=uQ4)'$~* 9V-Ci 2\v3gW(JB}l:b-BxtQv1]~/u{XDK 2!???oJ,zP0c@e>c;c:c:jAb<)`:\3:&sH'\3i@f>lDg@g@30-~P-zN,uM-?)4M'&9Pw=  %7#MrD *wM+~R/Z7^:|O,`=^:d>^7[3jAd;a8^5d;X/]4f>c;vQ47%\7W3}R0xP1rL.*'&nnn YURkJ0`C, "D}yRŐd4:tLaњxj.N .'!ZA.R6 ???zU8^>jGjD{R`7i@esJi@S*e];_?xR4aAgDc>mFoFmDmD`7b9Z1i@i@h?uL{RpJ(d>_<V5.+)cccE<5b?#X9!Ug.%> B-PaQ:pKhyESg<%NpföXrM'%%' ]<"OOO!!!???YYYiD'vL*Y4]6c:b9_6^5`7R)L#, [2\3b9`8[4]7^80-,{N*T2|Q0J/    Ch>'"6 E,zO-|Q.}R.|R1vO/iF)rK,V4V1\4^5T+N%W.{G[2]4`7\3d;a8d;I0U7}P-wN-,*(bbbI@9nL1sO4$ R t_FĴ?/ gH/fE+lK0iH.hG-8''...???fff///zT5a?];b>mFnGpItMxQqJlE EEE2/-hF^=^= /sM1 7&'?\9I1%*bA\;a?|S4vP3fE+uP3[;b?tNvOtMqJgAtMsLuNjDrKgAgAlEX9/ dBbB-+)dddJB;]B|Y=_CY@,@. V?-*079S E@ Sf$#0 iL6tUxV;|[?~\BvU:[A+??????kM6aCjIiGyUuP\wRlGvQnIe@Z5?-|X={VsNyUkGwT^B0.+fDmMaB `@gHaCqS;_B\A- zU9oI,**eEpOjIhJ]ArR8\@fGrQoKiDjEhC\7zUzUxSuPyTrM~YwR[ ^>cD,)'fffPF>}Y<~Y=~Y<}X:|X;vS7M5# (? #6$$R;)kL2vU:wT8uQ6zW:wS7uR6xT9L5#/"  1$         2#cB(aAtRkIgDpKiD{WxStPsOrMd@S<)<'pKtP{WkHjGmJ4441.,yO.^>bE/$^>`A]>]@[=lN91#{U8pI,^?rJ+\?fFfF`@~Y;vT:vR4gG];tPfGkGrMsOpK[7qLrMZrMjFqLhD)gI3dE.+)kkk.,,VQLUOKVQNVQMTOKPLHOJG @=:EB>1.+-*(IDBLGDNIFPLHRMIROJSOKTOKSNIOKHMIEKFCIFBIEAIEAIEAIEAIDBIDBIEAHDAHEAJFBIEAIDAHDAHCAHDAIDBIEBJFBIEBJFBJFBJFCIEBJFBJFCJFBJFBIFBLGDNJFRMITOKga\f`\c^Ye_[d^Yiiiiiigggfffffffff_ZV^XT_YU]XTZUQVRM PKGUPMXTO_YUd_[\VRooochunky_png-chunky_png-1.2.8/spec/resources/pixelstream_best_compression.png000066400000000000000000000513501212556323000275020ustar00rootroot00000000000000PNG  IHDR%6RIDATx\Wu.|znozs7lـ8$H!@KxxB ;Ƙf\7VWWOog{_.%۸ ?b;s}ZZ{u$@"R$Ё:@@:@@H@ H  H  $t $Ё$Ё:@@H@@H@ H  $t  $t $Ё:Ё:@@H@ H@ H  $t  $t $Ё:Ё:@@H@ H@ H  $t $t $Ё:@@Ar2yO+`ϑ֟)`8TA+0yE"I7 ֧Jm۶MB޴=u6rj]3╍Bk8澞Z%,+^٩0Fh4)mI\<ϗ7`ͮv*gmx珒x݉ZD{ds @jyՙL +Ӕr0G?@N&ׯ_}:-@5@N!BU?̡-J'g*KBq <3 -^p.4p߁ԞX4!\˭ȑ :th hs"Ѹ(KӲEY;8{a]K'e6t]@NQ,+BqǤxwT+;5-䤦i6nJEY_lL0xD"ГD,)r<GkՊmٚfheh4nih 4fj%H=ONޟ#гY}Dyn bk _BPڠ{[hU~۷o섵Za/3cBk7{:'O[=T5jKjzG=e и|(ްU#)QMDCR2\.';+3Ur޵ds|tv hQ(L3S=Yj "&2Ӵe#?SyP8"2Jx ho@k!{d2-bllȑ# ™tz^}t6ӑE`h"np QmY<7м~yCxHyW>ū@4-JӲsEv]]xD9*-9{(lUQYm;&[__X!E krOg;46:-ofNf&HIVe<˅|4mV\ñU oh:W:k':Sq? [.V%8cǡa cXK+W*LOO\/|ҥܜB~QaA݇BР Dv.rRXq< <1yhD%ޏ"F.IUj֢^oꆢ>ԀUVuuDRb.LģTCy/Fp6W_T?N%e]#c) $NaJIxQ)W_ypU?孟뮺yJg׬Yze2E 艉 0i(cЉ(DGǫ^*0 .@UUY9|>48h2O%$ڟ_]10b^tovȋUQƦ]kv/N4j,twuJԒ3,! r[Nj45ٞ zz1h4 i/l+9I%yT*i t8aa$dIuxʳᰩ'ȑX|whMhx<2ގ187.W^~9;?Wr1>W*b)2|ΖI|vsa5Qv;_b!MÎm~aq`и/u(˶e1͖?tˑ` OȨ<_tUJgnlyѨ@c8ax`]F(5,LlK[[>Mc ٴ{HI>nVH!XD۶D'gPh4qr`" $NbPLWL Wb Aj{DZ'H}Zj `jpl}4Sq-#M\DS.Sw}gwj4m t2bT^y e~SC7]d<ڞI&@Ié`hBu(Z4Ecm3bnlf9 t؞h, ՑB=A 5 }f[k}  j*?[9+ .:=Q^nL0TP4\s>zh+VԢ*q$%44-ڀ7L߇Ba(P8ڀwl4uJ Bᰇ]eH`fe'(bVbJP: &HeEjjZc7ftwuu0NL{EX>uL&Pxzy,3+shP? %xM.|P,tvmǰEqωBD^]i. >ɴk5I2XHXW$j  Na2mKʥ2LJ #1Q4JgzZ6[“!'VEh*N`AbVt% ek/ϯݡ h\㒬LNg1Ɨ^GwSSS=]W58ӸLN\.440WІzӞUMoR'ʊyv2$6mςI$HB^$i-k|Gt u0`1oz\Z F07`WGI  yhhC 4i%9䫍g]N>|*P% 6ަi* ,W.Y<91!V*cX5y^(fz 8.蝜|/ѳ@$Tg&s:UQM{&&v+eE2綝ǡzheagXMI.@Y=8bo0d2u998Q'@~ӟ[-@!2$⛄  ~pe+{R,s*B{x \NۿBМ7j ?yl)4²?˶p@o: ˁ[>Y4pD)BՓZs4a 4@s6 7؊tuu+ڎ+T7o޶xY?Y)$.g-fds,s!t Lvr80Ԭ9LeoځG5ԋ %QػiV  *m4S%at<=w27eP{Oe|}?_,w-Zf͚?bSpLsX-Pl?ܿ?1$WkzxH6!~'g& `wuO}S۷oVȌ aFr4DH5 ˗4$m(YQ W'Lbft: .NW{;$k˒g5 +sFmmxP2ܬ*GccD$@= Q`Dm>IfdD֚qa}U[7]AQ; 9]?xp``%dCс+U,bTjEccGWkb[lC͖Fc8Ə!M}luηuwo?[-2ݲЕ|Mkԫd#;1X ԙň@U*}X56!;_?4KY!]^G2*6F oZG|Yo'\q0@쒊LdiZ Zscz=k7qzu5A)0oScsm +FڣGdLrV3ꗯUY3d<k@[^` $&FٸO 4*`Z$ڰ'f`6O*+z1'"%ez( 2L=_&ً=U@a:A.AX8PΎAԫUIxTx6?m;8oh$ $HMܸmkj55IISɎ~F6 &p0-|'o:I9Jb2jqҞD2*< y˧a 4n+]ӀhEJ%c%xz¡CY#DwͤHG4I^hL^w0*Uy8: bTg{֑Λѧ0&[WTIDHuM4="b4y1_F Rk=::7u9ySg\4P(Jy F*P7\Wyp-= \ G+M5GR4nͧs%gY5#jPo&{"a7z%1ohrf$U*Qx N9+$~0ۅ @0j%% j&>4e|͹ A,ہGj (ӋN¦[yfsw'Cz2-m 91j(+ `l/34=t+Vؼyw;]'M2-r6`Xv|4u :dKt"D݇j"2 t } 4ԕIɡ`lγ#(FXf}1vV+p$L$aH,A`qʪ‘I,}ugҘX$q|4iYt8zgjj {d7> %+ġ@ ҂_Ecw"=7^F ˧2 -l&f?>A|$ާK}\ãzasj6*$i&Yq\.W(5 ZͿ[VZj__ox: Kda-|n&2/n>[NX,y=^@N9rCAvѲ\ 0l… 00dqRZ$]G~q*;qyW*e!5Ж}.@8N.U~NoƵI.޷o~``h40WZr衩rnRiUUjڲys;E[(TEQ=h?=R{gO؎`VM 1΀ E>D+]wu۶mЇ>N_@㮖L@z4M;HP]Gg9Iv9Y!Yr"eTK5M^jߏjgXxL3檳?Vl&s’doX =@v"3\3$*Q]RIֈe#G`RKAZǖi,xbbW8`ʕh@ amxQdZAk+hMM\Y,3#Uo@*_Bq'.I-a-ܸ_ܩj͆Y7|k׮}OKŐSҿ;*1Mr\V& cs#v$CK ==RI$XQpuVcE ͋zeI3 1C^#e!eq*(-fA9RXxg&*eHU&ՊiൻS =9Ih:Z,[Z)?A19iهLyk$HE_QH}X)sZec+T2K%=FwOJWjƩ=ORy˴[֛?S=P<[k03_ o@$khHw,O3 cx$d)vpB!]hh Y"D|%{Y`HX$Lұ!‰*N#E>pŽT$+`1Eq0s[;@'ʕ &-|}Ȳ=9s3riCRIVE5nxmYMZ T4ZnǡW@CbFÐyW$g3`:>>qDj{ldIqqB&9}ߟJxtM3/I$2Fc{Ԁ#;$Z4.^ғò"5>pv5^ףJI&Qmej*Y#! xܧB#c[Y>gMbh'5zW[.mY!M-NƠa|p#Ul%IL[R2'sOfQI,K^dn4 J8D>al˭YR$;Hqk->=7l(LxVH(X^%2ʚ2Yj6 frl(h"Ѱ\U j۝ѕ]v]w$ufdԗdc'9F3qtƞ$3>.Ю#Z<뺺^?/o$uc3^ ;ԧURw-x>'ph5zlI$@6BG<.϶'O`@'c[F˕Bh>ٺn $ .ͧ t2q9024&CLV 2+b(!؇mDYzgf[JAUbl\zK(fxզ=֬YJ? Yy1r"ց!x$ zg ҹ(nyFq-;≧¾M,/σV&hM!Z(tƷme;bƑ\jC,gzJ$[eum^V춢{ZJX$=Q̱Y{7L4+x~y47hP~FJb7#aQ>[6tu݌~ס.}%A %s8{`I(Ĥg3x| +Xʆ f#"ŒY>;a0PDZeqna0&+c[ݲdZ8fv-f8]&\\!x!q̮jjtN_fev~WG{g̵$J t,74ہ*IyJSQdILz"U* ^ZCkF~] kmX93A W4CZ=繗{ոLtC ޽V#Oyl˗vᆿcY՞IQyj4Op#ӢDGG$hV %  A8p+G9pIl92δ2;buV7l 5QovHa2IN5Ue! SUhQu)eH;A-[[_>Y=ɯ7[<$ (s="C$ª"򤂄,QZpe֤Q9zqa#twP\se3Z1?#07̂t I:{[d=9:do,qË/޹kO j7<|]_̻jGG׬<\AUI"ؤ7LE&Ar5i}A%G\B[`s`78LbX2; 8 ] LݢZ'+r.0XLObuZNsM|#ugfrpq`^4vDzZkFHqN{֭[w4ȯO)T/|Lhޓ =_5*o,J5^`lŷ#Hc$h鲢͆RDkg~sbp4YIEZ-QWkn+F3)GxETB~}w>aSřkGʯwNδ/ԧˆ iRwXgXY3`PrE`kjDuׇ6MqH^CdAWUhD}WK\o!,=|e֪uMƌne7Aɘ(sXa1DIНs*޳L% ,`%bu[y$zEk@M")cIj%{{]9W( =pT:X1hxd#hV'ڬH  rd_&۟b [gs9ӑ98rEymSd7 $ghiWxdQxIWGR5YU!]~*:ĶIJFн$0HFgI2WTIͮ3&s,飸;]IM?D㼨w" sOwMOe7\?6^89 :IGWv]{Y8iCJ4%8^G[oxy5cؾD#9lFu0Lkzt=J`[&3 sn#Z<!g=əwNK,}ʫ{n ElBs9=ȡ*Քm EN|\*NR'ib&QL$azd+nT1hy-^6_Vm+5Ӵ k P)PWj糰#PؠJ(r#Rpj?rLA|nTm45M+X*tWHrРf2Sꘁh hD"vFKXB.tʴ]i8Lnf8ibʉ"a8j`ahJrW|i4He6fGW4T`o1gQW~q " ^>Gm7&-qEH =wap$c[mxͅk\Yv%D>"uE6/›Γ%n>=Fj}&JXXB4uFr ,` doԓ'zN7Zr݄֭Yw?jhA~;/XqCtW4P&@ՅkMMNH YhZI灁TKE%AhksY,76CHӨYX4V)ؒ58JFR'_: jt n';JCCC,B ld*5el9L&؉>g|utx@N­~;ֆuQ(7ѽ Lqu;ѱnrm_ߺ.fTZ x8LHJYzINO31}q_'#D`{M ዺS熐yI‡,QE yE@2%B~EU WXqheb`\,Y)I jiP?~pLJ>!8´¥nѭBaTNtX&R䵆LלN Q70D˦K,gǎ¾q##1EIσR 1Urt&3saVZpUd:$V#+eSYN3ۼR9`m 5r9Y M狠+14I[.N~-׿yk?e+.?-}W5nxs w =<3.y|b#̴1+>C;GF#'kӲ' o=N]Ae1Bq9S\ʉqcaT|2>>NO4 4(+ʨ6-jONLtW%tn>0'w*;MC+ -NMQDT.ÎDsF=R٩ <}Wהj9tӨT%éf,<|J"DZv<.`V0w2 Yq| cY_ziOkÓ &\"m>c|1%R|FR`>*YG OdҎE'н{K]{|[=/u߮Iu^^ظq?ym7׭D,zz˹tyWa MMMm;R_^>NAnsg uUӥ{~ŗ̋G3kw ~}Q2e7:dDRMhZ0q& U5zڴLb;AA%,7:oeΎ>L g@%5M1:[9B6KҠa@m}=!] gѕKv%Y mrGv~[?yo8q3+p&Mz4|<w4yKսVz7վnk"BHP`sELsڂ-wr _Ogg7u_Sެwyor!\(4x݃ zgyX֮7{@ HI] MO,DS]K“/_8ҳx׿?ryx.ˑpe]B!TUPgBYe'{̀p8 *-i"0dӱh4%M 0]EOP:n IAgrJ1u[;(ӱ׬]/$Z׸>?@Galϵ:zמA=8w(_?Y8/eYRxORcCr gc\z 3vd1r b 0yNQT}z& P(wuvr z>wo_t.`X};>]황)AY.aˤ2UX81&_Ny߶%Փ9s|?[׷|{%f|h;HM wS%ӗ4fD 8}<d(k$ >NEZJECPWKeV% D=.MArQX R7u]h4p|4W˕k R2}7i ~M,׹fɼGw }7\, mXRoھ`e9k}~XL(3陚wX-7*xPؘjS nĜMmٽ}]0F %hݪSQSg>ZaE$8G}m{;Ǯ\6֗4-V om|0מ]nX/_l]?=N#MLN2)IN\Ӵ=$/.(EV΋'bIEDGUk2!_m<8lq2** gSȶd&t10E ʅLj#ΫϺБ_mE+KX&FdBlovͲ)vfuv{ KWbā&c<#-*'d9[npS?zf#nJ_0oti˞޶+7?3ww.^"JK.9E~lا>S|{=g8xEK _MɈy˞4-7rrRgYhIwѼwˈu}_}7-3roҕK,JAZoQχ)SEQj-44۩6k}?V5Їر p {;/\Ԙ C\֫/[?ꃿ+8zȲN~vǎ[n]hzˮw'0 /l7^yiTkլkD*\`5s#{~z~.v̭z7 "yal*M[{@36p X0p'y^n(Ţszw@ku̴XX#ʈC0˘ޙL摙O\C;/Y5μBK?p o|MkX?;͒NopEIR_xvާ7E},bp SJpVLիW3'D4;"ZH(gēiQ4:k[<^װl0c립z U}_z`ךLb^rq:.6JglץJ+DYd5JmD?oIo4=}l놦")w>q M>hPT2z9<=Y'&3wrg}kPFi?kºHB x|}V8H۷G[8پ}]{9" @GQI7Lo]yΙW-";\D"WkUW]bMz^,~X^w~:w&k/>3z$^RI+!h4LꞨHvNK RH%U0?qC\nLeşSv^@35v) $u,m\[la7ԣN/$6?0l׬[*pI'm}+Vҫt~OJ;S?l= آ:eˇBh E]WnS0٥B3sx衭tptُl~n'\͛75 ggTTǸ n:$c'O\nhҰ$Aڿw@KdU 3ٙJ>|GꆑB=jfPJJ[<2/9Vd9l#Oڤ[t؇C%<>MdIE-ޖ[r,Kv.(nC:l Ko<9D˱'#7LtѭtYƍiw] fgB;{ Ko(i|Qu.ҩNzIRvLW0=OX?ny?~q˶?M'jiɧo{octwvHiWQRZ߈b>U* Jo{|!e}44,>ٱklZܳ;䎟կwx =9pp_;g 2yإ| _V'&1o>OmNy췭Vi紿F 'ߖ8Rٳgυ^غ'?jQm!. tR$.ʺΤ8+Zưʢ{}NF>|P(,Z-S;&E5!NV8@[m ;`3*'.˲ǭpʩ;<$ǝأ9h)#\z\֥ ;hᏽaz >5-i V:[f1HVSnn-R[8;ZKHl=m4"NkϹp"'~ξjjoQǢs NkbFg:F^}tjkhZ;o0;|ZXY׳ 27 rO5=^,6~Z-@~Nžge|̵c/tϽAa=fu[[{=N^h ؙ闊,Jxf:@@H@ H@ H  $t  $t $Ё:Ё:@@H@ H@ H  $t $t $Ё:@@:@@H@ H  H  $t $Ё$Ё:@@:@'j)Q'IENDB`chunky_png-chunky_png-1.2.8/spec/resources/pixelstream_fast_rgba.png000066400000000000000000000637731212556323000260700ustar00rootroot00000000000000PNG  IHDRgIDATxtն86** MB4ы*DƸwn['q8K}e `'&=w޹S9};)k)pGq>?| b /O))8<>|c@.\>sS?| b /O))8<>|c@.\>sS?| b /O))8<>|c@.\>sS?| b /O))8<>|c@.\>sS?| b /O))8<>|c@.\>sS?| b /O))8<>|c@.\>sS?| b /O))8<>|c@.\>sS?| b /O7ߦ10}|e?۟'4uc84c1XO|1Ph׿򗿴'r~f_}k{i}_~{7#f6bf~y?я߷zx㍛˗/믿ګjk/ym9yU;M}{c^s7޸`73G}d?O?sǠ`V .b矷Ç޽{mΝyf۴ii6l׻n:[vYV^mV+Wڊ+rix6ne n޳qoڵsݬy?}}wGsonwuw-o{zcgLqWջ6[lmf{챃:_qc$M;a;[58`?¦03?3[ȟwv*!!&z|#*t#FTZ .=^PRUP*ʗsBvm+}WSl!*Z5vXծU 0 ׾ *h5kTZ= ,hw^z5;V} i5CjXXjVzUGjUz*VJHξ:nx-[Z=_=oMw>u4}+LK6?ޏR1^3З+~9}mjPbkWx.FȖ6]-`6sb[٘Vm|Z6}}%Pa%F"p [=MlS2:ַm~F6[DIژp]k[B5 z}EƠʸC]Vb2dHC6Q"/7Q/Ɔ޽{[rr·xw}.]BC5놅@~sb)ַqup"ymxZs[3VKˏ] g7k kշ뵄7M}A9KЦu@ )S0 رÉs;Nm_2{8znshѪMIٚ[fh[GKfin[ǵۻg"'ֺx_ٖ/fT6fvcفe\;K$O y'@=ύqjF|1ΦhBֶ$'rMV6)&=ߢ-ln$DRQ^p#ݟ/xg7ܮf ~b8'\> *hXbE[%&&ZRRjRSSjժu$9ZֱAtՒW]_X޹QǷ, Yiul$'|m` Ĉ|8^T-lv(7a?v=zԎ9bs]?–md7CZXZZJT5hpON1jHK4:նkc[GؚAMlex[ާ-eKzK:h_bӷ;AQk-Vk0k=VVKp kN׏zƁMuƶZ϶mLmjF2y&j$rW.aCڐaV#p`:q j<13"3cZ|Db 4ƍ[-99ڴiciii֥KٳuѽSNIpYeā7jÚU4mIO v`m]n֛/-::={Μ9cϟwDճ65lF6QI(y jpoTƧԴI)m@{}H E]ز][ع-amCmrJ kX-ٛne[pmPIbNp[إX˶ kjhNk/\hѢk P2;ֶu0שDI߶-B]j&Zۺ-r+0S{~H x:L %" mfǧڎѭr~ة<;Am2dER=CAll't#ڦNv&vd[7'op[;WGuY]\_Lmg'fuv6vxz'EF;V7JClD;0 ڶ}bm߸n,[~kFuڕl6|'<>ieȰ<c\@0"qSN֭[7KOOw pflсt-ڰH 00-geD 3. ^տʺFbmvhjk?=/5cށ % qusLh'ةȔvlF>YŶ1-%Jo긊% ZzhK8Nk⸸=v, i30}3.1l˘6gR{?lL w6gr?>}Ƶ?n% <Ǐn`y/]6S@ǻ5k Xl1V{`[L'B,HF?؅q̰6Q6"5,չ^D!3mbG:9ݯpacڊ-ldyF+ZB 6sC)VZNo#- m%6HDjfw㳻<ˍ|dF'WK[9EU Xjp}wL (Ngu`?=4\lQg82ŎaigRC_N }ĉlֶwR;SxZHhm j8?a% {. .]ݷZ!xJZ `O&Ѹx 0|pԳgv"s"(n&H^*$)7j*,&FKG.lExV aez6Uniblrz6S͔6UMצE:cؔ*]-ЖeҲcoMl31Qapm6s1ul#kW(b8cuOHNlۇ7H'NжIVz7 މխ[\jB,]԰uKicBU6֯`cZ!Qw;W$ղ1%Jqc[ۖIom`ۆ5#l_[mk%qVKmLI/]bFs4U|l F.^:7vn5R k@ʶqlu T*lY:"x6baEƺ ,ͳfrmhO<񄋱$GÂddtkqa~7GwkU Xaj*!Ǩ#wWćynh-mOkmF:^2pL`pTzketm;Ʒl+!FV!-jĎ ln$[>$ٲz4:߮mvvJ"+\n=cZ8=)S'+l!p#7aM4|厕͂~Ijx_:"ʡɎ&pm.w#Ν[JWMp\UC70f%-O$kr-G lz $MزS qv̮$'I!zJ^+^(C{wYJ7m?>*[F,׍_s]V7o^?EH2H^^͔z_RvO¶DQA 汱ԙ=lf[22I?C=mWMf:.$RRAUguD37HhCr^/dixкi3'v2Lse)FXzC2xz]>J{ť:U4\3Lr }bJMHVDhr[gAImӰ$-^5ƄH="leH[? މ[D:wkAk k&]؊uv C3 s4ld/\>왚#Q#Bi5NhSG(ĺam@ZC"o]kUK]IbXK!^H8қYjխwG۾ Ulx 9%5! ۠gBuz4kwBX>]iZ:t5J[mB'}!=m~^N+|H 0cdD+?f =iG6Ms1_ٽmPZ2'05q0ofx&q-W>JGTZSZV9޶i(vq <,QzK @[Hmlh۾2"mH:6E նqoBuwNIdwBV*![mfSbo;ĸEߧsYo6Q$%Y > s>g[ lD_/[eНw:L%K`[`sFԽc5P'}X}fF8Ri]q p*Źsŋ"c)9εNZqr@zH3k ՠFY[OW[Ar| e%Mn++/`M+!M젬㇦u=o6 MrIa'4ivPz spԎ2WSi&iU߆|4M}bGAd9`^6å$qo;7?=`mINyHK4dtgRl9:p>sp-vkè[5L.%p͕wVMZPlpuxc.HD"2Ed [$nx,+%:p-pb`{εRsDO-\C㸍ۺ fy3uj;*>n'C*1GJ,附n f 6 (.%Hgel;+U$ժPLWmV5i  b8>\@Xe{򬜛;k*Hۃ: 20xb+ s{9` BN 0:q}qτ6ڻr&}@ڿ_n{P`ƯIn]vKѳ͙$˘yhcO/nL2^y9[98 uV/Z$b`z=΍lNGQhݓ;9pLgt׏L[&tigvUW MvNJ]j[e@( m5w#6ɪ{8ÿ_xV;8G7)C*Y?ʭZغR-V̹hgH|#[5} gHp3n- P ;`a1H_8K^ `8  tn؄OY@$\Ƶ\\xь.voF'M0AnRaSk.Ogg?1RXɆƽʹM.[4Q74(~&]3:d=ǽ=z4wz̤ѐ/;f2z}T]dWfwn`?+\NZ96sR';$ -ogcs[`l~"3]COc>,!YO-Ԡ0ێ-koK){^Nc;Ǧ8b<[<..dg{5mʹw/GIYT1|%P6W#gQ3^\6..(}5IdҒ 6,YexqP3OOL4#S5uԨZUˬFbV|q U TZT|Mhݓ"vRVU kV(!;Ix9[pw\I^;.<ױQfN-hCGWOrwiXw,% ?=dt Ihv^>uU~&QIeʨW-QL킎a2)F2UEm ٽl8XD1NnY`YvlfP,/^XTPގ* W)uPݥ6VjiZ 8tM=߂5ݳcE'Sֳ0`ef}ˁl_鶿+[זc||}KvV{l\Gp[Qyey[XGN8v <',$pw<,Ψs AD pXR-P5k$SRYhI<{" fw&x+,(d=IbpKǙh<_UJ +_TFR*RDV-Yqu*KJъƗg[ oxBdf?1Ȗ7 ;Z%{AlDI EIk#kme$WRΛ7h#"afzD032KX5A΋xx'^r \sP_i @# `/|zq-n eIݯhw7mi;K`-C[:E m'8QQZsŇ+{s dBWH H[qc\֐8ljWzr.e꾳:Ԗ[$*̦ =1FWDB`BC7gG[VGev5FI+Y(Uwkc[׶ɲ=:džw {}-6^nEg+N#%.c~0DpkXV+gB' P/4e,1(xu71nc3X3"@L%IN5LN>z)'Ni,]oݩJR4 KJO u" B":0/j*DmZjulOds)z ks2E X_J_=1+``:>D?D>/,R /m ځ@Jwޯk@-҃ɣ%iϤ.'@_yd"рqo:#bM9Rj.w|wYߤ0nDr|rwq\2ƨr!"J"C]5=1hupi $85{FNU9( fhw/Cɓ70*-Mb]C/_Xba' ݋2GExN䐈̀Ơ|YvoMD]o_}sO/׉lNj߰Y#Kg;dï[T|\n;9M͝pP/ :td`2Ж s2z>cA=cI_7**70/"7-%2t(P2&!.;+7\p"e0@o] e\#}gמ`/>rΞpҞ{~{i{){؛>,`?a??}+_ H:SKUKVkTZ b~&ɓU\,@BJJOW9- 2$`7r/^ tLI5vS5vǹC7K gA#Ĝ.dn5혚nۯ4/x)r9nZvݏ~,Q!>c^Ў.vL883q<49z/9 R+plip8Cz080#PK1Q`n*R2j"w& >rj㔶蹿߼d?z37S;?|YWctwGx6G{1]Ҿ: [\.:M݅UEj*Z*TBnyoK]y+9\ ܄}Oc>>K#iQ)|stXt>DI/TZe!%!uKGh?a CO\."}τ~ J`Sk:RAƆ3j cyFc 15w]f*2X)BAͲ-8GYHy%*䢎Rv\/*F̂psNXXXKF, Tpȟy_9^BX'!$瀋+`U[Hcǯ=#Q7nW€x@|xQW@~ ?a#n*@.:VkH&}B9&(فFAshkܿD<+Y+wA,ȠE#aR9kXRJ"@`ޫɐ5b -V c1_+ȺCIvdzHswLmRbڬ$u c^+@ gf2d!J{.KhMrVכBq70$(W{vR_>EOt,5zd@g+ƹOv hq5鳜u?s~{q{ɋ+/ew$b'tmplN*uFJFV B@dnD$Ҽ=\!^1;4f.Np OWc!9,`"GrIiuv83 cH$tIΥ6bR[/0ȁIB:r\*V:8F|#S(5U;ԕ&A;ɳd,ЊH&} r,<@*KS\K_wQ~ >g sxp+@}ʧVaoXP=Y MIggիជwmw^|~!XO5B0|9zAWQ5jBZƪf1U8j+dEػvJ["15Rl2 }<804;\W[t yɟ'whN&i~MZ[WNAu)4EQQ13%ڣ~fǵ؞8P5!I1uq 9}kBpxJrֺjR|?BI,y;TaKADs** p xwq aif}ټN=`O]8eox=PWѰh2N)-m GU+c Dm8Y~-}ɫyۥ"1X~"sF >X7m+P85|:qS5n{`Q?ǁ~}]?ʝOu⮔l~Ds!*mKQ|R*zk@Y[3zz[sڝ˱q|9޹l9~RJ$8Ek {O|=1H5DLF^BkC9R or]{52v~( d;w'`fO/=x*)f,Ρ]K*yDžId@\dտƁ׉u"[7H ;bbgH 3n H42k5ve9-L&9 h#+XȉQERX :1gIS\~u2W^_R^ jKQ7%!bW}1RŽOrVI/ΔAp`| Yi wІwÖ'K{;.WA' [8)lʼnON J8`DhJ]=;h]0nHIW:gϖUr=g}}:Iu_Ёl[gg\Io ʈP@Ax(u,GDB#׏lm~w*A0x(H (ʂ&Y\ ࢈$)Up8 F\JFI9ϥ) `Pq6uOq`u;#7<`D(BEl='8,Rg#pf '0‚ ց`Kĉo o;fFyse "> oudžev WޖH,>?zI{7}g>˳OۧyX `23s3pIAp(ҭ)+:HOUբs%\dts_ HD*9<4@8-"I sih8qFdF26.<j =o4HS,"zAg噙<{BBVwJxZVގL$s[l޿ks ̅ pֵK앇O O'?c*'ҋ?|1{~p=ux\^dTƒ EBG1 pDpH' v1U]=)r zÓeDc@c! g+(CȦ9J]IV*wV}u2"j:@5#㼞>|3nycv|gm}?{^8p c0H&>'h`yO/ީGc|f G]5pt›n>|{4["B{4J*CBMҹ%%%ykW $)}=q=ȳvnC}sSڇ Dп8+/f~~c# SZJZ@!t&p_oz€80ӃDa9=XĈ?΄Oib ѶK߭v^K6we+j:LT3l2Isbr\.- hY $ W:ߚX[Җ[r%K: -.%0">Íez ;um\)ڞS{=lJ,^IkN!qE{vwe{>0TϹ&E\MB} F x*n]浯i[V,]5ۦK[7s j6-9Uw\U4+cӚusm~4lvsTLm7s?d8oVJ%iukJRi//Yz,3_NI*cqMۄUlp+P;zU$&Ջ߻9.ZakZ%T-dGd:U9u(jl|Ry֪m[ͽh 'A ̳B9:wzJJ_p5Fשhꗲ' [Q.oYZH2]cu+w. P1djDg^Z^ DU_β'ʅM}j5Oim,-u}WڛOP>6d*tL.UD_}$EkgjQk^znUqᶈaLޅ8*\Oh+u#W& 1\牃|w=J)Z &\>q 'pb9λ>h|'d 5d氢pU.qARExj\z7e[Fw[n?8ֽrkVi 8WzD KlVyT *HuĘwXgzϵ i=!>nƬ8$Vmhe6oPZ~r茡ߤ{k: [aŠo&ژqV'涶|~c(汃UX[$&ZO-: =pt'go] TzWAYr+PVD]-mE}nl7 4 Z$4|g0c Xb1q Xp99|G<ѵa i\ί@uOJ͢=U=9yTtU,u/}7@dՇu NY1i~X5яulIU΢:Guci]MjZJ VfMw=#ke&+yMt+hTܨM 4&,h}&D&E,ln&)uɠ&*;pgk녊]6f6:UR B֬vYHpgo s36{j_`=F5hqRľo tk|(>ھ'[>w^pjMT[:@ap(&F&  Z^@鳞95r pD[ \%}8΁{odJY0Ȃ+c,NpH1PՑ /h@ν'U5Q+pre䵑ZXk4Ok'Ì}$u2j14-7e-w\kK;\9~%bJ *KMK/tAЛ}^ /~qYFC[Xz&p^_V'ޥBZ'vh+U8к7ÒU}Cy},6)v,e?pp;"ⴾcGý-{Q5$RIB& %>ˊ]T12@#,{x{|{tWwqpM@-d`xb)CA0pyUpS- @U}h f eD%``Z=3%VNjms328a_7tohUGKFs/!g`{LYG7SZ9ji$K NoZD6Ѳ[cYM} Rbs*_\ +*JMi˳B&(z$4x14{ wpil;Ki^*NK8g+;3WnZ9K`nXkeY-BԜQ6s/h"E@7=A/ R-s'$\o,fGZ2H׹{/aB) E-t V$4a\Ԫ*b# XҁJ,ض5m\YHR~KQrC+ݪ~ S+`NU~ѽJĦ%畕#N{8PO{pIUZYgy rcF[q9q.EWVN㾁t>rbv=}".{2q?C9yF}8Hjp_<=:S>]%JsYE-bgT`8*s{Y)[ WYݛR|o{>[EU%Uȗz +Yn͈nɕ} EREMҪGi28_[pkB\F2D Mh|H,pa/iǛD>*Z2+U4nx`c^޿F, `{/u-TjฎqVI'-|Unk1ݛ=Ϟ9ޟeDVfZ,Wؾ'`m-(B{-%WSuϸ hš>5wHq{@mru)v2Kcqvkf/F-kN0?~~²ZG:C8QX q?$2=nNn@ Z^iKd>ynR+EY#YY?ZĢ =D')kia 1TSJԓڮdZqOVjXQaqa͖/|EZ~t]9k$Ae%D%BHHWROlK/Gtf5c|F8#w` 7lST`e"sk\ozJYL/ֲvZK:V~Nr?QfPjʂ]MMkؘ䚊]Fi`a\/Q0$>SRtJc49kB[=-W*V%fVWձ;SFk_RjtLHeG5t^g W]XJbBRPeð7F`#X, ]8\}U)=Pޣh;qԼBmV[>_ոۆ1,ƺU6Qx$^bIݣx--Xy+EoRW+2<%Re[E*B-岤id,UݖFerҠFDZRVFi=>W_Ȩ 7 ?PE,FQcߙRZ%սJ<[)rxu6KqAJ*ػ?|jiY1mV68SeZ[}j XɆ7dwrY|w[b[5VBZFV&-XBXkUh=E}ũGqm-`bj-lX{2Iiڄ۴RT BoJ$Sl] ǧ? d0ڮFX#p85*up.]"z5)tRp֪Uj˗;Ѣ\;Uzh+e f;D^+0WT=IalG`kSSCfb+^-aX\)gD]$Z(IB4r*c2 ̞?!5RX PB3&Eh==c 'Z?5[l3aR䞌.nXwY*`ҥKnrxB)k1j}Ν;j\Vڸ&=K#[ղ萢.jAV5Ka,S$iP݇(*URQd& B qK-Ѣg*pAWsZaR#2||ĵ90noecl<7)4H+V-&?` qS֛=Zs4Ss B_SeJLiSztntn$ kVI|*k;~ؔ=6`QG%L(c7׳۲J6h]M&ImJ%Ki.rKwoz,nc=fV> #V!JWENR5WĘ8p`3 Q7W K/%Bp>{pL}%:z{fxl["蝖:& (⢋~M%-HdJ`6"(;B.7KcX" xxF&,3ڎAǤ!{0D8u=̀湍=/ίgGJ%k| Y-.}⟉ Y(?p;x*yUK:"ben* W% #,yv%eEZ vbY%Zlc!MRbAqb*xi0_f|Q[vR&_ZREmDJ]'10^1P.D7 { @e9sq`pLT}S\::bq}*_LabL :ԲCb|Kj)P$E鮉^]cJ_%`k6F:˜uB% 1|':Uc?H W& =VGkB,h;3S8T=A7铬ȸOq|,<fr$$tlhYLrl,2cDWZ@sͳ!@k/-6D) J{M-l^&Z Yj}x~QQM;@ٻ[b}ْ-fDi빲]PZ̭dc'u"9;:O~m^֪]e-vD<9o~F:2>slbQsDCaAD2^ۮ(, 5ؔ0$h55i0:p\[:+yRx9wlaVQ%4)@W~ VVxi+862ԗ- 8^'{obkeZ# ::0}#s3t5.TJy׳ڪ=ƍo>𭌩1x6x.U,]lLH2UkUbse3+m0_dO]`gت^Z!Vzu>s;"BJЯYUSXעT I&@B Oknܞ/daLЍe[WW 7HUD>+Vªԁ`8p]Enk,jmDjIdV4 ?w_/?4ڟ'? u]F Vw4^:#+HO|Л+4''K5eК*CZՑd'7e9ĭXv ٴiӷX69QM,Ie-m\cK_F.lcW%Y!-s0뛕jCJXqp˔qjBXӺLa>駟vMk믻ve n h-[ތ_n@Yf9Ե3xpzg 0fJ c 4XK4Iuaq A-z jSņwW#P7GխL5‡YHD7 {-u{6 #4xA+0x ^kkŅ: R ŚCJeT2 Mb YD/7onxȐ!9/?5)H(`U=G {Z0.ن-f#VXt 5Tk%j52 <ֵkל{y?%NFbE<#i  :WQkA( pNQ{a3Uʆ5 Ik]椊aqlebKشz[E[wBTrV+ 0/SG.*v 47ph3@wR(5 VjNW^&wɍcou$0tQB-XD6sM\FL#[hZ1t]Tn/E 幔`*VRLT#!7ղ nǾ^MَjfVpDqU:Fz,Fp.| -OaZA;Nn)hZ#n141`ak("m (8M#Z= u-_3 1+ל.Q77xTJ֡+Kr[$j@kai:L RJZ8{[S؀mt qughQ`;Á!^f$>Gٔ Jܡ w؝ygyӦH>k=aKR2Z߆kG)a s1WЯ ;we> ׯCXq$DFAJ.0l)*@b.Qw4ns|X񁳍{JZ>CJP޸rU,FVBkmB=n`ˢK;NtmJ y}u`"%5xhI; }Aa?1W#YK9+ YEo ㏝9tМܶEKDқյfX$JT"Z2H7O\]ux yR mj6+eH9},|t4#w q覷 8WqU80 8W5ʘ27˔AMr $voJn:+Gt%2eIA|JX׽r(mVl J(^(-UՓ#R]Joa}TguoE:ז߽mb#۪*:0vOo*Ii&Zl|<bJ͊42oOjJ/=FCZY𰜱 _~e{ |cVh @|pN)MmnLm)6G})!Vk JR`QƝV%1Xgȩ>a{Ν֥e浮"ûո(&nMASjMr|7_MP["BD,.YHp(.B:EHJB1\_ ɭ7w=wf;;9sΙ9wgemϿ̷Do9rh%z%X?{U{6E|>{)pR7v-uDld> ZarX䅑E[lA:뵻so,W.m}T8okk|~+{[×'vS9ސ-Ir7wuwhMԁQ9`IClɣmW;kah/ϻW8p <77G{rqE [2amm-6{06k0juۢK!$?y҅Px?Y}_}W@JL?_ Ã=aS@1 vb!^ +XY.3x:>cm)]ǟߗ2蟽Z[JדNw&md{Fxj"ܳ-od6==;H:I9us<ܾ>N>;uc,ܲrn] L|Q:MOƆX{MxLc:??W"Q %` +lj|8ԏBˉ'ϱng`sDFxq&yJ\ѱ+^:ZNV۰y|r ]g7>{ˁryjV)-ϕ1K$̍=3OѣG4u5[]]CcOGX((3.\sw_luc0#&9QgAfH/# aR'皲=eЁ;12Y`qM 'Fe? hhbi44DFDcy) TŖ*?Ǫ| Ra?'Bl4TVjT䥌JvX.hgq;<߲0͹RgcR q"ĆN a4 pUb[mwՅɓxrb&by~?"66: :C@?jy= < s&f8M%9#y@06p(={{{1ӧO'qK'X\aK _ Viv3<-CCcyruu]yD9wʡ 3d1|S{F%ˍ~809`\*7;8OIw0apK1O|`ۥmyfۨ Za`<W^|_ΪsLS()dJ@40rKRH)F2% g%)$JJ#3 p %L HFn@  8҈@$L#D BIiD Sp["B@N4") 8-H! PRȔi`S()dJ@40rKRH)F2% g%)$JJ#3 p %L HFn@  8҈@$L#D BIiD Sp["B@N4") 8-H! PRȔi`S()dJ@40rKRH)F2% g%)$JJ#3 p %L HFn@  8҈@$L#D BIiD S%ɲIENDB`chunky_png-chunky_png-1.2.8/spec/resources/pixelstream_reference.png000066400000000000000000000567621212556323000260760ustar00rootroot00000000000000PNG  IHDR%6]IDATx}]U6sBzB(Hoʧ(">XP.^R M&̝{fnD|s3޵pe)4,eFRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%e@eZIeVRtY]i%Ųrjrj #L&NRdYTdBSh eD""HSHSȧyNY%GQ`j89>Y@t3ǑtdX G~a=))e=,IUGzGZ :փfN |Si ֭[Tټy)qF eˤ 6E'M*ǗREr[:EKc1B3iIW%)WL5rt_4 |>oYS(kno\n7qַo;Τq8kp=n[O|mDO4=$}r[:EKc?G=g>4JBGbj5zTMW/~]w݁4t =e L_ =Ck I'ͮUX-6l364u|#DWؚZ[Zښ[?kzrYC]mS}-:(L6}$4- uu5 K}MumNl357hZlmnkij!iƅU( d[-b3HQ5W4aiFj<'NiD '0/%mEO}EkW3ZM,3fХEͳ?Zx[>kA$[ӨX7KEyQEsy% {M7Q[ ܝS]ߦba,u C}CC#C#5u盯oX&faM5w9{%w s $a)>9 1έ⯰ϫ^>[s3UKu*+-ٳ$i?FR,^,?0K\,Ԧ DyY女TZdW7 8saﲘ ~1=y}@SfSR-Ndᄉk^=ȖH$_~9g>SRq86҉ۑZ ki[ zdĩuSb( 9bR=YsS ZC~py/2!r9lq;^=F28scžyf) b٨%I/Q%BZ)r=a~ R( 48Ze3J96KŽq 9B*SLrF4"~oqؤE7拞 >7:Ɔ kb'b6eHȟϤ'װQ;P(ţB&K%bB"(B&8A{;s\~h4J/$UҧSd\a5zݻwCtT[˸tX"Xbd ւlhT*8Hr arß#T(V&bи%\67zl|wBH;.2l6KA&̩38f:m2l2p>\&=sFC}mMu^s2VSQaUP2^0kFJ)G"ofج5OZIfH i׹}Z}lwL?J@aFL&J*?JSCT}c׭[šlSPfO[&A˃Kbѝ]J]F;܏+?R'籋2dЁEWH$Rj)oP H@QϪP8b  Uf)Pw1 'UHQ)K@|)4pe? >9(> P(tW_}_Fs܃>aÆMMM% ' LiwМJ8-ϗV߼pK-m-iޱ~.Y˗ؙ_gq͜_4ߠcɁZ"*ЦV5GrVӜ RUaY[ =p1"OlӀ:J$DѠR ).$B! (ajtZ5X`3 d ?D9L/i$ K7&moo?y澾>cիш{[oĚv5n=pdRU(‘Ȅ5pO ށ>|6Pl"V̦VWZ$b\: xٹt^g$#a'>A.'<*ubqPbU"B)ӀD"`b(ёX,2Byb5Kk/;ꊥ-_%,liÅg.Sq,ͷ;|,9;ڕMΪSOHvWc ]G/}3dmk}abNg__)K(ٺ}~ܨ';L"HO D3IY" *aʕ~;ΪUČP D;Px\&6u8{2Jޘ#uD;[?7:NO5H\jk"rY:ܠQLJ]'Mf ZRI\rD[گb ڦJ!7hMKJ_^tw__>+^ZVtZhL`gl4ڌ-3Z4Wokhj ̪ԛM9u%3iSgm.jXxF&{Sb!Bm%bpE:쀐Ab24jJU)< D.k2Vc5jqlTJF͂Ie֬^=|lW]:)5Yh"+ b@ R-KUU2f˖-w5\s#s hѢseDVVV@$B6`wҚ-u8:={YŠ$aYRO#L&I:Ԝ ٠UH\dLa-S?=IxLVs؟\((p(J&>o*42 j2K'8(,u9ZFFj\6]l1:\Ȧ`@]T$ش Q[ H#2Hʦng!L#xUrH ӡhzO&@  ǣl:JDujR*VHzR|6u; 5 C>7G"av.{pg[gGO,-hRJ5߿l;._g7aUϯvK/[qª/Wnʥ W/kzyUKZZ{Ͳk^E}+bbqKꈝ]\ {ʋW[yL lE.Ϋ\Xg~j3~9kj52@,M ;ѪRO)Ĭ\Lă"nԚ+LrxG^-MIz̤RBAe5ZU?K҉X2I%"O.uZ>f:ȓ9NCaƂ?e&ר\ \v^,}`C38XS뎄|դש08<TJ"ˬċdR  ar8MsdRmZդBKRI6YLF P)Eh ҉Eh;X FV/S%R@XQQgl28L"F3GBNc1 EmM5χDb`_:0ラOƎP.GJeBJZQe3 EV)96F+WCjpǒxz\ kzSI| ȥTj5q٨B('\# HtT,:=/F68h˓B>f (-:v)hW4iuAW,nitьӾXvmhBBQ2 9R؁ DegP|*px&*0|(! l'#0E$q4'4`ԈZ;j3|,#xb EP+"2n1IuZ-w2Hhك(P2(]:E'TjE]uhP :)Sw: 2 ;5V#aBqQWJA_ R,x56 j.FK R?JE`Z(ā)˜ŒZZ<@ m&.+}L +%B(XAl4d>ʧӅ lhv2eYPJx? da#ǓJ(0DV蚈<0YsHb:yP)pФPǫf@u}pxp\y]4 F#@ןLT<|8ǓiJX^PXY:pg:OY\F PPi\>B0~tp8hHx;Z-jPYx84E8(kh=2Plr9Mlhԥg6+^h%"|ZtJM_7Rll1 оv[" *kvLm| eJ!qe͌8d_՜%jsJdz5xvl>OM2!nFbQyPN-.D"0urh,_|_y啗^zvWo߸? LHe ~MxCtVYšnYL_T#^mib^,Ik;~||+wbA}3_>uxe ;z3=n}lu/ѣ=N! GGGFz;5e\#CǂEd,tYKg\W3{etyk wa.j/.:f f'wq9+- mUg{h5}Ql;zKˮT\=2Ibl$B~c\> P#Ç;:&&q/jQ,E*1,"0 '6vCA@t ܿo鰻ޢh3ϗ&.Fm۶["䵏zlI$j/0:^*Uګf̬WiBk}w8%KBqŲSBH5E<YpIԳ8,erHpH"Qk4*UjـA-^C.p Pn¥.>c5U?hh+W<%;FAP\>J[&ʲ̸QTW[e*I l*ud4 Mhw6BNZljYEZȻ$l#2iHBcsw=omI^y啳&d-=oP"3,lS_xdCSԳ8Mzy^ۋ&wBc (dк̡89bt:b5J< E܈8ɑPOg ^挙JUiȼC=M;>qk2Vh;ލnx 8lҮG& Y@:+**QCC_d _©)|VI " dd&߿݃#Oo:99=  & %p8bnߧo,MnB.)nLYYsQ)H2p}WVe2Տvosq:@M}P8+|egR 7P`ri" KpJij^Oƕy|v;ލnx h bĐf(U:v#/V'L]%ma4;4p(oFSf4L.v/Xh֠}=RÓpIxrdł| 6D |Th,ƀ0|f V)g"n[%#T,$5 I/FY,$B\DŽ}4FD: @3PsQhX^TEqd  )kz#S(i˚?f֠&4-8thжjZ+tm,f BG:P(/xhAh:#_P\F3Ԁ29rgyw߽nݺ'銇Nfr;xa˻ E'E4/ l.̬)CWHO<C x2ZE*%& b"EǴ{F8J:$#Q#?V^յiӦ3?FN&@Q7;{]`ZTqeK@.rkSlɹg$@OV"njrQ NNd<k$"@n];Z+3G Q,,+vݽ.h70PKt$XR`W,QP* dW;B5SmfcT0 *㉤PDRsbLtpc5-U..Yud4kd hs f46KyPFy"( 2%Pavm޼yw~7> >J@[Y[;p᳣"!AQ6Hgs$0 d욌:9$M"կxJ:/ dCfgɸ!$RL~Bh@xjlQ?x6Gt0??h$Ըt(XͤsdXxd;.Og0Her̳3^(O8v+]S(]\RxKP xRRgRb}^"Iv>LIEk>gXJ:Y(eS<ނ\Q2E"G)&ߏ_:C(<,Ed*uT%;n;)%9-FR9 R(&sE΁Qt֊Y3#A5܂{cfd`ÇQHĢRW̮kk1I'd'LMYyv {bbb||l ~{{0F^t4q}H3|дfd:uR:()C%tq,ԋqM鲩 ,GF%644a^GGsAff'2~rFGFήN/OJз~?[+9-ˤ<.E5Ly;ݗgȚ2+_,Ld/NޱYv'l% [edΜ9Zf¤9ͩ fԇY4W.k6k4RN%S%zn f~ ; xWeeEFl>EVjnnFRǍMJlYwxFFkMf3x|,FV+ڻ6# ҩd*_=o0;}PϬuߧ]W}I,"P((]$)z&]Ipp=M['sfͧ˩tזC.2xA<TIov9P@J((.'Ke2&l$ڣĚR&t, ه n]b];4rLQ!2Ȍ{ fg"Ջ v;s8477 (Ř 2zzN!Fi5r$3 -V@7S>(9ϩ5$N))_NDhriR%}G\jh5">A0p"O^FNO,ii(BqPcIp0#Gtb@ =.2b А\z=ȃz&'C?b! |dl4ЏYzfC IF0kޥ@ebIi~4gWmvIR#]cHQP QLti ff}] )yT>AoQڧq]ޞQ.O&qRYH$Y`LQt0Hmp;(4;H`A Qyp9B+zB 8U=kT*n)$A!rU u9tbIu{pꇟ|n0r"\!WU(R-G-Lwt(pDA6(,d=yAMZtzY\|9@SXOj%SjR8S!c!u?T)躉SkǙ4~O98EO|ɨdyCŧk`p wIer%bXkT*BQ(:*2TR "ƉTBWљ0Zv53#χc:k֬:Fpn3Z͝[__~<3fЉ8HWNSS.N-&ps3eoT췴444OCzzq7{9% <.Me8dHQ!z=J"ܡyE} fjS$ܲBѯ̟Ýdegt>-䰁Y)0aB Iy1AC'3E63rtM~: +PBNc1 L_9S0Jtf[Y*.+Ψ2~҅bI%IGܬ"83-‘="}r̠W=BLd.P ȤȌB&hd *GC/]7X_Jϑ@l6xMO#w]T(RL"$bQ,IhX><]3GGY O-=t6{2+#4v O)0 .헥!Ju6[NoeM 6`E^.SHerTH$ʟ\/`j^F ήFC˜4d;>fd6QUACk76p˅WϲYΫϱy }p>> 7wj*kM=A\67vgl0hT磌+Y %#(ɄB0p>BbZdrt!:>tUqaC0΁h6_!Z0\rpמ3/xh5O`2',$#HuwGMo@ϞXGbPd`?vҎNl;|;^~φ?׿奐{F92fr YLЧ J~T2-t6.Cc$Ǭ ZE2 ѱH4L]nwY( _0@xD'SiktoI4R ?;"C"|N.t'6w?}t}1 Y?:&۴2luaEkLW)$(&>`%@fH9vbسn[ ҇ku :0v<9-ȝX$FC=&O'P гw9mǪgۜC;Ě#sD2OdyT lp`16a r2S^epGɄX$ 1ȹ 5#'LK%"J8dUa362*H0J|4 (w3L.7NMqBѾ#1Mh##;rjұg^^xrjy(̽W/hiv@ě(;Ƃ,tg]wOUVp 'X}G]r .x饗DR^wiP[d@hT*Ph|?Y,i#ovt,)dV=WTUjM3jc<ظ6']n%o~k]?znn\p+(8B3PVIRXͦJj^PșI?-f'Xl6[Ml4,F&龠S***l .[E\74"_"WqFJF}G>Cf&yB?s=Udx{j n4\(+[ C0g$Xgמ- TiH\7Z P۷o߆ Nk)y晷~-+|gݬ}n6ZԠ4?aT0]kKOmU_~U>mrd6O- :Q˪\?w#{diYYJ\]P`dM tv8PdsBHR(pTdr0HC(D*]`5:=_($J2œ@pY<: F\)We h<]np(;'̴B00gos:o^}xp|l4u?~4kt&4u-}헫\Pi! E^PgZV., ^NByUzT dU ҁ@;X7ꫯ1V[\*щ~^uj9]n,tj )m9Ug\wb2J@ŝ"*r?莘VԮ^1br8eՎh/`n IB0,Kd (Os,'N"pBdx*M $ l"7+D6%EN2[$|cI7JƐH|\\xγH+,l2ǧ_`a1Pzx΅s+7w>Eﯟ} }>-5I sitGgXQhoasYl|îׅ0'` \&5bנ*d곖-Yjh.틐^j:Murz~ixPx j wNK˚n>_SL{#Gis*,F̳ t4U"|Cd3[?xvYiGgiEK> ]:18dQ3zS@$t1)@"pbY s||'=0ٖ899-7gʈppOe\A!zMmQ:OW_ZSq9,w+f@OO4|E^F<3Aā+_^׷G~kZh^`\HQ'>t|l rxM_ԠsP*z=Caeo}޷͆ N'oL\=|?u߾}~m~?v{Ol]yt+ouzo;لS_{u??mĽ˻=lx2.8-xO CC4F )z=d͜~~o$fؓ0;ju![ns;::ΜY}6qLܱkߋES?z-lc*vW{O෬[3˲C{άғ <3k N&AR &#<"_T n\3w\ge$BbsD=& C[B){h483 M+,j+**$gMo4L156QFbE|BÆhUK)}ߖ.WAཀྵlBJ! D" dBZku<6K.qE!%tЧ*YH+yTL8fO۱խ[ګ5bi,k^?ww!3qE}\&arMÇB'AvwwISk.7Y`>,F>lLޤavbq}z4`0HNB'WObWjN+kfig#PWb6hQ~LgW\q o$6 t}Jw*[ H,^dQt4]E d'X\lEp 0V*DR*2l1 3"pBĸj5HIILƜƜmLsbLOEFEP`@J fwߗy BèyΜ9o޼wc.|xd©n7:mi,|_7I5L jZ}SkIHB~A]^祌i|U,$5/k -C(tv8!  oֶmۂ8@O!;$%3.LcB6PQaa3=L)nV5p(mVG_aKR8ߟ*U :ycW?5R+ J 4ؾ}{}}}CC$(%Kر6ZuֵkRVVfbH|%ỸN2vJ{{Kk+*4A:z ˫=xxL__hk]0C ~||}Q~}Cz&;$h ^4[Hw ,޾^c_nkCPB^}C5/ژ. $xM!+O E+CGcj&#+~7z#BܖR]L{eqP&L==QU M >| ߏ$ %R|hWC(Tar#zTI,6Pfe@ff.q^g2MG_qK7[40(p݃17:걄w)*uͯT|o4=h&NDÖɤ ♑w1Qݽnjڱtssseee>}zJ2cJ,W2nI04Lɛ,WW-ܲi|l \ M[vs2œ.gp[j/%4z| /F\dLٽC6Tp #d"09N#%]ᎿC{4wJ@׭}џ=k]䪇VXxjs&+F}}`"%]>c"z 䒠`Yz" yt]Pj{BD-ni]M2yO,k `d5Mdnz D60 TjkxFI0>n371${"?TШiy+[sFliL-RoŔ!1*zytt)pB-D.-v|.4` 2)q%=,)qןO bfrGYs6%͎3/;̚`v9w=~agɳm=Ρ=g~C'Νh]USzT_PRRVKKK[ݣL?g M)oCi-HWI8iCn[ْ|콼Ϫ%u/ R%$LEwk7ȉk{ןwp `3)CMKjz4n=e|Pjjv~qɹ+ݻK; ڏčkٛz]גvKJ}Kh#h i&QViSNUWWWTTAɐk`=ز`58eܽ pImp\5'O EEEP^p}\gϦ2lyKߤ" 455edd؋SW3{XyBjac8 +!pEEW𗛋gsἐ݀(j54 Չr.Wۗ$F~@5n. kM89p Ųkr|G2>vI8g;!ՄL;Zv YQ*rwӺp`99Dv1/dJB'!Lxg 3*JG 06 oRH/Uܨi lIۉy18 {>6nv5\<1qp3gqe{x2@ؾ%k&FlM.ys 7+?&1<>z4ܴK|k:?Vn PAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84SPAS84S :IENDB`chunky_png-chunky_png-1.2.8/spec/resources/polygon_filled_horizontal.png000066400000000000000000000002721212556323000267630ustar00rootroot00000000000000PNG  IHDRڄ PLTE=-tRNSDP!]IDATxE ! Փ_@A@@At@Y@wR֐*+Mi&7j[K?&;1O஻ ;2WA HpiIENDB`chunky_png-chunky_png-1.2.8/spec/resources/polygon_filled_vertical.png000066400000000000000000000002751212556323000264060ustar00rootroot00000000000000PNG  IHDRڄ PLTE=-tRNSDP!`IDATxE 0 ?/Sd FHÔ4R ./Kgl[;'.?V+]i殍8=0L5PJ扴 W),#^(֙#IENDB`chunky_png-chunky_png-1.2.8/spec/resources/polygon_triangle_filled.png000066400000000000000000000004051212556323000263750ustar00rootroot00000000000000PNG  IHDR6q-PLTE&-5=ZtRNS9UqnYD^xIDATxUϱ l4V`6l`c n"ZMx@!?ylj[o$t0}#aT5]UɀAwIfN2q C%{gVr7:`l)-ؿ8IENDB`chunky_png-chunky_png-1.2.8/spec/resources/polygon_unfilled.png000066400000000000000000000004511212556323000250540ustar00rootroot00000000000000PNG  IHDRnIDATxK@EO&l3g%*vCv@{ ^SA%ztl~9 Ti3`’7/+KO(@!SV9,S v0혩 #?6?:wfͰJ?Q9Vo ^[g)*c6{ɭ+w2MF,݊:]f $FjXlg4In8Bָ#POЍ^Ϲ5A-XIENDB`chunky_png-chunky_png-1.2.8/spec/resources/rect.png000066400000000000000000000002461212556323000224420ustar00rootroot00000000000000PNG  IHDRRPLTEMO_H__گj@^ FIDATxc߫`0PgϩY @"߻b0d- a*IENDB`chunky_png-chunky_png-1.2.8/spec/resources/replaced.png000066400000000000000000000015471212556323000232710ustar00rootroot00000000000000PNG  IHDR(-SPLTE 0@P`p 0@P`p  0 @ P ` p 000 000@0P0`0p00000000@@@ @0@@@P@`@p@@@@@@@@PPP P0P`PpPPPPPPPP``` `0```p````````ppp p0p`pppppppppp 0@P`p 0@P`p 0@P`p 0@P`p 0@P`p 0@P`pЀАРа 0@P`p 0@P`p1IDATxcbP0p˩ꚵjۯѨ ]3j( X3IENDB`chunky_png-chunky_png-1.2.8/spec/resources/text_chunk.png000066400000000000000000000002221212556323000236530ustar00rootroot00000000000000PNG  IHDR ';6tEXtAuthorWillem van Bergenv tEXtTitleMy amazing icon!Y!=IDATxcπ "q )IENDB`chunky_png-chunky_png-1.2.8/spec/resources/ztxt_chunk.png000066400000000000000000000013611212556323000237050ustar00rootroot00000000000000PNG  IHDR )gAMA1_tEXtTitlePngSuiteOUL1tEXtAuthorWillem A.J. van Schaik (willem@schaik.com)GAzTXtCopyrightxs/,L(QIU(KSNHQKO,/JU04յ4aRZzTXtDescriptionx-0 D~MLX`\C"8J UWY{wwRg+]|?BO&2 .IlL} "$ܮg<-.=KMk.QI#|Ě=&'*EQ5 o>ᤊ{,SPm{͹<}mUxoGmҥVbz@zTXtSoftwarexs.JM,IMQSHTK).I,sJ3 rK ҕBQzTXtDisclaimerxs+JM-O,JU`ԮIDATx] 0 P*@# #T10lPF`ؠF=IQ*u`%qk H񚈩mߟ э=,fOK t(F ;P{xp]9/p*$(*yՃ@C  cqNU#)11.rf0gh(tEkIENDB`chunky_png-chunky_png-1.2.8/spec/spec_helper.rb000066400000000000000000000020551212556323000216030ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'chunky_png' module PNGSuite def png_suite_file(kind, file) File.join(png_suite_dir(kind), file) end def png_suite_dir(kind) File.expand_path("./png_suite/#{kind}", File.dirname(__FILE__)) end def png_suite_files(kind, pattern = '*.png') Dir[File.join(png_suite_dir(kind), pattern)] end end module ResourceFileHelper def resource_file(name) File.expand_path("./resources/#{name}", File.dirname(__FILE__)) end def resource_data(name) data = nil File.open(resource_file(name), 'rb') { |f| data = f.read } data end def reference_canvas(name) ChunkyPNG::Canvas.from_file(resource_file("#{name}.png")) end def reference_image(name) ChunkyPNG::Image.from_file(resource_file("#{name}.png")) end def display(png) filename = resource_file('_tmp.png') png.save(filename) `open #{filename}` end end RSpec.configure do |config| config.extend PNGSuite config.include PNGSuite config.include ResourceFileHelper end chunky_png-chunky_png-1.2.8/tasks/000077500000000000000000000000001212556323000171565ustar00rootroot00000000000000chunky_png-chunky_png-1.2.8/tasks/benchmarks.rake000066400000000000000000000012551212556323000221420ustar00rootroot00000000000000all_benchamrk_tasks = [] namespace(:benchmark) do Dir[File.join(File.dirname(__FILE__), '..', 'benchmarks', '*_benchmark.rb')]. each do |benchmark_file| task_name = File.basename(benchmark_file, '_benchmark.rb').to_sym desc "Run the #{task_name} benchmark." task(task_name, :n) do |task, args| ENV['N'] = args[:n] if args[:n] load(File.expand_path(benchmark_file)) end all_benchamrk_tasks << "benchmark:#{task_name}" end end unless all_benchamrk_tasks.empty? desc 'Run the whole benchmark suite' task(:benchmark, :n) do |task, args| all_benchamrk_tasks.each do |t| task(t).invoke(args[:n]) puts end end end chunky_png-chunky_png-1.2.8/tasks/github-gem.rake000066400000000000000000000320151212556323000220530ustar00rootroot00000000000000require 'rubygems' require 'rake' require 'rake/tasklib' require 'date' require 'set' module GithubGem # Detects the gemspc file of this project using heuristics. def self.detect_gemspec_file FileList['*.gemspec'].first end # Detects the main include file of this project using heuristics def self.detect_main_include if File.exist?(File.expand_path("../lib/#{File.basename(detect_gemspec_file, '.gemspec').gsub(/-/, '/')}.rb", detect_gemspec_file)) "lib/#{File.basename(detect_gemspec_file, '.gemspec').gsub(/-/, '/')}.rb" elsif FileList['lib/*.rb'].length == 1 FileList['lib/*.rb'].first else nil end end class RakeTasks include Rake::DSL if Rake.const_defined?('DSL') attr_reader :gemspec, :modified_files attr_accessor :gemspec_file, :task_namespace, :main_include, :root_dir, :spec_pattern, :test_pattern, :remote, :remote_branch, :local_branch # Initializes the settings, yields itself for configuration # and defines the rake tasks based on the gemspec file. def initialize(task_namespace = :gem) @gemspec_file = GithubGem.detect_gemspec_file @task_namespace = task_namespace @main_include = GithubGem.detect_main_include @modified_files = Set.new @root_dir = Dir.pwd @test_pattern = 'test/**/*_test.rb' @spec_pattern = 'spec/**/*_spec.rb' @local_branch = 'master' @remote = 'origin' @remote_branch = 'master' yield(self) if block_given? load_gemspec! define_tasks! end protected def git @git ||= ENV['GIT'] || 'git' end # Define Unit test tasks def define_test_tasks! require 'rake/testtask' namespace(:test) do Rake::TestTask.new(:basic) do |t| t.pattern = test_pattern t.verbose = true t.libs << 'test' end end desc "Run all unit tests for #{gemspec.name}" task(:test => ['test:basic']) end # Defines RSpec tasks def define_rspec_tasks! require 'rspec/core/rake_task' namespace(:spec) do desc "Verify all RSpec examples for #{gemspec.name}" RSpec::Core::RakeTask.new(:basic) do |t| t.pattern = spec_pattern end desc "Verify all RSpec examples for #{gemspec.name} and output specdoc" RSpec::Core::RakeTask.new(:specdoc) do |t| t.pattern = spec_pattern t.rspec_opts = ['--format', 'documentation', '--color'] end desc "Run RCov on specs for #{gemspec.name}" RSpec::Core::RakeTask.new(:rcov) do |t| t.pattern = spec_pattern t.rcov = true t.rcov_opts = ['--exclude', '"spec/*,gems/*"', '--rails'] end end desc "Verify all RSpec examples for #{gemspec.name} and output specdoc" task(:spec => ['spec:specdoc']) end # Defines the rake tasks def define_tasks! define_test_tasks! if has_tests? define_rspec_tasks! if has_specs? namespace(@task_namespace) do desc "Updates the filelist in the gemspec file" task(:manifest) { manifest_task } desc "Builds the .gem package" task(:build => :manifest) { build_task } desc "Sets the version of the gem in the gemspec" task(:set_version => [:check_version, :check_current_branch]) { version_task } task(:check_version => :fetch_origin) { check_version_task } task(:fetch_origin) { fetch_origin_task } task(:check_current_branch) { check_current_branch_task } task(:check_clean_status) { check_clean_status_task } task(:check_not_diverged => :fetch_origin) { check_not_diverged_task } checks = [:check_current_branch, :check_clean_status, :check_not_diverged, :check_version] checks.unshift('spec:basic') if has_specs? checks.unshift('test:basic') if has_tests? # checks.push << [:check_rubyforge] if gemspec.rubyforge_project desc "Perform all checks that would occur before a release" task(:release_checks => checks) release_tasks = [:release_checks, :set_version, :build, :github_release, :gemcutter_release] # release_tasks << [:rubyforge_release] if gemspec.rubyforge_project desc "Release a new version of the gem using the VERSION environment variable" task(:release => release_tasks) { release_task } namespace(:release) do desc "Release the next version of the gem, by incrementing the last version segment by 1" task(:next => [:next_version] + release_tasks) { release_task } desc "Release the next version of the gem, using a patch increment (0.0.1)" task(:patch => [:next_patch_version] + release_tasks) { release_task } desc "Release the next version of the gem, using a minor increment (0.1.0)" task(:minor => [:next_minor_version] + release_tasks) { release_task } desc "Release the next version of the gem, using a major increment (1.0.0)" task(:major => [:next_major_version] + release_tasks) { release_task } end # task(:check_rubyforge) { check_rubyforge_task } # task(:rubyforge_release) { rubyforge_release_task } task(:gemcutter_release) { gemcutter_release_task } task(:github_release => [:commit_modified_files, :tag_version]) { github_release_task } task(:tag_version) { tag_version_task } task(:commit_modified_files) { commit_modified_files_task } task(:next_version) { next_version_task } task(:next_patch_version) { next_version_task(:patch) } task(:next_minor_version) { next_version_task(:minor) } task(:next_major_version) { next_version_task(:major) } desc "Updates the gem release tasks with the latest version on Github" task(:update_tasks) { update_tasks_task } end end # Updates the files list and test_files list in the gemspec file using the list of files # in the repository and the spec/test file pattern. def manifest_task # Load all the gem's files using "git ls-files" repository_files = `#{git} ls-files`.split("\n") test_files = Dir[test_pattern] + Dir[spec_pattern] update_gemspec(:files, repository_files) update_gemspec(:test_files, repository_files & test_files) end # Builds the gem def build_task sh "gem build -q #{gemspec_file}" Dir.mkdir('pkg') unless File.exist?('pkg') sh "mv #{gemspec.name}-#{gemspec.version}.gem pkg/#{gemspec.name}-#{gemspec.version}.gem" end def newest_version `#{git} tag`.split("\n").map { |tag| tag.split('-').last }.compact.map { |v| Gem::Version.new(v) }.max || Gem::Version.new('0.0.0') end def next_version(increment = nil) next_version = newest_version.segments increment_index = case increment when :micro then 3 when :patch then 2 when :minor then 1 when :major then 0 else next_version.length - 1 end next_version[increment_index] ||= 0 next_version[increment_index] = next_version[increment_index].succ ((increment_index + 1)...next_version.length).each { |i| next_version[i] = 0 } Gem::Version.new(next_version.join('.')) end def next_version_task(increment = nil) ENV['VERSION'] = next_version(increment).version puts "Releasing version #{ENV['VERSION']}..." end # Updates the version number in the gemspec file, the VERSION constant in the main # include file and the contents of the VERSION file. def version_task update_gemspec(:version, ENV['VERSION']) if ENV['VERSION'] update_gemspec(:date, Date.today) update_version_file(gemspec.version) update_version_constant(gemspec.version) end def check_version_task raise "#{ENV['VERSION']} is not a valid version number!" if ENV['VERSION'] && !Gem::Version.correct?(ENV['VERSION']) proposed_version = Gem::Version.new((ENV['VERSION'] || gemspec.version).dup) raise "This version (#{proposed_version}) is not higher than the highest tagged version (#{newest_version})" if newest_version >= proposed_version end # Checks whether the current branch is not diverged from the remote branch def check_not_diverged_task raise "The current branch is diverged from the remote branch!" if `#{git} rev-list HEAD..#{remote}/#{remote_branch}`.split("\n").any? end # Checks whether the repository status ic clean def check_clean_status_task raise "The current working copy contains modifications" if `#{git} ls-files -m`.split("\n").any? end # Checks whether the current branch is correct def check_current_branch_task raise "Currently not on #{local_branch} branch!" unless `#{git} branch`.split("\n").detect { |b| /^\* / =~ b } == "* #{local_branch}" end # Fetches the latest updates from Github def fetch_origin_task sh git, 'fetch', remote end # Commits every file that has been changed by the release task. def commit_modified_files_task really_modified = `#{git} ls-files -m #{modified_files.entries.join(' ')}`.split("\n") if really_modified.any? really_modified.each { |file| sh git, 'add', file } sh git, 'commit', '-m', "Released #{gemspec.name} gem version #{gemspec.version}." end end # Adds a tag for the released version def tag_version_task sh git, 'tag', '-a', "#{gemspec.name}-#{gemspec.version}", '-m', "Released #{gemspec.name} gem version #{gemspec.version}." end # Pushes the changes and tag to github def github_release_task sh git, 'push', '--tags', remote, remote_branch end def gemcutter_release_task sh "gem", 'push', "pkg/#{gemspec.name}-#{gemspec.version}.gem" end # Gem release task. # All work is done by the task's dependencies, so just display a release completed message. def release_task puts puts "Release successful." end private # Checks whether this project has any RSpec files def has_specs? FileList[spec_pattern].any? end # Checks whether this project has any unit test files def has_tests? FileList[test_pattern].any? end # Loads the gemspec file def load_gemspec! @gemspec = eval(File.read(@gemspec_file)) end # Updates the VERSION file with the new version def update_version_file(version) if File.exists?('VERSION') File.open('VERSION', 'w') { |f| f << version.to_s } modified_files << 'VERSION' end end # Updates the VERSION constant in the main include file if it exists def update_version_constant(version) if main_include && File.exist?(main_include) file_contents = File.read(main_include) if file_contents.sub!(/^(\s+VERSION\s*=\s*)[^\s].*$/) { $1 + version.to_s.inspect } File.open(main_include, 'w') { |f| f << file_contents } modified_files << main_include end end end # Updates an attribute of the gemspec file. # This function will open the file, and search/replace the attribute using a regular expression. def update_gemspec(attribute, new_value, literal = false) unless literal new_value = case new_value when Array then "%w(#{new_value.join(' ')})" when Hash, String then new_value.inspect when Date then new_value.strftime('%Y-%m-%d').inspect else raise "Cannot write value #{new_value.inspect} to gemspec file!" end end spec = File.read(gemspec_file) regexp = Regexp.new('^(\s+\w+\.' + Regexp.quote(attribute.to_s) + '\s*=\s*)[^\s].*$') if spec.sub!(regexp) { $1 + new_value } File.open(gemspec_file, 'w') { |f| f << spec } modified_files << gemspec_file # Reload the gemspec so the changes are incorporated load_gemspec! # Also mark the Gemfile.lock file as changed because of the new version. modified_files << 'Gemfile.lock' if File.exist?(File.join(root_dir, 'Gemfile.lock')) end end # Updates the tasks file using the latest file found on Github def update_tasks_task require 'net/https' require 'uri' uri = URI.parse('https://raw.github.com/wvanbergen/github-gem/master/tasks/github-gem.rake') http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE response = http.request(Net::HTTP::Get.new(uri.path)) if Net::HTTPSuccess === response open(__FILE__, "w") { |file| file.write(response.body) } relative_file = File.expand_path(__FILE__).sub(%r[^#{@root_dir}/], '') if `#{git} ls-files -m #{relative_file}`.split("\n").any? sh git, 'add', relative_file sh git, 'commit', '-m', "Updated to latest gem release management tasks." else puts "Release managament tasks already are at the latest version." end else raise "Download failed with HTTP status #{response.code}!" end end end end