chunky_png 1.3.0 → 1.3.1
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/README.rdoc +3 -0
- data/lib/chunky_png/canvas.rb +99 -62
- data/lib/chunky_png/canvas/drawing.rb +91 -68
- data/lib/chunky_png/canvas/operations.rb +155 -120
- data/lib/chunky_png/canvas/stream_importing.rb +2 -2
- data/lib/chunky_png/chunk.rb +111 -93
- data/lib/chunky_png/color.rb +312 -253
- data/lib/chunky_png/datastream.rb +1 -1
- data/lib/chunky_png/palette.rb +66 -49
- data/lib/chunky_png/version.rb +1 -1
- data/spec/chunky_png/canvas/stream_importing_spec.rb +9 -0
- data/spec/chunky_png/color_spec.rb +92 -68
- data/spec/resources/pixelstream.bgr +67 -0
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -54,6 +54,9 @@ provides a massive speed boost to encoding and decoding.
|
|
54
54
|
png_stream = ChunkyPNG::Datastream.from_file('filename.png')
|
55
55
|
png_stream.each_chunk { |chunk| p chunk.type }
|
56
56
|
|
57
|
+
Also check out the following screencast by John Davison, which illustrates
|
58
|
+
basic usage of the library: http://devcasts.co/ruby-chunky_png-a-great-gem-for-on-the-fly-png-manipulation.
|
59
|
+
|
57
60
|
For more information, see the project wiki on http://github.com/wvanbergen/chunky_png/wiki
|
58
61
|
or the RDOC documentation on http://rdoc.info/gems/chunky_png/frames
|
59
62
|
|
data/lib/chunky_png/canvas.rb
CHANGED
@@ -11,7 +11,6 @@ require 'chunky_png/canvas/resampling'
|
|
11
11
|
require 'chunky_png/canvas/masking'
|
12
12
|
|
13
13
|
module ChunkyPNG
|
14
|
-
|
15
14
|
# The ChunkyPNG::Canvas class represents a raster image as a matrix of
|
16
15
|
# pixels.
|
17
16
|
#
|
@@ -32,7 +31,6 @@ module ChunkyPNG
|
|
32
31
|
# the whole canvas, like cropping and alpha compositing. Simple drawing
|
33
32
|
# functions are imported from the {ChunkyPNG::Canvas::Drawing} module.
|
34
33
|
class Canvas
|
35
|
-
|
36
34
|
include PNGEncoding
|
37
35
|
extend PNGDecoding
|
38
36
|
extend Adam7Interlacing
|
@@ -55,7 +53,7 @@ module ChunkyPNG
|
|
55
53
|
attr_reader :height
|
56
54
|
|
57
55
|
# @return [Array<ChunkyPNG::Color>] The list of pixels in this canvas.
|
58
|
-
#
|
56
|
+
# This array always should have +width * height+ elements.
|
59
57
|
attr_reader :pixels
|
60
58
|
|
61
59
|
|
@@ -68,26 +66,28 @@ module ChunkyPNG
|
|
68
66
|
# @overload initialize(width, height, background_color)
|
69
67
|
# @param [Integer] width The width in pixels of this canvas
|
70
68
|
# @param [Integer] height The height in pixels of this canvas
|
71
|
-
# @param [Integer, ...] background_color The initial background color of
|
72
|
-
#
|
69
|
+
# @param [Integer, ...] background_color The initial background color of
|
70
|
+
# this canvas. This can be a color value or any value that
|
71
|
+
# {ChunkyPNG::Color.parse} can handle.
|
73
72
|
#
|
74
73
|
# @overload initialize(width, height, initial)
|
75
74
|
# @param [Integer] width The width in pixels of this canvas
|
76
75
|
# @param [Integer] height The height in pixels of this canvas
|
77
|
-
# @param [Array<Integer>] initial The initial pizel values. Must be an
|
78
|
-
# <tt>width * height</tt> elements.
|
76
|
+
# @param [Array<Integer>] initial The initial pizel values. Must be an
|
77
|
+
# array with <tt>width * height</tt> elements.
|
79
78
|
def initialize(width, height, initial = ChunkyPNG::Color::TRANSPARENT)
|
80
|
-
|
81
79
|
@width, @height = width, height
|
82
80
|
|
83
81
|
if initial.kind_of?(Array)
|
84
|
-
|
82
|
+
unless initial.length == width * height
|
83
|
+
raise ArgumentError, "The initial array should have #{width}x#{height} = #{width*height} elements!"
|
84
|
+
end
|
85
85
|
@pixels = initial
|
86
86
|
else
|
87
87
|
@pixels = Array.new(width * height, ChunkyPNG::Color.parse(initial))
|
88
88
|
end
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
# Initializes a new Canvas instances when being cloned.
|
92
92
|
# @param [ChunkyPNG::Canvas] other The canvas to duplicate
|
93
93
|
# @return [void]
|
@@ -101,7 +101,7 @@ module ChunkyPNG
|
|
101
101
|
# @param [ChunkyPNG::Canvas] canvas The canvas to duplicate
|
102
102
|
# @return [ChunkyPNG::Canvas] The newly constructed canvas instance.
|
103
103
|
def self.from_canvas(canvas)
|
104
|
-
|
104
|
+
new(canvas.width, canvas.height, canvas.pixels.dup)
|
105
105
|
end
|
106
106
|
|
107
107
|
|
@@ -110,11 +110,12 @@ module ChunkyPNG
|
|
110
110
|
#################################################################
|
111
111
|
|
112
112
|
# Returns the dimension (width x height) for this canvas.
|
113
|
-
# @return [ChunkyPNG::Dimension] A dimension instance with the width and
|
113
|
+
# @return [ChunkyPNG::Dimension] A dimension instance with the width and
|
114
|
+
# height set for this canvas.
|
114
115
|
def dimension
|
115
116
|
ChunkyPNG::Dimension.new(width, height)
|
116
117
|
end
|
117
|
-
|
118
|
+
|
118
119
|
# Returns the area of this canvas in number of pixels.
|
119
120
|
# @return [Integer] The number of pixels in this canvas
|
120
121
|
def area
|
@@ -125,8 +126,10 @@ module ChunkyPNG
|
|
125
126
|
# @param [Integer] x The x-coordinate of the pixel (column)
|
126
127
|
# @param [Integer] y The y-coordinate of the pixel (row)
|
127
128
|
# @param [Integer] color The new color for the provided coordinates.
|
128
|
-
# @return [Integer] The new color value for this pixel, i.e.
|
129
|
-
#
|
129
|
+
# @return [Integer] The new color value for this pixel, i.e.
|
130
|
+
# <tt>color</tt>.
|
131
|
+
# @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the
|
132
|
+
# image's dimensions.
|
130
133
|
# @see #set_pixel
|
131
134
|
def []=(x, y, color)
|
132
135
|
assert_xy!(x, y)
|
@@ -135,25 +138,26 @@ module ChunkyPNG
|
|
135
138
|
|
136
139
|
# Replaces a single pixel in this canvas, without bounds checking.
|
137
140
|
#
|
138
|
-
# This method return value and effects are undefined for coordinates
|
141
|
+
# This method return value and effects are undefined for coordinates
|
139
142
|
# out of bounds of the canvas.
|
140
143
|
#
|
141
144
|
# @param [Integer] x The x-coordinate of the pixel (column)
|
142
145
|
# @param [Integer] y The y-coordinate of the pixel (row)
|
143
146
|
# @param [Integer] pixel The new color for the provided coordinates.
|
144
|
-
# @return [Integer] The new color value for this pixel, i.e.
|
147
|
+
# @return [Integer] The new color value for this pixel, i.e.
|
148
|
+
# <tt>color</tt>.
|
145
149
|
def set_pixel(x, y, color)
|
146
150
|
@pixels[y * width + x] = color
|
147
151
|
end
|
148
|
-
|
152
|
+
|
149
153
|
# Replaces a single pixel in this canvas, with bounds checking. It will do
|
150
154
|
# noting if the provided coordinates are out of bounds.
|
151
155
|
#
|
152
156
|
# @param [Integer] x The x-coordinate of the pixel (column)
|
153
157
|
# @param [Integer] y The y-coordinate of the pixel (row)
|
154
158
|
# @param [Integer] pixel The new color value for the provided coordinates.
|
155
|
-
# @return [Integer] The new color value for this pixel, i.e.
|
156
|
-
#
|
159
|
+
# @return [Integer] The new color value for this pixel, i.e.
|
160
|
+
# <tt>color</tt>, or <tt>nil</tt> if the coordinates are out of bounds.
|
157
161
|
def set_pixel_if_within_bounds(x, y, color)
|
158
162
|
return unless include_xy?(x, y)
|
159
163
|
@pixels[y * width + x] = color
|
@@ -163,15 +167,18 @@ module ChunkyPNG
|
|
163
167
|
# @param [Integer] x The x-coordinate of the pixel (column)
|
164
168
|
# @param [Integer] y The y-coordinate of the pixel (row)
|
165
169
|
# @return [Integer] The current color value at the provided coordinates.
|
166
|
-
# @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the
|
170
|
+
# @raise [ChunkyPNG::OutOfBounds] when the coordinates are outside of the
|
171
|
+
# image's dimensions.
|
167
172
|
# @see #get_pixel
|
168
173
|
def [](x, y)
|
169
174
|
assert_xy!(x, y)
|
170
175
|
@pixels[y * width + x]
|
171
176
|
end
|
172
177
|
|
173
|
-
# Returns a single pixel from this canvas, without checking bounds. The
|
174
|
-
# this method is undefined if the coordinates are out of
|
178
|
+
# Returns a single pixel from this canvas, without checking bounds. The
|
179
|
+
# return value for this method is undefined if the coordinates are out of
|
180
|
+
# bounds.
|
181
|
+
#
|
175
182
|
# @param (see #[])
|
176
183
|
# @return [Integer] The current pixel at the provided coordinates.
|
177
184
|
def get_pixel(x, y)
|
@@ -196,7 +203,8 @@ module ChunkyPNG
|
|
196
203
|
|
197
204
|
# Replaces a row of pixels on this canvas.
|
198
205
|
# @param [Integer] y The 0-based row index.
|
199
|
-
# @param [Array<Integer>] vector The vector of pixels to replace the row
|
206
|
+
# @param [Array<Integer>] vector The vector of pixels to replace the row
|
207
|
+
# with.
|
200
208
|
# @return [void]
|
201
209
|
def replace_row!(y, vector)
|
202
210
|
assert_y!(y) && assert_width!(vector.length)
|
@@ -205,7 +213,8 @@ module ChunkyPNG
|
|
205
213
|
|
206
214
|
# Replaces a column of pixels on this canvas.
|
207
215
|
# @param [Integer] x The 0-based column index.
|
208
|
-
# @param [Array<Integer>] vector The vector of pixels to replace the column
|
216
|
+
# @param [Array<Integer>] vector The vector of pixels to replace the column
|
217
|
+
# with.
|
209
218
|
# @return [void]
|
210
219
|
def replace_column!(x, vector)
|
211
220
|
assert_x!(x) && assert_height!(vector.length)
|
@@ -215,49 +224,55 @@ module ChunkyPNG
|
|
215
224
|
end
|
216
225
|
|
217
226
|
# Checks whether the given coordinates are in the range of the canvas
|
218
|
-
# @param [ChunkyPNG::Point, Array, Hash, String] point_like The point to
|
219
|
-
#
|
220
|
-
#
|
227
|
+
# @param [ChunkyPNG::Point, Array, Hash, String] point_like The point to
|
228
|
+
# check.
|
229
|
+
# @return [true, false] True if the x and y coordinates of the point are
|
230
|
+
# within the limits of this canvas.
|
221
231
|
# @see ChunkyPNG.Point
|
222
232
|
def include_point?(*point_like)
|
223
233
|
dimension.include?(ChunkyPNG::Point(*point_like))
|
224
234
|
end
|
225
|
-
|
235
|
+
|
226
236
|
alias_method :include?, :include_point?
|
227
|
-
|
228
|
-
# Checks whether the given x- and y-coordinate are in the range of the
|
237
|
+
|
238
|
+
# Checks whether the given x- and y-coordinate are in the range of the
|
239
|
+
# canvas
|
240
|
+
#
|
229
241
|
# @param [Integer] x The x-coordinate of the pixel (column)
|
230
242
|
# @param [Integer] y The y-coordinate of the pixel (row)
|
231
|
-
# @return [true, false] True if the x- and y-coordinate is in the range of
|
243
|
+
# @return [true, false] True if the x- and y-coordinate is in the range of
|
244
|
+
# this canvas.
|
232
245
|
def include_xy?(x, y)
|
233
246
|
y >= 0 && y < height && x >= 0 && x < width
|
234
247
|
end
|
235
|
-
|
248
|
+
|
236
249
|
# Checks whether the given y-coordinate is in the range of the canvas
|
237
250
|
# @param [Integer] y The y-coordinate of the pixel (row)
|
238
|
-
# @return [true, false] True if the y-coordinate is in the range of this
|
251
|
+
# @return [true, false] True if the y-coordinate is in the range of this
|
252
|
+
# canvas.
|
239
253
|
def include_y?(y)
|
240
254
|
y >= 0 && y < height
|
241
255
|
end
|
242
256
|
|
243
257
|
# Checks whether the given x-coordinate is in the range of the canvas
|
244
258
|
# @param [Integer] x The y-coordinate of the pixel (column)
|
245
|
-
# @return [true, false] True if the x-coordinate is in the range of this
|
259
|
+
# @return [true, false] True if the x-coordinate is in the range of this
|
260
|
+
# canvas.
|
246
261
|
def include_x?(x)
|
247
262
|
x >= 0 && x < width
|
248
263
|
end
|
249
264
|
|
250
265
|
# Returns the palette used for this canvas.
|
251
|
-
# @return [ChunkyPNG::Palette] A palette which contains all the colors that
|
252
|
-
#
|
266
|
+
# @return [ChunkyPNG::Palette] A palette which contains all the colors that
|
267
|
+
# are being used for this image.
|
253
268
|
def palette
|
254
269
|
ChunkyPNG::Palette.from_canvas(self)
|
255
270
|
end
|
256
271
|
|
257
272
|
# Equality check to compare this canvas with other matrices.
|
258
273
|
# @param other The object to compare this Matrix to.
|
259
|
-
# @return [true, false] True if the size and pixel values of the other
|
260
|
-
#
|
274
|
+
# @return [true, false] True if the size and pixel values of the other
|
275
|
+
# canvas are exactly the same as this canvas's size and pixel values.
|
261
276
|
def eql?(other)
|
262
277
|
other.kind_of?(self.class) && other.pixels == self.pixels &&
|
263
278
|
other.width == self.width && other.height == self.height
|
@@ -274,7 +289,7 @@ module ChunkyPNG
|
|
274
289
|
def to_image
|
275
290
|
ChunkyPNG::Image.from_canvas(self)
|
276
291
|
end
|
277
|
-
|
292
|
+
|
278
293
|
# Alternative implementation of the inspect method.
|
279
294
|
# @return [String] A nicely formatted string representation of this canvas.
|
280
295
|
# @private
|
@@ -285,51 +300,73 @@ module ChunkyPNG
|
|
285
300
|
end
|
286
301
|
inspected << "\n]>"
|
287
302
|
end
|
288
|
-
|
303
|
+
|
289
304
|
protected
|
290
|
-
|
305
|
+
|
291
306
|
# Replaces the image, given a new width, new height, and a new pixel array.
|
292
307
|
def replace_canvas!(new_width, new_height, new_pixels)
|
293
|
-
|
308
|
+
unless new_pixels.length == new_width * new_height
|
309
|
+
raise ArgumentError, "The provided pixel array should have #{new_width * new_height} items"
|
310
|
+
end
|
294
311
|
@width, @height, @pixels = new_width, new_height, new_pixels
|
295
|
-
|
312
|
+
self
|
296
313
|
end
|
297
|
-
|
314
|
+
|
298
315
|
# Throws an exception if the x-coordinate is out of bounds.
|
299
316
|
def assert_x!(x)
|
300
|
-
|
301
|
-
|
317
|
+
unless include_x?(x)
|
318
|
+
raise ChunkyPNG::OutOfBounds, "Column index #{x} out of bounds!"
|
319
|
+
end
|
320
|
+
true
|
302
321
|
end
|
303
|
-
|
322
|
+
|
304
323
|
# Throws an exception if the y-coordinate is out of bounds.
|
305
324
|
def assert_y!(y)
|
306
|
-
|
307
|
-
|
325
|
+
unless include_y?(y)
|
326
|
+
raise ChunkyPNG::OutOfBounds, "Row index #{y} out of bounds!"
|
327
|
+
end
|
328
|
+
true
|
308
329
|
end
|
309
|
-
|
330
|
+
|
310
331
|
# Throws an exception if the x- or y-coordinate is out of bounds.
|
311
332
|
def assert_xy!(x, y)
|
312
|
-
|
313
|
-
|
333
|
+
unless include_xy?(x, y)
|
334
|
+
raise ChunkyPNG::OutOfBounds, "Coordinates (#{x},#{y}) out of bounds!"
|
335
|
+
end
|
336
|
+
true
|
314
337
|
end
|
315
338
|
|
316
|
-
# Throws an exception if the vector_length does not match this canvas'
|
339
|
+
# Throws an exception if the vector_length does not match this canvas'
|
340
|
+
# height.
|
317
341
|
def assert_height!(vector_length)
|
318
|
-
|
319
|
-
|
342
|
+
if height != vector_length
|
343
|
+
raise ChunkyPNG::ExpectationFailed,
|
344
|
+
"The length of the vector (#{vector_length}) does not match the canvas height (#{height})!"
|
345
|
+
end
|
346
|
+
true
|
320
347
|
end
|
321
348
|
|
322
|
-
# Throws an exception if the vector_length does not match this canvas'
|
349
|
+
# Throws an exception if the vector_length does not match this canvas'
|
350
|
+
# width.
|
323
351
|
def assert_width!(vector_length)
|
324
|
-
|
325
|
-
|
352
|
+
if width != vector_length
|
353
|
+
raise ChunkyPNG::ExpectationFailed,
|
354
|
+
"The length of the vector (#{vector_length}) does not match the canvas width (#{width})!"
|
355
|
+
end
|
356
|
+
true
|
326
357
|
end
|
327
358
|
|
328
359
|
# Throws an exception if the matrix width and height does not match this canvas' dimensions.
|
329
360
|
def assert_size!(matrix_width, matrix_height)
|
330
|
-
|
331
|
-
|
332
|
-
|
361
|
+
if width != matrix_width
|
362
|
+
raise ChunkyPNG::ExpectationFailed,
|
363
|
+
'The width of the matrix does not match the canvas width!'
|
364
|
+
end
|
365
|
+
if height != matrix_height
|
366
|
+
raise ChunkyPNG::ExpectationFailed,
|
367
|
+
'The height of the matrix does not match the canvas height!'
|
368
|
+
end
|
369
|
+
true
|
333
370
|
end
|
334
371
|
end
|
335
372
|
end
|
@@ -1,17 +1,19 @@
|
|
1
1
|
module ChunkyPNG
|
2
2
|
class Canvas
|
3
|
-
|
3
|
+
|
4
4
|
# Module that adds some primitive drawing methods to {ChunkyPNG::Canvas}.
|
5
5
|
#
|
6
|
-
# All of these methods change the current canvas instance and do not create
|
7
|
-
# even though the method names do not end with a bang.
|
6
|
+
# All of these methods change the current canvas instance and do not create
|
7
|
+
# a new one, even though the method names do not end with a bang.
|
8
8
|
#
|
9
|
-
# @note Drawing operations will not fail when something is drawn outside of
|
10
|
-
#
|
9
|
+
# @note Drawing operations will not fail when something is drawn outside of
|
10
|
+
# the bounds of the canvas; these pixels will simply be ignored.
|
11
11
|
# @see ChunkyPNG::Canvas
|
12
12
|
module Drawing
|
13
|
-
|
14
|
-
# Composes a pixel on the canvas by alpha blending a color with its
|
13
|
+
|
14
|
+
# Composes a pixel on the canvas by alpha blending a color with its
|
15
|
+
# background color.
|
16
|
+
#
|
15
17
|
# @param [Integer] x The x-coordinate of the pixel to blend.
|
16
18
|
# @param [Integer] y The y-coordinate of the pixel to blend.
|
17
19
|
# @param [Integer] color The foreground color to blend with
|
@@ -20,49 +22,49 @@ module ChunkyPNG
|
|
20
22
|
return unless include_xy?(x, y)
|
21
23
|
compose_pixel_unsafe(x, y, ChunkyPNG::Color.parse(color))
|
22
24
|
end
|
23
|
-
|
24
|
-
# Composes a pixel on the canvas by alpha blending a color with its
|
25
|
-
# without bounds checking.
|
25
|
+
|
26
|
+
# Composes a pixel on the canvas by alpha blending a color with its
|
27
|
+
# background color, without bounds checking.
|
28
|
+
#
|
26
29
|
# @param (see #compose_pixel)
|
27
30
|
# @return [Integer] The composed color.
|
28
31
|
def compose_pixel_unsafe(x, y, color)
|
29
32
|
set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y)))
|
30
33
|
end
|
31
|
-
|
34
|
+
|
32
35
|
# Draws a Bezier curve
|
33
36
|
# @param [Array, Point] A collection of control points
|
34
37
|
# @return [Chunky:PNG::Canvas] Itself, with the curve drawn
|
35
38
|
def bezier_curve(points, stroke_color = ChunkyPNG::Color::BLACK)
|
36
|
-
|
37
39
|
points = ChunkyPNG::Vector(*points)
|
38
40
|
case points.length
|
39
41
|
when 0, 1; return self
|
40
42
|
when 2; return line(points[0].x, points[0].y, points[1].x, points[1].y, stroke_color)
|
41
43
|
end
|
42
|
-
|
44
|
+
|
43
45
|
curve_points = Array.new
|
44
|
-
|
45
|
-
t
|
46
|
-
n
|
46
|
+
|
47
|
+
t = 0
|
48
|
+
n = points.length - 1
|
47
49
|
bicof = 0
|
48
|
-
|
50
|
+
|
49
51
|
while t <= 100
|
50
52
|
cur_p = ChunkyPNG::Point.new(0,0)
|
51
|
-
|
53
|
+
|
52
54
|
# Generate a float of t.
|
53
55
|
t_f = t / 100.00
|
54
|
-
|
56
|
+
|
55
57
|
cur_p.x += ((1 - t_f) ** n) * points[0].x
|
56
58
|
cur_p.y += ((1 - t_f) ** n) * points[0].y
|
57
|
-
|
59
|
+
|
58
60
|
for i in 1...points.length - 1
|
59
61
|
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
|
62
|
+
|
63
|
+
cur_p.x += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].x
|
64
|
+
cur_p.y += (bicof * (1 - t_f) ** (n - i)) * (t_f ** i) * points[i].y
|
63
65
|
i += 1
|
64
66
|
end
|
65
|
-
|
67
|
+
|
66
68
|
cur_p.x += (t_f ** n) * points[n].x
|
67
69
|
cur_p.y += (t_f ** n) * points[n].y
|
68
70
|
|
@@ -76,10 +78,9 @@ module ChunkyPNG
|
|
76
78
|
line_xiaolin_wu(p1.x.round, p1.y.round, p2.x.round, p2.y.round, stroke_color)
|
77
79
|
end
|
78
80
|
|
79
|
-
|
81
|
+
self
|
80
82
|
end
|
81
|
-
|
82
|
-
|
83
|
+
|
83
84
|
# Draws an anti-aliased line using Xiaolin Wu's algorithm.
|
84
85
|
#
|
85
86
|
# @param [Integer] x0 The x-coordinate of the first control point.
|
@@ -87,81 +88,93 @@ module ChunkyPNG
|
|
87
88
|
# @param [Integer] x1 The x-coordinate of the second control point.
|
88
89
|
# @param [Integer] y1 The y-coordinate of the second control point.
|
89
90
|
# @param [Integer] stroke_color The color to use for this line.
|
90
|
-
# @param [true, false] inclusive Whether to draw the last pixel.
|
91
|
-
#
|
91
|
+
# @param [true, false] inclusive Whether to draw the last pixel. Set to
|
92
|
+
# false when drawing multiple lines in a path.
|
92
93
|
# @return [ChunkyPNG::Canvas] Itself, with the line drawn.
|
93
94
|
def line_xiaolin_wu(x0, y0, x1, y1, stroke_color, inclusive = true)
|
94
|
-
|
95
95
|
stroke_color = ChunkyPNG::Color.parse(stroke_color)
|
96
|
-
|
96
|
+
|
97
97
|
dx = x1 - x0
|
98
98
|
sx = dx < 0 ? -1 : 1
|
99
99
|
dx *= sx
|
100
100
|
dy = y1 - y0
|
101
101
|
sy = dy < 0 ? -1 : 1
|
102
102
|
dy *= sy
|
103
|
-
|
103
|
+
|
104
104
|
if dy == 0 # vertical line
|
105
105
|
x0.step(inclusive ? x1 : x1 - sx, sx) do |x|
|
106
106
|
compose_pixel(x, y0, stroke_color)
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
elsif dx == 0 # horizontal line
|
110
110
|
y0.step(inclusive ? y1 : y1 - sy, sy) do |y|
|
111
111
|
compose_pixel(x0, y, stroke_color)
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
elsif dx == dy # diagonal
|
115
115
|
x0.step(inclusive ? x1 : x1 - sx, sx) do |x|
|
116
116
|
compose_pixel(x, y0, stroke_color)
|
117
117
|
y0 += sy
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
elsif dy > dx # vertical displacement
|
121
121
|
compose_pixel(x0, y0, stroke_color)
|
122
122
|
e_acc = 0
|
123
123
|
e = ((dx << 16) / dy.to_f).round
|
124
124
|
(dy - 1).downto(0) do |i|
|
125
125
|
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
|
126
|
-
x0 += sx if
|
126
|
+
x0 += sx if e_acc <= e_acc_temp
|
127
127
|
w = 0xff - (e_acc >> 8)
|
128
128
|
compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w))
|
129
|
-
|
129
|
+
if inclusive || i > 0
|
130
|
+
compose_pixel(x0 + sx,
|
131
|
+
y0 + sy,
|
132
|
+
ChunkyPNG::Color.fade(stroke_color, 0xff - w))
|
133
|
+
end
|
130
134
|
y0 += sy
|
131
135
|
end
|
132
136
|
compose_pixel(x1, y1, stroke_color) if inclusive
|
133
|
-
|
137
|
+
|
134
138
|
else # horizontal displacement
|
135
139
|
compose_pixel(x0, y0, stroke_color)
|
136
140
|
e_acc = 0
|
137
141
|
e = ((dy << 16) / dx.to_f).round
|
138
142
|
(dx - 1).downto(0) do |i|
|
139
143
|
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
|
140
|
-
y0 += sy if
|
144
|
+
y0 += sy if e_acc <= e_acc_temp
|
141
145
|
w = 0xff - (e_acc >> 8)
|
142
146
|
compose_pixel(x0, y0, ChunkyPNG::Color.fade(stroke_color, w))
|
143
|
-
|
147
|
+
if inclusive || i > 0
|
148
|
+
compose_pixel(x0 + sx,
|
149
|
+
y0 + sy,
|
150
|
+
ChunkyPNG::Color.fade(stroke_color, 0xff - w))
|
151
|
+
end
|
144
152
|
x0 += sx
|
145
153
|
end
|
146
154
|
compose_pixel(x1, y1, stroke_color) if inclusive
|
147
155
|
end
|
148
|
-
|
149
|
-
|
156
|
+
|
157
|
+
self
|
150
158
|
end
|
151
|
-
|
159
|
+
|
152
160
|
alias_method :line, :line_xiaolin_wu
|
153
|
-
|
154
|
-
|
155
|
-
#
|
161
|
+
|
162
|
+
# Draws a polygon on the canvas using the stroke_color, filled using the
|
163
|
+
# fill_color if any.
|
156
164
|
#
|
157
|
-
# @param [Array, String] The control point vector. Accepts everything
|
165
|
+
# @param [Array, String] The control point vector. Accepts everything
|
166
|
+
# {ChunkyPNG.Vector} accepts.
|
158
167
|
# @param [Integer] stroke_color The stroke color to use for this polygon.
|
159
168
|
# @param [Integer] fill_color The fill color to use for this polygon.
|
160
169
|
# @return [ChunkyPNG::Canvas] Itself, with the polygon drawn.
|
161
|
-
def polygon(path,
|
162
|
-
|
170
|
+
def polygon(path,
|
171
|
+
stroke_color = ChunkyPNG::Color::BLACK,
|
172
|
+
fill_color = ChunkyPNG::Color::TRANSPARENT)
|
173
|
+
|
163
174
|
vector = ChunkyPNG::Vector(*path)
|
164
|
-
|
175
|
+
if path.length < 3
|
176
|
+
raise ArgumentError, 'A polygon requires at least 3 points'
|
177
|
+
end
|
165
178
|
|
166
179
|
stroke_color = ChunkyPNG::Color.parse(stroke_color)
|
167
180
|
fill_color = ChunkyPNG::Color.parse(fill_color)
|
@@ -184,15 +197,15 @@ module ChunkyPNG
|
|
184
197
|
end
|
185
198
|
end
|
186
199
|
end
|
187
|
-
|
200
|
+
|
188
201
|
# Stroke
|
189
202
|
vector.each_edge do |(from_x, from_y), (to_x, to_y)|
|
190
203
|
line(from_x, from_y, to_x, to_y, stroke_color, false)
|
191
204
|
end
|
192
205
|
|
193
|
-
|
206
|
+
self
|
194
207
|
end
|
195
|
-
|
208
|
+
|
196
209
|
# Draws a rectangle on the canvas, using two control points.
|
197
210
|
#
|
198
211
|
# @param [Integer] x0 The x-coordinate of the first control point.
|
@@ -202,11 +215,13 @@ module ChunkyPNG
|
|
202
215
|
# @param [Integer] stroke_color The line color to use for this rectangle.
|
203
216
|
# @param [Integer] fill_color The fill color to use for this rectangle.
|
204
217
|
# @return [ChunkyPNG::Canvas] Itself, with the rectangle drawn.
|
205
|
-
def rect(x0, y0, x1, y1,
|
206
|
-
|
218
|
+
def rect(x0, y0, x1, y1,
|
219
|
+
stroke_color = ChunkyPNG::Color::BLACK,
|
220
|
+
fill_color = ChunkyPNG::Color::TRANSPARENT)
|
221
|
+
|
207
222
|
stroke_color = ChunkyPNG::Color.parse(stroke_color)
|
208
223
|
fill_color = ChunkyPNG::Color.parse(fill_color)
|
209
|
-
|
224
|
+
|
210
225
|
# Fill
|
211
226
|
unless fill_color == ChunkyPNG::Color::TRANSPARENT
|
212
227
|
[x0, x1].min.upto([x0, x1].max) do |x|
|
@@ -215,16 +230,16 @@ module ChunkyPNG
|
|
215
230
|
end
|
216
231
|
end
|
217
232
|
end
|
218
|
-
|
233
|
+
|
219
234
|
# Stroke
|
220
235
|
line(x0, y0, x0, y1, stroke_color, false)
|
221
236
|
line(x0, y1, x1, y1, stroke_color, false)
|
222
237
|
line(x1, y1, x1, y0, stroke_color, false)
|
223
238
|
line(x1, y0, x0, y0, stroke_color, false)
|
224
|
-
|
225
|
-
|
239
|
+
|
240
|
+
self
|
226
241
|
end
|
227
|
-
|
242
|
+
|
228
243
|
# Draws a circle on the canvas.
|
229
244
|
#
|
230
245
|
# @param [Integer] x0 The x-coordinate of the center of the circle.
|
@@ -233,7 +248,9 @@ module ChunkyPNG
|
|
233
248
|
# @param [Integer] stroke_color The color to use for the line.
|
234
249
|
# @param [Integer] fill_color The color to use that fills the circle.
|
235
250
|
# @return [ChunkyPNG::Canvas] Itself, with the circle drawn.
|
236
|
-
def circle(x0, y0, radius,
|
251
|
+
def circle(x0, y0, radius,
|
252
|
+
stroke_color = ChunkyPNG::Color::BLACK,
|
253
|
+
fill_color = ChunkyPNG::Color::TRANSPARENT)
|
237
254
|
|
238
255
|
stroke_color = ChunkyPNG::Color.parse(stroke_color)
|
239
256
|
fill_color = ChunkyPNG::Color.parse(fill_color)
|
@@ -283,20 +300,26 @@ module ChunkyPNG
|
|
283
300
|
|
284
301
|
unless fill_color == ChunkyPNG::Color::TRANSPARENT
|
285
302
|
lines.each_with_index do |length, y|
|
286
|
-
|
287
|
-
|
303
|
+
if length > 0
|
304
|
+
line(x0 - length, y0 - y, x0 + length, y0 - y, fill_color)
|
305
|
+
end
|
306
|
+
if length > 0 && y > 0
|
307
|
+
line(x0 - length, y0 + y, x0 + length, y0 + y, fill_color)
|
308
|
+
end
|
288
309
|
end
|
289
310
|
end
|
290
311
|
|
291
|
-
|
312
|
+
self
|
292
313
|
end
|
293
|
-
|
314
|
+
|
294
315
|
private
|
295
|
-
|
316
|
+
|
296
317
|
# Calculates the binomial coefficient for n over k.
|
297
318
|
#
|
298
|
-
# @param [Integer] n first parameter in coeffient (the number on top when
|
299
|
-
#
|
319
|
+
# @param [Integer] n first parameter in coeffient (the number on top when
|
320
|
+
# looking at the mathematic formula)
|
321
|
+
# @param [Integer] k k-element, second parameter in coeffient (the number
|
322
|
+
# on the bottom when looking at the mathematic formula)
|
300
323
|
# @return [Integer] The binomial coeffcient of (n,k)
|
301
324
|
def binomial_coefficient(n, k)
|
302
325
|
return 1 if n == k || k == 0
|