pax_global_header00006660000000000000000000000064130114024320014500gustar00rootroot0000000000000052 comment=cb4057d0368223daa4a1334dc9b734f61fdeab64 chunky_png-1.3.8/000077500000000000000000000000001301140243200136565ustar00rootroot00000000000000chunky_png-1.3.8/.gitignore000066400000000000000000000001341301140243200156440ustar00rootroot00000000000000.DS_Store spec/resources/_*.png /pkg /tmp /doc /.yardoc /.bundle /_site Gemfile.lock test.* chunky_png-1.3.8/.travis.yml000066400000000000000000000002741301140243200157720ustar00rootroot00000000000000language: ruby script: bundle exec rake sudo: false rvm: - "2.0" - "2.1" - "2.2" - ruby-head - rbx-19mode - jruby-19mode matrix: allow_failures: - rvm: rbx-19mode - rvm: ruby-head chunky_png-1.3.8/.yardopts000066400000000000000000000001561301140243200155260ustar00rootroot00000000000000--title "ChunkyPNG - The pure Ruby PNG library" lib/**/*.rb - BENCHMARKS.rdoc --no-private --hide-void-return chunky_png-1.3.8/BENCHMARKING.rdoc000066400000000000000000000033051301140243200163600ustar00rootroot00000000000000= ChunkyPNG benchmark suite I would like the performance of this library as good as possible, and I will gladly accept changes to this library that improves performance. The library comes with a basic benchmark suite is intended to test the speed of PNG decoding and encoding against different ruby interpreters. Execute them using rake. You can set the number of runs by passing the N environment variable. bundle exec rake benchmark:encoding bundle exec rake benchmark:decoding bundle exec rake benchmark N=10 # Run all of them with 10 iterations You can use rvm to run the benchmarks 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 bundle exec rake benchmark N=10 == Results What is a speed improvement on one interpreter doesn't necessarily mean the performance will be better on other interpreters as well. Please make sure to benchamrk different RUby interpreters. When it comes to different Ruby interpreters, the priority is the performance on recent MRI versions. Some very old benchmark result (using N=50) on my 2007 iMac can be found at https://gist.github.com/wvanbergen/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-1.3.8/CHANGELOG.rdoc000066400000000000000000000226531301140243200160260ustar00rootroot00000000000000= Changelog The file documents the changes to this library over the different versions. - ChunkyPNG uses semantic versioning. This means that the public API will not change except for major versions. - Please add an entry to the "Unreleased changes" section in your pull requests, so I can move them into a numbered version section on release. === Unreleased changes - Nothing yet! === 1.3.8 - 2016-08-31 - Add support for reading and writing an image's physical dimension (pHYs chunks). === 1.3.7 - 2016-08-31 - Performance improvement for Color.euclidean_distance_rgba. - Bugfix in decoding transparent pixels when decoding multiple images in a row. === 1.3.6 - 2016-06-19 - Allow reading images from streams that have trailing data after the IEND chunk. - Add compatibility for Ruby 2.3's frozen string literals. - Documentation updates and small cleanups. === 1.3.5 - 2015-10-28 - Performance improvements for Canvas#crop! and ImageData.combine_chunks - Update chunky_png/rmagick to work with the latest versions of RMagick. - Bugfix in Color#from_hsl and Color#from_hsv when hue value is 360. - Fix encoding issue in Datastream#to_blob === 1.3.4 - 2015-02-16 - Assert compatibility with Ruby 2.2 - Improved documentation using RDoc, so it is included on http://www.rubydoc.info/gems/chunky_png - Update chunkypng.com website; migrate some stuff from the wiki. === 1.3.3 - 2014-10-24 - Improve performance of Canvas#crop and Canvas#crop! by doing less memory allocations. - Update to RSPEC 3 - Add CONTRIBUTING.rdoc file. === 1.3.2 - 2014-10-18 - Add HSV/HSL color conversions: Color.from_hsl, Color.to_hsl - Allow empty IDAT chunks to better conform to the PNG standard. - Small bugfix in image resampling. - Documentation and code readability improvements. === 1.3.1 - 2014-04-28 - Improve performance of Palette.from_canvas. - Add Color.euclidean_distance_rgba to compare colors. - Bugix in Canvas.from_bgr_stream. - Documentation and code readibility improvements. - README updates, include mention of screencast. === 1.3.0 - 2014-02-10 - Add support for parsing color that use three-hex notation. (e.g. #aaa instead of #aaaaaa) - Add Canvas#border! and Canvas#border to draw a border around a canvas. - Add Canvas#trim! and Canvas#trim to trim a border from a canvas. === 1.2.9 - 2013-10-17 - Set license in chunky_png.gemspec for better discoverability. - Improve error messages for Canvas#crop. - Use better gem release management tasks from bundler. === 1.2.8 - 2013-03-30 - Ruby 2.0 compatibility. - Fixed some encoding issues on JRuby. - Update Travis CI configuration to test on more Ruby versions. === 1.2.7 - 2013-01-07 - Small PNG decoding performnace improvements by using bitwise math. === 1.2.6 - 2012-08-07 - Add decompression bomb security warning to README. - Fix RMagick loading issue on case sensitive filesystems. - Some compatibility fixes for the upcoming Ruby 2.0. - Allow more data-url notations for ChunkyPNG::Canvas.from_data_url. === 1.2.5 - 2011-09-23 - Edge case bugfix in Color.decompose_alpha_component that could get triggered in the change_theme_color! method. === 1.2.4 - 2011-09-14 - Added data URL importing Canvas.from_data_url. === 1.2.3 - 2011-09-14 - Added data URL exporting Canvas#to_data_url to easily use PNGs inline in CSS or HTML. === 1.2.2 - 2011-09-14 - Workaround for performance bug in REE. === 1.2.1 - 2011-08-10 - Added bicubic resampling of images. - Update resampling code to use integer math instead of floating points. === 1.2.0 - 2011-05-08 - Properly read PNG files with a tRNS chunk in color mode 0 (grayscale) or 2 (true color). === 1.1.2 - 2011-05-06 - Added Color.to_grayscale and Canvas#grayscale! to convert colors and canvases to grayscale. - Memory footprint improvement of Canvas#resample! === 1.1.1 - 2011-04-22 - Added Canvas#to_alpha_channel_bytes and Canvas#to_grayscale_stream to export raw pixel data. - Spec suite cleanup === 1.1.0 - 2011-03-19 - Add bezier curve drawing: Canvas#bezier_curve. - RDoc fixes & improvements. === 1.0.1 - 2011-03-08 - Performance improvements. === 1.0.0 - 2011-03-06 There are some API changes for this release. If you are using Canvas#compose or Canvas#replace, these methods will no longer operate in place, but will return a new canvas instance instead. The in place versions have been renamed to compose! and replace! to be more consistent with the rest of the API. - Added image resampling using the nearest neighbor algorithm: Canvas#resample. - Added circle and polygon drawing methods: Canvas#circle and Canvas#polygon. - Added in place version of Canvas#crop, Canvas#rotate_180, Canvas#flip_horizontally and Canvas#flip_vertically. Just add a bang to the method name (e.g. Canvas#crop!) and it will change the current canvas instead of returning a new one. These implementations are also more memory and CPU efficient. - Added geometry helper classes: ChunkyPNG::Point, ChunkyPNG::Dimension and ChunkyPNG::Vector. - Added a list of HTML named colors. Get them by calling ChunkyPNG::Color(:teal) or ChunkyPNG::Color('red @ 0.8') - Added encoding support for 1-, 2-, and 4-bit grayscale images. - Cleaned up auto-detection of color mode settings. It will now choose 1 bit grayscale mode if an image only contains black and white. (The other low bitrate grayscale modes are never chosen automatically.) - RDoc improvements. See http://rdoc.info/gems/chunky_png/frames. - ChunkyPNG is now also tested on Ruby 1.8.6. === 0.12.0 - 2010-12-12 - Added support for encoding indexed images with a low bitrate. It will automatically use less bits per pixel if possible. - Improved testing setup. ChunkyPNG is now tested on Ruby 1.8.7, 1.9.2, JRuby and Rubinius. === 0.11.0 - 2010-11-16 - Decoding of 1, 2 and 4 bit indexed color images. - Decoding of 1, 2 and 4 bit grayscale images. - Decoding 16 bit images. The extra bits will be discarded, so the image will be loaded as 8 bit. - Used the official PNG suite to build a more complete test suite. === 0.10.5 - 2010-10-21 - Bugfix: allow 256 instead of 255 colors for indexed images. === 0.10.4 - 2010-10-17 - Improved handling of binary encoding for strings in Ruby 1.9. === 0.10.3 - 2010-10-07 - Small fix to make grayscale use the B byte consistently. === 0.10.2 - 2010-10-04 - Another small fix for OilyPNG compatibility === 0.10.1 - 2010-10-03 - Small fix for OilyPNG compatibility === 0.10.0 - 2010-10-03 - Refactored decoding and encoding to work on binary strings instead of arrays of integers. This gives a nice speedup and uses less memory. Thanks to Yehuda Katz for the idea. === 0.9.2 - 2010-09-16 - Fixed an issue with interlaced images. === 0.9.1 - 2010-09-15 - Fixed image metadata issue when duplicating images. === 0.9.0 - 2010-08-18 - Added flip_horizontally, flip_vertically, rotate_left, rotate_right and rotate_180 to ChunkyPNG::Canvas. - Now raises ChunkyPNG::OutOfBounds exceptions when referencing coordinates outside the image bounds. - Added Gemfile for development dependency management. === 0.8.0 - 2010-06-30 - Added ChunkyPNG::Image#rect to draw simple rectangles. - Fixed composing a transparent color on a fully transparent background. === 0.7.3 - 2010-04-28 - Based on the suggestion of [Dirkjan Bussink](http://github.com/dbussink), introduced custom exception classes: - ChunkyPNG::SignatureMismatch is raised when the PNG signature could not be found. Usually this means the the file is not a PNG image. - ChunkyPNG::CRCMismatch is raised when the a CRC check for a chunk in the PNG file fails. - ChunkyPNG::NotSupported is raised when the PNG image uses a feature that ChunkyPNG does not support. - ChunkyPNG::ExpectationFailed is raised when a required expectation failed. === 0.7.2 - 2010-04-28 [YANKED] === 0.7.1 - 2010-03-23 - Some fixes for 32-bit systems. === 0.7.0 - 2010-03-15 - Added :best_compression saving routine to allow creating the smallest images possible. - Added option to control Zlib compression level while saving. === 0.6.0 - 2010-02-25 - Added methods to easily create different color variants of an image with a color theme. See [[Images with a color theme]] for more information. === 0.5.8 - 2010-02-24 - Ruby 1.8.6 compatibility fixes - Improved API documentation. === 0.5.5 - 2010-02-15 - Added alpha decomposition to extract a color mask from a themed image. - Improved API documentation. === 0.5.4 - 2010-01-17 - Added point and line anti-aliased drawing functions. === 0.5.3 - 2010-01-16 - Removed last occurrences of floating math to speed up the library. - Added importing of ABGR and BGR streams. - Added exporting an image as ABGR stream. === 0.5.2 - 2010-01-15 - Ruby 1.9 compatibility fixes. - Improved speed of PNG decoding. - Bugfix in *average* scanline decoding filter. === 0.5.1 - 2010-01-15 - Added :fast_rgba and :fast_rgb saving routines, which yield a 1500% speedup when saving an image. === 0.5.0 - 2010-01-15 - Complete rewrite of the earlier versions, now including awesomeness and unicorns. chunky_png-1.3.8/CONTRIBUTING.rdoc000066400000000000000000000043061301140243200164410ustar00rootroot00000000000000= Contributing to ChunkyPNG I will gladly accept any contributions from anybody to improve this library. However, I like to keep the scope and complexity of this project relatively small. This way, with the limited amount of time I dedicate to this project, this library remains a solid piece of software that many people rely on in their workflow. == Project scope The scope of this library is defined as: 1. Reading and writing any PNG image that conforms to the spec, with proper handling of images that do not. 2. Basic canvas drawing and compositing operations to create new or edit existing images. 3. Remain compatible with Ruby versions that are widely used. I will close issues and pull requests that go beyond this scope. If you want to work on something, but are not sure if it will be in scope, feel free to reach out to me! == Reporting bugs - First, see if somebody else has reported the problem already. - Try to include as much relevant information as possible, so I can recreate the problem. - If possible, include the PNG image that is causing the issue. - If possible, include a code snippet that exposes the problem. == Pull requests Title and description: - If you are not yet done, please include [WIP] in the title of your pull request. - Explain why your changes are relevant in the description of your pull request. - If your changes improve performance, include benchmark methodology and results in the PR. See BENCHMARKING.rdoc for more information. Code: - Don't break backwards compatibility. - Follow code conventions. They are not defined, so look at the code around you. - Add Yardoc comments as documentation. Specs: - Always include specs that test your changes, to prevent me from breaking your code later. - If your specs use PNG files, try to keep them as small as possible to keep the test suite snappy. - Make sure that the specs are passing for all Rubies on Travis CI: https://travis-ci.org/wvanbergen/chunky_png/ Misc: - Add an entry to CHANGELOG.rdoc. - Do not change ChunkyPNG::VERSION == Releases - Ensure the latest master is passing on Travis CI. - Update CHANGELOG.rdoc - Update ChunkyPNG::VERSION - Run bundle exec rake release chunky_png-1.3.8/Gemfile000066400000000000000000000001721301140243200151510ustar00rootroot00000000000000source 'https://rubygems.org' gemspec platforms :jruby do gem 'jruby-openssl' end platform :rbx do gem 'rubysl' end chunky_png-1.3.8/LICENSE000066400000000000000000000020521301140243200146620ustar00rootroot00000000000000Copyright (c) 2010-2015 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-1.3.8/README.md000066400000000000000000000071231301140243200151400ustar00rootroot00000000000000# ChunkyPNG [![Build Status](https://travis-ci.org/wvanbergen/chunky_png.svg?branch=master)](https://travis-ci.org/wvanbergen/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, all transparency, and 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](http://github.com/wvanbergen/oily_png) which is a mixin module that implements some of the ChunkyPNG algorithms in C, which provides a massive speed boost to encoding and decoding. ## Usage ```ruby 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 } ``` Also check out the screencast on the ChunkyPNG homepage by John Davison, which illustrates basic usage of the library on the [ChunkyPNG website](http://chunkypng.com/). For more information, see the [project wiki](https://github.com/wvanbergen/chunky_png/wiki) or the [RDOC documentation](http://www.rubydoc.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 CHANGELOG.rdoc 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-1.3.8/Rakefile000066400000000000000000000003741301140243200153270ustar00rootroot00000000000000require "bundler/gem_tasks" require "rspec/core/rake_task" Dir['tasks/*.rake'].each { |file| load(file) } RSpec::Core::RakeTask.new(:spec) do |task| task.pattern = "./spec/**/*_spec.rb" task.rspec_opts = ['--color'] end task :default => [:spec] chunky_png-1.3.8/benchmarks/000077500000000000000000000000001301140243200157735ustar00rootroot00000000000000chunky_png-1.3.8/benchmarks/decoding_benchmark.rb000066400000000000000000000025231301140243200221100ustar00rootroot00000000000000require '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-1.3.8/benchmarks/encoding_benchmark.rb000066400000000000000000000031221301140243200221160ustar00rootroot00000000000000require '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-1.3.8/benchmarks/filesize_benchmark.rb000066400000000000000000000021231301140243200221420ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'benchmark' require 'chunky_png' files = ['pixelstream_reference.png', 'operations.png', 'clock.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-1.3.8/chunky_png.gemspec000066400000000000000000000037661301140243200174040ustar00rootroot00000000000000# -*- encoding: utf-8 -*- lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'chunky_png/version' Gem::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 = ChunkyPNG::VERSION 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.license = 'MIT' s.add_development_dependency('rake') s.add_development_dependency('rspec', '~> 3') s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source' s.extra_rdoc_files = ['README.md', 'BENCHMARKING.rdoc', 'CONTRIBUTING.rdoc', 'CHANGELOG.rdoc'] s.files = `git ls-files`.split($/) s.test_files = s.files.grep(%r{^(test|spec|features)/}) end chunky_png-1.3.8/lib/000077500000000000000000000000001301140243200144245ustar00rootroot00000000000000chunky_png-1.3.8/lib/chunky_png.rb000066400000000000000000000120631301140243200171200ustar00rootroot00000000000000# 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 ################################################### # 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 # Exception that is raised when requesting the DPI of a PNG that doesn't # specify the units of its physical pixel dimensions. class UnitsUnknown < ChunkyPNG::Exception 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(String.new).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(String.new("\0")).freeze end require 'chunky_png/version' # 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-1.3.8/lib/chunky_png/000077500000000000000000000000001301140243200165715ustar00rootroot00000000000000chunky_png-1.3.8/lib/chunky_png/canvas.rb000066400000000000000000000324321301140243200203750ustar00rootroot00000000000000require '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) unless initial.length == width * height raise ArgumentError, "The initial array should have #{width}x#{height} = #{width*height} elements!" end @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) 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) unless new_pixels.length == new_width * new_height raise ArgumentError, "The provided pixel array should have #{new_width * new_height} items" end @width, @height, @pixels = new_width, new_height, new_pixels self end # Throws an exception if the x-coordinate is out of bounds. def assert_x!(x) unless include_x?(x) raise ChunkyPNG::OutOfBounds, "Column index #{x} out of bounds!" end true end # Throws an exception if the y-coordinate is out of bounds. def assert_y!(y) unless include_y?(y) raise ChunkyPNG::OutOfBounds, "Row index #{y} out of bounds!" end true end # Throws an exception if the x- or y-coordinate is out of bounds. def assert_xy!(x, y) unless include_xy?(x, y) raise ChunkyPNG::OutOfBounds, "Coordinates (#{x},#{y}) out of bounds!" end true end # Throws an exception if the vector_length does not match this canvas' # height. def assert_height!(vector_length) if height != vector_length raise ChunkyPNG::ExpectationFailed, "The length of the vector (#{vector_length}) does not match the canvas height (#{height})!" end true end # Throws an exception if the vector_length does not match this canvas' # width. def assert_width!(vector_length) if width != vector_length raise ChunkyPNG::ExpectationFailed, "The length of the vector (#{vector_length}) does not match the canvas width (#{width})!" end true end # Throws an exception if the matrix width and height does not match this canvas' dimensions. def assert_size!(matrix_width, matrix_height) if width != matrix_width raise ChunkyPNG::ExpectationFailed, 'The width of the matrix does not match the canvas width!' end if height != matrix_height raise ChunkyPNG::ExpectationFailed, 'The height of the matrix does not match the canvas height!' end true end end end chunky_png-1.3.8/lib/chunky_png/canvas/000077500000000000000000000000001301140243200200445ustar00rootroot00000000000000chunky_png-1.3.8/lib/chunky_png/canvas/adam7_interlacing.rb000066400000000000000000000065161301140243200237510ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/canvas/data_url_exporting.rb000066400000000000000000000006631301140243200242700ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/canvas/data_url_importing.rb000066400000000000000000000014251301140243200242560ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/canvas/drawing.rb000066400000000000000000000272721301140243200220360ustar00rootroot00000000000000module 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 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)) if inclusive || i > 0 compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) end 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)) if inclusive || i > 0 compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) end x0 += sx end compose_pixel(x1, y1, stroke_color) if inclusive end 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) if path.length < 3 raise ArgumentError, 'A polygon requires at least 3 points' end 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 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) 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_offset| if length > 0 line(x0 - length, y0 - y_offset, x0 + length, y0 - y_offset, fill_color) end if length > 0 && y_offset > 0 line(x0 - length, y0 + y_offset, x0 + length, y0 + y_offset, fill_color) end end end 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-1.3.8/lib/chunky_png/canvas/masking.rb000066400000000000000000000113541301140243200220260ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/canvas/operations.rb000066400000000000000000000370241301140243200225620ustar00rootroot00000000000000module 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 grayscale. # # 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) } self end # Converts the canvas to grayscale, 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 # grayscale. # @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) if crop_width + x > width raise ChunkyPNG::OutOfBounds, 'Original image width is too small!' end if crop_height + y > height raise ChunkyPNG::OutOfBounds, 'Original image height is too small!' end if crop_width == width && x == 0 # We only need to crop off the top and/or bottom, so we can take a # shortcut. replace_canvas!(crop_width, crop_height, pixels.slice(y * width, width * crop_height)) else new_pixels = [] for cy in 0...crop_height do new_pixels.concat pixels.slice((cy + y) * width + x, crop_width) end replace_canvas!(crop_width, crop_height, new_pixels) end 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 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 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. # # @return [ChunkyPNG::Canvas] A clockwise-rotated copy. # @see #rotate_right! for the in place version. def rotate_right dup.rotate_right! end # Rotates the image 90 degrees clockwise in place. # # This method will change the current canvas. # # @return [ChunkyPNG::Canvas] Itself, but rotated clockwise. # @see #rotate_right for a version that leaves the current canvas intact def rotate_right! 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. # # @return [ChunkyPNG::Canvas] A rotated copy of itself. # @see #rotate_left! for the in-place version. 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! self end # Trims the border around the image, presumed to be the color of the # first pixel. # # @param [Integer] border The color to attempt to trim. # @return [ChunkyPNG::Canvas] The trimmed image. # @see #trim! def trim(border = pixels.first) dup.trim! end # Trims the border around the image in place. # # @param [Integer] border The color to attempt to trim. # @return [ChunkyPNG::Canvas] Returns itself, but with the border # trimmed. # @see #trim def trim!(border = pixels.first) x1 = [*0...width].index { |c| column(c).uniq != [border] } x2 = [*0...width].rindex { |c| column(c).uniq != [border] } y1 = [*0...height].index { |r| row(r).uniq != [border] } y2 = [*0...height].rindex { |r| row(r).uniq != [border] } crop! x1, y1, x2 - x1 + 1, y2 - y1 + 1 end # Draws a border around the image. # # @param [Integer] size The size of the border. # @param [Integer] color The color of the border. # @return [ChunkyPNG::Canvas] Returns a bordered version of the image. # @see #border! def border(size, color = ChunkyPNG::Color::BLACK) dup.border!(size, color) end # Draws a border around the image in place. # # @param [Integer] size The size of the border. # @param [Integer] color The color of the border. # @return [ChunkyPNG::Canvas] Returns itself with the border added. # @see #border def border!(size, color = ChunkyPNG::Color::BLACK) new_width = width + size * 2 new_height = height + size * 2 bg = Canvas.new(new_width, new_height, color).replace(self, size, size) replace_canvas!(new_width, new_height, bg.pixels) 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) if width < other.width + offset_x raise ChunkyPNG::OutOfBounds, 'Background image width is too small!' end if height < other.height + offset_y raise ChunkyPNG::OutOfBounds, 'Background image height is too small!' end end end end end chunky_png-1.3.8/lib/chunky_png/canvas/png_decoding.rb000066400000000000000000000621061301140243200230160ustar00rootroot00000000000000module 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 # 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 decoding_palette, transparent_color = nil, nil case color_mode when ChunkyPNG::COLOR_INDEXED decoding_palette = ChunkyPNG::Palette.from_chunks(ds.palette_chunk, ds.transparency_chunk) when ChunkyPNG::COLOR_TRUECOLOR transparent_color = ds.transparency_chunk.truecolor_entry(depth) if ds.transparency_chunk when ChunkyPNG::COLOR_GRAYSCALE transparent_color = ds.transparency_chunk.grayscale_entry(depth) if ds.transparency_chunk end decode_png_pixelstream(ds.imagedata, width, height, color_mode, depth, interlace, decoding_palette, transparent_color) 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. # @param [ChunkyPNG::Palette] decoding_palette The palette to use to decode colors. # @param [Integer] transparent_color The color that should be considered fully transparent. # @return [ChunkyPNG::Canvas] The decoded Canvas instance. def decode_png_pixelstream(stream, width, height, color_mode, depth, interlace, decoding_palette, transparent_color) 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, decoding_palette) when ChunkyPNG::INTERLACING_ADAM7; decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth, decoding_palette) 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) # @param [ChunkyPNG::Palette] decoding_palette The palette to use to decode colors. # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) def decode_png_without_interlacing(stream, width, height, color_mode, depth, decoding_palette) decode_png_image_pass(stream, width, height, color_mode, depth, 0, decoding_palette) 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) # @param [ChunkyPNG::Palette] decoding_palette The palette to use to decode colors. # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) def decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth, decoding_palette) 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, decoding_palette) 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. # @param [ChunkyPNG::Palette] decoding_palette The palette to use to decode colors. # @return [Array] An array of decoded pixels. def decode_png_pixels_from_scanline_indexed_1bit(stream, pos, width, decoding_palette) (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, decoding_palette) (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, decoding_palette) (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, decoding_palette) (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, _decoding_palette) 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, _decoding_palette) 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, _decoding_palette) 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, _decoding_palette) 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, _decoding_palette) (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, _decoding_palette) 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, _decoding_palette) (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, _decoding_palette) (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, _decoding_palette) (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, _decoding_palette) (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, _decoding_palette) 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. # @param [ChunkyPNG::Palette] decoding_palette The palette to use to decode colors. # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream) def decode_png_image_pass(stream, width, height, color_mode, depth, start_pos, decoding_palette) 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 _ in 0...height do decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size) pixels.concat(send(pixel_decoder, stream, pos, width, decoding_palette)) 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-1.3.8/lib/chunky_png/canvas/png_encoding.rb000066400000000000000000000524641301140243200230360ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/canvas/resampling.rb000066400000000000000000000116061301140243200225360ustar00rootroot00000000000000 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(new_width, obj=nil) residues = Array.new(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.new(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.new(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-1.3.8/lib/chunky_png/canvas/stream_exporting.rb000066400000000000000000000040601301140243200237630ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/canvas/stream_importing.rb000066400000000000000000000077031301140243200237630ustar00rootroot00000000000000module 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.dup # 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-1.3.8/lib/chunky_png/chunk.rb000066400000000000000000000352021301140243200202300ustar00rootroot00000000000000module 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 [IO, #read] The IO stream to read from. # @return [ChunkyPNG::Chung::Base] The loaded chunk instance. def self.read(io) length, type = read_bytes(io, 8).unpack('Na4') content = read_bytes(io, length) crc = read_bytes(io, 4).unpack('N').first verify_crc!(type, content, crc) CHUNK_TYPES.fetch(type, Generic).read(type, content) end # Reads an exact number of bytes from an IO stream. # @param io [IO, #read] The IO stream to read from. # @param length [Integer] The IO exact number of bytes to read. # @return [String] A binary string of exactly length bytes. # @raise [ChunkyPNG::ExpectationFailed] If not exactly length # bytes could be read from the IO stream. def self.read_bytes(io, length) data = io.read(length) raise ExpectationFailed, "Couldn't read #{length} bytes from IO stream." if data.nil? || data.bytesize != length data end # Verifies the CRC of a chunk. # @param type [String] The chunk's type. # @param content [String] The chunk's content. # @param found_crc [Integer] The chunk's found CRC value. # @raise [ChunkyPNG::CRCMismatch] 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 type [String] The four character chunk type indicator. # @param attributes [Hash] A hash of attributes to set on this chunk. def initialize(type, attributes = {}) self.type = type attributes.each { |k, v| send("#{k}=", v) } end # Writes the chunk to the IO stream, using the provided content. # The checksum will be calculated and appended to the stream. # @param io [IO] The IO stream to write to. # @param content [String] The content for this chunk. def write_with_crc(io, content) io << [content.length].pack('N') << type << content io << [Zlib.crc32(content, Zlib.crc32(type))].pack('N') end # Writes the chunk to the IO stream. # # It will call the +content+ method to get the content for this chunk, # and will calculate and append the checksum automatically. # @param io [IO] The IO stream to write to. 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 type [String] The four character chunk type indicator. # @param content [String] The content read from the chunk. # @return [ChunkyPNG::Chunk::Generic] The new chunk instance. def self.read(type, content) 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 type [String] The four character chunk type indicator (= "IHDR"). # @param content [String] The 13 bytes of content read from the chunk. # @return [ChunkyPNG::Chunk::End] The new Header chunk instance with the # variables set to the values according to the content. def self.read(type, content) fields = content.unpack('NNC5') 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 type [String] The four character chunk type indicator (= # "IEND"). # @param content [String] The content read from the chunk. Should be # empty. # @return [ChunkyPNG::Chunk::End] The new End chunk instance. # @raise [RuntimeError] Raises an exception if the content was not empty. def self.read(type, content) raise ExpectationFailed, 'The IEND chunk should be empty!' if content.bytesize > 0 self.new 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 do |c| ChunkyPNG::Canvas.send(:"decode_png_resample_#{bit_depth}bit_value", c) end ChunkyPNG::Color.rgb(*values) end # Returns the grayscale entry to be replaced by transparent pixels. # # This method should only be used for images having color mode # ChunkyPNG::COLOR_GRAYSCALE (0). # # @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.combine_chunks(data_chunks) zstream = Zlib::Inflate.new data_chunks.each { |c| zstream << c.content } inflated = zstream.finish zstream.close inflated 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 Physical (pHYs) chunk specifies the intended pixel size or aspect # ratio for display of the image. # # http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.pHYs # class Physical < Generic attr_accessor :ppux, :ppuy, :unit def initialize(ppux, ppuy, unit = :unknown) raise ArgumentError, 'unit must be either :meters or :unknown' unless [:meters, :unknown].member?(unit) super('pHYs') @ppux, @ppuy, @unit = ppux, ppuy, unit end def dpix raise ChunkyPNG::UnitsUnknown, 'the PNG specifies its physical aspect ratio, but does not specify the units of its pixels\' physical dimensions' unless unit == :meters ppux * INCHES_PER_METER end def dpiy raise ChunkyPNG::UnitsUnknown, 'the PNG specifies its physical aspect ratio, but does not specify the units of its pixels\' physical dimensions' unless unit == :meters ppuy * INCHES_PER_METER end def self.read(type, content) ppux, ppuy, unit = content.unpack('NNC') unit = unit == 1 ? :meters : :unknown new(ppux, ppuy, unit) 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 [ppux, ppuy, unit == :meters ? 1 : 0].pack('NNC') end INCHES_PER_METER = 0.0254 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, 'pHYs' => Physical, } end end chunky_png-1.3.8/lib/chunky_png/color.rb000066400000000000000000001173041301140243200202420ustar00rootroot00000000000000module 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 3-digit hex color values. HEX3_COLOR_REGEXP = /\A(?:#|0x)?([0-9a-f]{3})\z/i # @private # @return [Regexp] The regexp to parse 6- and 8-digit hex color values. HEX6_COLOR_REGEXP = /\A(?:#|0x)?([0-9a-f]{6})([0-9a-f]{2})?\z/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 HEX3_COLOR_REGEXP, HEX6_COLOR_REGEXP; from_hex(source.to_s) when HTML_COLOR_REGEXP; 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 # as well as the 3-digit short format (#rgb) for those without. # 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) base_color = case hex_value when HEX3_COLOR_REGEXP $1.gsub(/([0-9a-f])/i, '\1\1').hex << 8 when HEX6_COLOR_REGEXP $1.hex << 8 else raise ArgumentError, "Not a valid hex color notation: #{hex_value.inspect}!" end opacity ||= $2 ? $2.hex : 0xff base_color | opacity end # Creates a new color from an HSV triple. # # Create a new color using an HSV (sometimes also called HSB) triple. The # words `value` and `brightness` are used interchangeably and synonymously # in descriptions of this colorspace. This implementation follows the modern # convention of 0 degrees hue indicating red. # # @param [Fixnum] hue The hue component (0-360) # @param [Fixnum] saturation The saturation component (0-1) # @param [Fixnum] value The value (brightness) component (0-1) # @param [Fixnum] alpha Defaults to opaque (255). # @return [Integer] The newly constructed color value. # @raise [ArgumentError] if the hsv triple is invalid. # @see http://en.wikipedia.org/wiki/HSL_and_HSV def from_hsv(hue, saturation, value, alpha = 255) raise ArgumentError, "Hue must be between 0 and 360" unless (0..360).include?(hue) raise ArgumentError, "Saturation must be between 0 and 1" unless (0..1).include?(saturation) raise ArgumentError, "Value/brightness must be between 0 and 1" unless (0..1).include?(value) chroma = value * saturation rgb = cylindrical_to_cubic(hue, saturation, value, chroma) rgb.map! { |component| ((component + value - chroma) * 255).to_i } rgb << alpha self.rgba(*rgb) end alias_method :from_hsb, :from_hsv # Creates a new color from an HSL triple. # # This implementation follows the modern convention of 0 degrees hue # indicating red. # # @param [Fixnum] hue The hue component (0-360) # @param [Fixnum] saturation The saturation component (0-1) # @param [Fixnum] lightness The lightness component (0-1) # @param [Fixnum] alpha Defaults to opaque (255). # @return [Integer] The newly constructed color value. # @raise [ArgumentError] if the hsl triple is invalid. # @see http://en.wikipedia.org/wiki/HSL_and_HSV def from_hsl(hue, saturation, lightness, alpha = 255) raise ArgumentError, "Hue #{hue} was not between 0 and 360" unless (0..360).include?(hue) raise ArgumentError, "Saturation #{saturation} was not between 0 and 1" unless (0..1).include?(saturation) raise ArgumentError, "Lightness #{lightness} was not between 0 and 1" unless (0..1).include?(lightness) chroma = (1 - (2 * lightness - 1).abs) * saturation rgb = cylindrical_to_cubic(hue, saturation, lightness, chroma) rgb.map! { |component| ((component + lightness - 0.5 * chroma) * 255).to_i } rgb << alpha self.rgba(*rgb) end # Convert one HSL or HSV triple and associated chroma to a scaled rgb triple # # This method encapsulates the shared mathematical operations needed to # convert coordinates from a cylindrical colorspace such as HSL or HSV into # coordinates of the RGB colorspace. # # Even though chroma values are derived from the other three coordinates, # the formula for calculating chroma differs for each colorspace. Since it # is calculated differently for each colorspace, it must be passed in as # a parameter. # # @param [Fixnum] hue The hue-component (0-360) # @param [Fixnum] saturation The saturation-component (0-1) # @param [Fixnum] y_component The y_component can represent either lightness # or brightness/value (0-1) depending on which scheme (HSV/HSL) is being used. # @param [Fixnum] chroma The associated chroma value. # @return [Array] A scaled r,g,b triple. Scheme-dependent # adjustments are still needed to reach the true r,g,b values. # @see http://en.wikipedia.org/wiki/HSL_and_HSV # @see http://www.tomjewett.com/colors/hsb.html # @private def cylindrical_to_cubic(hue, saturation, y_component, chroma) hue_prime = hue.fdiv(60) x = chroma * (1 - (hue_prime % 2 - 1).abs) case hue_prime when (0...1); [chroma, x, 0] when (1...2); [x, chroma, 0] when (2...3); [0, chroma, x] when (3...4); [0, x, chroma] when (4...5); [x, 0, chroma] when (5..6); [chroma, 0, x] end end private :cylindrical_to_cubic #################################################################### # 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 background 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 background 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)) 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 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 HSV components of a color. # # Because ChunkyPNG internally handles colors as Integers for performance # reasons, some rounding occurs when importing or exporting HSV colors # whose coordinates are float-based. Because of this rounding, #to_hsv and # #from_hsv may not be perfect inverses. # # This implementation follows the modern convention of 0 degrees hue # indicating red. # # @param [Integer] color The ChunkyPNG color to convert. # @param [Boolean] include_alpha Flag indicates whether a fourth element # representing alpha channel should be included in the returned array. # @return [Array[0]] The hue of the color (0-360) # @return [Array[1]] The saturation of the color (0-1) # @return [Array[2]] The value of the color (0-1) # @return [Array[3]] Optional fourth element for alpha, included if # include_alpha=true (0-255) # @see http://en.wikipedia.org/wiki/HSL_and_HSV def to_hsv(color, include_alpha = false) hue, chroma, max, _ = hue_and_chroma(color) value = max saturation = chroma.zero? ? 0.0 : chroma.fdiv(value) include_alpha ? [hue, saturation, value, a(color)] : [hue, saturation, value] end alias_method :to_hsb, :to_hsv # Returns an array with the separate HSL components of a color. # # Because ChunkyPNG internally handles colors as Integers for performance # reasons, some rounding occurs when importing or exporting HSL colors # whose coordinates are float-based. Because of this rounding, #to_hsl and # #from_hsl may not be perfect inverses. # # This implementation follows the modern convention of 0 degrees hue indicating red. # # @param [Integer] color The ChunkyPNG color to convert. # @param [Boolean] include_alpha Flag indicates whether a fourth element # representing alpha channel should be included in the returned array. # @return [Array[0]] The hue of the color (0-360) # @return [Array[1]] The saturation of the color (0-1) # @return [Array[2]] The lightness of the color (0-1) # @return [Array[3]] Optional fourth element for alpha, included if # include_alpha=true (0-255) # @see http://en.wikipedia.org/wiki/HSL_and_HSV def to_hsl(color, include_alpha = false) hue, chroma, max, min = hue_and_chroma(color) lightness = 0.5 * (max + min) saturation = chroma.zero? ? 0.0 : chroma.fdiv(1 - (2 * lightness - 1).abs) include_alpha ? [hue, saturation, lightness, a(color)] : [hue, saturation, lightness] end # This method encapsulates the logic needed to extract hue and chroma from # a ChunkPNG color. This logic is shared by the cylindrical HSV/HSB and HSL # color space models. # # @param [Integer] A ChunkyPNG color. # @return [Fixnum] hue The hue of the color (0-360) # @return [Fixnum] chroma The chroma of the color (0-1) # @return [Fixnum] max The magnitude of the largest scaled rgb component (0-1) # @return [Fixnum] min The magnitude of the smallest scaled rgb component (0-1) # @private def hue_and_chroma(color) scaled_rgb = to_truecolor_bytes(color) scaled_rgb.map! { |component| component.fdiv(255) } min, max = scaled_rgb.minmax chroma = max - min r, g, b = scaled_rgb hue_prime = chroma.zero? ? 0 : case max when r; (g - b).fdiv(chroma) when g; (b - r).fdiv(chroma) + 2 when b; (r - g).fdiv(chroma) + 4 else 0 end hue = 60 * hue_prime return hue.round, chroma, max, min end private :hue_and_chroma # 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_grayscale def to_grayscale_alpha_bytes(color) [b(color), a(color)] # assumption r == g == b end #################################################################### # COMPARISON #################################################################### # Compute the Euclidean distance between 2 colors in RGBA # # This method simply takes the Euclidean distance between the RGBA channels # of 2 colors, which gives us a measure of how different the two colors # are. # # Although it would be more perceptually accurate to calculate a proper # Delta E in Lab colorspace, this method should serve many use-cases while # avoiding the overhead of converting RGBA to Lab. # # @param pixel_after [Integer] # @param pixel_before [Integer] # @return [Float] def euclidean_distance_rgba(pixel_after, pixel_before) return 0.0 if pixel_after == pixel_before Math.sqrt( (r(pixel_after) - r(pixel_before))**2 + (g(pixel_after) - g(pixel_before))**2 + (b(pixel_after) - b(pixel_before))**2 + (a(pixel_after) - a(pixel_before))**2 ) end # Could be simplified as MAX * 2, but this format mirrors the math in # {#euclidean_distance_rgba} # @return [Float] The maximum Euclidean distance of two RGBA colors. MAX_EUCLIDEAN_DISTANCE_RGBA = Math.sqrt(MAX**2 * 4) #################################################################### # 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 number 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-1.3.8/lib/chunky_png/compatibility.rb000066400000000000000000000006071301140243200217720ustar00rootroot00000000000000# 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-1.3.8/lib/chunky_png/datastream.rb000066400000000000000000000154311301140243200212470ustar00rootroot00000000000000module 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 then 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 chunk containing the physical dimensions of the PNG's pixels. # @return [ChunkyPNG::Chunk::Physical] attr_accessor :physical_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 while ds.end_chunk.nil? 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::Physical; ds.physical_chunk = 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 yield(physical_chunk) if physical_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 str.set_encoding('ASCII-8BIT') write(str) return str.string end alias :to_string :to_blob alias :to_s :to_blob end end chunky_png-1.3.8/lib/chunky_png/dimension.rb000066400000000000000000000110761301140243200211100ustar00rootroot00000000000000module 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; build_dimension_from_object(args.first) else raise ArgumentError, "Don't know how to construct a dimension from #{args.inspect}" end end def self.build_dimension_from_object(source) case source 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 width = source[:width] || source['width'] height = source[:height] || source['height'] ChunkyPNG::Dimension.new(width, 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 dimension from #{source.inspect}!" end end end private_class_method :build_dimension_from_object # 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) return false unless other.respond_to?(:width) && other.respond_to?(:height) 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-1.3.8/lib/chunky_png/image.rb000066400000000000000000000054071301140243200202060ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/palette.rb000066400000000000000000000175031301140243200205620ustar00rootroot00000000000000module 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 enum [Enumerable] The set of colors to include in this # palette.This Enumerable can contain duplicates. # @param decoding_map [Array] 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 palette_chunk [ChunkyPNG::Chunk::Palette] The palette chunk to # load from # @param transparency_chunk [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 new(decoding_map, decoding_map) end # Builds a palette instance from a given canvas. # @param canvas [ChunkyPNG::Canvas] The canvas to create a palette for. # @return [ChunkyPNG::Palette] The palette instance. def self.from_canvas(canvas) # Although we don't need to call .uniq.sort before initializing, because # Palette subclasses SortedSet, we get significantly better performance # by doing so. new(canvas.pixels.uniq.sort) end # Builds a palette instance from a given set of pixels. # @param pixels [Enumerable] An enumeration of pixels to create a # palette for # @return [ChunkyPNG::Palette] The palette instance. def self.from_pixels(pixels) 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 index [Integer] 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 color [ChunkyPNG::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 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 then 1 when 3..4 then 2 when 5..16 then 4 when 17..256 then 8 else nil end end end end chunky_png-1.3.8/lib/chunky_png/point.rb000066400000000000000000000110131301140243200202430ustar00rootroot00000000000000module 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; build_point_from_object(args.first) else raise ArgumentError, "Don't know how to construct a point from #{args.inspect}!" end end def self.build_point_from_object(source) case source 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 x = source[:x] || source['x'] y = source[:y] || source['y'] ChunkyPNG::Point.new(x, 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 end private_class_method :build_point_from_object # 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-1.3.8/lib/chunky_png/rmagick.rb000066400000000000000000000025751301140243200205440ustar00rootroot00000000000000require '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-1.3.8/lib/chunky_png/vector.rb000066400000000000000000000154631301140243200204310ustar00rootroot00000000000000module 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-1.3.8/lib/chunky_png/version.rb000066400000000000000000000002221301140243200205770ustar00rootroot00000000000000module ChunkyPNG # The current version of ChunkyPNG. # Set it and commit the change this before running rake release. VERSION = "1.3.8" end chunky_png-1.3.8/spec/000077500000000000000000000000001301140243200146105ustar00rootroot00000000000000chunky_png-1.3.8/spec/chunky_png/000077500000000000000000000000001301140243200167555ustar00rootroot00000000000000chunky_png-1.3.8/spec/chunky_png/canvas/000077500000000000000000000000001301140243200202305ustar00rootroot00000000000000chunky_png-1.3.8/spec/chunky_png/canvas/adam7_interlacing_spec.rb000066400000000000000000000071151301140243200251430ustar00rootroot00000000000000require '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 expect(adam7_pass_sizes(8, 8)).to eql [ [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 expect(adam7_pass_sizes(12, 12)).to eql [ [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 expect(adam7_pass_sizes(33, 47)).to eql [ [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 expect(adam7_pass_sizes(1, 1)).to eql [ [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 expect(adam7_pass_sizes(0, 0)).to eql [ [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) expect(pass_sizes.inject(0) { |sum, (w, h)| sum + (w*h) }).to eql width * height end end end describe '#adam7_multiplier_offset' do it "should get the multiplier and offset values for pass 1 correctly" do expect(adam7_multiplier_offset(0)).to eql [3, 0, 3, 0] end it "should get the multiplier and offset values for pass 2 correctly" do expect(adam7_multiplier_offset(1)).to eql [3, 4, 3, 0] end it "should get the multiplier and offset values for pass 3 correctly" do expect(adam7_multiplier_offset(2)).to eql [2, 0, 3, 4] end it "should get the multiplier and offset values for pass 4 correctly" do expect(adam7_multiplier_offset(3)).to eql [2, 2, 2, 0] end it "should get the multiplier and offset values for pass 5 correctly" do expect(adam7_multiplier_offset(4)).to eql [1, 0, 2, 2] end it "should get the multiplier and offset values for pass 6 correctly" do expect(adam7_multiplier_offset(5)).to eql [1, 1, 1, 0] end it "should get the multiplier and offset values for pass 7 correctly" do expect(adam7_multiplier_offset(6)).to eql [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) } expect(canvas).to eql 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) expect(sm.pixels.length).to eql sm.width * sm.height expect(sm.pixels.uniq.length).to eql 1 expect(ChunkyPNG::Color.r(sm[0,0])).to eql pass * 10 end end end end chunky_png-1.3.8/spec/chunky_png/canvas/data_url_exporting_spec.rb000066400000000000000000000011321301140243200254560ustar00rootroot00000000000000require '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)]) expect(canvas.to_data_url).to eql "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACAgMAAAAP2OW3AAAADFBMVEUBAgMEAwIFBgcIBwazgAAdAAAABHRSTlMEAQgFhYDlfQAAAAxJREFUeJxjUmAKAAAAwAB1GNhIEwAAAABJRU5ErkJggg==" end end end chunky_png-1.3.8/spec/chunky_png/canvas/data_url_importing_spec.rb000066400000000000000000000007641301140243200254610ustar00rootroot00000000000000require '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 expect(ChunkyPNG::Canvas.from_data_url(data_url)).to eql reference_canvas('operations') end it "should raise an exception if the string is not a proper data URL" do expect { ChunkyPNG::Canvas.from_data_url('whatever') }.to raise_error(ChunkyPNG::SignatureMismatch) end end end chunky_png-1.3.8/spec/chunky_png/canvas/drawing_spec.rb000066400000000000000000000154451301140243200232330ustar00rootroot00000000000000require '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)) expect(subject[0, 0]).to eql ChunkyPNG::Color(150, 150, 150) end it "should return the composed color" do expect(subject.compose_pixel(0, 0, ChunkyPNG::Color.rgba(100, 150, 200, 128))).to eql ChunkyPNG::Color.rgb(150, 150, 150) end it "should do nothing when the coordinates are out of bounds" do expect(subject.compose_pixel(1, -1, :black)).to be_nil expect { subject.compose_pixel(1, -1, :black) }.to_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)) expect(canvas).to eql 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') expect(canvas.pixels).to eql [ChunkyPNG::Color::BLACK, ChunkyPNG::Color::WHITE] end it "should return itself to allow chaining" do canvas = ChunkyPNG::Canvas.new(16, 16, ChunkyPNG::Color::WHITE) expect(canvas.line(1, 1, 10, 10, :black)).to 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)) expect(subject).to eql reference_canvas('rect') end it "should return itself to allow chaining" do expect(subject.rect(1, 1, 10, 10)).to 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) expect(subject[0, 0]).to eql 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) expect(subject[0, 0]).to eql 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')) expect(subject).to eql 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)) expect(subject).to eql reference_canvas('partial_circles') end it "should return itself to allow chaining" do expect(subject.circle(10, 10, 5, :red)).to 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)) expect(subject).to eql 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)) expect(subject).to eql 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)) expect(subject).to eql 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)) expect(subject).to eql reference_canvas('polygon_filled_horizontal') end it "should return itself to allow chaining" do expect(subject.polygon('(2,2) (20,5) (5,20)')).to 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') expect(subject[3, 20]).to eql 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') expect(subject[20, 20]).to eql 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) expect(subject[3, 20]).to eql ChunkyPNG::Color(:green) end it "should draw a three point bezier curve" do expect(subject.bezier_curve('1,23 12,10 23,23')).to eql reference_canvas('bezier_three_point') end it "should draw a three point bezier curve flipped" do expect(subject.bezier_curve('1,1 12,15 23,1')).to eql reference_canvas('bezier_three_point_flipped') end it "should draw a four point bezier curve" do expect(subject.bezier_curve('1,23 1,5 22,5 22,23')).to eql reference_canvas('bezier_four_point') end it "should draw a four point bezier curve flipped" do expect(subject.bezier_curve('1,1 1,19 22,19 22,1')).to eql reference_canvas('bezier_four_point_flipped') end it "should draw a four point bezier curve with a shape of an s" do expect(subject.bezier_curve('1,23 1,5 22,23 22,5')).to eql reference_canvas('bezier_four_point_s') end it "should draw a five point bezier curve" do expect(subject.bezier_curve('10,23 1,10 12,5 23,10 14,23')).to eql reference_canvas('bezier_five_point') end it "should draw a six point bezier curve" do expect(subject.bezier_curve('1,23 4,15 8,20 2,2 23,15 23,1')).to eql reference_canvas('bezier_six_point') end end end chunky_png-1.3.8/spec/chunky_png/canvas/masking_spec.rb000066400000000000000000000032211301140243200232160ustar00rootroot00000000000000require '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) expect(subject).to eql 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) expect(base).to eql reference_canvas('clock_base') expect(mask).to eql 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) expect(mask.palette.opaque_palette.size).to eql 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) expect(@mask).to eql reference_canvas('clock_mask_updated') end it "should still only have one opaque color" do @mask.change_mask_color!(@new_color) expect(@mask.palette.opaque_palette.size).to eql 1 end it "should raise an exception when the mask image has more than once color" do not_a_mask = reference_canvas('operations') expect { not_a_mask.change_mask_color!(@new_color) }.to raise_error(ChunkyPNG::ExpectationFailed) end end end chunky_png-1.3.8/spec/chunky_png/canvas/operations_spec.rb000066400000000000000000000311711301140243200237550ustar00rootroot00000000000000require '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 expect(subject.grayscale).to eql reference_canvas('operations_grayscale') end it "should not adjust the current image" do expect { subject.grayscale }.to_not change { subject.pixels } end end describe '#grayscale!' do it "should return itself" do expect(subject.grayscale!).to equal(subject) end it "should convert the image correctly" do subject.grayscale! expect(subject).to eql reference_canvas('operations_grayscale') end end describe '#crop' do it "should crop the right pixels from the original canvas" do expect(subject.crop(10, 5, 4, 8)).to eql 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 expect { subject.crop(10, 5, 4, 8) }.to_not change { subject.pixels } end it "should raise an exception when the cropped image falls outside the oiginal image" do expect { subject.crop(16, 16, 2, 2) }.to raise_error(ChunkyPNG::OutOfBounds) end end describe '#crop!' do context 'when cropping both width and height' do let(:crop_opts) { [10, 5, 4, 8] } it "should crop the right pixels from the original canvas" do subject.crop!(*crop_opts) expect(subject).to eql reference_canvas('cropped') end it "should have a new width and height" do expect { subject.crop!(*crop_opts) }.to change { subject.dimension }. from(ChunkyPNG::Dimension('16x16')).to(ChunkyPNG::Dimension('4x8')) end it "should return itself" do expect(subject.crop!(*crop_opts)).to equal(subject) end end context "when cropping just the height" do let(:crop_opts) { [0, 5, 16, 8] } it "should crop the right pixels from the original canvas" do subject.crop!(*crop_opts) expect(subject).to eql reference_canvas('cropped_height') end it "should have a new width and height" do expect { subject.crop!(*crop_opts) }.to change { subject.dimension }. from(ChunkyPNG::Dimension('16x16')).to(ChunkyPNG::Dimension('16x8')) end it "should return itself" do expect(subject.crop!(*crop_opts)).to equal(subject) end end context "when the cropped image falls outside the original image" do it "should raise an exception" do expect { subject.crop!(16, 16, 2, 2) }.to raise_error(ChunkyPNG::OutOfBounds) end end end describe '#compose' do it "should compose pixels correctly" do subcanvas = ChunkyPNG::Canvas.new(4, 8, ChunkyPNG::Color.rgba(0, 0, 0, 75)) expect(subject.compose(subcanvas, 8, 4)).to eql reference_canvas('composited') end it "should leave the original intact" do subject.compose(ChunkyPNG::Canvas.new(1,1)) expect(subject).to eql 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 expect { subject.compose(ChunkyPNG::Canvas.new(1,1), 16, 16) }.to 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) expect(subject).to eql reference_canvas('composited') end it "should return itself" do expect(subject.compose!(ChunkyPNG::Canvas.new(1,1))).to 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) expect(base).to eql reference_canvas('clock_updated') end it "should raise an exception when the pixels to compose fall outside the image" do expect { subject.compose!(ChunkyPNG::Canvas.new(1,1), 16, 16) }.to 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)) expect(subject.replace(subcanvas, 5, 4)).to eql 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)) expect(subject).to eql reference_canvas('operations') end it "should raise an exception when the pixels to replace fall outside the image" do expect { subject.replace(ChunkyPNG::Canvas.new(1,1), 16, 16) }.to 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) expect(subject).to eql reference_canvas('replaced') end it "should return itself" do expect(subject.replace!(ChunkyPNG::Canvas.new(1,1))).to equal(subject) end it "should raise an exception when the pixels to replace fall outside the image" do expect { subject.replace!(ChunkyPNG::Canvas.new(1,1), 16, 16) }.to 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! expect(subject).to eql ChunkyPNG::Canvas.new(2, 3, [5, 6, 3, 4, 1, 2]) end it "should return itself" do expect(subject.flip_horizontally!).to equal(subject) end end describe '#flip_horizontally' do it "should flip the pixels horizontally" do expect(subject.flip_horizontally).to eql 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 expect(subject.flip_horizontally.flip_horizontally).to eql subject end end describe '#flip_vertically!' do it "should flip the pixels vertically" do subject.flip_vertically! expect(subject).to eql ChunkyPNG::Canvas.new(2, 3, [2, 1, 4, 3, 6, 5]) end it "should return itself" do expect(subject.flip_horizontally!).to equal(subject) end end describe '#flip_vertically' do it "should flip the pixels vertically" do expect(subject.flip_vertically).to eql 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 expect(subject.flip_vertically.flip_vertically).to eql subject end end describe '#rotate_left' do it "should rotate the pixels 90 degrees counter-clockwise" do expect(subject.rotate_left).to eql 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 expect { subject.rotate_left }.to_not change { subject.dimension } end it "it should rotate 180 degrees when applied twice" do expect(subject.rotate_left.rotate_left).to eql subject.rotate_180 end it "it should rotate right when applied three times" do expect(subject.rotate_left.rotate_left.rotate_left).to eql subject.rotate_right end it "should return itself when applied four times" do expect(subject.rotate_left.rotate_left.rotate_left.rotate_left).to eql subject end end describe '#rotate_left!' do it "should rotate the pixels 90 degrees clockwise" do subject.rotate_left! expect(subject).to eql ChunkyPNG::Canvas.new(3, 2, [2, 4, 6, 1, 3, 5] ) end it "should return itself" do expect(subject.rotate_left!).to equal(subject) end it "should change the image dimensions" do expect { subject.rotate_left! }.to 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 expect(subject.rotate_right).to eql 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 expect { subject.rotate_right }.to_not change { subject.dimension } end it "it should rotate 180 degrees when applied twice" do expect(subject.rotate_right.rotate_right).to eql subject.rotate_180 end it "it should rotate left when applied three times" do expect(subject.rotate_right.rotate_right.rotate_right).to eql subject.rotate_left end it "should return itself when applied four times" do expect(subject.rotate_right.rotate_right.rotate_right.rotate_right).to eql subject end end describe '#rotate_right!' do it "should rotate the pixels 90 degrees clockwise" do subject.rotate_right! expect(subject).to eql ChunkyPNG::Canvas.new(3, 2, [5, 3, 1, 6, 4, 2] ) end it "should return itself" do expect(subject.rotate_right!).to equal(subject) end it "should change the image dimensions" do expect { subject.rotate_right! }.to 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 expect(subject.rotate_180).to eql 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 expect(subject.rotate_180.rotate_180).to eql subject end end describe '#rotate_180!' do it "should rotate the pixels 180 degrees" do subject.rotate_180! expect(subject).to eql ChunkyPNG::Canvas.new(2, 3, [6, 5, 4, 3, 2, 1]) end it "should return itself" do expect(subject.rotate_180!).to equal(subject) end end end describe ChunkyPNG::Canvas::Operations do subject { ChunkyPNG::Canvas.new(4, 4).rect(1, 1, 2, 2, 255, 255) } describe "#trim" do it "should trim the border" do expect(subject.trim).to eql ChunkyPNG::Canvas.new(2, 2, 255) end it "should not return itself" do subject.trim.should_not equal(subject) end it "should be able to fail to trim a specified color" do expect { subject.trim(ChunkyPNG::Color::BLACK) }.to_not change { subject.pixels } end it "should be the same after trimming an added border" do expect(subject.border(2).trim).to eql subject end end describe "#trim!" do it "should trim the border" do subject.trim! expect(subject).to eql ChunkyPNG::Canvas.new(2, 2, 255) end it "should return itself" do expect(subject.trim!).to equal(subject) end it "should change the image dimensions" do expect { subject.trim! }.to change { subject.dimension }. from(ChunkyPNG::Dimension('4x4')).to(ChunkyPNG::Dimension('2x2')) end end end describe ChunkyPNG::Canvas::Operations do subject { ChunkyPNG::Canvas.new(4, 4) } describe "#border" do it "should add the border" do expect(subject.border(2)).to eql reference_canvas('operations_border') end it "should not return itself" do subject.border(1).should_not equal(subject) end it "should retain transparency" do expect(ChunkyPNG::Canvas.new(1, 1).border(1).pixels).to include(0) end end describe "#border!" do it "should add the border" do subject.border!(2) expect(subject).to eql reference_canvas('operations_border') end it "should return itself" do expect(subject.border!(1)).to equal(subject) end it "should retain transparency" do subject.border!(1) expect(subject.pixels).to include(0) end it "should change the image dimensions" do expect { subject.border!(1) }.to change { subject.dimension }. from(ChunkyPNG::Dimension('4x4')).to(ChunkyPNG::Dimension('6x6')) end end end chunky_png-1.3.8/spec/chunky_png/canvas/png_decoding_spec.rb000066400000000000000000000104371301140243200242140ustar00rootroot00000000000000require '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) expect(stream.unpack('@1C*')).to eql [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) expect(stream.unpack('@1C*')).to eql [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) expect(stream.unpack('@1C*')).to eql [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) expect(stream.unpack('@1C*')).to eql [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) expect(stream.unpack('@11C9')).to eql [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) expect(stream.unpack('@14C12')).to eql [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) expect(stream.unpack('@14C12')).to eql [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 expect(decode_png_extract_4bit_value('10010110'.to_i(2), 0)).to eql '1001'.to_i(2) end it "should extract the low bits successfully" do expect(decode_png_extract_4bit_value('10010110'.to_i(2), 17)).to eql '0110'.to_i(2) end end describe '#decode_png_extract_2bit_value' do it "should extract the first 2 bits successfully" do expect(decode_png_extract_2bit_value('10010110'.to_i(2), 0)).to eql '10'.to_i(2) end it "should extract the second 2 bits successfully" do expect(decode_png_extract_2bit_value('10010110'.to_i(2), 5)).to eql '01'.to_i(2) end it "should extract the third 2 bits successfully" do expect(decode_png_extract_2bit_value('10010110'.to_i(2), 2)).to eql '01'.to_i(2) end it "should extract the low two bits successfully" do expect(decode_png_extract_2bit_value('10010110'.to_i(2), 7)).to eql '10'.to_i(2) end end describe '#decode_png_extract_1bit_value' do it "should extract all separate bits correctly" do expect(decode_png_extract_1bit_value('10010110'.to_i(2), 0)).to eql 1 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 1)).to eql 0 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 2)).to eql 0 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 3)).to eql 1 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 4)).to eql 0 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 5)).to eql 1 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 6)).to eql 1 expect(decode_png_extract_1bit_value('10010110'.to_i(2), 7)).to eql 0 end end end chunky_png-1.3.8/spec/chunky_png/canvas/png_encoding_spec.rb000066400000000000000000000275071301140243200242340ustar00rootroot00000000000000require '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) expect(ds.header_chunk.color).to eql color_mode expect(ChunkyPNG::Canvas.from_datastream(ds)).to eql 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) expect(ds.header_chunk.color).to eql ChunkyPNG::COLOR_INDEXED expect(ds.header_chunk.depth).to eql 1 expect(@canvas).to eql 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) expect(ds.header_chunk.color).to eql ChunkyPNG::COLOR_INDEXED expect(ds.header_chunk.depth).to eql 2 expect(@canvas).to eql 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) expect(ds.header_chunk.color).to eql ChunkyPNG::COLOR_INDEXED expect(ds.header_chunk.depth).to eql 4 expect(@canvas).to eql 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) expect(ds.header_chunk.color).to eql ChunkyPNG::COLOR_INDEXED expect(ds.header_chunk.depth).to eql 8 expect(@canvas).to eql 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)) expect(ds.header_chunk.color).to eql ChunkyPNG::COLOR_INDEXED expect(ds.header_chunk.depth).to eql 4 expect(@canvas).to eql 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) expect(ds.header_chunk.interlace).to eql ChunkyPNG::INTERLACING_ADAM7 expect(ChunkyPNG::Canvas.from_datastream(ds)).to eql input_canvas end it "should save an image using the normal routine correctly" do canvas = reference_canvas('operations') expect(Zlib::Deflate).to 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') expect(canvas).to_not receive(:encode_png_str_scanline_none) expect(canvas).to_not receive(:encode_png_str_scanline_sub) expect(canvas).to_not receive(:encode_png_str_scanline_up) expect(canvas).to_not receive(:encode_png_str_scanline_average) expect(canvas).to_not receive(:encode_png_str_scanline_paeth) expect(Zlib::Deflate).to 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') expect(canvas).to_not receive(:encode_png_str_scanline_none) expect(canvas).to_not receive(:encode_png_str_scanline_sub) expect(canvas).to_not receive(:encode_png_str_scanline_up) expect(canvas).to_not receive(:encode_png_str_scanline_average) expect(canvas).to_not receive(:encode_png_str_scanline_paeth) expect(Zlib::Deflate).to 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') expect(canvas).to receive(:encode_png_str_scanline_paeth).exactly(canvas.height).times expect(Zlib::Deflate).to 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)) expect(ds.header_chunk.color).to eql ChunkyPNG::COLOR_GRAYSCALE expect(ds.header_chunk.depth).to eql 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) expect(stream).to eql 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) expect(stream).to eql 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) expect(stream).to eql 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) expect(stream).to eql 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) expect(stream).to eql 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) expect(stream).to eql 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 expect { @canvas.encode_png_pixelstream(ChunkyPNG::COLOR_INDEXED, 1, ChunkyPNG::INTERLACING_NONE, ChunkyPNG::FILTER_NONE) }.to 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) expect(stream).to eql 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) expect(stream).to eql 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) expect(stream).to eql 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) expect(stream.unpack('C*')).to eql [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) expect(stream.unpack('@10C10')).to eql [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) expect(stream.unpack('@0C10')).to eql [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) expect(stream.unpack('@10C10')).to eql [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) expect(stream.unpack('@0C10')).to eql [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) expect(stream.unpack('@13C13')).to eql [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) expect(stream.unpack('@0C13')).to eql [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) expect(stream.unpack('@13C13')).to eql [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) expect(stream.unpack('@0C13')).to eql [ChunkyPNG::FILTER_PAETH, 10, 20, 30, 30, 30, 30, 30, 30, 20, 30, 30, 40] end end end chunky_png-1.3.8/spec/chunky_png/canvas/resampling_spec.rb000066400000000000000000000105351301140243200237340ustar00rootroot00000000000000require '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]) expect(canvas.resample_nearest_neighbor(1, 1)).to eql 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]) expect(canvas.resample_nearest_neighbor(4, 4)).to eql 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 expect(subject.resample_nearest_neighbor(45, 45)).to eql reference_canvas('clock_nn_xup_yup') end it "should downscale both axis of the image" do expect(subject.resample_nearest_neighbor(12, 12)).to eql reference_canvas('clock_nn_xdown_ydown') end it "should downscale the x-axis and upscale the y-axis of the image" do expect(subject.resample_nearest_neighbor(20, 50)).to eql 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 expect { subject.resample_nearest_neighbor(1, 1) }.to_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) expect(subject).to eql reference_canvas('clock_nn_xup_yup') end it "should downscale both axis of the image" do subject.resample_nearest_neighbor!(12, 12) expect(subject).to eql 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) expect(subject).to eql reference_canvas('clock_nn_xdown_yup') end it "should return itself" do expect(subject.resample_nearest_neighbor!(1, 1)).to equal(subject) end it "should change the original image's dimensions" do expect { subject.resample_nearest_neighbor!(1, 1) }.to 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]) expect(canvas.resample_bilinear(1, 1)).to eql 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]) expect(canvas.resample_bilinear(4, 4)).to eql 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 expect(subject.resample_bilinear(45, 45)).to eql reference_canvas('clock_bl_xup_yup') end it "should downscale both axis of the image" do expect(subject.resample_bilinear(12, 12)).to eql reference_canvas('clock_bl_xdown_ydown') end it "should downscale the x-axis and upscale the y-axis of the image" do expect(subject.resample_bilinear(20, 50)).to eql 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 expect { subject.resample_bilinear(1, 1) }.to_not change { subject.dimension } end end describe '#resample_bilinear!' do it "should upscale both axis of the image" do subject.resample_bilinear!(45, 45) expect(subject).to eql reference_canvas('clock_bl_xup_yup') end it "should downscale both axis of the image" do subject.resample_bilinear!(12, 12) expect(subject).to eql 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) expect(subject).to eql reference_canvas('clock_bl_xdown_yup') end it "should return itself" do expect(subject.resample_bilinear!(1, 1)).to equal(subject) end it "should change the original image's dimensions" do expect { subject.resample_bilinear!(1, 1) }.to change { subject.dimension }.to(ChunkyPNG::Dimension('1x1')) end end end chunky_png-1.3.8/spec/chunky_png/canvas/stream_exporting_spec.rb000066400000000000000000000053551301140243200251710ustar00rootroot00000000000000require '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)]) expect(canvas.to_rgba_stream).to eql [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 expect(reference_canvas('pixelstream_reference').to_rgba_stream).to eql 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)]) expect(canvas.to_rgb_stream).to eql [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 expect(reference_canvas('pixelstream_reference').to_rgb_stream).to eql 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)]) expect(canvas.to_grayscale_stream).to eql [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)]) expect(canvas.to_grayscale_stream).to eql [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) expect(reference_canvas('pixelstream_reference').to_alpha_channel_stream).to eql 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)]) expect(canvas.to_alpha_channel_stream).to eql [4,8,1,5].pack('C4') end end end chunky_png-1.3.8/spec/chunky_png/canvas/stream_importing_spec.rb000066400000000000000000000020021301140243200251440ustar00rootroot00000000000000require '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) expect(matrix).to eql reference_canvas('pixelstream_reference') end end end describe '.from_bgr_stream' do it "should load an image correctly from a datastream" do File.open(resource_file('pixelstream.bgr')) do |stream| matrix = ChunkyPNG::Canvas.from_bgr_stream(240, 180, stream) expect(matrix).to eql 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) expect(matrix).to eql reference_canvas('pixelstream_reference') end end end end chunky_png-1.3.8/spec/chunky_png/canvas_spec.rb000066400000000000000000000207041301140243200215720ustar00rootroot00000000000000require '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') expect(canvas[1, 0]).to eql ChunkyPNG::Color.parse('red @ 0.8') end it "should raise an error if the color value is not understood" do expect { ChunkyPNG::Canvas.new(2, 2, :nonsense) }.to raise_error(ArgumentError) end it "should accept an array as initial pixel values" do canvas = ChunkyPNG::Canvas.new(2, 2, [1,2,3,4]) expect(canvas[0, 0]).to eql 1 expect(canvas[1, 0]).to eql 2 expect(canvas[0, 1]).to eql 3 expect(canvas[1, 1]).to eql 4 end it "should raise an ArgumentError if the initial array does not have the correct number of elements" do expect { ChunkyPNG::Canvas.new(2, 2, [1,2,3]) }.to raise_error(ArgumentError) expect { ChunkyPNG::Canvas.new(2, 2, [1,2,3,4,5]) }.to raise_error(ArgumentError) end it "should use a transparent background by default" do canvas = ChunkyPNG::Canvas.new(1, 1) expect(canvas[0,0]).to eql ChunkyPNG::Color::TRANSPARENT end end describe '#dimension' do it "should return the dimensions as a Dimension instance" do expect(subject.dimension).to eql ChunkyPNG::Dimension('1x1') end end describe '#area' do it "should return the dimensions as two-item array" do expect(subject.area).to eql ChunkyPNG::Dimension('1x1').area end end describe '#include?' do it "should return true if the coordinates are within bounds, false otherwise" do expect(subject.include_xy?( 0, 0)).to eql true expect(subject.include_xy?(-1, 0)).to eql false expect(subject.include_xy?( 1, 0)).to eql false expect(subject.include_xy?( 0, -1)).to eql false expect(subject.include_xy?( 0, 1)).to eql false expect(subject.include_xy?(-1, -1)).to eql false expect(subject.include_xy?(-1, 1)).to eql false expect(subject.include_xy?( 1, -1)).to eql false expect(subject.include_xy?( 1, 1)).to eql false end it "should accept strings, arrays, hashes and points as well" do expect(subject).to include('0, 0') subject.should_not include('0, 1') expect(subject).to include([0, 0]) subject.should_not include([0, 1]) expect(subject).to include(:y => 0, :x => 0) subject.should_not include(:y => 1, :x => 0) expect(subject).to 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 expect(subject.include_x?( 0)).to eql true expect(subject.include_x?(-1)).to eql false expect(subject.include_x?( 1)).to eql false end end describe '#include_y?' do it "should return true if the y-coordinate is within bounds, false otherwise" do expect(subject.include_y?( 0)).to eql true expect(subject.include_y?(-1)).to eql false expect(subject.include_y?( 1)).to eql false end end describe '#assert_xy!' do it "should not raise an exception if the coordinates are within bounds" do expect(subject).to receive(:include_xy?).with(0, 0).and_return(true) expect { subject.send(:assert_xy!, 0, 0) }.to_not raise_error end it "should raise an exception if the coordinates are out of bounds bounds" do expect(subject).to receive(:include_xy?).with(0, -1).and_return(false) expect { subject.send(:assert_xy!, 0, -1) }.to raise_error(ChunkyPNG::OutOfBounds) end end describe '#assert_x!' do it "should not raise an exception if the x-coordinate is within bounds" do expect(subject).to receive(:include_x?).with(0).and_return(true) expect { subject.send(:assert_x!, 0) }.to_not raise_error end it "should raise an exception if the x-coordinate is out of bounds bounds" do expect(subject).to receive(:include_y?).with(-1).and_return(false) expect { subject.send(:assert_y!, -1) }.to raise_error(ChunkyPNG::OutOfBounds) end end describe '#[]' do it "should return the pixel value if the coordinates are within bounds" do expect(subject[0, 0]).to eql ChunkyPNG::Color::WHITE end it "should assert the coordinates to be within bounds" do expect(subject).to 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 expect(subject.get_pixel(0, 0)).to eql ChunkyPNG::Color::WHITE end it "should not assert nor check the coordinates" do expect(subject).to_not receive(:assert_xy!) expect(subject).to_not receive(:include_xy?) subject.get_pixel(0, 0) end end describe '#[]=' do it "should change the pixel's color value" do expect { subject[0, 0] = ChunkyPNG::Color::BLACK }.to change { subject[0, 0] }. from(ChunkyPNG::Color::WHITE).to(ChunkyPNG::Color::BLACK) end it "should assert the bounds of the image" do expect(subject).to 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 expect { subject.set_pixel(0, 0, ChunkyPNG::Color::BLACK) }.to 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 expect(subject).to_not receive(:assert_xy!) expect(subject).to_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 expect { subject.set_pixel_if_within_bounds(0, 0, ChunkyPNG::Color::BLACK) }.to change { subject[0, 0] }. from(ChunkyPNG::Color::WHITE).to(ChunkyPNG::Color::BLACK) end it "should not assert, but only check the coordinates" do expect(subject).to_not receive(:assert_xy!) expect(subject).to 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 expect(subject.set_pixel_if_within_bounds(-1, 1, ChunkyPNG::Color::BLACK)).to be_nil expect(subject[0, 0]).to eql 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 expect { @canvas.row(-1) }.to raise_error(ChunkyPNG::OutOfBounds) expect { @canvas.row(16) }.to raise_error(ChunkyPNG::OutOfBounds) end it "should return the correct pixels" do data = @canvas.row(0) expect(data.length).to eql @canvas.width expect(data).to eql [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 expect { @canvas.column(-1) }.to raise_error(ChunkyPNG::OutOfBounds) expect { @canvas.column(16) }.to raise_error(ChunkyPNG::OutOfBounds) end it "should return the correct pixels" do data = @canvas.column(0) expect(data.length).to eql @canvas.height expect(data).to eql [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 expect { subject.send(:replace_canvas!, 2, 2, [1,2,3,4]) }.to change { subject.dimension }. from(ChunkyPNG::Dimension('1x1')).to(ChunkyPNG::Dimension('2x2')) end it "should change the pixel array" do expect { subject.send(:replace_canvas!, 2, 2, [1,2,3,4]) }.to change { subject.pixels }. from([ChunkyPNG::Color('white')]).to([1,2,3,4]) end it "should return itself" do expect(subject.send(:replace_canvas!, 2, 2, [1,2,3,4])).to equal(subject) end end end chunky_png-1.3.8/spec/chunky_png/color_spec.rb000066400000000000000000000347711301140243200214460ustar00rootroot00000000000000require 'spec_helper' describe 'ChunyPNG.Color' do it 'should interpret 4 arguments as RGBA values' do expect(ChunkyPNG::Color(1, 2, 3, 4)).to eql ChunkyPNG::Color.rgba(1, 2, 3, 4) end it 'should interpret 3 arguments as RGBA values' do expect(ChunkyPNG::Color(1, 2, 3)).to eql ChunkyPNG::Color.rgb(1, 2, 3) end it 'should interpret 2 arguments as a color to parse and an opacity value' do expect(ChunkyPNG::Color('0x0a649664', 0xaa)).to eql 0x0a6496aa expect(ChunkyPNG::Color('spring green @ 0.6666', 0xff)).to eql 0x00ff7fff end it 'should interpret 1 argument as a color to parse' do expect(ChunkyPNG::Color).to 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 @red = 0xff0000ff @green = 0x00ff00ff @blue = 0x0000ffff end describe '#parse' do it 'should interpret a hex string correctly' do expect(parse('0x0a649664')).to eql ChunkyPNG::Color.from_hex('#0a649664') end it 'should interpret a color name correctly' do expect(parse(:spring_green)).to eql 0x00ff7fff expect(parse('spring green')).to eql 0x00ff7fff expect(parse('spring green @ 0.6666')).to eql 0x00ff7faa end it 'should return numbers as is' do expect(parse('12345')).to eql 12345 expect(parse(12345)).to eql 12345 end end describe '#pixel_bytesize' do it 'should return the normal amount of bytes with a bit depth of 8' do expect(pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8)).to eql 3 end it 'should return a multiple of the normal amount of bytes with a bit depth greater than 8' do expect(pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 16)).to eql 6 expect(pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 16)).to eql 8 expect(pixel_bytesize(ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 16)).to eql 4 end it 'should return 1 with a bit depth lower than 0' do expect(pixel_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 4)).to eql 1 expect(pixel_bytesize(ChunkyPNG::COLOR_INDEXED, 2)).to eql 1 expect(pixel_bytesize(ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 1)).to eql 1 end end describe '#pass_bytesize' do it 'should calculate a pass size correctly' do expect(pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 10, 10)).to eql 310 end it 'should return 0 if one of the dimensions is zero' do expect(pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 0, 10)).to eql 0 expect(pass_bytesize(ChunkyPNG::COLOR_TRUECOLOR, 8, 10, 0)).to eql 0 end end describe '#rgba' do it 'should represent pixels as the correct number' do expect(rgba(255, 255, 255, 255)).to eql @white expect(rgba( 0, 0, 0, 255)).to eql @black expect(rgba( 10, 100, 150, 255)).to eql @opaque expect(rgba( 10, 100, 150, 100)).to eql @non_opaque expect(rgba( 10, 100, 150, 0)).to eql @fully_transparent end end describe '#from_hex' do it 'should load colors correctly from hex notation' do expect(from_hex('0a649664')).to eql @non_opaque expect(from_hex('#0a649664')).to eql @non_opaque expect(from_hex('0x0a649664')).to eql @non_opaque expect(from_hex('0a6496')).to eql @opaque expect(from_hex('#0a6496')).to eql @opaque expect(from_hex('0x0a6496')).to eql @opaque expect(from_hex('abc')).to eql 0xaabbccff expect(from_hex('#abc')).to eql 0xaabbccff expect(from_hex('0xabc')).to eql 0xaabbccff end it 'should allow setting opacity explicitly' do expect(from_hex('0x0a6496', 0x64)).to eql @non_opaque expect(from_hex('#0a6496', 0x64)).to eql @non_opaque expect(from_hex('0xabc', 0xdd)).to eql 0xaabbccdd expect(from_hex('#abc', 0xdd)).to eql 0xaabbccdd end end describe '#from_hsv' do it 'should load colors correctly from an HSV triple' do # At 0 brightness, should be @black independent of hue or sat expect(from_hsv(0, 0, 0)).to eql @black expect(from_hsv(100, 1, 0)).to eql @black expect(from_hsv(100, 0.5, 0)).to eql @black # At brightness 1 and sat 0, should be @white regardless of hue expect(from_hsv(0, 0, 1)).to eql @white expect(from_hsv(100, 0, 1)).to eql @white # Converting the "pure" colors should work expect(from_hsv(0, 1, 1)).to eql @red expect(from_hsv(120, 1, 1)).to eql @green expect(from_hsv(240, 1, 1)).to eql @blue # And, finally, one random color expect(from_hsv(120, 0.5, 0.80)).to eql 0x66cc66ff # Hue 0 and hue 360 should be equivalent expect(from_hsv(0, 0.5, 0.5)).to eql from_hsv(360, 0.5, 0.5) expect(from_hsv(0, 0.5, 0.5)).to eql from_hsv(360.0, 0.5, 0.5) end it 'should optionally accept a fourth param for alpha' do expect(from_hsv(0, 1, 1, 255)).to eql @red expect(from_hsv(120, 1, 1, 255)).to eql @green expect(from_hsv(240, 1, 1, 255)).to eql @blue expect(from_hsv(0, 1, 1, 0)).to eql 0xff000000 # transparent red expect(from_hsv(120, 1, 1, 0)).to eql 0x00ff0000 # transparent green expect(from_hsv(240, 1, 1, 0)).to eql 0x0000ff00 # transparent blue end end describe '#from_hsl' do it 'should load colors correctly from an HSL triple' do # At 0 lightness, should always be black expect(from_hsl(0, 0, 0)).to eql @black expect(from_hsl(100, 0, 0)).to eql @black expect(from_hsl(54, 0.5, 0)).to eql @black # At 1 lightness, should always be white expect(from_hsl(0, 0, 1)).to eql @white expect(from_hsl(0, 0.5, 1)).to eql @white expect(from_hsl(110, 0, 1)).to eql @white # 'Pure' colors should work expect(from_hsl(0, 1, 0.5)).to eql @red expect(from_hsl(120, 1, 0.5)).to eql @green expect(from_hsl(240, 1, 0.5)).to eql @blue # Random colors from_hsl(87.27, 0.5, 0.5686) == 0x96c85aff from_hsl(271.83, 0.5399, 0.4176) == 0x6e31a4ff from_hsl(63.6, 0.5984, 0.4882) == 0xbec732ff # Hue 0 and hue 360 should be equivalent expect(from_hsl(0, 0.5, 0.5)).to eql from_hsl(360, 0.5, 0.5) expect(from_hsl(0, 0.5, 0.5)).to eql from_hsl(360.0, 0.5, 0.5) end it 'should optionally accept a fourth param for alpha' do expect(from_hsl(0, 1, 0.5, 255)).to eql @red expect(from_hsl(120, 1, 0.5, 255)).to eql @green expect(from_hsl(240, 1, 0.5, 255)).to eql @blue expect(from_hsl(0, 1, 0.5, 0)).to eql 0xff000000 # transparent red expect(from_hsl(120, 1, 0.5, 0)).to eql 0x00ff0000 # transparent green expect(from_hsl(240, 1, 0.5, 0)).to eql 0x0000ff00 # transparent blue end end describe '#html_color' do it 'should find the correct color value' do expect(html_color(:springgreen)).to eql 0x00ff7fff expect(html_color(:spring_green)).to eql 0x00ff7fff expect(html_color('springgreen')).to eql 0x00ff7fff expect(html_color('spring green')).to eql 0x00ff7fff expect(html_color('SpringGreen')).to eql 0x00ff7fff expect(html_color('SPRING_GREEN')).to eql 0x00ff7fff end it 'should set the opacity level explicitly' do expect(html_color(:springgreen, 0xff)).to eql 0x00ff7fff expect(html_color(:springgreen, 0xaa)).to eql 0x00ff7faa expect(html_color(:springgreen, 0x00)).to eql 0x00ff7f00 end it 'should set opacity levels from the color name' do expect(html_color('Spring green @ 1.0')).to eql 0x00ff7fff expect(html_color('Spring green @ 0.666')).to eql 0x00ff7faa expect(html_color('Spring green @ 0.0')).to eql 0x00ff7f00 end it 'should raise for an unkown color name' do expect { html_color(:nonsense) }.to raise_error(ArgumentError) end end describe '#opaque?' do it 'should correctly check for opaqueness' do expect(opaque?(@white)).to eql true expect(opaque?(@black)).to eql true expect(opaque?(@opaque)).to eql true expect(opaque?(@non_opaque)).to eql false expect(opaque?(@fully_transparent)).to eql false end end describe 'extraction of separate color channels' do it 'should extract components from a color correctly' do expect(r(@opaque)).to eql 10 expect(g(@opaque)).to eql 100 expect(b(@opaque)).to eql 150 expect(a(@opaque)).to eql 255 end end describe '#grayscale_teint' do it 'should calculate the correct grayscale teint' do expect(grayscale_teint(@opaque)).to eql 79 expect(grayscale_teint(@non_opaque)).to eql 79 end end describe '#to_grayscale' do it 'should use the grayscale teint for r, g and b' do gs = to_grayscale(@non_opaque) expect(r(gs)).to eql grayscale_teint(@non_opaque) expect(g(gs)).to eql grayscale_teint(@non_opaque) expect(b(gs)).to eql grayscale_teint(@non_opaque) end it 'should preserve the alpha channel' do expect(a(to_grayscale(@non_opaque))).to eql a(@non_opaque) expect(a(to_grayscale(@opaque))).to eql ChunkyPNG::Color::MAX end end describe '#to_hex' do it 'should represent colors correcly using hex notation' do expect(to_hex(@white)).to eql '#ffffffff' expect(to_hex(@black)).to eql '#000000ff' expect(to_hex(@opaque)).to eql '#0a6496ff' expect(to_hex(@non_opaque)).to eql '#0a649664' expect(to_hex(@fully_transparent)).to eql '#0a649600' end it 'should represent colors correcly using hex notation without alpha channel' do expect(to_hex(@white, false)).to eql '#ffffff' expect(to_hex(@black, false)).to eql '#000000' expect(to_hex(@opaque, false)).to eql '#0a6496' expect(to_hex(@non_opaque, false)).to eql '#0a6496' expect(to_hex(@fully_transparent, false)).to eql '#0a6496' end end describe '#to_hsv' do it 'should return a [hue, saturation, value] array' do expect(to_hsv(@white)).to eql [0, 0.0, 1.0] expect(to_hsv(@black)).to eql [0, 0.0, 0.0] expect(to_hsv(@red)).to eql [0, 1.0, 1.0] expect(to_hsv(@blue)).to eql [240, 1.0, 1.0] expect(to_hsv(@green)).to eql [120, 1.0, 1.0] expect(to_hsv(0x805440ff)[0]).to be_within(1).of(19) expect(to_hsv(0x805440ff)[1]).to be_within(0.01).of(0.5) expect(to_hsv(0x805440ff)[2]).to be_within(0.01).of(0.5) end it 'should optionally include the alpha channel' do expect(to_hsv(@white, true)).to eql [0, 0.0, 1.0, 255] expect(to_hsv(@red, true)).to eql [0, 1.0, 1.0, 255] expect(to_hsv(@blue, true)).to eql [240, 1.0, 1.0, 255] expect(to_hsv(@green, true)).to eql [120, 1.0, 1.0, 255] expect(to_hsv(@opaque, true)[3]).to eql 255 expect(to_hsv(@fully_transparent, true)[3]).to eql 0 end end describe '#to_hsl' do it 'should return a [hue, saturation, lightness] array' do expect(to_hsl(@white)).to eql [0, 0.0, 1.0] expect(to_hsl(@black)).to eql [0, 0.0, 0.0] expect(to_hsl(@red)).to eql [0, 1.0, 0.5] expect(to_hsl(@blue)).to eql [240, 1.0, 0.5] expect(to_hsl(@green)).to eql [120, 1.0, 0.5] end it 'should optionally include the alpha channel in the returned array' do expect(to_hsl(@white, true)).to eql [0, 0.0, 1.0, 255] expect(to_hsl(@black, true)).to eql [0, 0.0, 0.0, 255] expect(to_hsl(@red, true)).to eql [0, 1.0, 0.5, 255] expect(to_hsl(@blue, true)).to eql [240, 1.0, 0.5, 255] expect(to_hsl(@green, true)).to eql [120, 1.0, 0.5, 255] expect(to_hsl(@opaque, true)[3]).to eql 255 expect(to_hsl(@fully_transparent, true)[3]).to eql 0 end end describe 'conversion to other formats' do it 'should convert the individual color values back correctly' do expect(to_truecolor_bytes(@opaque)).to eql [10, 100, 150] expect(to_truecolor_alpha_bytes(@non_opaque)).to eql [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 expect(compose(@non_opaque, @fully_transparent)).to eql @non_opaque end it 'should use the foregorund color as is when an opaque color is given as foreground color' do expect(compose(@opaque, @white)).to eql @opaque end it 'should use the background color as is when a fully transparent pixel is given as foreground color' do expect(compose(@fully_transparent, @white)).to eql @white end it 'should compose pixels correctly with both algorithms' do expect(compose_quick(@non_opaque, @white)).to eql 0x9fc2d6ff expect(compose_precise(@non_opaque, @white)).to eql 0x9fc2d6ff end end describe '#decompose_alpha' do it 'should decompose the alpha channel correctly' do expect(decompose_alpha(0x9fc2d6ff, @opaque, @white)).to eql 0x00000064 end it 'should return fully transparent if the background channel matches the resulting color' do expect(decompose_alpha(0xabcdefff, 0xff000000, 0xabcdefff)).to eql 0x00 end it 'should return fully opaque if the background channel matches the mask color' do expect(decompose_alpha(0xff000000, 0xabcdefff, 0xabcdefff)).to eql 0xff end it 'should return fully opaque if the resulting color matches the mask color' do expect(decompose_alpha(0xabcdefff, 0xabcdefff, 0xffffffff)).to eql 255 end end describe '#blend' do it 'should blend colors correctly' do expect(blend(@opaque, @black)).to eql 0x05324bff end it 'should not matter what color is used as foreground, and what as background' do expect(blend(@opaque, @black)).to eql blend(@black, @opaque) end end describe '#euclidean_distance_rgba' do subject { euclidean_distance_rgba(color_a, color_b) } context 'with white and black' do let(:color_a) { @white } let(:color_b) { @black } it { should == Math.sqrt(195_075) } # sqrt(255^2 * 3) end context 'with black and white' do let(:color_a) { @black } let(:color_b) { @white } it { should == Math.sqrt(195_075) } # sqrt(255^2 * 3) end context 'with the same colors' do let(:color_a) { @white } let(:color_b) { @white } it { should == 0 } end end end chunky_png-1.3.8/spec/chunky_png/datastream_spec.rb000066400000000000000000000046311301140243200224450ustar00rootroot00000000000000require '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') expect { ChunkyPNG::Datastream.from_file(filename) }.to raise_error(ChunkyPNG::SignatureMismatch) end it "should raise an error if the CRC of a chunk is incorrect" do filename = resource_file('damaged_chunk.png') expect { ChunkyPNG::Datastream.from_file(filename) }.to raise_error(ChunkyPNG::CRCMismatch) end it "should raise an error for a stream that is too short" do stream = StringIO.new expect { ChunkyPNG::Datastream.from_io(stream) }.to raise_error(ChunkyPNG::SignatureMismatch) end it "should read a stream with trailing data without failing" do filename = resource_file('trailing_bytes_after_iend_chunk.png') image = ChunkyPNG::Datastream.from_file(filename) expect(image).to be_instance_of(ChunkyPNG::Datastream) 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) expect(ds.metadata['Title']).to eql 'My amazing icon!' expect(ds.metadata['Author']).to eql "Willem van Bergen" end it "should load compressed zTXt chunks correctly" do filename = resource_file('ztxt_chunk.png') ds = ChunkyPNG::Datastream.from_file(filename) expect(ds.metadata['Title']).to eql 'PngSuite' expect(ds.metadata['Copyright']).to eql "Copyright Willem van Schaik, Singapore 1995-96" end end describe '#physical_chunk' do it 'should read and write pHYs chunks correctly' do filename = resource_file('clock.png') ds = ChunkyPNG::Datastream.from_file(filename) expect(ds.physical_chunk.unit).to eql :meters expect(ds.physical_chunk.dpix.round).to eql 72 expect(ds.physical_chunk.dpiy.round).to eql 72 ds = ChunkyPNG::Datastream.from_blob(ds.to_blob) expect(ds.physical_chunk).not_to be_nil end it 'should raise ChunkyPNG::UnitsUnknown if we request dpi but the units are unknown' do physical_chunk = ChunkyPNG::Chunk::Physical.new(2835, 2835, :unknown) expect{physical_chunk.dpix}.to raise_error(ChunkyPNG::UnitsUnknown) expect{physical_chunk.dpiy}.to raise_error(ChunkyPNG::UnitsUnknown) end end end chunky_png-1.3.8/spec/chunky_png/dimension_spec.rb000066400000000000000000000030001301140243200222720ustar00rootroot00000000000000require '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 expect(subject.area).to eql 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 expect(ChunkyPNG::Dimension([1, 2])).to eql subject expect(ChunkyPNG::Dimension(['1', '2'])).to eql subject end it "should create a dimension from a hash with x and y keys" do expect(ChunkyPNG::Dimension(:width => 1, :height => 2)).to eql subject expect(ChunkyPNG::Dimension('width' => '1', 'height' => '2')).to eql 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 = Struct.new(:width, :height).new(1, 2) expect(ChunkyPNG::Dimension(mock_object)).to eql subject end it "should raise an exception if the input is not understood" do expect { ChunkyPNG::Dimension(Object.new) }.to raise_error(ArgumentError) expect { ChunkyPNG::Dimension(1, 2, 3) }.to raise_error(ArgumentError) end end chunky_png-1.3.8/spec/chunky_png/image_spec.rb000066400000000000000000000017461301140243200214060ustar00rootroot00000000000000require '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')) expect(image.metadata['Title']).to eql 'My amazing icon!' expect(image.metadata['Author']).to eql '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 expect(metadata['Title']).to eql 'My amazing icon!' expect(metadata['Author']).to eql 'Willem van Bergen' end it "should load empty images correctly" do expect do ChunkyPNG::Image.from_file(resource_file('empty.png')) end.to_not raise_error end end end chunky_png-1.3.8/spec/chunky_png/point_spec.rb000066400000000000000000000053341301140243200214520ustar00rootroot00000000000000require '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 expect((subject <=> ChunkyPNG::Point.new(1, 2))).to eql 0 end it "should return -1 if the y coordinate is smaller than the other one" do expect((subject <=> ChunkyPNG::Point.new(1, 3))).to eql -1 expect((subject <=> ChunkyPNG::Point.new(0, 3))).to eql -1 # x doesn't matter expect((subject <=> ChunkyPNG::Point.new(2, 3))).to eql -1 # x doesn't matter end it "should return 1 if the y coordinate is larger than the other one" do expect((subject <=> ChunkyPNG::Point.new(1, 0))).to eql 1 expect((subject <=> ChunkyPNG::Point.new(0, 0))).to eql 1 # x doesn't matter expect((subject <=> ChunkyPNG::Point.new(2, 0))).to eql 1 # x doesn't matter end it "should return -1 if the x coordinate is smaller and y is the same" do expect((subject <=> ChunkyPNG::Point.new(2, 2))).to eql -1 end it "should return 1 if the x coordinate is larger and y is the same" do expect((subject <=> ChunkyPNG::Point.new(0, 2))).to eql 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 expect(ChunkyPNG::Point([1, 2])).to eql subject expect(ChunkyPNG::Point(['1', '2'])).to eql subject end it "should create a point from a hash with x and y keys" do expect(ChunkyPNG::Point(:x => 1, :y => 2)).to eql subject expect(ChunkyPNG::Point('x' => '1', 'y' => '2')).to eql subject end it "should create a point from a ChunkyPNG::Dimension object" do dimension = ChunkyPNG::Dimension.new(1, 2) ChunkyPNG::Point(dimension) == 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 = Struct.new(:x, :y).new(1, 2) expect(ChunkyPNG::Point(mock_object)).to eql subject end it "should raise an exception if the input is not understood" do expect { ChunkyPNG::Point(Object.new) }.to raise_error(ArgumentError) expect { ChunkyPNG::Point(1, 2, 3) }.to raise_error(ArgumentError) end end chunky_png-1.3.8/spec/chunky_png/rmagick_spec.rb000066400000000000000000000012231301140243200217270ustar00rootroot00000000000000require '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) expect(canvas).to eql 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' expect(canvas).to eql ChunkyPNG::Canvas.from_blob(image.to_blob) end end rescue LoadError => e # skipping RMagick tests end chunky_png-1.3.8/spec/chunky_png/vector_spec.rb000066400000000000000000000065661301140243200216330ustar00rootroot00000000000000require '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) } describe '#length' do it "shopuld have 3 items" do expect(subject.length).to eql(3) end end describe '#x_range' do it "should get the right range of x values" do expect(subject.x_range).to eql(1..4) end it "should find the minimum x-coordinate" do expect(subject.min_x).to eql(1) end it "should find the maximum x-coordinate" do expect(subject.max_x).to eql(4) end it "should calculate the width correctly" do expect(subject.width).to eql(4) end end describe '#y_range' do it "should get the right range of y values" do expect(subject.y_range).to eql(3..6) end it "should find the minimum x-coordinate" do expect(subject.min_y).to eql(3) end it "should find the maximum x-coordinate" do expect(subject.max_y).to eql(6) end it "should calculate the height correctly" do expect(subject.height).to eql(4) end end describe '#offset' do it "should return a ChunkyPNG::Point" do expect(subject.offset).to be_kind_of(ChunkyPNG::Point) end it "should use the mininum x and y coordinates as values for the point" do expect(subject.offset.x).to eql subject.min_x expect(subject.offset.y).to eql subject.min_y end end describe '#dimension' do it "should return a ChunkyPNG::Dimension" do expect(subject.dimension).to be_kind_of(ChunkyPNG::Dimension) end it "should use the width and height of the vector for the dimension" do expect(subject.dimension.width).to eql subject.width expect(subject.dimension.height).to eql subject.height end end describe '#edges' do it "should get three edges when closing the path" do expect(subject.edges(true).to_a).to eql [[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 expect(subject.edges(false).to_a).to eql [[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 expect(ChunkyPNG::Vector()).to eql ChunkyPNG::Vector.new([]) expect(ChunkyPNG::Vector(*[])).to eql ChunkyPNG::Vector.new([]) end it "should raise an error when an odd number of numerics is given" do expect { ChunkyPNG::Vector(1, 2, 3) }.to raise_error(ArgumentError) end it "should create a vector from a string" do expect(ChunkyPNG::Vector('(2,4) (1,2) (3,6)')).to eql example end it "should create a vector from a flat array" do expect(ChunkyPNG::Vector(2,4,1,2,3,6)).to eql example end it "should create a vector from a nested array" do expect(ChunkyPNG::Vector('(2,4)', [1, 2], :x => 3, :y => 6)).to eql example end end chunky_png-1.3.8/spec/chunky_png_spec.rb000066400000000000000000000002341301140243200203130ustar00rootroot00000000000000require 'spec_helper' describe ChunkyPNG do it "should have a VERSION constant" do expect(ChunkyPNG.const_defined?('VERSION')).to eql true end end chunky_png-1.3.8/spec/png_suite/000077500000000000000000000000001301140243200166055ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/background_chunks/000077500000000000000000000000001301140243200222775ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/background_chunks/bgai4a08.png000077500000000000000000000003261301140243200243100ustar00rootroot00000000000000PNG  IHDR tgAMA1_IDATx 0 ֽA((sOAVG":ݙbH$ @ɾzM2x<7U[0t<n!Y~.,>RfqXAh٪wϤ50o0N N6 O= YU]IENDB`chunky_png-1.3.8/spec/png_suite/background_chunks/bgai4a16.png000077500000000000000000000054471301140243200243200ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/background_chunks/bgan6a08.png000077500000000000000000000002701301140243200243150ustar00rootroot00000000000000PNG  IHDR szzgAMA1_oIDATx1 0 F'dhO?U!ExRP(M(ي0^{~3uG XN5"}\TB\.y 6{@<P6@R LIENDB`chunky_png-1.3.8/spec/png_suite/background_chunks/bgan6a16.png000077500000000000000000000065531301140243200243260ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/background_chunks/bgbn4a08.png000077500000000000000000000002141301140243200243120ustar00rootroot00000000000000PNG  IHDR sgAMA1_bKGD#25IDATxch41",(,?a0T1`4GÀ*hP* }IENDB`chunky_png-1.3.8/spec/png_suite/background_chunks/bggn4a16.png000077500000000000000000000042541301140243200243260ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/000077500000000000000000000000001301140243200176665ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/basic/basi0g01.png000077500000000000000000000003311301140243200217020ustar00rootroot00000000000000PNG  IHDR ,wgAMA1_IDATx-10 EƂz.z' V9cX,e5|KxOp笹pi\Yc*L'Dd[6癹+MKOKzҍ2-czpEHp-/zQ!塌Qf"IENDB`chunky_png-1.3.8/spec/png_suite/basic/basi0g01.rgba000066400000000000000000000100001301140243200220200ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/basic/basi0g02.png000077500000000000000000000002321301140243200217030ustar00rootroot00000000000000PNG  IHDR k gAMA1_QIDATxcPb`r`p16nDgg]!dDCH dJHR? 0A 9RM}IENDB`chunky_png-1.3.8/spec/png_suite/basic/basi0g02.rgba000066400000000000000000000100001301140243200220210ustar00rootroot00000000000000UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUchunky_png-1.3.8/spec/png_suite/basic/basi0g04.png000077500000000000000000000003671301140243200217160ustar00rootroot00000000000000PNG  IHDR gAMA1_IDATxeQ0D:(u AAAAA$TI~ò¶EuEĺCsGjw<# ^bs8Al.iGZ'(CYd:"k@i2Gpr:1(Kkce s {ig 826N'MIENDB`chunky_png-1.3.8/spec/png_suite/basic/basi0g04.rgba000066400000000000000000000100001301140243200220230ustar00rootroot00000000000000""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww""""""""""""333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwww333333333333DDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwDDDDDDDDDDDDUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwUUUUUUUUUUUUffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwchunky_png-1.3.8/spec/png_suite/basic/basi0g08.png000077500000000000000000000003761301140243200217220ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basi0g16.png000077500000000000000000000004531301140243200217150ustar00rootroot00000000000000PNG  IHDR qgAMA1_IDATx;0DI ]0S ,D @C#IVhk& \lX5R 2'+d~G @4 @4߁@\\y9۩ܵEkQט-} Q FAQ wbk@|DVIENDB`chunky_png-1.3.8/spec/png_suite/basic/basi0g16.rgba000066400000000000000000000100001301140243200220260ustar00rootroot00000000000000 $$$---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-1.3.8/spec/png_suite/basic/basi2c08.png000077500000000000000000000004731301140243200217160ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basi2c16.png000077500000000000000000000011231301140243200217060ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basi3p01.rgba000066400000000000000000000100001301140243200220340ustar00rootroot00000000000000"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""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-1.3.8/spec/png_suite/basic/basi3p02.png000077500000000000000000000003011301140243200217140ustar00rootroot00000000000000PNG  IHDR ygAMA1_sBIT|.w PLTEe?+QIDATxcxǰ]̌!9J޸b.??dCH dJHR?t#B,*9Z}uIENDB`chunky_png-1.3.8/spec/png_suite/basic/basi3p02.rgba000066400000000000000000000100001301140243200220350ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/basic/basi3p04.png000077500000000000000000000005071301140243200217260ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basi3p04.rgba000066400000000000000000000100001301140243200220370ustar00rootroot00000000000000ffffwwww""""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-1.3.8/spec/png_suite/basic/basi3p08.png000077500000000000000000000027671301140243200217440ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basi3p08.rgba000066400000000000000000000100001301140243200220430ustar00rootroot00000000000000        """"""""""""""""""""""""""""""""""""""""""""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-1.3.8/spec/png_suite/basic/basi4a08.png000077500000000000000000000003261301140243200217130ustar00rootroot00000000000000PNG  IHDR tgAMA1_IDATx 0 ֽA((sOAVG":ݙbH$ @ɾzM2x<7U[0t<n!Y~.,>RfqXAh٪wϤ50o0N N6 O= YU]IENDB`chunky_png-1.3.8/spec/png_suite/basic/basi4a08.rgba000066400000000000000000000100001301140243200220250ustar00rootroot00000000000000 )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-1.3.8/spec/png_suite/basic/basi4a16.png000077500000000000000000000054471301140243200217230ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basi4a16.rgba000066400000000000000000000100001301140243200220240ustar00rootroot00000000000000!!!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-1.3.8/spec/png_suite/basic/basn0g16.png000077500000000000000000000002471301140243200217230ustar00rootroot00000000000000PNG  IHDR kgAMA1_^IDATx1 0 CQ9[ܠ({2*ُ?8Wc:`݂@B&@=2 -hL`?oO8K_+IENDB`chunky_png-1.3.8/spec/png_suite/basic/basn0g16.rgba000066400000000000000000000100001301140243200220330ustar00rootroot00000000000000 $$$---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-1.3.8/spec/png_suite/basic/basn2c08.png000077500000000000000000000002211301140243200217120ustar00rootroot00000000000000PNG  IHDR gAMA1_HIDATx 0 @r;D++ ; }Lx@J„(t8#@pw^@KIENDB`chunky_png-1.3.8/spec/png_suite/basic/basn2c08.rgba000066400000000000000000000100001301140243200220320ustar00rootroot00000000000000~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#"!  ~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJIIIHHHGGGFFFEEEDDDCCCBBBAAA@@@???>>>===<<<;;;:::999888777666555444333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!  chunky_png-1.3.8/spec/png_suite/basic/basn2c16.png000077500000000000000000000004561301140243200217230ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basn2c16.rgba000066400000000000000000000100001301140243200220310ustar00rootroot00000000000000{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-1.3.8/spec/png_suite/basic/basn3p01.png000077500000000000000000000001601301140243200217230ustar00rootroot00000000000000PNG  IHDR IgAMA1_PLTE""fl&IDATxc4?fYIENDB`chunky_png-1.3.8/spec/png_suite/basic/basn3p01.rgba000066400000000000000000000100001301140243200220410ustar00rootroot00000000000000"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""f"f"f"f"""""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-1.3.8/spec/png_suite/basic/basn3p02.png000077500000000000000000000002221301140243200217230ustar00rootroot00000000000000PNG  IHDR ggAMA1_sBIT|.w PLTEe?+"IDATxc0,| =IꉎIENDB`chunky_png-1.3.8/spec/png_suite/basic/basn3p02.rgba000066400000000000000000000100001301140243200220420ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/basic/basn3p04.png000077500000000000000000000003301301140243200217250ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw-PLTE""fwDDҰIGIDATxc =sfժrcwfd C+(H*ŅTݻq@*)#MK#G{7}IENDB`chunky_png-1.3.8/spec/png_suite/basic/basn3p04.rgba000066400000000000000000000100001301140243200220440ustar00rootroot00000000000000ffffwwww""""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-1.3.8/spec/png_suite/basic/basn3p08.png000077500000000000000000000024061301140243200217370ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basn3p08.rgba000066400000000000000000000100001301140243200220500ustar00rootroot00000000000000        """"""""""""""""""""""""""""""""""""""""""""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-1.3.8/spec/png_suite/basic/basn4a08.png000077500000000000000000000001761301140243200217230ustar00rootroot00000000000000PNG  IHDR sgAMA1_5IDATxch41",(,?a0T1`4GÀ*hP* }IENDB`chunky_png-1.3.8/spec/png_suite/basic/basn4a08.rgba000066400000000000000000000100001301140243200220320ustar00rootroot00000000000000 )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-1.3.8/spec/png_suite/basic/basn4a16.png000077500000000000000000000042361301140243200217230ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/basic/basn6a16.rgba000066400000000000000000000100001301140243200220330ustar00rootroot00000000000000{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-1.3.8/spec/png_suite/chunk_ordering/oi2n0g16.png000077500000000000000000000002631301140243200235650ustar00rootroot00000000000000PNG  IHDR kgAMA1_@IDATx1 0 CQ9[ܠ({2*ُ?8Wc:`݂@B&@=2:3IDAT -hL`?oO8K_z_}IENDB`chunky_png-1.3.8/spec/png_suite/chunk_ordering/oi2n2c16.png000077500000000000000000000004721301140243200235650ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/chunk_ordering/oi4n0g16.png000077500000000000000000000003131301140243200235630ustar00rootroot00000000000000PNG  IHDR kgAMA1_IDATx1 0 CQ9[ܠ({2*ُ?8mIDATWc:`݂@B&@=ΣIDAT2mf IDAT -hL`?oO8K_z_}IENDB`chunky_png-1.3.8/spec/png_suite/chunk_ordering/oi4n2c16.png000077500000000000000000000005221301140243200235630ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/chunk_ordering/oi9n0g16.png000077500000000000000000000024031301140243200235720ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/compression_levels/000077500000000000000000000000001301140243200225205ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/compression_levels/z00n2c08.png000077500000000000000000000061441301140243200244220ustar00rootroot00000000000000PNG  IHDR  +IDATx                                                                                                                 UNnIENDB`chunky_png-1.3.8/spec/png_suite/compression_levels/z03n2c08.png000077500000000000000000000003501301140243200244160ustar00rootroot00000000000000PNG  IHDR IDATx^@0 J\l" [ןED=SJSf^տ> xЀ(9CNщ֠ .Gtx_AC:$%tgcݳ"n]ZWXtyF]؁]ȥ:Й1zd\ROx DWUNnCIENDB`chunky_png-1.3.8/spec/png_suite/compression_levels/z06n2c08.png000077500000000000000000000003401301140243200244200ustar00rootroot00000000000000PNG  IHDR IDATxK Кv1~a>W$n(8yKr-"ˡ %PzPE:wNh~uJLН#uOE" m(t:][ZWXtF]]Tҗ^}@'#si" |A~UUNn1R,IENDB`chunky_png-1.3.8/spec/png_suite/compression_levels/z09n2c08.png000077500000000000000000000003401301140243200244230ustar00rootroot00000000000000PNG  IHDR IDATxڵK Кv1~a>W$n(8yKr-"ˡ %PzPE:wNh~uJLН#uOE" m(t:][ZWXtF]]Tҗ^}@'#si" |A~UUNnIENDB`chunky_png-1.3.8/spec/png_suite/filtering/000077500000000000000000000000001301140243200205705ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/filtering/f00n0g08.png000077500000000000000000000004771301140243200224530ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f00n0g08.rgba000066400000000000000000000100001301140243200225560ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f00n0g08_reference.png000066400000000000000000000007741301140243200244660ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f00n0g08_reference.rgba000066400000000000000000000100001301140243200245740ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f00n2c08.png000077500000000000000000000046531301140243200224510ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f00n2c08.rgba000066400000000000000000000100001301140243200225540ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/filtering/f00n2c08_reference.png000066400000000000000000000046211301140243200244570ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f00n2c08_reference.rgba000066400000000000000000000100001301140243200245720ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/filtering/f01n0g08.png000077500000000000000000000005011301140243200224400ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f01n0g08.rgba000066400000000000000000000100001301140243200225570ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f01n0g08_reference.png000066400000000000000000000007271301140243200244650ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f01n0g08_reference.rgba000066400000000000000000000100001301140243200245750ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f01n2c08.png000077500000000000000000000022341301140243200224430ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f01n2c08_reference.png000066400000000000000000000047111301140243200244600ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f01n2c08_reference.rgba000066400000000000000000000100001301140243200245730ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/filtering/f02n0g08.png000077500000000000000000000005431301140243200224470ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f02n0g08.rgba000066400000000000000000000100001301140243200225600ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f02n0g08_reference.png000066400000000000000000000007371301140243200244670ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f02n0g08_reference.rgba000066400000000000000000000100001301140243200245760ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f02n2c08.png000077500000000000000000000033011301140243200224400ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f02n2c08.rgba000066400000000000000000000100001301140243200225560ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/filtering/f02n2c08_reference.png000066400000000000000000000046611301140243200244650ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f03n0g08.png000077500000000000000000000006051301140243200224470ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f03n0g08.rgba000066400000000000000000000100001301140243200225610ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f03n0g08_reference.png000066400000000000000000000007311301140243200244620ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f03n0g08_reference.rgba000066400000000000000000000100001301140243200245770ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f03n2c08.png000077500000000000000000000024131301140243200224440ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f03n2c08_reference.png000066400000000000000000000046471301140243200244720ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f03n2c08_reference.rgba000066400000000000000000000100001301140243200245750ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/filtering/f04n0g08.png000077500000000000000000000004151301140243200224470ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f04n0g08.rgba000066400000000000000000000100001301140243200225620ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f04n0g08_reference.png000066400000000000000000000007301301140243200244620ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f04n0g08_reference.rgba000066400000000000000000000100001301140243200246000ustar00rootroot00000000000000vvvooovvvgggooovvv```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-1.3.8/spec/png_suite/filtering/f04n2c08.png000077500000000000000000000017311301140243200224470ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f04n2c08.rgba000066400000000000000000000100001301140243200225600ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/filtering/f04n2c08_reference.png000066400000000000000000000047111301140243200244630ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/filtering/f04n2c08_reference.rgba000066400000000000000000000100001301140243200245760ustar00rootroot00000000000000 ')/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-1.3.8/spec/png_suite/gamma/000077500000000000000000000000001301140243200176675ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/gamma/g03n0g16.png000077500000000000000000000005311301140243200215440ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g03n2c08.png000077500000000000000000000005621301140243200215470ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g03n3p04.png000077500000000000000000000003261301140243200215570ustar00rootroot00000000000000PNG  IHDR TggAMAIPLTEȭ1 cIDATxc(% 0A `@`@W,@G.@p`06`FRalb\%U@ l@-H*@ SRpIENDB`chunky_png-1.3.8/spec/png_suite/gamma/g04n0g16.png000077500000000000000000000005531301140243200215510ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g04n2c08.png000077500000000000000000000005711301140243200215500ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g05n2c08.png000077500000000000000000000005361301140243200215520ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g05n3p04.png000077500000000000000000000003161301140243200215600ustar00rootroot00000000000000PNG  IHDR TggAMAOX2PLTEs$[IDATxcP pA `@`P@W,@Gi@` p00vq`AV`CU ``gjARQ^w*x;JUIENDB`chunky_png-1.3.8/spec/png_suite/gamma/g07n0g16.png000077500000000000000000000005011301140243200215450ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g07n2c08.png000077500000000000000000000005241301140243200215510ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g07n3p04.png000077500000000000000000000003171301140243200215630ustar00rootroot00000000000000PNG  IHDR TggAMAp;XVPLTEvvvNTl\IDATxc4 PA `@`0 @P p0S EX (//(Wbb 00 ``gRBQQ^w*#kr=IENDB`chunky_png-1.3.8/spec/png_suite/gamma/g10n0g16.png000077500000000000000000000004061301140243200215430ustar00rootroot00000000000000PNG  IHDR kgAMA1_IDATx1 E? ,=\!^KXHbřDdcfy΁HkMqye--HwZ_< JMGi =ضB;݃al>hly,kdǔ41{mi!,RIϴϫ IENDB`chunky_png-1.3.8/spec/png_suite/gamma/g10n2c08.png000077500000000000000000000004351301140243200215440ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g10n3p04.png000077500000000000000000000003261301140243200215550ustar00rootroot00000000000000PNG  IHDR TggAMA1_PLTETTUZcIDATxcP H% `@`@W,@G.@` p00NK`CV`Ɩ *@-H*ـ"(/YLFə:IENDB`chunky_png-1.3.8/spec/png_suite/gamma/g25n0g16.png000077500000000000000000000005771301140243200215620ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g25n2c08.png000077500000000000000000000006251301140243200215530ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/gamma/g25n3p04.png000077500000000000000000000003271301140243200215640ustar00rootroot00000000000000PNG  IHDR TggAMAАTOPLTE--\\\RQdIDATxα 0 DQ[J+ ^msQ$s5HІ:P 5B) oe p/)FqjS!*$BB?x(:5tf;PIENDB`chunky_png-1.3.8/spec/png_suite/metadata/000077500000000000000000000000001301140243200203655ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/metadata/cm0n0g04.png000077500000000000000000000004441301140243200223300ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/metadata/cm7n0g04.png000077500000000000000000000004441301140243200223370ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/metadata/cm9n0g04.png000077500000000000000000000004441301140243200223410ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/000077500000000000000000000000001301140243200177265ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/other/ccwn2c08.png000077500000000000000000000027521301140243200217740ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/ccwn3p08.png000077500000000000000000000030221301140243200220010ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/cdsn2c08.png000077500000000000000000000003501301140243200217610ustar00rootroot00000000000000PNG  IHDRKm)gAMA1_sBITw pHYsO%{IDATxEQ @DSga-B+ X JV++|4L2]C0بp|̡b?Liy+[}5kIR0_66T$'GjD>Pe}P] aO_w |*/^֮IENDB`chunky_png-1.3.8/spec/png_suite/other/cdun2c08.png000077500000000000000000000013241301140243200217650ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/ch1n3p04.png000077500000000000000000000004021301140243200216730ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw-PLTE""fwDDҰIhIST@p0`` P@0PpHYAGIDATxc =sfժrcwfd C+(H*ŅTݻq@*)#MK#G{7}IENDB`chunky_png-1.3.8/spec/png_suite/other/ch2n3p08.png000077500000000000000000000034221301140243200217050ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/cs3n2c16.png000077500000000000000000000003261301140243200217020ustar00rootroot00000000000000PNG  IHDR 1gAMA1_sBIT 7~IDATx @ H*)^ < 4\u wClPgXPR3ޛߟ:Ž~!N$#|G}LCli(?c"B@aEDG@0S&i\vgIENDB`chunky_png-1.3.8/spec/png_suite/other/cs3n3p08.png000077500000000000000000000004031301140243200217150ustar00rootroot00000000000000PNG  IHDR DgAMA1_sBITBTPLTEmmI$$II$m$Imp+KIDATxbWT#$tNzC{h t. mtn:B"@3w&IENDB`chunky_png-1.3.8/spec/png_suite/other/cs5n2c08.png000077500000000000000000000002721301140243200217050ustar00rootroot00000000000000PNG  IHDR gAMA1_sBIT&CbIDATx핱@ Ô;/Awl@*QEDqm\a .[p=jE'-X@O0 WyH0o y<Aa=^ohIENDB`chunky_png-1.3.8/spec/png_suite/other/cs5n3p08.png000077500000000000000000000004171301140243200217240ustar00rootroot00000000000000PNG  IHDR DgAMA1_sBIT&C`PLTEB{c!Z::cZ!B{G/KIDATx0[>@Vj%(`R A XtY VA,`d\p *-hq>IENDB`chunky_png-1.3.8/spec/png_suite/other/cs8n2c08.png000077500000000000000000000002251301140243200217060ustar00rootroot00000000000000PNG  IHDR gAMA1_LIDATxA 0 BQcjS҉hi Dk`j}0MA_uoFIENDB`chunky_png-1.3.8/spec/png_suite/other/cs8n3p08.png000077500000000000000000000004001301140243200217170ustar00rootroot00000000000000PNG  IHDR DgAMA1_`PLTE ߠ?_`@`@_ ?cKIDATx0[> |Gp "lX *A/P $BP Z.Y N-x)>SIENDB`chunky_png-1.3.8/spec/png_suite/other/ct0n0g04.png000077500000000000000000000004211301140243200216730ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/ct1n0g04.png000077500000000000000000000014301301140243200216750ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/ctzn0g04.png000077500000000000000000000013611301140243200220110ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/pp0n2c16.png000077500000000000000000000017021301140243200217100ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/pp0n6a08.png000077500000000000000000000014621301140243200217160ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/ps1n0g08.png000077500000000000000000000027051301140243200217230ustar00rootroot00000000000000PNG  IHDR V%(gAMA1_/spALsix-cubePNG group 1996-10-223f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f3333f333ff3fffff3f3f̙3f3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3f@AIDATxcd`$ȳ )?`y00gdy\ q10edPq5YIENDB`chunky_png-1.3.8/spec/png_suite/other/ps1n2c16.png000077500000000000000000000031511301140243200217140ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/other/ps2n0g08.png000077500000000000000000000044451301140243200217270ustar00rootroot00000000000000PNG  IHDR V%(gAMA1_spALsix-cubePNG group 1996-10-223f3333f333ff3fffff3f3f3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fffffff3fffff3f3333f333ff3fffff3f3f3f3f3333f333ff3fffff3f3f3f3f3333f333ff3fffff3f3f3f AIDATxcd`$ȳ )?`y00gdy\ q10edPq5YIENDB`chunky_png-1.3.8/spec/png_suite/other/ps2n2c16.png000077500000000000000000000047111301140243200217200ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/000077500000000000000000000000001301140243200177425ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/sizes/s01i3p01.png000077500000000000000000000001611301140243200216310ustar00rootroot00000000000000PNG  IHDRRf\gAMA1_sBITwPLTExW IDATxc`HqIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s01n3p01.png000077500000000000000000000001611301140243200216360ustar00rootroot00000000000000PNG  IHDR%VgAMA1_sBITwPLTExW IDATxc`HqIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s02i3p01.png000077500000000000000000000001621301140243200216330ustar00rootroot00000000000000PNG  IHDR?gAMA1_sBITwPLTE\/% IDATxc`gIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s02n3p01.png000077500000000000000000000001631301140243200216410ustar00rootroot00000000000000PNG  IHDRHxggAMA1_sBITwPLTE\/% IDATxc```8UIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s03i3p01.png000077500000000000000000000001661301140243200216400ustar00rootroot00000000000000PNG  IHDRjgAMA1_sBITwPLTEw奟 IDATxc`LAIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s03n3p01.png000077500000000000000000000001701301140243200216400ustar00rootroot00000000000000PNG  IHDRl'gAMA1_sBITwPLTEw奟IDATxc``p``A91 KIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s04i3p01.png000077500000000000000000000001761301140243200216420ustar00rootroot00000000000000PNG  IHDR8<gAMA1_sBITwPLTEwCIDATxch`  >7IENDB`chunky_png-1.3.8/spec/png_suite/sizes/s04n3p01.png000077500000000000000000000001711301140243200216420ustar00rootroot00000000000000PNG  IHDR? =gAMA1_sBITwPLTEwCIDATxc0?  IENDB`chunky_png-1.3.8/spec/png_suite/sizes/s05i3p02.png000077500000000000000000000002061301140243200216360ustar00rootroot00000000000000PNG  IHDRgAMA1_sBITw PLTEwAsIDATxch`h`X";tOm"Mp$FEIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s05n3p02.png000077500000000000000000000002011301140243200216360ustar00rootroot00000000000000PNG  IHDRvgAMA1_sBITw PLTEwAsIDATxcX0a"\*JIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s06i3p02.png000077500000000000000000000002171301140243200216410ustar00rootroot00000000000000PNG  IHDR7MgAMA1_sBITw PLTEwEh"IDATxch`h` - +<"&0LH`XYLe$IENDB`chunky_png-1.3.8/spec/png_suite/sizes/s06n3p02.png000077500000000000000000000002031301140243200216410ustar00rootroot00000000000000PNG  IHDRgAMA1_sBITw PLTEwEhIDATxcXaj02V-9_)pIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s07i3p02.png000077500000000000000000000002251301140243200216410ustar00rootroot00000000000000PNG  IHDR;gAMA1_sBITw PLTEwwC%IDATxc8p 3aH? Wcn UIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s07n3p02.png000077500000000000000000000002121301140243200216420ustar00rootroot00000000000000PNG  IHDR<@gAMA1_sBITw PLTEwwCIDATxcj í9 7H qy ]mIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s08i3p02.png000077500000000000000000000002251301140243200216420ustar00rootroot00000000000000PNG  IHDRffgAMA1_sBITw PLTEwwY%IDATxc` ~`Ǡ0aXAAPPpJIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s08n3p02.png000077500000000000000000000002131301140243200216440ustar00rootroot00000000000000PNG  IHDRaVgAMA1_sBITw PLTEwwYIDATxc```ZA =V=LIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s09i3p02.png000077500000000000000000000002231301140243200216410ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw PLTEwwVd#IDATxc`@ X ^3\b  duFIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s09n3p02.png000077500000000000000000000002171301140243200216510ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw PLTEwwVdIDATxc` V,XB q iejC#IENDB`chunky_png-1.3.8/spec/png_suite/sizes/s32i3p04.png000077500000000000000000000005431301140243200216440ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s32n3p04.png000077500000000000000000000004071301140243200216500ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw'PLTEwwwwww;|IDATxѽ Wq zZ6w +'a0MHR\GrvX41$TxAsŖ?hnsof]A[ $ \V~01YB6ipaHz&.IENDB`chunky_png-1.3.8/spec/png_suite/sizes/s33i3p04.png000077500000000000000000000006011301140243200216400ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s34n3p04.png000077500000000000000000000003701301140243200216510ustar00rootroot00000000000000PNG  IHDR""igAMA1_sBITw'PLTEwwwwww;mIDATxϱ _ [2г0&G+.%:r*Hv4,$ĂY49n2=xNNm'c)*+ TxVR,v5H\-Y~H:+NIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s35i3p04.png000077500000000000000000000006171301140243200216510ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s35n3p04.png000077500000000000000000000005221301140243200216510ustar00rootroot00000000000000PNG  IHDR##jgAMA1_sBITw'PLTEwwwwww;IDATxe!@״p иJ,{>a3Fު`Y\Ռ:T4b2(h%sn݆oEd=d2ܙBcF5μWhQd *F&Ny7zAwe\/x7rL#"ckLiq6>G΢HIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s36i3p04.png000077500000000000000000000005441301140243200216510ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s36n3p04.png000077500000000000000000000004021301140243200216470ustar00rootroot00000000000000PNG  IHDR$$.gAMA1_sBITw'PLTEwwwwww;wIDATxc`eG$! .dp]]h*t !,[*`:B,!,Bl7݅,NEPWB BB Є$BB62`XoIӑIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s37i3p04.png000077500000000000000000000006111301140243200216450ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s37n3p04.png000077500000000000000000000005201301140243200216510ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s38i3p04.png000077500000000000000000000005451301140243200216540ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s38n3p04.png000077500000000000000000000003651301140243200216610ustar00rootroot00000000000000PNG  IHDR&&ZgAMA1_sBITw'PLTEwwwwww;jIDATxc`eG`$61 !fpCCl* L1lvX*`q*b 0h-܌81 bhb)`@da1-i+άDIENDB`chunky_png-1.3.8/spec/png_suite/sizes/s39i3p04.png000077500000000000000000000006441301140243200216550ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s39n3p04.png000077500000000000000000000005401301140243200216550ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s40i3p04.png000077500000000000000000000005451301140243200216450ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/sizes/s40n3p04.png000077500000000000000000000004001301140243200216400ustar00rootroot00000000000000PNG  IHDR((~Х^gAMA1_sBITw'PLTEwwwwww;uIDATxб 0 DC,!alE6H%2EHVNRbG@!FeQk7^ Bؚp877*,mWBMwG)Rc!wu'gWpH])IENDB`chunky_png-1.3.8/spec/png_suite/transparency/000077500000000000000000000000001301140243200213165ustar00rootroot00000000000000chunky_png-1.3.8/spec/png_suite/transparency/tbbn1g04.png000077500000000000000000000006431301140243200233530ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tbbn3p08.png000077500000000000000000000021501301140243200233650ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tbgn2c16.png000077500000000000000000000037121301140243200233600ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tbgn3p08.png000077500000000000000000000021501301140243200233720ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tbrn2c08.png000077500000000000000000000025031301140243200233710ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tbwn1g16.png000077500000000000000000000021721301140243200234020ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tbyn3p08.png000077500000000000000000000021531301140243200234170ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tp0n1g08.png000077500000000000000000000012611301140243200233100ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tp0n2c08.png000077500000000000000000000024371301140243200233130ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tp0n3p08.png000077500000000000000000000021401301140243200233200ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite/transparency/tp1n3p08.png000077500000000000000000000021331301140243200233230ustar00rootroot00000000000000PNG  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-1.3.8/spec/png_suite_spec.rb000066400000000000000000000124701301140243200201500ustar00rootroot00000000000000require '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 expect { ChunkyPNG::Image.from_file(file) }.to raise_error(ChunkyPNG::Exception) 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| expect(decoded.to_rgba_stream).to eql 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')) expect(image.metadata).to 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) expect(decoded).to eql 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 expect(ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi2n0g16.png')).imagedata).to eql reference expect(ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi4n0g16.png')).imagedata).to eql reference expect(ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi9n0g16.png')).imagedata).to eql 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 expect(ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi2n2c16.png')).imagedata).to eql reference expect(ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi4n2c16.png')).imagedata).to eql reference expect(ChunkyPNG::Datastream.from_file(png_suite_file(:chunk_ordering, 'oi9n2c16.png')).imagedata).to eql 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 expect(ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z03n2c08.png')).imagedata).to eql reference expect(ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z06n2c08.png')).imagedata).to eql reference expect(ChunkyPNG::Datastream.from_file(png_suite_file(:compression_levels, 'z09n2c08.png')).imagedata).to eql 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 expect(ChunkyPNG::Color.a(ChunkyPNG::Image.from_file(file)[0,0])).to eql 255 end end png_suite_files(:transparency, 'tp1*.png').each do |file| it "should have transparency in #{File.basename(file)}" do expect(ChunkyPNG::Color.a(ChunkyPNG::Image.from_file(file)[0,0])).to eql 0 end end png_suite_files(:transparency, 'tb*.png').each do |file| it "should have transparency in #{File.basename(file)}" do expect(ChunkyPNG::Color.a(ChunkyPNG::Image.from_file(file)[0,0])).to eql 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) expect(canvas.width).to eql dimension expect(canvas.height).to eql 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') expect(ChunkyPNG::Image.from_file(interlaced_file)).to eql ChunkyPNG::Image.from_file(file) end end end end chunky_png-1.3.8/spec/resources/000077500000000000000000000000001301140243200166225ustar00rootroot00000000000000chunky_png-1.3.8/spec/resources/adam7.png000066400000000000000000000002611301140243200203200ustar00rootroot00000000000000PNG  IHDR~/tEXtSoftwareAdobe ImageReadyqe<PLTEF <2(dd ܣ/IDATxb` Vb`a ff  & h@1@;SdIENDB`chunky_png-1.3.8/spec/resources/bezier_five_point.png000066400000000000000000000001461301140243200230330ustar00rootroot00000000000000PNG  IHDR U-IDATxc?a78`74012@qX- cIENDB`chunky_png-1.3.8/spec/resources/bezier_four_point.png000066400000000000000000000001561301140243200230560ustar00rootroot00000000000000PNG  IHDR U5IDATxc?AL~10}a```W ]~ :vIENDB`chunky_png-1.3.8/spec/resources/bezier_four_point_flipped.png000066400000000000000000000001661301140243200245620ustar00rootroot00000000000000PNG  IHDR U=IDATxc?L (XILL< /.013,`bbt`b Z' .6lIENDB`chunky_png-1.3.8/spec/resources/bezier_four_point_s.png000066400000000000000000000001611301140243200233740ustar00rootroot00000000000000PNG  IHDR U8IDATxc?L@ ? (+ NIENDB`chunky_png-1.3.8/spec/resources/bezier_six_point.png000066400000000000000000000002041301140243200227000ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/clock_bl_xdown_ydown.png000066400000000000000000000004701301140243200235400ustar00rootroot00000000000000PNG  IHDR alPLTEz~$$#$pqrqrcIDATx 0ԟ@P}W^61b]wI¾3DXM7 @ ueeaz'ÅDsUxq$=*%i|IƔ* j{ȠnY[Zy6B?%+ IENDB`chunky_png-1.3.8/spec/resources/clock_bl_xdown_yup.png000066400000000000000000000025611301140243200232200ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/clock_bl_xup_yup.png000066400000000000000000000040361301140243200226740ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/clock_nn_xup_yup.png000066400000000000000000000017601301140243200227130ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/clock_updated.png000066400000000000000000000016171301140243200221360ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/composited.png000066400000000000000000000002541301140243200214770ustar00rootroot00000000000000PNG  IHDRh6sIDATxcb`/_ 0o`?`310Pí[z3g7"J Ij.Z..&.޴iir2 IENDB`chunky_png-1.3.8/spec/resources/cropped.png000066400000000000000000000002671301140243200207710ustar00rootroot00000000000000PNG  IHDR7`PLTEP`pP`pP`pP`pЀАРаBIDATxcb`b Z]1CIENDB`chunky_png-1.3.8/spec/resources/cropped_height.png000066400000000000000000000015001301140243200223100ustar00rootroot00000000000000PNG  IHDRǨgAMA a cHRMz&u0`:pQ<PLTEPP P0P@PPP`PpPPPPPPPPP`` `0`@`P```p`````````pp p0p@pPp`ppppppppppp 0@P`pЀ 0@P`pА 0@P`pР 0@P`pа 0@P`pAbKGDeh oFFs]BtIME 42' vpAg\ƭIDATc``dbfaecggPPTRVQUSg00426153gpptrvqusg  gHHLJNIMKg((,*.)-+3)%tEXtdate:create2015-10-25T16:52:50-07:00I%tEXtdate:modify2015-10-25T16:52:50-07:000IENDB`chunky_png-1.3.8/spec/resources/damaged_chunk.png000066400000000000000000000001071301140243200221000ustar00rootroot00000000000000PNG  IHDR 09IDATxcb&3(09IEND09chunky_png-1.3.8/spec/resources/damaged_signature.png000066400000000000000000000003601301140243200227720ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/empty.png000066400000000000000000000001211301140243200204600ustar00rootroot00000000000000PNG  IHDRwS IDATc?YIDAT5IENDB`chunky_png-1.3.8/spec/resources/lines.png000066400000000000000000000006741301140243200204510ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/operations.png000066400000000000000000000002011301140243200215040ustar00rootroot00000000000000PNG  IHDRh6HIDATxcb`/_ 0o`?`310F5j:&ҫIENDB`chunky_png-1.3.8/spec/resources/operations_border.png000066400000000000000000000001301301140243200230420ustar00rootroot00000000000000PNG  IHDRnvIDATxcb D * ^ z`IENDB`chunky_png-1.3.8/spec/resources/operations_grayscale.png000066400000000000000000000001721301140243200235450ustar00rootroot00000000000000PNG  IHDR:AIDATxcQT7sr OaB"@,$ T!&4@hz8LŪEHkM%a. SIENDB`chunky_png-1.3.8/spec/resources/partial_circles.png000066400000000000000000000002731301140243200224720ustar00rootroot00000000000000PNG  IHDR g PLTEݵ_tRNS]<ZIDATxc &(ʐIѫUL l _@ U %@0L.""L0cLJ`1X`x%߹TIENDB`chunky_png-1.3.8/spec/resources/pixelstream.bgr000066400000000000000000003751001301140243200216610ustar00rootroot00000000000000Ȧoookkkggg#%'  "# "$ $&(%'*&(*&(+'),'(*%') #$&#%(%'*&(+&(+'), //////........./// &(+')+&')%&)$&)"$'!#& "$!"%!# !# # "$!"%"$&#%(%')&(*(),(*-(*-(*-)+-)+-)+.(*,')*&(* "$$&($&(&(*&)+(*-)+-(*,)+-(*-(*,&(+%&)$&'#$'$&(&(*&(+'),)+-)+-)+.(*-'),'),(*,'(+%')#%' ddd:AJ3MnΣVVV 6T!:Z0,Gj0Mq=[6U|]5V~>_+Kt$Dm%En8X;[-Lt:a!3 0Kn)Fj%Ch-Ks:Z@`8X?h'Gp#Dk(Gn/Lq7Ru4V:];`*Ip/Ox>^Gg9Z4T|>])Ho,Kq)Ek6Ru/Jm!#%ccc7>H.GiڒZZZ%%%''' -Fb#<\ 1(CfA^IhIiEe5T|5Sw.'5.'5.'5.'5.'5.'5.'5.'5/(6/(6/(6/(6/(6/(63-:/Ij7Rv5Rw6Tz.Mu$Dm000)+.,Lt8W~7Tz@\3CW#8T(Dg2Ns6U{8Y2T~9[0R|9[AcLmLnIi-Ls(; /Jm(Ei-Jq1QyKkVxKmAc:[:[,Lt,Io6Ru9St0Lq7U{+Js(Ju?aEg>_<\,Ls%Ck-LsB_?Z}=Wx"$&ccc;BK7Rt ,,,;;;???DDDHHH!!!555+Ca/Ih!2 )Ei)Gn0Qy2T+Nz'Gq*Iq0)70)70)70)70)7/(6/(6/(6/(6/(6/(6.'5.'5.'54-;1Km;X|>\=]Bd1R{-/29\<^Bc(;S 1Nr3QxCcHjImFj=a8]6[0U1U8Z+Lu 3 /Ko1Ov8Y1S~6Z9]6[.S*N}:]?a8W:W|">`#Af*Ks<_=aFkHlGi>`>_1Qz"'%@c'Ac!#$ddd5e Bl8]1W(Kx)Is/(6/(6/(6/(6/(6/(6/(6/(6/(6/(6/(6/(6/(6 EEE.14$Ix&Iw#Dp'Fo 9^&Fn#Fq.S/UFx+R/V/V*Q-T%Jx$Fr!6;`%Dl+Lw)M|,R(O>e5\+R1W&Kx$Fq!?g&Bg$Ck*Mx*O~)P'O*Q,P+N{'It+:CPgd3V$>`!#&nnnTY^(Eh666***>>>BBBGGGKKKOOOSSSWWWOOO666*A^2Ji$4 .JnBaHkHmJqGmCf0)80)80)80)8/(6/(70)80)80)70)70)81*81*95/=6Ty:[2T@dHnDj247>eDh?b1Q{+>4Rw@a@d7^EmS|>g9bPyWPyEkAe-B :X}4T|7Z]MoBgFmPvJn 5U}BdFj;_LrMt369Dk:`EiBc,Hm:Y2T~ImOv:bT}JsNwT}Z[>eGk 1F :X~CdCgJpW]JsLuU~OxTz>cDf:ZIkLqW~EnKtLtHm;^]r| б4Pt666@@@ZZZ@@@%%% RRR1Ie1Hg&6 ;Wz=\DgPuU|JqImJlBb>]0Lq4Ps:Uw:Uw);(:6Qs5Pr;Ux>Y{5Pr$41GdAaGi=`DiSxT{U{369PwU{@d>`Dc"1AaLoHmJqV\U~ZYU~VU|Rv 1G"GgFhVzW}XW\ZPyWU|QvJmKmGjKqW~U~VBiGkJlS__ б3Ns666 BBB{{{2P!9Y. -Ik%Dj,Lv6Y7[8\,Ny'Fo':#3Ot3Pu.Kr2Pv5Sy5Sy&Dj&Dj0Nt)Gn.Ls 9Y 26X8[AfCi8]>dIp136;b`0Qy!6O*3U6ZFkHoDmMvGpMv=f0Y9b6]Dj/Nv9Z#;Z 7T%;V%:T#8Q%:S"6P&;T3L*D1J/H!5O4M-F/I1K"7R(?[-Gh:\;^Gl?f1Y:cZDDD???zzz$>]5Np'7 !4M 4O'>[%7N&6K,3=Z~>]@`1R{2S}5V3T~5VEfLmIj+Mx)>`GlMsPwLr9`?f147ElNuEkKnCd-Lt 0H(Bc4Op?[}Ec6V0Q}CfInKrWPy>gBk?h?h?hMvW~[Fj3WDf@`DcB`9W|2Pv.Lr,Jo-Kq)Gl7U{?]Hf:X~,Jo=[:X~EdGgDeAd9]:`Cj:cMvWcW6_NvJqQvPtIlCe0Q{4U~8X4V=_KoW{Rw0WFnLuQySyJnAaJX]䠹 б,Jo  9[9]#5 +, , -NYy -Il7W~4WBf>bEiCgHlLpHl=a+O~!?f0;a?f>f@gJqAg136(O>ec>e>fBkOxHqJsNwR{OxRzHo,R-R$Hv8[5W6X8X<\8X8X>_@a?`Ij@`%Fp$Eo$Dn1R|7Y:\?c;`?eFmFmLuPy\W3\-V0X8_BiAg?e6[>b=`?cCgHmPuPv9`+S/W3\Aj>e?b2S|ET^ʫyб,Jp+Io5Tz5Tz2Ns)De(@_!9V4Q4Q1L%:U2M 6P4N3M4O&;W'fb=`=`7[5Y7[2U,P|0T9]1U1U5Z8]:_?eCiJq=d=fBk>g6_7`Bk6_8`:bBjAh?fDkKqe@g=d5]6_;c3\?h;c^2Km7Rs8Su:Ux3Or3Os0Mr,KsCcCc?`?a6W4T},Ks9W}?]A^>[:X}9W}0Ns-KpDb;X~:X};Y0Nt/Lo%?a'(67DDCC89--"" 1Mq4V7^IrYZVR{U~ClFoVT}S|7Sw +Gk9`@gMt258NtKqFm;b;a7Z@c#4I$4<]<]4XAgS{QyJrIqBkGp=fNwVU~YKtGp>f=cMtOuJpCh?d?d5ZFkNsNsPuFkDi;`5ZOtLqHmIn@ef_QzPyQzEnCl:cMuV~YWR{T}BkDm\PyOxMvEk5Y.Ox7HPg~б=\:Y?`Fi@b<\Ba@][;X}4Qw]@`?_?_:]?aHlDgJlKn;]CdAbCd>^@aDeAb<]2R|BbBb?`CdJj;[6Tx*+9:EE??45)) ?[?aElGpJsQzT}V[WDmYMvPyOxKl (*Ba5Rw136KqOvU{KrFmCiEi@a FgJmQuOvJrT}U~ZR{LuPyWQzOxXV~RzT{LsEjOtInNsLp@eIoKqMrDiJpMrHlFj>cDiMsInOtPv?dIoGlJnFjInOuMtIpbGkLpLpBeHh237834+,!":VyDfZYMvWn_R{WNwClEnNwOxS|S{>Y|(8 GGG136EkNuMtMtU|[FjFj"/@(dX}Gk<_EgFhFhFjRvHkFiUyRvQtGj=_Jm@d?bHlLpLp?cPtJl@cNq[~TxJoJpRy@hGpR{WXOx]XLuZt]PyS|U~@iEnPyR{QxJnMoS__бLmPqTxW|^_V|X~EjY~?d9^AfMrRwQv_{l{k][Fl[CiAhEjl}ea[[~~jW~Ho_GmTzElKrU{Ry|e '(*+&' !;W{QsaYboj_T}jdcR{LuWPybdYGfXXXkkkhhhfffhhh... 9XSyLs``ZW|Z~Jf FiW{GlOvIqVVbdc_fg`_HnTw1H !"$$%)#3GKnOp-?U*'!&  !)=DgNq/B)'#&))'9O?a\ElXJsPyYXdbT}`qj]PylYaNwNwVPwX}UxHYcʫvб;[1S~3X-T'O7_Px9b#K~)Q3\1Z=e>fFnFnλضҪ¦кֳΩֻ֬ĨҖ l 8Tx4V4[9b&O3\DmIr7`'P1Z1Z6_f*QG]yPfTjH^x@UoEZt #BiOvHogMvBk꘤  l 5Rv6YJrLtBeRyPwKsAjLuW>gFoDmAjNw?hKsIqMtSzT{Jq;`DiIngAjLuNw뗢z 7SxDf:`Ah';+O|,R:b1Z6_.WAj?hMvU~Qz?hKs.V:b.V*Q2X.S3XKrPwQxDmMu0W0V+O}*Mz 'Dh%Iv=b9`DlOxS|IrFo=f1Z8a2Y)O2V)Jt$;  1G*Gl0Nr'Di5Q +In/Lq8U{4Qw 6S)Ho$Fp-Q;a;cClQzJsR{=f?h6_?h/X5^;d1Z4]5^=fAjPyNvMt9_?d*Nz4V&Fo!?e)Ej```б*Kt0R~)N})Q)R(Q%N'P/X3\4]8a5^6_,U5^얢">b,Ny4Y:\(D"Er,S2Z3\6_4]>g%N0Y3\6_1Y'O$L~$L%M(O.T-S&L}.U1X6_/X"I|"Iy#Gv"Fs'@./R~+O|0U,T-U6_:c.W,U*R&N"Hy"Ft&Gq%Ci*䎎 &6U8X6W $!<]%?`8Z9Y䎎 #$?b)Gn)Kv5Z8_:a1Y1Z7`i=e!?e000б0Pz5WBg>f8aLuYPyPyMvU~PyFoDmLu=f떢$;W.Nx@d  A^HlOvRzWJs>gDmFo9bJsOx@i4]\VQyQxIp?fEk8`FoMvgT}Nw۾ٿ똤 |u|,!4J5Tz!3  B`CgRyS|[BkS|PyIr8aNwLuWWW[HoFhFmJqGo9bEnIrKsMsKpPs'8$6LKn?c;aMuJr<?3$5 2I +?_SwGnXOxHqEnJsPyS|XT}^S|Lu:^MtCiKsS|PyPyU}YMrEi4Sz IlCgGlElS{OxOxU~V|Sx\x\x\x\x\x\x[vYtYtYtYt[v[vYtYtXsYtYtYtYtZu[v[v[w[v[v[v[w\x\x\x\x\x=]IlInElIqJsHqPy\PyVS|T}[\JsZPyHqFoIrIrS|XQzRxGj>_ б+Ku*Kw/S+Q,S#Hx(M{3W-R0T(M|(N~-S4\5^6_Ͼ- ',-,* (+"7.H4N "#%""$ $>eAp-T0X8a;d9b-V1Z7`1Z+T-V8`#>a9`9`7^.W3\0Y.W(P+Q3W1T ,H)Ly3W6\6]3\7`.V-T"Ft@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So:a&Hr+O}1W9a4]2[3\5^5^)R4]:c5^.W%N0Y2[7`:c7`0Y1Z0Y4\'M~)Ly.OyPPPб3Rz-Nx2T7ZDh;^3U4Mm"2HAa6Y8\5ZRxOwCkƸ=Wv4Pt7U|-KqDc?^g:cQzOxDmQz[QxW~Ho?gb5X:_8_AiBkPxInAc@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So'Be;b:\8]g>gHqAjLuGpEnAgFi?`OOOб0Mr0Ns9X&:R  2S}2U7\>e߷񨵿HpMte0J ""$456BBC@@A455'() "<[2U?e@hPyPyHqAj7`Cl?h=d;b3Z;bBjHqPyLuFo9bBj=d9^5Y 4M 11T5Y=b?fKtMuBg5WXrYt[v[vZuXrXrXrXrXrXrXrXrYtYtYtYtYtYtYtYtYt[v[v\x\x\x\x\x\x\x\x\x+Fh*Io,Mx6ZFmDmLuKtOx@iClDm:^GnJrS|WR{QzYR{Em>e@gBhDlKtU~R{NwPyV~OvBh7[4U 4U:]=bJpRzHpEjAc@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So5Pr9W~8ZFkAhKtPyR{T}U~S|WOxDmDmOxOxEnKtClIrNwQzXR{LrMpAb+++ ___б'8 """### CcDfKpٰS|U~U~R{VVZEnX<^"1/01;<=9:;./0""$ FgDjEnKtMvXS|KtYRyKrPwPwHpIrKtOxU~S|HqV}QwImKn#4%7MHkEiFkIpQxLqCdYtYtYtYtYtYtYtYtYtYtYtZu[v[v[v[v[v[v[v[vYtYtYtXsYtYtZu[v[v\x\x\x[w6Qt6TzCeImYPyFoR{T}U~S|U~WYFo]JsPyT}ZMvFoMvQzU~MsDgIjWWW@@@)))а+@[ ###### )?[@aGjڲÿ_hVepLu]ClHpMrCe ./0./0&'($:VCd^T}^h]]n\QxAgCiNuYZ^`^_d^JqChJn;Z FiIl\V|ZUzPr@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So0Km?^BcEiMuhPy^hVfpMv\DmKtT}U~HqR{kT}^fXYaEfvvv^^^а  ###############$FeLmڲ[^_`\GpdMvVDl;_Ab#0 ""$  5N;YLoU{^dZZefNwNuV}GnU{][_[YieT}IpW}6[Ei$1GcSwUzV|W|MqUvZu[vYtYtYsXrXrXrXrYs[v[v[vZuYtYtZuYtXrYsXsXrXrXrYsZuZuYtYtYtYtYtZu8Su2Pv7XKoZ^^[^^`]GpbNwXHqBkR{]Zad^YZUx=^@@@а!!!###"""###############(Bb6WٲCl@i=fgAjBk8a.V4[3YCiDjFn:cHq9bFoAj?h,U/W3Y.S1T$<\#Ad3W>d>d:]2S~@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So#>a)Gm7X8];bBk=fBk?h]ڰ`S|=f>gT}Yc]FoDmBkHoHmGh'9P$+Ee2Qw4VCiIqS|U~]W9b9bQzT}QxPw>eHoT|GpZb;d3\R{S|WOwFl?c9] ;Y~>bQvW}7[%GrXsYtYtYtYtXrXrXrXrXrXrXrXrXrXrXrXrXrXrXrYsYtYt[v[v[v[v[v[v[v[v[v[v*Eh1Pv7XEiNvU~V`S|=f>gS|Ya\FoDmBkKtOxWX_S|9a7]DgFgpppа"2########################-Giݷ9b3\8a1Z`,Jp4U>c:bJs:c:c2[8a0Y;dAjBk7`2[:cAjHqEnIr5^@i-V5].T-P}5V000б'?]### !!!################## -ܷø=fMvNw:cCl>g3\9b@iJsVKtKtGnFl7Y!3K!8V)Gn3V?fU~BkQzBkLuGpLuEnBk?h5]9`5\>eMt>fKtHqGpClEn?h5^>g8`c<`6X[vYtYtZu[v[v[v[v[v[v[v[vZu[v[v[v[v\w\x\x[v[v[v\x\x[v[v[v[v[v\x\x^z*Eh:XCeFjElLuLu=fMvNw:cBk=f4]9b?hIrVJsMvGpLuClHqGp7^6Y,Mv@@@б)Bd 0 ###### ###### }̶ѴѺҴѵвαȪ¢˽c9b:cLuWPyOxOxQzAj?hJsCl?hMuHn-Oy*&:S9V{EhKqBj?hIr@igCl^QzOxPyWIp=d@fDj>deFl2V5W@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So@So7Sv0Ov7YAfBjGpNwT}9bfMuKr6]=d<`Eh;[б2Mo(?[###  ,@hpy ^clĬĨõ̹zGpQz\c[NwS|S|[MvQzEnPy=fFmCh7V~ !B`CdJoRzWJsVAjQzAjgLuHqPy\c[OxS|S|[LuPyBkLs:aBhCi>cMpJjEcPPPС5Ru,Gi( """""" 2@@~ %FJPȱʪħôʵϻxZPyU~WKtNw_U~T}NwGpU~Gp:cHqT|NsGjEf=`?dYVPyQz@iQzEnBkJsVX\YKtBjBhIpDlGpGpPyU~]_T}HqW^OwU}AiMuMu@hDkMtMsRvA`FgAeGmU}Js;dGpZQzU~VJsMv_U~S|NwEnRzCj6\AfLoHkDe>]3PuqqqY]a2X5Y.N"""/2x[BWZZVV00776κպյҹӸҹӸҶҸԼؾg)R'PG{#L#L%NBvDx"KDx I}%N"K'P#LH|%L~"HxEu!Gw)P@sAu%NDxEy$M$M$M#L%N#L&O#L!I}:\#J{"Hy!I|!J~%NFz!J~!J~*S#L.WDx?sH|Dx'PH{Gz#L%N?sEv>m Co9b3X*J%?#=%?":$> !:"<(D0Q-S1Z>j?nDu"J}G{'P$M'P'PG{#L$M%NAuDx#LDxG{"J|Fv"Gv )%Kg])%+2/PС5Ry3Pv3Qw, !!!4X\$;>13a=%HL>NN>>66DDffzU~GpAjT}LuNwEn9b>gOxBkPyQzKtGpS|EnFoOwHqNwIr=f;dKtLuQzPyGpHqPyGpKtKtHq:X}KrIpJrFoIrLuOxHqNwMvT}EnClHq@iLuKtKtHqHqHpRy?eEi?`=[)Df&Z-C^ 4N$:U 6Q )?[-Fe1Lp2Qy3U9]Gm9`JsR{MvJsU~GpAjT}LuNwFo8a>gOxBkMuJo@c-=8pxxd]]]б/Nu3Qx0Ov1Kn  p7fk)+TJ<MMJJ55--;;,,W.04{KtJsKtLuR{PyIrBk,UAjBk>gBkHqKtKtDmHqLuJsXKtBk-V>gEnEnClFoKtHqDmJsHqGpQx6SxCiDj>fFoDmNwIrLu@iMvOxLuFod>f:cBkIrLuKtIrLuLuQzQzJsAj,UAjAjg1Z:cEn@iQzS|NwDmeGpPyMvLu6^ClCl?h?hGp1Y4\9aFo@iFoMvU~R{>e=d5Z3V+Jp-C.Hh5No4Nn'@_!5%>_%?a(Ce'@_/G /F 5N+In.Nw<_1X9`4[9bClBkOxQzS|ClClCl;d=fIr]DdJiEeBa?\%5 $6%:Uf7gl!57hZ=0ci 22qDD8M_sg}y~yzi`XT}ZT}GpIrU~GpVVR{W`Y_ZU~]WHqHqNw>gXQzX][[\YY_XFm@gMtSySyU|]T|V}XZXJr>eGn`BhRyLuU~[QzU~cZ`XT}[U~GpIrT}EnVS|MtLoKl.AFixxNkcб8W~7V}8X5V5U}5Op 1#ZK i>sy +?2QtAhIrWMvR{Ir=fLuNwT}S|PyR{OxPyFoMvXOxS|T}?hMvNwVVOxS|S|R{JsPyWR{R{T}BkKtJsR{T}LuT}LuLuEnNwVT}R{PyDmIr?hMuRzHpSzJpAgHmJpFkCkEl>eEkOvGmQwOu@fEkFlOvMtLsJqGnGoBkGpQzKtPwNtHj  $9Z=] 1F);Fh>`!2G +>FjHnLsClLuXOxR{V@iMvNwU~VNwS|R{QzJsPyWPyLsGk4T~2HLpxx\v аA_GfJj  8E D 0 =bf(JO<> !" (BF:<58/2(+0E7UzLrT{Qx]SzdYZW^V~\YKsHpQyWPxcRzcW\Z^Y\XMuFoPyT|QzcOxhS|_Zd]_`S|KtIrQzU~bOxhMv^^aXZ]FlJoTyIoV|KqYV|YV{OtW|BgQv9^JpTzU{QxT{\]^^hNwZLsNs 2H "1DNpMpVxFhKlGg=]<\FhIlGjRuIlVxKlIiFfLlHjPsPtGmEmPyWR{dT}dX^\`[^ZOxHqR{T}R{bKq[Aa-=8pxxdЯEbFe7OmP= 6`BH%% &&!  #+C_8W}=b=a;_SwQuQuMrFkV{X}Y~NtCiHnLrPvDj7]QvU{W}QvKqU{QvbQwCiEkOuT{Jp5]PxZ_WYS|T}j_JsHqWLuU~8aQz[aT}\V}U|f^Ms@ePu "EjTyV{NtX}SxOsaOmLp7[Mq4XHl5ZDjYX~Z[T|PxjbQy;bPv>b3W6X:[NoKmRtMpFiImZ~OsGj:\DfBe@c=`9]QvQtMqJnBdOqPsTwLpCiIpPwWJs;dW^`XS|[VkYIrKtU~ZOx8aRzU{TwEf.AFayxxIe`ϯ7Ty0Ns,@ 0<%n|Ai -U:zW"pOX3(\?=_K+`P/77~11sWI!!d& "$>\&Ek+Mv1S}=_:[;\;]AbAc=_1T.Q|+Ny:\5X-P|1T4W=`>a:]Bd@cBe;^0S-P|,O{9\-P|+N{2V6[?dEk>eIqCkOxBk5^5^5^>gGp.W9b;dClHqAjIr>fNvDkbgnafl`fl_dj]ag[`f.02 3S{Bf=`Cg;`Gl?c:^1T *@5W,O{'Jv3V7[Di@eHn?eIpJrAi7_:c5];b6]0V4X4XBe=_Ad<`EjGk=b4Y6Z.S:^8\0U2W7]EjAgBhBhHmFkAe5Y2W/UAh:a3[:c>gHqJsEnOxKtOxEn7`4]4]Dm8a1Z8a=fEnHqg3\6_S|U~h]Aj@id9^7\;`PuMr@eGm7]2Z;aMu\XEl;b3Z;b@gW~QyElOv:a3Z9`Mt[]RzCk4]@iBkZU~HqNw?h5^:cS|ZdZFo=f>g;dZRxAe<\!08ixxF`Zϭ53.!L+wT'Tgi"YYWqg9b8a8aBk@i=e)Lx 6R  GGG,/2)Lz3V5X6Y+Oz(Dh2:\"40! 1Nu*Eg 4I)Lx3V5Y;_2W6\4[,T7`Ajf:b,UCl5^8a6_4]Cl9a6^4\>gEn=fCl;d0YGp8a=f5^=f:cgAjFoGpNwIrDf+ "4I)>Z-03=`?bFi?aJl:Ux ?kH  )=VGkMsQwHoGnBjAj:cEn@iEnFoGpMuQyS{S|T}T}MvBkAj?hCl>gGpEnHqPyR{R{S|S|OxOxEn=fBk?h9bDmMvHqNwQzQzXT}MvMvGp:cAj *$8SEiMvOvOsHk-=8ixx^| ά!2 D4UzfT'mywB-d{ERn0NNpJ%RRkBJk=xc ; 3M???/Ov.R~:^3Nq!1(,FgZMvLuOxNwR{:c,Fg  9YBjGnCi/133V;^6X/"# (M45cB   &J1  6Z5ZIoCi8_5^PxS{FmGnOvFm>f?g@h=e1XLsFm?g;cBjT|LsHoGnLsBjGnAi;c2YIpMtJq9a9aYNuIpBjNuHoElBjb6ZImPtW{JoKpDiBgDiEi:_HlLq@d9]4YHlOsVzNrLqHlChImHm7\EjTzEkbElT}XU~Hl%6AbZS|Gk,BiVAjFnIpNu/14JmBeBd3T~+A]9kF2X; WcFuO  Df8[>bBfBg<[IjHkGj:\=^LnFgHj9[Hj<]7X>`>`BdGiCeHjAb<]Km<]Fg:\JkBc:\>`9[EfDfDfGiDf=^Jk<]Ef<^Fg:V{9[Bf@eRyRzT}U~MvZNwS|NwKtS|1Ns />`IlHjBb=Z4Qu$')ά~* -V9 iC\2vg3(WJ}B:l-bxBt1vQ/~]X{uKD !2???,Jo0Pz@c>e;c:c:cAjfDl@g@g-03-P~,Nz-Mu)?'M49&=wP  #7%DrM *+Mw/R~7Z:^,O|=`:^>d7^3[Aj;d8a5^;d/X4]>f;c4Qv%77\3W0R}1Px.Lr&'*nnn RUY0Jk,C` D"}Ryŵ4dt:La‚xj N.!'..AZ 6R???8Uz>^GjDjR{7`@igJs@i*Sa;]?_4RxAaDg>cFmFoDmDm7`9b1Z@i@i?hLuR{Jp(>d<_5V)+.ccc5%BP-:QaKpEyhhC'6" ,E-Oz.Q|.R}1R|/Ov)Fi,Kr4V1V4\5^+T%N.WG{2[4]7`3\;d8a;d0I7U-P}-Nw(*,bbb9@I1Ln4Os$  RF_tĹ? //Hg+Ef0Kl.Hi-Gh'8'...???fff///5Tz?a;]>bFmGnIpMtQxJqEl EEE-/2Fh=^=^ /1Ms &7'9\?1I%*Ab;\?a4S|3Pv+Ef3Pu;[?bNtOvMtJqAgMtLsNuDjKrAgAgEl9X /BdBb)+-ddd;BJB]=Y|C_,@Y .@-?V*70S9E @ $fS0# 6LiYz;Vx?[|B\~:Uv+A[??????6MkCaIjGiUyPu\RwGlQvIn@e5Z-?=X|V{NsUyGkTwB^+.0DfMmBa @`HgCa;SqB_-A\ 9Uz,Io**EeOpIjJhA]8Rr@\GfQrKoDiEjCh7\UzUzSxPuTyMrY~Rw[ >^Dc'),fff>FP^/Eb$>^A`>]@]=[9Nl#18U{,Ip?^+Jr?\FfFf@`;Y~:Tv4RvGg;]PtGfGkMrOsKp7[LqMrZMrFjLqDh)3IgEd)+.kkk,,.LQVKOUNQVMQVKOTHLPGJO :=@>BE+.1(*-BDIDGLFINHLPIMRJORKOSKOTINSHKOEIMCFKBFIAEIAEIAEIAEIBDIBDIAEIADHAEHBFJAEIADIADHACHADHBDIBEIBFJBEIBFJBFJCFJBEIBFJCFJBFJBFJBFIDGLFJNIMRKOT\ag\`fY^c[_eY^diiiiiigggfffffffffVZ_TX^UY_TX]QUZMRV GKPMPUOTXUY_[_dRV\oooÿѺի֬Ĭ»ṹñ¼chunky_png-1.3.8/spec/resources/pixelstream.rgb000066400000000000000000003751001301140243200216610ustar00rootroot00000000000000Ȧ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-1.3.8/spec/resources/pixelstream.rgba000066400000000000000000005214001301140243200220160ustar00rootroot00000000000000oookkkggg'%# #" $" (&$*'%*(&+(&,)'*(')'% &$#(%#*'%+(&+(&,)' //////........./// +(&+)')'&)&%)&$'$"&#!$" %"!#!#! # $" %"!&$"(%#)'%*(&,)(-*(-*(-*(-+)-+).+),*(*)'*(&$" (&$(&$*(&+)&-*(-+),*(-+)-*(,*(+(&)&%'&$'$#(&$*(&+(&,)'-+)-+).+)-*(,)',)',*(+(')'%'%# 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-1.3.8/spec/resources/pixelstream_best_compression.png000066400000000000000000000513501301140243200253270ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/pixelstream_fast_rgba.png000066400000000000000000000637731301140243200237150ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/pixelstream_reference.png000066400000000000000000000567621301140243200237230ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/polygon_filled_horizontal.png000066400000000000000000000002721301140243200246100ustar00rootroot00000000000000PNG  IHDRڄ PLTE=-tRNSDP!]IDATxE ! Փ_@A@@At@Y@wR֐*+Mi&7j[K?&;1O஻ ;2WA HpiIENDB`chunky_png-1.3.8/spec/resources/polygon_filled_vertical.png000066400000000000000000000002751301140243200242330ustar00rootroot00000000000000PNG  IHDRڄ PLTE=-tRNSDP!`IDATxE 0 ?/Sd FHÔ4R ./Kgl[;'.?V+]i殍8=0L5PJ扴 W),#^(֙#IENDB`chunky_png-1.3.8/spec/resources/polygon_triangle_filled.png000066400000000000000000000004051301140243200242220ustar00rootroot00000000000000PNG  IHDR6q-PLTE&-5=ZtRNS9UqnYD^xIDATxUϱ l4V`6l`c n"ZMx@!?ylj[o$t0}#aT5]UɀAwIfN2q C%{gVr7:`l)-ؿ8IENDB`chunky_png-1.3.8/spec/resources/polygon_unfilled.png000066400000000000000000000004511301140243200227010ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/rect.png000066400000000000000000000002461301140243200202670ustar00rootroot00000000000000PNG  IHDRRPLTEMO_H__گj@^ FIDATxc߫`0PgϩY @"߻b0d- a*IENDB`chunky_png-1.3.8/spec/resources/replaced.png000066400000000000000000000015471301140243200211160ustar00rootroot00000000000000PNG  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-1.3.8/spec/resources/text_chunk.png000066400000000000000000000002221301140243200215000ustar00rootroot00000000000000PNG  IHDR ';6tEXtAuthorWillem van Bergenv tEXtTitleMy amazing icon!Y!=IDATxcπ "q )IENDB`chunky_png-1.3.8/spec/resources/trailing_bytes_after_iend_chunk.png000066400000000000000000000002751301140243200257230ustar00rootroot00000000000000PNG  IHDR IDAT8OA ƠYB H=ht"#(hL 4U(@=]9th 4պO`u8t҃C\L'FOXz;7G 5`Y9=4ApnIENDB`chunky_png-1.3.8/spec/resources/ztxt_chunk.png000066400000000000000000000013611301140243200215320ustar00rootroot00000000000000PNG  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-1.3.8/spec/spec_helper.rb000066400000000000000000000021461301140243200174310ustar00rootroot00000000000000require '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 config.expect_with :rspec do |c| c.syntax = [:should, :expect] end end chunky_png-1.3.8/tasks/000077500000000000000000000000001301140243200150035ustar00rootroot00000000000000chunky_png-1.3.8/tasks/benchmarks.rake000066400000000000000000000012551301140243200177670ustar00rootroot00000000000000all_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