toge's diary

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

テクスチャ読み込みルーチン その4

朝の一番頭の働く時期になにやっているんだか・・・。

原因不明なのが分かりました。
透明色をALPHAチャンネルに変換するように指定した場合、画像のBPPが上がるんですね。それに対応していなかった。
色々試行錯誤した挙句、以下のようなコードになりましたとさ。
結構簡単になるものね。・・・というより例外処理使えよ > 自分。

/** 指定値より大きい2の累乗の値を得る
 * SDL/test/testgl.cから借用
 */
inline static int
power_of_two(int input_)
{
  int value = 1;
  while (value < input_)
  {
    value *= 2;
  }
  return value;
}

static
void
readPNGData(png_structp png_ptr, png_bytep buf, png_size_t length)
{
  Reader* io = (Reader*)(png_ptr->io_ptr);
  io->read*1
    {
      png_set_tRNS_to_alpha(png_);
      colortype_ |= PNG_COLOR_MASK_ALPHA;
    }

    // 16bitは8bitに縮小
    if (bpp_ == 16)
    {
      png_set_strip_16(png_);
    }
    return true;
  }
    
  //--------------------------------------------------------------------------------
  /** グレイスケールのイメージの読み取り
   */
  void loadGrayScaleBit()
  {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
                 width_align_, height_align_,
                 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    unsigned char* buffer = new unsigned char[width_];

    for (int y = 0; y < height_; ++y)
    {
      png_read_row(png_, buffer, NULL);
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, width_, 1,
                      GL_ALPHA, GL_UNSIGNED_BYTE, (const GLvoid*)buffer);
    }

    delete  buffer;
  }

  //--------------------------------------------------------------------------------
  /** アルファチャンネル付きのグレイスケールイメージの読み取り
   a*/
  void loadGrayScaleAlphaBit( void )
  {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
                 width_align_, height_align_,
                 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 2);

    unsigned char* buffer = new unsigned char[width_ * 2];

    for (int y = 0; y < height_; ++y)
    {
      png_read_row(png_, buffer, NULL);
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, width_, 1,
                      GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (const GLvoid*)buffer);
    }

    delete  buffer;
  }

  //--------------------------------------------------------------------------------
  /** true color のイメージの読み取り
   */
  bool loadRGBBit( void )
  {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
                 width_align_, height_align_,
                 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    unsigned char* buffer = new unsigned char[width_ * 3];

    for (int y = 0; y < height_; ++y)
    {
      png_read_row(png_, buffer, NULL);
      glTexSubImage2D(GL_TEXTURE_2D, 0,
                      0, y, width_, 1,
                      GL_BGR, GL_UNSIGNED_BYTE,
                      (const GLvoid*)buffer);
    }

    delete  buffer;
  }

  //--------------------------------------------------------------------------------
  /** true color のイメージの読み取り(アルファ値付き)
   */
  bool loadRGBAlphaBit( void )
  {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                 width_align_, height_align_,
                 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

    unsigned char* buffer = new unsigned char[width_ * 4];

    for (int y = 0; y < height_; ++y)
    {
      png_read_row(png_, buffer, NULL);
      glTexSubImage2D(GL_TEXTURE_2D, 0,
                      0, y, width_, 1,
                      GL_BGRA, GL_UNSIGNED_BYTE,
                      (const GLvoid*)buffer);
    }

    delete  buffer;
  }

  //--------------------------------------------------------------------------------
  /** イメージの読み取り
    */
  bool loadImage()
  {
    // テクスチャを作成
    glGenTextures(1, &id_);
    glBindTexture(GL_TEXTURE_2D, id_);

    switch(colortype_)
    {
    case PNG_COLOR_TYPE_GRAY:
      loadGrayScaleBit();
      break;
    case PNG_COLOR_TYPE_RGB:
    case PNG_COLOR_TYPE_PALETTE:
      loadRGBBit();
      break;
    case PNG_COLOR_TYPE_GRAY_ALPHA:
      loadGrayScaleAlphaBit();
      break;
    case PNG_COLOR_TYPE_RGB_ALPHA:
      loadRGBAlphaBit();
      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;
 }
};

*1:pointer)buf, (size_type)length); } 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_; 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_); png_set_bgr(png_); // 以後RGBと同じように扱う colortype_ = PNG_COLOR_TYPE_RGB; break; case PNG_COLOR_TYPE_GRAY: if (bpp_ < 8) { png_set_gray_1_2_4_to_8(png_); } break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: png_set_bgr(png_); break; default: ; } // 透明色がある場合にはALPHAレイヤを追加する if (png_get_valid(png_, info_, PNG_INFO_tRNS