rgd2-ffij 0.0.3

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.
Files changed (56) hide show
  1. data/.gitignore +5 -0
  2. data/COPYING +340 -0
  3. data/COPYRIGHT +17 -0
  4. data/README +34 -0
  5. data/Rakefile +32 -0
  6. data/gd2-ffij.gemspec +96 -0
  7. data/lib/gd2-ffij.rb +215 -0
  8. data/lib/gd2/canvas.rb +422 -0
  9. data/lib/gd2/color.rb +240 -0
  10. data/lib/gd2/ffi_struct.rb +76 -0
  11. data/lib/gd2/font.rb +343 -0
  12. data/lib/gd2/image.rb +795 -0
  13. data/lib/gd2/palette.rb +253 -0
  14. data/test/canvas_test.rb +186 -0
  15. data/test/image_test.rb +149 -0
  16. data/test/images/test.bmp +0 -0
  17. data/test/images/test.gd +0 -0
  18. data/test/images/test.gd2 +0 -0
  19. data/test/images/test.gif +0 -0
  20. data/test/images/test.jpg +0 -0
  21. data/test/images/test.png +0 -0
  22. data/test/images/test.wbmp +0 -0
  23. data/test/images/test.xbm +686 -0
  24. data/test/images/test.xcf +0 -0
  25. data/test/images/test.xpm +261 -0
  26. data/test/images/test_arc.gd2 +0 -0
  27. data/test/images/test_canvas_filled_polygon.gd2 +0 -0
  28. data/test/images/test_canvas_filled_rectangle.gd2 +0 -0
  29. data/test/images/test_canvas_line.gd2 +0 -0
  30. data/test/images/test_canvas_move_to_and_line_to.gd2 +0 -0
  31. data/test/images/test_canvas_polygon.gd2 +0 -0
  32. data/test/images/test_canvas_rectangle.gd2 +0 -0
  33. data/test/images/test_circle.gd2 +0 -0
  34. data/test/images/test_color.gd2 +0 -0
  35. data/test/images/test_color.png +0 -0
  36. data/test/images/test_color.xcf +0 -0
  37. data/test/images/test_color_indexed.gd2 +0 -0
  38. data/test/images/test_color_sharpened.gd2 +0 -0
  39. data/test/images/test_cropped.gd2 +0 -0
  40. data/test/images/test_ellipse.gd2 +0 -0
  41. data/test/images/test_fill.gd2 +0 -0
  42. data/test/images/test_fill_to.gd2 +0 -0
  43. data/test/images/test_filled_circle.gd2 +0 -0
  44. data/test/images/test_filled_ellipse.gd2 +0 -0
  45. data/test/images/test_filled_wedge.gd2 +0 -0
  46. data/test/images/test_polar_transform.gd2 +0 -0
  47. data/test/images/test_resampled.gd2 +0 -0
  48. data/test/images/test_resized.gd2 +0 -0
  49. data/test/images/test_rotated_180.gd2 +0 -0
  50. data/test/images/test_text.gd2 +0 -0
  51. data/test/images/test_text_circle.gd2 +0 -0
  52. data/test/images/test_wedge.gd2 +0 -0
  53. data/test/test_helper.rb +13 -0
  54. data/vendor/fonts/ttf/DejaVuSans.ttf +0 -0
  55. data/vendor/fonts/ttf/LICENSE +99 -0
  56. metadata +123 -0
@@ -0,0 +1,215 @@
1
+ #
2
+ # Ruby/GD2 -- Ruby binding for gd 2 graphics library
3
+ #
4
+ # Copyright © 2005-2006 Robert Leslie, 2010 J Smith
5
+ #
6
+ # This file is part of Ruby/GD2.
7
+ #
8
+ # Ruby/GD2 is free software; you can redistribute it and/or modify it under
9
+ # the terms of the GNU General Public License as published by the Free
10
+ # Software Foundation; either version 2 of the License, or (at your option)
11
+ # any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful, but
14
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16
+ # for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License along
19
+ # with this program; if not, write to the Free Software Foundation, Inc.,
20
+ # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #
22
+
23
+ require 'ffi'
24
+ require 'rbconfig'
25
+
26
+ module GD2
27
+ VERSION = '0.0.3'.freeze
28
+
29
+ module GD2FFI
30
+ def self.gd_library_name
31
+ if ENV['GD2_LIBRARY_PATH']
32
+ ENV['GD2_LIBRARY_PATH']
33
+ else
34
+ lib = case Config::CONFIG['arch']
35
+ when /darwin/
36
+ [ '/usr/lib', '/usr/local/lib', '/opt/local/lib' ].detect { |path|
37
+ File.exists?("#{path}/libgd.2.dylib")
38
+ }.to_s + '/libgd.2.dylib'
39
+ when /mswin32/, /cygwin/
40
+ 'bgd.dll'
41
+ else
42
+ 'libgd.so.2'
43
+ end
44
+ end
45
+ end
46
+
47
+ extend FFI::Library
48
+ ffi_lib(*gd_library_name)
49
+
50
+ {
51
+ :gdImageCreate => [ :pointer, :int, :int ],
52
+ :gdImageCreateTrueColor => [ :pointer, :int, :int ],
53
+ :gdImageCreatePaletteFromTrueColor => [ :pointer, :pointer, :int, :int ],
54
+ :gdImageCreateFromJpeg => [ :pointer, :pointer ],
55
+ :gdImageCreateFromJpegPtr => [ :pointer, :int, :pointer ],
56
+ :gdImageCreateFromPng => [ :pointer, :pointer ],
57
+ :gdImageCreateFromPngPtr => [ :pointer, :int, :pointer ],
58
+ :gdImageCreateFromGif => [ :pointer, :pointer ],
59
+ :gdImageCreateFromGifPtr => [ :pointer, :int, :pointer ],
60
+ :gdImageCreateFromWBMP => [ :pointer, :pointer ],
61
+ :gdImageCreateFromWBMPPtr => [ :pointer, :int, :pointer ],
62
+ :gdImageCreateFromGd => [ :pointer, :pointer ],
63
+ :gdImageCreateFromGdPtr => [ :pointer, :int, :pointer ],
64
+ :gdImageCreateFromGd2 => [ :pointer, :pointer ],
65
+ :gdImageCreateFromGd2Ptr => [ :pointer, :int, :pointer ],
66
+ :gdImageCreateFromGd2Part => [ :pointer, :pointer, :int, :int, :int, :int ],
67
+ :gdImageCreateFromXbm => [ :pointer, :pointer ],
68
+ :gdImageCreateFromXpm => [ :pointer, :pointer ],
69
+ :gdImageCompare => [ :int, :pointer, :pointer ],
70
+ :gdImageJpeg => [ :void, :pointer, :pointer, :int ],
71
+ :gdImageJpegPtr => [ :pointer, :pointer, :pointer, :int ],
72
+ :gdImagePngEx => [ :void, :pointer, :pointer, :int ],
73
+ :gdImagePngPtrEx => [ :pointer, :pointer, :pointer, :int ],
74
+ :gdImageGif => [ :void, :pointer, :pointer ],
75
+ :gdImageGifPtr => [ :pointer, :pointer, :pointer ],
76
+ :gdImageWBMP => [ :void, :pointer, :int, :pointer ],
77
+ :gdImageWBMPPtr => [ :pointer, :pointer, :pointer, :int ],
78
+ :gdImageGd => [ :void, :pointer, :pointer ],
79
+ :gdImageGdPtr => [ :pointer, :pointer, :pointer ],
80
+ :gdImageGd2 => [ :void, :pointer, :pointer, :int, :int ],
81
+ :gdImageGd2Ptr => [ :pointer, :pointer, :int, :int, :pointer ],
82
+ :gdImageDestroy => [ :void, :pointer ],
83
+ :gdImageSetPixel => [ :void, :pointer, :int, :int, :int ],
84
+ :gdImageGetPixel => [ :int, :pointer, :int, :int ],
85
+ :gdImageGetTrueColorPixel => [ :int, :pointer, :int, :int ],
86
+ :gdImageLine => [ :void, :pointer, :int, :int, :int, :int, :int ],
87
+ :gdImageRectangle => [ :void, :pointer, :int, :int, :int, :int, :int ],
88
+ :gdImageFilledRectangle => [ :void, :pointer, :int, :int, :int, :int, :int ],
89
+ :gdImagePolygon => [ :void, :pointer, :pointer, :int, :int ],
90
+ :gdImageOpenPolygon => [ :void, :pointer, :pointer, :int, :int ],
91
+ :gdImageFilledPolygon => [ :void, :pointer, :pointer, :int, :int ],
92
+ :gdImageArc => [ :void, :pointer, :int, :int, :int, :int, :int, :int, :int ],
93
+ :gdImageFilledArc => [ :void, :pointer, :int, :int, :int, :int, :int, :int, :int, :int ],
94
+ # :gdImageEllipse => [ :void, :pointer, :int, :int, :int, :int, :int ],
95
+ :gdImageFilledEllipse => [ :void, :pointer, :int, :int, :int, :int, :int ],
96
+ :gdImageFill => [ :void, :pointer, :int, :int, :int ],
97
+ :gdImageFillToBorder => [ :void, :pointer, :int, :int, :int, :int ],
98
+ :gdImageSetClip => [ :void, :pointer, :int, :int, :int, :int ],
99
+ :gdImageGetClip => [ :void, :pointer, :pointer, :pointer, :pointer, :pointer ],
100
+ :gdImageBoundsSafe => [ :int, :pointer, :int, :int ],
101
+ :gdImageSetBrush => [ :void, :pointer, :pointer ],
102
+ :gdImageSetTile => [ :void, :pointer, :pointer ],
103
+ :gdImageSetAntiAliased => [ :void, :pointer, :int ],
104
+ :gdImageSetAntiAliasedDontBlend => [ :void, :pointer, :int, :int ],
105
+ :gdImageSetStyle => [ :void, :pointer, :pointer, :int ],
106
+ :gdImageSetThickness => [ :void, :pointer, :int ],
107
+ :gdImageInterlace => [ :void, :pointer, :int ],
108
+ :gdImageAlphaBlending => [ :void, :pointer, :int ],
109
+ :gdImageSaveAlpha => [ :void, :pointer, :int ],
110
+ :gdImageColorTransparent => [ :void, :pointer, :int ],
111
+ :gdImageColorResolveAlpha => [ :int, :pointer, :int, :int, :int, :int ],
112
+ :gdImageColorExactAlpha => [ :int, :pointer, :int, :int, :int, :int ],
113
+ :gdImageColorClosestAlpha => [ :int, :pointer, :int, :int, :int, :int ],
114
+ :gdImageColorClosestHWB => [ :int, :pointer, :int, :int, :int ],
115
+ :gdImageColorAllocateAlpha => [ :int, :pointer, :int, :int, :int, :int ],
116
+ :gdImageColorDeallocate => [ :void, :pointer, :int ],
117
+ :gdAlphaBlend => [ :int, :int, :int ],
118
+ :gdImageCopy => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int ],
119
+ :gdImageCopyResized => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int, :int ],
120
+ :gdImageCopyResampled => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int, :int ],
121
+ :gdImageCopyRotated => [ :void, :pointer, :pointer, :double, :double, :int, :int, :int, :int, :int ],
122
+ :gdImageCopyMerge => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int ],
123
+ :gdImageCopyMergeGray => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int ],
124
+ :gdImageSquareToCircle => [ :pointer, :pointer, :int ],
125
+ :gdImageSharpen => [ :void, :pointer, :int ],
126
+ :gdImageChar => [ :void, :pointer, :pointer, :int, :int, :int, :int ],
127
+ :gdImageCharUp => [ :void, :pointer, :pointer, :int, :int, :int, :int ],
128
+ :gdImageString => [ :void, :pointer, :pointer, :int, :int, :pointer, :int ],
129
+ :gdImageStringUp => [ :void, :pointer, :pointer, :int, :int, :pointer, :int ],
130
+ :gdImageStringFTEx => [ :pointer, :pointer, :pointer, :int, :pointer, :double, :double, :int, :int, :pointer, :pointer ],
131
+ :gdImageStringFTCircle => [ :pointer, :pointer, :int, :int, :double, :double, :double, :pointer, :double, :pointer, :pointer, :int ],
132
+ :gdFontGetSmall => [ :pointer ],
133
+ :gdFontGetLarge => [ :pointer ],
134
+ :gdFontGetMediumBold => [ :pointer ],
135
+ :gdFontGetGiant => [ :pointer ],
136
+ :gdFontGetTiny => [ :pointer ],
137
+ :gdFontCacheSetup => [ :int ],
138
+ :gdFontCacheShutdown => [ :void ],
139
+ :gdFTUseFontConfig => [ :int, :int ],
140
+ :gdFree => [ :void, :pointer ]
141
+ }.each do |fun, ary|
142
+ ret = ary.shift
143
+ attach_function(fun, ary, ret)
144
+ end
145
+ end
146
+
147
+ # Bit flags for Image#compare
148
+
149
+ CMP_IMAGE = 1 # Actual image IS different
150
+ CMP_NUM_COLORS = 2 # Number of Colours in pallette differ
151
+ CMP_COLOR = 4 # Image colours differ
152
+ CMP_SIZE_X = 8 # Image width differs
153
+ CMP_SIZE_Y = 16 # Image heights differ
154
+ CMP_TRANSPARENT = 32 # Transparent colour
155
+ CMP_BACKGROUND = 64 # Background colour
156
+ CMP_INTERLACE = 128 # Interlaced setting
157
+ CMP_TRUECOLOR = 256 # Truecolor vs palette differs
158
+
159
+ # Format flags for Image#gd2
160
+
161
+ FMT_RAW = 1
162
+ FMT_COMPRESSED = 2
163
+
164
+ # Color constants
165
+
166
+ MAX_COLORS = 256
167
+
168
+ RGB_MAX = 255
169
+
170
+ ALPHA_MAX = 127
171
+ ALPHA_OPAQUE = 0
172
+ ALPHA_TRANSPARENT = 127
173
+
174
+ class LibraryError < StandardError; end
175
+ end
176
+
177
+ require 'gd2/image'
178
+ require 'gd2/color'
179
+ require 'gd2/palette'
180
+ require 'gd2/canvas'
181
+ require 'gd2/font'
182
+ require 'gd2/ffi_struct'
183
+
184
+ class Numeric
185
+ if not self.instance_methods.include? 'degrees'
186
+ # Express an angle in degrees, e.g. 90.degrees. Angles are converted to
187
+ # radians.
188
+ def degrees
189
+ self * 2 * Math::PI / 360
190
+ end
191
+ alias degree degrees
192
+ end
193
+
194
+ if not self.instance_methods.include? 'to_degrees'
195
+ # Convert an angle (in radians) to degrees.
196
+ def to_degrees
197
+ self * 360 / Math::PI / 2
198
+ end
199
+ end
200
+
201
+ if not self.instance_methods.include? 'percent'
202
+ # Express a percentage, e.g. 50.percent. Percentages are floating point
203
+ # values, e.g. 0.5.
204
+ def percent
205
+ self / 100.0
206
+ end
207
+ end
208
+
209
+ if not self.instance_methods.include? 'to_percent'
210
+ # Convert a number to a percentage value, e.g. 0.5 to 50.0.
211
+ def to_percent
212
+ self * 100
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,422 @@
1
+ #
2
+ # Ruby/GD2 -- Ruby binding for gd 2 graphics library
3
+ #
4
+ # Copyright © 2005 Robert Leslie, 2010 J Smith
5
+ #
6
+ # This file is part of Ruby/GD2.
7
+ #
8
+ # Ruby/GD2 is free software; you can redistribute it and/or modify it under
9
+ # the terms of the GNU General Public License as published by the Free
10
+ # Software Foundation; either version 2 of the License, or (at your option)
11
+ # any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful, but
14
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16
+ # for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License along
19
+ # with this program; if not, write to the Free Software Foundation, Inc.,
20
+ # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #
22
+
23
+ require 'matrix'
24
+
25
+ module GD2
26
+ class Canvas
27
+ class NoColorSelectedError < StandardError; end
28
+ class NoFontSelectedError < StandardError; end
29
+
30
+ class Point
31
+ attr_reader :x, :y
32
+
33
+ def initialize(x, y)
34
+ @x, @y = x, y
35
+ end
36
+
37
+ def coordinates
38
+ [@x, @y]
39
+ end
40
+
41
+ def transform!(matrix)
42
+ @x, @y = (Matrix.row_vector([@x, @y, 1]) * matrix)[0, 0..1]
43
+ self
44
+ end
45
+
46
+ def transform(matrix)
47
+ dup.transform!(matrix)
48
+ end
49
+
50
+ def draw(image, mode)
51
+ image.set_pixel(@x, @y, mode)
52
+ end
53
+ end
54
+
55
+ class Line
56
+ def initialize(point1, point2)
57
+ @p1, @p2 = point1, point2
58
+ end
59
+
60
+ def draw(image, mode)
61
+ GD2FFI.send(:gdImageLine, image.image_ptr,
62
+ @p1.x.to_i, @p1.y.to_i, @p2.x.to_i, @p2.y.to_i, mode.to_i)
63
+ nil
64
+ end
65
+ end
66
+
67
+ class Rectangle
68
+ def initialize(point1, point2)
69
+ @p1, @p2 = point1, point2
70
+ end
71
+
72
+ def draw(image, mode)
73
+ GD2FFI.send(draw_sym, image.image_ptr, @p1.x.to_i, @p1.y.to_i, @p2.x.to_i, @p2.y.to_i, mode.to_i)
74
+ nil
75
+ end
76
+
77
+ def draw_sym
78
+ :gdImageRectangle
79
+ end
80
+ end
81
+
82
+ class FilledRectangle < Rectangle
83
+ def draw_sym
84
+ :gdImageFilledRectangle
85
+ end
86
+ end
87
+
88
+ class Polygon
89
+ def initialize(points)
90
+ @points = points
91
+ end
92
+
93
+ def draw(image, mode)
94
+ GD2FFI.send(draw_sym, image.image_ptr, @points.map { |point|
95
+ point.coordinates.pack('i_i_')
96
+ }.join(''), @points.length, mode.to_i)
97
+ nil
98
+ end
99
+
100
+ def draw_sym
101
+ :gdImagePolygon
102
+ end
103
+ end
104
+
105
+ class OpenPolygon < Polygon
106
+ def draw_sym
107
+ :gdImageOpenPolygon
108
+ end
109
+ end
110
+
111
+ class FilledPolygon < Polygon
112
+ def draw_sym
113
+ :gdImageFilledPolygon
114
+ end
115
+ end
116
+
117
+ class Arc
118
+ def initialize(center, width, height, range)
119
+ @center, @width, @height = center, width, height
120
+ @range = Range.new(360.degrees - range.end, 360.degrees - range.begin,
121
+ range.exclude_end?)
122
+ end
123
+
124
+ def draw(image, mode)
125
+ GD2FFI.send(:gdImageArc, image.image_ptr, @center.x.to_i, @center.y.to_i,
126
+ @width.to_i, @height.to_i,
127
+ @range.begin.to_degrees.round.to_i, @range.end.to_degrees.round.to_i, mode.to_i)
128
+ nil
129
+ end
130
+ end
131
+
132
+ class Wedge < Arc
133
+ # Arc styles
134
+
135
+ ARC = 0
136
+ PIE = ARC
137
+ CHORD = 1
138
+ NO_FILL = 2
139
+ EDGED = 4
140
+
141
+ def initialize(center, width, height, range, chord = false)
142
+ super(center, width, height, range)
143
+ @chord = chord
144
+ end
145
+
146
+ def draw(image, mode)
147
+ GD2FFI.send(:gdImageFilledArc, image.image_ptr, @center.x.to_i, @center.y.to_i,
148
+ @width.to_i, @height.to_i,
149
+ @range.begin.to_degrees.round.to_i, @range.end.to_degrees.round.to_i,
150
+ mode.to_i, style.to_i)
151
+ nil
152
+ end
153
+
154
+ def style
155
+ (@chord ? CHORD : ARC) | NO_FILL | EDGED
156
+ end
157
+ end
158
+
159
+ class FilledWedge < Wedge
160
+ def style
161
+ super & ~(NO_FILL | EDGED)
162
+ end
163
+ end
164
+
165
+ class Ellipse
166
+ def initialize(center, width, height)
167
+ @center, @width, @height = center, width, height
168
+ end
169
+
170
+ def draw(image, mode)
171
+ GD2FFI.send(:gdImageArc, image.image_ptr, @center.x.to_i, @center.y.to_i,
172
+ @width.to_i, @height.to_i, 0, 360, mode.to_i)
173
+ nil
174
+ end
175
+ end
176
+
177
+ class FilledEllipse < Ellipse
178
+ def draw(image, mode)
179
+ GD2FFI.send(:gdImageFilledEllipse, image.image_ptr, @center.x.to_i, @center.y.to_i,
180
+ @width.to_i, @height.to_i, mode.to_i)
181
+ end
182
+ end
183
+
184
+ class Text
185
+ def initialize(font, point, angle, string)
186
+ @font = font
187
+ @point = point
188
+ @angle = angle
189
+ @string = string
190
+ end
191
+
192
+ def draw(image, color)
193
+ @font.draw(image.image_ptr, @point.x, @point.y, @angle, @string, color)
194
+ end
195
+ end
196
+
197
+ class TextCircle
198
+ def initialize(font, point, radius, text_radius, fill_portion,
199
+ top, bottom)
200
+ @font = font
201
+ @point = point
202
+ @radius = radius
203
+ @text_radius = text_radius
204
+ @fill_portion = fill_portion
205
+ @top = top
206
+ @bottom = bottom
207
+ end
208
+
209
+ def draw(image, color)
210
+ @font.draw_circle(image.image_ptr, @point.x, @point.y, @radius,
211
+ @text_radius, @fill_portion, @top, @bottom, color)
212
+ end
213
+ end
214
+
215
+ attr_reader :color, :thickness, :style, :brush, :tile, :dont_blend,
216
+ :transformation_matrix
217
+ attr_accessor :anti_aliasing, :font
218
+
219
+ # Special colors
220
+
221
+ STYLED = -2
222
+ BRUSHED = -3
223
+ STYLED_BRUSHED = -4
224
+ TILED = -5
225
+
226
+ TRANSPARENT = -6 # Line styles only; not a color index
227
+ ANTI_ALIASED = -7
228
+
229
+ def initialize(image)
230
+ @image = image
231
+ self.thickness = 1
232
+ self.anti_aliasing = false
233
+ @transformation_matrix = Matrix.identity(3)
234
+ move_to(0, 0)
235
+ end
236
+
237
+ def color=(color)
238
+ @pixel = @image.color2pixel(@color = color)
239
+ @brush = @style = nil
240
+ end
241
+
242
+ def thickness=(thickness)
243
+ GD2FFI.send(:gdImageSetThickness, @image.image_ptr, @thickness = thickness.to_i)
244
+ end
245
+
246
+ def style=(ary)
247
+ if @style = ary
248
+ GD2FFI.send(:gdImageSetStyle, @image.image_ptr,
249
+ ary.map { |c|
250
+ !c ? TRANSPARENT : true == c ? -1 : @image.color2pixel(c)
251
+ }, ary.length)
252
+ end
253
+ end
254
+
255
+ def brush=(image)
256
+ if @brush = image
257
+ GD2FFI.send(:gdImageSetBrush, @image.image_ptr, image.image_ptr)
258
+ end
259
+ end
260
+
261
+ def tile=(image)
262
+ if @tile = image
263
+ GD2FFI.send(:gdImageSetTile, @image.image_ptr, image.image_ptr)
264
+ end
265
+ end
266
+
267
+ alias anti_aliasing? anti_aliasing
268
+
269
+ def dont_blend=(color)
270
+ @dont_blend = color ? @image.color2pixel(color) : nil
271
+ end
272
+
273
+ def affine_transform(a, b, c, d, tx, ty)
274
+ old_matrix = @transformation_matrix
275
+ begin
276
+ @transformation_matrix = Matrix[[a, b, 0], [c, d, 0], [tx, ty, 1]] *
277
+ @transformation_matrix
278
+ yield
279
+ ensure
280
+ @transformation_matrix = old_matrix
281
+ end
282
+ end
283
+
284
+ def translate(tx, ty, &block)
285
+ affine_transform(1, 0, 0, 1, tx, ty, &block)
286
+ end
287
+
288
+ def scale(sx, sy = sx, &block)
289
+ affine_transform(sx, 0, 0, sy, 0, 0, &block)
290
+ end
291
+
292
+ def rotate(angle, &block)
293
+ cos = Math.cos(angle)
294
+ sin = Math.sin(angle)
295
+ affine_transform(cos, sin, -sin, cos, 0, 0, &block)
296
+ end
297
+
298
+ def cartesian(&block)
299
+ affine_transform(1, 0, 0, -1, 0, @image.height - 1, &block)
300
+ end
301
+
302
+ def point(x, y)
303
+ Point.new(x, y).transform!(transformation_matrix)
304
+ end
305
+
306
+ def move_to(x, y)
307
+ @point = point(x, y)
308
+ self
309
+ end
310
+
311
+ def move(x, y)
312
+ @point.transform!(Matrix[[1, 0, 0], [0, 1, 0], [x, y, 1]] *
313
+ @transformation_matrix)
314
+ # @point = point(@point.x + x, @point.y + y)
315
+ self
316
+ end
317
+
318
+ def location
319
+ @point.transform(transformation_matrix.inverse).coordinates
320
+ end
321
+
322
+ def line(x1, y1, x2, y2)
323
+ Line.new(point(x1, y1), point(x2, y2)).draw(@image, line_pixel)
324
+ end
325
+
326
+ def line_to(x, y)
327
+ point2 = point(x, y)
328
+ Line.new(@point, point2).draw(@image, line_pixel)
329
+ @point = point2
330
+ self
331
+ end
332
+
333
+ def fill
334
+ GD2FFI.send(:gdImageFill, @image.image_ptr, @point.x.to_i, @point.y.to_i, fill_pixel.to_i)
335
+ self
336
+ end
337
+
338
+ def fill_to(border)
339
+ # An apparent bug in gd prevents us from using fill_pixel
340
+ GD2FFI.send(:gdImageFillToBorder, @image.image_ptr, @point.x.to_i, @point.y.to_i,
341
+ @image.color2pixel(border), pixel.to_i)
342
+ self
343
+ end
344
+
345
+ def rectangle(x1, y1, x2, y2, filled = false)
346
+ (filled ? FilledRectangle : Rectangle).new(point(x1, y1), point(x2, y2)).
347
+ draw(@image, filled ? fill_pixel : line_pixel)
348
+ end
349
+
350
+ def polygon(points, filled = false, open = false)
351
+ points = points.map { |(x, y)| point(x, y) }
352
+ if filled
353
+ FilledPolygon.new(points).draw(@image, fill_pixel)
354
+ else
355
+ (open ? OpenPolygon : Polygon).new(points).draw(@image, line_pixel)
356
+ end
357
+ end
358
+
359
+ def arc(cx, cy, width, height, range)
360
+ Arc.new(point(cx, cy), width, height, range).draw(@image, line_pixel)
361
+ end
362
+
363
+ def wedge(cx, cy, width, height, range, filled = false, chord = false)
364
+ (filled ? FilledWedge : Wedge).new(point(cx, cy), width, height,
365
+ range, chord).draw(@image, filled ? fill_pixel : line_pixel)
366
+ end
367
+
368
+ def ellipse(cx, cy, width, height, filled = false)
369
+ (filled ? FilledEllipse : Ellipse).new(point(cx, cy), width, height).
370
+ draw(@image, filled ? fill_pixel : line_pixel)
371
+ end
372
+
373
+ def circle(cx, cy, diameter, filled = false)
374
+ ellipse(cx, cy, diameter, diameter, filled)
375
+ end
376
+
377
+ def text(string, angle = 0.0)
378
+ Text.new(get_font, @point, angle, string).draw(@image, pixel)
379
+ end
380
+
381
+ def text_circle(top, bottom, radius, text_radius, fill_portion)
382
+ TextCircle.new(get_font, @point, radius, text_radius, fill_portion,
383
+ top, bottom).draw(@image, pixel)
384
+ end
385
+
386
+ private
387
+
388
+ def get_font
389
+ raise NoFontSelectedError, 'No font selected' unless @font
390
+ @font
391
+ end
392
+
393
+ def pixel
394
+ raise NoColorSelectedError, 'No drawing color selected' unless @pixel
395
+ @pixel
396
+ end
397
+
398
+ def line_pixel
399
+ if @style && @brush
400
+ STYLED_BRUSHED
401
+ elsif @style
402
+ STYLED
403
+ elsif @brush
404
+ BRUSHED
405
+ elsif anti_aliasing?
406
+ if @dont_blend
407
+ GD2FFI.send(:gdImageSetAntiAliasedDontBlend, @image.image_ptr,
408
+ pixel.to_i, @dont_blend.to_i)
409
+ else
410
+ GD2FFI.send(:gdImageSetAntiAliased, @image.image_ptr, pixel.to_i)
411
+ end
412
+ ANTI_ALIASED
413
+ else
414
+ pixel
415
+ end
416
+ end
417
+
418
+ def fill_pixel
419
+ @tile ? TILED : pixel
420
+ end
421
+ end
422
+ end