ゆとりーなの日記

日記的な事を書いて行くと思はれる

xlibとglxとimlib2で画像表示

この時代にこの組み合わせを使う人がいるかについては疑問が残りますがメモ程度に。

#include <iostream>
#include <memory>
#include <string>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <Imlib2.h>

// スマポで管理したいよねDisplay編
struct display_delete {
  void operator ()(Display *display) const {
    XCloseDisplay(display);
  }
};

// スマポで管理したいよねXVisualInfo編
struct visual_info_delete {
  void operator ()(XVisualInfo *visual) const {
    XFree(visual);
  }
};

// 描画用頂点構造体
struct vertex {
  float x;
  float y;
  float z;
  float red;
  float green;
  float blue;
  float alpha;
  float u;
  float v;
};

// OpenGL初期設定
void init_gl() {
  glClearColor(0.f, 0.f, 0.f, 1.f);
  glViewport(0, 0, 640, 480);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.f, 640.f, 480.f, 0.f, 0.f, 1.f);
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

int main() {
  // ディスプレイ初期化
  const std::unique_ptr<Display, 
                        display_delete> display(XOpenDisplay(nullptr));
  if (!display) {
    std::cerr << "display failed" << std::endl;
    return -1;
  }
  // ウィンドウ作成
  const Window window = XCreateSimpleWindow(
                             display.get(),
                             DefaultRootWindow(display.get()), 
                             0,
                             0,
                             640, 
                             480, 
                             1,
                             BlackPixel(display.get(),
                                        DefaultScreen(display.get())),
                             WhitePixel(display.get(),
                                        DefaultScreen(display.get())));
  switch (window) {
    case BadAlloc:
      std::cerr << "window bad alloc" << std::endl;
      return -1;
    case BadMatch:
      std::cerr << "window bad match" << std::endl;
      return -1;
    case BadValue:
      std::cerr << "window bad value" << std::endl;
      return -1;
    case BadWindow:
      std::cerr << "window bad window" << std::endl;
      return -1;
    default:
      std::cout << "window create success" << std::endl;
      break;
  }
  // 冷静に終了出来るようにする何かその1
  Atom atom1 = XInternAtom(display.get(), "WM_PROTOCOLS", False);
  switch (atom1) {
    case BadAlloc:
      std::cerr << "atom1 bad alloc" << std::endl;
      return -1;
    case BadAtom:
      std::cerr << "atom1 bad atom" << std::endl;
      return -1;
    case BadValue:
      std::cerr << "atom1 bad value" << std::endl;
      return -1;
    default:
      std::cout << "atom1 create success" << std::endl;
      break;
  }
  // 冷静に終了出来るようにする何かその2
  Atom atom2 = XInternAtom(display.get(), "WM_DELETE_WINDOW", False);
  switch (atom2) {
    case BadAlloc:
      std::cerr << "atom2 bad alloc" << std::endl;
      return -1;
    case BadAtom:
      std::cerr << "atom2 bad atom" << std::endl;
      return -1;
    case BadValue:
      std::cerr << "atom2 bad value" << std::endl;
      return -1;
    default:
      std::cout << "atom2 create success" << std::endl;
      break;
  }
  GLint glx_attrs[] = {
    GLX_RGBA,
    GLX_RED_SIZE,
    1,
    GLX_GREEN_SIZE,
    1,
    GLX_BLUE_SIZE,
    1,
    GLX_DOUBLEBUFFER,
    None
  };
  // XVisualInfo取得
  const std::unique_ptr<XVisualInfo,
                        visual_info_delete> visual_info(
                                                glXChooseVisual(
                                                    display.get(),
                                                    DefaultScreen(
                                                        display.get()),
                                                        glx_attrs));
  if (!visual_info) {
    std::cerr << "visual_info failed" << std::endl;
    return -1;
  }
  // コンテキスト作成
  GLXContext context = glXCreateContext(display.get(),
                                        visual_info.get(),
                                        nullptr,
                                        True);
  if (!context) {
    std::cerr << "context NULL" << std::endl;
    return -1;
  } else {
    std::cout << "context create success" << std::endl;
  }
  glXMakeCurrent(display.get(), window, context);
  const std::string str = glXQueryExtensionsString(
                              display.get(), 
                              DefaultScreen(display.get()));
  // 所謂vsync
  const auto it = str.find("SGIX_swap_control");
  if (it != std::string::npos) {
    std::cout << "sgi not support" << std::endl;
    return -1;
  }
  if (reinterpret_cast<int (*)(int)>(
          glXGetProcAddress(
              reinterpret_cast<const GLubyte *>("glXSwapIntervalSGI")))(1)) {
    std::cout << "glXSwapIntervalSGI failed" << std::endl;
    return -1;
  }
  init_gl();
  XSetWMProtocols(display.get(), window, &atom2, 1);
  // ウィンドウ表示
  XMapWindow(display.get(), window);
  XFlush(display.get());
  // 画像ファイル読み込み
  Imlib_Image image = imlib_load_image("hoge.png");
  if (!image) {
    std::cout << "load image failed" << std::endl;
    return -1;
  }
  // 画像データを取得出来るように
  imlib_context_set_image(image);
  // テクスチャ作成
  GLuint id;
  glEnable(GL_TEXTURE_2D);
  glGenTextures(1, &id);
  glBindTexture(GL_TEXTURE_2D, id);
  gluBuild2DMipmaps(GL_TEXTURE_2D,
                    GL_RGBA,
                    imlib_image_get_width(),
                    imlib_image_get_height(),
                    GL_BGRA,
                    GL_UNSIGNED_BYTE,
                    imlib_image_get_data());
  // 画像解放
  imlib_free_image();
  // メッセージループ!
  for (;;) {
    XEvent event;
    while (XPending(display.get())) {
      XNextEvent(display.get(), &event);
      if (event.type == ClientMessage) {
        if (event.xclient.message_type == atom1 && 
            event.xclient.data.l[0] == atom2) { 
          glDeleteTextures(1, &id);
          glXMakeCurrent(display.get(), 0, nullptr);
          glXDestroyContext(display.get(), context);
          XDestroyWindow(display.get(), window);
          return 0;
        }
      }
    }
    glClear(GL_COLOR_BUFFER_BIT);
    const vertex v[4] = {
      {0, 0, 0, 1, 1, 1, 1, 0, 0},
      {480, 0, 0, 1, 1, 1, 1, 1, 0},
      {0, 480, 0, 1, 1, 1, 1, 0, 1},
      {480, 480, 0, 1, 1, 1, 1, 1, 1}
    };
    // テクスチャ指定
    glBindTexture(GL_TEXTURE_2D, id);
    // 頂点情報指定
    glVertexPointer(3, GL_FLOAT, sizeof(v[0]), &v[0].x);
    glColorPointer(4, GL_FLOAT, sizeof(v[0]), &v[0].red);
    glTexCoordPointer(2, GL_FLOAT, sizeof(v[0]), &v[0].u);
    // 描画
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glXSwapBuffers(display.get(), window);
  }
}

main函数への詰め込み及びに随所にコピペが見られますが悪しからず。出来るだけエラー判定も入れてみましたが、若干怪しいところがあるのでこちらも悪しからず。