/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/25
/// 
/// @brief  DIB関連。
/// 
/// ****************************************************************************


#include "CPL_WIN_DIBitmap.h"

#include <wingdi.h>

namespace CPL {
namespace WIN {


// _____________________________________________________________________________
/// @brief コンストラクタ。
/// 
/// @param[in] in_create_info : イメージ作成情報を格納したCreateInfo構造体への参照。
/// 
/// @exception CPL::Core::Exception<std::exception>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DIBitmap::DIBitmap( _in<CreateInfo> in_create_info )
{ 
  CPL_THROW_IF( !Create( in_create_info ), CPL_WIN_MSG_NOT_CREATE_DIB );
}


// _____________________________________________________________________________
/// @brief コンストラクタ（バイナリバッファから作成）
/// 
/// @param[in] in_buffer : イメージデータを指すバッファのアドレス。
/// 
/// @exception CPL::Core::Exception<std::exception>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DIBitmap::DIBitmap( pin<void> in_buffer )
  : m_flag_change(true)
{
  CPL_THROW_IF( !Create( in_buffer, -1 ), CPL_WIN_MSG_NOT_CREATE_DIB );
}



// _____________________________________________________________________________
/// @brief 指定されたデバイスコンテキストにビットマップを描画。
/// 
/// @param[in] in_hdc
///   : クライアント領域を表すデバイスコンテキストのハンドルを指定します。
/// @param[in] in_draw_info
///   : 描画情報が設定された、DIBitmap::DrawInfoオブジェクトを指定します。￥ｎ
///   : 詳しくはCPL::WIN::DIBitmap::DrawInfoを参照してください。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::Draw( _in<HDC> in_hdc, _in<DrawInfo> in_draw_info ) const
{
  if( m_buffer.GetSize() == 0 )
  {
    return;
  }
  const int bmp_w = m_ptr_bmp_info->bmiHeader.biWidth;
  const int bmp_h = Abs(m_ptr_bmp_info->bmiHeader.biHeight);
  const int bit   = GetBitCount();
  Rect rt_source
  { in_draw_info.rect_source.left
  , in_draw_info.rect_source.top
  , in_draw_info.rect_source.right == 0 ? bmp_w : in_draw_info.rect_source.right
  , in_draw_info.rect_source.bottom == 0 ? bmp_h : in_draw_info.rect_source.bottom
  };

  // 描画
//  in_back_color = nullptr;
  // 回転なし
  if( in_draw_info.rotate == 0 )
  {
    if( in_draw_info.alpha == 1.0f /* &&  bit != 32*/ )
    {
      int width  = rt_source.right - rt_source.left;
      int height = rt_source.bottom - rt_source.top;
      if( bit == 32 ) // alpha付き。
      {
        // 非対応。
        //AlphaBlend
        //  ( in_hdc
        //  , p.x
        //  , p.y
        //  , static_cast<int>(width * scl.x)
        //  , static_cast<int>(height * scl.y)
        //  , HDC hdcSrc
        //  int nXOriginSrc,
        //  int nYOriginSrc,
        //  int nWidthSrc,
        //  int nHeightSrc,
        //  BLENDFUNCTION blendFunction
        //  );
      }
      else
      {
        StretchDIBits
          ( in_hdc
          , in_draw_info.pos.x
          , in_draw_info.pos.y
          , static_cast<int>(width  * in_draw_info.scale.x)
          , static_cast<int>(height * in_draw_info.scale.y)
          , rt_source.left
          , rt_source.top
          , width
          , height
          , GetBmp()
          , m_ptr_bmp_info
          , DIB_RGB_COLORS
          , SRCCOPY
          );
      }
    }
    // アルファのみ
//    ef( in_back_color == nullptr ){
//    }
    
    // 背景抜き描画
    else{
      int w_byte = (bmp_w + 7) >> 3;
      int tmp_x = in_draw_info.pos.x;
      int tmp_y = in_draw_info.pos.y;

      // 転送元設定
      if( rt_source.left   < 0     ){  rt_source.left   = 0;      }
      if( rt_source.top    < 0     ){  rt_source.top    = 0;      }
      if( rt_source.right  > bmp_w ){  rt_source.right  = bmp_w;  }
      if( rt_source.bottom > bmp_h ){  rt_source.bottom = bmp_h;  }
      // 
      int width = rt_source.right - rt_source.left;
      if( width < 0 ){ return; }
      int height = rt_source.bottom - rt_source.top;
      if( height < 0 ){ return; }
      if( tmp_x < 0 ){ rt_source.left -= tmp_x; tmp_x = 0; }
      if( tmp_y < 0 ){ rt_source.top  -= tmp_y; tmp_y = 0; }
//      if( p.x + width  >= bmp_w ){ rt_source.right  -= (p.x + width - bmp_w); }
//      if( p.y + height >= bmp_h ){ rt_source.bottom -= (p.y + height - bmp_h); }

      if( bit == 32 )
      { // 
        A8R8G8B8 color_img, color;
        COLORREF color_bk;
        float a, a_; // a = in_alpha, a_ = 1.0 - in_alpha
        const int x_ = tmp_x - rt_source.left;
        const int y_ = tmp_y - rt_source.top;
        for( int y = rt_source.top; y < rt_source.bottom; y++ )
        {
          for( int x = rt_source.left; x < rt_source.right; x++ )
          {
            color_img.SetColor( GetPixel(x,y).GetColor() );
            color_bk = ::GetPixel( in_hdc, x+x_, y+y_ );
            a  = color_img.GetA() / 255.0f;
            a_ = 1.0f - a;
            color.SetColor
              ( 0
              , static_cast<U8>((color_bk       & 0xff) * a_ + color_img.GetR() * a)
              , static_cast<U8>(((color_bk>>8)  & 0xff) * a_ + color_img.GetG() * a)
              , static_cast<U8>(((color_bk>>16) & 0xff) * a_ + color_img.GetB() * a)
              );
            ::SetPixel
              ( in_hdc
              , static_cast<int>((x + x_) * in_draw_info.scale.x)
              , static_cast<int>((y + y_) * in_draw_info.scale.y)
              , color.GetColorRef()
              );
          }
        }
      }
      else // 24bit
      {
        if( in_draw_info.back_color )
        {
          if( m_flag_change )
          {
            zzf_SetPenetrationLine( in_draw_info.back_color );
          }
          COLORREF color = in_draw_info.back_color.GetColorRef();

          const int x_ = tmp_x - rt_source.left;
          const int y_ = tmp_y - rt_source.top;
          for( int y = rt_source.top; y < rt_source.bottom; y++ )
          {
            for( int x = rt_source.left; x < rt_source.right; x++ )
            {
              if( m_line[ y*w_byte + (x>>3) ] & (1<<(x&7)) )
              {
                ::SetPixel
                  ( in_hdc
                  , static_cast<int>((x + x_) * in_draw_info.scale.x)
                  , static_cast<int>((y + y_) * in_draw_info.scale.y)
                  , GetPixel(x,y).GetColorRef()
                  );
              }
            }
          }
        }
        else
        {
          COLORREF color;
          int      a = static_cast<int>(in_draw_info.alpha * 255);
          for( int y = 0; y < height; y++ )
          {
            for( int x = 0; x < width; x++ )
            {
              color = GetPixel(x+rt_source.top, y+rt_source.left).GetColorRef();
              color = (((color * a) & 0xff000000) >> 8) +
                  ((((color & 0xff00) * a) >> 8) & 0xff00) +
                  ((((color & 0xff) * a) >> 8) & 0xff);
              ::SetPixel
                ( in_hdc
                , static_cast<int>((x + tmp_x) * in_draw_info.scale.x)
                , static_cast<int>((y + tmp_y) * in_draw_info.scale.y)
                , color
                );
            }
          }
        }
      }
    }
  }

  // 回転あり
  else{
/*    if( m_flag_change ){
      zzf_SetPenetrationLine( *in_back_color );
    }
    COLORREF color = in_back_color->GetColorRef();
//    int x, y;
    int w_byte = (GetWidth() + 7) / 8;

    // 傾きを求める
    float ax = atanf(in_rotate);

    // クリッピング領域を計算
    int sx = rt_source.right - rt_source.left;
    int sy = rt_source.bottom - rt_source.top;
    D3DXVECTOR2 vx( sx, 0 );
    D3DXVECTOR2 vy( 0, sy );
    D3DXVECTOR2 vl( sx, sy );
    D3DXVECTOR2 vec2( cosf(in_rotate), sinf(in_rotate) );
    float len = dnaMath::GetVecLength( &vec );
    dnaMath::ChangeNormalize( &vec );      // 正規化
    dnaMath::ChangeAddVec( &vec, &vec2 );  // ベクトルを加算
    vec *= len;
    // 幅*/
  }
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 抜き色描画高速化用配列作成
// １ラインの透過色と非透過色の切り替えを計算する
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::zzf_SetPenetrationLine( A8R8G8B8 in_back_color ) const
{
  int height = GetHeight();
  int width  = GetWidth();
  int w_byte = (width + 7) /8;
  int len    = w_byte * height;

  // 確保
  m_line.resize( len );
  ::memset( &m_line[0], 0, len ); // ０クリア

  int x, y;

  for( y = 0; y < height; y++ )
  {
    for( x = 0; x < width; x++ )
    {
      if( GetPixel(x,y) != in_back_color )
      {
        m_line[ y*w_byte + (x>>3)/*(/8)*/ ] |= (1 << (x&7));
      }
    }
  }
  m_flag_change = false;
}




// _____________________________________________________________________________
/// @brief 自身のDIBにDIBを描画。
/// 
/// @param[in] in_ptr_bmp
///   : 描画するDIBのアドレスを指定します。
/// @param[in] in_pos
///   : 描画する左上の座標を指定します。
/// @param[in] in_rect_source
///   : 描画対象とする範囲の対角座標を表すRect型を渡します。
/// @param[in] in_scale
///   : 描画倍率をfloat2型で指定します。\n※ 現在未対応です。
/// @param[in] in_ptr_back_color
///   : 背景色(透過色)。ここで指定したカラーが完全透過色として処理される。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  DIBitmap::DrawImage
    ( pin<DIBitmap > in_ptr_bmp
    , _in<td_Coord2> in_pos
    , _in<Rect     > in_rect_source
    , _in<float2   > in_scale
    , pin<A8R8G8B8 > in_ptr_back_color
    )
{
  if( in_ptr_bmp == nullptr )
  {
    return;
  }

  // 描画矩形
  Rect rt = in_rect_source;
  if( rt.right < rt.left )
  {
    rt.left  = in_rect_source.right;
    rt.right = in_rect_source.left;
  }
  if( rt.bottom < rt.top )
  {
    rt.top    = in_rect_source.bottom;
    rt.bottom = in_rect_source.top;
  }
  if( rt.left   < 0                       ){ rt.left = 0;                         }
  if( rt.right  > in_ptr_bmp->GetWidth()  ){ rt.right = in_ptr_bmp->GetWidth();   }
  if( rt.top    < 0                       ){ rt.top = 0;                          }
  if( rt.bottom > in_ptr_bmp->GetHeight() ){ rt.bottom = in_ptr_bmp->GetHeight(); }
  // 幅
  if( in_pos.x < 0 )
  {
    rt.left -= in_pos.x;
  }
  int width = rt.right - rt.left;
  if( in_pos.x + width > GetWidth() )
  {
    rt.right -= in_pos.x + width - GetWidth();
  }
  width = rt.right - rt.left;
  if( width < 0 )
  {
    return;
  }
  // 高さ
  if( in_pos.y < 0 )
  {
    rt.top -= in_pos.y;
  }
  int height = rt.bottom - rt.top;
  if( height + in_pos.y > GetHeight() )
  {
    height -= in_pos.y + height - GetHeight();
  }
  if( height < 0 )
  {
    return;
  }

  int x, y, x_ = 0;
  // 回転無し
//  if( rotate == 0 ){
    // 透過色あり
    if( in_ptr_back_color )
    {
      if( GetBitCount() == in_ptr_bmp->GetBitCount() ) // ビット深度が同じ場合（今の処理方法では遅いです）
      {
        for( y = 0; y < height; y++ )
        {
          for( x = 0; x < width; x++ )
          {
            for(      ; in_ptr_bmp->GetPixel(rt.left+x, rt.top+y).GetColor() == in_ptr_back_color->GetColor()  &&  x<width; x++){} // 透過色をとばす
            for(x_ = x; in_ptr_bmp->GetPixel(rt.left+x, rt.top+y).GetColor() != in_ptr_back_color->GetColor()  &&  x<width; x++){} // 透過色でない色を検索
            memcpy
              ( GetBmp(x_+in_pos.x, y+in_pos.y)
              , in_ptr_bmp->GetBmp(x_+rt.left, y+rt.top)
              , ((x-x_)*GetBitCount())>>3 );
          }
        }
      }
      else // ビット深度が一致していない場合（遅いです）
      {
        A8R8G8B8 c;
        for( y = 0; y < height; y++ )
        {
          for( x = 0; x < width; x++ )
          {
            c = in_ptr_bmp->GetPixel( rt.left + x, rt.top + y );
            if( c.GetColor() != in_ptr_back_color->GetColor() )
            {
              SetPixel( in_pos.x + x, in_pos.y + y, c.GetColor() );
            }
          }
        }
      }
    }

    // 透過色なし
    else
    {
      if( GetBitCount() == in_ptr_bmp->GetBitCount() ) // ビット深度が同じ場合
      {
        int size = width * GetBitCount() / 8;
        for( y = 0; y < height; y++ )
        {
          ::memcpy
            ( GetBmp(in_pos.x, in_pos.y + y)
            , in_ptr_bmp->GetBmp(rt.left, rt.top + y)
            , size
            );
        }
      }
      else // ビット深度が一致していない場合（遅いです）
      {
        for( y = 0; y < height; y++ )
        {
          for( x = 0; x < width; x++ )
          {
            SetPixel
              ( in_pos.x + x
              , in_pos.y + y
              , in_ptr_bmp->GetPixel(rt.left, rt.top+y).GetColor()
              );
          }
        }
      }
    }
//  }

  // 回転あり
//  else{
//    if( GetBitCount() == in_ptr_bmp->GetBitCount() ){ // ビット深度が同じ場合
//      g_if2 if_x;
//      g_if2 if_y;
//    }else{
//    }
//  }
}




// _____________________________________________________________________________
/// @brief 指定した色で消去（塗りつぶし）
/// 
/// @param[in] in_color
///   : 設定するカラー値。\n
///   : ビット深度が4bitか8bitの場合、パレットインデックスを指定する。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::Clear( _in<A8R8G8B8> in_color )
{
  auto const& c32 = in_color;
  R8G8B8      c24( in_color );
  R5G5B5      c15( c24.GetR(), c24.GetG(), c24.GetB() );
  BYTE        c8 = (BYTE)in_color;
  BYTE        c4 = (in_color<<4)&0xf0 + in_color&0xf0;
//  int loop = GetWidthByte() * GetHeight();
  int w = GetWidth();
  int h = GetHeight();
  int x, y;


  switch(GetBitCount())
  {
  case 4:
    if( w % 2 == 0 ) // 偶数幅の場合
    {
      for( y = 0; y < h; y++ )
      {
        for( x = 0; x < w; x += 2 )
        {
          *GetBmp(x,y) = c4;
        }
      }
    }
    else // 奇数幅の場合
    {
      for( y = 0; y < h; y++ )
      {
        for( x = 0; x < w-1; x += 2 )
        {
          *GetBmp(x,y) = c4;
        }
        *GetBmp( w, y ) = static_cast<BYTE>(in_color & 0x0f);
      }
    }
    break;

  case 8:
    for( y = 0; y < h; y++ )
    {
      for( x = 0; x < w; x++ )
      {
        *GetBmp(x,y) = c8;
      }
    }
    break;

  case 16:
    for( y = 0; y < h; y++ )
    {
      for( x = 0; x < w; x++ )
      {
        ::memcpy( GetBmp(x,y), &c15, 2 );
      }
    }
    break;

  case 24:
    for( y = 0; y < h; y++ )
    {
      for( x = 0; x < w; x++ )
      {
        ::memcpy( GetBmp(x,y), &c24, 3 );
      }
    }
    break;

  case 32:
    for( y = 0; y < h; y++ )
    {
      for( x = 0; x < w; x++ )
      {
        ::memcpy( GetBmp(x,y), &c32, 4 );
      }
    }
    break;
  }
  m_flag_change = true;
}

// _____________________________________________________________________________
/// @brief 画像を指定色にクリア。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::Clear( _in<float4> in_color )
{
  Clear(
    U8x4( static_cast<U8>(in_color.r * 255)
        , static_cast<U8>(in_color.g * 255)
        , static_cast<U8>(in_color.b * 255)
        , static_cast<U8>(in_color.a * 255)
        )
    );
}

// _____________________________________________________________________________
/// @brief 画像を指定色にクリア。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::Clear( _in<U32> in_color )
{
  Clear( A8R8G8B8(in_color) );
}

// _____________________________________________________________________________
/// @brief 画像を指定色にクリア。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::Clear( _in<U8x4> in_color )
{
  Clear( A8R8G8B8(in_color.r, in_color.g, in_color.b, in_color.a) );
}




// _____________________________________________________________________________
/// @copydoc CPL::Core::Image_i::Create(_in<CreateInfo>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 DIBitmap::Create( _in<CreateInfo> in_create_info )
{
  auto const width  = in_create_info.width;
  auto const height = in_create_info.height;
  auto const bit    = in_create_info.bit;

  Release();
  // サイズ計算
  int info_size = sizeof(BITMAPINFOHEADER);
  switch( bit )
  {
  case 4: info_size += sizeof(RGBQUAD)*15;  break;
  case 8: info_size += sizeof(RGBQUAD)*255; break;
  case 16:
  case 24:
  case 32:
    break;
  default:
    return 0;
  }


  // イメージサイズ計算
  int w_len     = int((width * bit + 31) / 32) * 4; // イメージの幅（バイト数）
  int img_size  = w_len * Abs(height);
  int offset    = sizeof(BITMAPFILEHEADER) + info_size;
  int file_size = offset + img_size;

  try{
    // ファイル領域確保
    m_buffer.Resize( file_size );
  }
  catch(...)
  {
    return 0;
  }

  // ファイルヘッダーポインタ設定
  m_ptr_bmp_file_head = (BITMAPFILEHEADER*)m_buffer.GetDataAddr();
  m_ptr_bmp_file_head->bfType = 0x4D42; // BM
  m_ptr_bmp_file_head->bfSize = file_size;
  m_ptr_bmp_file_head->bfReserved1 = 0;
  m_ptr_bmp_file_head->bfReserved2 = 0;
  m_ptr_bmp_file_head->bfOffBits   = offset;

  // 情報ヘッダーポインタ
  m_ptr_bmp_info = m_buffer.GetDataAddrT<U8*,BITMAPINFO*>( sizeof(BITMAPFILEHEADER) );
  m_ptr_bmp_info->bmiHeader.biSize      = sizeof(BITMAPINFOHEADER);
  m_ptr_bmp_info->bmiHeader.biWidth     = width;
  m_ptr_bmp_info->bmiHeader.biHeight    = height;
  m_ptr_bmp_info->bmiHeader.biPlanes    = 1;        // 常に１
  m_ptr_bmp_info->bmiHeader.biBitCount  = bit;      // 色数
  m_ptr_bmp_info->bmiHeader.biCompression   = BI_RGB; // 圧縮方法
  m_ptr_bmp_info->bmiHeader.biSizeImage     = 0;      // ピクセルデータ全バイト数
  m_ptr_bmp_info->bmiHeader.biXPelsPerMeter = 0;
  m_ptr_bmp_info->bmiHeader.biYPelsPerMeter = 0;
  m_ptr_bmp_info->bmiHeader.biClrUsed       = 0;
  m_ptr_bmp_info->bmiHeader.biClrImportant  = 0;

  // イメージへの先頭ポインタ
  m_ptr_image = m_buffer.GetDataAddrT<U8*>( offset );

  // 背景色に統一
  Clear( in_create_info.color );

  return m_buffer.GetSize(); // 成功
}



// _____________________________________________________________________________
/// @copydoc Core::Image_i::Create(pin<void>,_in<U64>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 DIBitmap::Create( pin<void> in_ptr_buf, _in<U64> in_size )
{
  if( in_ptr_buf == nullptr )
    return 0;

  try{
    // ファイルヘッダーポインタ設定
    m_ptr_bmp_file_head = (BITMAPFILEHEADER*)in_ptr_buf;
    int size = ((BITMAPFILEHEADER*)in_ptr_buf)->bfSize;

    Release();
    m_buffer.Resize( size );
    ::memcpy( m_buffer.GetDataAddr(), in_ptr_buf, size );
    
    // ファイルヘッダーポインタ設定
    m_ptr_bmp_file_head = (BITMAPFILEHEADER*)m_buffer.GetDataAddr();
    // ＢＭ検査
    if( ::strncmp( (char*)(&m_ptr_bmp_file_head->bfType), "BM", 2 ) != 0 )
    {
      Release();
      return 0;
    }
    // 情報ヘッダーポインタ
    m_ptr_bmp_info = m_buffer.GetDataAddrT<U8*,BITMAPINFO*>( sizeof(BITMAPFILEHEADER) );
    // イメージへの先頭ポインタ
    m_ptr_image = m_buffer.GetDataAddrT<U8*>( m_ptr_bmp_file_head->bfOffBits );

    m_flag_change = true;
  }
  catch(...)
  {
    Release();
    return 0;
  }
  return m_buffer.GetSize();
}



// _____________________________________________________________________________
/// @copydoc CPL::Core::Image_i::Create(_in<Buffer_i>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 DIBitmap::Create( _in<Core::Buffer_i> in_stream )
{
  return Create( in_stream.GetDataAddr(), -1 );
}


// _____________________________________________________________________________
/// @copydoc CPL::Core::Image_i::Create(pin<str_type>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 DIBitmap::Create( pin<str_type> in_file_path )
{
  try{
    // ファイルオープン
    Core::FileStream fs( in_file_path, Core::FileStream::kREAD_BINARY );

    //
    return Create( fs );
  }
  catch( ... )
  {
    return 0;
  }
}

// _____________________________________________________________________________
/// @copydoc DIBitmap::Create(pin<str_type>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 DIBitmap::Create( _in<unstr_cptr> in_file_path )
{
  return Create( Core::ChangeCodeStrings<Core::String>( in_file_path ) );
}


// _____________________________________________________________________________
/// @copydoc CPL::Core::Image_i::Output(pin<str_type>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t DIBitmap::Output( pin<str_type> in_file_path ) const
{
  U64 buf_size = m_buffer.GetSize();
  if( in_file_path == nullptr  ||
      buf_size <= 0 )
  {
    return -1;
  }
  
  try{
    Core::FileStream
      file_stm
        ( in_file_path
        , Core::FileStream::kBINARY | Core::FileStream::kWRITE
        );
    file_stm.Write( m_buffer.GetDataAddr(), buf_size );
  }
  catch(int)
  {
    return -1;
  }
  return 0;
}


// _____________________________________________________________________________
/// @copydoc Output(pin<str_type>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t DIBitmap::Output( _in<unstr_cptr> in_file_path ) const
{
  return Output( Core::ChangeCodeStrings<Core::String>( in_file_path ) );
}

// _____________________________________________________________________________
/// @copydoc Core::Image_i::Output(rout<Stream_i_>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t DIBitmap::Output( rout<Core::Stream_i_> out_stream ) const
{
  out_stream.Write( m_buffer.GetDataAddr(), m_buffer.GetSize() );
  return 0; // 成功。
}

// _____________________________________________________________________________
/// @copydoc Core::Image_i::Output(rout<ImageChannel>,_in<int>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  DIBitmap::Output
    ( rout<Core::ImageChannel> out_imgch
    , _in<int>                 in_ch_No
    ) const
{
  if( in_ch_No > 4  ||  GetBitCount() <= 24 && in_ch_No > 3 )
  {
    return -1;
  }

  // 
  auto pxl_size = GetBitCount() / 8;
  auto length = static_cast<size_t>(m_buffer.GetSize());
  U8_cptr src = static_cast<U8_cptr>(m_buffer.GetDataAddr());
  out_imgch.resize( length );
  switch( GetBitCount() )
  {
  case 4:
    CPL_LOOP2( length, idx )
    {
      auto color = m_ptr_bmp_info->bmiColors[ ((*src) >> ((idx&1)*4)) & 0xf ];
      out_imgch[ idx ] = ((U8*)&color)[in_ch_No];
      ++src;
    }
    break;

  case 8: 
    CPL_LOOP2( length, idx )
    {
      auto color = m_ptr_bmp_info->bmiColors[ *src ];
      out_imgch[ idx ] = ((U8*)&color)[in_ch_No];
      ++src;
    }
    break;

  case 16: // R5G5B5
    CPL_LOOP2( length, idx )
    {
      switch( in_ch_No )
      {
      case 0: out_imgch[ idx ] = R5G5B5(*(U16*)src).GetR(); break;
      case 1: out_imgch[ idx ] = R5G5B5(*(U16*)src).GetG(); break;
      case 2: out_imgch[ idx ] = R5G5B5(*(U16*)src).GetB(); break;
      }
      src += pxl_size;
    }
    break;

  case 24:
  case 32:
    CPL_LOOP2( length, idx )
    {
      out_imgch[ idx ] = src[ in_ch_No ];
      src += pxl_size;
    }
    break;
  }

  return 0;
}

// _____________________________________________________________________________
/// @copydoc Core::Image_i::Output(rout<std::vector<ImageChannel>>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  DIBitmap::Output
    ( rout<std::vector<Core::ImageChannel>> out_imgch_mat ) const
{
  out_imgch_mat.resize( GetChannelNum() );
  CPL_LOOP2( out_imgch_mat.size(), idx )
  {
    Output( out_imgch_mat[ idx ], (int)idx );
  }

  return 0;
}


// _____________________________________________________________________________
/// @brief 開放。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DIBitmap::Release()
{
  m_buffer.Release();
  m_ptr_image         = nullptr;
  m_ptr_bmp_file_head = nullptr;
  m_ptr_bmp_info      = nullptr;

  m_line.clear();
  m_flag_change = true;
}


// _____________________________________________________________________________
/// @brief クローンの作成。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool DIBitmap::Clone( rout<Image_i> out_image ) const
{
  auto* ptr_dib = dynamic_cast<DIBitmap*>(&out_image);

  if( ptr_dib )
  {
    ptr_dib->Create( m_buffer.GetDataAddr(), m_buffer.GetSize() );
  }
  else
  {
    // 作成用パラメタ。
    // ※ formatは無視される。
    CreateInfo
      ci( GetWidth()
        , GetHeight()
        , GetBitCount()
        );
    ptr_dib->Create( ci );

    //
    accessBuffer_(ptr_dib) = m_buffer;
  }

  return nullptr;
}



// _____________________________________________________________________________
/// @brief イメージサイズ変更。
///
/// ※ ビット深度のみ変えることも可。
/// 
/// @param[in] in_width
///   : 新しいイメージの横幅。負数を指定すると、以前の横幅を引き継ぐ。
/// @param[in] in_height
///   : 新しいイメージの縦幅。負数を指定すると、以前の縦幅を引き継ぐ。
/// @param[in] in_color
///   : 塗りつぶしカラー。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  DIBitmap::Resize
    ( _in<int     > in_width
    , _in<int     > in_height
    , _in<A8R8G8B8> in_color
    )
{
  auto width  = in_width;
  auto height = in_height;
  auto bit    = GetBitCount();

  // サイズ検査
  if( width  < 0 )
  {
    width  = GetWidth();
  }
  if( height < 0 )
  {
    height = GetHeight();
  }
  // 深さ比較
  if( bit <= 0  &&  m_ptr_bmp_info )
  {
    bit = m_ptr_bmp_info->bmiHeader.biBitCount;
  }

  if( this->m_buffer.GetSize() == 0 )
  {
    auto& c = in_color;
    // 作成
    CreateInfo
      ci( width
        , height
        , bit
        , float4(c.GetR(), c.GetG(), c.GetB(), c.GetA())
        );
    Create( ci );
  }
  else
  {
    DIBitmap tmp_image( *this ); // 現データを一時コピー
    
    auto& c = in_color;
    // 作成
    CreateInfo
      ci( width
        , height
        , bit
        , float4(c.GetR(), c.GetG(), c.GetB(), c.GetA())
        );
    Create( ci );

    // もとのデータを反映
    int h, w;
    int h_min  = Min<int>(height, tmp_image.GetHeight());
    int w_min  = Min<int>(width,  tmp_image.GetWidth());
    // コピー
    for( h = 0; h < h_min; h++ )
    {
      for( w = 0; w < w_min; w++ )
      {
        auto const color = tmp_image.GetPixel( w, h );
        if( GetBitCount() <= 8 )
        {
          SetPixel( w, h, color.GetA() ); // alpha値にindex が格納されている
        }
        else
        {
          SetPixel( w, h, color.GetColor() );
        }
      }
    }
  }
  return true;
}



//// _____________________________________________________________________________
///// @brief イメージコピー。
///// 
///// @param[in] in_image : コピー元のDIBitmapクラス。
///// 
///// @retval true  : コピー成功。
///// @retval false : コピー失敗。
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//bool DIBitmap::SetDIB( _in<DIBitmap> in_image )
//{
//  try{
//    Release();
//    m_buffer = in_image.m_buffer; // コピー（代入ではない）
//
//    // ファイルヘッダーポインタ設定
//    m_ptr_bmp_file_head = (BITMAPFILEHEADER*)m_buffer.GetDataAddr();
//    // 情報ヘッダーポインタ
//    m_ptr_bmp_info = m_buffer.GetDataAddrT<U8*,BITMAPINFO*>( sizeof(BITMAPFILEHEADER) );
//    // イメージへの先頭ポインタ
//    m_ptr_image = m_buffer.GetDataAddrT<U8*>( m_ptr_bmp_file_head->bfOffBits );
//  }
//  catch(...)
//  {
//    Release();
//    return false;
//  }
//  return true;
//}


// _____________________________________________________________________________
/// @brief 指定した位置のカラーを取得。
/// 
/// ビット深度が4bitもしくは8bitの場合、alpha値にインデックスが格納される。
/// 
/// @param[in] in_X : ｘ座標。
/// @param[in] in_Y : ｙ座標。
/// 
/// @return 指定位置のカラーを返す。範囲外ならば、黒が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DIBitmap::A8R8G8B8 DIBitmap::GetPixel( _in<int> in_X, _in<int> in_Y ) const
{
  if( in_X < 0  ||  GetWidth()  < in_X  ||
      in_Y < 0  ||  GetHeight() < in_Y )
  {
    return A8R8G8B8(); // 範囲外は黒を返す。
  }

//  in_Y = GetHeight() - in_Y - 1;
//  int w_len  = int((GetWidth() * GetBitCount() + 31) / 32) * 4; // イメージの幅（バイト数）
//  int offset = w_len * in_Y + (in_X*GetBitCount()/8);

  BYTE const* buf = GetBmp( in_X, in_Y );

  R5G5B5   c15;
  R8G8B8   c24;
  A8R8G8B8 c32;
  RGBQUAD  argb;

  switch( GetBitCount() )
  {
  case 4:
    argb = m_ptr_bmp_info->bmiColors[ (*buf >> ((~in_X&1)<<2))&0x0f ];
    c32.SetA( *buf >> ((~in_X&1)<<2) );
    c32.SetR( argb.rgbRed );
    c32.SetG( argb.rgbGreen );
    c32.SetB( argb.rgbBlue );
    break;
  case 8:
    argb = m_ptr_bmp_info->bmiColors[ *buf ];
    c32.SetA( *buf );
    c32.SetR( argb.rgbRed );
    c32.SetG( argb.rgbGreen );
    c32.SetB( argb.rgbBlue );
    break;
  case 16:
    ::memcpy( &c15, buf, 2 );
    c32.SetColor( c15.GetColor() );
    break;
  case 24:
    ::memcpy( &c24, buf, 3 );
    c32.SetColor( 255, c24.GetR(), c24.GetG(), c24.GetB() );
    break;
  case 32:
    ::memcpy( &c32, buf, 4 );
    break;
  }
  return c32;
}




// _____________________________________________________________________________
/// @brief カラーの設定（RGBカラー）
/// 
/// @param[in] in_X : ｘ座標。
/// @param[in] in_Y : ｙ座標。
/// @param[in] in_color_or_index
///   : 設定するカラー値。\n
///   : ビット深度が4bitか8bitの場合、パレットインデックスを指定する。
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  DIBitmap::SetPixel
    ( _in<int  > in_X
    , _in<int  > in_Y
    , _in<DWORD> in_color_or_index
    )
{
  if( in_X < 0  ||  GetWidth() < in_X  ||
      in_Y < 0  ||  GetHeight() < in_Y )
  {
    return false;
  }

//  in_Y = GetHeight() - in_Y - 1;
//  int w_len  = int((GetWidth() * GetBitCount() + 31) / 32) * 4; // イメージの幅（バイト数）
//  int offset = GetWidthByte() * in_Y + (in_X*GetBitCount()/8);
  BYTE* buf = GetBmp( in_X, in_Y );
  auto color = A8R8G8B8(in_color_or_index);

  switch( GetBitCount() )
  {
  case 4:
    if( in_X&1 )
    {
      *buf = static_cast<BYTE>((*buf & 0xf0)  |  (in_color_or_index & 0x0f));
    }
    else
    {
      *buf = static_cast<BYTE>((*buf & 0x0f)  |  ((in_color_or_index<<4) & 0xf0));
    }
    break;
  case 8:  *buf = (BYTE)in_color_or_index; break;
  case 16: ::memcpy( buf, &color, 2 ); break;
  case 24: ::memcpy( buf, &color, 3 ); break;
  case 32: ::memcpy( buf, &in_color_or_index, 4 ); break;
  default: return false;
  }
  m_flag_change = true;
  return true;
}


/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// カラーの設定（インデックスカラー）
// 戻り値　true：成功  false：失敗
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool DIBitmap::SetColor( int x, int y, int pal_idx ){
  if( x < 0  ||  GetWidth() < x  ||
    y < 0  ||  GetHeight() < y ){
    return false;
  }

  y = GetHeight() - y - 1;
  int w_len  = int((GetWidth() * GetBitCount() + 31) / 32) * 4; // イメージの幅（バイト数）
  int offset = w_len * y + (x*GetBitCount()/8);

  switch( GetBitCount() ){
  case 4:
    if( x%2 == 0 ){
      m_ptr_image[offset] = (m_ptr_image[offset] & 0xf0)  |  (pal_idx & 0x0f);
    }else{
      m_ptr_image[offset] = (m_ptr_image[offset] & 0x0f)  |  ((pal_idx<<4) & 0xf0);
    }
    break;
  case 8:
    m_ptr_image[ offset ] = (BYTE)pal_idx;
    break;
  default:
    return false;
  }
  return true;
}*/



// _____________________________________________________________________________
/// @brief DIBへの直接ライン描画。
/// 
/// @param[in] in_start_x : 始点ｘ座標。
/// @param[in] in_start_y : 始点ｙ座標。
/// @param[in] in_end_x   : 終点ｘ座標。
/// @param[in] in_end_y   : 終点ｙ座標。
/// @param[in] in_color_or_index
///   : 設定するカラー値。\n
///   : ビット深度が4bitか8bitの場合、パレットインデックスを指定する。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  DIBitmap::DrawLine
    ( _in<int  > in_start_x
    , _in<int  > in_start_y
    , _in<int  > in_end_x
    , _in<int  > in_end_y
    , _in<DWORD> in_color_or_index
    )
{
  // 
/*  int sx = in_start_x, sy = in_start_y;
  int ex = in_end_y,   ey = in_end_y;
  if( sx > ex ){
    sx = in_end_y;
    ex = in_start_x;
  }
  if( sy > ey ){
    sy = in_end_y;
    ey = in_start_y;
  }*/
  if( in_start_x == in_end_x  &&  in_start_y == in_end_y )
  {
    SetPixel( in_start_x, in_start_y, in_color_or_index );
    return;
  }

  float x = (float)in_start_x;
  float y = (float)in_start_y;
  float ax = float(in_end_x - in_start_x);
  float ay = float(in_end_y - in_start_y);
  int   len_x = (int)Abs(ax);
  int   len_y = (int)Abs(ay);

  if( len_x > len_y )
  {
    float add_x = static_cast<float>(Sign(ax));
    float add_y = ay / len_x;
    while( len_x-- )
    {
      SetPixel( (int)x, (int)y, in_color_or_index );
      x += add_x;
      y += add_y;
    }
  }
  else
  {
    float add_x = ax / len_y;
    float add_y = static_cast<float>(Sign(ay));
    while( len_y-- )
    {
      SetPixel( static_cast<int>(x), static_cast<int>(y), in_color_or_index );
      x += add_x;
      y += add_y;
    }
  }

/*  float add_x = (float)ax / (float)len_y;
  float add_y = (float)ay / (float)len_x;
  float a = 1.0f / Max(Abs(add_x),Abs(add_y));
  if     ( Abs(add_x) > 1.0f ){ add_x *= a; }
  ef( Abs(add_y) > 1.0f ){ add_y *= a; }
  int pixel_num = len_x < len_y ? len_y : len_x;
  while( pixel_num-- ){
    SetPixel( (int)x, (int)y, in_color_or_index );
    x += add_x;
    y += add_y;
  };

/*  intfix4 ifX = intfix4(in_start_x); // 初期ｘ
  intfix4 ifY = intfix4(in_start_y); // 初期ｙ
//  intfix4 ifXnx, ifYnx; // 次のピクセルｘｙ
  intfix4 ifXad = intfix4(len_x < len_y ? (float)ax / (float)ay : 1.0f); // x増加分
  intfix4 ifYad = intfix4(len_x < len_y ? 1.0f : (float)ay / (float)ax); // y増加分
  int pixel_num = len_x < len_y ? len_y : len_x;
  while( pixel_num-- ){
    SetPixel( ifX.Int(), ifY.Int(), in_color_or_index );
    ifX += ifXad;
    ifY += ifYad;
  };*/
/*
  // ラインが垂直に近いか、水平に近いかで処理を分ける
  if( lx > ly ){  // 水平に近い場合
    // 固定小数にセット
    intfix4 ay( (float)ly / (float)lx ); // 増分
    intfix4 my( (float)sy );
    // セット
    for( i = sx; i < ex  &&  i < GetWidth(); ++i ){
      SetPixel( i, my.Int(), in_color_or_index );
      my += ay;
      if( my.Int() >= GetHeight() ) break;
    }
  }
  else // 垂直に近い
  {
    // 固定小数にセット
    intfix4 ax( (float)lx / (float)ly ); // 増分
    intfix4 mx( (float)sx );
    // セット
    for( i = sy; i < ey  &&  i < GetHeight(); ++i ){
      SetPixel( mx.Int(), i, in_color_or_index );
      mx += ax;
      if( mx.Int() >= GetWidth() ) break;
    }
  }*/
}



// _____________________________________________________________________________
/// @brief パレットの設定。
/// 
/// インデックスカラー(深度8bit以下)でない場合、パレットインデックスが範囲外の
/// 場合、失敗する。
/// 
/// @param[in] in_index : パレットのインデックス。
/// @param[in] in_color : パレットに設定する32bitARGBカラー値。
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  DIBitmap::SetPaletteColor
    ( _in<int     > in_index
    , _in<A8R8G8B8> in_color
    )
{
  if( FAILED_INDEX(in_index) ) return false;
  int bit = GetBitCount();
  if( bit > 8 )
  {
    return false;
  }
  if( 0 < (int)m_ptr_bmp_info->bmiHeader.biClrUsed  &&
      (int)m_ptr_bmp_info->bmiHeader.biClrUsed <= in_index )
  {
    return false;
  }

  ::memcpy( &m_ptr_bmp_info->bmiColors[ in_index ], &in_color, 4 );
  m_flag_change = true;
  return true;
}



// _____________________________________________________________________________
/// @brief 指定したパレットのカラーを取得。
/// 
/// @param[in] in_index : パレットインデックス。
/// 
/// @return 
/// 成功した場合、in_indexのカラーが返る。\n
/// 失敗した場合、黒(ARGB[0,0,0,0])が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DIBitmap::A8R8G8B8 DIBitmap::GetPaletteColor( _in<int> in_index ) const
{
  if( FAILED_INDEX(in_index) )
  {
    return A8R8G8B8(255,255,255,255);
  }
  int bit = GetBitCount();
  if( bit > 8 )
  {
    return A8R8G8B8(255,255,255,255);
  }
  if( 0 < (int)m_ptr_bmp_info->bmiHeader.biClrUsed  &&
      (int)m_ptr_bmp_info->bmiHeader.biClrUsed <= in_index )
  {
    return A8R8G8B8(255,255,255,255);
  }

  A8R8G8B8 color;
  memcpy( &color, &m_ptr_bmp_info->bmiColors[ in_index ], 4 );
  return color;
}


} // namespace WIN
} // namespace CPL
