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.
- 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
|