pruim 0.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/README ADDED
@@ -0,0 +1,44 @@
1
+ +------------------------------------+
2
+ | Pruim -- a Pure RUby IMage library |
3
+ +------------------------------------+
4
+
5
+ Introduction
6
+ ------------
7
+
8
+ Pruim is a Pure RUby IMage library, with the indent of supporting
9
+ multiple image formats. Currently only supports loading from and
10
+ saving to PPM files and paletted BMP files. Contributions of
11
+ other formats are welcome. :)
12
+
13
+ Requirements
14
+ ------------
15
+
16
+ BinData (gem install bindata) is required. It makes decoding images much easier.
17
+ Watch and autowatchr for testing.
18
+ Run watchr test/test.watchr to run the tests.
19
+
20
+
21
+ License
22
+ -------
23
+
24
+ You may use Pruim under the Zlib license:
25
+
26
+ Pruim is Copyright (C) 2011 Beoran beoran@rubyforge.org
27
+
28
+ This software is provided 'as-is', without any express or implied
29
+ warranty. In no event will the authors be held liable for any damages
30
+ arising from the use of this software.
31
+
32
+ Permission is granted to anyone to use this software for any purpose,
33
+ including commercial applications, and to alter it and redistribute it
34
+ freely, subject to the following restrictions:
35
+
36
+ 1. The origin of this software must not be misrepresented; you must not
37
+ claim that you wrote the original software. If you use this software
38
+ in a product, an acknowledgment in the product documentation would be
39
+ appreciated but is not required.
40
+ 2. Altered source versions must be plainly marked as such, and must not be
41
+ misrepresented as being the original software.
42
+ 3. This notice may not be removed or altered from any source distribution.
43
+
44
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package_task'
3
+ require 'rake/clean'
4
+ CLEAN.include("pkg/*.gem")
5
+
6
+
7
+ PRUIM_VERSION = "0.1.0"
8
+
9
+ def apply_spec_defaults(s)
10
+ end
11
+
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.platform = Gem::Platform::RUBY
15
+ s.summary = "Pure Ruby Image Library."
16
+ s.name = 'pruim'
17
+ s.version = '0.1.0'
18
+ s.add_dependency('bindata', '>= 1.0.0')
19
+ s.add_development_dependency('atto', '>= 0.9.2')
20
+ s.require_path = 'lib'
21
+ s.files = ['README', 'Rakefile'] +
22
+ FileList["lib/pruim.rb", "lib/pruim/*.rb",
23
+ "test/*.rb" , "test/pruim/*.rb" ].to_a
24
+ s.description = <<EOF
25
+ Pruim is a Pure Ruby Image Library. Currently only supports BMP, PPM and PBM
26
+ formats, but preserves palette ordering on reading and writing.
27
+ EOF
28
+ s.author = 'beoran'
29
+ s.email = 'beoran@rubyforge.org'
30
+ s.homepage = 'http://github.com/beoran/pruim'
31
+ s.date = Time.now.strftime '%Y-%m-%d'
32
+ end
33
+
34
+ Gem::PackageTask.new(spec) do |pkg|
35
+ pkg.need_zip = false
36
+ pkg.need_tar = false
37
+ end
38
+
39
+
40
+ task :test do
41
+ for file in FileList["test/*.rb" , "test/pruim/*.rb" ] do
42
+ puts("Running tests for #{file}:")
43
+ res = system("ruby -I lib #{file}")
44
+ puts res ? "OK!" : "Failed!"
45
+ end
46
+ end
47
+
48
+ task :default => :test
49
+
data/lib/pruim/bmp.rb ADDED
@@ -0,0 +1,238 @@
1
+ require 'bindata'
2
+
3
+ module Pruim
4
+ class BMP
5
+ include Codec
6
+
7
+ class Header < BinData::Record
8
+ endian :little
9
+ string :magic, :length => 2
10
+ uint32 :filesize
11
+ uint16 :creator1
12
+ uint16 :creator2
13
+ uint32 :bitmap_offset
14
+ def set(*args)
15
+ self.magic, self.filesize, self.creator1, self.creator2,
16
+ self.bitmap_offset = *args
17
+ self
18
+ end
19
+ end
20
+
21
+ class CoreHeader < BinData::Record
22
+ endian :little
23
+ uint32 :header_size
24
+ int32 :width
25
+ int32 :height
26
+ uint16 :nplanes
27
+ uint16 :bitspp
28
+ def set(*args)
29
+ self.header_size, self.width, self.height, self.nplanes,
30
+ self.bitspp = *args
31
+ self
32
+ end
33
+ end
34
+
35
+ class ExtraHeader < BinData::Record
36
+ endian :little
37
+ # uint32 :header_size
38
+ # int32 :width
39
+ # int32 :height
40
+ # uint16 :nplanes
41
+ # uint16 :bitspp
42
+ uint32 :compress_type
43
+ uint32 :bmp_bytesz
44
+ int32 :hres
45
+ int32 :vres
46
+ uint32 :ncolors
47
+ uint32 :nimpcolors
48
+
49
+ def set(*args)
50
+ self.compress_type, self.bmp_bytesz, self.hres, self.vres, self.ncolors,
51
+ self.nimpcolors = *args
52
+ self
53
+ end
54
+ end
55
+
56
+ BI_RGB = 0 #
57
+ BI_RLE8 = 1 # RLE 8-bit/pixel Can be used only with 8-bit/pixel bitmaps
58
+ BI_RLE4 = 2 # RLE 4-bit/pixel Can be used only with 4-bit/pixel bitmaps
59
+ BI_BITFIELDS = 3 # Bit field or Huffman 1D compression for BITMAPCOREHEADER2
60
+ BI_JPEG = 4 # JPEG or RLE-24 compression for BITMAPCOREHEADER2
61
+ BI_PNG = 5 # PNG The bitmap contains a PNG image.
62
+ BITMAPINFOHEADER=40 # Commonly used
63
+ BITMAPV5HEADER =124# Latest version
64
+ HEADER_SIZE = 14
65
+
66
+
67
+ class BGRX < BinData::Record
68
+ uint8 :b
69
+ uint8 :g
70
+ uint8 :r
71
+ uint8 :x
72
+ def set(b, g, r, x)
73
+ self.b = b
74
+ self.g = g
75
+ self.r = r
76
+ self.x = x
77
+ self
78
+ end
79
+ end
80
+
81
+ class BGR < BinData::Record
82
+ uint8 :b
83
+ uint8 :g
84
+ uint8 :r
85
+
86
+ def set(b, g, r)
87
+ self.b = b
88
+ self.g = g
89
+ self.r = r
90
+ self
91
+ end
92
+ end
93
+
94
+ class ColorTableRGBX < BinData::Record
95
+ array :type => BGRX
96
+ end
97
+
98
+ def can_decode?(io)
99
+ header = Header.read(io)
100
+ io.rewind
101
+ return header && header.magic == 'BM'
102
+ end
103
+
104
+
105
+ def read_palette(io, header, core, extra)
106
+ palette = Pruim::Palette.new
107
+ ncolors = 2 ** core.bitspp
108
+ for i in 0..255
109
+ color = BGRX.read(io)
110
+ palette.new_rgb(color.r, color.g, color.b)
111
+ raise "Unexpected end of file whilst reading bmp palette!" if io.eof?
112
+ end
113
+ return palette
114
+ end
115
+
116
+
117
+ def decode_bpp8_rgb(io, header, core, extra, padding)
118
+ data = []
119
+ for ypos in (0...core.height)
120
+ size = core.width + padding
121
+ # read a line with padding
122
+ raise "End of file when reading BMP btyes!" if io.eof?
123
+ str = io.read(size)
124
+ raise "Short read when reading BMP bytes!" unless str && str.bytesize == size
125
+ # make an array out of it and drop the padding
126
+ arr = str.bytes.to_a
127
+ arr.pop(padding)
128
+ data = arr + data # prepend data
129
+ end
130
+ return data
131
+ end
132
+
133
+ # Calculate padding size
134
+ def calc_padding(wide, palette = true)
135
+ bs = palette ? 1 : 3
136
+ padding = (1 * bs * wide) % 4;
137
+ padding = 4 - padding if padding != 0
138
+ return padding
139
+ end
140
+
141
+
142
+ def decode_bpp8(io, header, core, extra)
143
+ # p header, core, extra
144
+ io.seek(HEADER_SIZE + core.header_size)
145
+ palette = read_palette(io, header, core, extra)
146
+ # Skip to the bitmap, gap may be there...
147
+ io.seek(header.bitmap_offset)
148
+ # Calculate padding size
149
+ padding = calc_padding(core.width, true)
150
+ data = nil
151
+ case extra.compress_type
152
+ when BI_RGB
153
+ data = decode_bpp8_rgb(io, header, core, extra, padding)
154
+ else
155
+ return nil
156
+ end
157
+ image = Image.new(core.width, core.height, :depth => core.bitspp,
158
+ :palette => palette, :pages => 1, :data => [data])
159
+ return image
160
+ end
161
+
162
+ def decode(io)
163
+ header = Header.read(io)
164
+ core = CoreHeader.read(io)
165
+ extra = nil
166
+ if (core.header_size == BITMAPINFOHEADER)
167
+ extra = ExtraHeader.read(io)
168
+ end
169
+ io.seek(HEADER_SIZE + core.header_size)
170
+ # skip rest of header that we don't support
171
+ if core.bitspp == 8
172
+ return decode_bpp8(io, header, core, extra)
173
+ end
174
+ # TODO: real color bitmaps.
175
+ return nil
176
+ end
177
+
178
+ def encode_palette(image, io)
179
+ image.palette.each do |color|
180
+ r, g, b = *Color.to_rgb(color)
181
+ bgrx = BGRX.new.set(b, g, r, 0)
182
+ bgrx.write(io)
183
+ end
184
+ end
185
+
186
+ def encode_bpp8_rgb(image, io, padding)
187
+ page = image.active
188
+ ypos = image.h - 1
189
+ while ypos >= 0
190
+ row = page.row(ypos)
191
+ row = row + [0] * padding
192
+ str = row.pack('C*')
193
+ io.write(str)
194
+ ypos -= 1
195
+ end
196
+ end
197
+
198
+
199
+ def encode_bpp8(image, io, padding)
200
+ encode_palette(image, io)
201
+ encode_bpp8_rgb(image, io, padding)
202
+ end
203
+
204
+ def encode_bpp24(image, io, padding)
205
+ end
206
+
207
+ def encode(image, io)
208
+ bitcount = (image.palette? ? 8 : 24)
209
+ bitmap_size = ((image.w * bitcount) / 8) * image.h
210
+ info_size = BITMAPINFOHEADER
211
+ # Data for the palette
212
+ if image.palette?
213
+ info_size += image.palette.size * 4
214
+ end
215
+ total_size = 14 + info_size + bitmap_size
216
+ # write bmp header info
217
+ header = Header.new.set('BM', total_size, 0, 0, 14 + info_size)
218
+ header.write(io)
219
+ core = CoreHeader.new.set(BITMAPINFOHEADER, image.w, image.h, 1, bitcount)
220
+ core.write(io)
221
+ extra = ExtraHeader.new.set(BI_RGB, 0, 0, bitcount, image.palette.size, 0)
222
+ extra.write(io)
223
+ # Calculate padding size
224
+ padding = calc_padding(image.active.w, image.palette?)
225
+ if image.palette?
226
+ encode_bpp8(image, io, padding)
227
+ else
228
+ encode_bpp24(image, io, padding)
229
+ end
230
+ return image
231
+ end
232
+
233
+
234
+
235
+ end
236
+ end
237
+
238
+
@@ -0,0 +1,74 @@
1
+ module Pruim
2
+ module Codec
3
+
4
+ def self.register(name, klass)
5
+ @codecs ||= {}
6
+ @codecs[name] = klass
7
+ end
8
+
9
+ # Returns a codec based on it's short name.
10
+ def self.for_name(name)
11
+ return Pruim.const_get(name.upcase)
12
+ end
13
+
14
+ # Returns the codec to use for the given filename, based on the extension
15
+ def self.for_filename(filename)
16
+ ext = File.extname(flename) # get filename extension
17
+ return self.codec_for(name)
18
+ end
19
+
20
+ # Returns an instance of codec to use for the given filename, based on the
21
+ # extension of the filename.
22
+ def self.new_for_filename(filename)
23
+ codec = for_filename(filename)
24
+ return codec.new()
25
+ end
26
+
27
+ # Returns a new instance of a codec based on it's short name.
28
+ def self.new_for_name(name)
29
+ codec = for_name(name)
30
+ return nil unless codec
31
+ return codec.new()
32
+ end
33
+
34
+ # Returns a codec based on it's short name or on the filename's extension.
35
+ def self.for_filename_name(filename, codecname = nil)
36
+ codec = nil
37
+ return for_name(codecname) if codecname
38
+ return for_filename(codecname)
39
+ end
40
+
41
+ # Returns a new instance of a codec based on it's short name or on
42
+ # the filename's extension.
43
+ def self.new_for_filename_name(filename, codecname = nil)
44
+ codec = for_filename_name(filename, codecname)
45
+ return nil unless codec
46
+ return codec.new()
47
+ end
48
+
49
+ # Stream should be an StringIO, or otherwise IO compatible object.
50
+ def decode(io)
51
+ raise "not implemented"
52
+ end
53
+
54
+ def encode(image, io)
55
+ raise "not implemented"
56
+ end
57
+
58
+ def can_decode?(io)
59
+ raise "not implemented"
60
+ end
61
+
62
+ def can_encode?(image)
63
+ raise "not implemented"
64
+ end
65
+
66
+ def encode_will_degrade?(image)
67
+ raise "not implemented"
68
+ end
69
+
70
+ def text
71
+ return "A codec that does nothing."
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,34 @@
1
+ module Pruim
2
+ class Color
3
+ def self.rgba(r, g, b, a)
4
+ (a | (b << 8) | (g << 16) | (r << 24))
5
+ end
6
+
7
+ def self.rgb(r, g, b)
8
+ return rgba(r, g, b, 255)
9
+ end
10
+
11
+ # Cpolors are encoded in abgr
12
+ def self.to_rgba(color)
13
+ a = (color) & 255
14
+ b = (color >> 8) & 255
15
+ g = (color >> 16) & 255
16
+ r = (color >> 24) & 255
17
+ return r, g, b, a
18
+ end
19
+
20
+ def self.to_rgb(color)
21
+ r, g, b, a = to_rgba(color)
22
+ return r, g, b
23
+ end
24
+
25
+ BRIGHT_TRESHOLD = 382
26
+
27
+ # Returns true if the color is bright (above threshold)
28
+ # And false if black. Transparency is takeninto account as well.
29
+ def to_bool(treshold = BRIGHT_TRESHOLD)
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,160 @@
1
+ module Pruim
2
+ # An image consists of one or more pages.
3
+ # Whether a page is a layer, a frame in an animation
4
+ # or both, is determined by the properties of the page.
5
+
6
+ class Image
7
+
8
+ attr_reader :w
9
+ attr_reader :h
10
+ attr_reader :palette
11
+ attr_reader :pages
12
+ # Currently "active" page.
13
+ attr_reader :active
14
+ # image mode, may be one of :monochrome, :palette, :grayscale, :rgba
15
+ attr_reader :mode
16
+ # Color depth, may be one of 1, 2, 4, 8, 16, 24, 32
17
+ attr_reader :depth
18
+
19
+ # Extra information data hash table.
20
+ attr_reader :info
21
+
22
+ def self.depth_for_colors(ncolors)
23
+ (Math.log(ncolors) / Math.log(2)).to_i
24
+ end
25
+
26
+ # Sets the comment for this image
27
+ def comment=(comm)
28
+ @info[:comment] = comm
29
+ end
30
+
31
+ # gets the comment for this image
32
+ def comment
33
+ @info[:comment]
34
+ end
35
+
36
+ def initialize(w, h, extra = {})
37
+ @w = w
38
+ @h = h
39
+ @info = {}
40
+ @palette = extra[:palette]
41
+ @mode = extra[:mode]
42
+ @mode ||= (@palette ? :palette : :rgba)
43
+ @palette = Palette.new if !@palette && @mode == :palette
44
+ @depth = extra[:depth]
45
+ @depth ||= (@palette ? 8 : 32)
46
+ @pages = []
47
+ @ordered = {}
48
+ @active = nil
49
+ # Construct many pages.
50
+ if extra[:pages]
51
+ data = extra[:data] || []
52
+ extra[:pages].times { |i| self.new_page(@w, @h, :data => data[i]) }
53
+ # Construct a single page if data is given nevertheless
54
+ elsif extra[:data]
55
+ self.new_page(@w, @h, :data => extra[:data])
56
+ end
57
+ # Set comments if any
58
+ self.comment = extra[:comment]
59
+ end
60
+
61
+ def palette?
62
+ return !(@palette.nil?)
63
+ end
64
+
65
+ # Sets the page with the given index as active if it exists.
66
+ def activate(index)
67
+ @active = @pages[index]
68
+ end
69
+
70
+ # Create a new a page and adds it to this image.
71
+ # The page is also set as the active page.
72
+ def new_page(w = nil, h = nil, extra = {})
73
+ w ||= self.w
74
+ h ||= self.h
75
+ page = Page.new(self, w, h, extra)
76
+ return self.add_page(page)
77
+ end
78
+
79
+ # Adds a page to this image. The page is also set as the active page.
80
+ def add_page(page)
81
+ @pages << page
82
+ @ordered[page.frame] = {} unless @ordered[page.frame]
83
+ @ordered[page.frame][page.layer] = [] unless @ordered[page.frame][page.layer]
84
+ @ordered[page.frame][page.layer] << page
85
+ @active = page
86
+ return page
87
+ end
88
+
89
+ # Returns an array of all pages at the given frame and layer
90
+ def pages_at(frame = 0, layer = 0)
91
+ @ordered[frame, layer]
92
+ end
93
+
94
+ # Gets a pixel from the current active page, if any.
95
+ def getpixel(x, y)
96
+ @active.getpixel!(x, y)
97
+ end
98
+
99
+ # Sets a pixel to the current active page, if any.
100
+ def putpixel(x, y, color)
101
+ @active.putpixel!(x, y, color)
102
+ end
103
+
104
+ # Fills the current active page, if any.
105
+ def fill(color)
106
+ @active.fill(color)
107
+ end
108
+
109
+ # Saves the image to the given filename using the given codec
110
+ # If codec is missig, it's determined from the filename's extension.
111
+ def save_as(filename, codecname = nil)
112
+ codec = Codec.new_for_filename_name(filename, codecname)
113
+ return false unless codec
114
+ io = File.open(filename, 'wb+')
115
+ res = codec.encode(self, io)
116
+ io.close
117
+ return res
118
+ end
119
+
120
+ # Loads the image from the given filename using the given codec
121
+ # If codec is missig, it's determined from the filename's extension.
122
+ def self.load_from(filename, codecname = nil)
123
+ codec = Codec.new_for_filename_name(filename, codecname)
124
+ return false unless codec
125
+ io = File.open(filename, 'rb+')
126
+ res = nil
127
+ if codec.can_decode?(io)
128
+ res = codec.decode(io)
129
+ else
130
+ raise "Malformed file #{filename} cannot be decoded as a #{codec} file."
131
+ end
132
+ io.close
133
+ return res
134
+ end
135
+
136
+ # Creates a new rgb color for use with this image.
137
+ # If the image is palleted, the color is added to the palette and the index
138
+ # is returned, otherwise the color is returned
139
+ def new_rgb(r, g, b)
140
+ if palette?
141
+ return @palette.new_rgb
142
+ else
143
+ return Color.rgb(r, g, b)
144
+ end
145
+ end
146
+
147
+ # Creates a new rgba color for use with this image.
148
+ # If the image is palleted, the color is added to the palette and the index
149
+ # is returned, otherwise the color is returned
150
+ def new_rgba(r, g, b, a)
151
+ if palette?
152
+ return @palette.new_rgba
153
+ else
154
+ return Color.rgba(r, g, b, a)
155
+ end
156
+ end
157
+
158
+
159
+ end
160
+ end
@@ -0,0 +1,15 @@
1
+ module Pruim
2
+ class Movie
3
+ attr_reader :w
4
+ attr_reader :h
5
+ attr_reader :frames
6
+
7
+ def initialize(w, h, index = 0, nframes = 1, nlayers = 1, data = nil)
8
+ @w = w
9
+ @h = h
10
+ @frames = Array.new(nframes) do | index |
11
+ Movie.new(self, w, h, index, nlayers, data)
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/pruim/page.rb ADDED
@@ -0,0 +1,82 @@
1
+ module Pruim
2
+ # A Page can represents both a layer and a frame of animation, for
3
+ # multi-page image formats.
4
+ # Inside a Page, bitmap data is stored in an array as follows:
5
+ # For a monochrome image, true and false are stored, where true is "black"
6
+ # meaning pixel activated, on, say, an LCD screen, and false means white
7
+ # or pixel not activated.
8
+ # For :palette mode images, what is stored are palette indexes.
9
+ # for an RBGA , integers are stored that encode the color in an 8888 bits
10
+ # ABGR way
11
+ class Page
12
+ attr_reader :image
13
+ attr_reader :w
14
+ attr_reader :h
15
+ attr_reader :layer
16
+ attr_reader :frame
17
+ attr_reader :x
18
+ attr_reader :y
19
+
20
+ # Extra information data hash table.
21
+ attr_reader :info
22
+
23
+ # Returns a row of pixels with the given y coordinates as an array
24
+ def row(y)
25
+ return @data.slice(y * self.w, self.w)
26
+ end
27
+
28
+
29
+ def pixels
30
+ return @data
31
+ end
32
+
33
+ def initialize(image, w, h, extra = {})
34
+ @image = image
35
+ @info = {}
36
+ @w = w
37
+ @h = h
38
+ @frame = extra[:frame] || 0
39
+ @layer = extra[:layer] || 0
40
+ @x = extra[:x] || 0
41
+ @y = extra[:y] || 0
42
+ @data = extra[:data]
43
+ if !@data
44
+ @data = Array.new(@h * @w, 0)
45
+ end
46
+ end
47
+
48
+ # Returns true if the coordinates are outside of the limits of this page.
49
+ def outside?(x, y)
50
+ return true if (x < 0 ) || (y < 0 )
51
+ return true if (x >= @w) || (y >= @h)
52
+ return false
53
+ end
54
+
55
+ # Gets the pixel at coordinates x,y. Returns nil if out of bounds.
56
+ def getpixel!(x, y)
57
+ @data[y * @w + x]
58
+ end
59
+
60
+ # Gets the pixel at coordinates x,y. Returns nil if out of bounds.
61
+ def getpixel(x, y)
62
+ return getpixel!(x, y)
63
+ end
64
+
65
+ def putpixel!(x, y, color)
66
+ @data[y * @w + x] = color
67
+ end
68
+
69
+ def putpixel(x, y, color)
70
+ return nil if outside?(x, y)
71
+ putpixel!(x, y, color)
72
+ end
73
+
74
+ def fill(color)
75
+ for i in (0..(@h*@w))
76
+ @data[i] = color
77
+ end
78
+ end
79
+
80
+
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ module Pruim
2
+ class Palette < Array
3
+
4
+ # Adds a new RGB color to this palette. Returns the palette index.
5
+ def new_rgb(r, g, b)
6
+ self << Pruim::Color.rgb(r, g, b)
7
+ return self.size - 1
8
+ end
9
+
10
+ # Adds a new RGBA color to this palette. Returns the palette index.
11
+ def new_rgba(r, g, b, a)
12
+ self << Pruim::Color.rgba(r, g, b, a)
13
+ return self.size - 1
14
+ end
15
+
16
+ end
17
+ end
data/lib/pruim/pbm.rb ADDED
@@ -0,0 +1,70 @@
1
+ # Tiny PBM parser, only works for 2 color (monochrome) pbm's
2
+ #
3
+ module Pruim
4
+ # Tiny PBM parser integrated with Prium codecs.
5
+ class PBM
6
+ include Codec
7
+ Codec.register('pbm', self)
8
+
9
+ # Read in a pbm file from an io object.
10
+ def decode(stream)
11
+ header = stream.gets
12
+ lines = []
13
+ until stream.eof?
14
+ lines << stream.gets
15
+ end
16
+ data = []
17
+ image = nil
18
+ page = nil
19
+ w, h = nil, nil
20
+ y = 0
21
+ comment = ''
22
+ lines.each do |line|
23
+ if line[0] == '#'
24
+ comment << line.chomp.sub(/\A#/, '')
25
+ next
26
+ end
27
+ if !w
28
+ w , h = line.chomp.split(' ').map { |v| v.to_i }
29
+ next
30
+ end
31
+ # Converts 1 to true, rest to false.
32
+ bits = line.chomp.split('').map { |v| (v == '1') }
33
+ data += bits
34
+ end
35
+ image = Image.new(w, h, :mode => :monochrome, :data => data)
36
+ image.comment = comment
37
+ return image
38
+ end
39
+
40
+ def encode(image, stream)
41
+ stream.puts("P1")
42
+ stream.puts("##{image.comment}") if image.comment
43
+ stream.puts("#{image.w} #{image.h}")
44
+ page = image.pages.first
45
+ for y in (0...page.h) do
46
+ for x in (0...page.w) do
47
+ white = page.getpixel!(x, y)
48
+ stream.write(white ? '1' : '0')
49
+ end
50
+ stream.puts()
51
+ end
52
+ end
53
+
54
+ def can_decode?(stream)
55
+ header = stream.gets
56
+ stream.rewind
57
+ return header == "P1\n"
58
+ end
59
+
60
+ # Can only encode monocrome images.
61
+ def can_encode?(image)
62
+ return image.mode == :monochrome
63
+ end
64
+
65
+ # Will only save first page of image.
66
+ def encode_will_degrade?(image)
67
+ return (image.pages.size > 1)
68
+ end
69
+ end # class PBM
70
+ end # module Pruim
data/lib/pruim/ppm.rb ADDED
@@ -0,0 +1,76 @@
1
+ module Pruim
2
+ class PPM
3
+ include Codec
4
+ Codec.register('ppm', self)
5
+
6
+ def decode(stream)
7
+ header = stream.gets
8
+ lines = []
9
+ until stream.eof?
10
+ lines << stream.gets
11
+ end
12
+ image = nil
13
+ page = nil
14
+ w, h, d = nil, nil
15
+ y = 0
16
+ comment = ''
17
+ lines.each do |line|
18
+ if line[0] == '#'
19
+ comment << line.chomp.sub(/\A#/, '')
20
+ next
21
+ end
22
+ if !image
23
+ w , h = line.chomp.split(' ').map { |v| v.to_i }
24
+ image = Image.new(w, h)
25
+ page = image.new_page(w, h)
26
+ next
27
+ end
28
+ if !d
29
+ d = line.chomp.to_i
30
+ next
31
+ end
32
+ triplets = line.chomp.split(' ').map { |v| v.to_i }
33
+ for x in (0...page.w) do
34
+ r, g, b = triplets.shift(3)
35
+ color = Color.rgb(r, g, b)
36
+ page.putpixel(x, y, color)
37
+ end
38
+ y += 1
39
+ end
40
+ image.comment = comment
41
+ return image
42
+ end
43
+
44
+
45
+ def encode(image, stream)
46
+ stream.puts("P3")
47
+ stream.puts("##{image.comment}") if image.comment
48
+ stream.puts("#{image.w} #{image.h}")
49
+ stream.puts("255")
50
+ page = image.pages.first
51
+ for y in (0...page.h) do
52
+ for x in (0...page.w) do
53
+ color = page.getpixel!(x, y)
54
+ r, g, b = Color.to_rgb(color)
55
+ stream.write(" ") if x > 0
56
+ stream.write("#{r} #{g} #{b}")
57
+ end
58
+ stream.write("\n")
59
+ end
60
+ end
61
+
62
+ def can_decode?(stream)
63
+ header = stream.gets
64
+ stream.rewind
65
+ return header == "P3\n"
66
+ end
67
+
68
+ def can_encode?(image)
69
+ return true
70
+ end
71
+
72
+ def encode_will_degrade?(image)
73
+ return (image.pages.size > 1)
74
+ end
75
+ end
76
+ end
data/lib/pruim.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Pruim
2
+ autoload :Color , 'pruim/color'
3
+ autoload :Codec , 'pruim/codec'
4
+ autoload :Palette , 'pruim/palette'
5
+ autoload :Image , 'pruim/image'
6
+ autoload :Page , 'pruim/page'
7
+ autoload :PBM , 'pruim/pbm'
8
+ autoload :PPM , 'pruim/ppm'
9
+ autoload :BMP , 'pruim/bmp'
10
+ end
data/test/atto.rb ADDED
@@ -0,0 +1,9 @@
1
+ # Atto is a tiny KISS library full of useful functionality for TDD and BDD.
2
+ module Atto
3
+ # Ansi colors for red/green display after testing.
4
+ autoload :Ansi, 'atto/ansi'
5
+ # TDD
6
+ autoload :Test, 'atto/test'
7
+ # BDD
8
+ autoload :Spec, 'atto/spec'
9
+ end
@@ -0,0 +1,90 @@
1
+ require 'test_helper'
2
+ require 'pruim'
3
+
4
+ test_w, test_h = 64, 32
5
+ bitmap1_w, bitmap1_h = 64, 64
6
+
7
+ assert { Pruim }
8
+ assert { Pruim::BMP }
9
+ inname = test_file('data', 'bitmap1.bmp')
10
+ inname2 = test_file('data', 'bitmap_alpha.bmp')
11
+ outname = test_file('data', 'out1.bmp')
12
+ outname2= test_file('data', 'out2.bmp')
13
+
14
+ codec = Pruim::Codec.for_name('bmp')
15
+ assert { codec }
16
+
17
+ image = Pruim::Image.load_from(inname, :bmp)
18
+
19
+ #
20
+ # fin = File.open(inname, 'r+')
21
+ # assert { codec.can_decode?(fin) }
22
+ # image = nil
23
+ # assert { image = codec.decode(fin) }
24
+ # fin.close
25
+ assert { image.w == bitmap1_w }
26
+ assert { image.h == bitmap1_h }
27
+
28
+ image.save_as(outname, :bmp)
29
+
30
+ #
31
+ # fout = File.open(outname, 'w+')
32
+ # assert { codec.encode(image, fout) }
33
+ # fout.close
34
+
35
+ # system("display #{outname} &")
36
+
37
+
38
+ image2 = Pruim::Image.new(test_w, test_h, :mode => :palette, :pages => 1)
39
+ assert { image2 }
40
+ assert { image2.w == test_w }
41
+ assert { image2.h == test_h }
42
+ gray = image2.palette.new_rgb(127, 128, 129)
43
+ red = image2.palette.new_rgb(255, 0, 0)
44
+ green = image2.palette.new_rgb(0 , 255, 255)
45
+ blue = image2.palette.new_rgb(0 , 0, 255)
46
+ p red
47
+ image2.fill(gray)
48
+ image2.putpixel(4, 5, red)
49
+ image2.putpixel(5, 6, green)
50
+ image2.putpixel(6, 7, blue)
51
+ assert { image2.getpixel(4, 5) == red }
52
+ assert { image2.getpixel(5, 6) == green }
53
+ assert { image2.getpixel(6, 7) == blue }
54
+
55
+ image2.save_as(outname2, :bmp)
56
+ #
57
+ # fout2 = File.open(outname2, 'w+')
58
+ # assert { codec.encode(image2, fout2) }
59
+ # fout2.close
60
+ assert { system("display #{outname2} &") }
61
+
62
+
63
+
64
+ #
65
+ # assert { codec }
66
+ # assert { codec.encode(image, fout) }
67
+ # fout.close
68
+ # # system("eog #{outname} &")
69
+ # fin = File.open(outname, 'r+')
70
+ # image2 = nil
71
+ # assert { image2 = codec.decode(fin) }
72
+ # fin.close
73
+ # page2 = image2.active
74
+ # assert { page2 }
75
+ # assert { image2.w == test_w }
76
+ # assert { image2.h == test_h }
77
+ # assert { page2.w == test_w }
78
+ # assert { page2.h == test_h }
79
+ #
80
+ # assert { page2.getpixel!(1, 1) == green }
81
+ # assert { page2.getpixel!(10, 10) == blue }
82
+ # assert { page2.getpixel!(0, 0) == red }
83
+ #
84
+ # assert { image2.getpixel(1, 1) == green }
85
+ # assert { image2.getpixel(10, 10) == blue }
86
+ # assert { image2.getpixel(0, 0) == red }
87
+ #
88
+
89
+
90
+
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+ require 'pruim'
3
+
4
+ test_w, test_h = 64, 32
5
+
6
+ assert { Pruim }
7
+ assert { Pruim::Image }
8
+ image = Pruim::Image.new(test_w, test_h, :pages => 1)
9
+ assert { image }
10
+ assert { image.w == test_w }
11
+ assert { image.h == test_h }
12
+ red = Pruim::Color.rgb(255, 0, 0)
13
+ green = Pruim::Color.rgb(0 ,255, 255)
14
+ blue = Pruim::Color.rgb(0 , 0, 255)
15
+ page = image.active
16
+ assert { page }
17
+ assert { page.w == test_w }
18
+ assert { page.h == test_h }
19
+ page.fill(red)
20
+ page.putpixel(1, 1, green)
21
+ page.putpixel(10, 10, blue)
22
+ assert { image.getpixel(1, 1) == green }
23
+ assert { image.getpixel(10, 10) == blue }
24
+ assert { image.getpixel(0, 0) == red }
25
+
26
+ image2 = Pruim::Image.new(2, 3, :data => [ 1, 2, 3, 4, 5, 6])
27
+ assert { image2 }
28
+ assert { image2.getpixel(0, 0) == 1 }
29
+ assert { image2.getpixel(1, 1) == 4 }
30
+ assert { image2.getpixel(1, 2) == 6 }
31
+
32
+
33
+
34
+
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+ require 'pruim'
3
+
4
+ test_w = 2
5
+ test_h = 3
6
+ assert { Pruim }
7
+ assert { Pruim::Page }
8
+ page = Pruim::Page.new(nil, test_w, test_h, :data => [1, 2, 3, 4, 5, 6] )
9
+ assert { page }
10
+ assert { page.w == test_w }
11
+ assert { page.h == test_h }
12
+ assert { page.getpixel(0,0) == 1 }
13
+ assert { page.getpixel(1,2) == 6 }
14
+
15
+
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+ require 'pruim'
3
+
4
+ test_w, test_h = 16, 16
5
+ test_comment = "Test PBM file for Pruim."
6
+ assert { Pruim }
7
+ assert { Pruim::Image }
8
+ image = Pruim::Image.new(test_w, test_h, :mode => :monochrome, :pages => 1)
9
+ page = image.active
10
+ page.fill(false)
11
+ page.putpixel(1, 1, true)
12
+ page.putpixel(10, 10, true)
13
+ image.comment = test_comment
14
+
15
+ outname = test_file("out.pbm")
16
+ assert { image.save_as(outname, "pbm") }
17
+
18
+ image2 = Pruim::Image.load_from(outname, "pbm")
19
+ #
20
+ # fin = File.open(outname, 'r+')
21
+ # image2 = nil
22
+ # assert { image2 = codec.decode(fin) }
23
+ # fin.close
24
+ page2 = image2.active
25
+ assert { page2 }
26
+ assert { image2.w == test_w }
27
+ assert { image2.h == test_h }
28
+ assert { page2.w == test_w }
29
+ assert { page2.h == test_h }
30
+ assert { page2.getpixel!(1, 1) }
31
+ assert { page2.getpixel!(10, 10) }
32
+ assert { !(page2.getpixel!(0, 0)) }
33
+ assert { image2.getpixel(1, 1) }
34
+ assert { image2.getpixel(10, 10) }
35
+ assert { !(image2.getpixel(0, 0)) }
36
+ assert { image2.comment == test_comment }
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+ require 'pruim'
3
+
4
+ test_w, test_h = 64, 32
5
+ test_comment = "Test PPM file for Pruim."
6
+ assert { Pruim }
7
+ assert { Pruim::Image }
8
+ image = Pruim::Image.new(test_w, test_h, :pages => 1)
9
+ red = Pruim::Color.rgb(255, 0, 0)
10
+ green = Pruim::Color.rgb(0 ,255, 255)
11
+ blue = Pruim::Color.rgb(0 , 0, 255)
12
+ page = image.active
13
+ page.fill(red)
14
+ page.putpixel(1, 1, green)
15
+ page.putpixel(10, 10, blue)
16
+ image.comment = test_comment
17
+
18
+ outname = test_file("out.ppm")
19
+ assert { image.save_as(outname, "ppm") }
20
+
21
+ image2 = Pruim::Image.load_from(outname, "ppm")
22
+ #
23
+ # fin = File.open(outname, 'r+')
24
+ # image2 = nil
25
+ # assert { image2 = codec.decode(fin) }
26
+ # fin.close
27
+ page2 = image2.active
28
+ assert { page2 }
29
+ assert { image2.w == test_w }
30
+ assert { image2.h == test_h }
31
+ assert { page2.w == test_w }
32
+ assert { page2.h == test_h }
33
+
34
+ assert { page2.getpixel!(1, 1) == green }
35
+ assert { page2.getpixel!(10, 10) == blue }
36
+ assert { page2.getpixel!(0, 0) == red }
37
+
38
+ assert { image2.getpixel(1, 1) == green }
39
+ assert { image2.getpixel(10, 10) == blue }
40
+ assert { image2.getpixel(0, 0) == red }
41
+ assert { image2.comment == test_comment }
42
+
43
+
44
+
@@ -0,0 +1,20 @@
1
+ # require 'nanotest'
2
+ # require 'redgreen'
3
+
4
+ require 'pathname'
5
+ require 'timeout'
6
+
7
+ $: << '.'
8
+
9
+ require 'atto'
10
+ include Atto::Test
11
+
12
+
13
+ def test_file(*fname)
14
+ return File.join('test', *fname)
15
+ end
16
+
17
+
18
+ $: << '../lib'
19
+
20
+
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+ require 'pruim'
3
+
4
+ test_w, test_h = 64, 32
5
+
6
+ assert { Pruim }
7
+ assert { Pruim::Image }
8
+ image = Pruim::Image.new(test_w, test_h)
9
+ assert { image }
10
+ assert { image.w == test_w }
11
+ assert { image.h == test_h }
12
+ red = Pruim::Color.rgb(255, 0, 0)
13
+ green = Pruim::Color.rgb(0 ,255, 255)
14
+ blue = Pruim::Color.rgb(0 , 0, 255)
15
+ page = image.new_page(test_w, test_h)
16
+ assert { page }
17
+ assert { page.w == test_w }
18
+ assert { page.h == test_h }
19
+ page.fill(red)
20
+ page.putpixel(1, 1, green)
21
+ page.putpixel(10, 10, blue)
22
+ outname = test_file("out.ppm")
23
+ fout = File.open(outname, 'w+')
24
+ codec = Pruim::Codec.new_codec_for('ppm')
25
+ assert { codec }
26
+ assert { codec.encode(image, fout) }
27
+ fout.close
28
+ # system("eog #{outname} &")
29
+ fin = File.open(outname, 'r+')
30
+ image2 = nil
31
+ assert { image2 = codec.decode(fin) }
32
+ fin.close
33
+ page2 = image2.pages.first
34
+ assert { page2 }
35
+ assert { image2.w == test_w }
36
+ assert { image2.h == test_h }
37
+ assert { page2.w == test_w }
38
+ assert { page2.h == test_h }
39
+
40
+ assert { page2.getpixel!(1, 1) == green }
41
+ assert { page2.getpixel!(10, 10) == blue }
42
+ assert { page2.getpixel!(0, 0) == red }
43
+
44
+
45
+
46
+
47
+
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pruim
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - beoran
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-12-13 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bindata
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: atto
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.9.2
35
+ type: :development
36
+ version_requirements: *id002
37
+ description: " Pruim is a Pure Ruby Image Library. Currently only supports BMP, PPM and PBM\n formats, but preserves palette ordering on reading and writing.\n"
38
+ email: beoran@rubyforge.org
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - README
47
+ - Rakefile
48
+ - lib/pruim.rb
49
+ - lib/pruim/pbm.rb
50
+ - lib/pruim/bmp.rb
51
+ - lib/pruim/ppm.rb
52
+ - lib/pruim/page.rb
53
+ - lib/pruim/image.rb
54
+ - lib/pruim/palette.rb
55
+ - lib/pruim/codec.rb
56
+ - lib/pruim/color.rb
57
+ - lib/pruim/movie.rb
58
+ - test/test_helper.rb
59
+ - test/test_image.rb
60
+ - test/atto.rb
61
+ - test/pruim/test_bmp.rb
62
+ - test/pruim/test_image.rb
63
+ - test/pruim/test_pbm.rb
64
+ - test/pruim/test_ppm.rb
65
+ - test/pruim/test_page.rb
66
+ homepage: http://github.com/beoran/pruim
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options: []
71
+
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.12
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Pure Ruby Image Library.
93
+ test_files: []
94
+