Module: ChunkyPNG::Canvas::Drawing
- Included in:
- ChunkyPNG::Canvas
- Defined in:
- lib/chunky_png/canvas/drawing.rb
Overview
Drawing operations will not fail when something is drawn outside of the bounds of the canvas; these pixels will simply be ignored.
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.
Instance Method Summary collapse
-
#bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK) ⇒ Chunky:PNG::Canvas
Draws a Bezier curve.
-
#circle(x0, y0, radius, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) ⇒ ChunkyPNG::Canvas
Draws a circle on the canvas.
-
#compose_pixel(x, y, color) ⇒ Integer
Composes a pixel on the canvas by alpha blending a color with its background color.
-
#compose_pixel_unsafe(x, y, color) ⇒ Integer
Composes a pixel on the canvas by alpha blending a color with its background color, without bounds checking.
-
#line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true) ⇒ ChunkyPNG::Canvas
(also: #line)
Draws an anti-aliased line using Xiaolin Wu’s algorithm.
-
#polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) ⇒ ChunkyPNG::Canvas
Draws a polygon on the canvas using the stroke_color, filled using the fill_color if any.
-
#rect(x0, y0, x1, y1, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) ⇒ ChunkyPNG::Canvas
Draws a rectangle on the canvas, using two control points.
Instance Method Details
#bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK) ⇒ Chunky:PNG::Canvas
Draws a Bezier curve
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/chunky_png/canvas/drawing.rb', line 39 def bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK) points = ChunkyPNG::Vector(*points) case points.length when 0, 1 then return self when 2 then return line(points[0].x, points[0].y, points[1].x, points[1].y, stroke_color) end curve_points = [] t = 0 n = points.length - 1 while t <= 100 bicof = 0 cur_p = ChunkyPNG::Point.new(0, 0) # Generate a float of t. t_f = t / 100.00 cur_p.x += ((1 - t_f)**n) * points[0].x cur_p.y += ((1 - t_f)**n) * points[0].y for i in 1...points.length - 1 bicof = binomial_coefficient(n, i) cur_p.x += (bicof * (1 - t_f)**(n - i)) * (t_f**i) * points[i].x cur_p.y += (bicof * (1 - t_f)**(n - i)) * (t_f**i) * points[i].y i += 1 end cur_p.x += (t_f**n) * points[n].x cur_p.y += (t_f**n) * points[n].y curve_points << cur_p t += 1 end curve_points.each_cons(2) do |p1, p2| line_xiaolin_wu(p1.x.round, p1.y.round, p2.x.round, p2.y.round, stroke_color) end self end |
#circle(x0, y0, radius, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) ⇒ ChunkyPNG::Canvas
Draws a circle on the canvas.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/chunky_png/canvas/drawing.rb', line 241 def circle(x0, y0, radius, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) stroke_color = ChunkyPNG::Color.parse(stroke_color) fill_color = ChunkyPNG::Color.parse(fill_color) f = 1 - radius dd_f_x = 1 dd_f_y = -2 * radius x = 0 y = radius compose_pixel(x0, y0 + radius, stroke_color) compose_pixel(x0, y0 - radius, stroke_color) compose_pixel(x0 + radius, y0, stroke_color) compose_pixel(x0 - radius, y0, stroke_color) lines = [radius - 1] unless fill_color == ChunkyPNG::Color::TRANSPARENT while x < y if f >= 0 y -= 1 dd_f_y += 2 f += dd_f_y end x += 1 dd_f_x += 2 f += dd_f_x unless fill_color == ChunkyPNG::Color::TRANSPARENT lines[y] = lines[y] ? [lines[y], x - 1].min : x - 1 lines[x] = lines[x] ? [lines[x], y - 1].min : y - 1 end compose_pixel(x0 + x, y0 + y, stroke_color) compose_pixel(x0 - x, y0 + y, stroke_color) compose_pixel(x0 + x, y0 - y, stroke_color) compose_pixel(x0 - x, y0 - y, stroke_color) unless x == y compose_pixel(x0 + y, y0 + x, stroke_color) compose_pixel(x0 - y, y0 + x, stroke_color) compose_pixel(x0 + y, y0 - x, stroke_color) compose_pixel(x0 - y, y0 - x, stroke_color) end end unless fill_color == ChunkyPNG::Color::TRANSPARENT lines.each_with_index do |length, y_offset| if length > 0 line(x0 - length, y0 - y_offset, x0 + length, y0 - y_offset, fill_color) end if length > 0 && y_offset > 0 line(x0 - length, y0 + y_offset, x0 + length, y0 + y_offset, fill_color) end end end self end |
#compose_pixel(x, y, color) ⇒ Integer
Composes a pixel on the canvas by alpha blending a color with its background color.
21 22 23 24 |
# File 'lib/chunky_png/canvas/drawing.rb', line 21 def compose_pixel(x, y, color) return unless include_xy?(x, y) compose_pixel_unsafe(x, y, ChunkyPNG::Color.parse(color)) end |
#compose_pixel_unsafe(x, y, color) ⇒ Integer
Composes a pixel on the canvas by alpha blending a color with its background color, without bounds checking.
31 32 33 |
# File 'lib/chunky_png/canvas/drawing.rb', line 31 def compose_pixel_unsafe(x, y, color) set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y))) end |
#line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true) ⇒ ChunkyPNG::Canvas Also known as: line
Draws an anti-aliased line using Xiaolin Wu’s algorithm.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 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 |
# File 'lib/chunky_png/canvas/drawing.rb', line 94 def line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true) stroke_color = ChunkyPNG::Color.parse(stroke_color) dx = x1 - x0 sx = dx < 0 ? -1 : 1 dx *= sx dy = y1 - y0 sy = dy < 0 ? -1 : 1 dy *= sy if dy == 0 # vertical line x0.step(inclusive ? x1 : x1 - sx, sx) do |x| compose_pixel(x, y0, stroke_color) end elsif dx == 0 # horizontal line y0.step(inclusive ? y1 : y1 - sy, sy) do |y| compose_pixel(x0, y, stroke_color) end elsif dx == dy # diagonal x0.step(inclusive ? x1 : x1 - sx, sx) do |x| compose_pixel(x, y0, stroke_color) y0 += sy end elsif dy > dx # vertical displacement compose_pixel(x0, y0, stroke_color) e_acc = 0 e = ((dx << 16) / dy.to_f).round (dy - 1).downto(0) do |i| e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff x0 += sx if e_acc <= e_acc_temp w = 0xff - (e_acc >> 8) compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w)) if inclusive || i > 0 compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) end y0 += sy end compose_pixel(x1, y1, stroke_color) if inclusive else # horizontal displacement compose_pixel(x0, y0, stroke_color) e_acc = 0 e = ((dy << 16) / dx.to_f).round (dx - 1).downto(0) do |i| e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff y0 += sy if e_acc <= e_acc_temp w = 0xff - (e_acc >> 8) compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w)) if inclusive || i > 0 compose_pixel(x0 + sx, y0 + sy, ChunkyPNG::Color.fade(stroke_color, 0xff - w)) end x0 += sx end compose_pixel(x1, y1, stroke_color) if inclusive end self end |
#polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) ⇒ ChunkyPNG::Canvas
Draws a polygon on the canvas using the stroke_color, filled using the fill_color if any.
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 |
# File 'lib/chunky_png/canvas/drawing.rb', line 166 def polygon(path, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) vector = ChunkyPNG::Vector(*path) if path.length < 3 raise ArgumentError, "A polygon requires at least 3 points" end stroke_color = ChunkyPNG::Color.parse(stroke_color) fill_color = ChunkyPNG::Color.parse(fill_color) # Fill unless fill_color == ChunkyPNG::Color::TRANSPARENT vector.y_range.each do |y| intersections = [] vector.edges.each do |p1, p2| if (p1.y < y && p2.y >= y) || (p2.y < y && p1.y >= y) intersections << (p1.x + (y - p1.y).to_f / (p2.y - p1.y) * (p2.x - p1.x)).round end end intersections.sort! 0.step(intersections.length - 1, 2) do |i| intersections[i].upto(intersections[i + 1]) do |x| compose_pixel(x, y, fill_color) end end end end # Stroke vector.each_edge do |(from_x, from_y), (to_x, to_y)| line(from_x, from_y, to_x, to_y, stroke_color, false) end self end |
#rect(x0, y0, x1, y1, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) ⇒ ChunkyPNG::Canvas
Draws a rectangle on the canvas, using two control points.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/chunky_png/canvas/drawing.rb', line 211 def rect(x0, y0, x1, y1, stroke_color = ChunkyPNG::Color::BLACK, fill_color = ChunkyPNG::Color::TRANSPARENT) stroke_color = ChunkyPNG::Color.parse(stroke_color) fill_color = ChunkyPNG::Color.parse(fill_color) # Fill unless fill_color == ChunkyPNG::Color::TRANSPARENT [x0, x1].min.upto([x0, x1].max) do |x| [y0, y1].min.upto([y0, y1].max) do |y| compose_pixel(x, y, fill_color) end end end # Stroke line(x0, y0, x0, y1, stroke_color, false) line(x0, y1, x1, y1, stroke_color, false) line(x1, y1, x1, y0, stroke_color, false) line(x1, y0, x0, y0, stroke_color, false) self end |