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 +22 -0
- data/README.md +67 -0
- data/lib/magick/canvas.rb +127 -0
- data/lib/magick/command.rb +32 -0
- data/lib/magick/effect.rb +34 -0
- data/lib/magick/functions/canvas.rb +41 -0
- data/lib/magick/functions/drawing.rb +156 -0
- data/lib/magick/functions/operations/effects.rb +143 -0
- data/lib/magick/functions/operations.rb +211 -0
- data/lib/magick/functions/sprites.rb +28 -0
- data/lib/magick/functions/types.rb +77 -0
- data/lib/magick/functions.rb +31 -0
- data/lib/magick/plugins.rb +14 -0
- data/lib/magick/scriptable.rb +31 -0
- data/lib/magick/shapes.rb +94 -0
- data/lib/magick/types/gradients.rb +182 -0
- data/lib/magick/types/solid.rb +40 -0
- data/lib/magick/types.rb +39 -0
- data/lib/magick/utils.rb +92 -0
- data/lib/magick.rb +79 -0
- data/lib/plugins/corners.rb +29 -0
- data/lib/plugins/pattern.rb +49 -0
- data/lib/stylesheets/_magick.sass +0 -0
- data/spec/canvas_spec.rb +19 -0
- data/spec/helpers.rb +5 -0
- data/spec/types/gradients_spec.rb +29 -0
- data/spec/types/solid_spec.rb +19 -0
- metadata +144 -0
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
|