png 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +18 -0
- data/Manifest.txt +6 -1
- data/README.txt +65 -0
- data/Rakefile +10 -49
- data/example/lines.rb +4 -3
- data/example/profile.rb +16 -0
- data/example/profile_lines.rb +37 -0
- data/lib/png.rb +296 -68
- data/lib/png/pie.rb +47 -0
- data/test/test_png.rb +423 -0
- metadata +36 -13
- data/README +0 -28
data/History.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
*** 1.1.0 / 2007-03-26
|
2
|
+
|
3
|
+
+ 4 major enhancements:
|
4
|
+
+ Fixed and incorporated Dominik Barathon's optimizations.
|
5
|
+
+ Wrote inline methods for png_crc and png_join. Now about 15x faster overall.
|
6
|
+
+ Basic PNG loading.
|
7
|
+
+ Reoriented x/y origin to bottom left. This will break things!
|
8
|
+
+ 3 minor enhancements:
|
9
|
+
+ Awesome ascii art patches from Tom Werner: Canvas#inpsect, Canvas#to_s, Color#to_ascii.
|
10
|
+
+ Switched to Hoe.
|
11
|
+
+ PNG.pie_chart from png/pie.
|
12
|
+
+ 1 bug fix:
|
13
|
+
+ Fixed bug in PNG::Canvas#each.
|
14
|
+
|
15
|
+
*** 1.0.0 / 2006-09-31
|
16
|
+
|
17
|
+
+ Birthday!
|
18
|
+
|
data/Manifest.txt
CHANGED
data/README.txt
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
= PNG
|
2
|
+
|
3
|
+
* http://seattlerb.rubyforge.org/
|
4
|
+
|
5
|
+
== DESCRIPTION
|
6
|
+
|
7
|
+
PNG is an almost-pure-ruby PNG library. It lets you write a PNG
|
8
|
+
without any C libraries.
|
9
|
+
|
10
|
+
== FEATURES
|
11
|
+
|
12
|
+
* Very simple interface.
|
13
|
+
* Outputs simple PNG files with ease.
|
14
|
+
* Basic PNG reader as well (someday it might do compositing and the like!).
|
15
|
+
* Almost pure ruby, does require a compiler.
|
16
|
+
|
17
|
+
== SYNOPSYS
|
18
|
+
|
19
|
+
require 'png'
|
20
|
+
|
21
|
+
canvas = PNG::Canvas.new 200, 200
|
22
|
+
|
23
|
+
# Set a point to a color
|
24
|
+
canvas[100, 100] = PNG::Color::Black
|
25
|
+
|
26
|
+
# draw an anti-aliased line
|
27
|
+
canvas.line 50, 50, 100, 50, PNG::Color::Blue
|
28
|
+
|
29
|
+
png = PNG.new canvas
|
30
|
+
png.save 'blah.png'
|
31
|
+
|
32
|
+
== REQUIREMENTS
|
33
|
+
|
34
|
+
+ C compiler
|
35
|
+
+ RubyInline
|
36
|
+
+ Hoe
|
37
|
+
|
38
|
+
== INSTALL
|
39
|
+
|
40
|
+
+ sudo gem install -y png
|
41
|
+
|
42
|
+
== LICENSE
|
43
|
+
|
44
|
+
(The MIT License)
|
45
|
+
|
46
|
+
Copyright (c) 2006-2007 Ryan Davis, Eric Hodel, Zen Spider Software
|
47
|
+
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
49
|
+
a copy of this software and associated documentation files (the
|
50
|
+
"Software"), to deal in the Software without restriction, including
|
51
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
52
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
53
|
+
permit persons to whom the Software is furnished to do so, subject to
|
54
|
+
the following conditions:
|
55
|
+
|
56
|
+
The above copyright notice and this permission notice shall be
|
57
|
+
included in all copies or substantial portions of the Software.
|
58
|
+
|
59
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
60
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
61
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
62
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
63
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
64
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
65
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,56 +1,17 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'rake/testtask'
|
4
|
-
require 'rake/rdoctask'
|
5
|
-
require 'rake/gempackagetask'
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/png.rb'
|
6
3
|
|
7
|
-
|
4
|
+
Hoe.new 'png', PNG::VERSION do |s|
|
5
|
+
s.rubyforge_name = 'seattlerb'
|
6
|
+
s.author = ['Ryan Davis', 'Eric Hodel']
|
7
|
+
s.email = 'support@zenspider.com'
|
8
8
|
|
9
|
-
|
10
|
-
s.
|
11
|
-
s.version = '1.0.0'
|
12
|
-
s.summary = 'A pure ruby PNG library'
|
13
|
-
s.description = 'png allows you to write a PNG file without any C libraries.'
|
14
|
-
s.author = 'Ryan Davis'
|
15
|
-
s.email = 'ryand-ruby@zenspider.com'
|
9
|
+
s.summary = 'An almost-pure-ruby PNG library'
|
10
|
+
s.description = s.paragraphs_of('README.txt', 3..7).join("\n\n")
|
16
11
|
|
17
|
-
s.
|
18
|
-
s.files = File.read('Manifest.txt').split($/)
|
19
|
-
s.require_path = 'lib'
|
20
|
-
end
|
21
|
-
|
22
|
-
desc 'Run tests'
|
23
|
-
task :default => [ :test ]
|
24
|
-
|
25
|
-
Rake::TestTask.new('test') do |t|
|
26
|
-
t.libs << 'test'
|
27
|
-
t.pattern = 'test/test_*.rb'
|
28
|
-
t.verbose = true
|
29
|
-
end
|
30
|
-
|
31
|
-
desc 'Update Manifest.txt'
|
32
|
-
task :update_manifest do
|
33
|
-
sh "find . -type f | sed -e 's%./%%' | egrep -v 'svn|swp|~' | egrep -v '^(doc|pkg)/' | sort > Manifest.txt"
|
34
|
-
end
|
35
|
-
|
36
|
-
desc 'Generate RDoc'
|
37
|
-
Rake::RDocTask.new :rdoc do |rd|
|
38
|
-
rd.rdoc_dir = 'doc'
|
39
|
-
rd.rdoc_files.add 'lib', 'README'
|
40
|
-
rd.main = 'README'
|
41
|
-
rd.options << '-d' if `which dot` =~ /\/dot/
|
42
|
-
rd.options << '-t png'
|
43
|
-
end
|
12
|
+
s.changes = s.paragraphs_of('History.txt', 0..1).join("\n\n")
|
44
13
|
|
45
|
-
|
46
|
-
Rake::GemPackageTask.new spec do |pkg|
|
47
|
-
pkg.need_tar = true
|
14
|
+
s.extra_deps << ['RubyInline', '>= 3.5.0']
|
48
15
|
end
|
49
16
|
|
50
|
-
desc 'Clean up'
|
51
|
-
task :clean => [ :clobber_rdoc, :clobber_package ]
|
52
|
-
|
53
|
-
desc 'Clean up'
|
54
|
-
task :clobber => [ :clean ]
|
55
|
-
|
56
17
|
# vim: syntax=Ruby
|
data/example/lines.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'png'
|
4
4
|
|
5
|
-
canvas = PNG::Canvas.new 1024, 1024, PNG::Color::
|
5
|
+
canvas = PNG::Canvas.new 1024, 1024, PNG::Color::White
|
6
6
|
|
7
7
|
#canvas.each do |x, y|
|
8
8
|
# case x
|
@@ -17,9 +17,10 @@ canvas = PNG::Canvas.new 1024, 1024, PNG::Color::Black
|
|
17
17
|
canvas.line 50, 50, 100, 50, PNG::Color::Blue
|
18
18
|
canvas.line 50, 50, 50, 100, PNG::Color::Blue
|
19
19
|
canvas.line 100, 50, 150, 100, PNG::Color::Blue
|
20
|
-
canvas.line 100, 50, 125, 100, PNG::Color::Green
|
21
|
-
canvas.line 100, 50, 200, 75, PNG::Color::Green
|
20
|
+
canvas.line 100, 50, 125, 100, PNG::Color::Green
|
21
|
+
canvas.line 100, 50, 200, 75, PNG::Color::Green
|
22
22
|
canvas.line 0, 200, 200, 0, PNG::Color::Black
|
23
|
+
canvas.line 0, 200, 150, 0, PNG::Color::Red
|
23
24
|
|
24
25
|
png = PNG.new canvas
|
25
26
|
png.save 'blah.png'
|
data/example/profile.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'png'
|
2
|
+
|
3
|
+
class PNGProfileLine
|
4
|
+
|
5
|
+
COLORS = [
|
6
|
+
PNG::Color::Red,
|
7
|
+
PNG::Color::Orange,
|
8
|
+
PNG::Color::Yellow,
|
9
|
+
PNG::Color::Green,
|
10
|
+
PNG::Color::Blue,
|
11
|
+
PNG::Color::Purple,
|
12
|
+
]
|
13
|
+
|
14
|
+
def draw
|
15
|
+
line = 0
|
16
|
+
canvas = PNG::Canvas.new 100, 100
|
17
|
+
|
18
|
+
0.step 99, 10 do |x|
|
19
|
+
canvas.line x, 0, 99 - x, 99, COLORS[line % 6]
|
20
|
+
line += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
0.step 99, 10 do |y|
|
24
|
+
canvas.line 0, y, 99, y, COLORS[line % 6]
|
25
|
+
line += 1
|
26
|
+
end
|
27
|
+
|
28
|
+
canvas
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
ppl = PNGProfileLine.new
|
34
|
+
|
35
|
+
5.times do ppl.draw end
|
36
|
+
#PNG.new(ppl.draw).save 'x.png'
|
37
|
+
|
data/lib/png.rb
CHANGED
@@ -1,33 +1,56 @@
|
|
1
|
+
begin; require 'rubygems'; rescue LoadError; end
|
1
2
|
require 'zlib'
|
3
|
+
require 'inline'
|
2
4
|
|
3
|
-
class String # :nodoc:
|
5
|
+
class String # :nodoc: # ZenTest SKIP
|
4
6
|
|
5
7
|
##
|
6
8
|
# Calculates a CRC using the algorithm in the PNG specification.
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
c = (c & 1 == 1) ? 0xedb88320 ^ (c >> 1) : c >> 1
|
15
|
-
end
|
16
|
-
@@crc[n] = c
|
17
|
-
end
|
10
|
+
inline do |builder|
|
11
|
+
if RUBY_VERSION < "1.8.6" then
|
12
|
+
builder.prefix <<-EOM
|
13
|
+
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
14
|
+
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
15
|
+
EOM
|
18
16
|
end
|
19
17
|
|
20
|
-
c
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
builder.c <<-EOM
|
19
|
+
unsigned long png_crc() {
|
20
|
+
static unsigned long crc[256];
|
21
|
+
static char crc_table_computed = 0;
|
22
|
+
|
23
|
+
if (! crc_table_computed) {
|
24
|
+
unsigned long c;
|
25
|
+
int n, k;
|
26
|
+
|
27
|
+
for (n = 0; n < 256; n++) {
|
28
|
+
c = (unsigned long) n;
|
29
|
+
for (k = 0; k < 8; k++) {
|
30
|
+
c = (c & 1) ? 0xedb88320L ^ (c >> 1) : c >> 1;
|
31
|
+
}
|
32
|
+
crc[n] = c;
|
33
|
+
}
|
34
|
+
crc_table_computed = 1;
|
35
|
+
}
|
36
|
+
|
37
|
+
unsigned long c = 0xffffffff;
|
38
|
+
unsigned len = RSTRING_LEN(self);
|
39
|
+
char * s = StringValuePtr(self);
|
40
|
+
unsigned i;
|
41
|
+
|
42
|
+
for (i = 0; i < len; i++) {
|
43
|
+
c = crc[(c ^ s[i]) & 0xff] ^ (c >> 8);
|
44
|
+
}
|
45
|
+
|
46
|
+
return c ^ 0xffffffff;
|
47
|
+
}
|
48
|
+
EOM
|
25
49
|
end
|
26
|
-
|
27
50
|
end
|
28
51
|
|
29
52
|
##
|
30
|
-
#
|
53
|
+
# An almost-pure-ruby Portable Network Graphics (PNG) writer.
|
31
54
|
#
|
32
55
|
# http://www.libpng.org/pub/png/spec/1.2/
|
33
56
|
#
|
@@ -42,7 +65,7 @@ end
|
|
42
65
|
# = Example
|
43
66
|
#
|
44
67
|
# require 'png'
|
45
|
-
#
|
68
|
+
#
|
46
69
|
# canvas = PNG::Canvas.new 200, 200
|
47
70
|
# canvas[100, 100] = PNG::Color::Black
|
48
71
|
# canvas.line 50, 50, 100, 50, PNG::Color::Blue
|
@@ -51,6 +74,46 @@ end
|
|
51
74
|
|
52
75
|
class PNG
|
53
76
|
|
77
|
+
VERSION = '1.1.0'
|
78
|
+
SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10].pack("C*")
|
79
|
+
|
80
|
+
inline do |builder|
|
81
|
+
if RUBY_VERSION < "1.8.6" then
|
82
|
+
builder.prefix <<-EOM
|
83
|
+
#define RARRAY_PTR(s) (RARRAY(s)->ptr)
|
84
|
+
#define RARRAY_LEN(s) (RARRAY(s)->len)
|
85
|
+
EOM
|
86
|
+
end
|
87
|
+
|
88
|
+
# C equivalent of:
|
89
|
+
# @data.map { |row| "\0" + row.map { |p| p.values }.join }.join
|
90
|
+
builder.c <<-EOM
|
91
|
+
VALUE png_join() {
|
92
|
+
int i, j;
|
93
|
+
VALUE data = rb_iv_get(self, "@data");
|
94
|
+
unsigned int data_len = RARRAY_LEN(data);
|
95
|
+
unsigned int row_len = RARRAY_LEN(RARRAY_PTR(data)[0]);
|
96
|
+
unsigned long size = data_len * (1 + (row_len * 4));
|
97
|
+
char * result = malloc(size);
|
98
|
+
unsigned long idx = 0;
|
99
|
+
for (i = 0; i < data_len; i++) {
|
100
|
+
VALUE row = RARRAY_PTR(data)[i];
|
101
|
+
result[idx++] = 0;
|
102
|
+
for (j = 0; j < row_len; j++) {
|
103
|
+
VALUE color = RARRAY_PTR(row)[j];
|
104
|
+
VALUE values = rb_iv_get(color, "@values");
|
105
|
+
char * value = StringValuePtr(values);
|
106
|
+
result[idx++] = value[0];
|
107
|
+
result[idx++] = value[1];
|
108
|
+
result[idx++] = value[2];
|
109
|
+
result[idx++] = value[3];
|
110
|
+
}
|
111
|
+
}
|
112
|
+
return rb_str_new(result, size);
|
113
|
+
}
|
114
|
+
EOM
|
115
|
+
end
|
116
|
+
|
54
117
|
##
|
55
118
|
# Creates a PNG chunk of type +type+ that contains +data+.
|
56
119
|
|
@@ -58,6 +121,116 @@ class PNG
|
|
58
121
|
[data.size, type, data, (type + data).png_crc].pack("Na*a*N")
|
59
122
|
end
|
60
123
|
|
124
|
+
def self.load(png)
|
125
|
+
png = png.dup
|
126
|
+
signature = png.slice! 0, 8
|
127
|
+
raise ArgumentError, 'Invalid PNG signature' unless signature == SIGNATURE
|
128
|
+
|
129
|
+
type, data = read_chunk png
|
130
|
+
|
131
|
+
raise ArgumentError, 'Invalid PNG, no IHDR chunk' unless type == 'IHDR'
|
132
|
+
|
133
|
+
canvas = read_IHDR data
|
134
|
+
type, data = read_chunk png
|
135
|
+
read_IDAT data, canvas
|
136
|
+
type, data = read_chunk png
|
137
|
+
raise 'oh no! IEND not next? crashing and burning!' unless type == 'IEND'
|
138
|
+
|
139
|
+
new canvas
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.check_crc(type, data, crc)
|
143
|
+
return true if (type + data).png_crc == crc
|
144
|
+
raise ArgumentError, "Invalid CRC encountered in #{type} chunk"
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.paeth(a, b, c) # left, above, upper left
|
148
|
+
p = a + b - c
|
149
|
+
pa = (p - a).abs
|
150
|
+
pb = (p - b).abs
|
151
|
+
pc = (p - c).abs
|
152
|
+
|
153
|
+
return a if pa <= pb && pa <= pc
|
154
|
+
return b if pb <= pc
|
155
|
+
c
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.read_chunk(png)
|
159
|
+
size, type = png.slice!(0, 8).unpack 'Na4'
|
160
|
+
data, crc = png.slice!(0, size + 4).unpack "a#{size}N"
|
161
|
+
|
162
|
+
check_crc type, data, crc
|
163
|
+
|
164
|
+
return type, data
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.read_IDAT(data, canvas)
|
168
|
+
data = Zlib::Inflate.inflate(data).unpack 'C*'
|
169
|
+
scanline_length = 4 * canvas.width + 1 # for filter
|
170
|
+
row = 0
|
171
|
+
until data.empty? do
|
172
|
+
row_data = data.slice! 0, scanline_length
|
173
|
+
filter = row_data.shift
|
174
|
+
case filter
|
175
|
+
when 0 then # None
|
176
|
+
when 1 then # Sub
|
177
|
+
row_data.each_with_index do |byte, index|
|
178
|
+
left = index < 4 ? 0 : row_data[index - 4]
|
179
|
+
row_data[index] = (byte + left) % 256
|
180
|
+
#p [byte, left, row_data[index]]
|
181
|
+
end
|
182
|
+
when 2 then # Up
|
183
|
+
row_data.each_with_index do |byte, index|
|
184
|
+
col = index / 4
|
185
|
+
upper = row == 0 ? 0 : canvas[col, row - 1].values[index % 4]
|
186
|
+
row_data[index] = (upper + byte) % 256
|
187
|
+
end
|
188
|
+
when 3 then # Average
|
189
|
+
row_data.each_with_index do |byte, index|
|
190
|
+
col = index / 4
|
191
|
+
upper = row == 0 ? 0 : canvas[col, row - 1].values[index % 4]
|
192
|
+
left = index < 4 ? 0 : row_data[index - 4]
|
193
|
+
|
194
|
+
row_data[index] = (byte + ((left + upper)/2).floor) % 256
|
195
|
+
end
|
196
|
+
when 4 then # Paeth
|
197
|
+
left = upper = upper_left = nil
|
198
|
+
row_data.each_with_index do |byte, index|
|
199
|
+
col = index / 4
|
200
|
+
|
201
|
+
left = index < 4 ? 0 : row_data[index - 4]
|
202
|
+
if row == 0 then
|
203
|
+
upper = upper_left = 0
|
204
|
+
else
|
205
|
+
upper = canvas[col, row - 1].values[index % 4]
|
206
|
+
upper_left = col == 0 ? 0 :
|
207
|
+
canvas[col - 1, row - 1].values[index % 4]
|
208
|
+
end
|
209
|
+
|
210
|
+
paeth = paeth left, upper, upper_left
|
211
|
+
row_data[index] = (byte + paeth) % 256
|
212
|
+
#p [byte, paeth, row_data[index]]
|
213
|
+
end
|
214
|
+
else
|
215
|
+
raise ArgumentError, "Invalid filter algorithm #{filter}"
|
216
|
+
end
|
217
|
+
|
218
|
+
col = 0
|
219
|
+
row_data.each_slice 4 do |slice|
|
220
|
+
canvas[col, row] = PNG::Color.new(*slice)
|
221
|
+
col += 1
|
222
|
+
end
|
223
|
+
|
224
|
+
row += 1
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.read_IHDR(data)
|
229
|
+
width, height, *rest = data.unpack 'N2C5'
|
230
|
+
raise ArgumentError, 'unsupported PNG file' unless rest == [8, 6, 0, 0, 0]
|
231
|
+
return PNG::Canvas.new(height, width)
|
232
|
+
end
|
233
|
+
|
61
234
|
##
|
62
235
|
# Creates a new PNG object using +canvas+
|
63
236
|
|
@@ -72,15 +245,22 @@ class PNG
|
|
72
245
|
# Writes the PNG to +path+.
|
73
246
|
|
74
247
|
def save(path)
|
75
|
-
File.open
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
248
|
+
File.open path, 'wb' do |f| f.write to_blob end
|
249
|
+
end
|
250
|
+
|
251
|
+
##
|
252
|
+
# Raw PNG data
|
253
|
+
|
254
|
+
def to_blob
|
255
|
+
blob = []
|
256
|
+
|
257
|
+
blob << SIGNATURE
|
258
|
+
blob << PNG.chunk('IHDR', [@width, @height, @bits, 6, 0, 0, 0 ].pack("N2C5"))
|
259
|
+
# 0 == filter type code "none"
|
260
|
+
data = self.png_join
|
261
|
+
blob << PNG.chunk('IDAT', Zlib::Deflate.deflate(data))
|
262
|
+
blob << PNG.chunk('IEND', '')
|
263
|
+
blob.join
|
84
264
|
end
|
85
265
|
|
86
266
|
##
|
@@ -90,28 +270,42 @@ class PNG
|
|
90
270
|
|
91
271
|
attr_reader :values
|
92
272
|
|
273
|
+
def self.from str, name = nil
|
274
|
+
str = "%08x" % str if Integer === str
|
275
|
+
colors = str.scan(/[\da-f][\da-f]/i).map { |n| n.hex }
|
276
|
+
colors << name
|
277
|
+
self.new(*colors)
|
278
|
+
end
|
279
|
+
|
93
280
|
##
|
94
281
|
# Creates a new color with values +red+, +green+, +blue+, and +alpha+.
|
95
282
|
|
96
|
-
def initialize(red, green, blue, alpha)
|
97
|
-
@values = [red, green, blue, alpha]
|
283
|
+
def initialize(red, green, blue, alpha, name = nil)
|
284
|
+
@values = "%c%c%c%c" % [red, green, blue, alpha]
|
285
|
+
@name = name
|
98
286
|
end
|
99
287
|
|
100
288
|
##
|
101
289
|
# Transparent white
|
102
290
|
|
103
|
-
Background = Color.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
291
|
+
Background = Color.from 0x00000000, "Transparent"
|
292
|
+
Black = Color.from 0x000000FF, "Black"
|
293
|
+
Blue = Color.from 0x0000FFFF, "Blue"
|
294
|
+
Brown = Color.from 0x996633FF, "Brown"
|
295
|
+
Bubblegum = Color.from 0xFF66FFFF, "Bubblegum"
|
296
|
+
Cyan = Color.from 0x00FFFFFF, "Cyan"
|
297
|
+
Gray = Color.from 0x7F7F7FFF, "Gray"
|
298
|
+
Green = Color.from 0x00FF00FF, "Green"
|
299
|
+
Magenta = Color.from 0xFF00FFFF, "Magenta"
|
300
|
+
Orange = Color.from 0xFF7F00FF, "Orange"
|
301
|
+
Purple = Color.from 0x7F007FFF, "Purple"
|
302
|
+
Red = Color.from 0xFF0000FF, "Red"
|
303
|
+
White = Color.from 0xFFFFFFFF, "White"
|
304
|
+
Yellow = Color.from 0xFFFF00FF, "Yellow"
|
305
|
+
|
306
|
+
def ==(other) # :nodoc:
|
307
|
+
self.class === other and other.values == values
|
308
|
+
end
|
115
309
|
|
116
310
|
##
|
117
311
|
# Red component
|
@@ -147,13 +341,37 @@ class PNG
|
|
147
341
|
# Returns a new color with an alpha value adjusted by +i+.
|
148
342
|
|
149
343
|
def intensity(i)
|
150
|
-
return Color.new(r,b,
|
344
|
+
return Color.new(r,g,b,(a*i) >> 8)
|
151
345
|
end
|
152
346
|
|
153
347
|
def inspect # :nodoc:
|
154
|
-
|
348
|
+
if @name then
|
349
|
+
"#<%s %s>" % [self.class, @name]
|
350
|
+
else
|
351
|
+
"#<%s %02x %02x %02x %02x>" % [self.class, r, g, b, a]
|
352
|
+
end
|
155
353
|
end
|
156
354
|
|
355
|
+
##
|
356
|
+
# An ASCII representation of this color, almost suitable for making ASCII
|
357
|
+
# art!
|
358
|
+
|
359
|
+
def to_ascii
|
360
|
+
return ' ' if a == 0x00
|
361
|
+
brightness = (((r + g + b) / 3) * a) / 0xFF
|
362
|
+
return '00' if brightness >= 0xc0
|
363
|
+
return '++' if brightness >= 0x7F
|
364
|
+
return ',,' if brightness >= 0x40
|
365
|
+
return '..'
|
366
|
+
end
|
367
|
+
|
368
|
+
def to_s
|
369
|
+
if @name then
|
370
|
+
@name
|
371
|
+
else
|
372
|
+
super
|
373
|
+
end
|
374
|
+
end
|
157
375
|
end
|
158
376
|
|
159
377
|
##
|
@@ -176,39 +394,32 @@ class PNG
|
|
176
394
|
|
177
395
|
attr_reader :data
|
178
396
|
|
179
|
-
def initialize(
|
180
|
-
@height = height
|
397
|
+
def initialize(width, height, background = Color::Background)
|
181
398
|
@width = width
|
182
|
-
@
|
399
|
+
@height = height
|
400
|
+
@data = Array.new(@height) { |x| Array.new(@width, background) }
|
183
401
|
end
|
184
402
|
|
185
403
|
##
|
186
404
|
# Retrieves the color of the pixel at (+x+, +y+).
|
187
405
|
|
188
406
|
def [](x, y)
|
189
|
-
raise "bad x value #{x} >= #{@
|
190
|
-
raise "bad y value #{y} >= #{@
|
191
|
-
@data[y][x]
|
407
|
+
raise "bad x value #{x} >= #{@width}" if x >= @width
|
408
|
+
raise "bad y value #{y} >= #{@height}" if y >= @height
|
409
|
+
@data[@height-y-1][x]
|
192
410
|
end
|
193
411
|
|
194
412
|
##
|
195
413
|
# Sets the color of the pixel at (+x+, +y+) to +color+.
|
196
414
|
|
197
415
|
def []=(x, y, color)
|
198
|
-
raise "bad x value #{x} >= #{@
|
199
|
-
raise "bad y value #{y} >= #{@
|
200
|
-
@data[y][x] = color
|
416
|
+
raise "bad x value #{x} >= #{@width}" if x >= @width
|
417
|
+
raise "bad y value #{y} >= #{@height}" if y >= @height
|
418
|
+
@data[@height-y-1][x] = color
|
201
419
|
end
|
202
420
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
def each
|
207
|
-
@data.each_with_index do |row, y|
|
208
|
-
row.each_with_index do |pixel, x|
|
209
|
-
yield x, y, color
|
210
|
-
end
|
211
|
-
end
|
421
|
+
def inspect # :nodoc:
|
422
|
+
'#<%s %dx%d>' % [self.class, @width, @height]
|
212
423
|
end
|
213
424
|
|
214
425
|
##
|
@@ -224,9 +435,10 @@ class PNG
|
|
224
435
|
# http://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm
|
225
436
|
|
226
437
|
def line(x0, y0, x1, y1, color)
|
438
|
+
y0, y1, x0, x1 = y1, y0, x1, x0 if y0 > y1
|
227
439
|
dx = x1 - x0
|
228
440
|
sx = dx < 0 ? -1 : 1
|
229
|
-
dx *= sx
|
441
|
+
dx *= sx
|
230
442
|
dy = y1 - y0
|
231
443
|
|
232
444
|
# 'easy' cases
|
@@ -245,7 +457,7 @@ class PNG
|
|
245
457
|
end
|
246
458
|
|
247
459
|
if dx == dy then
|
248
|
-
|
460
|
+
x0.step(x1, sx) do |x|
|
249
461
|
point(x, y0, color)
|
250
462
|
y0 += 1
|
251
463
|
end
|
@@ -259,7 +471,7 @@ class PNG
|
|
259
471
|
e = (dx << 16) / dy
|
260
472
|
(y0...y1-1).each do |i|
|
261
473
|
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
|
262
|
-
x0 = x0 + sx if (e_acc <= e_acc_temp)
|
474
|
+
x0 = x0 + sx if (e_acc <= e_acc_temp)
|
263
475
|
w = 0xFF-(e_acc >> 8)
|
264
476
|
point(x0, y0, color.intensity(w))
|
265
477
|
y0 = y0 + 1
|
@@ -271,7 +483,7 @@ class PNG
|
|
271
483
|
|
272
484
|
# horizontal displacement
|
273
485
|
e = (dy << 16) / dx
|
274
|
-
(
|
486
|
+
(dx - 1).downto(0) do |i|
|
275
487
|
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
|
276
488
|
y0 += 1 if (e_acc <= e_acc_temp)
|
277
489
|
w = 0xFF-(e_acc >> 8)
|
@@ -282,7 +494,23 @@ class PNG
|
|
282
494
|
point(x1, y1, color)
|
283
495
|
end
|
284
496
|
|
285
|
-
|
497
|
+
##
|
498
|
+
# Returns an ASCII representation of this image
|
286
499
|
|
287
|
-
|
500
|
+
def to_s
|
501
|
+
image = []
|
502
|
+
scale = (@width / 39) + 1
|
503
|
+
|
504
|
+
@data.each_with_index do |row, x|
|
505
|
+
next if x % scale != 0
|
506
|
+
row.each_with_index do |color, y|
|
507
|
+
next if y % scale != 0
|
508
|
+
image << color.to_ascii
|
509
|
+
end
|
510
|
+
image << "\n"
|
511
|
+
end
|
288
512
|
|
513
|
+
return image.join
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
data/lib/png/pie.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
require 'png'
|
4
|
+
|
5
|
+
class PNG
|
6
|
+
FULL = 360.0
|
7
|
+
HALF = FULL / 2
|
8
|
+
|
9
|
+
def self.angle(x, y)
|
10
|
+
return 0 if x == 0 and y == 0
|
11
|
+
rad_to_deg = 180.0 / Math::PI
|
12
|
+
(Math.atan2(-y, x) * rad_to_deg + 90) % 360
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Makes a pie chart you can pass to PNG.new:
|
17
|
+
#
|
18
|
+
# png = PNG.new pie_chart(250, 0.30)
|
19
|
+
# png.save "pie.png"
|
20
|
+
# system 'open pie.png'
|
21
|
+
|
22
|
+
def self.pie_chart(diameter, pct_green,
|
23
|
+
good_color=PNG::Color::Green, bad_color=PNG::Color::Red)
|
24
|
+
diameter += 1 if diameter % 2 == 0
|
25
|
+
radius = (diameter / 2.0).to_i
|
26
|
+
pct_in_deg = FULL * pct_green
|
27
|
+
rad_to_deg = HALF / Math::PI
|
28
|
+
|
29
|
+
canvas = PNG::Canvas.new(diameter, diameter)
|
30
|
+
|
31
|
+
(-radius..radius).each do |x|
|
32
|
+
(-radius..radius).each do |y|
|
33
|
+
magnitude = Math.sqrt(x*x + y*y)
|
34
|
+
if magnitude <= radius then
|
35
|
+
angle = PNG.angle(x, y)
|
36
|
+
color = ((angle <= pct_in_deg) ? good_color : bad_color)
|
37
|
+
|
38
|
+
rx, ry = x+radius, y+radius
|
39
|
+
|
40
|
+
canvas[ rx, ry ] = color
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
canvas
|
46
|
+
end
|
47
|
+
end
|
data/test/test_png.rb
ADDED
@@ -0,0 +1,423 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'png'
|
4
|
+
require 'png/pie'
|
5
|
+
|
6
|
+
class TestPng < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@canvas = PNG::Canvas.new 5, 10, PNG::Color::White
|
10
|
+
@png = PNG.new @canvas
|
11
|
+
|
12
|
+
@IHDR_length = "\000\000\000\r"
|
13
|
+
@IHDR_crc = "\2152\317\275"
|
14
|
+
@IHDR_crc_value = @IHDR_crc.unpack('N').first
|
15
|
+
@IHDR_data = "\000\000\000\n\000\000\000\n\b\006\000\000\000"
|
16
|
+
@IHDR_chunk = "#{@IHDR_length}IHDR#{@IHDR_data}#{@IHDR_crc}"
|
17
|
+
|
18
|
+
@blob = <<-EOF.unpack('m*').first
|
19
|
+
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAKCAYAAAB8OZQwAAAAD0lEQVR4nGP4
|
20
|
+
jwUwDGVBALuJxzlQugpEAAAAAElFTkSuQmCC
|
21
|
+
EOF
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_class_check_crc
|
25
|
+
assert PNG.check_crc('IHDR', @IHDR_data, @IHDR_crc_value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_class_check_crc_exception
|
29
|
+
begin
|
30
|
+
PNG.check_crc('IHDR', @IHDR_data, @IHDR_crc_value + 1)
|
31
|
+
rescue ArgumentError => e
|
32
|
+
assert_equal "Invalid CRC encountered in IHDR chunk", e.message
|
33
|
+
else
|
34
|
+
flunk "exception wasn't raised"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_class_chunk
|
39
|
+
chunk = PNG.chunk 'IHDR', [10, 10, 8, 6, 0, 0, 0 ].pack('N2C5')
|
40
|
+
assert_equal @IHDR_chunk, chunk
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_class_chunk_empty
|
44
|
+
chunk = PNG.chunk 'IHDR'
|
45
|
+
expected = "#{0.chr * 4}IHDR#{["IHDR".png_crc].pack 'N'}"
|
46
|
+
assert_equal expected, chunk
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_to_blob
|
50
|
+
assert_equal @blob, @png.to_blob
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_save
|
54
|
+
path = "blah.png"
|
55
|
+
@png.save(path)
|
56
|
+
assert_equal @blob, File.read(path)
|
57
|
+
ensure
|
58
|
+
assert_equal 1, File.unlink(path)
|
59
|
+
end
|
60
|
+
|
61
|
+
class TestCanvas < Test::Unit::TestCase
|
62
|
+
|
63
|
+
def setup
|
64
|
+
@canvas = PNG::Canvas.new 5, 10, PNG::Color::White
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_index
|
68
|
+
assert_equal PNG::Color::White, @canvas[1, 2]
|
69
|
+
assert_same @canvas[1, 2], @canvas.data[1][2]
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_index_tall
|
73
|
+
@canvas = PNG::Canvas.new 2, 4, PNG::Color::White
|
74
|
+
@canvas[ 0, 0] = PNG::Color::Black
|
75
|
+
@canvas[ 0, 3] = PNG::Color::Background
|
76
|
+
@canvas[ 1, 0] = PNG::Color::Yellow
|
77
|
+
@canvas[ 1, 3] = PNG::Color::Blue
|
78
|
+
|
79
|
+
expected = " ,,\n0000\n0000\n..++\n"
|
80
|
+
|
81
|
+
assert_equal expected, @canvas.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_index_wide
|
85
|
+
@canvas = PNG::Canvas.new 4, 2, PNG::Color::White
|
86
|
+
@canvas[ 0, 0] = PNG::Color::Black
|
87
|
+
@canvas[ 3, 0] = PNG::Color::Background
|
88
|
+
@canvas[ 0, 1] = PNG::Color::Yellow
|
89
|
+
@canvas[ 3, 1] = PNG::Color::Blue
|
90
|
+
|
91
|
+
expected = "++0000,,\n..0000 \n"
|
92
|
+
|
93
|
+
assert_equal expected, @canvas.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_index_bad_x
|
97
|
+
begin
|
98
|
+
@canvas[6, 1]
|
99
|
+
rescue => e
|
100
|
+
assert_equal "bad x value 6 >= 5", e.message
|
101
|
+
else
|
102
|
+
flunk "didn't raise"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_index_bad_y
|
107
|
+
begin
|
108
|
+
@canvas[1, 11]
|
109
|
+
rescue => e
|
110
|
+
assert_equal "bad y value 11 >= 10", e.message
|
111
|
+
else
|
112
|
+
flunk "didn't raise"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_index_equals
|
117
|
+
@canvas[1, 2] = PNG::Color::Red
|
118
|
+
assert_equal PNG::Color::Red, @canvas[1, 2]
|
119
|
+
assert_same @canvas[1, 2], @canvas.data[7][1]
|
120
|
+
|
121
|
+
expected = "
|
122
|
+
0000000000
|
123
|
+
0000000000
|
124
|
+
0000000000
|
125
|
+
0000000000
|
126
|
+
0000000000
|
127
|
+
0000000000
|
128
|
+
0000000000
|
129
|
+
00,,000000
|
130
|
+
0000000000
|
131
|
+
0000000000".strip + "\n"
|
132
|
+
actual = @canvas.to_s
|
133
|
+
assert_equal expected, actual
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_index_equals_bad_x
|
137
|
+
begin
|
138
|
+
@canvas[6, 1] = PNG::Color::Red
|
139
|
+
rescue => e
|
140
|
+
assert_equal "bad x value 6 >= 5", e.message
|
141
|
+
else
|
142
|
+
flunk "didn't raise"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_index_equals_bad_y
|
147
|
+
begin
|
148
|
+
@canvas[1, 11] = PNG::Color::Red
|
149
|
+
rescue => e
|
150
|
+
assert_equal "bad y value 11 >= 10", e.message
|
151
|
+
else
|
152
|
+
flunk "didn't raise"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# def test_point
|
157
|
+
# raise NotImplementedError, 'Need to write test_point'
|
158
|
+
# end
|
159
|
+
|
160
|
+
def test_inspect
|
161
|
+
assert_equal "#<PNG::Canvas 5x10>", @canvas.inspect
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_point
|
165
|
+
assert_equal PNG::Color.new(0xfe, 0x00, 0xfe, 0xfe),
|
166
|
+
@canvas.point(0, 0, PNG::Color::Magenta)
|
167
|
+
# flunk "this doesn't test ANYTHING"
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_line
|
171
|
+
@canvas.line 0, 9, 4, 0, PNG::Color::Black
|
172
|
+
|
173
|
+
expected = <<-EOF
|
174
|
+
..00000000
|
175
|
+
,,00000000
|
176
|
+
00,,000000
|
177
|
+
00..000000
|
178
|
+
00++++0000
|
179
|
+
0000..0000
|
180
|
+
0000++++00
|
181
|
+
000000..00
|
182
|
+
000000,,00
|
183
|
+
00000000..
|
184
|
+
EOF
|
185
|
+
|
186
|
+
assert_equal expected, @canvas.to_s
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_positive_slope_line
|
190
|
+
@canvas.line 0, 0, 4, 9, PNG::Color::Black
|
191
|
+
|
192
|
+
expected = <<-EOF
|
193
|
+
00000000..
|
194
|
+
00000000,,
|
195
|
+
000000,,00
|
196
|
+
000000..00
|
197
|
+
0000++++00
|
198
|
+
0000..0000
|
199
|
+
00++++0000
|
200
|
+
00..000000
|
201
|
+
00,,000000
|
202
|
+
..00000000
|
203
|
+
EOF
|
204
|
+
|
205
|
+
assert_equal expected, @canvas.to_s
|
206
|
+
end
|
207
|
+
|
208
|
+
def util_ascii_art(width, height)
|
209
|
+
(("0" * width * 2) + "\n") * height
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_to_s_normal
|
213
|
+
@canvas = PNG::Canvas.new 5, 10, PNG::Color::White
|
214
|
+
expected = util_ascii_art(5, 10)
|
215
|
+
assert_equal expected, @canvas.to_s
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_to_s_wide
|
219
|
+
@canvas = PNG::Canvas.new 250, 10, PNG::Color::White
|
220
|
+
expected = util_ascii_art(36, 2) # scaled
|
221
|
+
assert_equal expected, @canvas.to_s
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_to_s_tall
|
225
|
+
@canvas = PNG::Canvas.new 10, 250, PNG::Color::White
|
226
|
+
expected = util_ascii_art(10, 250)
|
227
|
+
assert_equal expected, @canvas.to_s
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_to_s_huge
|
231
|
+
@canvas = PNG::Canvas.new 250, 250, PNG::Color::White
|
232
|
+
expected = util_ascii_art(36, 36) # scaled
|
233
|
+
assert_equal expected, @canvas.to_s
|
234
|
+
end
|
235
|
+
|
236
|
+
# def test_class_read_chunk
|
237
|
+
# type, data = PNG.read_chunk @IHDR_chunk
|
238
|
+
|
239
|
+
# assert_equal 'IHDR', type
|
240
|
+
# assert_equal @IHDR_data, data
|
241
|
+
# end
|
242
|
+
|
243
|
+
# def test_class_read_IDAT
|
244
|
+
# canvas = PNG::Canvas.new 10, 10, PNG::Color::White
|
245
|
+
|
246
|
+
# data = "x\332c\370O$`\030UH_\205\000#\373\216\200"
|
247
|
+
|
248
|
+
# PNG.read_IDAT data, canvas
|
249
|
+
|
250
|
+
# assert_equal @blob, PNG.new(canvas).to_blob
|
251
|
+
# end
|
252
|
+
|
253
|
+
# def test_class_read_IHDR
|
254
|
+
# canvas = PNG.read_IHDR @IHDR_data
|
255
|
+
# assert_equal 10, canvas.width
|
256
|
+
# assert_equal 10, canvas.height
|
257
|
+
# end
|
258
|
+
end
|
259
|
+
|
260
|
+
class TestPng::TestColor < Test::Unit::TestCase
|
261
|
+
def setup
|
262
|
+
@color = PNG::Color.new 0x01, 0x02, 0x03, 0x04
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_class_from_str
|
266
|
+
@color = PNG::Color.from "0x01020304"
|
267
|
+
test_r
|
268
|
+
test_g
|
269
|
+
test_b
|
270
|
+
test_a
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_class_from_int
|
274
|
+
@color = PNG::Color.from 0x01020304
|
275
|
+
test_r
|
276
|
+
test_g
|
277
|
+
test_b
|
278
|
+
test_a
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_r
|
282
|
+
assert_equal 0x01, @color.r
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_g
|
286
|
+
assert_equal 0x02, @color.g
|
287
|
+
end
|
288
|
+
|
289
|
+
def test_b
|
290
|
+
assert_equal 0x03, @color.b
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_a
|
294
|
+
assert_equal 0x04, @color.a
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_blend
|
298
|
+
c1 = @color
|
299
|
+
c2 = PNG::Color.new 0xFF, 0xFE, 0xFD, 0xFC
|
300
|
+
|
301
|
+
assert_equal PNG::Color.new(0xfb, 0xfa, 0xf9, 0xf8), c1.blend(c2)
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_intensity
|
305
|
+
assert_equal PNG::Color.new(0x01, 0x02, 0x03, 0x3c), @color.intensity(0xf00)
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_inspect
|
309
|
+
assert_equal "#<PNG::Color 01 02 03 04>", @color.inspect
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_inspect_name
|
313
|
+
assert_equal "#<PNG::Color Red>", PNG::Color::Red.inspect
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_to_ascii
|
317
|
+
assert_equal '00', PNG::Color::White.to_ascii, "white"
|
318
|
+
assert_equal '++', PNG::Color::Yellow.to_ascii, "yellow"
|
319
|
+
assert_equal ',,', PNG::Color::Red.to_ascii, "red"
|
320
|
+
assert_equal '..', PNG::Color::Black.to_ascii, "black"
|
321
|
+
assert_equal ' ', PNG::Color::Background.to_ascii, "background"
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_to_ascii_alpha
|
325
|
+
assert_equal '00', PNG::Color.new(255,255,255,255).to_ascii
|
326
|
+
assert_equal '00', PNG::Color.new(255,255,255,192).to_ascii
|
327
|
+
assert_equal '++', PNG::Color.new(255,255,255,191).to_ascii
|
328
|
+
assert_equal '++', PNG::Color.new(255,255,255,127).to_ascii
|
329
|
+
assert_equal ',,', PNG::Color.new(255,255,255,126).to_ascii
|
330
|
+
assert_equal ',,', PNG::Color.new(255,255,255, 64).to_ascii
|
331
|
+
assert_equal '..', PNG::Color.new(255,255,255, 63).to_ascii
|
332
|
+
assert_equal '..', PNG::Color.new(255,255,255, 1).to_ascii
|
333
|
+
assert_equal ' ', PNG::Color.new(255,255,255, 0).to_ascii
|
334
|
+
end
|
335
|
+
|
336
|
+
def test_to_s_name
|
337
|
+
assert_equal 'Red', PNG::Color::Red.to_s
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_to_s
|
341
|
+
obj = PNG::Color.new(255,255,255, 0)
|
342
|
+
assert_equal '#<PNG::Color:0xXXXXXX>', obj.to_s.sub(/0x[0-9a-f]+/, '0xXXXXXX')
|
343
|
+
end
|
344
|
+
|
345
|
+
# def test_values
|
346
|
+
# raise NotImplementedError, 'Need to write test_values'
|
347
|
+
# end
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
class TestPng::TestPie < Test::Unit::TestCase
|
353
|
+
def setup
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
def test_pie_chart_odd
|
358
|
+
expected =
|
359
|
+
[" .. ",
|
360
|
+
" ,,,,,,........ ",
|
361
|
+
" ,,,,,,,,.......... ",
|
362
|
+
" ,,,,,,,,.......... ",
|
363
|
+
" ,,,,,,,,.......... ",
|
364
|
+
",,,,,,,,,,............",
|
365
|
+
" ,,,,,,,,,,,,,,,,,, ",
|
366
|
+
" ,,,,,,,,,,,,,,,,,, ",
|
367
|
+
" ,,,,,,,,,,,,,,,,,, ",
|
368
|
+
" ,,,,,,,,,,,,,, ",
|
369
|
+
" ,, ",
|
370
|
+
nil].join("\n")
|
371
|
+
|
372
|
+
actual = PNG::pie_chart(11, 0.25, PNG::Color::Black, PNG::Color::Green)
|
373
|
+
assert_equal expected, actual.to_s
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_pie_chart_even
|
377
|
+
expected =
|
378
|
+
[" .. ",
|
379
|
+
" ,,,,,,........ ",
|
380
|
+
" ,,,,,,,,.......... ",
|
381
|
+
" ,,,,,,,,.......... ",
|
382
|
+
" ,,,,,,,,.......... ",
|
383
|
+
",,,,,,,,,,............",
|
384
|
+
" ,,,,,,,,,,,,,,,,,, ",
|
385
|
+
" ,,,,,,,,,,,,,,,,,, ",
|
386
|
+
" ,,,,,,,,,,,,,,,,,, ",
|
387
|
+
" ,,,,,,,,,,,,,, ",
|
388
|
+
" ,, ",
|
389
|
+
nil].join("\n")
|
390
|
+
|
391
|
+
actual = PNG::pie_chart(10, 0.25, PNG::Color::Black, PNG::Color::Green)
|
392
|
+
assert_equal expected, actual.to_s
|
393
|
+
end
|
394
|
+
|
395
|
+
def util_angle(expect, x, y)
|
396
|
+
actual = PNG.angle(x, y)
|
397
|
+
case expect
|
398
|
+
when Integer then
|
399
|
+
assert_equal(expect, actual,
|
400
|
+
"[#{x}, #{y}] should be == #{expect}, was #{actual}")
|
401
|
+
else
|
402
|
+
assert_in_delta(expect, actual, 0.5)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_math_is_hard_lets_go_shopping
|
407
|
+
util_angle 0, 0, 0
|
408
|
+
(25..500).step(25) do |n|
|
409
|
+
util_angle 0, 0, n
|
410
|
+
util_angle 90, n, 0
|
411
|
+
util_angle 180, 0, -n
|
412
|
+
util_angle 270, -n, 0
|
413
|
+
end
|
414
|
+
|
415
|
+
util_angle 359.5, -1, 250
|
416
|
+
util_angle 0.0, 0, 250
|
417
|
+
util_angle 0.5, 1, 250
|
418
|
+
|
419
|
+
util_angle 89.5, 250, 1
|
420
|
+
util_angle 90.0, 250, 0
|
421
|
+
util_angle 90.5, 250, -1
|
422
|
+
end
|
423
|
+
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.
|
2
|
+
rubygems_version: 0.9.0.9
|
3
3
|
specification_version: 1
|
4
4
|
name: png
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date:
|
8
|
-
summary:
|
6
|
+
version: 1.1.0
|
7
|
+
date: 2007-03-26 00:00:00 -07:00
|
8
|
+
summary: An almost-pure-ruby PNG library
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
-
email:
|
12
|
-
homepage:
|
13
|
-
rubyforge_project:
|
14
|
-
description:
|
11
|
+
email: support@zenspider.com
|
12
|
+
homepage: http://www.zenspider.com/ZSS/Products/png/
|
13
|
+
rubyforge_project: seattlerb
|
14
|
+
description: "PNG is an almost-pure-ruby PNG library. It lets you write a PNG without any C libraries. == FEATURES * Very simple interface. * Outputs simple PNG files with ease. * Basic PNG reader as well (someday it might do compositing and the like!). * Almost pure ruby, does require a compiler. == SYNOPSYS require 'png' canvas = PNG::Canvas.new 200, 200 # Set a point to a color canvas[100, 100] = PNG::Color::Black # draw an anti-aliased line canvas.line 50, 50, 100, 50, PNG::Color::Blue png = PNG.new canvas png.save 'blah.png'"
|
15
15
|
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
@@ -28,14 +28,20 @@ cert_chain:
|
|
28
28
|
post_install_message:
|
29
29
|
authors:
|
30
30
|
- Ryan Davis
|
31
|
+
- Eric Hodel
|
31
32
|
files:
|
33
|
+
- History.txt
|
32
34
|
- Manifest.txt
|
33
|
-
- README
|
35
|
+
- README.txt
|
34
36
|
- Rakefile
|
35
37
|
- example/lines.rb
|
38
|
+
- example/profile.rb
|
39
|
+
- example/profile_lines.rb
|
36
40
|
- lib/png.rb
|
37
|
-
|
38
|
-
|
41
|
+
- lib/png/pie.rb
|
42
|
+
- test/test_png.rb
|
43
|
+
test_files:
|
44
|
+
- test/test_png.rb
|
39
45
|
rdoc_options: []
|
40
46
|
|
41
47
|
extra_rdoc_files: []
|
@@ -46,5 +52,22 @@ extensions: []
|
|
46
52
|
|
47
53
|
requirements: []
|
48
54
|
|
49
|
-
dependencies:
|
50
|
-
|
55
|
+
dependencies:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: RubyInline
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 3.5.0
|
64
|
+
version:
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: hoe
|
67
|
+
version_requirement:
|
68
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.2.0
|
73
|
+
version:
|
data/README
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
= png
|
2
|
-
|
3
|
-
== About
|
4
|
-
|
5
|
-
png is a pure-ruby PNG library. It lets you write a PNG without any C
|
6
|
-
libraries. Of course it is "slow".
|
7
|
-
|
8
|
-
== Installing png
|
9
|
-
|
10
|
-
Just install the gem:
|
11
|
-
|
12
|
-
$ sudo gem install png
|
13
|
-
|
14
|
-
== Using png
|
15
|
-
|
16
|
-
require 'png'
|
17
|
-
|
18
|
-
canvas = PNG::Canvas.new 200, 200
|
19
|
-
|
20
|
-
# Set a point to a color
|
21
|
-
canvas[100, 100] = PNG::Color::Black
|
22
|
-
|
23
|
-
# draw an anti-aliased line
|
24
|
-
canvas.line 50, 50, 100, 50, PNG::Color::Blue
|
25
|
-
|
26
|
-
png = PNG.new canvas
|
27
|
-
png.save 'blah.png'
|
28
|
-
|