Module ChunkyPNG::Canvas::Drawing
In: lib/chunky_png/canvas/drawing.rb

Module that adds some primitive drawing methods to {ChunkyPNG::Canvas}.

All of these methods change the current canvas instance and do not create a new one, even though the method names do not end with a bang.

@note Drawing operations will not fail when something is drawn outside of the bounds

      of the canvas; these pixels will simply be ignored.

@see ChunkyPNG::Canvas

Methods

Public Instance methods

Draws a Bezier curve @param [Array, Point] A collection of control points @return [Chunky:PNG::Canvas] Itself, with the curve drawn

[Source]

    # File lib/chunky_png/canvas/drawing.rb, line 35
35:       def bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK)
36:         
37:         points = ChunkyPNG::Vector(*points)
38:         case points.length
39:           when 0, 1; return self
40:           when 2; return line(points[0].x, points[0].y, points[1].x, points[1].y, stroke_color)
41:         end
42:         
43:         curve_points = Array.new
44:         
45:         t = 0
46:         n = points.length - 1
47:         bicof = 0
48:         
49:         while t <= 100
50:           cur_p = ChunkyPNG::Point.new(0,0)
51:           
52:           # Generate a float of t.
53:           t_f = t / 100.00
54:           
55:           cur_p.x += ((1 - t_f) ** n) * points[0].x
56:           cur_p.y += ((1 - t_f) ** n) * points[0].y
57:           
58:           for i in 1...points.length - 1
59:             bicof = binomial_coefficient(n , i)
60:             
61:             cur_p.x += (bicof * (1 - t_f) ** (n - i)) *  (t_f ** i) * points[i].x 
62:             cur_p.y += (bicof * (1 - t_f) ** (n - i)) *  (t_f ** i) * points[i].y 
63:             i += 1
64:           end
65:           
66:           cur_p.x += (t_f ** n) * points[n].x
67:           cur_p.y += (t_f ** n) * points[n].y
68: 
69:           curve_points << cur_p
70: 
71:           bicof = 0
72:           t += 1
73:         end
74: 
75:         curve_points.each_cons(2) do |p1, p2|
76:           line_xiaolin_wu(p1.x.round, p1.y.round, p2.x.round, p2.y.round, stroke_color)
77:         end
78: 
79:         return self
80:       end

Draws a circle on the canvas.

@param [Integer] x0 The x-coordinate of the center of the circle. @param [Integer] y0 The y-coordinate of the center of the circle. @param [Integer] radius The radius of the circle from the center point. @param [Integer] stroke_color The color to use for the line. @param [Integer] fill_color The color to use that fills the circle. @return [ChunkyPNG::Canvas] Itself, with the circle drawn.

[Source]

     # File lib/chunky_png/canvas/drawing.rb, line 236
236:       def circle(x0, y0, radius, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT)
237: 
238:         stroke_color = ChunkyPNG::Color.parse(stroke_color)
239:         fill_color   = ChunkyPNG::Color.parse(fill_color)
240: 
241:         f = 1 - radius
242:         ddF_x = 1
243:         ddF_y = -2 * radius
244:         x = 0
245:         y = radius
246: 
247:         compose_pixel(x0, y0 + radius, stroke_color)
248:         compose_pixel(x0, y0 - radius, stroke_color)
249:         compose_pixel(x0 + radius, y0, stroke_color)
250:         compose_pixel(x0 - radius, y0, stroke_color)
251: 
252:         lines = [radius - 1] unless fill_color == ChunkyPNG::Color::TRANSPARENT
253: 
254:         while x < y
255: 
256:           if f >= 0
257:             y -= 1
258:             ddF_y += 2
259:             f += ddF_y
260:           end
261: 
262:           x += 1
263:           ddF_x += 2
264:           f += ddF_x
265: 
266:           unless fill_color == ChunkyPNG::Color::TRANSPARENT
267:             lines[y] = lines[y] ? [lines[y], x - 1].min : x - 1
268:             lines[x] = lines[x] ? [lines[x], y - 1].min : y - 1
269:           end
270: 
271:           compose_pixel(x0 + x, y0 + y, stroke_color)
272:           compose_pixel(x0 - x, y0 + y, stroke_color)
273:           compose_pixel(x0 + x, y0 - y, stroke_color)
274:           compose_pixel(x0 - x, y0 - y, stroke_color)
275: 
276:           unless x == y
277:             compose_pixel(x0 + y, y0 + x, stroke_color)
278:             compose_pixel(x0 - y, y0 + x, stroke_color)
279:             compose_pixel(x0 + y, y0 - x, stroke_color)
280:             compose_pixel(x0 - y, y0 - x, stroke_color)
281:           end
282:         end
283: 
284:         unless fill_color == ChunkyPNG::Color::TRANSPARENT
285:           lines.each_with_index do |length, y|
286:             line(x0 - length, y0 - y, x0 + length, y0 - y, fill_color) if length > 0
287:             line(x0 - length, y0 + y, x0 + length, y0 + y, fill_color) if length > 0 && y > 0
288:           end
289:         end
290: 
291:         return self
292:       end

Composes a pixel on the canvas by alpha blending a color with its background color. @param [Integer] x The x-coordinate of the pixel to blend. @param [Integer] y The y-coordinate of the pixel to blend. @param [Integer] color The foreground color to blend with @return [Integer] The composed color.

[Source]

    # File lib/chunky_png/canvas/drawing.rb, line 19
19:       def compose_pixel(x, y, color)
20:         return unless include_xy?(x, y)
21:         compose_pixel_unsafe(x, y, ChunkyPNG::Color.parse(color))
22:       end

Composes a pixel on the canvas by alpha blending a color with its background color, without bounds checking. @param (see compose_pixel) @return [Integer] The composed color.

[Source]

    # File lib/chunky_png/canvas/drawing.rb, line 28
28:       def compose_pixel_unsafe(x, y, color)
29:         set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y)))
30:       end
line(x0, y0, x1, y1, stroke_color, inclusive = true)

Alias for line_xiaolin_wu

Draws an anti-aliased line using Xiaolin Wu‘s algorithm.

@param [Integer] x0 The x-coordinate of the first control point. @param [Integer] y0 The y-coordinate of the first control point. @param [Integer] x1 The x-coordinate of the second control point. @param [Integer] y1 The y-coordinate of the second control point. @param [Integer] stroke_color The color to use for this line. @param [true, false] inclusive Whether to draw the last pixel.

   Set to false when drawing multiple lines in a path.

@return [ChunkyPNG::Canvas] Itself, with the line drawn.

[Source]

     # File lib/chunky_png/canvas/drawing.rb, line 93
 93:       def line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true)
 94:         
 95:         stroke_color = ChunkyPNG::Color.parse(stroke_color)
 96:         
 97:         dx = x1 - x0
 98:         sx = dx < 0 ? -1 : 1
 99:         dx *= sx
100:         dy = y1 - y0
101:         sy = dy < 0 ? -1 : 1
102:         dy *= sy
103:         
104:         if dy == 0 # vertical line
105:           x0.step(inclusive ? x1 : x1 - sx, sx) do |x|
106:             compose_pixel(x, y0, stroke_color)
107:           end
108:           
109:         elsif dx == 0 # horizontal line
110:           y0.step(inclusive ? y1 : y1 - sy, sy) do |y|
111:             compose_pixel(x0, y, stroke_color)
112:           end
113:           
114:         elsif dx == dy # diagonal
115:           x0.step(inclusive ? x1 : x1 - sx, sx) do |x|
116:             compose_pixel(x, y0, stroke_color)
117:             y0 += sy
118:           end
119:           
120:         elsif dy > dx  # vertical displacement
121:           compose_pixel(x0, y0, stroke_color)
122:           e_acc = 0
123:           e = ((dx << 16) / dy.to_f).round
124:           (dy - 1).downto(0) do |i|
125:             e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
126:             x0 += sx if (e_acc <= e_acc_temp)
127:             w = 0xff - (e_acc >> 8)
128:             compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w))
129:             compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) if inclusive || i > 0
130:             y0 += sy
131:           end
132:           compose_pixel(x1, y1, stroke_color) if inclusive
133:           
134:         else # horizontal displacement
135:           compose_pixel(x0, y0, stroke_color)
136:           e_acc = 0
137:           e = ((dy << 16) / dx.to_f).round
138:           (dx - 1).downto(0) do |i|
139:             e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
140:             y0 += sy if (e_acc <= e_acc_temp)
141:             w = 0xff - (e_acc >> 8)
142:             compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w))
143:             compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) if inclusive || i > 0
144:             x0 += sx
145:           end
146:           compose_pixel(x1, y1, stroke_color) if inclusive
147:         end
148:         
149:         return self
150:       end

Draws a polygon on the canvas using the stroke_color, filled using the fill_color if any.

@param [Array, String] The control point vector. Accepts everything {ChunkyPNG.Vector} accepts. @param [Integer] stroke_color The stroke color to use for this polygon. @param [Integer] fill_color The fill color to use for this polygon. @return [ChunkyPNG::Canvas] Itself, with the polygon drawn.

[Source]

     # File lib/chunky_png/canvas/drawing.rb, line 161
161:       def polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT)
162:         
163:         vector = ChunkyPNG::Vector(*path)
164:         raise ArgumentError, "A polygon requires at least 3 points" if path.length < 3
165: 
166:         stroke_color = ChunkyPNG::Color.parse(stroke_color)
167:         fill_color   = ChunkyPNG::Color.parse(fill_color)
168: 
169:         # Fill
170:         unless fill_color == ChunkyPNG::Color::TRANSPARENT
171:           vector.y_range.each do |y|
172:             intersections = []
173:             vector.edges.each do |p1, p2|
174:               if (p1.y < y && p2.y >= y) || (p2.y < y && p1.y >= y)
175:                 intersections << (p1.x + (y - p1.y).to_f / (p2.y - p1.y) * (p2.x - p1.x)).round
176:               end
177:             end
178: 
179:             intersections.sort!
180:             0.step(intersections.length - 1, 2) do |i|
181:               intersections[i].upto(intersections[i + 1]) do |x|
182:                 compose_pixel(x, y, fill_color)
183:               end
184:             end
185:           end
186:         end
187:         
188:         # Stroke
189:         vector.each_edge do |(from_x, from_y), (to_x, to_y)|
190:           line(from_x, from_y, to_x, to_y, stroke_color, false)
191:         end
192: 
193:         return self
194:       end

Draws a rectangle on the canvas, using two control points.

@param [Integer] x0 The x-coordinate of the first control point. @param [Integer] y0 The y-coordinate of the first control point. @param [Integer] x1 The x-coordinate of the second control point. @param [Integer] y1 The y-coordinate of the second control point. @param [Integer] stroke_color The line color to use for this rectangle. @param [Integer] fill_color The fill color to use for this rectangle. @return [ChunkyPNG::Canvas] Itself, with the rectangle drawn.

[Source]

     # File lib/chunky_png/canvas/drawing.rb, line 205
205:       def rect(x0, y0, x1, y1, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT)
206:       
207:         stroke_color = ChunkyPNG::Color.parse(stroke_color)
208:         fill_color   = ChunkyPNG::Color.parse(fill_color)
209:       
210:         # Fill
211:         unless fill_color == ChunkyPNG::Color::TRANSPARENT
212:           [x0, x1].min.upto([x0, x1].max) do |x|
213:             [y0, y1].min.upto([y0, y1].max) do |y|
214:               compose_pixel(x, y, fill_color)
215:             end
216:           end
217:         end
218:         
219:         # Stroke
220:         line(x0, y0, x0, y1, stroke_color, false)
221:         line(x0, y1, x1, y1, stroke_color, false)
222:         line(x1, y1, x1, y0, stroke_color, false)
223:         line(x1, y0, x0, y0, stroke_color, false)
224:         
225:         return self
226:       end

[Validate]