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 +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
|