compass-magick 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+ ===============
3
+
4
+ > Copyright (c) 2011 Stan Angeloff
5
+ >
6
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ > of this software and associated documentation files (the "Software"), to deal
8
+ > in the Software without restriction, including without limitation the rights
9
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ > copies of the Software, and to permit persons to whom the Software is
11
+ > furnished to do so, subject to the following conditions:
12
+ >
13
+ > The above copyright notice and this permission notice shall be included in
14
+ > all copies or substantial portions of the Software.
15
+ >
16
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ > THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ Compass Magick
2
+ ==============
3
+
4
+ Dynamic image generation for Compass using ChunkyPNG (no dependency on RMagick).
5
+
6
+ Example
7
+ -------
8
+
9
+ body {
10
+ background: transparent magick-canvas(100px, 100px,
11
+ magick-fill(magick-linear-gradient(red, green, blue))
12
+ ) repeat-x;
13
+ }
14
+
15
+ A CSS3 [Sassy Button](http://jaredhardy.com/sassy-buttons/) recreated in Compass Magick:
16
+ [StanAngeloff.github.com/compass-magick/](http://stanangeloff.github.com/compass-magick/) (showcases all features)
17
+
18
+ Installation
19
+ ------------
20
+
21
+ Installation is simple via RubyGems. [Compass](http://beta.compass-style.org) and [ChunkyPNG](https://github.com/wvanbergen/chunky_png) are required.
22
+
23
+ gem install compass-magick
24
+
25
+ Usage
26
+ -----
27
+
28
+ To start a new Compass project with Magick included:
29
+
30
+ compass create -r magick my_project
31
+
32
+ To use Compass Magick within your existing projects, require the plugin from your Compass configuration:
33
+
34
+ require 'magick'
35
+
36
+ APIs
37
+ ----
38
+
39
+ [List of all available commands](https://github.com/StanAngeloff/compass-magick/blob/master/APIs.md).
40
+
41
+ RDoc is also [available for the entire project](http://stanangeloff.github.com/compass-magick/doc/frames.html).
42
+
43
+ Contribute
44
+ ----------
45
+
46
+ Compass Magick is very much a work in progress. Documentation is sparse, speed is a major issue and there are probably a lot of bugs.
47
+
48
+ You can help by looking at any of the following areas (ordered by priority):
49
+
50
+ - [Optimizing the code](https://github.com/StanAngeloff/compass-magick/blob/master/lib/magick/canvas.rb#L98) as floating point math may not always be needed and too many object copies sucks
51
+ - [Writing tests](https://github.com/StanAngeloff/compass-magick/tree/master/spec) to make sure nothing breaks in new versions
52
+ - [Performing code reviews](https://github.com/StanAngeloff/compass-magick/tree/master/lib) since this is my very first Ruby project and I don't have any experience with the language so there are probably many st**OO**pid things in code
53
+ - [Improving RDoc](http://stanangeloff.github.com/compass-magick/doc/frames.html) because you can never have too much documentation
54
+ - [Adding new plugins](http://stanangeloff.github.com/compass-magick/doc/Compass/Magick/Plugins.html). Compass Magick [looks](https://github.com/StanAngeloff/compass-magick/blob/master/lib/magick.rb#L39) for Ruby files and imports those on startup
55
+ - [Submitting more examples](https://github.com/StanAngeloff/compass-magick/tree/gh-pages) to show off what Compass Magick can do
56
+ - [Opening issues](https://github.com/StanAngeloff/compass-magick/issues) for features you find missing or broken
57
+ - Porting over some of [CamanJS](http://camanjs.com/)' goodness, we all love visual effects
58
+
59
+ ### Credits
60
+
61
+ - [Willem van Bergen](http://twitter.com/#!/wvanbergen) for his great work on ChunkyPNG which has been a major source of knowledge while working on the project
62
+ - [Chris Eppstein](http://twitter.com/#!/chriseppstein) for [Compass](https://github.com/chriseppstein/compass) itself
63
+ - The Ruby community for being awesome and welcoming a stranger
64
+
65
+ ### Copyright
66
+
67
+ Copyright (c) 2011 Stan Angeloff. See [LICENSE.md](https://github.com/StanAngeloff/compass-magick/blob/master/LICENSE.md) for details.
@@ -0,0 +1,127 @@
1
+ require 'base64'
2
+
3
+ module Compass::Magick
4
+ # A Canvas class that inherits ChunkyPNG::Canvas to represent the image as
5
+ # a matrix of pixels.
6
+ #
7
+ # The canvas is constructed from a given width and height on a transparent
8
+ # background. The list of commands is executed in order and the resulting
9
+ # image is returned as a Base64 encoded PNG-24 Data URI.
10
+ #
11
+ # @see http://rdoc.info/gems/chunky_png/0.12.0/ChunkyPNG/Canvas
12
+ # @example
13
+ #
14
+ # Canvas.new(320, 240).to_data_uri
15
+ class Canvas < ChunkyPNG::Canvas
16
+ include Scriptable
17
+ include Utils
18
+
19
+ # Initializes a new Canvas instance.
20
+ #
21
+ # @overload initialize(canvas, *commands)
22
+ # @param [Canvas] canvas Copy image from another Canvas object.
23
+ # @param [Array<Command>] commands The list of commands to execute on
24
+ # new Canvas instance.
25
+ # @overload initialize(data, *commands)
26
+ # @param [Sass::Script::String] data A Base64 encoded Data URL
27
+ # containing the image.
28
+ # @param [Array<Command>] commands The list of commands to execute on
29
+ # the Canvas instance.
30
+ # @overload initialize(url, *commands)
31
+ # @param [Sass::Script::String] url The URL to the image, relative to
32
+ # the stylesheet.
33
+ # @param [Array<Command>] commands The list of commands to execute on
34
+ # the Canvas instance.
35
+ # @overload initialize(path, *commands)
36
+ # @param [Sass::Script::String] path The path to the image, relative to
37
+ # the configured <tt>images_dir</tt>.
38
+ # @param [Array<Command>] commands The list of commands to execute on
39
+ # the Canvas instance.
40
+ # @overload initialize(width, height, *commands)
41
+ # @param [Sass::Script::Number] width The width of the new transparent
42
+ # Canvas.
43
+ # @param [Sass::Script::Number] height The height of the new transparent
44
+ # Canvas.
45
+ # @param [Array<Command>] commands The list of commands to execute on the
46
+ # Canvas instance.
47
+ def initialize(*commands)
48
+ from_any(commands)
49
+ list = []
50
+ commands.each do |command|
51
+ if command.kind_of?(Sass::Script::List)
52
+ list.push(command.value)
53
+ else
54
+ list.push(command)
55
+ end
56
+ end
57
+ list.flatten!
58
+ list.each_with_index { |command, index| assert_type "command[#{index}]", command, Command }
59
+ list.each do |command|
60
+ result = command.block.call(self)
61
+ inherit result, false if result.kind_of?(ChunkyPNG::Canvas) unless result == self
62
+ end
63
+ end
64
+
65
+ # Serializes the Canvas as a Base64 encoded PNG-24 Data URI.
66
+ #
67
+ # @return [String] A Base64 encoded PNG-24 Data URI for the generated
68
+ # image.
69
+ def to_data_uri(options = {})
70
+ data = Base64.encode64(to_blob).gsub("\n", '')
71
+ "url('data:image/png;base64,#{data}')"
72
+ end
73
+
74
+ alias :to_s :to_data_uri
75
+
76
+ # Tile the Canvas to fill the region defined by `width` and `height`.
77
+ #
78
+ # @return {Canvas} A new Canvas instance that fills the given region.
79
+ def tile(width, height)
80
+ canvas = ChunkyPNG::Canvas.new(width, height)
81
+ for y in (0...height)
82
+ for x in (0...width)
83
+ canvas.set_pixel(x, y, @pixels[(x % @width) + (y % @height) * @height])
84
+ end
85
+ end
86
+ canvas
87
+ end
88
+
89
+ private
90
+
91
+ def inherit(canvas, copy = true)
92
+ @width = canvas.width
93
+ @height = canvas.height
94
+ @pixels = (copy ? canvas.pixels.dup : canvas.pixels)
95
+ self
96
+ end
97
+
98
+ def from_any(args)
99
+ source = args.shift
100
+ if source.kind_of?(ChunkyPNG::Canvas)
101
+ inherit source
102
+ elsif source.kind_of?(Sass::Script::Number)
103
+ inherit ChunkyPNG::Canvas.new(source.value, args.shift.value), false
104
+ elsif source.kind_of?(Sass::Script::String)
105
+ if source.value.include?('url(')
106
+ if source.value.include?('base64,')
107
+ encoded = source.value.match(/base64,([a-zA-Z0-9+\/=]+)/)[1]
108
+ blob = Base64.decode64(encoded)
109
+ canvas = ChunkyPNG::Canvas.from_blob(blob)
110
+ else
111
+ filename = source.value.gsub(/^url\(['"]?|["']?\)$/, '')
112
+ path = File.join(Compass.configuration.css_path, filename.split('?').shift())
113
+ canvas = ChunkyPNG::Canvas.from_file(path)
114
+ end
115
+ else
116
+ path = File.join(Compass.configuration.images_path, source.value.split('?').shift())
117
+ canvas = ChunkyPNG::Canvas.from_file(path)
118
+ end
119
+ inherit canvas, false
120
+ else
121
+ raise NotSupported.new("Canvas.new(..) expected argument of type " +
122
+ "Compass::Magick::Canvas, Sass::Script::Number or Sass::Script::String " +
123
+ "got #{source.class}(#{source.inspect}) instead")
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,32 @@
1
+ module Compass::Magick
2
+ # A class that executes a block on a {Canvas} object.
3
+ #
4
+ # When a function is called in a Sass document, it delays its execution
5
+ # until placed within a canvas. To do so, it must store state and return a
6
+ # valid Sass node.
7
+ #
8
+ # @example
9
+ #
10
+ # Command.new do |canvas|
11
+ # canvas.rect(0, 0, canvas.width - 1, canvas.height - 1, ChunkyPNG::Color::BLACK)
12
+ # end
13
+ class Command
14
+ include Scriptable
15
+ include Utils
16
+
17
+ # Initializes a new Command instance.
18
+ #
19
+ # @param [Proc] block The block to execute.
20
+ def initialize(&block)
21
+ @block = block
22
+ end
23
+
24
+ # @return [Proc] The block to execute.
25
+ attr_reader :block
26
+
27
+ # Raises an error if the command is used outside of a {Canvas} object.
28
+ def to_s(options = {})
29
+ raise NotAllowed.new("#{self.class} cannot be used outside of a Compass::Magick::Canvas object")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module Compass::Magick
2
+ # A class that executes a processing block on each {Canvas} pixel.
3
+ #
4
+ # @example
5
+ #
6
+ # # Turn all pixels opaque
7
+ # Effect.new do |pixel|
8
+ # pixel | 0x000000ff
9
+ # end
10
+ class Effect < Command
11
+
12
+ # Initializes a new Effect instance.
13
+ #
14
+ # @param [Proc] process The processing block to execute.
15
+ def initialize(&process)
16
+ @process = process
17
+ @block = lambda do |canvas|
18
+ for y in 0...canvas.height do
19
+ for x in 0...canvas.width do
20
+ canvas.set_pixel(x, y, @process.call(canvas.get_pixel(x, y)))
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ # Clamp value between 0..255 so it doesn't overflow the 8-bit colorspace.
27
+ #
28
+ # @param [Float] value The value to clamp.
29
+ # @return [Integer] The value as an integer between 0..255.
30
+ def self.clamp(value)
31
+ [0, [value.to_i, 255].min].max
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Compass::Magick
2
+ module Functions
3
+ # Methods for creating a new {Compass::Magick::Canvas}.
4
+ module Canvas
5
+ # Creates a new {Compass::Magick::Canvas} and execute all
6
+ # commands on the instance.
7
+ #
8
+ # @overload magick_canvas(canvas, *commands)
9
+ # @param [Canvas] canvas Copy image from another Canvas object.
10
+ # @param [Array<Command>] commands The list of commands to execute on
11
+ # new Canvas instance.
12
+ # @overload magick_canvas(data, *commands)
13
+ # @param [Sass::Script::String] data A Base64 encoded Data URL
14
+ # containing the image.
15
+ # @param [Array<Command>] commands The list of commands to execute on
16
+ # the Canvas instance.
17
+ # @overload magick_canvas(url, *commands)
18
+ # @param [Sass::Script::String] url The URL to the image, relative to
19
+ # the stylesheet.
20
+ # @param [Array<Command>] commands The list of commands to execute on
21
+ # the Canvas instance.
22
+ # @overload magick_canvas(path, *commands)
23
+ # @param [Sass::Script::String] path The path to the image, relative
24
+ # to the configured <tt>images_dir</tt>.
25
+ # @param [Array<Command>] commands The list of commands to execute on
26
+ # the Canvas instance.
27
+ # @overload magick_canvas(width, height, *commands)
28
+ # @param [Sass::Script::Number] width The width of the new transparent
29
+ # Canvas.
30
+ # @param [Sass::Script::Number] height The height of the new
31
+ # transparent Canvas.
32
+ # @param [Array<Command>] commands The list of commands to execute on
33
+ # the Canvas instance.
34
+ # @return [String] A Base64 encoded PNG-24 Data URI for the generated
35
+ # image.
36
+ def magick_canvas(*commands)
37
+ Compass::Magick::Canvas.new(*commands)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,156 @@
1
+ module Compass::Magick
2
+ module Functions
3
+ # Methods for drawing on a {Compass::Magick::Canvas}.
4
+ module Drawing
5
+ # Fills the {Canvas} region with the given {Compass::Magick::Type}.
6
+ #
7
+ # @param [Object] type The type of fill to apply. Supported:
8
+ # * Sass::Script::Color
9
+ # * Sass::Script::String
10
+ # * {Compass::Magick::Types::Solid}
11
+ # * {Compass::Magick::Types::Gradients::Linear}
12
+ # @param [Sass::Script::Number] x1 The left coordinate of the fill.
13
+ # @param [Sass::Script::Number] y1 The top coordinate of the fill.
14
+ # @param [Sass::Script::Number] x2 The right coordinate of the fill.
15
+ # @param [Sass::Script::Number] y2 The bottom coordinate of the fill.
16
+ # @return {Command} A command which applies the fill on the canvas.
17
+ def magick_fill(type, x1 = nil, y1 = nil, x2 = nil, y2 = nil)
18
+ Compass::Magick::Utils.assert_type 'x1', x1, Sass::Script::Number
19
+ Compass::Magick::Utils.assert_type 'y1', y1, Sass::Script::Number
20
+ Compass::Magick::Utils.assert_type 'x2', x2, Sass::Script::Number
21
+ Compass::Magick::Utils.assert_type 'y2', y2, Sass::Script::Number
22
+ Command.new do |canvas|
23
+ canvas_x1 = Compass::Magick::Utils.value_of(x1, canvas.width, 0)
24
+ canvas_y1 = Compass::Magick::Utils.value_of(y1, canvas.height, 0)
25
+ canvas_x2 = Compass::Magick::Utils.value_of(x2, canvas.width, canvas.width)
26
+ canvas_y2 = Compass::Magick::Utils.value_of(y2, canvas.height, canvas.height)
27
+ canvas_x1, canvas_x2 = canvas_x2, canvas_x1 if canvas_x1 > canvas_x2
28
+ canvas_y1, canvas_y2 = canvas_y2, canvas_y1 if canvas_y1 > canvas_y2
29
+ width = Sass::Script::Number.new(canvas_x2 - canvas_x1)
30
+ height = Sass::Script::Number.new(canvas_y2 - canvas_y1)
31
+ top = type
32
+ tail = []
33
+ if top.kind_of?(Sass::Script::List)
34
+ tail = top.value
35
+ top = tail.shift
36
+ end
37
+ overlay = Compass::Magick::Utils.to_canvas(top, width, height)
38
+ tail.each do |mask_type|
39
+ mask_canvas = Compass::Magick::Utils.to_canvas(mask_type, width, height)
40
+ overlay = Compass::Magick::Canvas.new(overlay, magick_mask(mask_canvas))
41
+ end
42
+ canvas.compose(overlay, canvas_x1, canvas_y1)
43
+ end
44
+ end
45
+
46
+ # Draws a circle on the {Canvas} with the given fill
47
+ # {Compass::Magick::Type}.
48
+ #
49
+ # The X and Y coordinates are **not** the center of the circle, but
50
+ # rather the coordinates at which the circle is composed on top of the
51
+ # {Canvas}. E.g., <tt>100%, 100%</tt> would put the circle at the
52
+ # bottom-right corner of the {Canvas}.
53
+ #
54
+ # @param [Object] type The type of fill to apply. Supported:
55
+ # * Sass::Script::Color
56
+ # * Sass::Script::String
57
+ # * {Compass::Magick::Types::Solid}
58
+ # * {Compass::Magick::Types::Gradients::Linear}
59
+ # @param [Sass::Script::Number] radius The radius of the circle.
60
+ # @param [Sass::Script::Number] x The composite X coordinate of the
61
+ # circle.
62
+ # @param [Sass::Script::Number] y The composite Y coordinate of the
63
+ # circle.
64
+ # @param [Sass::Script::Number] feather The feater value determines the
65
+ # anti-aliasing the circle will get, defaults to <tt>1</tt>.
66
+ # @return {Command} A command(-set) which composes a circle on the
67
+ # canvas.
68
+ def magick_circle(type, radius, compose_x, compose_y, feather = nil)
69
+ Compass::Magick::Utils.assert_type 'radius', radius, Sass::Script::Number
70
+ Compass::Magick::Utils.assert_type 'feather', feather, Sass::Script::Number
71
+ Command.new do |canvas|
72
+ circle_radius = Compass::Magick::Utils.value_of(radius, [canvas.width, canvas.height].min, 1)
73
+ circle_feather = Compass::Magick::Utils.value_of(feather, [canvas.width, canvas.height].min, 1).to_f
74
+ mask = Compass::Magick::Shapes.circle(circle_radius, circle_radius, circle_feather)
75
+ overlay = Compass::Magick::Utils.to_canvas(type, Sass::Script::Number.new(circle_radius), Sass::Script::Number.new(circle_radius))
76
+ Compass::Magick::Canvas.new(canvas, magick_compose(Compass::Magick::Canvas.new(overlay, magick_mask(mask)), compose_x, compose_y))
77
+ end
78
+ end
79
+
80
+ # Draws a (rounded) border around the {Canvas} with the given width and
81
+ # fill {Compass::Magick::Type}.
82
+ #
83
+ # When <tt>width</tt> is not given, the border fills the entire image.
84
+ #
85
+ # @param [Object] type The type of fill to apply. Supported:
86
+ # * Sass::Script::Color
87
+ # * Sass::Script::String
88
+ # * {Compass::Magick::Types::Solid}
89
+ # * {Compass::Magick::Types::Gradients::Linear}
90
+ # @param [Sass::Script::Number] radius The border radius.
91
+ # @param [Sass::Script::Number] will The border width, defaults to
92
+ # filling the entire image.
93
+ # @param [Sass::Script::Bool] top_left Controls the top-left border
94
+ # radius effect (default <tt>true</tt>)
95
+ # @param [Sass::Script::Bool] top_right Controls the top-right border
96
+ # radius effect (default <tt>true</tt>)
97
+ # @param [Sass::Script::Bool] bottom_right Controls the bottom-right
98
+ # border radius effect (default <tt>true</tt>)
99
+ # @param [Sass::Script::Bool] bottom_left Controls the bottom-left
100
+ # border radius effect (default <tt>true</tt>)
101
+ # @return {Command} A command(-set) which composes the border on the
102
+ # canvas.
103
+ def magick_border(type, radius = nil, width = nil, top_left = nil, top_right = nil, bottom_right = nil, bottom_left = nil)
104
+ Compass::Magick::Utils.assert_type 'radius', radius, Sass::Script::Number
105
+ Compass::Magick::Utils.assert_type 'width', width, Sass::Script::Number
106
+ Compass::Magick::Utils.assert_type 'top_left', top_left, Sass::Script::Bool
107
+ Compass::Magick::Utils.assert_type 'top_right', top_right, Sass::Script::Bool
108
+ Compass::Magick::Utils.assert_type 'bottom_left', bottom_left, Sass::Script::Bool
109
+ Compass::Magick::Utils.assert_type 'bottom_right', bottom_right, Sass::Script::Bool
110
+ Command.new do |canvas|
111
+ max = [canvas.width, canvas.height].max
112
+ min = [canvas.width, canvas.height].min
113
+ border_width = Compass::Magick::Utils.value_of(width, max, max)
114
+ border_radius = Compass::Magick::Utils.value_of(radius, min, 10)
115
+ border_top_left = (top_left.nil? || top_left.value)
116
+ border_top_right = (top_right.nil? || top_right.value)
117
+ border_bottom_right = (bottom_right.nil? || bottom_right.value)
118
+ border_bottom_left = (bottom_left.nil? || bottom_left.value)
119
+ right_x = canvas.width - border_radius
120
+ bottom_y = canvas.height - border_radius
121
+ mask = ChunkyPNG::Canvas.new(canvas.width, canvas.height, ChunkyPNG::Color.rgba(0, 0, 0, 0))
122
+ for y in 0...canvas.height
123
+ for x in 0...canvas.width
124
+ unless (border_top_left && (y < border_radius && x < border_radius)) ||
125
+ (border_top_right && (y < border_radius && x > right_x)) ||
126
+ (border_bottom_right && (y > bottom_y && x > right_x)) ||
127
+ (border_bottom_left && (y > bottom_y && x < border_radius))
128
+ if y < border_width || y >= canvas.height - border_width ||
129
+ x < border_width || x >= canvas.width - border_width
130
+ mask.set_pixel(x, y, ChunkyPNG::Color::WHITE)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ if border_radius > 0
136
+ radius_mask = Compass::Magick::Shapes.circle(border_radius * 2, border_width)
137
+ if border_top_left
138
+ mask.compose!(radius_mask.crop(0, 0, border_radius, border_radius), 0, 0)
139
+ end
140
+ if border_top_right
141
+ mask.compose!(radius_mask.crop(border_radius, 0, border_radius, border_radius), mask.width - border_radius, 0)
142
+ end
143
+ if border_bottom_right
144
+ mask.compose!(radius_mask.crop(border_radius, border_radius, border_radius, border_radius), mask.width - border_radius, mask.height - border_radius)
145
+ end
146
+ if border_bottom_left
147
+ mask.compose!(radius_mask.crop(0, border_radius, border_radius, border_radius), 0, mask.height - border_radius)
148
+ end
149
+ end
150
+ overlay = Compass::Magick::Utils.to_canvas(type, Sass::Script::Number.new(canvas.width), Sass::Script::Number.new(canvas.height))
151
+ Compass::Magick::Canvas.new(canvas, magick_compose(Compass::Magick::Canvas.new(overlay, magick_mask(mask))))
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,143 @@
1
+ module Compass::Magick
2
+ module Functions::Operations
3
+ # Methods for performing various visual effects on a
4
+ # {Compass::Magick::Canvas}, e.g., fade, desaturate, etc.
5
+ module Effects
6
+ extend self
7
+
8
+ # Adjusts the intensity of a color by changing its alpha by a given
9
+ # value.
10
+ #
11
+ # @param [Sass::Script::Number] adjust Fade value as a float between
12
+ # 0.0 and 1.0.
13
+ # @return {Effect} A command which applies the fade to the canvas.
14
+ def fade(adjust = nil)
15
+ Compass::Magick::Utils.assert_type 'adjust', adjust, Sass::Script::Number
16
+ fade_adjust = 255 - (255 * Compass::Magick::Utils.value_of(adjust, 1.0, 0.5)).to_i
17
+ Effect.new { |pixel| ChunkyPNG::Color.fade(pixel, fade_adjust) }
18
+ end
19
+
20
+ # Adjusts the brightness of a color by changing its [R, G, B]
21
+ # components by a given value.
22
+ #
23
+ # Copyright (c) 2010, Ryan LeFevre
24
+ # http://www.camanjs.com
25
+ #
26
+ # @param [Sass::Script::Number] adjust Brightness value as a float
27
+ # between -1.0 and 1.0.
28
+ # @return {Effect} A command which applies the Brightness to the
29
+ # canvas.
30
+ def brightness(adjust = nil)
31
+ Compass::Magick::Utils.assert_type 'adjust', adjust, Sass::Script::Number
32
+ brightness_adjust = 255 * Compass::Magick::Utils.value_of(adjust, 1.0, 0.5)
33
+ Effect.new do |pixel|
34
+ ChunkyPNG::Color.rgba(
35
+ Effect.clamp(ChunkyPNG::Color.r(pixel) + brightness_adjust),
36
+ Effect.clamp(ChunkyPNG::Color.g(pixel) + brightness_adjust),
37
+ Effect.clamp(ChunkyPNG::Color.b(pixel) + brightness_adjust),
38
+ ChunkyPNG::Color.a(pixel)
39
+ )
40
+ end
41
+ end
42
+
43
+ # Adjusts the contrast of a color by changing its [R, G, B] components
44
+ # by a given value.
45
+ #
46
+ # Copyright (c) 2010, Ryan LeFevre
47
+ # http://www.camanjs.com
48
+ #
49
+ # @param [Sass::Script::Number] adjust Contrast value as a float,
50
+ # above 0.0.
51
+ # @return {Effect} A command which applies the contrast to the canvas.
52
+ def contrast(adjust = nil)
53
+ Compass::Magick::Utils.assert_type 'adjust', adjust, Sass::Script::Number
54
+ contrast_adjust = (1.0 + Compass::Magick::Utils.value_of(adjust, 1.0, 0.5))
55
+ Effect.new do |pixel|
56
+ ChunkyPNG::Color.rgba(
57
+ Effect.clamp(((ChunkyPNG::Color.r(pixel) / 255.0 - 0.5) * contrast_adjust + 0.5) * 255),
58
+ Effect.clamp(((ChunkyPNG::Color.g(pixel) / 255.0 - 0.5) * contrast_adjust + 0.5) * 255),
59
+ Effect.clamp(((ChunkyPNG::Color.b(pixel) / 255.0 - 0.5) * contrast_adjust + 0.5) * 255),
60
+ ChunkyPNG::Color.a(pixel)
61
+ )
62
+ end
63
+ end
64
+
65
+ # Adjusts the saturation of a color by changing its [R, G, B]
66
+ # components matching the highest intensity.
67
+ #
68
+ # Copyright (c) 2010, Ryan LeFevre
69
+ # http://www.camanjs.com
70
+ #
71
+ # @param [Sass::Script::Number] adjust Saturation value as a float,
72
+ # above 0.0.
73
+ # @return {Effect} A command which applies the saturation to the canvas.
74
+ def saturation(adjust = nil)
75
+ Compass::Magick::Utils.assert_type 'adjust', adjust, Sass::Script::Number
76
+ saturation_adjust = Compass::Magick::Utils.value_of(adjust, 1.0, 0.5) * -1
77
+ Effect.new do |pixel|
78
+ r = ChunkyPNG::Color.r(pixel)
79
+ g = ChunkyPNG::Color.g(pixel)
80
+ b = ChunkyPNG::Color.b(pixel)
81
+ max = [r, g, b].max
82
+ r = r + (max - r) * saturation_adjust unless r == max
83
+ g = g + (max - g) * saturation_adjust unless g == max
84
+ b = b + (max - b) * saturation_adjust unless b == max
85
+ ChunkyPNG::Color.rgba(Effect.clamp(r), Effect.clamp(g), Effect.clamp(b), ChunkyPNG::Color.a(pixel))
86
+ end
87
+ end
88
+
89
+ # Adjusts the vibrance of a color by changing its [R, G, B] components
90
+ # matching the average intensity.
91
+ #
92
+ # Copyright (c) 2010, Ryan LeFevre
93
+ # http://www.camanjs.com
94
+ #
95
+ # @param [Sass::Script::Number] adjust Vibrance value as a float,
96
+ # above 0.0.
97
+ # @return {Effect} A command which applies the vibrance to the canvas.
98
+ def vibrance(adjust = nil)
99
+ Compass::Magick::Utils.assert_type 'adjust', adjust, Sass::Script::Number
100
+ vibrance_adjust = Compass::Magick::Utils.value_of(adjust, 1.0, 0.5) * -1
101
+ Effect.new do |pixel|
102
+ r = ChunkyPNG::Color.r(pixel)
103
+ g = ChunkyPNG::Color.g(pixel)
104
+ b = ChunkyPNG::Color.b(pixel)
105
+ max = [r, g, b].max
106
+ average = (r + g + b) / 3.0
107
+ amount = (((max - average).abs * 2.0) * vibrance_adjust) / 100.0
108
+ r = r + (max - r) * amount unless r == max
109
+ g = g + (max - g) * amount unless g == max
110
+ b = b + (max - b) * amount unless b == max
111
+ ChunkyPNG::Color.rgba(Effect.clamp(r), Effect.clamp(g), Effect.clamp(b), ChunkyPNG::Color.a(pixel))
112
+ end
113
+ end
114
+
115
+ # Adjusts the intensity of a color by changing its [R, G, B] components
116
+ # according to BT709 luminosity factors.
117
+ #
118
+ # Copyright (c) 2010, Ryan LeFevre
119
+ # http://www.camanjs.com
120
+ #
121
+ # @param [Sass::Script::Number] adjust Grayscale factor as a float,
122
+ # above 0.0 and below 1.0.
123
+ # @return {Effect} A command which applies the intensity to the canvas.
124
+ def grayscale(adjust = nil)
125
+ Compass::Magick::Utils.assert_type 'adjust', adjust, Sass::Script::Number
126
+ grayscale_adjust = [0, [Compass::Magick::Utils.value_of(adjust, 1.0, 0.5), 1.0].min].max
127
+ Effect.new do |pixel|
128
+ r = ChunkyPNG::Color.r(pixel)
129
+ g = ChunkyPNG::Color.g(pixel)
130
+ b = ChunkyPNG::Color.b(pixel)
131
+ gray = r * 0.2125 + g * 0.7154 + b * 0.0721
132
+ r = r + (gray - r) * grayscale_adjust unless r == gray
133
+ g = g + (gray - g) * grayscale_adjust unless g == gray
134
+ b = b + (gray - b) * grayscale_adjust unless b == gray
135
+ ChunkyPNG::Color.rgba(Effect.clamp(r), Effect.clamp(g), Effect.clamp(b), ChunkyPNG::Color.a(pixel))
136
+ end
137
+ end
138
+
139
+ alias :greyscale :grayscale
140
+
141
+ end
142
+ end
143
+ end