oily_png-1.2.1/000077500000000000000000000000001276602571700133505ustar00rootroot00000000000000oily_png-1.2.1/.gitignore000066400000000000000000000001641276602571700153410ustar00rootroot00000000000000Makefile /conftest.dSYM *.bundle *.o *.rbc /.bundle/ Gemfile.lock /pkg oily_png-*.gem .DS_Store /spec/resources/_*.*oily_png-1.2.1/.travis.yml000066400000000000000000000002551276602571700154630ustar00rootroot00000000000000sudo: false language: ruby script: bundle exec rake rvm: - "2.0" - "2.1" - "2.2" - ruby-head - rbx-19mode matrix: allow_failures: - rvm: rbx-19mode - rvm: ruby-head oily_png-1.2.1/Gemfile000066400000000000000000000001511276602571700146400ustar00rootroot00000000000000source 'https://rubygems.org' gemspec gem 'chunky_png', :path => ENV['CHUNKY_PNG'] if ENV['CHUNKY_PNG'] oily_png-1.2.1/LICENSE000066400000000000000000000020521276602571700143540ustar00rootroot00000000000000Copyright (c) 2010-2014 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. oily_png-1.2.1/README.rdoc000066400000000000000000000021571276602571700151630ustar00rootroot00000000000000= OilyPNG {Build Status}[https://travis-ci.org/wvanbergen/oily_png] OilyPNG is a Ruby C extension to speed up the pure Ruby ChunkyPNG library. It is a standalone module, so it does not require LibPNG, ImageMagick or any other library. Currently it has an alternative implementation of decoding and encoding PNGs, making these operations much faster, especially for PNG images that apply filtering. Performance comparison: http://gist.github.com/611255 *Warning*: this is my first C code in years. It may blow up your PC after leaking memory all over the place, killing a kitten in the process. You have been warned. == Usage 1. First install the gem and make it available to your project. 2. Use require "oily_png" instead of require "chunky_png" 3. Presto! Now use ChunkyPNG as you normally would and get an instant speedup. See http://github.com/wvanbergen/chunky_png/wiki for more information on how to use the ChunkyPNG API. == About License: MIT (see LICENSE) This C module is written by Willem van Bergen with help from Dirkjan Bussink. oily_png-1.2.1/Rakefile000066400000000000000000000007301276602571700150150ustar00rootroot00000000000000require "bundler/gem_tasks" require "rspec/core/rake_task" require "rake/extensiontask" Dir['tasks/*.rake'].each { |file| load(file) } Rake::ExtensionTask.new('oily_png') do |ext| ext.lib_dir = File.join('lib', 'oily_png') ext.config_options = '--with-cflags="-std=c99"' end RSpec::Core::RakeTask.new(:spec) do |task| task.pattern = "./spec/**/*_spec.rb" task.rspec_opts = ['--color'] end Rake::Task['spec'].prerequisites << :compile task :default => [:spec] oily_png-1.2.1/ext/000077500000000000000000000000001276602571700141505ustar00rootroot00000000000000oily_png-1.2.1/ext/oily_png/000077500000000000000000000000001276602571700157705ustar00rootroot00000000000000oily_png-1.2.1/ext/oily_png/color.c000066400000000000000000000047141276602571700172600ustar00rootroot00000000000000#include "oily_png_ext.h" #include PIXEL oily_png_compose_color(PIXEL fg, PIXEL bg) { BYTE a_com, new_r, new_g, new_b, new_a; // Check for simple cases first if ((A_BYTE(fg) == 0xff) || (A_BYTE(bg) == 0x00)) return fg; if (A_BYTE(fg) == 0x00) return bg; // Calculate the new values using fast 8-bit multiplication a_com = INT8_MULTIPLY(0xff - A_BYTE(fg), A_BYTE(bg)); new_r = INT8_MULTIPLY(A_BYTE(fg), R_BYTE(fg)) + INT8_MULTIPLY(a_com, R_BYTE(bg)); new_g = INT8_MULTIPLY(A_BYTE(fg), G_BYTE(fg)) + INT8_MULTIPLY(a_com, G_BYTE(bg)); new_b = INT8_MULTIPLY(A_BYTE(fg), B_BYTE(fg)) + INT8_MULTIPLY(a_com, B_BYTE(bg)); new_a = A_BYTE(fg) + a_com; return BUILD_PIXEL(new_r, new_g, new_b, new_a); } PIXEL oily_png_color_interpolate_quick(PIXEL fg, PIXEL bg, int alpha) { BYTE a_com, new_r, new_g, new_b, new_a; if (alpha >= 255) return fg; if (alpha <= 0) return bg; a_com = 255 - alpha; new_r = INT8_MULTIPLY(alpha, R_BYTE(fg)) + INT8_MULTIPLY(a_com, R_BYTE(bg)); new_g = INT8_MULTIPLY(alpha, G_BYTE(fg)) + INT8_MULTIPLY(a_com, G_BYTE(bg)); new_b = INT8_MULTIPLY(alpha, B_BYTE(fg)) + INT8_MULTIPLY(a_com, B_BYTE(bg)); new_a = INT8_MULTIPLY(alpha, A_BYTE(fg)) + INT8_MULTIPLY(a_com, A_BYTE(bg)); return BUILD_PIXEL(new_r, new_g, new_b, new_a); } VALUE oily_png_color_compose_quick(VALUE self, VALUE fg_color, VALUE bg_color) { UNUSED_PARAMETER(self); return UINT2NUM(oily_png_compose_color(NUM2UINT(fg_color), NUM2UINT(bg_color))); } VALUE oily_png_euclidean_distance_rgba(VALUE self, VALUE color_after, VALUE color_before) { UNUSED_PARAMETER(self); return rb_float_new(sqrt(pow((R_BYTE(NUM2UINT(color_after)) - R_BYTE(NUM2UINT(color_before))), 2) + pow((G_BYTE(NUM2UINT(color_after)) - G_BYTE(NUM2UINT(color_before))), 2) + pow((B_BYTE(NUM2UINT(color_after)) - B_BYTE(NUM2UINT(color_before))), 2) + pow((A_BYTE(NUM2UINT(color_after)) - A_BYTE(NUM2UINT(color_before))), 2))); } VALUE oily_png_color_r(VALUE self, VALUE value) { UNUSED_PARAMETER(self); return INT2FIX(R_BYTE(NUM2UINT(value))); } VALUE oily_png_color_g(VALUE self, VALUE value) { UNUSED_PARAMETER(self); return INT2FIX(G_BYTE(NUM2UINT(value))); } VALUE oily_png_color_b(VALUE self, VALUE value) { UNUSED_PARAMETER(self); return INT2FIX(B_BYTE(NUM2UINT(value))); } VALUE oily_png_color_a(VALUE self, VALUE value) { UNUSED_PARAMETER(self); return INT2FIX(A_BYTE(NUM2UINT(value))); } oily_png-1.2.1/ext/oily_png/color.h000066400000000000000000000023541276602571700172630ustar00rootroot00000000000000#ifndef OILY_PNG_COLOR_H #define OILY_PNG_COLOR_H #define R_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0xff000000) >> 24)) #define G_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x00ff0000) >> 16)) #define B_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x0000ff00) >> 8)) #define A_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x000000ff))) #define BUILD_PIXEL(r, g, b, a) (((PIXEL) (r) << 24) + ((PIXEL) (g) << 16) + ((PIXEL) (b) << 8) + (PIXEL) (a)) #define INT8_MULTIPLY(a, b) (((((a) * (b) + 0x80) >> 8) + ((a) * (b) + 0x80)) >> 8) /* Ruby replacement method for color composition using alpha transparency. This method should replace ChunkyPNG::Color.compose_quick */ VALUE oily_png_color_compose_quick(VALUE self, VALUE fg_color, VALUE bg_color); /* Color composition using alpha transparency. */ PIXEL oily_png_compose_color(PIXEL fg, PIXEL bg); PIXEL oily_png_color_interpolate_quick(PIXEL fg, PIXEL bg, int alpha); /* Color comparison */ VALUE oily_png_euclidean_distance_rgba(VALUE self, VALUE color_after, VALUE color_before); /* Accessors */ VALUE oily_png_color_r(VALUE self, VALUE pixel); VALUE oily_png_color_g(VALUE self, VALUE pixel); VALUE oily_png_color_b(VALUE self, VALUE pixel); VALUE oily_png_color_a(VALUE self, VALUE pixel); #endif oily_png-1.2.1/ext/oily_png/extconf.rb000066400000000000000000000001101276602571700177530ustar00rootroot00000000000000require 'mkmf' $CFLAGS << ' -Wall' create_makefile('oily_png/oily_png') oily_png-1.2.1/ext/oily_png/oily_png_ext.c000066400000000000000000000057521276602571700206450ustar00rootroot00000000000000#include "oily_png_ext.h" void Init_oily_png() { VALUE OilyPNG = rb_define_module("OilyPNG"); VALUE OilyPNG_Canvas = rb_define_module_under(OilyPNG, "Resampling"); rb_define_private_method(OilyPNG_Canvas, "steps_residues", oily_png_canvas_steps_residues, 2); rb_define_private_method(OilyPNG_Canvas, "steps", oily_png_canvas_steps, 2); rb_define_method(OilyPNG_Canvas, "resample_nearest_neighbor!", oily_png_canvas_resample_nearest_neighbor_bang, 2); rb_define_method(OilyPNG_Canvas, "resample_bilinear!", oily_png_canvas_resample_bilinear_bang, 2); // Setup decoding module VALUE OilyPNG_PNGDecoding = rb_define_module_under(OilyPNG, "PNGDecoding"); rb_define_method(OilyPNG_PNGDecoding, "decode_png_image_pass", oily_png_decode_png_image_pass, 7); // Setup encoding module VALUE OilyPNG_PNGEncoding = rb_define_module_under(OilyPNG, "PNGEncoding"); rb_define_method(OilyPNG_PNGEncoding, "encode_png_image_pass_to_stream", oily_png_encode_png_image_pass_to_stream, 4); // Setup Color module VALUE OilyPNG_Color = rb_define_module_under(OilyPNG, "Color"); rb_define_method(OilyPNG_Color, "compose_quick", oily_png_color_compose_quick, 2); rb_define_method(OilyPNG_Color, "euclidean_distance_rgba", oily_png_euclidean_distance_rgba, 2); rb_define_method(OilyPNG_Color, "r", oily_png_color_r, 1); rb_define_method(OilyPNG_Color, "g", oily_png_color_g, 1); rb_define_method(OilyPNG_Color, "b", oily_png_color_b, 1); rb_define_method(OilyPNG_Color, "a", oily_png_color_a, 1); // Setup Operations module VALUE OilyPNG_Operations = rb_define_module_under(OilyPNG, "Operations"); rb_define_method(OilyPNG_Operations, "compose!", oily_png_compose_bang, -1); rb_define_method(OilyPNG_Operations, "replace!", oily_png_replace_bang, -1); rb_define_method(OilyPNG_Operations, "rotate_left!", oily_png_rotate_left_bang, 0); rb_define_method(OilyPNG_Operations, "rotate_right!", oily_png_rotate_right_bang, 0); } char oily_png_samples_per_pixel(char color_mode) { switch (color_mode) { case OILY_PNG_COLOR_GRAYSCALE: return 1; case OILY_PNG_COLOR_TRUECOLOR: return 3; case OILY_PNG_COLOR_INDEXED: return 1; case OILY_PNG_COLOR_GRAYSCALE_ALPHA: return 2; case OILY_PNG_COLOR_TRUECOLOR_ALPHA: return 4; default: rb_raise(rb_eRuntimeError, "Unsupported color mode: %d", color_mode); return 0; } } char oily_png_pixel_bitsize(char color_mode, char bit_depth) { return oily_png_samples_per_pixel(color_mode) * bit_depth; } char oily_png_pixel_bytesize(char color_mode, char bit_depth) { return (bit_depth < 8) ? 1 : (oily_png_pixel_bitsize(color_mode, bit_depth) + 7) >> 3; } long oily_png_scanline_bytesize(char color_mode, char bit_depth, long width) { return (8 + ((oily_png_pixel_bitsize(color_mode, bit_depth) * width) + 7)) >> 3; } long oily_png_pass_bytesize(char color_mode, char bit_depth, long width, long height) { return (width == 0 || height == 0) ? 0 : (oily_png_scanline_bytesize(color_mode, bit_depth, width)) * height; } oily_png-1.2.1/ext/oily_png/oily_png_ext.h000066400000000000000000000041241276602571700206420ustar00rootroot00000000000000#ifndef OILY_PNG_OILY_PNG_EXT #define OILY_PNG_OILY_PNG_EXT #include "ruby.h" #define RSTRING_NOT_MODIFIED // PNG color mode constants #define OILY_PNG_COLOR_GRAYSCALE 0 #define OILY_PNG_COLOR_TRUECOLOR 2 #define OILY_PNG_COLOR_INDEXED 3 #define OILY_PNG_COLOR_GRAYSCALE_ALPHA 4 #define OILY_PNG_COLOR_TRUECOLOR_ALPHA 6 // PNG filter constants #define OILY_PNG_FILTER_NONE 0 #define OILY_PNG_FILTER_SUB 1 #define OILY_PNG_FILTER_UP 2 #define OILY_PNG_FILTER_AVERAGE 3 #define OILY_PNG_FILTER_PAETH 4 // Macro to surpress warnings about unused parameters. #define UNUSED_PARAMETER(param) (void) param // Type definitions typedef uint32_t PIXEL; // Pixels use 32 bits unsigned integers typedef unsigned char BYTE; // Bytes use 8 bits unsigned integers #include "png_decoding.h" #include "png_encoding.h" #include "color.h" #include "operations.h" #include "resampling.h" /* Initialize the extension by creating the OilyPNG modules, and registering the encoding and decoding replacement functions. Note, this does not actually replace functionality in ChunkyPNG; you will need to extend the ChunkyPNG::Canvas class with the OilyPNG::PNGDecoding module to speed up decoding, and include OilyPNG::PNGEncoding into the same class to speed up encoding. This is done in lib/oily_png.rb */ void Init_oily_png(); /* Returns the number of samples per pixel for a given color mode */ char oily_png_samples_per_pixel(char color_mode); /* Returns the number of bits per pixel for a given color mode and bit depth. */ char oily_png_pixel_bitsize(char color_mode, char bit_depth); /* Returns the number of bytes per pixel for a given color mode and bit depth. */ char oily_png_pixel_bytesize(char color_mode, char bit_depth); /* Returns the number of bytes per scanline for a given width, color mode and bit depth. */ long oily_png_scanline_bytesize(char color_mode, char bit_depth, long width); /* Returns the number of bytes in an image pass with the given properties. */ long oily_png_pass_bytesize(char color_mode, char bit_depth, long width, long height); #endif oily_png-1.2.1/ext/oily_png/operations.c000066400000000000000000000164001276602571700203200ustar00rootroot00000000000000#include "oily_png_ext.h" void oily_png_check_size_constraints(long self_width, long self_height, long other_width, long other_height, long offset_x, long offset_y){ // For now, these raise a standard runtime error. They should however raise custom exception classes (OutOfBounds) if(self_width < other_width + offset_x){ rb_raise(rb_eRuntimeError, "Background image width is too small!"); } if(self_height < other_height + offset_y){ rb_raise(rb_eRuntimeError, "Background image height is too small!"); } } VALUE oily_png_compose_bang(int argc, VALUE *argv, VALUE self) { // Corresponds to the other image(foreground) that we want to compose onto this one(background). VALUE other; // The offsets are optional arguments, so these may or may not be null pointers. // We'll prefix them with 'opt' to identify this. VALUE opt_offset_x; VALUE opt_offset_y; // Scan the passed in arguments, and populate the above-declared variables. Notice that '12' // specifies that oily_png_compose_bang takes in 1 required parameter, and 2 optional ones (the offsets) rb_scan_args(argc, argv, "12", &other,&opt_offset_x,&opt_offset_y); // Regardless of whether offsets were provided, we must specify a default value for them since they will // be used in calculating the position of the composed element. long offset_x = 0; long offset_y = 0; // If offsets were provided, then the opt_offset_* variables will not be null pointers. FIXNUM_P checks // whether they point to a fixnum object. If they do, then we can safely assign our offset_* variables to the values. if(FIXNUM_P(opt_offset_x)){ offset_x = FIX2LONG(opt_offset_x); } if(FIXNUM_P(opt_offset_y)){ offset_y = FIX2LONG(opt_offset_y); } // Get the dimension data for both foreground and background images. long self_width = FIX2LONG(rb_funcall(self, rb_intern("width"), 0)); long self_height = FIX2LONG(rb_funcall(self, rb_intern("height"), 0)); long other_width = FIX2LONG(rb_funcall(other, rb_intern("width"), 0)); long other_height = FIX2LONG(rb_funcall(other, rb_intern("height"), 0)); // Make sure that the 'other' image fits within the current image. If it doesn't, an exception is raised // and the operation should be aborted. oily_png_check_size_constraints( self_width, self_height, other_width, other_height, offset_x, offset_y ); // Get the pixel data for both the foreground(other) and background(self) pixels. VALUE* bg_pixels = RARRAY_PTR(rb_funcall(self, rb_intern("pixels"), 0)); VALUE* fg_pixels = RARRAY_PTR(rb_funcall(other, rb_intern("pixels"), 0)); long x = 0; long y = 0; long bg_index = 0; // corresponds to the current index in the bg_pixels array. for( y = 0; y < other_height; y++ ){ for( x = 0; x < other_width; x++ ){ // We need to find the value of bg_index twice, so we only calculate and store it once. bg_index = ( x + offset_x ) + ( y + offset_y ) * self_width; // Replace the background pixel with the composition of background + foreground bg_pixels[bg_index] = UINT2NUM( oily_png_compose_color( NUM2UINT( fg_pixels[x+ y * other_width] ), NUM2UINT( bg_pixels[bg_index] ) ) ); } } return self; } VALUE oily_png_replace_bang(int argc, VALUE *argv, VALUE self) { // Corresponds to the other image(foreground) that we want to compose onto this one(background). VALUE other; // The offsets are optional arguments, so these may or may not be null pointers. // We'll prefix them with 'opt' to identify this. VALUE opt_offset_x; VALUE opt_offset_y; // Scan the passed in arguments, and populate the above-declared variables. Notice that '12' // specifies that oily_png_compose_bang takes in 1 required parameter, and 2 optional ones (the offsets) rb_scan_args(argc, argv, "12", &other,&opt_offset_x,&opt_offset_y); // Regardless of whether offsets were provided, we must specify a default value for them since they will // be used in calculating the position of the composed element. long offset_x = 0; long offset_y = 0; // If offsets were provided, then the opt_offset_* variables will not be null pointers. FIXNUM_P checks // whether they point to a fixnum object. If they do, then we can safely assign our offset_* variables to the values. if(FIXNUM_P(opt_offset_x)){ offset_x = FIX2LONG(opt_offset_x); } if(FIXNUM_P(opt_offset_y)){ offset_y = FIX2LONG(opt_offset_y); } // Get the dimension data for both foreground and background images. long self_width = FIX2LONG(rb_funcall(self, rb_intern("width"), 0)); long self_height = FIX2LONG(rb_funcall(self, rb_intern("height"), 0)); long other_width = FIX2LONG(rb_funcall(other, rb_intern("width"), 0)); long other_height = FIX2LONG(rb_funcall(other, rb_intern("height"), 0)); // Make sure that the 'other' image fits within the current image. If it doesn't, an exception is raised // and the operation should be aborted. oily_png_check_size_constraints( self_width, self_height, other_width, other_height, offset_x, offset_y ); // Get the pixel data for both the foreground(other) and background(self) pixels. VALUE* bg_pixels = RARRAY_PTR(rb_funcall(self, rb_intern("pixels"), 0)); VALUE* fg_pixels = RARRAY_PTR(rb_funcall(other, rb_intern("pixels"), 0)); long x = 0; long y = 0; long bg_index = 0; // corresponds to the current index in the bg_pixels array. for( y = 0; y < other_height; y++ ){ for( x = 0; x < other_width; x++ ){ // We need to find the value of bg_index twice, so we only calculate and store it once. bg_index = ( x + offset_x ) + ( y + offset_y ) * self_width; // Replace the background pixel with the composition of background + foreground bg_pixels[bg_index] = fg_pixels[x+ y * other_width]; } } return self; } VALUE oily_png_rotate_left_bang(VALUE self){ int store_at; VALUE pixel_value; int canvas_width = NUM2INT(rb_funcall(self, rb_intern("width"), 0)); int canvas_height = NUM2INT(rb_funcall(self, rb_intern("height"), 0)); VALUE original_pixels = rb_funcall(self, rb_intern("pixels"), 0); VALUE new_pixels = rb_ary_dup(original_pixels); int i, j; for( j = 0 ; j < canvas_width; j++ ){ for( i = 0 ; i < canvas_height; i++ ){ store_at = (canvas_width - 1 - j)*canvas_height + i; pixel_value = rb_ary_entry(original_pixels, i*canvas_width + j); rb_ary_store(new_pixels, store_at, pixel_value ); } } rb_funcall(self, rb_intern("replace_canvas!"), 3, INT2NUM(canvas_height), INT2NUM(canvas_width), new_pixels); return self; } VALUE oily_png_rotate_right_bang(VALUE self){ int store_at; VALUE pixel_value; int canvas_width = NUM2INT(rb_funcall(self, rb_intern("width"), 0)); int canvas_height = NUM2INT(rb_funcall(self, rb_intern("height"), 0)); VALUE original_pixels = rb_funcall(self, rb_intern("pixels"), 0); VALUE new_pixels = rb_ary_dup(original_pixels); int i, j; for( j = 0; j < canvas_width; j++ ){ for( i = 0; i < canvas_height; i++ ){ store_at = j * canvas_height + (canvas_height - i - 1); pixel_value = rb_ary_entry(original_pixels, i*canvas_width + j); rb_ary_store(new_pixels, store_at, pixel_value ); } } rb_funcall(self, rb_intern("replace_canvas!"), 3, INT2NUM(canvas_height), INT2NUM(canvas_width), new_pixels); return self; }oily_png-1.2.1/ext/oily_png/operations.h000066400000000000000000000034351276602571700203310ustar00rootroot00000000000000#ifndef OILY_PNG_OPERATIONS_H #define OILY_PNG_OPERATIONS_H /* Checks whether an image 'other' can fits into 'self'. Takes offset into account. An exception is raised if the check fails. Instead of taking in an object 'self' and an object 'other' and then calculating their parameters, we ask for the respective height and width directly. This is because these variables will need to be calculated by 'rb_intern()' within the method calling oily_png_check_size_constraints (ex: oily_png_compose), so there's no use in calculating them twice. */ void oily_png_check_size_constraints(long self_width, long self_height, long other_width, long other_height, long offset_x, long offset_y); /* C replacement method for composing another image onto this image using alpha blending. TODO: Implement functionality with ChunkyPNG and OilyPNG so that an image can be composited onto another regardless of its size: however, only the intersecting elements of both images should be mixed. This method should replace ChunkyPNG::Canvas.compose! */ VALUE oily_png_compose_bang(int argc, VALUE *argv, VALUE c); /* C replacement method for composing another image onto this image by simply replacing pixels. TODO: Implement functionality with ChunkyPNG and OilyPNG so that an image can be composited onto another regardless of its size: however, only the intersecting elements of both images should be mixed. This method should replace ChunkyPNG::Canvas.replace! */ VALUE oily_png_replace_bang(int argc, VALUE *argv, VALUE c); /* C replacement method for rotating the image 90 degrees counter-clockwise. */ VALUE oily_png_rotate_left_bang(VALUE self); /* C replacement method for rotating the image 90 degrees clockwise. */ VALUE oily_png_rotate_right_bang(VALUE self); #endif oily_png-1.2.1/ext/oily_png/png_decoding.c000066400000000000000000000341051276602571700205570ustar00rootroot00000000000000#include "oily_png_ext.h" ///////////////////////////////////////////////////////////////////// // UNFILTERING SCANLINES ///////////////////////////////////////////////////////////////////// // Decodes a SUB filtered scanline at the given position in the byte array void oily_png_decode_filter_sub(BYTE* bytes, long pos, long line_length, char pixel_size) { long i; for (i = 1 + pixel_size; i < line_length; i++) { UNFILTER_BYTE(bytes[pos + i], bytes[pos + i - pixel_size]); } } // Decodes an UP filtered scanline at the given position in the byte array void oily_png_decode_filter_up(BYTE* bytes, long pos, long line_size, char pixel_size) { UNUSED_PARAMETER(pixel_size); long i; if (pos >= line_size) { // The first line is not filtered because there is no privous line for (i = 1; i < line_size; i++) { UNFILTER_BYTE(bytes[pos + i], bytes[pos + i - line_size]); } } } // Decodes an AVERAGE filtered scanline at the given position in the byte array void oily_png_decode_filter_average(BYTE* bytes, long pos, long line_size, char pixel_size) { long i; BYTE a, b; for (i = 1; i < line_size; i++) { a = (i > pixel_size) ? bytes[pos + i - pixel_size] : 0; b = (pos >= line_size) ? bytes[pos + i - line_size] : 0; UNFILTER_BYTE(bytes[pos + i], (a + b) >> 1); } } // Decodes a PAETH filtered scanline at the given position in the byte array void oily_png_decode_filter_paeth(BYTE* bytes, long pos, long line_size, char pixel_size) { BYTE a, b, c, pr; long i, p, pa, pb, pc; for (i = 1; i < line_size; i++) { a = (i > pixel_size) ? bytes[pos + i - pixel_size] : 0; b = (pos >= line_size) ? bytes[pos + i - line_size] : 0; c = (pos >= line_size && i > pixel_size) ? bytes[pos + i - line_size - pixel_size] : 0; p = a + b - c; pa = (p > a) ? p - a : a - p; pb = (p > b) ? p - b : b - p; pc = (p > c) ? p - c : c - p; pr = (pa <= pb) ? (pa <= pc ? a : c) : (pb <= pc ? b : c); UNFILTER_BYTE(bytes[pos + i], pr); } } ///////////////////////////////////////////////////////////////////// // BIT HANDLING ///////////////////////////////////////////////////////////////////// BYTE oily_png_extract_1bit_element(BYTE* bytes, long start, long x) { BYTE byte = bytes[start + 1 + (x >> 3)]; char bitshift = 7 - (x & (BYTE) 0x07); return (byte & (0x01 << bitshift)) >> bitshift; } BYTE oily_png_extract_2bit_element(BYTE* bytes, long start, long x) { BYTE byte = bytes[start + 1 + (x >> 2)]; char bitshift = (6 - ((x & (BYTE) 0x03) << 1)); return (byte & (0x03 << bitshift)) >> bitshift; } BYTE oily_png_extract_4bit_element(BYTE* bytes, long start, long x) { return ((x & 0x01) == 0) ? ((bytes[(start) + 1 + ((x) >> 1)] & (BYTE) 0xf0) >> 4) : (bytes[(start) + 1 + ((x) >> 1)] & (BYTE) 0x0f); } BYTE oily_png_resample_1bit_element(BYTE* bytes, long start, long x) { BYTE value = oily_png_extract_1bit_element(bytes, start, x); return (value == 0) ? 0x00 : 0xff; } BYTE oily_png_resample_2bit_element(BYTE* bytes, long start, long x) { return oily_png_extract_2bit_element(bytes, start, x) * 85; } BYTE oily_png_resample_4bit_element(BYTE* bytes, long start, long x) { return oily_png_extract_4bit_element(bytes, start, x) * 17; } ///////////////////////////////////////////////////////////////////// // PIXEL DECODING SCANLINES ///////////////////////////////////////////////////////////////////// void oily_png_decode_scanline_grayscale_1bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, oily_png_resample_1bit_element(bytes, start, x), oily_png_resample_1bit_element(bytes, start, x), oily_png_resample_1bit_element(bytes, start, x), 0xff); } } void oily_png_decode_scanline_grayscale_2bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, oily_png_resample_2bit_element(bytes, start, x), oily_png_resample_2bit_element(bytes, start, x), oily_png_resample_2bit_element(bytes, start, x), 0xff); } } void oily_png_decode_scanline_grayscale_4bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, oily_png_resample_4bit_element(bytes, start, x), oily_png_resample_4bit_element(bytes, start, x), oily_png_resample_4bit_element(bytes, start, x), 0xff); } } void oily_png_decode_scanline_grayscale_8bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + x], bytes[start + 1 + x], bytes[start + 1 + x], 0xff); } } void oily_png_decode_scanline_grayscale_16bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 2)], bytes[start + 1 + (x * 2)], bytes[start + 1 + (x * 2)], 0xff); } } void oily_png_decode_scanline_grayscale_alpha_8bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 2) + 0], bytes[start + 1 + (x * 2) + 0], bytes[start + 1 + (x * 2) + 0], bytes[start + 1 + (x * 2) + 1]); } } void oily_png_decode_scanline_grayscale_alpha_16bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 4) + 0], bytes[start + 1 + (x * 4) + 0], bytes[start + 1 + (x * 4) + 0], bytes[start + 1 + (x * 4) + 2]); } } void oily_png_decode_scanline_indexed_1bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_PALLETE(pixels, decoding_palette, oily_png_extract_1bit_element(bytes, start, x)); } } void oily_png_decode_scanline_indexed_2bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_PALLETE(pixels, decoding_palette, oily_png_extract_2bit_element(bytes, start, x)); } } void oily_png_decode_scanline_indexed_4bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_PALLETE(pixels, decoding_palette, oily_png_extract_4bit_element(bytes, start, x)); } } void oily_png_decode_scanline_indexed_8bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_PALLETE(pixels, decoding_palette, bytes[start + 1 + x]); } } void oily_png_decode_scanline_truecolor_8bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 3) + 0], bytes[start + 1 + (x * 3) + 1], bytes[start + 1 + (x * 3) + 2], 0xff); } } void oily_png_decode_scanline_truecolor_16bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 6) + 0], bytes[start + 1 + (x * 6) + 2], bytes[start + 1 + (x * 6) + 4], 0xff); } } void oily_png_decode_scanline_truecolor_alpha_8bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 4) + 0], bytes[start + 1 + (x * 4) + 1], bytes[start + 1 + (x * 4) + 2], bytes[start + 1 + (x * 4) + 3]); } } void oily_png_decode_scanline_truecolor_alpha_16bit(VALUE pixels, BYTE* bytes, long start, long width, VALUE decoding_palette) { UNUSED_PARAMETER(decoding_palette); long x; for (x = 0; x < width; x++) { ADD_PIXEL_FROM_RGBA(pixels, bytes[start + 1 + (x * 8) + 0], bytes[start + 1 + (x * 8) + 2], bytes[start + 1 + (x * 8) + 4], bytes[start + 1 + (x * 8) + 6]); } } scanline_decoder_func oily_png_decode_scanline_func(int color_mode, int bit_depth) { switch (color_mode) { case OILY_PNG_COLOR_GRAYSCALE: switch (bit_depth) { case 1: return &oily_png_decode_scanline_grayscale_1bit; case 2: return &oily_png_decode_scanline_grayscale_2bit; case 4: return &oily_png_decode_scanline_grayscale_4bit; case 8: return &oily_png_decode_scanline_grayscale_8bit; case 16: return &oily_png_decode_scanline_grayscale_16bit; default: return NULL; } case OILY_PNG_COLOR_TRUECOLOR: switch (bit_depth) { case 8: return &oily_png_decode_scanline_truecolor_8bit; case 16: return &oily_png_decode_scanline_truecolor_16bit; default: return NULL; } case OILY_PNG_COLOR_INDEXED: switch (bit_depth) { case 1: return &oily_png_decode_scanline_indexed_1bit; case 2: return &oily_png_decode_scanline_indexed_2bit; case 4: return &oily_png_decode_scanline_indexed_4bit; case 8: return &oily_png_decode_scanline_indexed_8bit; default: return NULL; } case OILY_PNG_COLOR_GRAYSCALE_ALPHA: switch (bit_depth) { case 8: return &oily_png_decode_scanline_grayscale_alpha_8bit; case 16: return &oily_png_decode_scanline_grayscale_alpha_16bit; default: return NULL; } case OILY_PNG_COLOR_TRUECOLOR_ALPHA: switch (bit_depth) { case 8: return &oily_png_decode_scanline_truecolor_alpha_8bit; case 16: return &oily_png_decode_scanline_truecolor_alpha_16bit; default: return NULL; } default: return NULL; } } ///////////////////////////////////////////////////////////////////// // DECODING AN IMAGE PASS ///////////////////////////////////////////////////////////////////// VALUE oily_png_decode_palette(VALUE decoding_palette) { if (decoding_palette != Qnil) { VALUE decoding_map = rb_iv_get(decoding_palette, "@decoding_map"); if (rb_funcall(decoding_map, rb_intern("kind_of?"), 1, rb_cArray) == Qtrue) { return decoding_map; } } rb_raise(rb_eRuntimeError, "Could not retrieve a decoding palette for this image!"); return Qnil; } VALUE oily_png_decode_png_image_pass(VALUE self, VALUE stream, VALUE width, VALUE height, VALUE color_mode, VALUE depth, VALUE start_pos, VALUE decoding_palette) { VALUE pixels = rb_ary_new(); if ((FIX2LONG(height) > 0) && (FIX2LONG(width) > 0)) { char pixel_size = oily_png_pixel_bytesize(FIX2INT(color_mode), FIX2INT(depth)); long line_size = oily_png_scanline_bytesize(FIX2INT(color_mode), FIX2INT(depth), FIX2LONG(width)); long pass_size = oily_png_pass_bytesize(FIX2INT(color_mode), FIX2INT(depth), FIX2LONG(width), FIX2LONG(height)); // Make sure that the stream is large enough to contain our pass. if (RSTRING_LEN(stream) < pass_size + FIX2LONG(start_pos)) { rb_raise(rb_eRuntimeError, "The length of the stream is too short to contain the image!"); } // Copy the bytes for this pass from the stream to a separate location // so we can work on this byte array directly. BYTE* bytes = ALLOC_N(BYTE, pass_size); memcpy(bytes, RSTRING_PTR(stream) + FIX2LONG(start_pos), pass_size); // Get the decoding palette for indexed images. VALUE decoding_map = Qnil; if (FIX2INT(color_mode) == OILY_PNG_COLOR_INDEXED) { decoding_map = oily_png_decode_palette(decoding_palette); } // Select the scanline decoder function for this color mode and bit depth. scanline_decoder_func scanline_decoder = oily_png_decode_scanline_func(FIX2INT(color_mode), FIX2INT(depth)); if (scanline_decoder == NULL) { rb_raise(rb_eRuntimeError, "No decoder for color mode %d and bit depth %d", FIX2INT(color_mode), FIX2INT(depth)); } long y, line_start; for (y = 0; y < FIX2LONG(height); y++) { line_start = y * line_size; // Apply filering to the line switch (bytes[line_start]) { case OILY_PNG_FILTER_NONE: break; case OILY_PNG_FILTER_SUB: oily_png_decode_filter_sub( bytes, line_start, line_size, pixel_size); break; case OILY_PNG_FILTER_UP: oily_png_decode_filter_up( bytes, line_start, line_size, pixel_size); break; case OILY_PNG_FILTER_AVERAGE: oily_png_decode_filter_average( bytes, line_start, line_size, pixel_size); break; case OILY_PNG_FILTER_PAETH: oily_png_decode_filter_paeth( bytes, line_start, line_size, pixel_size); break; default: rb_raise(rb_eRuntimeError, "Filter type not supported: %d", bytes[line_start]); } // Set the filter byte to 0 because the bytearray is now unfiltered. bytes[line_start] = OILY_PNG_FILTER_NONE; scanline_decoder(pixels, bytes, line_start, FIX2LONG(width), decoding_map); } xfree(bytes); } // Now, return a new ChunkyPNG::Canvas instance with the decoded pixels. return rb_funcall(self, rb_intern("new"), 3, width, height, pixels); } oily_png-1.2.1/ext/oily_png/png_decoding.h000066400000000000000000000022331276602571700205610ustar00rootroot00000000000000#ifndef OILY_PNG_PNG_DECODING_H #define OILY_PNG_PNG_DECODING_H #define UNFILTER_BYTE(byte, adjustment) byte = (BYTE) (((byte) + (adjustment)) & 0x000000ff) #define ADD_PIXEL_FROM_PALLETE(pixels, decoding_palette, palette_entry) \ if (RARRAY_LEN(decoding_palette) > (palette_entry)) { \ rb_ary_push(pixels, rb_ary_entry(decoding_palette, (palette_entry))); \ } else { \ rb_raise(rb_eRuntimeError, "The decoding palette does not have %d entries!", (palette_entry)); \ } #define ADD_PIXEL_FROM_RGBA(pixels, r, g, b, a) rb_ary_push(pixels, UINT2NUM(BUILD_PIXEL(r,g,b,a))); typedef void(*scanline_decoder_func)(VALUE, BYTE*, long, long, VALUE); /* Decodes an image pass from the given byte stream at the given position. A normal PNG will only have one pass that consumes the entire stream, while an interlaced image requires 7 passes which are loaded from different starting positions. This function shouild replace ChunkyPNG::Canvas::PNGDecoding.decode_png_image_pass */ VALUE oily_png_decode_png_image_pass(VALUE self, VALUE stream, VALUE width, VALUE height, VALUE color_mode, VALUE depth, VALUE start_pos, VALUE decoding_palette); #endif oily_png-1.2.1/ext/oily_png/png_encoding.c000066400000000000000000000307221276602571700205720ustar00rootroot00000000000000#include "oily_png_ext.h" ///// Scanline filtering functions ////////////////////////////////////////// void oily_png_encode_filter_sub(BYTE* bytes, long pos, long line_size, char pixel_size) { long x; for (x = line_size - 1; x > pixel_size; x--) { FILTER_BYTE(bytes[pos + x], bytes[pos + x - pixel_size]); } } void oily_png_encode_filter_up(BYTE* bytes, long pos, long line_size, char pixel_size) { UNUSED_PARAMETER(pixel_size); long x; if (pos >= line_size) { for (x = line_size - 1; x > 0; x--) { FILTER_BYTE(bytes[pos + x], bytes[pos + x - line_size]); } } } void oily_png_encode_filter_average(BYTE* bytes, long pos, long line_size, char pixel_size) { long x; BYTE a, b; for (x = line_size - 1; x > 0; x--) { a = (x > pixel_size) ? bytes[pos + x - pixel_size] : 0; b = (pos >= line_size) ? bytes[pos + x - line_size] : 0; FILTER_BYTE(bytes[pos + x], (a + b) >> 1); } } void oily_png_encode_filter_paeth(BYTE* bytes, long pos, long line_size, char pixel_size) { long x; int p, pa, pb, pc; BYTE a, b, c, pr; for (x = line_size - 1; x > 0; x--) { a = (x > pixel_size) ? bytes[pos + x - pixel_size] : 0; b = (pos >= line_size) ? bytes[pos + x - line_size] : 0; c = (pos >= line_size && x > pixel_size) ? bytes[pos + x - line_size - pixel_size] : 0; p = a + b - c; pa = abs(p - a); pb = abs(p - b); pc = abs(p - c); pr = (pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c); FILTER_BYTE(bytes[pos + x], pr); } } ///// Scanline encoding functions ////////////////////////////////////////// // Assume R == G == B. ChunkyPNG uses the B byte fot performance reasons. // We'll uses the same to remain compatible with ChunkyPNG. void oily_png_encode_scanline_grayscale_1bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; BYTE p1, p2, p3, p4, p5, p6, p7, p8; for (x = 0; x < width; x += 8) { p1 = (x + 0 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 0))) >> 7); p2 = (x + 1 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 1))) >> 7); p3 = (x + 2 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 2))) >> 7); p4 = (x + 3 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 3))) >> 7); p5 = (x + 4 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 4))) >> 7); p6 = (x + 5 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 5))) >> 7); p7 = (x + 6 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 6))) >> 7); p8 = (x + 7 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 7))) >> 7); bytes[x >> 3] = (BYTE) ((p1 << 7) | (p2 << 6) | (p3 << 5) | (p4 << 4) | (p5 << 3) | (p6 << 2) | (p7 << 1) | (p8)); } } // Assume R == G == B. ChunkyPNG uses the B byte fot performance reasons. // We'll uses the same to remain compatible with ChunkyPNG. void oily_png_encode_scanline_grayscale_2bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; BYTE p1, p2, p3, p4; for (x = 0; x < width; x += 4) { p1 = (x + 0 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 0))) >> 6); p2 = (x + 1 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 1))) >> 6); p3 = (x + 2 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 2))) >> 6); p4 = (x + 3 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 3))) >> 6); bytes[x >> 2] = (BYTE) ((p1 << 6) | (p2 << 4) | (p3 << 2) | (p4)); } } // Assume R == G == B. ChunkyPNG uses the B byte fot performance reasons. // We'll uses the same to remain compatible with ChunkyPNG. void oily_png_encode_scanline_grayscale_4bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; BYTE p1, p2; for (x = 0; x < width; x += 2) { p1 = (x + 0 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 0))) >> 4); p2 = (x + 1 >= width) ? 0 : (B_BYTE(NUM2UINT(rb_ary_entry(pixels, y * width + x + 1))) >> 4); bytes[x >> 1] = (BYTE) ((p1 << 4) | (p2)); } } // Assume R == G == B. ChunkyPNG uses the B byte fot performance reasons. // We'll uses the same to remain compatible with ChunkyPNG. void oily_png_encode_scanline_grayscale_8bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; PIXEL pixel; for (x = 0; x < width; x++) { pixel = NUM2UINT(rb_ary_entry(pixels, y * width + x)); bytes[x] = B_BYTE(pixel); } } // Assume R == G == B. ChunkyPNG uses the B byte fot performance reasons. // We'll uses the same to remain compatible with ChunkyPNG. void oily_png_encode_scanline_grayscale_alpha_8bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; PIXEL pixel; for (x = 0; x < width; x++) { pixel = NUM2UINT(rb_ary_entry(pixels, y * width + x)); bytes[x * 2 + 0] = B_BYTE(pixel); bytes[x * 2 + 1] = A_BYTE(pixel); } } void oily_png_encode_scanline_indexed_8bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { long x; for (x = 0; x < width; x++) { bytes[x] = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x); } } void oily_png_encode_scanline_indexed_4bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { long x; BYTE p1, p2; for (x = 0; x < width; x += 2) { p1 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 0); p2 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 1); bytes[x >> 1] = (BYTE) ((p1 << 4) | (p2)); } } void oily_png_encode_scanline_indexed_2bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { long x; BYTE p1, p2, p3, p4; for (x = 0; x < width; x += 4) { p1 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 0); p2 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 1); p3 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 2); p4 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 3); bytes[x >> 2] = (BYTE) ((p1 << 6) | (p2 << 4) | (p3 << 2) | (p4)); } } void oily_png_encode_scanline_indexed_1bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { long x; BYTE p1, p2, p3, p4, p5, p6, p7, p8; for (x = 0; x < width; x += 8) { p1 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 0); p2 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 1); p3 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 2); p4 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 3); p5 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 4); p6 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 5); p7 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 6); p8 = ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x + 7); bytes[x >> 3] = (BYTE) ((p1 << 7) | (p2 << 6) | (p3 << 5) | (p4 << 4) | (p5 << 3) | (p6 << 2) | (p7 << 1) | (p8)); } } void oily_png_encode_scanline_truecolor_8bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; PIXEL pixel; for (x = 0; x < width; x++) { pixel = NUM2UINT(rb_ary_entry(pixels, y * width + x)); bytes[x * 3 + 0] = R_BYTE(pixel); bytes[x * 3 + 1] = G_BYTE(pixel); bytes[x * 3 + 2] = B_BYTE(pixel); } } void oily_png_encode_scanline_truecolor_alpha_8bit(BYTE* bytes, VALUE pixels, long y, long width, VALUE encoding_palette) { UNUSED_PARAMETER(encoding_palette); long x; PIXEL pixel; for (x = 0; x < width; x++) { pixel = NUM2UINT(rb_ary_entry(pixels, y * width + x)); bytes[x * 4 + 0] = R_BYTE(pixel); bytes[x * 4 + 1] = G_BYTE(pixel); bytes[x * 4 + 2] = B_BYTE(pixel); bytes[x * 4 + 3] = A_BYTE(pixel); } } scanline_encoder_func oily_png_encode_scanline_func(char color_mode, char bit_depth) { switch (color_mode) { case OILY_PNG_COLOR_GRAYSCALE: switch (bit_depth) { case 8: return &oily_png_encode_scanline_grayscale_8bit; case 4: return &oily_png_encode_scanline_grayscale_4bit; case 2: return &oily_png_encode_scanline_grayscale_2bit; case 1: return &oily_png_encode_scanline_grayscale_1bit; default: return NULL; } case OILY_PNG_COLOR_GRAYSCALE_ALPHA: switch (bit_depth) { case 8: return &oily_png_encode_scanline_grayscale_alpha_8bit; default: return NULL; } case OILY_PNG_COLOR_INDEXED: switch (bit_depth) { case 8: return &oily_png_encode_scanline_indexed_8bit; case 4: return &oily_png_encode_scanline_indexed_4bit; case 2: return &oily_png_encode_scanline_indexed_2bit; case 1: return &oily_png_encode_scanline_indexed_1bit; default: return NULL; } case OILY_PNG_COLOR_TRUECOLOR: switch (bit_depth) { case 8: return &oily_png_encode_scanline_truecolor_8bit; default: return NULL; } case OILY_PNG_COLOR_TRUECOLOR_ALPHA: switch (bit_depth) { case 8: return &oily_png_encode_scanline_truecolor_alpha_8bit; default: return NULL; } default: return NULL; } } ///////////////////////////////////////////////////////////////////// // ENCODING AN IMAGE PASS ///////////////////////////////////////////////////////////////////// VALUE oily_png_encode_palette(VALUE self) { VALUE palette_instance = rb_funcall(self, rb_intern("encoding_palette"), 0); if (palette_instance != Qnil) { VALUE encoding_map = rb_iv_get(palette_instance, "@encoding_map"); if (rb_funcall(encoding_map, rb_intern("kind_of?"), 1, rb_cHash) == Qtrue) { return encoding_map; } } rb_raise(rb_eRuntimeError, "Could not retrieve a decoding palette for this image!"); return Qnil; } VALUE oily_png_encode_png_image_pass_to_stream(VALUE self, VALUE stream, VALUE color_mode, VALUE bit_depth, VALUE filtering) { UNUSED_PARAMETER(bit_depth); // Get the data char depth = (char) FIX2INT(bit_depth); long width = FIX2LONG(rb_funcall(self, rb_intern("width"), 0)); long height = FIX2LONG(rb_funcall(self, rb_intern("height"), 0)); VALUE pixels = rb_funcall(self, rb_intern("pixels"), 0); if (RARRAY_LEN(pixels) != width * height) { rb_raise(rb_eRuntimeError, "The number of pixels does not match the canvas dimensions."); } // Get the encoding palette if we're encoding to an indexed bytestream. VALUE encoding_palette = Qnil; if (FIX2INT(color_mode) == OILY_PNG_COLOR_INDEXED) { encoding_palette = oily_png_encode_palette(self); } char pixel_size = oily_png_pixel_bytesize(FIX2INT(color_mode), depth); long line_size = oily_png_scanline_bytesize(FIX2INT(color_mode), depth, width); long pass_size = oily_png_pass_bytesize(FIX2INT(color_mode), depth, width, height); // Allocate memory for the byte array. BYTE* bytes = ALLOC_N(BYTE, pass_size); // Get the scanline encoder function. scanline_encoder_func scanline_encoder = oily_png_encode_scanline_func(FIX2INT(color_mode), depth); if (scanline_encoder == NULL) { rb_raise(rb_eRuntimeError, "No encoder for color mode %d and bit depth %d", FIX2INT(color_mode), depth); } long y, pos; for (y = height - 1; y >= 0; y--) { pos = line_size * y; bytes[pos] = (BYTE) FIX2INT(filtering); scanline_encoder(bytes + pos + 1, pixels, y, width, encoding_palette); } if (FIX2INT(filtering) != OILY_PNG_FILTER_NONE) { // Get the scanline filter function void (*scanline_filter)(BYTE*, long, long, char) = NULL; switch (FIX2INT(filtering)) { case OILY_PNG_FILTER_SUB: scanline_filter = &oily_png_encode_filter_sub; break; case OILY_PNG_FILTER_UP: scanline_filter = &oily_png_encode_filter_up; break; case OILY_PNG_FILTER_AVERAGE: scanline_filter = &oily_png_encode_filter_average; break; case OILY_PNG_FILTER_PAETH: scanline_filter = &oily_png_encode_filter_paeth; break; default: rb_raise(rb_eRuntimeError, "Unsupported filter type: %d", FIX2INT(filtering)); } for (y = height - 1; y >= 0; y--) { scanline_filter(bytes, line_size * y, line_size, pixel_size); } } // Append to encoded image pass to the output stream. rb_str_cat(stream, (char*) bytes, pass_size); xfree(bytes); return Qnil; } oily_png-1.2.1/ext/oily_png/png_encoding.h000066400000000000000000000016151276602571700205760ustar00rootroot00000000000000#ifndef OILY_PNG_PNG_ENCODING_H #define OILY_PNG_PNG_ENCODING_H #define FILTER_BYTE(byte, adjustment) byte = (BYTE) (((byte) - (adjustment)) & 0x000000ff) #define ENCODING_PALETTE_INDEX(encoding_palette, pixels, width, y, x) (((x) < (width)) ? ((BYTE) NUM2UINT(rb_hash_aref(encoding_palette, rb_ary_entry(pixels, (y) * (width) + (x))))) : 0) typedef void(*scanline_encoder_func)(BYTE*, VALUE, long, long, VALUE); /* Encodes an image and append it to the stream. A normal PNG will only have one pass and call this method once, while interlaced images are split up in 7 distinct images. This method will be called for every one of these images, reusing the stream. This function should replace ChunkyPNG::Canvas::PNGEncoding.encode_png_image_pass_to_stream */ VALUE oily_png_encode_png_image_pass_to_stream(VALUE self, VALUE stream, VALUE color_mode, VALUE bit_depth, VALUE filtering); #endif oily_png-1.2.1/ext/oily_png/resampling.c000066400000000000000000000142411276602571700202770ustar00rootroot00000000000000#include "oily_png_ext.h" #include void oily_png_generate_steps_residues(long width, long new_width, long *steps, long *residues) { long base_step = width / new_width; long err_step = (width % new_width) << 1; long denominator = new_width << 1; long index; long err; /* We require an arithmetic modolus and rounding to the left of zero * This is standard Ruby behaviour (I hope!) but differs with how C/Java * typically handle integer division and modulo. But since we are workig * in mixed numbers, Ruby's convention is especially convienent */ if (width >= new_width) { index = (width - new_width) / denominator; err = (width - new_width) % denominator; } else { index = (width - new_width) / denominator - 1; err = denominator - ((new_width - width) % denominator); } long i; for (i=0; i < new_width; i++){ if (residues != NULL) { steps[i] = index; residues[i] = (long) round(255.0 * (float) err / (float) denominator); } else { /* If residues aren't requested, we round to the nearest pixel */ if (err < new_width) { steps[i] = index; } else { steps[i] = index + 1; } } index += base_step; err += err_step; if (err >= denominator) { index += 1; err -= denominator; } } } VALUE oily_png_canvas_steps(VALUE self, VALUE v_width, VALUE v_new_width) { UNUSED_PARAMETER(self); long width = NUM2LONG(v_width); long new_width = NUM2LONG(v_new_width); long *steps = ALLOC_N(long, new_width); VALUE ret = rb_ary_new2(new_width); oily_png_generate_steps_residues(width, new_width, steps, NULL); long i; for (i=0; i < new_width; i++) { rb_ary_store(ret, i, LONG2FIX(steps[i])); } /* This is an unprotected allocation; it will leak on exception. * However, rb_ary_store should not generate one as we have * pre-allocated the array. */ xfree(steps); steps = NULL; return ret; } VALUE oily_png_canvas_steps_residues(VALUE self, VALUE v_width, VALUE v_new_width) { UNUSED_PARAMETER(self); long width = NUM2LONG(v_width); long new_width = NUM2LONG(v_new_width); VALUE ret_steps = rb_ary_new2(new_width); VALUE ret_residues = rb_ary_new2(new_width); long *steps = ALLOC_N(long, new_width); long *residues = ALLOC_N(long, new_width); oily_png_generate_steps_residues(width, new_width, steps, residues); long i; for (i=0; i < new_width; i++) { rb_ary_store(ret_steps, i, LONG2FIX(steps[i])); rb_ary_store(ret_residues, i, LONG2FIX(residues[i])); } /* This is an unprotected allocation; it will leak on exception. * However, rb_ary_store should not generate one as we have * pre-allocated the array. */ xfree(steps); steps = NULL; xfree(residues); residues = NULL; /* We return multiple values */ VALUE ret = rb_ary_new2(2); rb_ary_store(ret, 0, ret_steps); rb_ary_store(ret, 1, ret_residues); return ret; } VALUE oily_png_canvas_resample_nearest_neighbor_bang(VALUE self, VALUE v_new_width, VALUE v_new_height) { long new_width = NUM2LONG(v_new_width); long new_height = NUM2LONG(v_new_height); long self_width = NUM2LONG(rb_funcall(self, rb_intern("width"), 0)); long self_height = NUM2LONG(rb_funcall(self, rb_intern("height"), 0)); VALUE pixels = rb_ary_new2(new_width*new_height); VALUE source = rb_iv_get(self, "@pixels"); long *steps_x = ALLOC_N(long, new_width); long *steps_y = ALLOC_N(long, new_height); oily_png_generate_steps_residues(self_width, new_width, steps_x, NULL); oily_png_generate_steps_residues(self_height, new_height, steps_y, NULL); long index = 0; long x, y; long src_index; for (y=0; y < new_height; y++) { for (x = 0; x < new_width; x++) { src_index = steps_y[y] * self_width + steps_x[x]; VALUE pixel = rb_ary_entry(source, src_index); rb_ary_store(pixels, index, pixel); index++; } } xfree(steps_x); steps_x = NULL; xfree(steps_y); steps_y = NULL; rb_iv_set(self, "@pixels", pixels); rb_iv_set(self, "@width", LONG2NUM(new_width)); rb_iv_set(self, "@height", LONG2NUM(new_height)); return self; } VALUE oily_png_canvas_resample_bilinear_bang(VALUE self, VALUE v_new_width, VALUE v_new_height) { long new_width = NUM2LONG(v_new_width); long new_height = NUM2LONG(v_new_height); long self_width = NUM2LONG(rb_funcall(self, rb_intern("width"), 0)); long self_height = NUM2LONG(rb_funcall(self, rb_intern("height"), 0)); VALUE pixels = rb_ary_new2(new_width*new_height); VALUE source = rb_iv_get(self, "@pixels"); long *index_x = ALLOC_N(long, new_width); long *index_y = ALLOC_N(long, new_height); long *interp_x = ALLOC_N(long, new_width); long *interp_y = ALLOC_N(long, new_height); oily_png_generate_steps_residues(self_width, new_width, index_x, interp_x); oily_png_generate_steps_residues(self_height, new_height, index_y, interp_y); long index = 0; long x, y; long y1, y2, x1, x2; PIXEL y_residue, x_residue; PIXEL pixel_11, pixel_21, pixel_12, pixel_22; PIXEL pixel_top, pixel_bot; for (y = 0; y < new_height; y++) { y1 = index_y[y] < 0 ? 0 : index_y[y]; y2 = index_y[y]+1 >= self_height ? self_height-1 : index_y[y]+1; y_residue = interp_y[y]; for (x = 0; x < new_width; x++) { x1 = index_x[x] < 0 ? 0 : index_x[x]; x2 = index_x[x]+1 >= self_width ? self_width-1 : index_x[x]+1; x_residue = interp_x[x]; pixel_11 = NUM2UINT(rb_ary_entry(source, y1*self_width + x1)); pixel_21 = NUM2UINT(rb_ary_entry(source, y1*self_width + x2)); pixel_12 = NUM2UINT(rb_ary_entry(source, y2*self_width + x1)); pixel_22 = NUM2UINT(rb_ary_entry(source, y2*self_width + x2)); pixel_top = oily_png_color_interpolate_quick(pixel_21, pixel_11, x_residue); pixel_bot = oily_png_color_interpolate_quick(pixel_22, pixel_12, x_residue); rb_ary_store(pixels, index++, UINT2NUM(oily_png_color_interpolate_quick(pixel_bot, pixel_top, y_residue))); } } xfree(index_x); xfree(index_y); xfree(interp_x); xfree(interp_y); interp_x = NULL; interp_y = NULL; rb_iv_set(self, "@pixels", pixels); rb_iv_set(self, "@width", LONG2NUM(new_width)); rb_iv_set(self, "@height", LONG2NUM(new_height)); return self; } oily_png-1.2.1/ext/oily_png/resampling.h000066400000000000000000000013611276602571700203030ustar00rootroot00000000000000#ifndef OILY_PNG_RESAMPLING_H #define OILY_PNG_RESAMPLING_H /* * Generates the interpolation steps (nearest neighbour) through two values. */ void oily_png_generate_steps_residues(long width, long new_width, long *steps, long *residues); /* * Generates the interpolation steps through two values. * * Returns a Ruby Array */ VALUE oily_png_canvas_steps_residues(VALUE self, VALUE width, VALUE new_width); VALUE oily_png_canvas_steps(VALUE self, VALUE width, VALUE new_width); /* * Performs nearest neighbor interpolation on the Canvas */ VALUE oily_png_canvas_resample_nearest_neighbor_bang(VALUE self, VALUE new_width, VALUE new_height); VALUE oily_png_canvas_resample_bilinear_bang(VALUE self, VALUE new_width, VALUE new_height); #endif oily_png-1.2.1/lib/000077500000000000000000000000001276602571700141165ustar00rootroot00000000000000oily_png-1.2.1/lib/oily_png.rb000066400000000000000000000010161276602571700162610ustar00rootroot00000000000000require 'chunky_png' module OilyPNG def self.included(base) base::Canvas.send(:extend, OilyPNG::PNGDecoding) base::Canvas.send(:include, OilyPNG::PNGEncoding) base::Color.send(:include, OilyPNG::Color) base::Color.extend OilyPNG::Color base::Canvas.send(:include, OilyPNG::Resampling) base::Canvas.send(:include, OilyPNG::Operations) end end require 'oily_png/version' require 'oily_png/oily_png' require 'oily_png/canvas' # Include mixin into ChunkyPNG ChunkyPNG.send(:include, OilyPNG) oily_png-1.2.1/lib/oily_png/000077500000000000000000000000001276602571700157365ustar00rootroot00000000000000oily_png-1.2.1/lib/oily_png/canvas.rb000066400000000000000000000004261276602571700175400ustar00rootroot00000000000000require 'chunky_png' require 'oily_png/oily_png' module OilyPNG class Canvas < ChunkyPNG::Canvas extend OilyPNG::PNGDecoding include OilyPNG::PNGEncoding include OilyPNG::Operations include OilyPNG::Resampling end module Color extend self end endoily_png-1.2.1/lib/oily_png/version.rb000066400000000000000000000000471276602571700177510ustar00rootroot00000000000000module OilyPNG VERSION = "1.2.1" end oily_png-1.2.1/oily_png.gemspec000066400000000000000000000023561276602571700165430ustar00rootroot00000000000000# -*- encoding: utf-8 -*- lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'oily_png/version' Gem::Specification.new do |s| s.name = 'oily_png' s.rubyforge_project = s.name # Do not change the version and date fields by hand. This will be done # automatically by the gem release script. s.version = OilyPNG::VERSION s.summary = "Native mixin to speed up ChunkyPNG" s.description = <<-EOT This Ruby C extenstion defines a module that can be included into ChunkyPNG to improve its speed. EOT s.license = 'MIT' s.authors = ['Willem van Bergen'] s.email = ['willem@railsdoctors.com'] s.homepage = 'http://wiki.github.com/wvanbergen/oily_png' s.extensions = ["ext/oily_png/extconf.rb"] s.require_paths = ["lib", "ext"] s.add_runtime_dependency('chunky_png', '~> 1.3.7') s.add_development_dependency('rake') s.add_development_dependency('rake-compiler') s.add_development_dependency('rspec', '~> 3') s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source' s.extra_rdoc_files = ['README.rdoc'] s.files = `git ls-files`.split($/) s.test_files = s.files.grep(%r{^(test|spec|features)/}) end oily_png-1.2.1/spec/000077500000000000000000000000001276602571700143025ustar00rootroot00000000000000oily_png-1.2.1/spec/color_spec.rb000066400000000000000000000030631276602571700167610ustar00rootroot00000000000000require 'spec_helper' describe OilyPNG::Color do include OilyPNG::Color before(:each) do @white = 0xffffffff @black = 0x000000ff @opaque = 0x0a6496ff @non_opaque = 0x0a649664 @fully_transparent = 0x0a649600 end describe '#compose_quick' do it 'should use the foregorund color as is when the background color is fully transparent' do expect(compose_quick(@non_opaque, @fully_transparent)).to be(@non_opaque) end it 'should use the foregorund color as is when an opaque color is given as foreground color' do expect(compose_quick(@opaque, @white)).to be(@opaque) end it 'should use the background color as is when a fully transparent pixel is given as foreground color' do expect(compose_quick(@fully_transparent, @white)).to be(@white) end it 'should compose pixels correctly' do expect(compose_quick(@non_opaque, @white)).to be(0x9fc2d6ff) end it 'should compose colors exactly the same as ChunkyPNG' do fg, bg = rand(0xffffffff), rand(0xffffffff) expect(compose_quick(fg, bg)).to be(ChunkyPNG::Color.compose_quick(fg, bg)) end end describe '#euclidean_distance_rgba' do let(:color_a) { rand(0xffffffff) } let(:color_b) { rand(0xffffffff) } subject { euclidean_distance_rgba(color_a, color_b) } it { is_expected.to eq(ChunkyPNG::Color.euclidean_distance_rgba(color_a, color_b)) } context 'when both colors are the same' do let(:color_b) { color_a } it { is_expected.to eq(0) } end end end oily_png-1.2.1/spec/decoding_spec.rb000066400000000000000000000043321276602571700174170ustar00rootroot00000000000000require 'spec_helper' describe OilyPNG::PNGDecoding do it "should call ChunkyPNG::Color.pixel_bytesize in the pure ruby version" do expect(ChunkyPNG::Color).to receive(:pixel_bytesize).and_return(3) ChunkyPNG::Canvas.from_file(resource_file('square.png')) end it "should not call ChunkyPNG::Color.pixel_bytesize in the native version" do expect(ChunkyPNG::Color).to receive(:pixel_bytesize).never OilyPNG::Canvas.from_file(resource_file('square.png')) end context 'decoding different filtering methods' do before(:all) { @reference = ChunkyPNG::Canvas.from_file(resource_file('nonsquare.png'))} it "should decode NONE filtering exactly the same as ChunkyPNG" do filtered_data = @reference.to_blob(:filtering => ChunkyPNG::FILTER_NONE) expect(ChunkyPNG::Canvas.from_blob(filtered_data)).to eq(OilyPNG::Canvas.from_blob(filtered_data)) end it "should decode SUB filtering exactly the same as ChunkyPNG" do filtered_data = @reference.to_blob(:filtering => ChunkyPNG::FILTER_SUB) expect(ChunkyPNG::Canvas.from_blob(filtered_data)).to eq(OilyPNG::Canvas.from_blob(filtered_data)) end it "should decode UP filtering exactly the same as ChunkyPNG" do filtered_data = @reference.to_blob(:filtering => ChunkyPNG::FILTER_UP) expect(ChunkyPNG::Canvas.from_blob(filtered_data)).to eq(OilyPNG::Canvas.from_blob(filtered_data)) end it "should decode AVERAGE filtering exactly the same as ChunkyPNG" do filtered_data = @reference.to_blob(:filtering => ChunkyPNG::FILTER_AVERAGE) expect(ChunkyPNG::Canvas.from_blob(filtered_data)).to eq(OilyPNG::Canvas.from_blob(filtered_data)) end it "should decode PAETH filtering exactly the same as ChunkyPNG" do filtered_data = @reference.to_blob(:filtering => ChunkyPNG::FILTER_PAETH) expect(ChunkyPNG::Canvas.from_blob(filtered_data)).to eq(OilyPNG::Canvas.from_blob(filtered_data)) end end context 'decoding compatibility with ChunkyPNG' do resource_files.each do |file| it "should #{File.basename(file)} the same as ChunkyPNG" do expect(OilyPNG::Canvas.from_file(file).pixels).to eq(ChunkyPNG::Canvas.from_file(file).pixels) end end end end oily_png-1.2.1/spec/encoding_spec.rb000066400000000000000000000202661276602571700174350ustar00rootroot00000000000000require 'spec_helper' describe OilyPNG::PNGEncoding do context 'encoding different color settings without palette' do before do @canvas = ChunkyPNG::Canvas.from_file(resource_file('gray.png')) @oily_canvas = OilyPNG::Canvas.from_canvas(@canvas) end it "should encode an image using 8-bit grayscale correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 8, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 8, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 4-bit grayscale correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 4, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 4, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 2-bit grayscale correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 2, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 2, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 1-bit grayscale correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 1, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE, 1, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 8-bit grayscale alpha correctly" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 8, ChunkyPNG::FILTER_NONE) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_GRAYSCALE_ALPHA, 8, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 8-bit truecolor correctly" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_NONE) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 8-bit truecolor alpha correctly" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 8, ChunkyPNG::FILTER_NONE) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR_ALPHA, 8, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end end context 'encoding with paletted images using different bitrates' do before do @canvas = ChunkyPNG::Canvas.from_file(resource_file('gray.png')) @oily_canvas = OilyPNG::Canvas.from_canvas(@canvas) @canvas.encoding_palette = @canvas.palette @canvas.encoding_palette.to_plte_chunk @oily_canvas.encoding_palette = @oily_canvas.palette @oily_canvas.encoding_palette.to_plte_chunk end it "should encode an image using 8-bit indexed colors correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 8, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 8, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 4-bit indexed colors correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 4, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 4, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 2-bit indexed colors correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 2, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 2, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode an image using 1-bit indexed colors correctly" do @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 1, ChunkyPNG::FILTER_NONE) @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_INDEXED, 1, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end end context 'encoding different filters' do before do @canvas = ChunkyPNG::Canvas.from_file(resource_file('nonsquare.png')) @oily_canvas = OilyPNG::Canvas.from_canvas(@canvas) end it "should encode correctly with no filtering" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_NONE) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_NONE) expect(stream1).to eq(stream2) end it "should encode correctly with sub filtering" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_SUB) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_SUB) expect(stream1).to eq(stream2) end it "should encode correctly with up filtering" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_UP) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_UP) expect(stream1).to eq(stream2) end it "should encode correctly with average filtering" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_AVERAGE) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_AVERAGE) expect(stream1).to eq(stream2) end it "should encode correctly with paeth filtering" do @oily_canvas.send(:encode_png_image_pass_to_stream, stream1 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_PAETH) @canvas.send(:encode_png_image_pass_to_stream, stream2 = ChunkyPNG::Datastream.empty_bytearray, ChunkyPNG::COLOR_TRUECOLOR, 8, ChunkyPNG::FILTER_PAETH) expect(stream1).to eq(stream2) end end it "should encode an interlaced image correctly" do canvas = ChunkyPNG::Canvas.from_file(resource_file('interlaced.png')) data = OilyPNG::Canvas.from_canvas(canvas).to_blob(:interlace => true) ds = ChunkyPNG::Datastream.from_blob(data) expect(ds.header_chunk.interlace).to eq(ChunkyPNG::INTERLACING_ADAM7) expect(ChunkyPNG::Canvas.from_datastream(ds)).to eq(canvas) end end oily_png-1.2.1/spec/operations_spec.rb000066400000000000000000000105461276602571700200320ustar00rootroot00000000000000require 'spec_helper' describe OilyPNG::Operations do describe '#compose!' do subject { oily_reference_canvas('operations') } it "should compose two images without offset exactly the same way as ChunkyPNG" do subcanvas = ChunkyPNG::Canvas.new(4, 8, ChunkyPNG::Color.rgba(0, 0, 0, 75)) subject.compose!(subcanvas) expect(ChunkyPNG::Canvas.from_canvas(subject)).to eq(reference_canvas('operations').compose(subcanvas)) end it "should compose two images with offset exactly the same way as ChunkyPNG" do subject.compose!(ChunkyPNG::Canvas.new(4, 8, ChunkyPNG::Color.rgba(0, 0, 0, 75)), 8, 4) expect(subject).to eql(oily_reference_canvas('composited')) end it "should return itself" do expect(subject.compose!(OilyPNG::Canvas.new(1,1))).to be(subject) end it "should raise an exception when the pixels to compose fall outside the image" do # For now this raises a runtime error, but it should probably raise a ChunkyPNG::OutOfBounds error expect { subject.compose!(OilyPNG::Canvas.new(1,1), 16, 16) }.to raise_error end end describe '#replace!' do subject { oily_reference_canvas('operations') } it "should compose two images without offset exactly the same way as ChunkyPNG" do subcanvas = ChunkyPNG::Canvas.new(3, 2, ChunkyPNG::Color.rgb(200, 255, 0)) subject.replace!(subcanvas) expect(ChunkyPNG::Canvas.from_canvas(subject)).to eq(reference_canvas('operations').replace(subcanvas)) end it "should compose two images with offset exactly the same way as ChunkyPNG" do subject.replace!(ChunkyPNG::Canvas.new(3, 2, ChunkyPNG::Color.rgb(200, 255, 0)), 5, 4) expect(subject).to eq(oily_reference_canvas('replaced')) end it "should return itself" do expect(subject.replace!(OilyPNG::Canvas.new(1,1))).to be(subject) end it "should raise an exception when the pixels to compose fall outside the image" do # For now this raises a runtime error, but it should probably raise a ChunkyPNG::OutOfBounds error expect { subject.replace!(OilyPNG::Canvas.new(1,1), 16, 16) }.to raise_error end end describe '#rotate_left!' do subject { OilyPNG::Canvas.new(2, 3, [1, 2, 3, 4, 5, 6]) } it "should rotate the pixels 90 degrees clockwise" do subject.rotate_left! expect(subject).to eql OilyPNG::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 it "it should rotate 180 degrees when applied twice" do subject_dup = subject.dup expect(subject.rotate_left!.rotate_left!).to eql subject_dup.rotate_180 end it "it should rotate right when applied three times" do subject_dup = subject.dup expect(subject.rotate_left!.rotate_left!.rotate_left!).to eql subject_dup.rotate_right! end it "should return itself when applied four times" do subject_dup = subject.dup expect(subject.rotate_left!.rotate_left!.rotate_left!.rotate_left!).to eql subject_dup end end describe '#rotate_right!' do subject { OilyPNG::Canvas.new(2, 3, [1, 2, 3, 4, 5, 6]) } it "should rotate the pixels 90 degrees clockwise" do subject.rotate_right! expect(subject).to eql OilyPNG::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 it "it should rotate 180 degrees when applied twice" do subject_dup = subject.dup expect(subject.rotate_right!.rotate_right!).to eql subject_dup.rotate_180 end it "it should rotate left when applied three times" do subject_dup = subject.dup expect(subject.rotate_right!.rotate_right!.rotate_right!).to eql subject_dup.rotate_left end it "should return itself when applied four times" do subject_dup = subject.dup expect(subject.rotate_right!.rotate_right!.rotate_right!.rotate_right!).to eql subject_dup end end end oily_png-1.2.1/spec/resampling_spec.rb000066400000000000000000000047271276602571700200140ustar00rootroot00000000000000require 'spec_helper' describe OilyPNG::Resampling do include OilyPNG::Resampling describe '#steps' do it "should generate the steps from 4 to 8 as [0,0,1,1,2,2,3,3]" do expect(steps(4, 8)).to eq([0, 0, 1, 1, 2, 2, 3, 3]) end it "should generate the steps the same as ChunkyPNG" do image = ChunkyPNG::Image.new(1,1) expect(steps( 2, 8)).to eq(image.send(:steps, 2, 8)) expect(steps( 2, 11)).to eq(image.send(:steps, 2,11)) expect(steps(11, 5)).to eq(image.send(:steps,11, 5)) end end describe '#resample_nearest_neighbor!' do before(:all) { @reference = ChunkyPNG::Canvas.from_file(resource_file('nonsquare.png'))} it "should resample [0,1,2,3] to 4x4 properly" do expect(OilyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(4,4)).to eq(OilyPNG::Canvas.new(4,4,[0,0,1,1,0,0,1,1,2,2,3,3,2,2,3,3])) end it "should resample [0,1,2,3] to 99x45 as ChunkyPNG does" do expect(ChunkyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(99,45)).to eq(OilyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(99,45)) end it "should resample an image to 10x20 as ChunkyPNG does" do expect(@reference.resample_nearest_neighbor(10,20)).to eq(OilyPNG::Canvas.from_canvas(@reference).resample_nearest_neighbor(10,20)) end it "should resample an image to 11x19 as ChunkyPNG does" do expect(@reference.resample_nearest_neighbor(11,19)).to eq(OilyPNG::Canvas.from_canvas(@reference).resample_nearest_neighbor(11,19)) end end describe '#resample_bilinear!' do before(:all) { @reference = ChunkyPNG::Canvas.from_file(resource_file('nonsquare.png'))} it "should resample an image to 10x20 as ChunkyPNG does" do expect(@reference.resample_bilinear(10,20)).to eq(OilyPNG::Canvas.from_canvas(@reference).resample_bilinear(10,20)) end it "should resample an image to 11x19 as ChunkyPNG does" do expect(@reference.resample_bilinear(11,19)).to eq(OilyPNG::Canvas.from_canvas(@reference).resample_bilinear(11,19)) end it "should upsample an image to 88x44 as ChunkyPNG does" do expect(@reference.resample_bilinear(88,44)).to eq(OilyPNG::Canvas.from_canvas(@reference).resample_bilinear(88,44)) end it "should not crash upsampling tall image" do @reference = ChunkyPNG::Canvas.from_file(resource_file('nonsquaretall.png')) expect { OilyPNG::Canvas.from_canvas(@reference).resample_bilinear(44,88) }.to_not raise_error end end end oily_png-1.2.1/spec/resources/000077500000000000000000000000001276602571700163145ustar00rootroot00000000000000oily_png-1.2.1/spec/resources/basi0g01.png000077500000000000000000000003311276602571700203300ustar00rootroot00000000000000PNG  IHDR ,wgAMA1_IDATx-10 EƂz.z' V9cX,e5|KxOp笹pi\Yc*L'Dd[6癹+MKOKzҍ2-czpEHp-/zQ!塌Qf"IENDB`oily_png-1.2.1/spec/resources/basi0g02.png000077500000000000000000000002321276602571700203310ustar00rootroot00000000000000PNG  IHDR k gAMA1_QIDATxcPb`r`p16nDgg]!dDCH dJHR? 0A 9RM}IENDB`oily_png-1.2.1/spec/resources/basi0g04.png000077500000000000000000000003671276602571700203440ustar00rootroot00000000000000PNG  IHDR gAMA1_IDATxeQ0D:(u AAAAA$TI~ò¶EuEĺCsGjw<# ^bs8Al.iGZ'(CYd:"k@i2Gpr:1(Kkce s {ig 826N'MIENDB`oily_png-1.2.1/spec/resources/basi0g08.png000077500000000000000000000003761276602571700203500ustar00rootroot00000000000000PNG  IHDR !gAMA1_IDATx] 0B{C%>x!Kܦ$]!2,$UIBH"*V$$ŏ Jl, lܲ 9:eSW}q@Şp쿣;_|?/ & \lX5R 2'+d~G @4 @4߁@\\y9۩ܵEkQט-} Q FAQ wbk@|DVIENDB`oily_png-1.2.1/spec/resources/basi2c08.png000077500000000000000000000004731276602571700203440ustar00rootroot00000000000000PNG  IHDR 5gAMA1_IDATxՓA! D{x=Ys3hhf gZYd1b|V%}֠Ɯ7~gv>^/-Jcm smXTmcm @Vx?0.;Xx0p0 SW+c Y}+EI$ ottЅ Awj88:A?/ĠnqsT`z!gAMA1_PLTE""fl&)IDATxc`P CѰ1Ù3IH6?@@!( B-vÞIENDB`oily_png-1.2.1/spec/resources/basi3p02.png000077500000000000000000000003011276602571700203420ustar00rootroot00000000000000PNG  IHDR ygAMA1_sBIT|.w PLTEe?+QIDATxcxǰ]̌!9J޸b.??dCH dJHR?t#B,*9Z}uIENDB`oily_png-1.2.1/spec/resources/basi3p04.png000077500000000000000000000005071276602571700203540ustar00rootroot00000000000000PNG  IHDR SWQgAMA1_sBITw-PLTE""fwDDҰIIDATxcc8N%(KS,1,ľ @ _B$ ,qP=L(-]]tލ0@L1 RL1pOGh3V{7s&*WPS)ʽ{Sˀ)UR@妥a rwvh t_IENDB`oily_png-1.2.1/spec/resources/basi3p08.png000077500000000000000000000027671276602571700203720ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/basi4a08.png000077500000000000000000000003261276602571700203410ustar00rootroot00000000000000PNG  IHDR tgAMA1_IDATx 0 ֽA((sOAVG":ݙbH$ @ɾzM2x<7U[0t<n!Y~.,>RfqXAh٪wϤ50o0N N6 O= YU]IENDB`oily_png-1.2.1/spec/resources/basi4a16.png000077500000000000000000000054471276602571700203510ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/basi6a08.png000077500000000000000000000005511276602571700203430ustar00rootroot00000000000000PNG  IHDR }JbgAMA1_ IDATxŕAN0EAfAME*q *e@ՔAHɊ|?'dG` 9c:1Ⓣ{=k ֵS˰gc. 3{_m /N @-~'.lM1! jѠ D h=F`u@]`^-^%x zRhb!9:XF/h.䋱 lY PtP΀W(3mYm πu(ש P:JSiNsBIENDB`oily_png-1.2.1/spec/resources/basi6a16.png000077500000000000000000000101241276602571700203370ustar00rootroot00000000000000PNG  IHDR T!gAMA1_ IDATxݙktU՝}&$@h⍠("І`YЎ" BZ.oY(ZAG$P t 8B[#CIIk_̇$̸Y]gc}m&ZCmI6D#?$XVIͮl̏d׽OB6nnm|¶,()^VnhO*sL-5S< ΍:wx4SEݰ# 𛷍[w8[ u,ZZY: ʊ+$n Pna WݹP^ql}cC#~NZ`^Z$;VWãȖG68F쾰=0@@ SCMpmr/$0J?B?;燈tKi ">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(/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`oily_png-1.2.1/spec/resources/basn3p01.png000077500000000000000000000001601276602571700203510ustar00rootroot00000000000000PNG  IHDR IgAMA1_PLTE""fl&IDATxc4?fYIENDB`oily_png-1.2.1/spec/resources/basn3p02.png000077500000000000000000000002221276602571700203510ustar00rootroot00000000000000PNG  IHDR ggAMA1_sBIT|.w PLTEe?+"IDATxc0,| =IꉎIENDB`oily_png-1.2.1/spec/resources/basn3p04.png000077500000000000000000000003301276602571700203530ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw-PLTE""fwDDҰIGIDATxc =sfժrcwfd C+(H*ŅTݻq@*)#MK#G{7}IENDB`oily_png-1.2.1/spec/resources/basn3p08.png000077500000000000000000000024061276602571700203650ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/basn4a08.png000077500000000000000000000001761276602571700203510ustar00rootroot00000000000000PNG  IHDR sgAMA1_5IDATxch41",(,?a0T1`4GÀ*hP* }IENDB`oily_png-1.2.1/spec/resources/basn4a16.png000077500000000000000000000042361276602571700203510ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/composited.png000066400000000000000000000002541276602571700211710ustar00rootroot00000000000000PNG  IHDRh6sIDATxcb`/_ 0o`?`310Pí[z3g7"J Ij.Z..&.޴iir2 IENDB`oily_png-1.2.1/spec/resources/gray.png000066400000000000000000000001111276602571700177550ustar00rootroot00000000000000PNG  IHDR YaIDATxcLa&3/Nx 2]IENDB`oily_png-1.2.1/spec/resources/interlaced.png000066400000000000000000000004171276602571700211360ustar00rootroot00000000000000PNG  IHDRhtEXtSoftwareAdobe ImageReadyqe<IDATxb gHv?@1DA" @p 01 `Ւ:ƪ SRR3(:`ƀLsF]8M!h@aD2H&E8N xU":`AT͛A `J0bZ& a@(Pr&U+E'mIENDB`oily_png-1.2.1/spec/resources/operations.png000066400000000000000000000002011276602571700211760ustar00rootroot00000000000000PNG  IHDRh6HIDATxcb`/_ 0o`?`310F5j:&ҫIENDB`oily_png-1.2.1/spec/resources/replaced.png000066400000000000000000000015471276602571700206100ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s01i3p01.png000077500000000000000000000001611276602571700202030ustar00rootroot00000000000000PNG  IHDRRf\gAMA1_sBITwPLTExW IDATxc`HqIENDB`oily_png-1.2.1/spec/resources/s01n3p01.png000077500000000000000000000001611276602571700202100ustar00rootroot00000000000000PNG  IHDR%VgAMA1_sBITwPLTExW IDATxc`HqIENDB`oily_png-1.2.1/spec/resources/s02i3p01.png000077500000000000000000000001621276602571700202050ustar00rootroot00000000000000PNG  IHDR?gAMA1_sBITwPLTE\/% IDATxc`gIENDB`oily_png-1.2.1/spec/resources/s02n3p01.png000077500000000000000000000001631276602571700202130ustar00rootroot00000000000000PNG  IHDRHxggAMA1_sBITwPLTE\/% IDATxc```8UIENDB`oily_png-1.2.1/spec/resources/s03i3p01.png000077500000000000000000000001661276602571700202120ustar00rootroot00000000000000PNG  IHDRjgAMA1_sBITwPLTEw奟 IDATxc`LAIENDB`oily_png-1.2.1/spec/resources/s03n3p01.png000077500000000000000000000001701276602571700202120ustar00rootroot00000000000000PNG  IHDRl'gAMA1_sBITwPLTEw奟IDATxc``p``A91 KIENDB`oily_png-1.2.1/spec/resources/s04i3p01.png000077500000000000000000000001761276602571700202140ustar00rootroot00000000000000PNG  IHDR8<gAMA1_sBITwPLTEwCIDATxch`  >7IENDB`oily_png-1.2.1/spec/resources/s04n3p01.png000077500000000000000000000001711276602571700202140ustar00rootroot00000000000000PNG  IHDR? =gAMA1_sBITwPLTEwCIDATxc0?  IENDB`oily_png-1.2.1/spec/resources/s05i3p02.png000077500000000000000000000002061276602571700202100ustar00rootroot00000000000000PNG  IHDRgAMA1_sBITw PLTEwAsIDATxch`h`X";tOm"Mp$FEIENDB`oily_png-1.2.1/spec/resources/s05n3p02.png000077500000000000000000000002011276602571700202100ustar00rootroot00000000000000PNG  IHDRvgAMA1_sBITw PLTEwAsIDATxcX0a"\*JIENDB`oily_png-1.2.1/spec/resources/s06i3p02.png000077500000000000000000000002171276602571700202130ustar00rootroot00000000000000PNG  IHDR7MgAMA1_sBITw PLTEwEh"IDATxch`h` - +<"&0LH`XYLe$IENDB`oily_png-1.2.1/spec/resources/s06n3p02.png000077500000000000000000000002031276602571700202130ustar00rootroot00000000000000PNG  IHDRgAMA1_sBITw PLTEwEhIDATxcXaj02V-9_)pIENDB`oily_png-1.2.1/spec/resources/s07i3p02.png000077500000000000000000000002251276602571700202130ustar00rootroot00000000000000PNG  IHDR;gAMA1_sBITw PLTEwwC%IDATxc8p 3aH? Wcn UIENDB`oily_png-1.2.1/spec/resources/s07n3p02.png000077500000000000000000000002121276602571700202140ustar00rootroot00000000000000PNG  IHDR<@gAMA1_sBITw PLTEwwCIDATxcj í9 7H qy ]mIENDB`oily_png-1.2.1/spec/resources/s08i3p02.png000077500000000000000000000002251276602571700202140ustar00rootroot00000000000000PNG  IHDRffgAMA1_sBITw PLTEwwY%IDATxc` ~`Ǡ0aXAAPPpJIENDB`oily_png-1.2.1/spec/resources/s08n3p02.png000077500000000000000000000002131276602571700202160ustar00rootroot00000000000000PNG  IHDRaVgAMA1_sBITw PLTEwwYIDATxc```ZA =V=LIENDB`oily_png-1.2.1/spec/resources/s09i3p02.png000077500000000000000000000002231276602571700202130ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw PLTEwwVd#IDATxc`@ X ^3\b  duFIENDB`oily_png-1.2.1/spec/resources/s09n3p02.png000077500000000000000000000002171276602571700202230ustar00rootroot00000000000000PNG  IHDR gAMA1_sBITw PLTEwwVdIDATxc` V,XB q iejC#IENDB`oily_png-1.2.1/spec/resources/s32i3p04.png000077500000000000000000000005431276602571700202160ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s32n3p04.png000077500000000000000000000004071276602571700202220ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw'PLTEwwwwww;|IDATxѽ Wq zZ6w +'a0MHR\GrvX41$TxAsŖ?hnsof]A[ $ \V~01YB6ipaHz&.IENDB`oily_png-1.2.1/spec/resources/s33i3p04.png000077500000000000000000000006011276602571700202120ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s34n3p04.png000077500000000000000000000003701276602571700202230ustar00rootroot00000000000000PNG  IHDR""igAMA1_sBITw'PLTEwwwwww;mIDATxϱ _ [2г0&G+.%:r*Hv4,$ĂY49n2=xNNm'c)*+ TxVR,v5H\-Y~H:+NIENDB`oily_png-1.2.1/spec/resources/s35i3p04.png000077500000000000000000000006171276602571700202230ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s35n3p04.png000077500000000000000000000005221276602571700202230ustar00rootroot00000000000000PNG  IHDR##jgAMA1_sBITw'PLTEwwwwww;IDATxe!@״p иJ,{>a3Fު`Y\Ռ:T4b2(h%sn݆oEd=d2ܙBcF5μWhQd *F&Ny7zAwe\/x7rL#"ckLiq6>G΢HIENDB`oily_png-1.2.1/spec/resources/s36i3p04.png000077500000000000000000000005441276602571700202230ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s36n3p04.png000077500000000000000000000004021276602571700202210ustar00rootroot00000000000000PNG  IHDR$$.gAMA1_sBITw'PLTEwwwwww;wIDATxc`eG$! .dp]]h*t !,[*`:B,!,Bl7݅,NEPWB BB Є$BB62`XoIӑIENDB`oily_png-1.2.1/spec/resources/s37i3p04.png000077500000000000000000000006111276602571700202170ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s37n3p04.png000077500000000000000000000005201276602571700202230ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s38i3p04.png000077500000000000000000000005451276602571700202260ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s38n3p04.png000077500000000000000000000003651276602571700202330ustar00rootroot00000000000000PNG  IHDR&&ZgAMA1_sBITw'PLTEwwwwww;jIDATxc`eG`$61 !fpCCl* L1lvX*`q*b 0h-܌81 bhb)`@da1-i+άDIENDB`oily_png-1.2.1/spec/resources/s39i3p04.png000077500000000000000000000006441276602571700202270ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s39n3p04.png000077500000000000000000000005401276602571700202270ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/s40i3p04.png000077500000000000000000000005451276602571700202170ustar00rootroot00000000000000PNG  IHDR(( וgAMA1_sBITw'PLTEwwwwww;IDATx 0EZk]dRPN7upqg'B YP1BCᐼ<DZrQeA|$`O sc#WLYDOLILȾ{aWvD( cNٮaXDCapl5pQDV @"$֓I#U&!AHt xnR=͈ki^ jNp{xIENDB`oily_png-1.2.1/spec/resources/s40n3p04.png000077500000000000000000000004001276602571700202120ustar00rootroot00000000000000PNG  IHDR((~Х^gAMA1_sBITw'PLTEwwwwww;uIDATxб 0 DC,!alE6H%2EHVNRbG@!FeQk7^ Bؚp877*,mWBMwG)Rc!wu'gWpH])IENDB`oily_png-1.2.1/spec/resources/square.png000066400000000000000000000002011276602571700203130ustar00rootroot00000000000000PNG  IHDRh6HIDATxcb`/_ 0o`?`310F5j:&ҫIENDB`oily_png-1.2.1/spec/resources/tbbn1g04.png000077500000000000000000000006431276602571700203510ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tbbn3p08.png000077500000000000000000000021501276602571700203630ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tbgn2c16.png000077500000000000000000000037121276602571700203560ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tbgn3p08.png000077500000000000000000000021501276602571700203700ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tbrn2c08.png000077500000000000000000000025031276602571700203670ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tbwn1g16.png000077500000000000000000000021721276602571700204000ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tbyn3p08.png000077500000000000000000000021531276602571700204150ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tp0n1g08.png000077500000000000000000000012611276602571700203060ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tp0n2c08.png000077500000000000000000000024371276602571700203110ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tp0n3p08.png000077500000000000000000000021401276602571700203160ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/resources/tp1n3p08.png000077500000000000000000000021331276602571700203210ustar00rootroot00000000000000PNG  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`oily_png-1.2.1/spec/spec_helper.rb000066400000000000000000000015501276602571700171210ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'chunky_png' require 'oily_png/canvas' module ResourceHelper def resource_files Dir[File.join(File.dirname(__FILE__), 'resources', '*.png')] end end module CanvasHelper def resource_files(pattern = "*.png") Dir[File.join(File.dirname(__FILE__), 'resources', pattern)] end def resource_file(name) File.join(File.dirname(__FILE__), 'resources', name) end def display(canvas) filename = resource_file('_tmp.png') canvas.to_datastream.save(filename) `open #{filename}` end def reference_canvas(name) ChunkyPNG::Canvas.from_file(resource_file("#{name}.png")) end def oily_reference_canvas(name) OilyPNG::Canvas.from_file(resource_file("#{name}.png")) end end RSpec.configure do |config| config.extend ResourceHelper config.include CanvasHelper end oily_png-1.2.1/tasks/000077500000000000000000000000001276602571700144755ustar00rootroot00000000000000oily_png-1.2.1/tasks/testing.rake000066400000000000000000000036241276602571700170230ustar00rootroot00000000000000task(:verify, :png_file) do |task, args| require 'rubygems' require 'bundler/setup' require 'chunky_png' require 'oily_png/oily_png_ext' class OilyPNG::Canvas < ChunkyPNG::Canvas extend OilyPNG::PNGDecoding include OilyPNG::PNGEncoding end file = args[:png_file] || ENV['PNG_FILE'] raise "Please specify a valid PNG file to verify!" unless File.exist?(file.to_s) decoding_reference = ChunkyPNG::Canvas.from_file(file) decoding_oily_png = OilyPNG::Canvas.from_file(file) if decoding_reference == decoding_oily_png puts "Decoding test succeeded!" else puts "Decoding test FAILED!" end oily_png = OilyPNG::Canvas.from_canvas(decoding_reference) [ChunkyPNG::FILTER_NONE, ChunkyPNG::FILTER_SUB, ChunkyPNG::FILTER_UP, ChunkyPNG::FILTER_AVERAGE, ChunkyPNG::FILTER_PAETH].each do |filter_method| encoding_reference = decoding_reference.to_blob(:filtering => filter_method, :color_mode => ChunkyPNG::COLOR_TRUECOLOR_ALPHA) encoding_oily_png = oily_png.to_blob(:filtering => filter_method, :color_mode => ChunkyPNG::COLOR_TRUECOLOR_ALPHA) if encoding_reference == encoding_oily_png puts "Encoding test succeeded for filter method #{filter_method}!" else puts "Encoding test FAILED for filter method #{filter_method}!" end end [ChunkyPNG::COLOR_GRAYSCALE, ChunkyPNG::COLOR_GRAYSCALE_ALPHA, ChunkyPNG::COLOR_INDEXED, ChunkyPNG::COLOR_TRUECOLOR, ChunkyPNG::COLOR_TRUECOLOR_ALPHA].each do |color_mode| encoding_reference = decoding_reference.to_blob(:filtering => ChunkyPNG::FILTER_NONE, :color_mode => color_mode) encoding_oily_png = oily_png.to_blob(:filtering => ChunkyPNG::FILTER_NONE, :color_mode => color_mode) if encoding_reference == encoding_oily_png puts "Encoding test succeeded for color mode #{color_mode}!" else puts "Decoding test FAILED for color mode #{color_mode}!" end end end