compass-magick 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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