gd2 1.0

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