axon 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ === 0.2.0 / 2012-??-??
2
+
3
+ * JRuby Support
4
+ * Add Axon.jpeg_file, Axon.png_file, Axon#jpeg_file, and Axon#png_file
5
+ * Removed #color_model since we can use #components.
6
+
1
7
  === 0.1.1 / 2012-01-06
2
8
 
3
9
  * Add a .gemtest file. Trying rubygems-test.
@@ -4,12 +4,8 @@ http://github.com/ender672/axon
4
4
 
5
5
  == DESCRIPTION:
6
6
 
7
- axon reads, manipulates, and writes images.
8
-
9
- axon depends only on libjpeg and libpng.
10
-
11
- axon never stores an entire image in memory. All images and operations are
12
- streamed. This keeps memory requirements and latency low.
7
+ Read, manipulate, and write images with an emphasis on speed and a low memory
8
+ profile.
13
9
 
14
10
  == FEATURES:
15
11
 
@@ -20,60 +16,38 @@ streamed. This keeps memory requirements and latency low.
20
16
 
21
17
  == SYNOPSIS:
22
18
 
23
- # Short, chained example. Reads a JPEG from io_in and writes scaled png to
24
- # io_out.
25
- Axon.jpeg(io_in).fit(100, 100).png(io_out)
26
-
27
- # Longer example, reads the JPEG header, looks at properties and header
28
- # values, sets decompression options, scales the image, sets compression
29
- # options, and writes a JPEG.
30
- reader = Axon::JPEG::Reader(io, [:APP2])
31
-
32
- puts reader.width #=> Integer
33
- puts reader.height #=> Integer
34
- puts reader[:APP2] #=> Array of Strings
35
- reader.scale_denom = 4 # Image will be scaled by 1/4 on read
36
-
37
- fitted = Axon::Fit.new(reader, 100, 100) # Fits image in a 100x100 box
38
- JPEG.write(fitted, io, :quality => 88)
39
-
40
- == BASIC API:
41
-
42
- All image objects are expected to respond to the following methods:
43
-
44
- * Image#height
45
- Output height of the image.
46
- * Image#width
47
- Output width of the image.
48
- * Image#color_model
49
- Can be :GRAYSCALE or :RGB
50
- * Image#components
51
- RGB image with alpha has 4 components.
52
- RGB image has 3 components.
53
- Grayscale image with alpha has 2 components.
54
- Grayscale image has 1 component.
55
- * Image#gets
56
- Returns the next line in the image as a binary ruby string.
57
- * Image#lineno
58
- The line number of the next line that will be returned, starting at 0.
59
-
60
- A Reader object has the same methods as an Image object, with added methods that
61
- are specific to the decoding of the image format.
19
+ # Read a JPEG and write it as a scaled PNG.
20
+ Axon.jpeg_file("image.jpg") do |image|
21
+ image.fit(100, 100)
22
+ image.png_file("image.png")
23
+ end
62
24
 
63
25
  == REQUIREMENTS:
64
26
 
65
- IJG JPEG Library Version 6b or later.
66
- pnglib version 1.2.x or later.
27
+ The JRuby extension uses the Java Image I/O API.
28
+
29
+ The MRI/Rubinius extension uses:
30
+
31
+ * IJG JPEG Library Version 6b or later.
32
+ * pnglib version 1.2.x or later.
33
+
34
+ Installing libjpeg headers (OSX):
35
+
36
+ $ brew install libjpeg # requires homebrew (http://mxcl.github.com/homebrew/)
37
+
38
+ Installing libjpeg and libpng headers (Debian/Ubuntu):
39
+
40
+ $ sudo apt-get install libjpeg-dev libpng-dev
67
41
 
68
42
  == INSTALL:
69
43
 
70
- gem install axon
44
+ $ gem install axon
71
45
 
72
46
  == LICENSE:
73
47
 
74
48
  (The MIT License)
75
49
 
76
- Copyright (c) 2011
50
+ Copyright (c) 2012
77
51
 
78
52
  * {Timothy Elliott}[http://holymonkey.com]
79
53
 
data/Rakefile CHANGED
@@ -1,21 +1,36 @@
1
1
  require 'rake/clean'
2
2
  require 'rake/testtask'
3
3
 
4
+ # Java compiling
5
+ def quote_file_list(l)
6
+ list = FileList.new(l)
7
+ (list.to_a.map { |f| "'#{f}'" }).join(' ')
8
+ end
9
+
10
+ file 'lib/axon/axon.jar' => FileList.new('ext/java/axon/*.java') do
11
+ cd 'ext/java' do
12
+ sh "javac -g -cp #{Config::CONFIG['prefix']}/lib/jruby.jar #{FileList['axon/*.java']}"
13
+ sh "jar cf axon/axon.jar #{quote_file_list('axon/*.class')}"
14
+ end
15
+
16
+ FileUtils.mv 'ext/java/axon/axon.jar', 'lib/axon/axon.jar'
17
+ end
18
+
19
+ # gcc compiling
4
20
  file 'ext/axon/Makefile' do
5
21
  cd 'ext/axon' do
6
22
  ruby "extconf.rb #{ENV['EXTOPTS']}"
7
23
  end
8
24
  end
9
25
 
10
- file 'ext/axon/axon.so' => FileList.new('ext/axon/Makefile', 'ext/axon/*{.c,.h}') do
26
+ file 'lib/axon/axon.so' => FileList.new('ext/axon/Makefile', 'ext/axon/*{.c,.h}') do
11
27
  cd 'ext/axon' do
12
28
  sh 'make'
13
29
  end
30
+
31
+ FileUtils.mv 'ext/axon/axon.so', 'lib/axon/axon.so'
14
32
  end
15
33
 
16
- CLEAN.add('ext/axon/*{.o,.so,.log}', 'ext/axon/Makefile')
17
- CLOBBER.add('*.gem')
18
-
19
34
  desc 'Clean up Rubinius .rbc files.'
20
35
  namespace :clean do
21
36
  task :rbc do
@@ -24,12 +39,23 @@ namespace :clean do
24
39
  end
25
40
 
26
41
  Rake::TestTask.new do |t|
27
- t.libs += ['test', 'ext']
42
+ t.libs << 'test'
28
43
  t.test_files = FileList['test/test*.rb']
29
44
  t.verbose = true
30
45
  end
31
46
 
32
- desc 'Compile axon'
33
- task :compile => 'ext/axon/axon.so'
47
+ CLEAN.add('ext/axon/*{.o,.so,.log}', 'ext/axon/Makefile')
48
+ CLEAN.add('ext/java/axon/*.class', 'ext/java/axon/axon.jar')
49
+ CLEAN.add('lib/axon/axon{.so,.jar}')
50
+ CLOBBER.add('*.gem')
51
+
52
+ desc 'Build the gem'
53
+ task :gem => "lib/axon/axon.jar" do
54
+ system "gem build axon.gemspec"
55
+ end
56
+
57
+ desc 'Compile the extension'
58
+ task :compile => "lib/axon/axon.#{RUBY_PLATFORM =~ /java/ ? 'jar' : 'so'}"
59
+
34
60
  task :test => :compile
35
61
  task :default => :test
data/TODO.rdoc CHANGED
@@ -1,5 +1,10 @@
1
1
  = TODO
2
- * get everything running on osx and windows
2
+ * get everything running on windows
3
+ * get all tests passing on jruby
4
+ * for jruby, consider adding kind of a back-door method, e.g.
5
+ gets_as_jruby_bufferedimage that allows internal calls to pass scanlines
6
+ along natively. Something similar might slightly speed up the C extension
7
+ as well by eliminating some memory copies.
3
8
  * have a setting where decode / encode warnings will result in exceptions
4
9
  * add #initialize_copy
5
10
  * Check frozen status on setters, etc.
@@ -19,7 +19,7 @@
19
19
  #define EXIF_MARKER (JPEG_APP0 + 1)
20
20
 
21
21
  #define WRITE_BUFSIZE 1024
22
- #define READ_SIZE 2048
22
+ #define READ_SIZE 1024
23
23
 
24
24
  static ID id_ISLOW, id_IFAST, id_FLOAT, id_DEFAULT, id_FASTEST;
25
25
  static ID id_GRAYSCALE, id_RGB, id_YCbCr, id_CMYK, id_YCCK, id_UNKNOWN;
@@ -45,7 +45,7 @@ struct readerdata {
45
45
  struct jpeg_source_mgr mgr;
46
46
 
47
47
  int header_read;
48
- int locked;
48
+ int decompress_started;
49
49
 
50
50
  VALUE source_io;
51
51
  VALUE buffer;
@@ -156,10 +156,6 @@ empty_output_buffer(j_compress_ptr cinfo)
156
156
  str = rb_str_new(dest->buffer, dest->alloc);
157
157
 
158
158
  write_len = rb_funcall(dest->io, id_write, 1, str);
159
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
160
- if (SYMBOL_P(write_len))
161
- rb_raise(rb_eTypeError, "write returned a symbol.");
162
-
163
159
  write_len_i = (size_t)NUM2INT(write_len);
164
160
  dest->total += write_len_i;
165
161
  if (write_len_i != dest->alloc)
@@ -181,9 +177,6 @@ term_destination(j_compress_ptr cinfo)
181
177
  if (len > 0) {
182
178
  str = rb_str_new(dest->buffer, len);
183
179
  write_len = rb_funcall(dest->io, id_write, 1, str);
184
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
185
- if (SYMBOL_P(write_len))
186
- rb_raise(rb_eTypeError, "write returned a symbol.");
187
180
  write_len_i = (size_t)NUM2INT(write_len);
188
181
  dest->total += write_len_i;
189
182
  if (write_len_i != len)
@@ -238,41 +231,32 @@ write_exif(j_compress_ptr cinfo, char *str, int len)
238
231
  static void
239
232
  write_configure(j_compress_ptr cinfo, VALUE image_in, VALUE quality)
240
233
  {
241
- VALUE color_model, width, components, rb_height;
234
+ VALUE width, components, rb_height;
242
235
  int height;
243
236
 
244
237
  width = rb_funcall(image_in, id_width, 0);
245
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
246
- if (SYMBOL_P(width))
247
- rb_raise(rb_eTypeError, "source image has a symbol for width.");
248
238
  cinfo->image_width = NUM2INT(width);
249
239
 
250
240
  rb_height = rb_funcall(image_in, id_height, 0);
251
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
252
- if (SYMBOL_P(rb_height))
253
- rb_raise(rb_eTypeError, "source image has a symbol for height.");
254
-
255
241
  height = NUM2INT(rb_height);
256
242
  if (height < 1)
257
243
  rb_raise(rb_eRuntimeError, "Source image gave an invalid height.");
258
244
  cinfo->image_height = height;
259
245
 
260
246
  components = rb_funcall(image_in, id_components, 0);
261
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
262
- if (SYMBOL_P(components))
263
- rb_raise(rb_eTypeError, "source image has a symbol for components.");
264
247
  cinfo->input_components = NUM2INT(components);
265
-
266
- color_model = rb_funcall(image_in, id_color_model, 0);
267
- if (SYMBOL_P(color_model))
268
- cinfo->in_color_space = id_to_j_color_space(SYM2ID(color_model));
269
- else
270
- rb_raise(rb_eTypeError, "source image has a non symbol color space");
271
-
272
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
273
- if (SYMBOL_P(quality))
274
- rb_raise(rb_eTypeError, "symbol for quality.");
275
-
248
+
249
+ switch (cinfo->input_components) {
250
+ case 1:
251
+ cinfo->in_color_space = JCS_GRAYSCALE;
252
+ break;
253
+ case 3:
254
+ cinfo->in_color_space = JCS_RGB;
255
+ break;
256
+ default:
257
+ rb_raise(rb_eRuntimeError, "source image has the wrong number of components");
258
+ break;
259
+ }
276
260
 
277
261
  jpeg_set_defaults(cinfo);
278
262
 
@@ -347,7 +331,7 @@ write_jpeg2(VALUE image_in, VALUE io_out, size_t bufsize, VALUE icc_profile,
347
331
  mgr.pub.init_destination = init_destination;
348
332
  mgr.pub.empty_output_buffer = empty_output_buffer;
349
333
  mgr.pub.term_destination = term_destination;
350
- mgr.alloc = 1024;
334
+ mgr.alloc = bufsize;
351
335
  mgr.io = io_out;
352
336
  cinfo.dest = (struct jpeg_destination_mgr *)&mgr;
353
337
 
@@ -394,10 +378,6 @@ write_jpeg(int argc, VALUE *argv, VALUE self)
394
378
 
395
379
  if (!NIL_P(options) && TYPE(options) == T_HASH) {
396
380
  rb_bufsize = rb_hash_aref(options, sym_bufsize);
397
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
398
- if (SYMBOL_P(rb_bufsize))
399
- rb_raise(rb_eTypeError, "symbol for bufsize.");
400
-
401
381
  if (!NIL_P(rb_bufsize)) {
402
382
  bufsize = NUM2INT(rb_bufsize);
403
383
  if (bufsize < 1)
@@ -441,8 +421,8 @@ init_jerror(struct jpeg_error_mgr * err)
441
421
  static void
442
422
  raise_if_locked(struct readerdata *reader)
443
423
  {
444
- if (reader->locked)
445
- rb_raise(rb_eRuntimeError, "Can't modify a locked Reader");
424
+ if (reader->decompress_started)
425
+ rb_raise(rb_eRuntimeError, "Can't modify a Reader after decompress started.");
446
426
  }
447
427
 
448
428
  static void
@@ -624,7 +604,6 @@ initialize(int argc, VALUE *argv, VALUE self)
624
604
  struct readerdata *reader;
625
605
  j_decompress_ptr cinfo;
626
606
  VALUE io, markers;
627
- int i;
628
607
 
629
608
  Data_Get_Struct(self, struct readerdata, reader);
630
609
  raise_if_locked(reader);
@@ -883,28 +862,6 @@ set_dct_method(VALUE self, VALUE dct_method)
883
862
  }
884
863
  }
885
864
 
886
- static VALUE
887
- j_gets2(struct readerdata *reader)
888
- {
889
- struct jpeg_decompress_struct * cinfo;
890
- VALUE sl;
891
- int width, components, sl_width, ret;
892
- JSAMPROW ijg_buffer;
893
-
894
- cinfo = &reader->cinfo;
895
-
896
- width = cinfo->output_width;
897
- components = cinfo->output_components;
898
-
899
- sl_width = width * components;
900
-
901
- sl = rb_str_new(0, sl_width);
902
- ijg_buffer = (JSAMPROW)RSTRING_PTR(sl);
903
- ret = jpeg_read_scanlines(cinfo, &ijg_buffer, 1);
904
-
905
- return ret == 0 ? Qnil : sl;
906
- }
907
-
908
865
  /*
909
866
  * call-seq:
910
867
  * gets -> string or nil
@@ -918,20 +875,29 @@ j_gets2(struct readerdata *reader)
918
875
  static VALUE
919
876
  j_gets(VALUE self)
920
877
  {
921
- struct jpeg_decompress_struct * cinfo;
922
878
  struct readerdata *reader;
879
+ struct jpeg_decompress_struct *cinfo;
880
+ VALUE sl;
881
+ int sl_width, ret;
882
+ JSAMPROW ijg_buffer;
923
883
 
924
884
  Data_Get_Struct(self, struct readerdata, reader);
885
+ cinfo = &reader->cinfo;
925
886
 
926
887
  if (!reader->header_read)
927
888
  read_header(reader, Qnil);
928
889
 
929
- if (reader->locked == 0) {
930
- reader->locked = 1;
931
- jpeg_start_decompress(&reader->cinfo);
890
+ if (!reader->decompress_started) {
891
+ reader->decompress_started = 1;
892
+ jpeg_start_decompress(cinfo);
932
893
  }
933
894
 
934
- return j_gets2(reader);
895
+ sl_width = cinfo->output_width * cinfo->output_components;
896
+ sl = rb_str_new(0, sl_width);
897
+ ijg_buffer = (JSAMPROW)RSTRING_PTR(sl);
898
+
899
+ ret = jpeg_read_scanlines(cinfo, &ijg_buffer, 1);
900
+ return ret == 0 ? Qnil : sl;
935
901
  }
936
902
 
937
903
  /*
@@ -1,8 +1,8 @@
1
1
  #include <ruby.h>
2
2
  #include <png.h>
3
3
 
4
- static ID id_write, id_GRAYSCALE, id_RGB, id_gets, id_width, id_height,
5
- id_color_model, id_components, id_read;
4
+ static ID id_write, id_GRAYSCALE, id_GRAYSCALE_ALPHA, id_RGB, id_RGB_ALPHA,
5
+ id_gets, id_width, id_height, id_color_model, id_components, id_read;
6
6
 
7
7
  struct png_data {
8
8
  png_structp png_ptr;
@@ -17,24 +17,39 @@ struct io_write {
17
17
  };
18
18
 
19
19
  static int
20
- id_to_color_type(ID rb, int components)
20
+ components_to_color_type(int components)
21
21
  {
22
- if (rb == id_GRAYSCALE && components == 1) return PNG_COLOR_TYPE_GRAY;
23
- else if (rb == id_GRAYSCALE && components == 2) return PNG_COLOR_TYPE_GRAY_ALPHA;
24
- else if (rb == id_RGB && components == 3) return PNG_COLOR_TYPE_RGB;
25
- else if (rb == id_RGB && components == 4) return PNG_COLOR_TYPE_RGB_ALPHA;
22
+ switch (components) {
23
+ case 1: return PNG_COLOR_TYPE_GRAY;
24
+ case 2: return PNG_COLOR_TYPE_GRAY_ALPHA;
25
+ case 3: return PNG_COLOR_TYPE_RGB;
26
+ case 4: return PNG_COLOR_TYPE_RGB_ALPHA;
27
+ }
28
+
29
+ rb_raise(rb_eRuntimeError, "Unrecognized number of components.");
30
+ }
31
+
32
+ static ID
33
+ png_color_type_to_id(png_byte color_type)
34
+ {
35
+ switch (color_type) {
36
+ case PNG_COLOR_TYPE_GRAY: return id_GRAYSCALE;
37
+ case PNG_COLOR_TYPE_GRAY_ALPHA: return id_GRAYSCALE_ALPHA;
38
+ case PNG_COLOR_TYPE_RGB: return id_RGB;
39
+ case PNG_COLOR_TYPE_RGB_ALPHA: return id_RGB_ALPHA;
40
+ }
26
41
 
27
- rb_raise(rb_eRuntimeError, "Color Space not recognized.");
42
+ rb_raise(rb_eRuntimeError, "PNG Color Type not recognized.");
28
43
  }
29
44
 
30
45
  static int
31
46
  get_components(png_structp png_ptr, png_infop info_ptr)
32
47
  {
33
48
  switch (png_get_color_type(png_ptr, info_ptr)) {
34
- case PNG_COLOR_TYPE_GRAY: return 1;
35
- case PNG_COLOR_TYPE_GRAY_ALPHA: return 2;
36
- case PNG_COLOR_TYPE_RGB: return 3;
37
- case PNG_COLOR_TYPE_RGB_ALPHA: return 4;
49
+ case PNG_COLOR_TYPE_GRAY: return 1;
50
+ case PNG_COLOR_TYPE_GRAY_ALPHA: return 2;
51
+ case PNG_COLOR_TYPE_RGB: return 3;
52
+ case PNG_COLOR_TYPE_RGB_ALPHA: return 4;
38
53
  }
39
54
 
40
55
  return 0;
@@ -65,11 +80,6 @@ write_data(png_structp png_ptr, png_bytep data, png_size_t length)
65
80
  str = rb_str_new(data, length);
66
81
  iw = (struct io_write *)png_get_io_ptr(png_ptr);
67
82
  rb_write_len = rb_funcall(iw->io, id_write, 1, str);
68
-
69
- /* Ruby 1.8.7 makes up odd numbers when running NUM2INT on a symbol */
70
- if (TYPE(rb_write_len) == T_SYMBOL)
71
- rb_raise(rb_eTypeError, "Number Expected, but got Symbol.");
72
-
73
83
  write_len = NUM2INT(rb_write_len);
74
84
 
75
85
  if ((size_t)write_len != length)
@@ -106,29 +116,14 @@ write_scanline(VALUE scan_line, png_structp png_ptr, png_infop info_ptr)
106
116
  static void
107
117
  write_configure(VALUE image_in, png_structp png_ptr, png_infop info_ptr)
108
118
  {
109
- VALUE width, height, color_model, components;
119
+ VALUE width, height, components;
110
120
  int color_type;
111
121
 
112
122
  width = rb_funcall(image_in, id_width, 0);
113
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
114
- if (SYMBOL_P(width))
115
- rb_raise(rb_eTypeError, "source image has a symbol for width.");
116
-
117
123
  height = rb_funcall(image_in, id_height, 0);
118
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
119
- if (SYMBOL_P(height))
120
- rb_raise(rb_eTypeError, "source image has a symbol for height.");
121
124
 
122
125
  components = rb_funcall(image_in, id_components, 0);
123
- /* in 1.8.7, NUM2INT gives funny numbers for Symbols */
124
- if (SYMBOL_P(components))
125
- rb_raise(rb_eTypeError, "source image has a symbol for components.");
126
-
127
- color_model = rb_funcall(image_in, id_color_model, 0);
128
- if (SYMBOL_P(color_model))
129
- color_type = id_to_color_type(SYM2ID(color_model), NUM2INT(components));
130
- else
131
- rb_raise(rb_eTypeError, "source image has a non symbol color space");
126
+ color_type = components_to_color_type(NUM2INT(components));
132
127
 
133
128
  png_set_IHDR(png_ptr, info_ptr, NUM2INT(width), NUM2INT(height), 8,
134
129
  color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
@@ -221,7 +216,7 @@ raise_if_locked(struct png_data *reader)
221
216
  }
222
217
 
223
218
  static void
224
- free_png(struct png_data *reader)
219
+ free_reader(struct png_data *reader)
225
220
  {
226
221
  png_structp png_ptr;
227
222
  png_infop info_ptr;
@@ -233,12 +228,7 @@ free_png(struct png_data *reader)
233
228
  png_destroy_read_struct(&png_ptr, &info_ptr, (png_info **)NULL);
234
229
  else if (png_ptr)
235
230
  png_destroy_read_struct(&png_ptr, (png_info **)NULL, (png_info **)NULL);
236
- }
237
231
 
238
- static void
239
- free_reader(struct png_data *reader)
240
- {
241
- free_png(reader);
242
232
  free(reader);
243
233
  }
244
234
 
@@ -264,7 +254,7 @@ read_data_fn(png_structp png_ptr, png_bytep data, png_size_t length)
264
254
  rb_raise(rb_eRuntimeError, "Read Error. Read %d instead of %d bytes.",
265
255
  (int)read_len, (int)length);
266
256
 
267
- memcpy(data, RSTRING_PTR(str), read_len);
257
+ memcpy(data, RSTRING_PTR(str), length);
268
258
  }
269
259
 
270
260
  static void
@@ -276,12 +266,15 @@ mark(struct png_data *reader)
276
266
  rb_gc_mark(io);
277
267
  }
278
268
 
279
- static void
280
- allocate_png(struct png_data *reader)
269
+ static VALUE
270
+ allocate(VALUE klass)
281
271
  {
272
+ VALUE self;
273
+ struct png_data *reader;
282
274
  png_structp png_ptr;
283
275
  png_infop info_ptr;
284
-
276
+
277
+ self = Data_Make_Struct(klass, struct png_data, mark, free_reader, reader);
285
278
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
286
279
  (png_error_ptr)png_error_fn,
287
280
  (png_error_ptr)png_warning_fn);
@@ -297,16 +290,6 @@ allocate_png(struct png_data *reader)
297
290
 
298
291
  reader->png_ptr = png_ptr;
299
292
  reader->info_ptr = info_ptr;
300
- }
301
-
302
- static VALUE
303
- allocate(VALUE klass)
304
- {
305
- VALUE self;
306
- struct png_data *reader;
307
-
308
- self = Data_Make_Struct(klass, struct png_data, mark, free_reader, reader);
309
- allocate_png(reader);
310
293
 
311
294
  return self;
312
295
  }
@@ -337,6 +320,11 @@ initialize(VALUE self, VALUE io)
337
320
  png_set_read_fn(png_ptr, (void *)io, read_data_fn);
338
321
  reader->io = io;
339
322
  png_read_info(png_ptr, info_ptr);
323
+
324
+ if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
325
+ png_set_palette_to_rgb(png_ptr);
326
+ png_read_update_info(png_ptr, info_ptr);
327
+ }
340
328
 
341
329
  return self;
342
330
  }
@@ -368,19 +356,6 @@ components(VALUE self)
368
356
  return INT2FIX(c);
369
357
  }
370
358
 
371
- static ID
372
- png_color_type_to_id(png_byte color_type)
373
- {
374
- switch (color_type) {
375
- case PNG_COLOR_TYPE_GRAY: return id_GRAYSCALE;
376
- case PNG_COLOR_TYPE_GRAY_ALPHA: return id_GRAYSCALE;
377
- case PNG_COLOR_TYPE_RGB: return id_RGB;
378
- case PNG_COLOR_TYPE_RGB_ALPHA: return id_RGB;
379
- }
380
-
381
- rb_raise(rb_eRuntimeError, "PNG Color Type not recognized.");
382
- }
383
-
384
359
  /*
385
360
  * call-seq:
386
361
  * reader.color_model -> symbol
@@ -420,7 +395,7 @@ p_gets(VALUE self)
420
395
  struct png_data *reader;
421
396
  png_structp png_ptr;
422
397
  png_infop info_ptr;
423
- int width, components, sl_width;
398
+ png_uint_32 sl_width;
424
399
  size_t height;
425
400
  VALUE sl;
426
401
 
@@ -432,9 +407,7 @@ p_gets(VALUE self)
432
407
  if (reader->lineno >= height)
433
408
  return Qnil;
434
409
 
435
- width = png_get_image_width(png_ptr, info_ptr);
436
- components = get_components(png_ptr, info_ptr);
437
- sl_width = width * components;
410
+ sl_width = png_get_rowbytes(png_ptr, info_ptr);
438
411
 
439
412
  sl = rb_str_new(0, sl_width);
440
413
  png_read_row(png_ptr, (png_bytep)RSTRING_PTR(sl), (png_bytep)NULL);
@@ -532,6 +505,8 @@ Init_PNG()
532
505
 
533
506
  id_GRAYSCALE = rb_intern("GRAYSCALE");
534
507
  id_RGB = rb_intern("RGB");
508
+ id_GRAYSCALE = rb_intern("GRAYSCALE_ALPHA");
509
+ id_RGB = rb_intern("RGB_ALPHA");
535
510
  id_write = rb_intern("write");
536
511
  id_read = rb_intern("read");
537
512
  id_gets = rb_intern("gets");