C++プログラミングで、画像処理をする時に使う、GDI+。

 きっと今ではもっといい方法、あるのかもしれないけれど、昔ながらのプログラミングでやっていると、これしかない。

 

// PNG 形式のイメージ (BPP = 32ビット) を構築 //
Gdiplus::Bitmap sImagePNG(width , height , PixelFormat32bppARGB) ;

 

ファイルを保存する時に、

" image/png"

みたいな、固有の文字列を使用しなければならない。

 

 これ、調べて直接指定してもいいけれど、それはアマチュアレベル。

 仕様が変更されてもいいように、システムから取り出す方がいい。


MSDNでサンプルがある。

その改良版。

 

// GDI+ で対応可能な拡張子の情報を検索 //
// ※ pResultFormat      : CLSID用フォーマット文字列 "image/***" //
// ※ pResultClsid       : CLSID //
// ※ pResultDescription : "JPEG" , "BMP" などの表題 //
BOOL FindEncoderClsidByExt(const CString& strEXT    , CString* pResultFormat , CLSID* pResultClsid , CString* pResultDescription)
{
 /************************************************************************
          CodecName          ,  FilenameExtension          , FormatDescription , MimeType
    [0]. Built-in BMP Codec  ,  *.BMP;*.DIB;*.RLE          ,  BMP              , image/bmp
    [1]. Built-in JPEG Codec ,  *.JPG;*.JPEG;*.JPE;*.JFIF  ,  JPEG             , image/jpeg
    [2]. Built-in GIF Codec  ,  *.GIF                      ,  GIF              , image/gif
    [3]. Built-in TIFF Codec ,  *.TIF;*.TIFF               ,  TIFF             , image/tiff
    [4]. Built-in PNG Codec  ,  *.PNG                      ,  PNG              , image/png
 **********************************************************************/
    UINT num  ; // number of image encoders
    UINT size ; // size, in bytes, of the image encoder array

    // 1. 初期設定 //
    BOOL bFound = FALSE ;

    // 2. 定数情報を読み取り //
    // (1). 読み取る情報のサイズを取得 //
    // How many encoders are there?
    // How big (in bytes) is the array of all ImageCodecInfo objects?
    GetImageEncodersSize(&num , &size) ;

    // (2). サイズ確保 //
    // Create a buffer large enough to hold the array of ImageCodecInfo
    // objects that will be returned by GetImageEncoders.
    ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size)) ;

    // (3). 実際に読み取り //
    // GetImageEncoders creates an array of ImageCodecInfo objects
    // and copies that array into a previously allocated buffer.
    // The third argument, imageCodecInfo, is a pointer to that buffer.
    GetImageEncoders(num , size , pImageCodecInfo) ;

    // 3. 拡張子による検索 //
    // Display the graphics file format (MimeType)
    // for each ImageCodecInfo object.
    for(UINT j = 0 ; j < num ; ++j)
    {
        // (1). 拡張子の連結を取得  :  "*.BMP;*.DIB;*.RLE" //
        LPCTSTR lpszEnumEXT = pImageCodecInfo[j].FilenameExtension ;

        // (2). 拡張子の連結 "*.BMP;*.DIB;*.RLE" に指定された拡張子が含まれるか //
        BOOL bResult = CheckEnumEXT(strEXT , lpszEnumEXT) ;

        // (3). 存在しなかった場合 //
        if ( ! bResult ) continue ;

        // (4). 結果 //
        // ※ pResultFormat      : CLSID用フォーマット文字列 "image/***" //
        if (pResultFormat) pResultFormat->Format(_T("%s") , pImageCodecInfo[j].MimeType) ;
        // ※ pResultClsid       : CLSID //
        if (pResultClsid) *pResultClsid = pImageCodecInfo[j].Clsid ;
        // ※ pResultDescription : "JPEG" , "BMP" などの表題 //
        if (pResultDescription) pResultDescription->Format(_T("%s") , pImageCodecInfo[j].FormatDescription) ;

        // (5). 終了 //
        bFound = TRUE ;
        break ;
    }

    // 3. メモリ解放 //
    ::free(pImageCodecInfo) ;

    return bFound ;
}

/////////////////////////////////////////////////////////////////////
// 拡張子の連結 "*.BMP;*.DIB;*.RLE" に指定された拡張子が含まれるか //
BOOL CheckEnumEXT(const CString& strEXT , LPCTSTR lpszEnumEXT)
{
    CString strFind ;

    // 1. CString 型 //
    // (1). 拡張子の連結を取得  :  "*.BMP;*.DIB;*.RLE" //
    CString strEnumExt(lpszEnumEXT);

    // (2). 最後に ";" を追加 //
    strEnumExt += _T(";") ;

    // (3). 検索する文字列 //
    strFind.Format(_T("*.%s;") , strEXT) ;
    strFind.MakeUpper() ;

    // 2. 検索 //
    int nPos = strEnumExt.Find(strFind , 0) ;

    // 3. 結果 //
    // (1). 存在しなかった場合 //
    if (nPos < 0) return FALSE ;

    return TRUE ;
}

こんな感じで、拡張子を大文字で指定するだけで、固有のフォーマットなどを取り出すようにしている。

 

MSDNでも、検索すると、出てくる。

 

 フリンジ。パープルフリンジ。

 

 そんな言葉は知らなかったけど、調べたら出てきた。

 キャラクター画像の背景色を塗り潰すと、境界線に汚れが残っている。

 主にJPEG画像で、そうなる。

 

全て背景が白に見えるが、そうではない。

この汚れをペイントで頑張って塗り潰すのは、本当に大変。

いつも、そう思ってきた。

 

それで、自動的にこのフリンジを消すソフトを作れないか、思っていた。

 

PNG形式ファイルでの保存が、技術的に上手くできなくて、以前一度挫折した。

 

今回、フリンジ、その対策でフリーソフトが完成。約10日間。

機能は極めてシンプル。

 

フリーソフト【 背景透過&境界線お掃除 】

  http://yoyontek-develop.minim.ne.jp/clean-pic-fringe/


 

 

*************************************************************
Windows用フリーソフト(無料)総合サイト
http://yoyontek-develop.minim.ne.jp/
-------------------------------------------------------------
  (1) 【 背景透過&境界線お掃除 】
  http://yoyontek-develop.minim.ne.jp/clean-pic-fringe/
  画像ファイルの背景を透過させ、境界線の汚れを除去(PNG対応)
-------------------------------------------------------------
  (2) 【 休憩タイムアラート for KIDS 】
  http://yoyontek-develop.minim.ne.jp/alert-kids/
  子供のゲーム中に休憩時間をお知らせ
*************************************************************

 PNG形式での保存方法。数回に分けて記事にする。

 それなりに頑張る必要はある。数行でOKというわけにはいかない。

 

 ポイントは

(1)DIB形式でデータ保持(24ビットでHBITMAPと連動しながら、透過情報を持たせる)

(2)GDI+をしっかり管理(初期化やメモリリーク防止など)

(3)CLSID sFormatClsid の取得
あたりだろうか。

 

(1)は、DIBクラスを独自に作成(世の中に例は多くある)。そこに透過情報をうまく加える。

(2)も、世の中、色々と使い方は例がある。

(3)は、MSDNのサンプルを解読して上手に使う。

 

SetPixel()を使うと処理が遅いけれど、仕方ないか。

 

 さて、独自のDIBクラスを作ってあるとして・・・

-----------------------------------------------------------------------------

#include <gdiplus.h>
using namespace Gdiplus;
// ライブラリ
#pragma comment( lib, "gdiplus.lib" )

-----------------------------------------------------------------------------

// (静的関数) DIB (CDibArgb) を PNG 形式で保存 //
BOOL SaveImageByPNG(LPCTSTR lpszPathFilename , const CDibArgb* pDibArgb)
{
    Gdiplus::Color sColor ;
    ARGB argb ;
    COLORREF clrRgb ;
    BOOL bTransparent ;

    // 1. 初期設定 //
    // (1). 不透明度 alpha //
    BYTE alpha[2] = { 255 , 0 } ; // 透明 = 0 //

    // (2). 画像サイズ //
    int width  = pDibArgb->GetWidth()  ;
    int height = pDibArgb->GetHeight() ;

    // (3). 拡張子 (大文字) //
    CString strEXT = (拡張子を抽出 / lpszPathFilename) ;

    // 2. PNG 形式のイメージ (BPP = 32ビット) を構築 //
    Gdiplus::Bitmap sImagePNG(width , height , PixelFormat32bppARGB) ;

    // 3. 全てのピクセルを設定 //
    for(int y = 0 ; y < height  ; y++)
    {
        for(int x = 0 ; x < width ; x++)
        {
            // (1). COLORREF 取得 //
            clrRgb = pDibArgb->GetPixel(x , y) ;

            // (2). 透過であるか取得 //
            bTransparent = pDibArgb->IsAttrTransparent(x , y) ;

            // (3). ARGB 作成 //
            argb = Gdiplus::Color::MakeARGB(
                            alpha[bTransparent] ,
                            GetRValue(clrRgb) ,
                            GetGValue(clrRgb) ,
                            GetBValue(clrRgb)
                        ) ;


            // (4). Color 変数を作成 //
            sColor.SetValue(argb) ;

            // (5). ピクセル設定 //
            sImagePNG.SetPixel(x , y , sColor) ;
        }
    }

    // 4. ファイルを PNG 形式で保存 //
    // (1). フォーマット //
    CLSID fPngClsid ;
    BOOL bResult = FindEncoderClsidByExt(strEXT , NULL , &fPngClsid , NULL) ;

    // (2). 保存 //
    if (bResult) sImagePNG.Save(lpszPathFilename , &fPngClsid , NULL) ;

    return bResult ;
}
-----------------------------------------------------------------------------
 独自で作成している関数がいくつもあって、これだけでは動かない。申し訳ない。

 

 for() 分の中身が重要だ。ピクセルに、透過情報を持った32ビット数値を代入して、GDI+に任せて保存している。

 それだけでよい。

 

あとは、(1)~(3)を、しっかり記述していくこと。

 

次回(2)に続く。

 

*************************************************************
Windows用フリーソフト(無料)
http://yoyontek-develop.minim.ne.jp/
-------------------------------------------------------------
【 背景透過&境界線お掃除 】
http://yoyontek-dev-clr.minim.ne.jp/
画像ファイルの背景を透過させ、境界線の汚れを除去(PNG対応)
-------------------------------------------------------------
【 休憩タイムアラート for KIDS 】
http://yoyontek-dev-alt.minim.ne.jp/
子供のゲーム中に休憩時間をお知らせ
*************************************************************