OpenGL のウィンドウを bmp 画像ファイルとして保存する(Ruby)

RubyOpenGL のウィンドウ・キャプチャをしようと苦労していたのですが、すばらしいライブラリを発見して成功しました。ウィンドウを BMP ファイルに落とします。RubyOpenGL を使うことについては、ここが参考になれば幸いです。

RubyでBMPファイルをあつかう: 脈絡なんか気にしない
リンク先のブログ記事で助かりました。コードはこちら。必要な部分を流用させてもらいました(感謝!)。まずはそれから。

class BitMap
  def initialize(width, height, dpi = 96)
    @width = width
    @height = height
    @line_size = width * 3 + (4 - (width * 3) % 4) % 4
    @buf_size = @line_size * height
    @buf = ("\000" * @buf_size).encode('BINARY')
    @bit_count = 24
    @compression = 0  # 圧縮無し
    @size_image = 0
    @x_pix_per_meter = (39.375 * dpi).round
    @y_pix_per_meter = (39.375 * dpi).round
    @clr_used = 0
    @cir_important = 0
  end
  
  attr_accessor :buf
  attr_reader :width, :height

  # BMPファイルを出力する
  def write(filename)
    file_size = 14 + 40 + @buf_size
    data_offset = 14 + 40

    open(filename, "wb") do |f|
      f.print 'BM'
      f.print [file_size, 0, data_offset].pack("l*")
      f.print [40, @width, @height].pack("l*")
      f.print [1, @bit_count].pack("S*")
      f.print [@compression, @size_image,
               @x_pix_per_meter, @y_pix_per_meter,
               @clr_used, @cir_important].pack("l*")
      f.print @buf
    end
  end
end

そして画面キャプチャをするメソッドです。今回作ったのはこれです。引数はファイル名です。

class BitMap
  def self.gl_capture(fname)
    x = glutGet(GLUT_WINDOW_WIDTH)
    y = glutGet(GLUT_WINDOW_HEIGHT)

    bitmap = BitMap.new(x, y)
    glPixelStorei(GL_PACK_ALIGNMENT, 1)
    imgdata = glReadPixels(0, 0, x, y, GL_BGR, GL_UNSIGNED_BYTE).unpack("H*")[0]
    
    data = ""
    y.times do |j|
      (x * 3).times {|i| data += imgdata[6 * x * j + i * 2, 2].to_i(16).chr}
      data += "\x00" * ((4 - 3 * x % 4) % 4)
    end
    
    bitmap.buf = data
    bitmap.write(fname)
  end
end

この記事が役に立ちました。

サンプルです。上の二つのコードを組み込んで実行して下さい。

require 'bundler/setup'
require 'opengl'
require 'glut'
include Gl, Glut

Width = Height = 200

def init
  glClearColor(0, 0, 255, 1)
end

display = proc do 
  vertex = [-0.9, 0.9, 0.9, 0.9, 0, -0.9]
  glClear(GL_COLOR_BUFFER_BIT)
  glEnableClientState(GL_VERTEX_ARRAY)
  glVertexPointer(2, GL_FLOAT, 0, vertex)
  glLineWidth(3)
  glColor3f(0, 1, 0)
  glDrawArrays(GL_LINE_LOOP, 0, 3)
  BitMap.gl_capture("sample.bmp")    #画像の取り込み
  glFlush
end

glutInit
glutInitWindowSize(Width, Height)
glutInitWindowPosition(200, 100)
glutCreateWindow("Triangle")
glutDisplayFunc(display)
init
glutMainLoop

キャプチャした画像です。