abstracted out engine for chunkypng
This commit is contained in:
parent
1c71df3b41
commit
03f4c23c04
@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
compass (0.11.beta.2.153582f)
|
compass (0.11.beta.2.1c71df3)
|
||||||
chunky_png (~> 0.12.0)
|
chunky_png (~> 0.12.0)
|
||||||
sass (>= 3.1.0.alpha.218)
|
sass (>= 3.1.0.alpha.218)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
require 'digest/md5'
|
require 'digest/md5'
|
||||||
require 'compass/sass_extensions/sprites/image'
|
require 'compass/sass_extensions/sprites/image'
|
||||||
require 'compass/sass_extensions/sprites/base'
|
require 'compass/sass_extensions/sprites/base'
|
||||||
|
require 'compass/sass_extensions/sprites/engines/chunky_png_engine'
|
||||||
|
|
||||||
module Compass::SassExtensions::Functions::Sprites
|
module Compass::SassExtensions::Functions::Sprites
|
||||||
ZERO = Sass::Script::Number::new(0)
|
ZERO = Sass::Script::Number::new(0)
|
||||||
@ -14,63 +15,8 @@ module Compass::SassExtensions::Functions::Sprites
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class SpriteMap < Compass::SassExtensions::Sprites::Base
|
class SpriteMap < Compass::SassExtensions::Sprites::ChunkyPngEngine
|
||||||
# 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
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def image_for(name)
|
|
||||||
@images.detect { |img| img.name == name}
|
|
||||||
end
|
|
||||||
|
|
||||||
def require_png_library!
|
|
||||||
begin
|
|
||||||
require 'oily_png'
|
|
||||||
rescue LoadError
|
|
||||||
require 'chunky_png'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a PNG object
|
|
||||||
def construct_sprite
|
|
||||||
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)
|
|
||||||
if image.repeat == "no-repeat"
|
|
||||||
output_png.replace input_png, image.left, image.top
|
|
||||||
else
|
|
||||||
x = image.left - (image.left / image.width).ceil * image.width
|
|
||||||
while x < width do
|
|
||||||
output_png.replace input_png, x, image.top
|
|
||||||
x += image.width
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
output_png
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a SpriteMap object. A sprite map, when used in a property is the same
|
# Creates a SpriteMap object. A sprite map, when used in a property is the same
|
||||||
|
@ -1,121 +1,156 @@
|
|||||||
module Compass
|
module Compass
|
||||||
module SassExtensions
|
module SassExtensions
|
||||||
module Sprites
|
module Sprites
|
||||||
class Base < Sass::Script::Literal
|
module Base
|
||||||
# Changing this string will invalidate all previously generated sprite images.
|
module ClassMethods
|
||||||
# We should do so only when the packing algorithm changes
|
def from_uri(uri, context, kwargs)
|
||||||
SPRITE_VERSION = "1"
|
path, name = Compass::Sprites.path_and_name(uri.value)
|
||||||
|
sprites = Compass::Sprites.discover_sprites(uri.value).map do |sprite|
|
||||||
attr_accessor :image_names, :path, :name, :options
|
sprite.gsub(Compass.configuration.images_path+"/", "")
|
||||||
attr_accessor :images, :width, :height
|
end
|
||||||
|
new(sprites, path, name, context, kwargs)
|
||||||
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
|
end
|
||||||
new(sprites, path, name, context, kwargs)
|
|
||||||
end
|
end
|
||||||
|
#instance Methods
|
||||||
|
module InstanceMethods
|
||||||
|
|
||||||
def initialize(image_names, path, name, context, options)
|
def self.included(base)
|
||||||
@image_names, @path, @name, @options = image_names, path, name, options
|
base.extend(ClassMethods)
|
||||||
@images = nil
|
end
|
||||||
@width = nil
|
|
||||||
@height = nil
|
|
||||||
@evaluation_context = context
|
|
||||||
validate!
|
|
||||||
compute_image_metadata!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Calculate the size of the sprite
|
# Changing this string will invalidate all previously generated sprite images.
|
||||||
def size
|
# We should do so only when the packing algorithm changes
|
||||||
[width, height]
|
SPRITE_VERSION = "1"
|
||||||
end
|
|
||||||
|
|
||||||
def sprite_names
|
attr_accessor :image_names, :path, :name, :options
|
||||||
image_names.map{|f| Compass::Sprites.sprite_name(f) }
|
attr_accessor :images, :width, :height
|
||||||
end
|
|
||||||
|
|
||||||
def validate!
|
|
||||||
for sprite_name in sprite_names
|
def initialize(image_names, path, name, context, options)
|
||||||
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
|
@image_names, @path, @name, @options = image_names, path, name, options
|
||||||
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
|
@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
|
end
|
||||||
end
|
end
|
||||||
end
|
# Calculates the overal image dimensions
|
||||||
|
# collects image sizes and input parameters for each sprite
|
||||||
# The on-the-disk filename of the sprite
|
def compute_image_positions!
|
||||||
def filename
|
@images.each_with_index do |image, index|
|
||||||
File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png")
|
image.left = image.position.unit_str == "%" ? (@width - image.width) * (image.position.value / 100) : image.position.value
|
||||||
end
|
next if index == 0
|
||||||
|
last_image = @images[index-1]
|
||||||
# Calculates the overal image dimensions
|
image.top = last_image.top + last_image.height + [image.spacing, last_image.spacing].max
|
||||||
# collects image sizes and input parameters for each sprite
|
end
|
||||||
def compute_image_metadata!
|
|
||||||
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
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def generation_required?
|
def image_for(name)
|
||||||
!File.exists?(filename) || outdated?
|
@images.detect { |img| img.name == name}
|
||||||
end
|
end
|
||||||
|
|
||||||
def uniqueness_hash
|
def sprite_names
|
||||||
@uniqueness_hash ||= begin
|
image_names.map{|f| Compass::Sprites.sprite_name(f) }
|
||||||
sum = Digest::MD5.new
|
end
|
||||||
sum << SPRITE_VERSION
|
|
||||||
sum << path
|
def validate!
|
||||||
images.each do |image|
|
for sprite_name in sprite_names
|
||||||
[:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
|
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
|
||||||
sum << image.send(attr).to_s
|
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sum.hexdigest[0...10]
|
|
||||||
end
|
end
|
||||||
@uniqueness_hash
|
|
||||||
end
|
|
||||||
|
|
||||||
# saves the sprite for later retrieval
|
# The on-the-disk filename of the sprite
|
||||||
def save!(output_png)
|
def filename
|
||||||
saved = output_png.save filename
|
File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png")
|
||||||
Compass.configuration.run_callback(:sprite_saved, filename)
|
end
|
||||||
saved
|
|
||||||
end
|
|
||||||
|
|
||||||
# All the full-path filenames involved in this sprite
|
# Generate a sprite image if necessary
|
||||||
def image_filenames
|
def generate
|
||||||
image_names.map do |image_name|
|
if generation_required?
|
||||||
File.join(Compass.configuration.images_path, image_name)
|
sprite_data = construct_sprite
|
||||||
|
save!(sprite_data)
|
||||||
|
Compass.configuration.run_callback(:sprite_generated, sprite_data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
to_s
|
||||||
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inspect
|
|
||||||
to_s
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
# Returns a PNG object
|
||||||
|
def construct_sprite
|
||||||
|
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)
|
||||||
|
if image.repeat == "no-repeat"
|
||||||
|
output_png.replace input_png, image.left, image.top
|
||||||
|
else
|
||||||
|
x = image.left - (image.left / image.width).ceil * image.width
|
||||||
|
while x < width do
|
||||||
|
output_png.replace input_png, x, image.top
|
||||||
|
x += image.width
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
output_png
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user