gd2-ffij 0.0.2

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 +209 -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 +347 -0
  12. data/lib/gd2/image.rb +785 -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 +118 -0
data/lib/gd2-ffij.rb ADDED
@@ -0,0 +1,209 @@
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.1'.freeze
28
+
29
+ module GD2FFI
30
+ def self.gd_library_name
31
+ case Config::CONFIG['arch']
32
+ when /darwin/
33
+ 'libgd.2.dylib'
34
+ when /mswin32/, /cygwin/
35
+ 'bgd.dll'
36
+ else
37
+ 'libgd.so.2'
38
+ end
39
+ end
40
+
41
+ extend FFI::Library
42
+ ffi_lib(*gd_library_name)
43
+
44
+ {
45
+ :gdImageCreate => [ :pointer, :int, :int ],
46
+ :gdImageCreateTrueColor => [ :pointer, :int, :int ],
47
+ :gdImageCreatePaletteFromTrueColor => [ :pointer, :pointer, :int, :int ],
48
+ :gdImageCreateFromJpeg => [ :pointer, :pointer ],
49
+ :gdImageCreateFromJpegPtr => [ :pointer, :int, :pointer ],
50
+ :gdImageCreateFromPng => [ :pointer, :pointer ],
51
+ :gdImageCreateFromPngPtr => [ :pointer, :int, :pointer ],
52
+ :gdImageCreateFromGif => [ :pointer, :pointer ],
53
+ :gdImageCreateFromGifPtr => [ :pointer, :int, :pointer ],
54
+ :gdImageCreateFromWBMP => [ :pointer, :pointer ],
55
+ :gdImageCreateFromWBMPPtr => [ :pointer, :int, :pointer ],
56
+ :gdImageCreateFromGd => [ :pointer, :pointer ],
57
+ :gdImageCreateFromGdPtr => [ :pointer, :int, :pointer ],
58
+ :gdImageCreateFromGd2 => [ :pointer, :pointer ],
59
+ :gdImageCreateFromGd2Ptr => [ :pointer, :int, :pointer ],
60
+ :gdImageCreateFromGd2Part => [ :pointer, :pointer, :int, :int, :int, :int ],
61
+ :gdImageCreateFromXbm => [ :pointer, :pointer ],
62
+ :gdImageCreateFromXpm => [ :pointer, :pointer ],
63
+ :gdImageCompare => [ :int, :pointer, :pointer ],
64
+ :gdImageJpeg => [ :void, :pointer, :pointer, :int ],
65
+ :gdImageJpegPtr => [ :pointer, :pointer, :pointer, :int ],
66
+ :gdImagePngEx => [ :void, :pointer, :pointer, :int ],
67
+ :gdImagePngPtrEx => [ :pointer, :pointer, :pointer, :int ],
68
+ :gdImageGif => [ :void, :pointer, :pointer ],
69
+ :gdImageGifPtr => [ :pointer, :pointer, :pointer ],
70
+ :gdImageWBMP => [ :void, :pointer, :int, :pointer ],
71
+ :gdImageWBMPPtr => [ :pointer, :pointer, :pointer, :int ],
72
+ :gdImageGd => [ :void, :pointer, :pointer ],
73
+ :gdImageGdPtr => [ :pointer, :pointer, :pointer ],
74
+ :gdImageGd2 => [ :void, :pointer, :pointer, :int, :int ],
75
+ :gdImageGd2Ptr => [ :pointer, :pointer, :int, :int, :pointer ],
76
+ :gdImageDestroy => [ :void, :pointer ],
77
+ :gdImageSetPixel => [ :void, :pointer, :int, :int, :int ],
78
+ :gdImageGetPixel => [ :int, :pointer, :int, :int ],
79
+ :gdImageGetTrueColorPixel => [ :int, :pointer, :int, :int ],
80
+ :gdImageLine => [ :void, :pointer, :int, :int, :int, :int, :int ],
81
+ :gdImageRectangle => [ :void, :pointer, :int, :int, :int, :int, :int ],
82
+ :gdImageFilledRectangle => [ :void, :pointer, :int, :int, :int, :int, :int ],
83
+ :gdImagePolygon => [ :void, :pointer, :pointer, :int, :int ],
84
+ :gdImageOpenPolygon => [ :void, :pointer, :pointer, :int, :int ],
85
+ :gdImageFilledPolygon => [ :void, :pointer, :pointer, :int, :int ],
86
+ :gdImageArc => [ :void, :pointer, :int, :int, :int, :int, :int, :int, :int ],
87
+ :gdImageFilledArc => [ :void, :pointer, :int, :int, :int, :int, :int, :int, :int, :int ],
88
+ # :gdImageEllipse => [ :void, :pointer, :int, :int, :int, :int, :int ],
89
+ :gdImageFilledEllipse => [ :void, :pointer, :int, :int, :int, :int, :int ],
90
+ :gdImageFill => [ :void, :pointer, :int, :int, :int ],
91
+ :gdImageFillToBorder => [ :void, :pointer, :int, :int, :int, :int ],
92
+ :gdImageSetClip => [ :void, :pointer, :int, :int, :int, :int ],
93
+ :gdImageGetClip => [ :void, :pointer, :pointer, :pointer, :pointer, :pointer ],
94
+ :gdImageBoundsSafe => [ :int, :pointer, :int, :int ],
95
+ :gdImageSetBrush => [ :void, :pointer, :pointer ],
96
+ :gdImageSetTile => [ :void, :pointer, :pointer ],
97
+ :gdImageSetAntiAliased => [ :void, :pointer, :int ],
98
+ :gdImageSetAntiAliasedDontBlend => [ :void, :pointer, :int, :int ],
99
+ :gdImageSetStyle => [ :void, :pointer, :pointer, :int ],
100
+ :gdImageSetThickness => [ :void, :pointer, :int ],
101
+ :gdImageInterlace => [ :void, :pointer, :int ],
102
+ :gdImageAlphaBlending => [ :void, :pointer, :int ],
103
+ :gdImageSaveAlpha => [ :void, :pointer, :int ],
104
+ :gdImageColorTransparent => [ :void, :pointer, :int ],
105
+ :gdImageColorResolveAlpha => [ :int, :pointer, :int, :int, :int, :int ],
106
+ :gdImageColorExactAlpha => [ :int, :pointer, :int, :int, :int, :int ],
107
+ :gdImageColorClosestAlpha => [ :int, :pointer, :int, :int, :int, :int ],
108
+ :gdImageColorClosestHWB => [ :int, :pointer, :int, :int, :int ],
109
+ :gdImageColorAllocateAlpha => [ :int, :pointer, :int, :int, :int, :int ],
110
+ :gdImageColorDeallocate => [ :void, :pointer, :int ],
111
+ :gdAlphaBlend => [ :int, :int, :int ],
112
+ :gdImageCopy => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int ],
113
+ :gdImageCopyResized => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int, :int ],
114
+ :gdImageCopyResampled => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int, :int ],
115
+ :gdImageCopyRotated => [ :void, :pointer, :pointer, :double, :double, :int, :int, :int, :int, :int ],
116
+ :gdImageCopyMerge => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int ],
117
+ :gdImageCopyMergeGray => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :int, :int ],
118
+ :gdImageSquareToCircle => [ :pointer, :pointer, :int ],
119
+ :gdImageSharpen => [ :void, :pointer, :int ],
120
+ :gdImageChar => [ :void, :pointer, :pointer, :int, :int, :int, :int ],
121
+ :gdImageCharUp => [ :void, :pointer, :pointer, :int, :int, :int, :int ],
122
+ :gdImageString => [ :void, :pointer, :pointer, :int, :int, :pointer, :int ],
123
+ :gdImageStringUp => [ :void, :pointer, :pointer, :int, :int, :pointer, :int ],
124
+ :gdImageStringFTEx => [ :pointer, :pointer, :pointer, :int, :pointer, :double, :double, :int, :int, :pointer, :pointer ],
125
+ :gdImageStringFTCircle => [ :pointer, :pointer, :int, :int, :double, :double, :double, :pointer, :double, :pointer, :pointer, :int ],
126
+ :gdFontGetSmall => [ :pointer ],
127
+ :gdFontGetLarge => [ :pointer ],
128
+ :gdFontGetMediumBold => [ :pointer ],
129
+ :gdFontGetGiant => [ :pointer ],
130
+ :gdFontGetTiny => [ :pointer ],
131
+ :gdFontCacheSetup => [ :int ],
132
+ :gdFontCacheShutdown => [ :void ],
133
+ :gdFTUseFontConfig => [ :int, :int ],
134
+ :gdFree => [ :void, :pointer ]
135
+ }.each do |fun, ary|
136
+ ret = ary.shift
137
+ attach_function(fun, ary, ret)
138
+ end
139
+ end
140
+
141
+ # Bit flags for Image#compare
142
+
143
+ CMP_IMAGE = 1 # Actual image IS different
144
+ CMP_NUM_COLORS = 2 # Number of Colours in pallette differ
145
+ CMP_COLOR = 4 # Image colours differ
146
+ CMP_SIZE_X = 8 # Image width differs
147
+ CMP_SIZE_Y = 16 # Image heights differ
148
+ CMP_TRANSPARENT = 32 # Transparent colour
149
+ CMP_BACKGROUND = 64 # Background colour
150
+ CMP_INTERLACE = 128 # Interlaced setting
151
+ CMP_TRUECOLOR = 256 # Truecolor vs palette differs
152
+
153
+ # Format flags for Image#gd2
154
+
155
+ FMT_RAW = 1
156
+ FMT_COMPRESSED = 2
157
+
158
+ # Color constants
159
+
160
+ MAX_COLORS = 256
161
+
162
+ RGB_MAX = 255
163
+
164
+ ALPHA_MAX = 127
165
+ ALPHA_OPAQUE = 0
166
+ ALPHA_TRANSPARENT = 127
167
+
168
+ class LibraryError < StandardError; end
169
+ end
170
+
171
+ require 'gd2/image'
172
+ require 'gd2/color'
173
+ require 'gd2/palette'
174
+ require 'gd2/canvas'
175
+ require 'gd2/font'
176
+ require 'gd2/ffi_struct'
177
+
178
+ class Numeric
179
+ if not self.instance_methods.include? 'degrees'
180
+ # Express an angle in degrees, e.g. 90.degrees. Angles are converted to
181
+ # radians.
182
+ def degrees
183
+ self * 2 * Math::PI / 360
184
+ end
185
+ alias degree degrees
186
+ end
187
+
188
+ if not self.instance_methods.include? 'to_degrees'
189
+ # Convert an angle (in radians) to degrees.
190
+ def to_degrees
191
+ self * 360 / Math::PI / 2
192
+ end
193
+ end
194
+
195
+ if not self.instance_methods.include? 'percent'
196
+ # Express a percentage, e.g. 50.percent. Percentages are floating point
197
+ # values, e.g. 0.5.
198
+ def percent
199
+ self / 100.0
200
+ end
201
+ end
202
+
203
+ if not self.instance_methods.include? 'to_percent'
204
+ # Convert a number to a percentage value, e.g. 0.5 to 50.0.
205
+ def to_percent
206
+ self * 100
207
+ end
208
+ end
209
+ end
data/lib/gd2/canvas.rb ADDED
@@ -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, @p1.y, @p2.x, @p2.y, mode)
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, @p1.y, @p2.x, @p2.y, mode)
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)
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, @center.y,
126
+ @width, @height,
127
+ @range.begin.to_degrees.round, @range.end.to_degrees.round, mode)
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, @center.y,
148
+ @width, @height,
149
+ @range.begin.to_degrees.round, @range.end.to_degrees.round,
150
+ mode, style)
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, @center.y,
172
+ @width, @height, 0, 360, mode)
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, @center.y,
180
+ @width, @height, mode)
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)
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, @point.y, fill_pixel)
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, @point.y,
341
+ @image.color2pixel(border), pixel)
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, @dont_blend)
409
+ else
410
+ GD2FFI.send(:gdImageSetAntiAliased, @image.image_ptr, pixel)
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