From bc13d6aed54ee5c3f1b5f9dc3bed335144bb388d Mon Sep 17 00:00:00 2001 From: Scott Davis Date: Thu, 24 Feb 2011 03:08:28 -0500 Subject: [PATCH] rmagick changes --- Gemfile | 1 + Gemfile.lock | 2 +- .../sass_extensions/functions/sprites.rb | 6 +- lib/compass/sass_extensions/sprites/base.rb | 279 ++++++++++-------- .../sprites/engines/chunky_png_engine.rb | 14 +- .../sprites/engines/rmagick_engine.rb | 30 ++ 6 files changed, 194 insertions(+), 138 deletions(-) create mode 100644 lib/compass/sass_extensions/sprites/engines/rmagick_engine.rb diff --git a/Gemfile b/Gemfile index 5a4d1042..433f23ed 100644 --- a/Gemfile +++ b/Gemfile @@ -15,3 +15,4 @@ gem "chunky_png", "~> 0.12.0" gem "ruby-prof" unless RUBY_PLATFORM == "java" +#gem 'rmagick' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index c4f9afa6..f078f652 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - compass (0.11.beta.2.1c71df3) + compass (0.11.beta.2.03f4c23) chunky_png (~> 0.12.0) sass (>= 3.1.0.alpha.218) diff --git a/lib/compass/sass_extensions/functions/sprites.rb b/lib/compass/sass_extensions/functions/sprites.rb index 71f64540..cdcd6df1 100644 --- a/lib/compass/sass_extensions/functions/sprites.rb +++ b/lib/compass/sass_extensions/functions/sprites.rb @@ -1,7 +1,5 @@ require 'digest/md5' -require 'compass/sass_extensions/sprites/image' require 'compass/sass_extensions/sprites/base' -require 'compass/sass_extensions/sprites/engines/chunky_png_engine' module Compass::SassExtensions::Functions::Sprites ZERO = Sass::Script::Number::new(0) @@ -15,8 +13,8 @@ module Compass::SassExtensions::Functions::Sprites end end - class SpriteMap < Compass::SassExtensions::Sprites::ChunkyPngEngine - + class SpriteMap < Compass::SassExtensions::Sprites::Base + end # Creates a SpriteMap object. A sprite map, when used in a property is the same diff --git a/lib/compass/sass_extensions/sprites/base.rb b/lib/compass/sass_extensions/sprites/base.rb index 70f7ee94..b94f27fc 100644 --- a/lib/compass/sass_extensions/sprites/base.rb +++ b/lib/compass/sass_extensions/sprites/base.rb @@ -1,154 +1,191 @@ +require 'compass/sass_extensions/sprites/image' module Compass module SassExtensions module Sprites - module Base - module ClassMethods - def from_uri(uri, context, kwargs) - path, name = Compass::Sprites.path_and_name(uri.value) - sprites = Compass::Sprites.discover_sprites(uri.value).map do |sprite| - sprite.gsub(Compass.configuration.images_path+"/", "") + class Base < Sass::Script::Literal + + def self.from_uri(uri, context, kwargs) + path, name = Compass::Sprites.path_and_name(uri.value) + sprites = Compass::Sprites.discover_sprites(uri.value).map do |sprite| + sprite.gsub(Compass.configuration.images_path+"/", "") + end + new(sprites, path, name, context, kwargs) + end + + + def require_engine! + begin + require 'rmagick' + require 'compass/sass_extensions/sprites/engines/rmagick_engine' + self.class.send(:include, ::Compass::SassExtensions::Sprites::RmagickEngine) + rescue LoadError + require 'compass/sass_extensions/sprites/engines/chunky_png_engine' + begin + require 'oily_png' + rescue LoadError + require 'chunky_png' end - new(sprites, path, name, context, kwargs) + self.class.send(:include, ::Compass::SassExtensions::Sprites::ChunkyPngEngine) end end - #instance Methods - module InstanceMethods - - def self.included(base) - base.extend(ClassMethods) + + # Changing this string will invalidate all previously generated sprite images. + # We should do so only when the packing algorithm changes + SPRITE_VERSION = "1" + + attr_accessor :image_names, :path, :name, :options + attr_accessor :images, :width, :height + + + def initialize(image_names, path, name, context, options) + require_engine! + @image_names, @path, @name, @options = image_names, path, name, options + @images = nil + @width = nil + @height = nil + @evaluation_context = context + validate! + compute_image_metadata! + end + + # Calculate the size of the sprite + def size + [width, height] + end + + # Calculates the overal image dimensions + # collects image sizes and input parameters for each sprite + def compute_image_metadata! + @width = 0 + init_images + compute_image_positions! + @height = @images.last.top + @images.last.height + end + + def init_images + @images = image_names.collect do |relative_file| + image = Compass::SassExtensions::Sprites::Image.new(relative_file, options) + @width = [ @width, image.width + image.offset ].max + image end - - # Changing this string will invalidate all previously generated sprite images. - # We should do so only when the packing algorithm changes - SPRITE_VERSION = "1" - - attr_accessor :image_names, :path, :name, :options - attr_accessor :images, :width, :height - - - def initialize(image_names, path, name, context, options) - @image_names, @path, @name, @options = image_names, path, name, options - @images = nil - @width = nil - @height = nil - @evaluation_context = context - validate! - compute_image_metadata! + end + # Calculates the overal image dimensions + # collects image sizes and input parameters for each sprite + def compute_image_positions! + @images.each_with_index do |image, index| + image.left = image.position.unit_str == "%" ? (@width - image.width) * (image.position.value / 100) : image.position.value + next if index == 0 + last_image = @images[index-1] + image.top = last_image.top + last_image.height + [image.spacing, last_image.spacing].max end + end - # Calculate the size of the sprite - def size - [width, height] - end - - # Calculates the overal image dimensions - # collects image sizes and input parameters for each sprite - def compute_image_metadata! - @width = 0 - init_images - compute_image_positions! - @height = @images.last.top + @images.last.height - end + def image_for(name) + @images.detect { |img| img.name == name} + end - def init_images - @images = image_names.collect do |relative_file| - image = Compass::SassExtensions::Sprites::Image.new(relative_file, options) - @width = [ @width, image.width + image.offset ].max - image - end - end - # Calculates the overal image dimensions - # collects image sizes and input parameters for each sprite - def compute_image_positions! - @images.each_with_index do |image, index| - image.left = image.position.unit_str == "%" ? (@width - image.width) * (image.position.value / 100) : image.position.value - next if index == 0 - last_image = @images[index-1] - image.top = last_image.top + last_image.height + [image.spacing, last_image.spacing].max + def sprite_names + image_names.map{|f| Compass::Sprites.sprite_name(f) } + end + + def validate! + for sprite_name in sprite_names + unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/ + raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier" end end + end - def image_for(name) - @images.detect { |img| img.name == name} - end + # The on-the-disk filename of the sprite + def filename + File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png") + end - def sprite_names - image_names.map{|f| Compass::Sprites.sprite_name(f) } + # Generate a sprite image if necessary + def generate + if generation_required? + sprite_data = construct_sprite + save!(sprite_data) + Compass.configuration.run_callback(:sprite_generated, sprite_data) end + end - def validate! - for sprite_name in sprite_names - unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/ - raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier" + def generation_required? + !File.exists?(filename) || outdated? + end + + def uniqueness_hash + @uniqueness_hash ||= begin + sum = Digest::MD5.new + sum << SPRITE_VERSION + sum << path + images.each do |image| + [:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr| + sum << image.send(attr).to_s end end + sum.hexdigest[0...10] end + @uniqueness_hash + end - # The on-the-disk filename of the sprite - def filename - File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png") + # saves the sprite for later retrieval + def save!(output_png) + saved = output_png.save filename + Compass.configuration.run_callback(:sprite_saved, filename) + saved + end + + # All the full-path filenames involved in this sprite + def image_filenames + image_names.map do |image_name| + File.join(Compass.configuration.images_path, image_name) end + end - # Generate a sprite image if necessary - def generate - if generation_required? - sprite_data = construct_sprite - save!(sprite_data) - Compass.configuration.run_callback(:sprite_generated, sprite_data) - end + def save!(output_png) + saved = output_png.save filename + Compass.configuration.run_callback(:sprite_saved, filename) + saved + end + + # All the full-path filenames involved in this sprite + def image_filenames + image_names.map do |image_name| + File.join(Compass.configuration.images_path, image_name) end + end - def generation_required? - !File.exists?(filename) || outdated? + # Checks whether this sprite is outdated + def outdated? + last_update = self.mtime + image_filenames.each do |image| + return true if File.mtime(image) > last_update end + false + end - def uniqueness_hash - @uniqueness_hash ||= begin - sum = Digest::MD5.new - sum << SPRITE_VERSION - sum << path - images.each do |image| - [:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr| - sum << image.send(attr).to_s - end - end - sum.hexdigest[0...10] - end - @uniqueness_hash - end + def mtime + File.mtime(filename) + end - # saves the sprite for later retrieval - def save!(output_png) - saved = output_png.save filename - Compass.configuration.run_callback(:sprite_saved, filename) - saved - end + def inspect + to_s + end - # All the full-path filenames involved in this sprite - def image_filenames - image_names.map do |image_name| - File.join(Compass.configuration.images_path, image_name) - end - end + def to_s(options = self.options) + sprite_url(self).value + end - def inspect - to_s - end + def respond_to?(meth) + super || @evaluation_context.respond_to?(meth) + end - def to_s(options = self.options) - sprite_url(self).value - end - - def respond_to?(meth) - super || @evaluation_context.respond_to?(meth) - end - - def method_missing(meth, *args, &block) - if @evaluation_context.respond_to?(meth) - @evaluation_context.send(meth, *args, &block) - else - super - end + def method_missing(meth, *args, &block) + if @evaluation_context.respond_to?(meth) + @evaluation_context.send(meth, *args, &block) + else + super end end end diff --git a/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb b/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb index 40db7adc..3997aee6 100644 --- a/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb +++ b/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb @@ -1,20 +1,10 @@ module Compass module SassExtensions module Sprites - class ChunkyPngEngine < Sass::Script::Literal - include Compass::SassExtensions::Sprites::Base::InstanceMethods - - def require_png_library! - begin - require 'oily_png' - rescue LoadError - require 'chunky_png' - end - end - + module ChunkyPngEngine # Returns a PNG object def construct_sprite - require_png_library! + #require_png_library! output_png = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT) images.each do |image| input_png = ChunkyPNG::Image.from_file(image.file) diff --git a/lib/compass/sass_extensions/sprites/engines/rmagick_engine.rb b/lib/compass/sass_extensions/sprites/engines/rmagick_engine.rb new file mode 100644 index 00000000..03085075 --- /dev/null +++ b/lib/compass/sass_extensions/sprites/engines/rmagick_engine.rb @@ -0,0 +1,30 @@ +module Compass + module SassExtensions + module Sprites + module RmagickEngine + class ::Magick::Image + alias :save :write + end + # Returns a PNG object + def construct_sprite + output_png = Magick::Image.new(width, height) + output_png.background_color = 'transparent' + output_png.format = 'PNG' + images.each do |image| + input_png = Magick::Image.read(image.file).first + if image.repeat == "no-repeat" + output_png.composite!(input_png, image.left, image.top, Magick::CopyCompositeOp) + else + x = image.left - (image.left / image.width).ceil * image.width + while x < width do + output_png.composite!(input_png, x, image.top, Magick::CopyCompositeOp) + x += image.width + end + end + end + output_png + end + end + end + end +end \ No newline at end of file