png 1.0.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.
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
+