実現したいこと
デバイスコンテキスト上のビットマップに画像をロードして図形とテキストを追記した後、.icoファイルとして出力したいです。
発生している問題・エラーメッセージ
出力した画像の画質が荒くなり、画質の荒れ具合も毎回変化して不安定。
元となる画像のビット数は24bitなのに、出力される画像のビット数が32bitになってしまう。
調査内容
以下のようにCImageクラスを利用しないパターンも実装してみましたが、出力されるファイルが真っ黒の何も描画されていない画像になってしまいます。
C++
1void CTalkListDlg::MakeUnreadCountIcon( int nUnreadCount ) 2{ 3 HICON hIcon = (HICON)LoadImage( AfxGetApp( )->m_hInstance, MAKEINTRESOURCE( アイコンファイルのリソースID ), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR ); 4 5 HDC hDC = ::GetDC( m_hWnd ); 6 HDC hMemDC = CreateCompatibleDC( hDC ); // メモリデバイスコンテキスト作成 7 HBITMAP hBmp = CreateCompatibleBitmap( hMemDC, 32, 32 ); // サイズ指定してビットマップ作成 8 ::ReleaseDC( m_hWnd, hDC ); 9 SelectObject( hMemDC, hBmp ); 10 DrawIconEx( hMemDC, 0, 0, hIcon, 32, 32, 0, NULL, DI_NORMAL | DI_DEFAULTSIZE ); 11 12 // 赤い円を描画 13 省略 14 Ellipse( hMemDC, nCircleLeft, nCircleTop, nCircleRight, nCircleBottom ); 15 省略 16 // 17 18 // 未読件数を描画 19 CString strUnreadCount; 20 省略 21 DrawText( hMemDC, strUnreadCount, strUnreadCount.GetLength( ), rcText, DT_SINGLELINE | DT_CENTER | DT_VCENTER ); 22 DeleteObject( hNewFont ); 23 // 24 25 CString strOutputFile = _T( "アイコンファイルの出力先パス" ); 26 HBITMAP hBitmap = (HBITMAP)GetCurrentObject( hMemDC, OBJ_BITMAP ); 27 SaveIconFile( hBitmap, strOutputFile ); 28 29 DeleteObject( hIcon ); 30 ::ReleaseDC( m_hWnd, hDC ); 31}
結果
出力される画像
※外側の黒い枠はスクリーンショットをトリミングした際の名残です。
該当のソースコード
C++
1typedef struct _ICONDIR 2 { 3 WORD wReserved; // 予約 常に0 4 WORD wResourceType; // リソースタイプ 1:アイコン 2:カーソル 5 WORD wResourceCount; // アイコンの数 6 } ICONDIR; 7 8 typedef struct _ICONDIRENTRY 9 { 10 BYTE bWidth; // アイコンの幅 11 BYTE bHeight; // アイコンの高さ 12 BYTE bColorCount; // カラー数 13 BYTE bReserved; // 予約 14 WORD wColorPlanes; // カラープレーン数(カーソル時、Xホットスポット) 15 WORD wPixcelBit; // ピクセルのビット深度(カーソル時、Yホットスポット) 16 DWORD dwIconFileSize; // アイコンファイルのサイズ 17 DWORD dwIconOffset; // ファイルの先頭からピクセルデータまでのオフセット 18 } ICONDIRENTRY; 19 20void CTalkListDlg::ChangeIconUnreadState() 21{ 22 int nUnreadCount = CountUnreadMessage(); // 未読コメント数を取得 23 24 if ( 0 < nUnreadCount ) { // 未読コメントがある場合 25 // 未読件数付きのアイコンを作成 26 MakeUnreadCountIcon( nUnreadCount ); 27 28 // タスクバーアイコン変更 29 theApp.m_hTaskBarIcon = (HICON)LoadImage( AfxGetApp()->m_hInstance, _T( "アイコンファイルのパス" ), IMAGE_ICON, 0, 0, LR_LOADFROMFILE ); 30 HICON hIcon = (HICON)LoadImage( AfxGetApp()->m_hInstance, MAKEINTRESOURCE( アイコンファイルのリソースID ), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR ); 31 SetIcon( hIcon, FALSE ); 32 SetIcon( theApp.m_hTaskBarIcon, TRUE ); 33 } 34} 35 36//// 未読件数を表示した画像を作成 37void CTalkListDlg::MakeUnreadCountIcon( int nUnreadCount ) 38{ 39 HICON hIcon = (HICON)LoadImage( AfxGetApp( )->m_hInstance, MAKEINTRESOURCE( アイコンファイルのリーソースID ), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR ); 40 41 CImage ciLoadImage; 42 CImage ciBackImage; 43 CDC *pDC = GetDC(); 44 CString strIconPath; 45 strIconPath.Format( _T( "下地となるアイコンファイルのパス" ) ); 46 ciLoadImage.Load( strIconPath ); 47 ciBackImage.Create( 32, 32, 24, 0 ); 48 HDC hDC = ciBackImage.GetDC( ); 49 DrawIconEx( hDC, 0, 0, hIcon, 32, 32, 0, NULL, DI_NORMAL ); 50 ciLoadImage.Draw( hDC, 0, 0, 32, 32 ); 51 ciLoadImage.Destroy(); 52 53 //HDC hDC = ::GetDC( m_hWnd ); 54 //HDC hMemDC = CreateCompatibleDC( hDC ); // メモリデバイスコンテキスト作成 55 //HBITMAP hBmp = CreateCompatibleBitmap( hMemDC, 32, 32 ); // サイズ指定してビットマップ作成 56 //::ReleaseDC( m_hWnd, hDC ); 57 //SelectObject( hMemDC, hBmp ); 58 //DrawIconEx( hMemDC, 0, 0, hIcon, 32, 32, 0, NULL, DI_NORMAL | DI_DEFAULTSIZE ); 59 60 // 赤い円を描画 61 int nCircleLeft = 13; 62 int nCircleTop = 13; 63 int nCircleRight = 32; 64 int nCircleBottom = 32; 65 HPEN hOldPen = (HPEN)SelectObject( hDC, GetStockObject( NULL_PEN ) ); 66 HBRUSH hNewBrush = (HBRUSH)CreateSolidBrush( RGB( 255, 0, 0 ) ); 67 HBRUSH hOldBrush = (HBRUSH)SelectObject( hDC, hNewBrush ); 68 Ellipse( hDC, nCircleLeft, nCircleTop, nCircleRight, nCircleBottom ); 69 SelectObject( hDC, hOldPen ); 70 SelectObject( hDC, hOldBrush ); 71 DeleteObject( hNewBrush ); 72 // 73 74 // 未読件数を描画 75 CString strUnreadCount; 76 HFONT hNewFont; 77 if ( 99 < nUnreadCount ) { 78 strUnreadCount = _T( "99+" ); 79 hNewFont = CreateFont( 16, 7, 0, 0, FW_MEDIUM, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T( "メイリオ" ) ); 80 } 81 else if ( 9 < nUnreadCount ) { 82 strUnreadCount.Format( _T( "%d" ), nUnreadCount ); 83 hNewFont = CreateFont( 16, 9, 0, 0, FW_MEDIUM, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T( "メイリオ" ) ); 84 } 85 else { 86 strUnreadCount.Format( _T( "%d" ), nUnreadCount ); 87 hNewFont = CreateFont( 18, 11, 0, 0, FW_MEDIUM, FALSE, FALSE, 0, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T( "メイリオ" ) ); 88 } 89 HGDIOBJ hObj = SelectObject( hDC, hNewFont ); 90 91 int nDifference = 4; 92 CRect rcText; 93 rcText.left = nCircleLeft + nDifference; 94 rcText.top = nCircleTop + nDifference; 95 rcText.right = nCircleRight - nDifference; 96 rcText.bottom = nCircleBottom - nDifference; 97 SetTextColor( hDC, RGB( 255, 255, 255 ) ); 98 SetBkColor( hDC, RGB( 255, 0, 0 ) ); 99 DrawText( hDC, strUnreadCount, strUnreadCount.GetLength( ), rcText, DT_SINGLELINE | DT_CENTER | DT_VCENTER ); 100 DeleteObject( hNewFont ); 101 // 102 103 CString strOutputFile = _T( "出力するアイコンファイルの保存先パス" ); 104 HBITMAP hBitmap = (HBITMAP)GetCurrentObject( hDC, OBJ_BITMAP ); 105 SaveIconFile( hBitmap, strOutputFile ); 106 107 //ciBackImage.Draw( *pDC, 0, 0, 32, 32 ); // 描画する画像の確認用 108 ciBackImage.ReleaseDC(); 109 ciBackImage.Destroy(); 110 111 DeleteObject( hIcon ); 112 ::ReleaseDC( m_hWnd, hDC ); 113} 114 115int sizebits( BITMAPINFO *lpbi ) 116{ 117 if ( 1 != lpbi->bmiHeader.biPlanes ) { 118 return -1; 119 } 120 121 switch ( lpbi->bmiHeader.biBitCount ) { 122 default: 123 return -1; 124 case 1: 125 case 4: 126 case 8: 127 case 16: 128 case 24: 129 case 32: 130 break; 131 } 132 int szbits = ( ( ( ( lpbi->bmiHeader.biWidth * lpbi->bmiHeader.biBitCount + 7 ) / 8 + 3 ) / 4 ) * 4 ) * ( lpbi->bmiHeader.biHeight ); 133 return szbits; 134} 135 136// Bitmapヘッダー作成、Bitmapファイル出力 137void CTalkListDlg::SaveIconFile( HBITMAP hBmp, CString strFileName ) 138{ 139 ICONDIR icDirHeader = { 0 }; // アイコンファイルヘッダー 6byte 140 ICONDIRENTRY icDirEntry = { 0 }; // アイコン情報 16byte 141 BITMAPINFO *pBmpInfo; // ビットマップ情報 40byte 142 143 BITMAP bmp = { 0 }; // ビットマップ構造体 144 LONG imageSize; // 画像のサイズ 145 LONG pixcels; // 画素数 146 LPBYTE bits; // 画像ビット 147 HDC hDC, hMemDC; 148 149 // BITMAP情報を取得する 150 GetObject( hBmp, sizeof( bmp ), &bmp ); 151 hDC = ::GetDC( 0 ); 152 hMemDC = CreateCompatibleDC( hDC ); 153 ::ReleaseDC( 0, hDC ); 154 HBITMAP hBmpOld = (HBITMAP)SelectObject( hMemDC, hBmp ); 155 156 // ファイルサイズ計算 157 imageSize = bmp.bmWidthBytes * bmp.bmHeight + sizeof( ICONDIR ) + sizeof( ICONDIRENTRY ) + sizeof( BITMAPINFOHEADER ); 158 switch ( bmp.bmBitsPixel ) { 159 case 2: 160 pixcels = 2; 161 break; 162 case 4: 163 pixcels = 16; 164 break; 165 case 8: 166 pixcels = 256; 167 break; 168 default: 169 pixcels = 0; 170 } 171 imageSize += ( sizeof( RGBQUAD ) * pixcels ); 172 173 // ICONDIR出力 174 ZeroMemory( &icDirHeader, sizeof( icDirHeader ) ); 175 icDirHeader.wReserved = 0; 176 icDirHeader.wResourceType = 1; 177 icDirHeader.wResourceCount = 1; 178 179 // BITMAPINFOHEADER 出力 180 pBmpInfo = new BITMAPINFO[sizeof( BITMAPINFOHEADER ) + sizeof( RGBQUAD ) * pixcels]; 181 ZeroMemory( pBmpInfo, sizeof( BITMAPINFOHEADER ) ); 182 pBmpInfo->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); 183 pBmpInfo->bmiHeader.biWidth = bmp.bmWidth; 184 pBmpInfo->bmiHeader.biHeight = bmp.bmHeight; 185 pBmpInfo->bmiHeader.biPlanes = 1; // 1 186 pBmpInfo->bmiHeader.biBitCount = bmp.bmBitsPixel; 187 pBmpInfo->bmiHeader.biCompression = BI_RGB; // 非圧縮 188 if ( 0 != pixcels ) { 189 GetDIBColorTable( hMemDC, 0, pixcels, pBmpInfo->bmiColors ); // カラーDIB情報取得 190 } 191 192 // 画像データ取得 193 bits = new BYTE[bmp.bmWidthBytes * bmp.bmHeight]; 194 GetDIBits( hMemDC, hBmp, 0, bmp.bmHeight, bits, pBmpInfo, DIB_RGB_COLORS ); 195 196 // ICONDIRENTRY出力 197 char chWidth = bmp.bmWidth; 198 char chHeight = bmp.bmHeight; 199 200 icDirEntry.bWidth = chWidth; 201 icDirEntry.bHeight = chHeight; 202 icDirEntry.bColorCount = 1 << pBmpInfo->bmiHeader.biBitCount; 203 icDirEntry.bReserved = 0; 204 icDirEntry.wColorPlanes = pBmpInfo->bmiHeader.biPlanes; 205 icDirEntry.wPixcelBit = pBmpInfo->bmiHeader.biBitCount; 206 207 int palettesize = pBmpInfo->bmiHeader.biBitCount <= 8 ? ( 1 << pBmpInfo->bmiHeader.biBitCount ) : 0; 208 icDirEntry.dwIconFileSize = palettesize + sizebits( pBmpInfo ) + sizebits( pBmpInfo ); // 対応するビットマップデータのバイト数 209 icDirEntry.dwIconOffset = sizeof( ICONDIR ) + sizeof( ICONDIRENTRY ); // 22 210 211 // バイナリファイル書き込み 212 CString strErrorMsg; 213 WriteIconBinary( strFileName, icDirHeader, icDirEntry, *pBmpInfo, bits, strErrorMsg ); 214 215 delete[] bits; 216 delete[] pBmpInfo; 217 SelectObject( hMemDC, hBmpOld ); 218 DeleteDC( hMemDC ); 219}
補足情報(FW/ツールのバージョンなど)
開発環境 Windows VisualStudio 2013 C++

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。