compass-magick 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ module Compass::Magick
2
+ module Types
3
+ # A type that generates a {Canvas} from a region filled with a solid
4
+ # color.
5
+ #
6
+ # @example
7
+ #
8
+ # Solid.new(Sass::Script::Color.new([255, 255, 255])).to_canvas(
9
+ # Sass::Script::Number(320),
10
+ # Sass::Script::Number(200)
11
+ # )
12
+ class Solid < Type
13
+ include Utils
14
+
15
+ # Initializes a new Solid instance.
16
+ #
17
+ # @param [Sass::Script::Color] color The solid background color.
18
+ def initialize(color)
19
+ assert_type 'color', color, Sass::Script::Color
20
+ @color = color
21
+ end
22
+
23
+ # @return [Sass::Script::Color] The solid background color.
24
+ attr_reader :color
25
+
26
+ def to_canvas(width, height)
27
+ assert_type 'width', width, Sass::Script::Number
28
+ assert_type 'height', height, Sass::Script::Number
29
+ color = to_chunky_color(@color)
30
+ canvas = Canvas.new(width, height)
31
+ (0...canvas.height).each do |y|
32
+ (0...canvas.width).each do |x|
33
+ canvas.set_pixel(x, y, color)
34
+ end
35
+ end
36
+ canvas
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ module Compass::Magick
2
+ # Abstract Type class.
3
+ #
4
+ # Defines the methods all Type instances must implement.
5
+ #
6
+ # @abstract
7
+ # @see Compass::Magick::Types
8
+ class Type
9
+ include Scriptable
10
+
11
+ # Generates a {Canvas} object.
12
+ #
13
+ # @abstract
14
+ # @param [Sass::Script::Number] width The width of the Canvas.
15
+ # @param [Sass::Script::Number] height The height of the Canvas.
16
+ # @return [Canvas] The Canvas object which is composed
17
+ # on top of the original image.
18
+ def to_canvas(width, height)
19
+ raise AbstractMethod.new("#{self.class} must implement 'to_canvas'")
20
+ end
21
+ end
22
+
23
+ # The Types module includes all fill types available in Compass Magick.
24
+ #
25
+ # Types generate {Canvas} objects with the given options, e.g., a solid
26
+ # color or a linear gradient. The resulting Canvas is then composed on top
27
+ # of the original image using alpha blending.
28
+ #
29
+ # This can be a slow operation, but it reduces the amount of work required
30
+ # and abstracts the drawing logic in four easy steps - draw B/W shape, fill
31
+ # rectangle, mask and compose on top of original image.
32
+ #
33
+ # @see Compass::Magick::Types::Solid
34
+ # @see Compass::Magick::Types::Gradients
35
+ module Types; end
36
+ end
37
+
38
+ require 'magick/types/solid'
39
+ require 'magick/types/gradients'
@@ -0,0 +1,92 @@
1
+ module Compass::Magick
2
+ # Utilities methods used throughout Compass Magick.
3
+ module Utils
4
+ extend self
5
+
6
+ # Checks if <tt>arg</tt> is of the expected <tt>type</tt> and raises a
7
+ # {TypeMismatch} exception otherwise.
8
+ #
9
+ # @param [String] name The argument name (used in the exception message).
10
+ # @param [Object] arg The argument to validate.
11
+ # @param [Object] type The expected <tt>arg</tt> type.
12
+ def assert_type(name, arg, type)
13
+ raise TypeMismatch.new("(#{self.class}) Type mismatch for argument '#{name}'; " +
14
+ "expected #{type} got #{arg.class}(#{arg.inspect}) instead") unless
15
+ arg.nil? ||
16
+ arg.kind_of?(type)
17
+ end
18
+
19
+ # Checks if <tt>arg</tt> is a sub-class of any of the supported
20
+ # <tt>types</tt> and raises a {NotSupported} exception otherwise.
21
+ #
22
+ # @param [String] name The argument name or method signature (used in the
23
+ # exception message).
24
+ # @param [Object] arg The argument to validate.
25
+ # @param [Array<Object>] types The list of supported <tt>arg</tt> types.
26
+ def assert_one_of(name, arg, *types)
27
+ for type in types do
28
+ return if arg.kind_of?(type)
29
+ end
30
+ raise NotSupported.new("#{name} expected argument of type [#{types.join ', '}] got #{arg.class}(#{arg.inspect}) instead")
31
+ end
32
+
33
+ # Converts a Sass::Script::Color to ChunkyPNG::Color object.
34
+ #
35
+ # @param [Sass::Script::Color] color The source color in Sass' format.
36
+ # @return [ChunkyPNG::Color] The source color in ChunkyPNG's format.
37
+ def to_chunky_color(color)
38
+ ChunkyPNG::Color.rgba(color.red, color.green, color.blue, color.alpha * 255)
39
+ end
40
+
41
+ # Converts a fill type (solid color or gradient) to a {Canvas} object.
42
+ #
43
+ # @param [Object] type The type of fill type to convert. Supported:
44
+ # * Sass::Script::Color
45
+ # * Sass::Script::String
46
+ # * {Compass::Magick::Types::Solid}
47
+ # * {Compass::Magick::Types::Gradients::Linear}
48
+ # @param [Sass::Script::Number] width The width of the generated
49
+ # {Canvas}.
50
+ # @param [Sass::Script::Number] height The height of the generated
51
+ # {Canvas}.
52
+ # @return [Canvas] The canvas in the dimensions given with the fill
53
+ # type applied.
54
+ def to_canvas(type, width, height)
55
+ Compass::Magick::Utils.assert_one_of 'to_canvas(..)', type, Sass::Script::Color, Sass::Script::String, Compass::Magick::Type, ChunkyPNG::Canvas
56
+ if type.kind_of?(Sass::Script::Color)
57
+ Compass::Magick::Types::Solid.new(type).to_canvas(width, height)
58
+ elsif type.kind_of?(Sass::Script::String)
59
+ if type.value == 'transparent'
60
+ ChunkyPNG::Canvas.new(width.value, height.value, ChunkyPNG::Color.rgba(0, 0, 0, 0))
61
+ else
62
+ raise NotSupported.new("to_canvas(..) supports String argument of values ['transparent'] got '#{type}' instead")
63
+ end
64
+ elsif type.kind_of?(ChunkyPNG::Canvas)
65
+ type.tile(width.value, height.value)
66
+ elsif type.kind_of?(Compass::Magick::Types::Solid) || type.kind_of?(Compass::Magick::Types::Gradients::Linear)
67
+ type.to_canvas(width, height)
68
+ end
69
+ end
70
+
71
+ # Converts the Sass::Script::Number to a fixed value.
72
+ #
73
+ # @param [Sass::Script::Number] number The number to convert.
74
+ # @param [Float] max The maximum allowed value for this number.
75
+ # @param [Float] default The default value for this number.
76
+ # @return [Float]
77
+ # If <tt>number</tt> is <tt>nil</tt>, <tt>true</tt> or <tt>false</tt>, the <tt>default</tt> is returned.
78
+ # If <tt>number</tt>'s units are '%', it is calculated as a percentage of <tt>max</tt>.
79
+ # If the value is negative, it is calculated as an offset against <tt>max</tt>.
80
+ # Otherwise, the value is returned as-is.
81
+ def value_of(number, max, default = nil)
82
+ return default if number.nil? || (number.kind_of?(Sass::Script::Bool) && ! number.value)
83
+ assert_type 'number', number, Sass::Script::Number
84
+ return max * (number.value.to_f / 100) if number.unit_str == '%'
85
+ return max + number.value if number.value < 0
86
+ number.value
87
+ end
88
+
89
+ # A helper Point(x, y) structure.
90
+ class Point < Struct.new(:x, :y); end
91
+ end
92
+ end
data/lib/magick.rb ADDED
@@ -0,0 +1,79 @@
1
+ # Local development requires we have the current directory on $LOAD_PATH.
2
+ $: << File.dirname(__FILE__)
3
+
4
+ begin
5
+ require 'oily_png'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ begin
9
+ require 'oily_png'
10
+ rescue LoadError
11
+ puts '(Compass:Magick) Unable to load OilyPNG, reverting to ChunkyPNG.'
12
+ puts 'OilyPNG is a C extension to speed up the pure Ruby library.'
13
+ puts "Please install it with the following command:\n\n"
14
+ puts " gem install oily_png\n\n"
15
+ begin
16
+ require 'chunky_png'
17
+ rescue LoadError
18
+ puts "(Compass:Magick) Unable to load ChunkyPNG. Please install it with the following command:\n\n"
19
+ puts " gem install chunky_png\n\n"
20
+ raise
21
+ end
22
+ end
23
+ end
24
+
25
+ # Compass Magick, a library for dynamic image generation.
26
+ #
27
+ # The Compass::Magick module defines constants and exception used throughout
28
+ # the project.
29
+ #
30
+ # @author Stan Angeloff
31
+ module Compass::Magick
32
+ # The current version of Compass Magick. This value is updated manually on
33
+ # release. If you are using a Git clone, the version will always end with
34
+ # '.git'.
35
+ VERSION = '0.1.2.git'
36
+
37
+ # The locations where plug-ins are located. These paths are scanned for
38
+ # *.rb files and loaded in order.
39
+ PLUGINS_PATH = [
40
+ File.join(File.dirname(__FILE__), 'plugins'),
41
+ File.join(ENV['HOME'], '.magick', 'plugins'),
42
+ File.join(Dir.getwd, 'plugins')
43
+ ]
44
+
45
+ # Default exception class for Compass Magick
46
+ class Exception < ::StandardError; end
47
+
48
+ # Exception that is raised when an argument's type does not match the
49
+ # expected.
50
+ class TypeMismatch < Exception; end
51
+
52
+ # Exception that is raised when an abstract method is called.
53
+ class AbstractMethod < Exception; end
54
+
55
+ # Exception that is raised when a method is called with arguments it does
56
+ # not support.
57
+ class NotSupported < Exception; end
58
+
59
+ # Exception that is raised when a method is called in a context it does not
60
+ # support.
61
+ class NotAllowed < Exception; end
62
+ end
63
+
64
+ require 'magick/utils'
65
+ require 'magick/scriptable'
66
+ require 'magick/command'
67
+ require 'magick/effect'
68
+ require 'magick/canvas'
69
+ require 'magick/shapes'
70
+ require 'magick/types'
71
+ require 'magick/functions'
72
+
73
+ # Register Compass Magick as a Compass framework.
74
+ #
75
+ # @see http://compass-style.org/docs/tutorials/extensions/
76
+ Compass::Frameworks.register('magick',
77
+ :stylesheets_directory => File.join(File.dirname(__FILE__), 'stylesheets'),
78
+ :templates_directory => File.join(File.dirname(__FILE__), 'templates')
79
+ )
@@ -0,0 +1,29 @@
1
+ module Compass::Magick
2
+ module Plugins
3
+ # Applies rounded corners around the {Canvas}.
4
+ #
5
+ # @param [Sass::Script::Number] radius The corner radius.
6
+ # @param [Sass::Script::Bool] top_left Controls the top-left corner
7
+ # radius effect (default <tt>true</tt>)
8
+ # @param [Sass::Script::Bool] top_right Controls the top-right corner
9
+ # radius effect (default <tt>true</tt>)
10
+ # @param [Sass::Script::Bool] bottom_right Controls the bottom-right
11
+ # corner radius effect (default <tt>true</tt>)
12
+ # @param [Sass::Script::Bool] bottom_left Controls the bottom-left
13
+ # corner radius effect (default <tt>true</tt>)
14
+ # @return {Command} A command(-set) which applies the corners on the
15
+ # canvas.
16
+ def magick_corners(radius, top_left = nil, top_right = nil, bottom_right = nil, bottom_left = nil)
17
+ Command.new do |canvas|
18
+ Canvas.new(canvas, magick_mask(
19
+ magick_canvas(
20
+ Sass::Script::Number.new(canvas.width),
21
+ Sass::Script::Number.new(canvas.height),
22
+ magick_fill(Sass::Script::Color.new([0, 0, 0])),
23
+ magick_border(Sass::Script::Color.new([255, 255, 255]), radius, nil, top_left, top_right, bottom_right, bottom_left)
24
+ )
25
+ ))
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ module Compass::Magick
2
+
3
+ # Exception that is raised when a pattern is not configured properly.
4
+ class PatternException < Exception; end
5
+
6
+ module Plugins
7
+ # Draws a pattern and returns a B/W {Canvas} ready for masking.
8
+ #
9
+ # Chris Eppstein's original tweet:
10
+ # {http://twitter.com/chriseppstein/status/56095925843668992}
11
+ #
12
+ # @param [Sass::Script::Number] width The width of the pattern.
13
+ # @param [Sass::Script::Number] height The height of the pattern.
14
+ # @param [Sass::Script::List] values The pattern itself. This is a list
15
+ # of values (or a multi-line string) where `1`, `true`, `yes`, `X`, `*`
16
+ # and `+` mark where an opaque white pixel will be placed. Any other
17
+ # value is ignored and is transparent in the output. The size of the
18
+ # list must match the width/height.
19
+ # @return {Canvas} A B/W Canvas with the pattern applied. The resulting
20
+ # image can be applied as a mask.
21
+ # @example Diagonal stripes:
22
+ # magick-pattern(3, 3,
23
+ # x _ _
24
+ # _ x _
25
+ # _ _ x
26
+ # );
27
+ def magick_pattern(width, height, values)
28
+ Compass::Magick::Utils.assert_type 'width', width, Sass::Script::Number
29
+ Compass::Magick::Utils.assert_type 'height', height, Sass::Script::Number
30
+ Compass::Magick::Utils.assert_one_of 'magick-pattern(..)', values, Sass::Script::List, Sass::Script::String
31
+ if values.kind_of?(Sass::Script::String)
32
+ list = values.value.strip().split(/[\r\n]+/).map { |line| line.strip().split(/\s/) }.flatten
33
+ else
34
+ list = values.value
35
+ end
36
+ size = width.value * height.value
37
+ raise PatternException.new("magick-pattern(..) expects #{size} values, got #{list.size} instead: #{list.inspect}") unless size == list.size
38
+ canvas = Canvas.new(width, height)
39
+ opaque = ['1', 'true', 'yes', 'X', '*', '+']
40
+ for y in (0...height.value)
41
+ for x in (0...width.value)
42
+ pixel = list[x + y * height.value].to_s.upcase
43
+ canvas.set_pixel(x, y, ChunkyPNG::Color::WHITE) if opaque.include?(pixel)
44
+ end
45
+ end
46
+ canvas
47
+ end
48
+ end
49
+ end
File without changes
@@ -0,0 +1,19 @@
1
+ require 'helpers'
2
+
3
+ describe Compass::Magick::Canvas do
4
+ describe "#initialize" do
5
+ it "should assert arguments' type" do
6
+ lambda { Compass::Magick::Canvas.new(100, 100) }.should raise_error(Compass::Magick::TypeMismatch)
7
+ end
8
+ end
9
+
10
+ subject { Compass::Magick::Canvas.new(Sass::Script::Number.new(100), Sass::Script::Number.new(100)) }
11
+
12
+ it "should be transparent" do
13
+ subject.get_pixel(0, 0).should == ChunkyPNG::Color::TRANSPARENT
14
+ end
15
+
16
+ it "should encode the image in Base64" do
17
+ subject.to_data_uri.value.include?('data:image/png;base64,').should be_true
18
+ end
19
+ end
data/spec/helpers.rb ADDED
@@ -0,0 +1,5 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib', 'magick')
2
+
3
+ require 'compass'
4
+ require 'chunky_png'
5
+ require 'magick'
@@ -0,0 +1,29 @@
1
+ require 'helpers'
2
+
3
+ describe Compass::Magick::Types::Gradients::Linear do
4
+ describe "#initialize" do
5
+ it "should assert arguments' type" do
6
+ lambda { Compass::Magick::Types::Gradients::Linear.new(45, 'string') }.should raise_error(Compass::Magick::TypeMismatch)
7
+ lambda { Compass::Magick::Types::Gradients::Linear.new(Sass::Script::Number.new(45), 'string') }.should raise_error(Compass::Magick::TypeMismatch)
8
+ lambda { Compass::Magick::Types::Gradients::Linear.new(Sass::Script::Number.new(45), ['string']) }.should raise_error(Compass::Magick::TypeMismatch)
9
+ end
10
+ end
11
+
12
+ subject { Compass::Magick::Types::Gradients::Linear.new(
13
+ Sass::Script::Number.new(45), [
14
+ Compass::Magick::Types::Gradients::ColorStop.new(Sass::Script::Number.new(0), Sass::Script::Color.new([255, 0, 0, 1])),
15
+ Compass::Magick::Types::Gradients::ColorStop.new(Sass::Script::Number.new(50), Sass::Script::Color.new([ 0, 255, 0, 0.5])),
16
+ Compass::Magick::Types::Gradients::ColorStop.new(Sass::Script::Number.new(100), Sass::Script::Color.new([ 0, 0, 255, 1]))
17
+ ]
18
+ ) }
19
+
20
+ it { should respond_to(:angle) }
21
+ it { should respond_to(:stops) }
22
+
23
+ it "should generate a linear-filled Canvas" do
24
+ canvas = subject.to_canvas(Sass::Script::Number.new(101), Sass::Script::Number.new(101))
25
+ canvas.get_pixel( 0, 0).should == ChunkyPNG::Color.rgba(255, 0, 0, 255)
26
+ canvas.get_pixel( 50, 50).should == ChunkyPNG::Color.rgba( 0, 255, 0, 127)
27
+ canvas.get_pixel(100, 100).should == ChunkyPNG::Color.rgba( 0, 0, 255, 255)
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require 'helpers'
2
+
3
+ describe Compass::Magick::Types::Solid do
4
+ describe "#initialize" do
5
+ it "should assert arguments' type" do
6
+ lambda { Compass::Magick::Types::Solid.new('#ff0000') }.should raise_error(Compass::Magick::TypeMismatch)
7
+ end
8
+ end
9
+
10
+ subject { Compass::Magick::Types::Solid.new(Sass::Script::Color.new([255, 0, 0])) }
11
+
12
+ it { should respond_to(:color) }
13
+
14
+ it "should generate a solid Canvas" do
15
+ canvas = subject.to_canvas(Sass::Script::Number.new(100), Sass::Script::Number.new(100))
16
+ canvas.get_pixel( 0, 0).should == ChunkyPNG::Color.rgba(255, 0, 0, 255)
17
+ canvas.get_pixel(99, 99).should == ChunkyPNG::Color.rgba(255, 0, 0, 255)
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: compass-magick
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
11
+ platform: ruby
12
+ authors:
13
+ - Stan Angeloff
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-16 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: compass
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 62196225
29
+ segments:
30
+ - 0
31
+ - 11
32
+ - beta
33
+ - 5
34
+ version: 0.11.beta.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: chunky_png
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 19
46
+ segments:
47
+ - 1
48
+ - 1
49
+ - 0
50
+ version: 1.1.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 15
62
+ segments:
63
+ - 2
64
+ - 0
65
+ - 0
66
+ version: 2.0.0
67
+ type: :development
68
+ version_requirements: *id003
69
+ description:
70
+ email:
71
+ - stanimir@angeloff.name
72
+ executables: []
73
+
74
+ extensions: []
75
+
76
+ extra_rdoc_files: []
77
+
78
+ files:
79
+ - README.md
80
+ - LICENSE.md
81
+ - lib/magick/canvas.rb
82
+ - lib/magick/command.rb
83
+ - lib/magick/effect.rb
84
+ - lib/magick/functions/canvas.rb
85
+ - lib/magick/functions/drawing.rb
86
+ - lib/magick/functions/operations/effects.rb
87
+ - lib/magick/functions/operations.rb
88
+ - lib/magick/functions/sprites.rb
89
+ - lib/magick/functions/types.rb
90
+ - lib/magick/functions.rb
91
+ - lib/magick/plugins.rb
92
+ - lib/magick/scriptable.rb
93
+ - lib/magick/shapes.rb
94
+ - lib/magick/types/gradients.rb
95
+ - lib/magick/types/solid.rb
96
+ - lib/magick/types.rb
97
+ - lib/magick/utils.rb
98
+ - lib/magick.rb
99
+ - lib/plugins/corners.rb
100
+ - lib/plugins/pattern.rb
101
+ - lib/stylesheets/_magick.sass
102
+ - spec/canvas_spec.rb
103
+ - spec/helpers.rb
104
+ - spec/types/gradients_spec.rb
105
+ - spec/types/solid_spec.rb
106
+ homepage: https://github.com/StanAngeloff/compass-magick
107
+ licenses: []
108
+
109
+ post_install_message:
110
+ rdoc_options: []
111
+
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ requirements: []
133
+
134
+ rubyforge_project:
135
+ rubygems_version: 1.7.2
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Dynamic image generation for Compass using ChunkyPNG.
139
+ test_files:
140
+ - spec/canvas_spec.rb
141
+ - spec/helpers.rb
142
+ - spec/types/gradients_spec.rb
143
+ - spec/types/solid_spec.rb
144
+ has_rdoc: true