jpeg読み込み拡張ライブラリ

libjpegを使って、jpegファイルのRGB値を読み込む拡張ライブラリを作ってみた。
ビルドでワーニングいっぱい。リークしてるかも…


libjpegの情報は以下のページを参考に(というかほぼそのまま…)。


拡張ライブラリの情報は以下のページを参考。

jpeg_loader.c

#include <ruby.h>
#include <jpeglib.h>

typedef struct {
  struct jpeg_source_mgr pub;
  JOCTET * buffer;
  unsigned long buffer_length;
} memory_source_mgr, *memory_src_ptr;

METHODDEF(void) memory_init_source(j_decompress_ptr cinfo) {}
METHODDEF(void) memory_term_source(j_decompress_ptr cinfo) {}

METHODDEF(boolean) memory_fill_input_buffer(j_decompress_ptr cinfo) {
  memory_src_ptr src = (memory_src_ptr) cinfo->src;

  src->buffer[0] = (JOCTET) 0xFF;
  src->buffer[1] = (JOCTET) JPEG_EOI;
  src->pub.next_input_byte = src->buffer;
  src->pub.bytes_in_buffer = 2;
  return TRUE;
}

METHODDEF(void) memory_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
  memory_src_ptr src = (memory_src_ptr) cinfo->src;

  if (num_bytes > 0) {
    src->pub.next_input_byte += (size_t) num_bytes;
    src->pub.bytes_in_buffer -= (size_t) num_bytes;
  }
}

GLOBAL(void) jpeg_memory_src(j_decompress_ptr cinfo, void* data, unsigned long len) {
  memory_src_ptr src;

  if (cinfo->src == NULL) {
    cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(memory_source_mgr));
    src = (memory_src_ptr) cinfo->src;
    src->buffer = (JOCTET *) (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, len * sizeof(JOCTET));
  }

  src = (memory_src_ptr) cinfo->src;

  src->pub.init_source = memory_init_source;
  src->pub.fill_input_buffer = memory_fill_input_buffer;
  src->pub.skip_input_data = memory_skip_input_data;
  src->pub.resync_to_restart = jpeg_resync_to_restart;
  src->pub.term_source = memory_term_source;

  src->pub.bytes_in_buffer = len;
  src->pub.next_input_byte = (JOCTET*)data;
}

typedef struct _jpeg_source {
  JSAMPARRAY image;
  int width;
  int height;
} jpeg_source;

static void jpgldr_free(jpeg_source *p) {
  int i;

  for (i = 0; i < p->height; i++) {
    free(p->image[i]);
  }

  free(p->image);
  free(p);
}

static VALUE jpgldr_s_alloc(VALUE klass) {
  jpeg_source *p = ALLOC(jpeg_source);
  return Data_Wrap_Struct(klass, 0, jpgldr_free, p);
}

static VALUE jpgldr_initialize(VALUE self, VALUE string) {
  jpeg_source *p;
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  unsigned long len;
  char *source;
  int i;

  Check_Type(string, T_STRING);
  Data_Get_Struct(self, jpeg_source, p);

  source = RSTRING(string)->ptr;
  len = RSTRING(string)->len;

  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);
  jpeg_memory_src(&cinfo, source, len);
  jpeg_read_header(&cinfo, TRUE);

  jpeg_start_decompress(&cinfo);

  p->width = cinfo.output_width;
  p->height = cinfo.output_height;

  p->image = (JSAMPARRAY) calloc(p->height, sizeof(JSAMPROW));

  for (i = 0; i < p->height; i++) {
    p->image[i] = (JSAMPROW) calloc(3 * p->width, sizeof(JSAMPLE));
  }

  while (cinfo.output_scanline < cinfo.output_height) {
    jpeg_read_scanlines(&cinfo, p->image + cinfo.output_scanline,  cinfo.output_height - cinfo.output_scanline);
  }

  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);

  return Qnil;
}

static VALUE jpgldr_get_width(VALUE self) {
  jpeg_source *p;
  Data_Get_Struct(self, jpeg_source, p);
  return INT2FIX(p->width);
}

static VALUE jpgldr_get_height(VALUE self) {
  jpeg_source *p;
  Data_Get_Struct(self, jpeg_source, p);
  return INT2FIX(p->height);
}

static VALUE jpgldr_rgb(VALUE self, VALUE x, VALUE y) {
  int ix, iy;
  jpeg_source *p;
  JSAMPLE r, g, b;

  ix = NUM2INT(x);
  iy = NUM2INT(y);
  Data_Get_Struct(self, jpeg_source, p);

  if(ix < 0 || iy < 0 || ix >= p->width || iy >= p->height) {
    return Qnil;
  }

  r = p->image[iy][ix * 3 + 0];
  g = p->image[iy][ix * 3 + 1];
  b = p->image[iy][ix * 3 + 2];

  return rb_ary_new3(3, INT2FIX(r), INT2FIX(g), INT2FIX(b));
}

static VALUE JpegSource;

void Init_jpeg_loader() {
  JpegSource = rb_define_class("JpegSource", rb_cObject);
  rb_define_alloc_func(JpegSource, jpgldr_s_alloc);
  rb_define_private_method(JpegSource, "initialize", jpgldr_initialize, 1);
  rb_define_method(JpegSource, "width", jpgldr_get_width, 0);
  rb_define_method(JpegSource, "height", jpgldr_get_height, 0);
  rb_define_method(JpegSource, "rgb", jpgldr_rgb, 2);
}

extconf

require 'mkmf'

dir_config('jpeg')
if have_header('jpeglib.h') and have_library('jpeg')
  create_makefile('jpeg_loader')
end

使う

jpg_src = JpegSource.new(open("src.jpg").read)
p jpg_src.rgb(145,145) #=> [1, 126, 254]