png 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/Manifest.txt +5 -0
  2. data/README +28 -0
  3. data/Rakefile +56 -0
  4. data/example/lines.rb +27 -0
  5. data/lib/png.rb +288 -0
  6. metadata +50 -0
data/Manifest.txt ADDED
@@ -0,0 +1,5 @@
1
+ Manifest.txt
2
+ README
3
+ Rakefile
4
+ example/lines.rb
5
+ lib/png.rb
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
+