sandofsky-gd2 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/COPYRIGHT +18 -0
- data/README +13 -0
- data/Rakefile +32 -0
- data/lib/gd2.rb +224 -0
- data/lib/gd2/canvas.rb +422 -0
- data/lib/gd2/color.rb +236 -0
- data/lib/gd2/font.rb +343 -0
- data/lib/gd2/image.rb +781 -0
- data/lib/gd2/palette.rb +253 -0
- data/test/image.rb +22 -0
- metadata +68 -0
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
|
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
|
+
SYM[:gdImageLine].call(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
|
+
SYM[draw_sym].call(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
|
+
SYM[draw_sym].call(image.image_ptr, @points.map { |point|
|
95
|
+
point.coordinates.pack('i_i_')
|
96
|
+
}.join('').to_ptr, @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
|
+
SYM[:gdImageArc].call(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
|
+
SYM[:gdImageFilledArc].call(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
|
+
SYM[:gdImageArc].call(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
|
+
SYM[:gdImageFilledEllipse].call(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
|
+
SYM[:gdImageSetThickness].call(@image.image_ptr, @thickness = thickness)
|
244
|
+
end
|
245
|
+
|
246
|
+
def style=(ary)
|
247
|
+
if @style = ary
|
248
|
+
SYM[:gdImageSetStyle].call(@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
|
+
SYM[:gdImageSetBrush].call(@image.image_ptr, image.image_ptr)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def tile=(image)
|
262
|
+
if @tile = image
|
263
|
+
SYM[:gdImageSetTile].call(@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
|
+
SYM[:gdImageFill].call(@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
|
+
SYM[:gdImageFillToBorder].call(@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
|
+
SYM[:gdImageSetAntiAliasedDontBlend].call(@image.image_ptr,
|
408
|
+
pixel, @dont_blend)
|
409
|
+
else
|
410
|
+
SYM[:gdImageSetAntiAliased].call(@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
|
data/lib/gd2/color.rb
ADDED
@@ -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
|
+
other.kind_of?(Color) && 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
|