gd2-ffij 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/COPYING +340 -0
- data/COPYRIGHT +17 -0
- data/README +34 -0
- data/Rakefile +32 -0
- data/gd2-ffij.gemspec +96 -0
- data/lib/gd2-ffij.rb +209 -0
- data/lib/gd2/canvas.rb +422 -0
- data/lib/gd2/color.rb +240 -0
- data/lib/gd2/ffi_struct.rb +76 -0
- data/lib/gd2/font.rb +347 -0
- data/lib/gd2/image.rb +785 -0
- data/lib/gd2/palette.rb +253 -0
- data/test/canvas_test.rb +186 -0
- data/test/image_test.rb +149 -0
- data/test/images/test.bmp +0 -0
- data/test/images/test.gd +0 -0
- data/test/images/test.gd2 +0 -0
- data/test/images/test.gif +0 -0
- data/test/images/test.jpg +0 -0
- data/test/images/test.png +0 -0
- data/test/images/test.wbmp +0 -0
- data/test/images/test.xbm +686 -0
- data/test/images/test.xcf +0 -0
- data/test/images/test.xpm +261 -0
- data/test/images/test_arc.gd2 +0 -0
- data/test/images/test_canvas_filled_polygon.gd2 +0 -0
- data/test/images/test_canvas_filled_rectangle.gd2 +0 -0
- data/test/images/test_canvas_line.gd2 +0 -0
- data/test/images/test_canvas_move_to_and_line_to.gd2 +0 -0
- data/test/images/test_canvas_polygon.gd2 +0 -0
- data/test/images/test_canvas_rectangle.gd2 +0 -0
- data/test/images/test_circle.gd2 +0 -0
- data/test/images/test_color.gd2 +0 -0
- data/test/images/test_color.png +0 -0
- data/test/images/test_color.xcf +0 -0
- data/test/images/test_color_indexed.gd2 +0 -0
- data/test/images/test_color_sharpened.gd2 +0 -0
- data/test/images/test_cropped.gd2 +0 -0
- data/test/images/test_ellipse.gd2 +0 -0
- data/test/images/test_fill.gd2 +0 -0
- data/test/images/test_fill_to.gd2 +0 -0
- data/test/images/test_filled_circle.gd2 +0 -0
- data/test/images/test_filled_ellipse.gd2 +0 -0
- data/test/images/test_filled_wedge.gd2 +0 -0
- data/test/images/test_polar_transform.gd2 +0 -0
- data/test/images/test_resampled.gd2 +0 -0
- data/test/images/test_resized.gd2 +0 -0
- data/test/images/test_rotated_180.gd2 +0 -0
- data/test/images/test_text.gd2 +0 -0
- data/test/images/test_text_circle.gd2 +0 -0
- data/test/images/test_wedge.gd2 +0 -0
- data/test/test_helper.rb +13 -0
- data/vendor/fonts/ttf/DejaVuSans.ttf +0 -0
- data/vendor/fonts/ttf/LICENSE +99 -0
- 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
|