gd2-ffij 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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