ruby-pixels 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/COPYING.GPL +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/lib/pixels.rb +636 -0
  4. metadata +56 -0
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
@@ -0,0 +1,636 @@
1
+ ######################################################################
2
+ # Author(s)::
3
+ # Dwayne C. Litzenberger (http://www.dlitz.net)
4
+ ######################################################################
5
+ # Copyright::
6
+ # Copyright (c) 2009 Dwayne C. Litzenberger <dlitz@dlitz.net>
7
+ # License::
8
+ # This file is part of Ruby Pixels.
9
+ #
10
+ # Ruby Pixels is free software: you can redistribute it and/or modify it
11
+ # under the terms of the GNU Lesser General Public License as published
12
+ # by the Free Software Foundation, either version 3 of the License, or
13
+ # (at your option) any later version.
14
+ #
15
+ # Ruby Pixels is distributed in the hope that it will be useful,
16
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ # GNU Lesser General Public License for more details.
19
+ #
20
+ # You should have received a copy of the GNU Lesser General Public
21
+ # License along with Ruby Pixels. If not, see
22
+ # <http://www.gnu.org/licenses/>.
23
+ ######################################################################
24
+ # Homepage:: http://www.dlitz.net/software/ruby-pixels
25
+ #
26
+ # The TGA format is documented at several places on the web, such as:
27
+ # http://local.wasp.uwa.edu.au/~pbourke/dataformats/tga/
28
+ #
29
+
30
+ require 'thread'
31
+
32
+ #
33
+ # Ruby Pixels allows you to read and write RGB/RGBA pixel data stored in
34
+ # uncompressed, non-interleaved TGA (Targa) files.
35
+ #
36
+ # Unlike some other libraries, Ruby Pixels reads and writes one row of pixels
37
+ # at a time, you can work with several large images at once without running
38
+ # out of memory.
39
+ #
40
+ # = Requirements
41
+ #
42
+ # Ruby Pixels needs no external libraries.
43
+ #
44
+ # = Limitations
45
+ #
46
+ # Ruby Pixels cannot read or write compressed, interleaved, or colour-mapped
47
+ # images. You may wish to use another tool (e.g. MiniMagick) to convert
48
+ # to and from other formats.
49
+ #
50
+ # Ruby Pixels currently has no support for reading or writing individual
51
+ # pixels. You need to do it on a row-by-row basis.
52
+ #
53
+ # = Example Code
54
+ #
55
+ # # invert-image.rb - Invert the colour of an image.
56
+ # require 'pixels'
57
+ #
58
+ # input = Pixels.open_tga("mm/mm-01.tga")
59
+ # output = Pixels.create_tga("output.tga", input.spec)
60
+ #
61
+ # input.each_row_rgb do |in_row, y|
62
+ # out_row = []
63
+ # for r, g, b in in_row
64
+ # out_row << [255-r, 255-g, 255-b]
65
+ # end
66
+ # output.put_row_rgb(y, out_row)
67
+ # end
68
+ #
69
+ # output.close
70
+ # input.close
71
+ #
72
+ module Pixels
73
+
74
+ VERSION = '0.0.1'
75
+
76
+ class DataFormatError < StandardError
77
+ end
78
+
79
+ # Open the specified TGA file.
80
+ #
81
+ # file_or_path may be a pathname or a file-like object. If it is a
82
+ # pathname, it is opened for reading.
83
+ #
84
+ # Returns an instance of one of TargaBase's children (one of Targa15,
85
+ # Targa16, Targa24, or Targa32), depending on the format of the specified
86
+ # file.
87
+ def self.open_tga(file_or_path)
88
+ if file_or_path.respond_to?(:read)
89
+ file = file_or_path
90
+ else
91
+ file = File.open(file_or_path, "rb:binary")
92
+ end
93
+
94
+ raw_header = file.read(18)
95
+ h, r = decode_tga_header(raw_header)
96
+ case [h[:bits_per_pixel], r[:alpha_depth]]
97
+ when [16, 0]
98
+ return Targa15.new(file, h, r, 2)
99
+ when [16, 1]
100
+ return Targa16.new(file, h, r, 2)
101
+ when [24, 0]
102
+ return Targa24.new(file, h, r, 3)
103
+ when [32, 0]
104
+ return Targa24.new(file, h, r, 4)
105
+ when [32, 8]
106
+ return Targa32.new(file, h, r, 4)
107
+ else
108
+ raise DataFormatError.new(
109
+ "#{h[:bits_per_pixel]} bpp with #{h[:alpha_depth]}-bit alpha channel not supported")
110
+ end
111
+ end
112
+
113
+ # Create a TGA file according to the given specification, and return an
114
+ # instance of one of TargaBase's children (one of Targa15, Targa16, Targa24,
115
+ # or Targa32), depending on the format specification.
116
+ #
117
+ # file_or_path may be a pathname or a file-like object. If it is a
118
+ # pathname, it is opened for reading.
119
+ #
120
+ # spec is a Hash containing the following keys:
121
+ # [:width]
122
+ # Width of the image in pixels
123
+ #
124
+ # [:height]
125
+ # Height of the image in pixels
126
+ #
127
+ # [:color_depth]
128
+ # Color depth of the image in bits per pixel. This does not include the
129
+ # bits used to represent the alpha channel (if any).
130
+ #
131
+ # Currently, this is always one of [15, 24].
132
+ #
133
+ # [:has_alpha]
134
+ # If the image has an alpha channel, this is true. Otherwise, it is
135
+ # false. (default false)
136
+ #
137
+ # [:origin]
138
+ # Specifies which pixel appears first in the TGA file. Must be one of
139
+ # :UPPER_LEFT or :LOWER_LEFT.
140
+ #
141
+ # Note: This only affects the TGA file's internal layout.
142
+ # get_row_rgb(0) always returns the uppermost row in Ruby Pixels.
143
+ # (default :UPPER_LEFT)
144
+ def self.create_tga(file_or_path, spec={})
145
+ spec = {
146
+ :width => nil,
147
+ :height => nil,
148
+ :color_depth => nil,
149
+ :has_alpha => false,
150
+ :origin => :UPPER_LEFT,
151
+ }.merge(spec)
152
+
153
+ case [spec[:color_depth], !!spec[:has_alpha]]
154
+ when [15, true]
155
+ bpp = 16
156
+ alpha_depth = 1
157
+ when [16, false]
158
+ bpp = 16
159
+ alpha_depth = 0
160
+ when [24, false]
161
+ bpp = 24
162
+ alpha_depth = 0
163
+ when [24, true]
164
+ bpp = 32
165
+ alpha_depth = 8
166
+ else
167
+ raise ArgumentError.new(
168
+ ":depth=#{colour_depth}-bpp with :alpha=#{has_alpha} not supported")
169
+ end
170
+
171
+ image_descriptor = alpha_depth
172
+ case spec[:origin]
173
+ when :LOWER_LEFT
174
+ # Do nothing
175
+ when :UPPER_LEFT
176
+ image_descriptor |= 0x20
177
+ else
178
+ raise ArgumentError.new(":origin must be :LOWER_LEFT or :UPPER_LEFT")
179
+ end
180
+
181
+ raw_header = [
182
+ 0, # idlength
183
+ 0, # colourmap type
184
+ 2, # data type: Uncompressed RGB(A)
185
+ 0, # colourmap_origin
186
+ 0, # colourmap_length
187
+ 0, # colourmap_depth
188
+ 0, # x_origin
189
+ 0, # y_origin
190
+ spec[:width],
191
+ spec[:height],
192
+ bpp,
193
+ image_descriptor,
194
+ ].pack("CCCvvCvvvvCC")
195
+
196
+ h, r = decode_tga_header(raw_header)
197
+
198
+ if file_or_path.respond_to?(:write)
199
+ file = file_or_path
200
+ else
201
+ file = File.open(file_or_path, "w+b:binary")
202
+ end
203
+
204
+ file.write(raw_header)
205
+ file.seek(0, IO::SEEK_SET)
206
+ return open_tga(file)
207
+ end
208
+
209
+ def self.decode_tga_header(raw_header) #:nodoc:
210
+ h = {}
211
+ h[:idlength], h[:colourmap_type], h[:data_type_code], h[:colourmap_origin],
212
+ h[:colourmap_length], h[:colourmap_depth], h[:x_origin], h[:y_origin],
213
+ h[:width], h[:height], h[:bits_per_pixel], h[:image_descriptor] =
214
+ raw_header.unpack("CCCvvCvvvvCC")
215
+
216
+ # Data type
217
+ if h[:data_type_code] != 2
218
+ raise DataFormatError.new(
219
+ "Only uncompressed, unmapped RGB or RGBA data is supported (is this a TGA file?)")
220
+ end
221
+
222
+ r = {}
223
+ r[:width] = h[:width]
224
+ r[:height] = h[:height]
225
+ r[:image_data_offset] = 18 + h[:idlength] + h[:colourmap_length]
226
+
227
+ r[:bpp] = h[:bits_per_pixel]
228
+ r[:alpha_depth] = h[:image_descriptor] & 0xf
229
+ r[:color_depth] = h[:bits_per_pixel] - r[:alpha_depth]
230
+ r[:origin] = (h[:image_descriptor] & 0x20 != 0) ? :UPPER_LEFT : :LOWER_LEFT
231
+
232
+ # Interleaving
233
+ if (h[:image_descriptor] & 0xc0) != 0
234
+ raise DataFormatError.new("Interleaved data not supported")
235
+ end
236
+
237
+ return [h, r]
238
+ end
239
+
240
+ # Abstract class
241
+ class TargaBase
242
+ # Width of the image (pixels)
243
+ attr_reader :width
244
+
245
+ # Height of the image (pixels)
246
+ attr_reader :height
247
+
248
+ # Number of bits used to store each pixel
249
+ attr_reader :bpp
250
+
251
+ # Color-depth of the image (bits per pixel)
252
+ attr_reader :color_depth
253
+
254
+ # Bit-depth of the alpha channel (bits per pixel)
255
+ attr_reader :alpha_depth
256
+
257
+ # Indicates which pixel appears first in the TGA file.
258
+ # One of :UPPER_LEFT or :LOWER_LEFT.
259
+ attr_reader :origin
260
+
261
+ # Do not instantiate this object directly. Use from_file.
262
+ def initialize(file, header, instance_vars, bytes_per_pixel)
263
+ @mutex = Mutex.new # Obtain this lock whenever you use @file
264
+ @file = file
265
+ @header = header
266
+ for k, v in instance_vars
267
+ instance_variable_set("@#{k.to_s}", v)
268
+ end
269
+ @bytes_per_pixel = bytes_per_pixel
270
+ @bytes_per_row = bytes_per_pixel * @width
271
+ end
272
+
273
+ # Return a Hash containing the file format specification, which can be used as the "spec"
274
+ # parameter in Pixels::create_tga.
275
+ def spec
276
+ return {
277
+ :width => width,
278
+ :height => height,
279
+ :color_depth => color_depth,
280
+ :has_alpha => has_alpha?,
281
+ :origin => origin,
282
+ }
283
+ end
284
+
285
+ # Return a string containing the raw bytes from the row at the
286
+ # specified y-coordinate.
287
+ #
288
+ # You probably want to use get_row_rgb or get_row_rgba instead.
289
+ def read_row_bytes(y)
290
+ @mutex.synchronize {
291
+ @file.seek(row_offset(y), IO::SEEK_SET)
292
+ return @file.read(@bytes_per_row)
293
+ }
294
+ end
295
+
296
+ # Write a string containing the raw bytes for a row Return a string containing the raw bytes from the row at the
297
+ # specified y-coordinate.
298
+ #
299
+ # You probably want to use put_row_rgb or put_row_rgba instead.
300
+ def write_row_bytes(y, raw_data)
301
+ if raw_data.length != @bytes_per_row
302
+ raise ArgumentError.new("raw_data.length was #{raw_data.length}, expected #{@bytes_per_row}")
303
+ end
304
+ @mutex.synchronize {
305
+ @file.seek(row_offset(y), IO::SEEK_SET)
306
+ return @file.write(raw_data)
307
+ }
308
+ end
309
+
310
+ # Close the underlying file.
311
+ def close
312
+ @mutex.synchronize {
313
+ @file.close
314
+ @file = nil
315
+ @mutex = nil
316
+ }
317
+ end
318
+
319
+ # Iterate through each row of the image, representing each pixel as an RGB
320
+ # value.
321
+ #
322
+ # For each y-coordinate in the image, this method calls the given block
323
+ # with two arguments: get_row_rgb(y) and y.
324
+ #
325
+ # If no block is provided, an Enumerator is returned.
326
+ def each_row_rgb
327
+ return Enumerable::Enumerator.new(self, :each_row_rgb) unless block_given?
328
+ for y in (0..@height-1)
329
+ yield get_row_rgb(y), y
330
+ end
331
+ end
332
+
333
+ # Iterate through each row of the image, representing each pixel as an
334
+ # RGBA value.
335
+ #
336
+ # For each y-coordinate in the image, this method calls the given block
337
+ # with two arguments: get_row_rgba(y) and y.
338
+ #
339
+ # If no block is provided, an Enumerator is returned.
340
+ def each_row_rgba
341
+ return Enumerable::Enumerator.new(self, :each_row_rgba) unless block_given?
342
+ for y in (0..@height-1)
343
+ yield get_row_rgba(y), y
344
+ end
345
+ end
346
+
347
+ # Return the row of pixels having the specified y-coordinate. The row is
348
+ # represented as an array of [r, g, b] values for each pixel in the row.
349
+ #
350
+ # Each r, g, b value is an integer between 0 and 255.
351
+ def get_row_rgb(y)
352
+ return get_row(y).map { |color| rgb_from_color(color) }
353
+ end
354
+
355
+ # Return the row of pixels having the specified y-coordinate. The row is
356
+ # represented as an array of [r, g, b, a] values for each pixel in the row.
357
+ #
358
+ # Each r, g, b, a value is an integer between 0 and 255.
359
+ def get_row_rgba(y)
360
+ return get_row(y).map { |color| rgba_from_color(color) }
361
+ end
362
+
363
+ # Replace the row of pixels having the specified y-coordinate. The row is
364
+ # represented as an array of [r, g, b] values for each pixel in the row.
365
+ #
366
+ # Each r, g, b value is an integer between 0 and 255.
367
+ def put_row_rgb(y, row_rgb)
368
+ return put_row(y, row_rgb.map { |r, g, b| color_from_rgb(r, g, b) })
369
+ end
370
+
371
+ # Replace the row of pixels having the specified y-coordinate. The row is
372
+ # represented as an array of [r, g, b, a] values for each pixel in the row.
373
+ #
374
+ # Each r, g, b, a value is an integer between 0 and 255.
375
+ def put_row_rgba(y, row_rgba)
376
+ return put_row(y, row_rgba.map { |r, g, b, a| color_from_rgba(r, g, b, a) })
377
+ end
378
+
379
+ protected
380
+
381
+ # Return the offset in the file where the specified row can be found.
382
+ def row_offset(y)
383
+ if y < 0 or y >= @height
384
+ raise ArgumentError.new("y-coordinate #{y} out of range")
385
+ end
386
+
387
+ # Flip the vertical axis when (0, 0) is in the lower-left
388
+ # corner of the image.
389
+ if @origin == :LOWER_LEFT
390
+ y = (@height-1) - y
391
+ end
392
+
393
+ return @image_data_offset + @bytes_per_row * y
394
+ end
395
+ end
396
+
397
+ # Mix-in module for image types having no alpha channel.
398
+ #
399
+ # It makes has_alpha? return false, and it emulates rgba_from_color and
400
+ # color_from_rgba.
401
+ module NoAlphaChannel
402
+
403
+ # Return true of the image has an alpha channel. Otherwise, return false.
404
+ def has_alpha?
405
+ false
406
+ end
407
+
408
+ # Given an integer colour value, return separate [r, g, b, 255] values.
409
+ #
410
+ # This is a wrapper around rgb_from_color. The alpha channel is always
411
+ # set fully opaque.
412
+ def rgba_from_color(color)
413
+ return rgb_from_color(color) + [255]
414
+ end
415
+
416
+ # Given separate [r, g, b, a] values, return the integer colour value.
417
+ #
418
+ # This is a wrapper around color_from_rgb. The alpha channel is ignored.
419
+ def color_from_rgba(r, g, b, a)
420
+ return color_from_rgb(r, g, b)
421
+ end
422
+ end
423
+
424
+ # Mix-in module for image types having an alpha channel.
425
+ #
426
+ # It makes has_alpha? return true, and it emulates rgb_from_color and
427
+ # color_from_rgb.
428
+ module HasAlphaChannel
429
+ # Return true of the image has an alpha channel. Otherwise, return false.
430
+ def has_alpha?
431
+ true
432
+ end
433
+
434
+ # Given an integer colour value, return separate [r, g, b] values.
435
+ #
436
+ # This is a wrapper around rgba_from_color. The alpha channel is ignored.
437
+ def rgb_from_color(color)
438
+ return rgba_from_color(color)[0..2]
439
+ end
440
+
441
+ # Given separate [r, g, b] values, return the integer colour value.
442
+ #
443
+ # This is a wrapper around color_from_rgba. The alpha channel is always
444
+ # set fully opaque.
445
+ def color_from_rgb(r, g, b)
446
+ return color_from_rgba(r, g, b, 255)
447
+ end
448
+ end
449
+
450
+ class Targa15 < TargaBase
451
+ include NoAlphaChannel
452
+
453
+ # You probably want to use TargaBase#get_row_rgb or TargaBase#get_row_rgba instead.
454
+ def get_row(y)
455
+ bytes = read_row_bytes(y)
456
+ row = []
457
+ for offset in (0..@width*@bytes_per_pixel-1).step(@bytes_per_pixel)
458
+ v, = bytes[offset,2].unpack("v")
459
+ row << (v & 0x7fff)
460
+ end
461
+ return row
462
+ end
463
+
464
+ # You probably want to use TargaBase#put_row_rgb or TargaBase#put_row_rgba instead.
465
+ def put_row(y, row)
466
+ bytes = row.pack("v" * row.length)
467
+ write_row_bytes(y, bytes)
468
+ end
469
+
470
+ # Given a 15-bit integer colour value, return separate [r, g, b] values.
471
+ #
472
+ # Each r, g, b value is an integer between 0 and 255.
473
+ def rgb_from_color(color)
474
+ # Extract 5 bits-per-channel values
475
+ b5 = color & 0x1f
476
+ g5 = (color >> 5) & 0x1f
477
+ r5 = (color >> 10) & 0x1f
478
+
479
+ # Convert 5 bits-per-channel to 8 bits-per-channel
480
+ r8 = r5 * 255 / 31
481
+ g8 = g5 * 255 / 31
482
+ b8 = b5 * 255 / 31
483
+ return [r, g, b]
484
+ end
485
+
486
+ # Return a 15-bit integer pixel value given separate red, green, and blue values.
487
+ #
488
+ # Each r, g, b value is an integer between 0 and 255.
489
+ def color_from_rgb(r, g, b)
490
+ # Convert 8 bits-per-channel to 5 bits-per-channel
491
+ r5 = (r.to_i >> 3) & 0x1f
492
+ g5 = (g.to_i >> 3) & 0x1f
493
+ b5 = (b.to_i >> 3) & 0x1f
494
+ return (b5 << 10) | (g5 << 5) | r5
495
+ end
496
+ end
497
+
498
+ class Targa16 < TargaBase
499
+ include HasAlphaChannel
500
+
501
+ # You probably want to use TargaBase#get_row_rgb or TargaBase#get_row_rgba instead.
502
+ def get_row(y)
503
+ bytes = read_row_bytes(y)
504
+ row = []
505
+ for offset in (0..@width*@bytes_per_pixel-1).step(@bytes_per_pixel)
506
+ v, = bytes[offset,2].unpack("v")
507
+ row << v
508
+ end
509
+ return row
510
+ end
511
+
512
+ # You probably want to use TargaBase#put_row_rgb or TargaBase#put_row_rgba instead.
513
+ def put_row(y, row)
514
+ bytes = row.pack("v" * row.length)
515
+ write_row_bytes(y, bytes)
516
+ end
517
+
518
+ # Given a 16-bit integer colour value, return separate [r, g, b, a] values.
519
+ #
520
+ # Each r, g, b, a value is an integer between 0 and 255.
521
+ def rgba_from_color(color)
522
+ # Extract 5 bits-per-channel values
523
+ b5 = color & 0x1f
524
+ g5 = (color >> 5) & 0x1f
525
+ r5 = (color >> 10) & 0x1f
526
+ a1 = (color >> 15) & 1
527
+
528
+ # Convert 5 bits-per-channel to 8 bits-per-channel
529
+ r8 = r5 * 255 / 31
530
+ g8 = g5 * 255 / 31
531
+ b8 = b5 * 255 / 31
532
+ a8 = (a1 > 0) ? 255 : 0
533
+ return [r8, g8, b8, a8]
534
+ end
535
+
536
+ # Return a 16-bit integer pixel value given separate red, green, blue, and alpha values.
537
+ #
538
+ # Each r, g, b, a value is an integer between 0 and 255.
539
+ def color_from_rgba(r, g, b, a)
540
+ # Convert 8 bits-per-channel to 5 bits-per-channel
541
+ r5 = (r.to_i >> 3) & 0x1f
542
+ g5 = (g.to_i >> 3) & 0x1f
543
+ b5 = (b.to_i >> 3) & 0x1f
544
+ a1 = (a.to_i >> 7) & 1
545
+ return (a1 << 15) | (b5 << 10) | (g5 << 5) | r5
546
+ end
547
+ end
548
+
549
+ class Targa24 < TargaBase
550
+ include NoAlphaChannel
551
+
552
+ # You probably want to use TargaBase#get_row_rgb or TargaBase#get_row_rgba instead.
553
+ def get_row(y)
554
+ bytes = read_row_bytes(y)
555
+ row = []
556
+ for offset in (0..@width*@bytes_per_pixel-1).step(@bytes_per_pixel)
557
+ v, = (bytes[offset,3] + "\x00").unpack("V")
558
+ row << (v & 0x00ffffff)
559
+ end
560
+ return row
561
+ end
562
+
563
+ # You probably want to use TargaBase#put_row_rgb or TargaBase#put_row_rgba instead.
564
+ def put_row(y, row)
565
+ bytes = row.map{|v| [v].pack("V")[0..2]}.join
566
+ write_row_bytes(y, bytes)
567
+ end
568
+
569
+ # Given a 24-bit integer colour value, return separate [r, g, b] values.
570
+ #
571
+ # Each r, g, b value is an integer between 0 and 255.
572
+ def rgb_from_color(color)
573
+ # Extract 8-bit-per-channel values
574
+ b = color & 0xff
575
+ g = (color >> 8) & 0xff
576
+ r = (color >> 16) & 0xff
577
+ return [r, g, b]
578
+ end
579
+
580
+ # Return a 24-bit integer pixel value given separate red, green, and blue values.
581
+ #
582
+ # Each r, g, b value is an integer between 0 and 255.
583
+ def color_from_rgb(r, g, b)
584
+ # Pack 8-bit-per-channel values
585
+ return ((r.to_i & 0xff) << 16) |
586
+ ((g.to_i & 0xff) << 8) |
587
+ (b.to_i & 0xff)
588
+ end
589
+ end
590
+
591
+ class Targa32 < TargaBase
592
+ include HasAlphaChannel
593
+
594
+ # You probably want to use TargaBase#get_row_rgb or TargaBase#get_row_rgba instead.
595
+ def get_row(y)
596
+ bytes = read_row_bytes(y)
597
+ row = []
598
+ for offset in (0..@width*@bytes_per_pixel-1).step(@bytes_per_pixel)
599
+ row += bytes[offset,4].unpack("V")
600
+ end
601
+ return row
602
+ end
603
+
604
+ # You probably want to use TargaBase#put_row_rgb or TargaBase#put_row_rgba instead.
605
+ def put_row(y, row)
606
+ bytes = row.pack("V" * row.length)
607
+ write_row_bytes(y, bytes)
608
+ end
609
+
610
+ # Given a 16-bit integer colour value, return separate [r, g, b, a] values.
611
+ #
612
+ # Each r, g, b, a value is an integer between 0 and 255.
613
+ def rgba_from_color(color)
614
+ # Extract 8-bit-per-channel values
615
+ b = color & 0xff
616
+ g = (color >> 8) & 0xff
617
+ r = (color >> 16) & 0xff
618
+ a = (color >> 24) & 0xff
619
+ return [r, g, b, a]
620
+ end
621
+
622
+ # Return a 32-bit integer pixel value given separate red, green, blue, and alpha values.
623
+ #
624
+ # Each r, g, b, a value is an integer between 0 and 255.
625
+ def color_from_rgba(r, g, b, a)
626
+ # Pack 8-bit-per-channel values
627
+ return ((a.to_i & 0xff) << 24) |
628
+ ((r.to_i & 0xff) << 16) |
629
+ ((g.to_i & 0xff) << 8) |
630
+ (b.to_i & 0xff)
631
+ end
632
+ end
633
+
634
+ end # Targa
635
+
636
+ # vim:set ts=2 sw=2 sts=2 expandtab: