Module: Compass::Magick::Functions::Operations

Included in:
Compass::Magick::Functions
Defined in:
lib/magick/functions/operations.rb

Overview

Methods for operating on a Canvas, e.g., crop, compose, etc.

Instance Method Summary collapse

Instance Method Details

#magick_compose(overlay, x = nil, y = nil) ⇒ Command

Composes one Canvas on top of another.

Parameters:

  • overlay (Canvas)

    The Canvas object to compose.

  • x (Integer) (defaults to: nil)

    The left coordinate of the composition operation.

  • y (Integer) (defaults to: nil)

    The top coordinate of the composition operation.

Returns:

  • (Command)

    A command which composes the two canvas objects together.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/magick/functions/operations.rb', line 15

def magick_compose(overlay, x = nil, y = nil)
  Compass::Magick::Utils.assert_type 'overlay', overlay, ChunkyPNG::Canvas
  Compass::Magick::Utils.assert_type 'x', x, Sass::Script::Number
  Compass::Magick::Utils.assert_type 'y', y, Sass::Script::Number
  Command.new do |canvas|
    canvas_x = Compass::Magick::Utils.value_of(x, canvas.width  - overlay.width,  0).to_i
    canvas_y = Compass::Magick::Utils.value_of(y, canvas.height - overlay.height, 0).to_i
    raise ChunkyPNG::OutOfBounds, 'Canvas image width is too small to compose overlay'  if canvas.width  < overlay.width  + canvas_x
    raise ChunkyPNG::OutOfBounds, 'Canvas image height is too small to compose overlay' if canvas.height < overlay.height + canvas_y
    for y in 0...overlay.height do
      for x in 0...overlay.width do
        overlay_pixel = overlay.get_pixel(x, y)
        canvas_pixel  = canvas.get_pixel(x + canvas_x, y + canvas_y)
        canvas.set_pixel(x + canvas_x, y + canvas_y, ChunkyPNG::Color.compose_precise(overlay_pixel, canvas_pixel))
      end
    end
  end
end

#magick_crop(x1 = nil, y1 = nil, x2 = nil, y2 = nil) ⇒ Command

Crops the Canvas to the given region.

Parameters:

  • x1 (Integer) (defaults to: nil)

    The left coordinate of the crop operation.

  • y1 (Integer) (defaults to: nil)

    The top coordinate of the crop operation.

  • x2 (Integer) (defaults to: nil)

    The right coordinate of the crop operation.

  • y2 (Integer) (defaults to: nil)

    The bottom coordinate of the crop operation.

Returns:

  • (Command)

    A command which applies the crop to the canvas.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/magick/functions/operations.rb', line 41

def magick_crop(x1 = nil, y1 = nil, x2 = nil, y2 = nil)
  Compass::Magick::Utils.assert_type 'x1', x1, Sass::Script::Number
  Compass::Magick::Utils.assert_type 'y1', y1, Sass::Script::Number
  Compass::Magick::Utils.assert_type 'x2', x2, Sass::Script::Number
  Compass::Magick::Utils.assert_type 'y2', y2, Sass::Script::Number
  Command.new do |canvas|
    canvas_x1 = Compass::Magick::Utils.value_of(x1, canvas.width,  0)
    canvas_y1 = Compass::Magick::Utils.value_of(y1, canvas.height, 0)
    canvas_x2 = Compass::Magick::Utils.value_of(x2, canvas.width,  canvas.width)
    canvas_y2 = Compass::Magick::Utils.value_of(y2, canvas.height, canvas.height)
    canvas_x1, canvas_x2 = canvas_x2, canvas_x1 if canvas_x1 > canvas_x2
    canvas_y1, canvas_y2 = canvas_y2, canvas_y1 if canvas_y1 > canvas_y2
    width     = canvas_x2 - canvas_x1
    height    = canvas_y2 - canvas_y1
    canvas.crop(canvas_x1, canvas_y1, width, height)
  end
end

#magick_drop_shadow(angle = nil, distance = nil, size = nil, color = nil) ⇒ Command

Apply drop shadow on the Canvas.

The alpha channel is used to construct a mask of the original image which is then used as a base for the horizontal/vertical shadow pass.

Copyright © 2005 by Romain Guy www.curious-creature.org/2005/07/06/non-rectangular-shadow/

Parameters:

  • angle (Sass::Script::Number) (defaults to: nil)

    The angle of the shadow.

  • distance (Sass::Script::Number) (defaults to: nil)

    The distance of the shadow from the original canvas.

  • size (Sass::Script::Number) (defaults to: nil)

    The size (blur) of the shadow.

  • color (Sass::Script::Color) (defaults to: nil)

    The color of the shadow.

Returns:

  • (Command)

    A command which applies the drop shadow to the canvas.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/magick/functions/operations.rb', line 120

def magick_drop_shadow(angle = nil, distance = nil, size = nil, color = nil)
  Compass::Magick::Utils.assert_type 'angle',    angle,    Sass::Script::Number
  Compass::Magick::Utils.assert_type 'distance', distance, Sass::Script::Number
  Compass::Magick::Utils.assert_type 'size',     size,     Sass::Script::Number
  Compass::Magick::Utils.assert_type 'color',    color,    Sass::Script::Color
  Command.new do |canvas|
    shadow_angle    = Compass::Magick::Utils.value_of(angle,    360,                               0)
    shadow_distance = Compass::Magick::Utils.value_of(distance, [canvas.width, canvas.height].min, 5)
    shadow_size     = Compass::Magick::Utils.value_of(size,     [canvas.width, canvas.height].min, 5)
    shadow_color    = Compass::Magick::Utils.to_chunky_color(color.nil? ? Sass::Script::Color.new([0, 0, 0, 1]) : color)
    shadow_canvas   = ChunkyPNG::Canvas.new(canvas.width + shadow_size * 2, canvas.height + shadow_size * 2).replace(canvas, shadow_size, shadow_size)
    shadow_pixels   = shadow_canvas.pixels
    shadow_red      = ChunkyPNG::Color.r(shadow_color)
    shadow_green    = ChunkyPNG::Color.g(shadow_color)
    shadow_blue     = ChunkyPNG::Color.b(shadow_color)
    shadow_alpha    = ChunkyPNG::Color.a(shadow_color)
    angle_radians   = shadow_angle * Math::PI / 180
    distance_x      = (Math.cos(angle_radians) * shadow_distance).to_i
    distance_y      = (Math.sin(angle_radians) * shadow_distance).to_i
    left            = (shadow_size - 1) >> 1
    right           = shadow_size - left
    x_start         = left
    x_finish        = shadow_canvas.width - right
    y_start         = left
    y_finish        = shadow_canvas.height - right
    a_history       = Array.new(shadow_size)
    history_index   = 0
    sum_divider     = (shadow_alpha / 255.0) / shadow_size
    buffer_offset   = 0
    last_pixel_offset = right * shadow_canvas.width
    for y in (0...shadow_canvas.height)
      a_sum         = 0
      history_index = 0
      for x in (0...shadow_size)
        a = ChunkyPNG::Color.a(shadow_pixels[buffer_offset])
        a_history[x]  = a
        a_sum         = a_sum + a
        buffer_offset = buffer_offset + 1
      end
      buffer_offset = buffer_offset - right
      for x in (x_start...x_finish)
        a = a_sum * sum_divider
        shadow_pixels[buffer_offset] = ChunkyPNG::Color.rgba(shadow_red, shadow_green, shadow_blue, (shadow_alpha * (a / 255.0)).to_i)
        a_sum = a_sum - a_history[history_index]
        a = ChunkyPNG::Color.a(shadow_pixels[buffer_offset + right])
        a_history[history_index] = a
        a_sum = a_sum + a
        history_index = history_index + 1
        history_index = history_index - shadow_size if history_index >= shadow_size
        buffer_offset = buffer_offset + 1
      end
      buffer_offset = y * shadow_canvas.width
    end
    buffer_offset = 0
    for x in (0...shadow_canvas.width)
      a_sum         = 0
      history_index = 0
      for y in (0...shadow_size)
        a = ChunkyPNG::Color.a(shadow_pixels[buffer_offset])
        a_history[y]  = a
        a_sum         = a_sum + a
        buffer_offset = buffer_offset + shadow_canvas.width
      end
      buffer_offset = buffer_offset - last_pixel_offset
      for y in (y_start...y_finish)
        a = a_sum * sum_divider
        shadow_pixels[buffer_offset] = ChunkyPNG::Color.rgba(shadow_red, shadow_green, shadow_blue, (shadow_alpha * (a / 255.0)).to_i)
        a_sum = a_sum - a_history[history_index]
        a = ChunkyPNG::Color.a(shadow_pixels[buffer_offset + last_pixel_offset])
        a_history[history_index] = a
        a_sum = a_sum + a
        history_index = history_index + 1
        history_index = history_index - shadow_size if history_index >= shadow_size
        buffer_offset = buffer_offset + shadow_canvas.width
      end
      buffer_offset = x
    end
    for y in 0...canvas.height do
      for x in 0...canvas.width do
        shadow_x = x + shadow_size + distance_x
        shadow_y = y + shadow_size + distance_y
        canvas.set_pixel(x, y, ChunkyPNG::Color.compose_precise(
          canvas.get_pixel(x, y),
          shadow_pixels[shadow_y * shadow_canvas.width + shadow_x]
        ))
      end
    end
  end
end

#magick_effect(name, *args) ⇒ Effect

Apply an effect on the Canvas.

Parameters:

  • name (Sass::Script::String)

    The name of the effect to apply.

  • args (Array)

    The arguments to pass to the effect.

Returns:

  • (Effect)

    A command which applies the effect to the canvas.



100
101
102
103
# File 'lib/magick/functions/operations.rb', line 100

def magick_effect(name, *args)
  Compass::Magick::Utils.assert_type 'name', name, Sass::Script::String
  Compass::Magick::Functions::Operations::Effects.send(name.value, *args)
end

#magick_mask(mask, x = nil, y = nil) ⇒ Command

Applies the mask on the Canvas.

Composes the alpha channel from the mask image with the one from the canvas and return the original canvas with the alpha-channel modified. Any opaque pixels from the mask are converted to grayscale using BT709 luminosity factors, i.e. black is fully transparent and white is fully opaque.

Parameters:

  • x (Integer) (defaults to: nil)

    The left coordinate of the mask operation.

  • y (Integer) (defaults to: nil)

    The top coordinate of the mask operation.

Returns:

  • (Command)

    A command which applies the mask on the canvas.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/magick/functions/operations.rb', line 70

def magick_mask(mask, x = nil, y = nil)
  Compass::Magick::Utils.assert_type 'mask', mask, ChunkyPNG::Canvas
  Compass::Magick::Utils.assert_type 'x',    x,    Sass::Script::Number
  Compass::Magick::Utils.assert_type 'y',    y,    Sass::Script::Number
  Command.new do |canvas|
    canvas_x = Compass::Magick::Utils.value_of(x, canvas.width  - 1,  0)
    canvas_y = Compass::Magick::Utils.value_of(y, canvas.height - 1, 0)
    raise ChunkyPNG::OutOfBounds, 'Canvas image width is too small to fit mask'  if canvas.width  < mask.width  + canvas_x
    raise ChunkyPNG::OutOfBounds, 'Canvas image height is too small to fit mask' if canvas.height < mask.height + canvas_y
    for y in 0...mask.height do
      for x in 0...mask.width do
        canvas_pixel = canvas.get_pixel(x + canvas_x, y + canvas_y)
        mask_pixel   = mask.get_pixel(x, y)
        mask_alpha   = (ChunkyPNG::Color.r(mask_pixel) * 0.2125 + ChunkyPNG::Color.g(mask_pixel) * 0.7154 + ChunkyPNG::Color.b(mask_pixel) * 0.0721) * (ChunkyPNG::Color.a(mask_pixel) / 255.0)
        canvas.set_pixel(x + canvas_x, y + canvas_y, ChunkyPNG::Color.rgba(
          ChunkyPNG::Color.r(canvas_pixel),
          ChunkyPNG::Color.g(canvas_pixel),
          ChunkyPNG::Color.b(canvas_pixel),
          (ChunkyPNG::Color.a(canvas_pixel) * (mask_alpha / 255.0)).to_i
        ))
      end
    end
  end
end