toge's diary

コンピュータ関連の趣味をつらつらと。

PBOとかそういうレベルでは・・・

そっかPBOを使おうが使わなかろうが、pngからの読み込みルーチンで時間がかかってるのか。
PBOでの高速化なんて全然効かないわけです。わざわビデオメモリー使ってPBOを使う意味ないですね。

ということで読み込みルーチンはシンプルにしておこう。ということでシンプルになったpng読み込みコード。
簡単なBMPの読み込みが800行超えそうなことを考えると、ライブラリがあるということはなんとも楽チンですね。

class LoadPNG
{
private:
  png_structp png_;
  png_infop info_;
  png_infop endinfo_;

  png_uint_32 width_;           // 幅
  png_uint_32 width_align_;     // 幅

  png_uint_32 height_;          // 高さ
  png_uint_32 height_align_;    // 高さ

  int bpp_;                     // 1pixelのbit数
  int colortype_;              // ピクセルの表すデータの種類

  png_colorp palette_;          // パレット情報
  int palette_number_;          // パレットの数

  GLuint id_;                   // テクスチャID

public:
  bool load(Reader& reader)
  {
    if (loadInit() == false)
    {
      return false;
    }
    
    // 読み込み関数のセット
    png_set_read_fn(png_, &reader, readPNGData);
    
    if (loadHeader() == false)
    {
      return false;
    }
    
    if (loadImage() == false)
    {
      return false;
    }
    
    png_read_end(png_, info_);
    png_destroy_read_struct(&png_, &info_, &endinfo_);
    
    return true;
  }

public:
  TextureInfo textureInfo() const
  {
    return TextureInfo(id_, 0, 0,
                       (float)width_  / width_align_, (float)height_ / height_align_);
  }

protected:
  //--------------------------------------------------------------------------------
  bool loadInit()
  {
    png_  = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    info_ = png_create_info_struct(png_);
    if (info_ == NULL)
    {
      png_destroy_read_struct(&png_, NULL, NULL);
      return false;
    }

    endinfo_ = png_create_info_struct(png_);
    if (endinfo_ == NULL)
    {
      png_destroy_read_struct(&png_, &info_, NULL);
      return false;
    }

    if (setjmp(png_jmpbuf(png_)))
    {
      png_destroy_read_struct(&png_, &info_, &endinfo_);
      return false;
    }
    return true;
  }

  //--------------------------------------------------------------------------------
  bool loadHeader()
  {
    // ヘッダ情報を読み込む
    png_read_info(png_, info_);
    png_get_IHDR(png_, info_, &width_, &height_, &bpp_, &colortype_, NULL, NULL, NULL);

    // 2のべき乗に合わせる
    width_align_  = power_of_two(width_);
    height_align_ = power_of_two(height_);

    // 入力データの変換ルール設定
    switch (colortype_)
    {
    case PNG_COLOR_TYPE_PALETTE:
      png_set_palette_to_rgb(png_);
      // 以後RGBと同じように扱う
      colortype_ = PNG_COLOR_TYPE_RGB;
    case PNG_COLOR_TYPE_RGB:
    case PNG_COLOR_TYPE_RGB_ALPHA:
      png_set_bgr(png_);
      break;

    case PNG_COLOR_TYPE_GRAY:
      if (bpp_ < 8)
      {
        png_set_gray_1_2_4_to_8(png_);
      }
      break;

    default:
      ;
    }

    // 透明色がある場合にはALPHAレイヤを追加する
    if (png_get_valid(png_, info_, PNG_INFO_tRNS))
    {
      png_set_tRNS_to_alpha(png_);
      colortype_ |= PNG_COLOR_MASK_ALPHA;
    }

    // 16bitは8bitに縮小
    if (bpp_ == 16)
    {
      png_set_strip_16(png_);
    }
    return true;
  }
    
  //--------------------------------------------------------------------------------
  /** イメージの読み取り
   */
  bool loadImage()
  {
    // テクスチャを作成
    glGenTextures(1, &id_);
    glBindTexture(GL_TEXTURE_2D, id_);

    // テクスチャへイメージの読み込み
    switch(colortype_)
    {
    case PNG_COLOR_TYPE_GRAY:
      loadBit(GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 1);
      break;

    case PNG_COLOR_TYPE_RGB:
    case PNG_COLOR_TYPE_PALETTE:
      loadBit(GL_RGB, GL_BGR, GL_UNSIGNED_BYTE, 3);
      break;

    case PNG_COLOR_TYPE_GRAY_ALPHA:
      loadBit(GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 2);
      break;

    case PNG_COLOR_TYPE_RGB_ALPHA:
      loadBit(GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 4);
      break;

    default:
      return false;
    }

    // テクスチャの設定
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    return true;
  }

  //--------------------------------------------------------------------------------
  /** イメージの読み取り
   */
  void loadBit(GLenum internal, GLenum upload, GLenum type, int bpp)
  {
    glTexImage2D(GL_TEXTURE_2D, 0, internal, width_align_, height_align_,
                 0, upload, type, NULL);

    unsigned char* buffer = new unsigned char[width_ * bpp];
    for (int y = 0; y < height_; ++y)
    {
      png_read_row(png_, buffer, NULL);
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, width_, 1, upload, type, (const GLvoid*)buffer);
    }
    delete [] buffer;
  }
}