png 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +5 -0
- data/README +28 -0
- data/Rakefile +56 -0
- data/example/lines.rb +27 -0
- data/lib/png.rb +288 -0
- metadata +50 -0
data/Manifest.txt
ADDED
data/README
ADDED
@@ -0,0 +1,28 @@
|
|
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
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
|
7
|
+
$VERBOSE = nil
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = 'png'
|
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'
|
16
|
+
|
17
|
+
s.has_rdoc = true
|
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
|
44
|
+
|
45
|
+
desc 'Build Gem'
|
46
|
+
Rake::GemPackageTask.new spec do |pkg|
|
47
|
+
pkg.need_tar = true
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'Clean up'
|
51
|
+
task :clean => [ :clobber_rdoc, :clobber_package ]
|
52
|
+
|
53
|
+
desc 'Clean up'
|
54
|
+
task :clobber => [ :clean ]
|
55
|
+
|
56
|
+
# vim: syntax=Ruby
|
data/example/lines.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
require 'png'
|
4
|
+
|
5
|
+
canvas = PNG::Canvas.new 1024, 1024, PNG::Color::Black
|
6
|
+
|
7
|
+
#canvas.each do |x, y|
|
8
|
+
# case x
|
9
|
+
# when y then
|
10
|
+
# canvas.point(x, y, Color::Black)
|
11
|
+
# when 50 then
|
12
|
+
# canvas.point(x, y, Color::Background)
|
13
|
+
# end
|
14
|
+
# canvas.point(x, y, Color::Green) if y = 200
|
15
|
+
#end
|
16
|
+
|
17
|
+
canvas.line 50, 50, 100, 50, PNG::Color::Blue
|
18
|
+
canvas.line 50, 50, 50, 100, PNG::Color::Blue
|
19
|
+
canvas.line 100, 50, 150, 100, PNG::Color::Blue
|
20
|
+
canvas.line 100, 50, 125, 100, PNG::Color::Green # currently wrong
|
21
|
+
canvas.line 100, 50, 200, 75, PNG::Color::Green # currently wrong
|
22
|
+
canvas.line 0, 200, 200, 0, PNG::Color::Black
|
23
|
+
|
24
|
+
png = PNG.new canvas
|
25
|
+
png.save 'blah.png'
|
26
|
+
`open blah.png`
|
27
|
+
|
data/lib/png.rb
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
class String # :nodoc:
|
4
|
+
|
5
|
+
##
|
6
|
+
# Calculates a CRC using the algorithm in the PNG specification.
|
7
|
+
|
8
|
+
def png_crc
|
9
|
+
unless defined? @@crc then
|
10
|
+
@@crc = Array.new(256)
|
11
|
+
256.times do |n|
|
12
|
+
c = n
|
13
|
+
8.times do
|
14
|
+
c = (c & 1 == 1) ? 0xedb88320 ^ (c >> 1) : c >> 1
|
15
|
+
end
|
16
|
+
@@crc[n] = c
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
c = 0xffffffff
|
21
|
+
each_byte do |b|
|
22
|
+
c = @@crc[(c^b) & 0xff] ^ (c >> 8)
|
23
|
+
end
|
24
|
+
return c ^ 0xffffffff
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# A pure Ruby Portable Network Graphics (PNG) writer.
|
31
|
+
#
|
32
|
+
# http://www.libpng.org/pub/png/spec/1.2/
|
33
|
+
#
|
34
|
+
# PNG supports:
|
35
|
+
# + 8 bit truecolor PNGs
|
36
|
+
#
|
37
|
+
# PNG does not support:
|
38
|
+
# + any other color depth
|
39
|
+
# + extra data chunks
|
40
|
+
# + filters
|
41
|
+
#
|
42
|
+
# = Example
|
43
|
+
#
|
44
|
+
# require 'png'
|
45
|
+
#
|
46
|
+
# canvas = PNG::Canvas.new 200, 200
|
47
|
+
# canvas[100, 100] = PNG::Color::Black
|
48
|
+
# canvas.line 50, 50, 100, 50, PNG::Color::Blue
|
49
|
+
# png = PNG.new canvas
|
50
|
+
# png.save 'blah.png'
|
51
|
+
|
52
|
+
class PNG
|
53
|
+
|
54
|
+
##
|
55
|
+
# Creates a PNG chunk of type +type+ that contains +data+.
|
56
|
+
|
57
|
+
def self.chunk(type, data="")
|
58
|
+
[data.size, type, data, (type + data).png_crc].pack("Na*a*N")
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Creates a new PNG object using +canvas+
|
63
|
+
|
64
|
+
def initialize(canvas)
|
65
|
+
@height = canvas.height
|
66
|
+
@width = canvas.width
|
67
|
+
@bits = 8
|
68
|
+
@data = canvas.data
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Writes the PNG to +path+.
|
73
|
+
|
74
|
+
def save(path)
|
75
|
+
File.open(path, "w") do |f|
|
76
|
+
f.write [137, 80, 78, 71, 13, 10, 26, 10].pack("C*") # PNG signature
|
77
|
+
f.write PNG.chunk('IHDR',
|
78
|
+
[ @height, @width, @bits, 6, 0, 0, 0 ].pack("N2C5"))
|
79
|
+
# 0 == filter type code "none"
|
80
|
+
data = @data.map { |row| [0] + row.map { |p| p.values } }.flatten
|
81
|
+
f.write PNG.chunk('IDAT', Zlib::Deflate.deflate(data.pack("C*"), 9))
|
82
|
+
f.write PNG.chunk('IEND', '')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# RGBA colors
|
88
|
+
|
89
|
+
class Color
|
90
|
+
|
91
|
+
attr_reader :values
|
92
|
+
|
93
|
+
##
|
94
|
+
# Creates a new color with values +red+, +green+, +blue+, and +alpha+.
|
95
|
+
|
96
|
+
def initialize(red, green, blue, alpha)
|
97
|
+
@values = [red, green, blue, alpha]
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Transparent white
|
102
|
+
|
103
|
+
Background = Color.new 0xFF, 0xFF, 0xFF, 0x00
|
104
|
+
|
105
|
+
White = Color.new 0xFF, 0xFF, 0xFF, 0xFF
|
106
|
+
Black = Color.new 0x00, 0x00, 0x00, 0xFF
|
107
|
+
Gray = Color.new 0x7F, 0x7F, 0x7F, 0xFF
|
108
|
+
|
109
|
+
Red = Color.new 0xFF, 0x00, 0x00, 0xFF
|
110
|
+
Orange = Color.new 0xFF, 0xA5, 0x00, 0xFF
|
111
|
+
Yellow = Color.new 0xFF, 0xFF, 0x00, 0xFF
|
112
|
+
Green = Color.new 0x00, 0xFF, 0x00, 0xFF
|
113
|
+
Blue = Color.new 0x00, 0x00, 0xFF, 0xFF
|
114
|
+
Purple = Color.new 0XFF, 0x00, 0xFF, 0xFF
|
115
|
+
|
116
|
+
##
|
117
|
+
# Red component
|
118
|
+
|
119
|
+
def r; @values[0]; end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Green component
|
123
|
+
|
124
|
+
def g; @values[1]; end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Blue component
|
128
|
+
|
129
|
+
def b; @values[2]; end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Alpha transparency component
|
133
|
+
|
134
|
+
def a; @values[3]; end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Blends +color+ into this color returning a new blended color.
|
138
|
+
|
139
|
+
def blend(color)
|
140
|
+
return Color.new((r * (0xFF - color.a) + color.r * color.a) >> 8,
|
141
|
+
(g * (0xFF - color.a) + color.g * color.a) >> 8,
|
142
|
+
(b * (0xFF - color.a) + color.b * color.a) >> 8,
|
143
|
+
(a * (0xFF - color.a) + color.a * color.a) >> 8)
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Returns a new color with an alpha value adjusted by +i+.
|
148
|
+
|
149
|
+
def intensity(i)
|
150
|
+
return Color.new(r,b,g,(a*i) >> 8)
|
151
|
+
end
|
152
|
+
|
153
|
+
def inspect # :nodoc:
|
154
|
+
"#<%s %02x %02x %02x %02x>" % [self.class, *@values]
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# PNG canvas
|
161
|
+
|
162
|
+
class Canvas
|
163
|
+
|
164
|
+
##
|
165
|
+
# Height of the canvas
|
166
|
+
|
167
|
+
attr_reader :height
|
168
|
+
|
169
|
+
##
|
170
|
+
# Width of the canvas
|
171
|
+
|
172
|
+
attr_reader :width
|
173
|
+
|
174
|
+
##
|
175
|
+
# Raw data
|
176
|
+
|
177
|
+
attr_reader :data
|
178
|
+
|
179
|
+
def initialize(height, width, background = Color::White)
|
180
|
+
@height = height
|
181
|
+
@width = width
|
182
|
+
@data = Array.new(@width) { |x| Array.new(@height) { background } }
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Retrieves the color of the pixel at (+x+, +y+).
|
187
|
+
|
188
|
+
def [](x, y)
|
189
|
+
raise "bad x value #{x} >= #{@height}" if x >= @height
|
190
|
+
raise "bad y value #{y} >= #{@width}" if y >= @width
|
191
|
+
@data[y][x]
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Sets the color of the pixel at (+x+, +y+) to +color+.
|
196
|
+
|
197
|
+
def []=(x, y, color)
|
198
|
+
raise "bad x value #{x} >= #{@height}" if x >= @height
|
199
|
+
raise "bad y value #{y} >= #{@width}" if y >= @width
|
200
|
+
@data[y][x] = color
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Iterates over each pixel in the canvas.
|
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
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# Blends +color+ onto the color at point (+x+, +y+).
|
216
|
+
|
217
|
+
def point(x, y, color)
|
218
|
+
self[x,y] = self[x,y].blend(color)
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Draws a line using Xiaolin Wu's antialiasing technique.
|
223
|
+
#
|
224
|
+
# http://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm
|
225
|
+
|
226
|
+
def line(x0, y0, x1, y1, color)
|
227
|
+
dx = x1 - x0
|
228
|
+
sx = dx < 0 ? -1 : 1
|
229
|
+
dx *= sx # TODO: abs?
|
230
|
+
dy = y1 - y0
|
231
|
+
|
232
|
+
# 'easy' cases
|
233
|
+
if dy == 0 then
|
234
|
+
Range.new(*[x0,x1].sort).each do |x|
|
235
|
+
point(x, y0, color)
|
236
|
+
end
|
237
|
+
return
|
238
|
+
end
|
239
|
+
|
240
|
+
if dx == 0 then
|
241
|
+
(y0..y1).each do |y|
|
242
|
+
point(x0, y, color)
|
243
|
+
end
|
244
|
+
return
|
245
|
+
end
|
246
|
+
|
247
|
+
if dx == dy then
|
248
|
+
Range.new(*[x0,x1].sort).each do |x|
|
249
|
+
point(x, y0, color)
|
250
|
+
y0 += 1
|
251
|
+
end
|
252
|
+
return
|
253
|
+
end
|
254
|
+
|
255
|
+
# main loop
|
256
|
+
point(x0, y0, color)
|
257
|
+
e_acc = 0
|
258
|
+
if dy > dx then # vertical displacement
|
259
|
+
e = (dx << 16) / dy
|
260
|
+
(y0...y1-1).each do |i|
|
261
|
+
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
|
262
|
+
x0 = x0 + sx if (e_acc <= e_acc_temp)
|
263
|
+
w = 0xFF-(e_acc >> 8)
|
264
|
+
point(x0, y0, color.intensity(w))
|
265
|
+
y0 = y0 + 1
|
266
|
+
point(x0 + sx, y0, color.intensity(0xFF-w))
|
267
|
+
end
|
268
|
+
point(x1, y1, color)
|
269
|
+
return
|
270
|
+
end
|
271
|
+
|
272
|
+
# horizontal displacement
|
273
|
+
e = (dy << 16) / dx
|
274
|
+
(x0...(x1-sx)).each do |i|
|
275
|
+
e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
|
276
|
+
y0 += 1 if (e_acc <= e_acc_temp)
|
277
|
+
w = 0xFF-(e_acc >> 8)
|
278
|
+
point(x0, y0, color.intensity(w))
|
279
|
+
x0 += sx
|
280
|
+
point(x0, y0 + 1, color.intensity(0xFF-w))
|
281
|
+
end
|
282
|
+
point(x1, y1, color)
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.99
|
3
|
+
specification_version: 1
|
4
|
+
name: png
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2006-08-31 00:00:00 -07:00
|
8
|
+
summary: A pure ruby PNG library
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: ryand-ruby@zenspider.com
|
12
|
+
homepage:
|
13
|
+
rubyforge_project:
|
14
|
+
description: png allows you to write a PNG file without any C libraries.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Ryan Davis
|
31
|
+
files:
|
32
|
+
- Manifest.txt
|
33
|
+
- README
|
34
|
+
- Rakefile
|
35
|
+
- example/lines.rb
|
36
|
+
- lib/png.rb
|
37
|
+
test_files: []
|
38
|
+
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
executables: []
|
44
|
+
|
45
|
+
extensions: []
|
46
|
+
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
dependencies: []
|
50
|
+
|