| はじめに |
なぜにLinux UnknownなのにWin32のプログラムが置いてあるのか、疑問にもつ人
が多いと思います。しかし、作ったものはしょうがない。製造元責任として、
ここに置かせといて下さい(コンテンツが少ないのが、本音)。
このJPEG,GIF,BMP画像Viewerは、そのプログラム名にあるとおり、
MDIでBITMAP, GIF, JPEGフォーマットの画像ファイルを表示します。
また表示したBITMAP,GIF,JPEGフォーマットの画像に対してモノクロ、
反転(ネガポジ)処理を実行できます。
ここでは、JPEG,GIF,BMP画像Viewerの作成において、最も重要なビットマップ
について説明します。
| 目次 |
| ビットマップ(DDBとDIB) |
| デバイス依存ビットマップ(DDB) |
typedef struct tagBITMAP
{
int bmType;
int bmWidth;
int bmHeight;
int bmWidthBytes;
BYTE bmPlanes;
BYTE bmBitsPixel;
LPVOID bmBits;
}BITMAP;
IDB_REDBLOCKS BITMAP DISCARDABLE "res\\RedBlocks.bmp"
CMyView::OnDraw(CDC* pDC)
{
CBitmap bitmap;
CDC dcDisplayMemory;
bitmap.LoadBitmap(IDB_REDBLOCKS);
dcDisplayMemory.CreateCompatibleDC(pDC);
dcDisplayMemory.SelectObject(&bitmap);
pDC->BitBlt(100, 100, 54, 96, &dcDisplayMemory, 0, 0, SRCCOPY);
}
| デバイス独立ビットマップ(DIB) |
DIBはカラー情報の形式が決まっているため、プログラム内で直接配列を書き換 えることが出来るため、非常に便利です。
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; // BITMAPINFOHEADERのサイズ
LONG biWidth; // 幅(ピクセル単位)
LONG biHeight; // 高さ(ピクセル単位)
WORD biPlanes=1;
WORD biBitCount; // 1ピクセルあたりのカラービット数
DWORD biCompression; // BI_RGB, BI_RLE4, BI_RLE8のいづれか
DWORD biSizeImage; // DIBビットイメージのバイト数(BI_RLE4,8の場合)
LONG biXPelsPerMeter=0;
LONG biYPelsPerMeter=0;
DWORD biClrUsed; // カラーテーブルエントリの数
DWORD biClrImportant; // カラーテーブルエントリの数
}BITMAPINFOHEADER, *LPBITMAPINFOHEADER;
typedef struct tagRGBQUAD
{
BYTE rgbBlue; // 青色用カラーテーブル
BYTE rgbGreen; // 緑色用カラーテーブル
BYTE rgbRed; // 赤色用カラーテーブル
BYTE rgbReserved; // フラグ用カラーテーブル
}RGBQUAD; // 32ビットカラーテーブルエントリ
IDB_REDBLOCKS BITMAP DISCARDABLE "res\\RedBlocks.bmp"
---CMyView.cpp---
LPBITMAPINFO lpBitmapInfo;
void CMyView::OnInitailUpdate()
{
CScrollView::OnInitailUpdate();
CSize totalSize(30000, 40000); //30 x 40 cm
CSize lineSize = CSize(totalsize.cx / 100, totalSize.cy / 100);
SetScrollSizes(MM_HIMETRIC, totalSize, totalSize, lineSize);
// リソース読み込み
LPVOID lpvResource = (LPVOID) ::LoadResource(NULL,
::FindResource(NULL,MAKEINTRESOURCE(IDB_REDBLOCKS),
RT_BITMAP));
lpBitmapInfo = (LPBITMAPINFO)::LockResource(lpvResource);
CClientDC dc(this);
}
void CMyView::OnDraw(CDC* pDC)
{
// DIBビットイメージの先頭アドレスを求める
int nNumColors;
switch(lpBitmap->biBitCount)
{
case 1: nNumColors = 2; break;
case 4: nNumColors = 16; break;
case 8: nNumColors = 256; break;
default: nNumColors = 0; break;
}
LPBYTE lpvBits = (LPBYTE)lpBitmapInfo + sizeof(LPBITMAPINFO)
+ sizof(RGBQUAD) * nNumColors;
// DIBリソース表示
::StretchDIBits(pDC->mhDC, 0, 0,
(int) lpBitmapInfo->biWidth, (int) lpBitmapInfo->biHeight,
lpvBits, lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY);
}
| ピクチャ(CPictureHolder) |
ピクチャは、Visual Basic のPictureプロパティとして知られています。ビット マップ、アイコン、メタファイルの他にJpeg,Gif画像も扱うことが出来ます。 CPictureHolderクラスは、このPictureプロパティをインプリメントしたもの です。
---CMyView.cpp---
#include < afxctl.h >
CPictureHolder* m_pPicture;
BOOL CMyView::OnOpenDocument(LPCTSTR lpszPathName)
{
LPSISPATCH pDisp;
COleVariant varName(lpszPathName);
if (SUCCEEDED(OleLoadPictureFile(varName, &pDisp)))
{
if (m_pPicture != NULL) DeleteContents();
m_pPicture = new CPictureHolder;
m_pPicture->SetPictureDispatch(LPPICTUREDISP)pDisp);
return TRUE;
}
return FALSE;
}
void CMyView::DeleteContents()
{
delete m_pPicture;
m_pPicture = NULL;
}
void CMyView::OnDraw(CDC* pDC)
{
CSize size(0,0);
OLE_XSIZE_HIMETRIC width;
OLE_YSIZE_HIMETRIC height;
// ピクチャをウィンドウの中央に描画
if (m_pPicture != NULL)
{
CRect rect;
GetClientRect(&rect);
if (SUCCEEDED(m_pPicture.m_pPict->get_Width(&width)) &&
SUCCEEDED(m_pPicture.m_pPict->get_Height(&height)))
size = CSize(width, height);
else return;
// 左上の位置を計算
CPoint ptOffset((rect.right - size.cx) / 2,
(rect.left - size.cy) / 2);
if (ptOffset.x < 0) ptOffset.x = 0;
if (ptOffset.y < 0) ptOffset.y = 0;
// ピクチャのレンダリング
rect = CRect(ptOffset, size);
m_pPicture->Render(pDC, &rect, &rect);
}
}
| ピクチャ => デバイス依存ビットマップ(DDB)変換 |
---CMyDoc.cpp---
#include < afxctl.h >
CPictureHolder* m_pPicture;
BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
LPSISPATCH pDisp;
COleVariant varName(lpszPathName);
if (SUCCEEDED(OleLoadPictureFile(varName, &pDisp)))
{
if (m_pPicture != NULL) DeleteContents();
m_pPicture = new CPictureHolder;
m_pPicture->SetPictureDispatch(LPPICTUREDISP)pDisp);
if(!GetPictureBitmap()) return FALSE;
return TRUE;
}
return FALSE;
}
void CMyDoc::DeleteContents()
{
delete m_pPicture;
m_pPicture = NULL;
CDocument::DeleteContents();
}
HBitmap CMyDoc::GetBitmap()
{
short type;
OLE_HANDLE hBitmap;
if (!m_pPicture) return NULL;
IPicture *pPict = m_pPicture->m_pPict;
ASSERT(pPict);
pPict->get_Type(&type);
return type == PICTYPE_BITMAP &&
SUCCEEDED(pPict->get_Handle(&hBitmap)) ? (HBITMAP)hBitmap : NULL;
}
BOOL CMyDoc::GetPictureBitmap()
{
HBITMAP hBitmap = GetBitmap();
if (hBitmap == NULL) return FALSE;
m_pBitmap = new CBitmap;
m_pPitmap = CBitmap::FromHandle(hBitmap);
if (m_pBitmap == NULL) return FALSE;
return TRUE;
}
| ピクチャ => デバイス独立ビットマップ(DIB)変換 |
---CMyView.cpp---
#include < afxctl.h >
LPBITMAPINFOHEADER m_lpBMIH;
CPalette* m_lpPalette;
LPVOID m_lpvColorTable;
char* m_lpImage;
DWORD m_dwSizeImage;
CMyView::CMyView()
{
m_lpPalette = new CPalette;
}
CMyView::~CMyView()
{
delete m_lpPalette;
delete m_lpImage;
delete m_lpBMIH;
m_lpImage = NULL;
m_lpBMIH = NULL;
}
CSize CMyView::GetPictureSize(CPictureHolder *pHolder)
{
ASSERT(pHolder);
CSize size(0,0);
OLE_XSIZE_HIMETRIC width;
OLE_YSIZE_HIMETRIC height;
if (pHolder && pHolder->m_pPict &&
SUCCEEDED(m_pPicture.m_pPict->get_Width(&width)) &&
SUCCEEDED(m_pPicture.m_pPict->get_Height(&height)))
{
CDC *pDC = getDC();
ASSERT(pDC);
size = CSize(width, height);
pDC->HIMETRICtoDP(&size);
ReleaseDC(pDC);
}
return size;
}
// 透明GIF使用時の背景の消去しきれないゴミを消去
BOOL CMyView::OnEraseBkgnd(CDC* pDC)
{
CRect rectClip;
if (pDC->GetClipBox(&rectClip) != NULLREGION)
pDC->FillSolidRect(&rectClip, ::GetSysColor(COLOR_APPWORKSPACE));
return TRUE;
}
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc->GetDocument();
ASSERT_VALID(pDoc);
CPoint orgin;
CSize size;
origin.x = 20;
origin.y = -20;
size.cx = m_lpBMIH->biWidth;
size.cy = m_lpBMIH->biHeight;
//表示用パレットを前景パレットで設定
CPalette* oldPalette = pDC->SelectPalette(m_lpPalette, FALSE);
pDC->RealizePalette();
::StretchDIBits(pDC->GetSafeHdc(), origin.x, origin.y, size.cx,size.cy,
0, 0, m_lpBMIH->biWidth, m_lpBMIH->biHeight,
m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);
//残りのパレットは背景パレットで設定
pDC->SelectPalette(oldPalette, TRUE);
}
void CMyView::OnInitailUpdate()
{
CScrollView::OnInitailUpdate();
CSize sizeTotal(0, 0);
SetScrollSizes(MM_HIMETRIC, sizeTotal);
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CDC* pDC = new CDC;
BITMAP bm;
HDC hdc = pDC->GetSafeHdc();
if (hcd == NULL)
{
CClientDC dc(this);
OnprepareDC(&dc);
HBITMAP hBitmap = pDoc->GetBitmap();
pDC->CreateCompatibleDC(&DC);
::SelectObject(pDC->GetSafeHdc(), hBitmap);
::GetObject(hBitmap, sizeof(bm), &bm);
if (PictureCnvDIB(pDC->GetSafeHdc(), &bm) == NULL) return;
ReleseDC(&dc);
}
delete pDC;
}
int CMyView::PictureCnvDIB(HDC hdc, BITMAP* pBitmap)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// 1ビットあたりのカラービット数からパレットのエントリ数を確認
int m_nColorTableEntries = 0;
switch(pBitmap->bmBitsPixel)
{
case 1: m_nColorTableEntries = 2; break;
case 4: m_nColorTableEntries = 16; break;
case 8: m_nColorTableEntries = 256; break;
default: m_nColorTableEntries = 0;
}
// BITMAPINFOHEADER 作成
int nSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)
* m_nColorTableEntries;
m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
m_lpvColorTable = (LPBYTE) m_lpBMIH->sizeof(BITMAPINFOHEADER);
memset(m_lpvColorTable, 0, sizeof(RGBQUAD) * m_nColorTableEntries);
m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);
m_lpBMIH->biWidth = pBitmap->bmWidth;
m_lpBMIH->biHeight = pBitmap->bmHeight;
m_lpBMIH->biPlanes = 1;
m_lpBMIH->biBitCount = pBitmap->bmBitsPixel;
m_lpBMIH->biCommpression = BI_RGB;
m_lpBMIH->biSizeImage = 0;
m_lpBMIH->biXpelsPerMeter = 0;
m_lpBMIH->biYpelsPerMeter = 0;
m_lpBMIH->biClrUsed = m_nColorTableEntries;
m_lpBMIH->biClrImportant = m_nColorTableEntries;
dwBytes = ((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) / 32;
if (((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) % 32) dwBytes++;
dwBytes *= 4;
m_dwSizeImage = dwBytes * m_lpBMIH->biHeight;
m_lpImage = new char[m_dwSizeImage];
int res = ::GetDIBits(hdc, pDoc->GetBitmap(),
(UINT)0, (UINT)m_lpBMIH->biHeight,
(LPVOID)m_lpImage, (LPBITMAPINFO)m_lpBMIH, DIB_RGB_COLORS);
//論理パレット作成
LPLOGPALETTE pLogPal = (LPLOGPALETTE) new char[2 * sizeof(WORD) +
m_nColorTableEntries * sizeof(PALETTEENTRY)];
//バージョンとパレットのエントリ数を設定
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = m_nColorTableEntries;
// システムパレットを取得する
::GetSystemPaletteEntries(hdc, 0, m_nColorTableEntries, pLogPal->palPalEntry);
m_lpPalette->CreatePalette(pLogPal);
delete pLogPal;
return res;
}
| サンプルプログラム |
| 問題点 |
今回のプログラムでは Win32 API ::OleLoadPictureFile 関数を使用して
BITMAP, GIF, JPEG画像を取得しています。この関数はパレット調節に不具合が
あるため、画面を256色表示に設定した場合に、原画像を正確に描画できません。