gd2 1.0

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.
@@ -0,0 +1,281 @@
1
+ #
2
+ # Ruby/GD2 -- Ruby binding for gd 2 graphics library
3
+ #
4
+ # Copyright © 2005 Robert Leslie
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
+ module GD2
24
+ class Canvas
25
+ class NoColorSelectedError < StandardError; end
26
+ class NoFontSelectedError < StandardError; end
27
+
28
+ class Point
29
+ attr_reader :x, :y
30
+
31
+ def initialize(x, y)
32
+ @x, @y = x, y
33
+ end
34
+
35
+ def coordinates
36
+ [@x, @y]
37
+ end
38
+
39
+ def draw(image, mode)
40
+ image.set_pixel(@x, @y, mode)
41
+ end
42
+ end
43
+
44
+ class Line
45
+ def initialize(point1, point2)
46
+ @p1, @p2 = point1, point2
47
+ end
48
+
49
+ def draw(image, mode)
50
+ SYM[:gdImageLine].call(image.image_ptr,
51
+ @p1.x, @p1.y, @p2.x, @p2.y, mode)
52
+ nil
53
+ end
54
+ end
55
+
56
+ class Rectangle
57
+ def initialize(point1, point2)
58
+ @p1, @p2 = point1, point2
59
+ end
60
+
61
+ def draw(image, mode)
62
+ SYM[draw_sym].call(image.image_ptr, @p1.x, @p1.y, @p2.x, @p2.y, mode)
63
+ nil
64
+ end
65
+
66
+ def draw_sym
67
+ :gdImageRectangle
68
+ end
69
+ end
70
+
71
+ class FilledRectangle < Rectangle
72
+ def draw_sym
73
+ :gdImageFilledRectangle
74
+ end
75
+ end
76
+
77
+ class Polygon
78
+ def initialize(points)
79
+ @points = points
80
+ end
81
+
82
+ def draw(image, mode)
83
+ SYM[draw_sym].call(image.image_ptr, @points.map { |point|
84
+ point.coordinates.pack('i_i_')
85
+ }.join('').to_ptr, @points.length, mode)
86
+ nil
87
+ end
88
+
89
+ def draw_sym
90
+ :gdImagePolygon
91
+ end
92
+ end
93
+
94
+ class OpenPolygon < Polygon
95
+ def draw_sym
96
+ :gdImageOpenPolygon
97
+ end
98
+ end
99
+
100
+ class FilledPolygon < Polygon
101
+ def draw_sym
102
+ :gdImageFilledPolygon
103
+ end
104
+ end
105
+
106
+ class Text
107
+ def initialize(font, point, angle, string)
108
+ @font = font
109
+ @point = point
110
+ @angle = angle
111
+ @string = string
112
+ end
113
+
114
+ def draw(image, color)
115
+ @font.draw(image.image_ptr, @point.x, @point.y, @angle, @string, color)
116
+ end
117
+ end
118
+
119
+ class TextCircle
120
+ def initialize(font, point, radius, text_radius, fill_portion,
121
+ top, bottom)
122
+ @font = font
123
+ @point = point
124
+ @radius = radius
125
+ @text_radius = text_radius
126
+ @fill_portion = fill_portion
127
+ @top = top
128
+ @bottom = bottom
129
+ end
130
+
131
+ def draw(image, color)
132
+ @font.draw_circle(image.image_ptr, @point.x, @point.y, @radius,
133
+ @text_radius, @fill_portion, @top, @bottom, color)
134
+ end
135
+ end
136
+
137
+ attr_reader :color, :thickness, :style, :brush, :tile, :dont_blend
138
+ attr_accessor :anti_aliasing, :font
139
+
140
+ # Special colors
141
+
142
+ STYLED = -2
143
+ BRUSHED = -3
144
+ STYLED_BRUSHED = -4
145
+ TILED = -5
146
+
147
+ TRANSPARENT = -6 # Line styles only; not a color index
148
+ ANTI_ALIASED = -7
149
+
150
+ def initialize(image)
151
+ @image = image
152
+ self.thickness = 1
153
+ self.anti_aliasing = false
154
+ move_to(0, 0)
155
+ end
156
+
157
+ def color=(color)
158
+ @pixel = @image.color2pixel(@color = color)
159
+ @brush = @style = nil
160
+ end
161
+
162
+ def thickness=(thickness)
163
+ SYM[:gdImageSetThickness].call(@image.image_ptr, @thickness = thickness)
164
+ end
165
+
166
+ def style=(ary)
167
+ if @style = ary
168
+ SYM[:gdImageSetStyle].call(@image.image_ptr,
169
+ ary.map { |c|
170
+ !c ? TRANSPARENT : c == true ? -1 : @image.color2pixel(c)
171
+ }, ary.length)
172
+ end
173
+ end
174
+
175
+ def brush=(image)
176
+ if @brush = image
177
+ SYM[:gdImageSetBrush].call(@image.image_ptr, image.image_ptr)
178
+ end
179
+ end
180
+
181
+ def tile=(image)
182
+ if @tile = image
183
+ SYM[:gdImageSetTile].call(@image.image_ptr, image.image_ptr)
184
+ end
185
+ end
186
+
187
+ alias anti_aliasing? anti_aliasing
188
+
189
+ def dont_blend=(color)
190
+ @dont_blend = color ? @image.color2pixel(color) : nil
191
+ end
192
+
193
+ def point(x, y)
194
+ Point.new(x, y)
195
+ end
196
+
197
+ def move_to(x, y)
198
+ @point = point(x, y)
199
+ self
200
+ end
201
+
202
+ def move(x, y)
203
+ @point = point(@point.x + x, @point.y + y)
204
+ self
205
+ end
206
+
207
+ def location
208
+ @point.coordinates
209
+ end
210
+
211
+ def line(x1, y1, x2, y2)
212
+ Line.new(point(x1, y1), point(x2, y2)).draw(@image, line_pixel)
213
+ end
214
+
215
+ def line_to(x, y)
216
+ point2 = point(x, y)
217
+ line(@point.x, @point.y, point2.x, point2.y)
218
+ @point = point2
219
+ self
220
+ end
221
+
222
+ def rectangle(x1, y1, x2, y2, filled = false)
223
+ (filled ? FilledRectangle : Rectangle).new(point(x1, y1),
224
+ point(x2, y2)).draw(@image, filled ? fill_pixel : line_pixel)
225
+ end
226
+
227
+ def polygon(points, filled = false, open = false)
228
+ points = points.map { |(x, y)| point(x, y) }
229
+ if filled
230
+ FilledPolygon.new(points).draw(@image, fill_pixel)
231
+ else
232
+ (open ? OpenPolygon : Polygon).new(points).draw(@image, line_pixel)
233
+ end
234
+ end
235
+
236
+ def text(string, angle = 0.0)
237
+ Text.new(get_font, @point, angle, string).draw(@image, pixel)
238
+ end
239
+
240
+ def text_circle(top, bottom, radius, text_radius, fill_portion)
241
+ TextCircle.new(get_font, @point, radius, text_radius, fill_portion,
242
+ top, bottom).draw(@image, pixel)
243
+ end
244
+
245
+ private
246
+
247
+ def get_font
248
+ raise NoFontSelectedError, 'No font selected' unless @font
249
+ @font
250
+ end
251
+
252
+ def pixel
253
+ raise NoColorSelectedError, 'No drawing color selected' unless @pixel
254
+ @pixel
255
+ end
256
+
257
+ def line_pixel
258
+ if @style && @brush
259
+ STYLED_BRUSHED
260
+ elsif @style
261
+ STYLED
262
+ elsif @brush
263
+ BRUSHED
264
+ elsif anti_aliasing?
265
+ if @dont_blend
266
+ SYM[:gdImageSetAntiAliasedDontBlend].call(@image.image_ptr,
267
+ pixel, @dont_blend)
268
+ else
269
+ SYM[:gdImageSetAntiAliased].call(@image.image_ptr, pixel)
270
+ end
271
+ ANTI_ALIASED
272
+ else
273
+ pixel
274
+ end
275
+ end
276
+
277
+ def fill_pixel
278
+ @tile ? TILED : pixel
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,236 @@
1
+ #
2
+ # Ruby/GD2 -- Ruby binding for gd 2 graphics library
3
+ #
4
+ # Copyright © 2005 Robert Leslie
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
+ module GD2
24
+ #
25
+ # = Description
26
+ #
27
+ # Color objects hold the red, green, blue, and alpha components for a pixel
28
+ # color. Color objects may also be linked to a particular palette index
29
+ # associated with an image.
30
+ #
31
+ # == Creating
32
+ #
33
+ # Color objects are created by specifying the individual components:
34
+ #
35
+ # red = Color[1.0, 0.0, 0.0]
36
+ # green = Color[0.0, 1.0, 0.0]
37
+ # blue = Color[0.0, 0.0, 1.0]
38
+ #
39
+ # transparent_yellow = Color[1.0, 1.0, 0.0, 0.5]
40
+ #
41
+ # The components may be specified as a percentage, as an explicit value
42
+ # between 0..RGB_MAX or 0..ALPHA_MAX, or as another color from which the
43
+ # associated component will be extracted.
44
+ #
45
+ class Color
46
+ attr_reader :rgba #:nodoc:
47
+
48
+ # The palette index of this color, if associated with an image palette
49
+ attr_reader :index
50
+
51
+ # The palette of this color, if associated with an image palette
52
+ attr_reader :palette
53
+
54
+ alias to_i rgba
55
+
56
+ def self.normalize(value, max, component = nil) #:nodoc:
57
+ case value
58
+ when Integer
59
+ (value < 0) ? 0 : (value > max) ? max : value
60
+ when Float
61
+ normalize((value * max).round, max, component)
62
+ when Color
63
+ value.send(component)
64
+ else
65
+ return normalize(value.to_i, max) if value.respond_to?(:to_i)
66
+ raise TypeError
67
+ end
68
+ end
69
+
70
+ def self.pack(r, g, b, a) #:nodoc:
71
+ (a << 24) + (r << 16) + (g << 8) + b
72
+ end
73
+
74
+ class << self
75
+ alias [] new
76
+ end
77
+
78
+ # Create a new Color object with the given component values.
79
+ def initialize(r, g, b, a = ALPHA_OPAQUE)
80
+ r = self.class.normalize(r, RGB_MAX, :red)
81
+ g = self.class.normalize(g, RGB_MAX, :green)
82
+ b = self.class.normalize(b, RGB_MAX, :blue)
83
+ a = self.class.normalize(a, ALPHA_MAX, :alpha)
84
+
85
+ init_with_rgba(self.class.pack(r, g, b, a))
86
+ end
87
+
88
+ def self.new_from_rgba(rgba) #:nodoc:
89
+ allocate.init_with_rgba(rgba)
90
+ end
91
+
92
+ def self.new_from_palette(r, g, b, a, index, palette) #:nodoc:
93
+ allocate.init_with_rgba(pack(r, g, b, a), index, palette)
94
+ end
95
+
96
+ def init_with_rgba(rgba, index = nil, palette = nil) #:nodoc:
97
+ @rgba = rgba
98
+ @index = index
99
+ @palette = palette
100
+ self
101
+ end
102
+
103
+ BLACK = Color[0.0, 0.0, 0.0].freeze
104
+ WHITE = Color[1.0, 1.0, 1.0].freeze
105
+ TRANSPARENT = Color[0.0, 0.0, 0.0, ALPHA_TRANSPARENT].freeze
106
+
107
+ # Return *true* if this color is associated with the specified palette,
108
+ # or with any palette if *nil* is given.
109
+ def from_palette?(palette = nil)
110
+ @palette && @index && (palette.nil? || palette.equal?(@palette))
111
+ end
112
+
113
+ # Return a string description of this color.
114
+ def to_s
115
+ s = 'RGB'
116
+ s += "A" if alpha != ALPHA_OPAQUE
117
+ s += "[#{@index}]" if @index
118
+ s += '#' + [red, green, blue].map { |e| '%02X' % e }.join('')
119
+ s += '%02X' % alpha if alpha != ALPHA_OPAQUE
120
+ s
121
+ end
122
+
123
+ def inspect #:nodoc:
124
+ "<#{to_s}>"
125
+ end
126
+
127
+ # Compare this color with another color. Returns *true* if the associated
128
+ # red, green, blue, and alpha components are identical.
129
+ def ==(other)
130
+ rgba == other.rgba
131
+ end
132
+
133
+ # Return *true* if this color is visually identical to another color.
134
+ def ===(other)
135
+ self == other || (self.transparent? && other.transparent?)
136
+ end
137
+
138
+ # Compare this color with another color in a manner that takes into account
139
+ # palette identities.
140
+ def eql?(other)
141
+ self == other &&
142
+ (palette.nil? || other.palette.nil? ||
143
+ (palette == other.palette && index == other.index))
144
+ end
145
+
146
+ def hash #:nodoc:
147
+ rgba.hash
148
+ end
149
+
150
+ def rgba=(value) #:nodoc:
151
+ @rgba = value
152
+ @palette[@index] = self if from_palette?
153
+ end
154
+
155
+ # Return the red component of this color (0..RGB_MAX).
156
+ def red
157
+ (rgba & 0xFF0000) >> 16
158
+ end
159
+ alias r red
160
+
161
+ # Modify the red component of this color. If this color is associated
162
+ # with a palette entry, this also modifies the palette.
163
+ def red=(value)
164
+ self.rgba = (rgba & ~0xFF0000) |
165
+ (self.class.normalize(value, RGB_MAX, :red) << 16)
166
+ end
167
+ alias r= red=
168
+
169
+ # Return the green component of this color (0..RGB_MAX).
170
+ def green
171
+ (rgba & 0x00FF00) >> 8
172
+ end
173
+ alias g green
174
+
175
+ # Modify the green component of this color. If this color is associated
176
+ # with a palette entry, this also modifies the palette.
177
+ def green=(value)
178
+ self.rgba = (rgba & ~0x00FF00) |
179
+ (self.class.normalize(value, RGB_MAX, :green) << 8)
180
+ end
181
+ alias g= green=
182
+
183
+ # Return the blue component of this color (0..RGB_MAX).
184
+ def blue
185
+ rgba & 0x0000FF
186
+ end
187
+ alias b blue
188
+
189
+ # Modify the blue component of this color. If this color is associated
190
+ # with a palette entry, this also modifies the palette.
191
+ def blue=(value)
192
+ self.rgba = (rgba & ~0x0000FF) |
193
+ self.class.normalize(value, RGB_MAX, :blue)
194
+ end
195
+ alias b= blue=
196
+
197
+ # Return the alpha component of this color (0..ALPHA_MAX).
198
+ def alpha
199
+ (rgba & 0x7F000000) >> 24
200
+ end
201
+ alias a alpha
202
+
203
+ # Modify the alpha component of this color. If this color is associated
204
+ # with a palette entry, this also modifies the palette.
205
+ def alpha=(value)
206
+ self.rgba = (rgba & ~0xFF000000) |
207
+ (self.class.normalize(value, ALPHA_MAX, :alpha) << 24)
208
+ end
209
+ alias a= alpha=
210
+
211
+ # Alpha blend this color with the given color. If this color is associated
212
+ # with a palette entry, this also modifies the palette.
213
+ def alpha_blend!(other)
214
+ self.rgba = SYM[:gdAlphaBlend].call(rgba, other.rgba)[0]
215
+ self
216
+ end
217
+ alias << alpha_blend!
218
+
219
+ # Like Color#alpha_blend! except returns a new Color without modifying
220
+ # the receiver.
221
+ def alpha_blend(other)
222
+ dup.alpha_blend!(other)
223
+ end
224
+
225
+ # Return *true* if the alpha channel of this color is completely
226
+ # transparent.
227
+ def transparent?
228
+ alpha == ALPHA_TRANSPARENT
229
+ end
230
+
231
+ # Return *true* if the alpha channel of this color is completely opaque.
232
+ def opaque?
233
+ alpha == ALPHA_OPAQUE
234
+ end
235
+ end
236
+ end