png 1.0.0 → 1.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.
- 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
|
-
|