VC2005, MFC, SDI, CFormView アプリケーションです。
子ダイアログ上にJPEG画像を表示し、それを印刷しようと考えています。
このページを参考にしてやってみました。
http://www.athomejp.com/goldfish/mfc/cobject/jpegview.asp
COMのIStreamとIPictureを使い、Renderメソッドで描く手法となっています。
表示は問題なく出来ました。
印刷が出来ません。
OnPrint(CDC* pDC, CPrintInfo* pInfo) の中に、上記とほぼ同じコードを書きました。
Render関数の中の、次の2点が問題と思っています。
m_pJpeg->Render(dc, 0, 0, size.cx, size.cy,
0, hmHeight, hmWidth, -hmHeight, &rc);
1.第1引数の dc の代わりに pDC とすると「HDCではない」と叱られるので、
HDC hDC = ::GetDC(m_hWnd);
とやって、この hDC を使いました。
2.最後の引数 &rc は GetClientRect ですから、これはいけないと思って
NULL にしました。
初心者で理屈がよく判っていません。
Render関数も MSDN Library で出てきません。
OnPrint の中ではどのようなコードにしたらいいか、どうぞご教授ください。
よろしくお願いいたします。
画面に表示するときはウィンドウのデバイスコンテキストに描画するのと同じように、
印刷するときはプリンタのデバイスコンテキストに描画してやります。
> 1.第1引数の dc の代わりに pDC とすると「HDCではない」と叱られるので、
> HDC hDC = ::GetDC(m_hWnd);
> とやって、この hDC を使いました。
これではやはりダイアログのデバイスコンテキストになってしまいます。
pDC->GetSafeHdc() で取得したものを渡してください。
> 2.最後の引数 &rc は GetClientRect ですから、これはいけないと思って
> NULL にしました。
NULL で構いませんが、その理由は MSDN を見て確認しておいてください。
http://msdn.microsoft.com/en-us/library/ms682202(VS.85).aspx
aetos さん、有り難うございます。
HDC hDC = pDC->GetSafeHdc();
とやって、Render の第1引数にこの hDC を置いたら、一応うまくいきました。
hDC の解放の仕方が判りません。
hDC->unused;
としたのですが、これでいいでしょうか?
また、OnPaint のマップモードを都合があって LOENGLISH にしているので、
HIMETRIC から LOENGLISH に換算するためサイズを 25.40 で割りました。つまり、
size.cx, size.cy のところを、(int)(hmWidth/25.40), (int)(hmHeight/25.40)
と書きました。
しかし、印刷結果は約2割小さくなります。
何故でしょう?
よろしくお願いいたします。
> hDC->unused;
> としたのですが、これでいいでしょうか?
ダメです。
その hDC は解放する必要はありません。
> しかし、印刷結果は約2割小さくなります。
size、hmWidth、hmHeight を取得しているコードと、OnPaint のコードを全部載せられま
すか?
長すぎるようなら適度に省略してください。
一つ前の発言中、OnPaint は OnPrint の書き間違いでした。
> その hDC は解放する必要はありません。
判りました。
void CJPEGPrint::OnPrint(CDC *pDC, CPrintInfo* pInfo)
の中に、以下のように記述しています。
pDC->SetMapMode(MM_LOENGLISH);
CFile f;
if (f.Open(_T(Sample.jpg), CFile::modeRead) == FALSE)
{
AfxMessageBox(_T(画像ファイルが見つかりません));
return;
}
DWORD dwSize;
HGLOBAL h;
void *pBuf;
LPSTREAM pstm;
HRESULT hr;
dwSize = (DWORD)f.GetLength();
h = ::GlobalAlloc(GMEM_MOVEABLE, dwSize);
if (h == NULL)
{
f.Close();
AfxMessageBox(_T(メモリが確保できないので\n画像の表示・印刷を中止しま
す));
return;
}
pBuf = ::GlobalLock(h);
if (pBuf == NULL)
{
f.Close();
::GlobalFree(h);
AfxMessageBox(_T(GlobalLockが失敗したので\n画像の表示・印刷を中止しま
す));
return;
}
if (f.Read(pBuf, dwSize) != dwSize)
{
f.Close();
::GlobalUnlock(h);
::GlobalFree(h);
AfxMessageBox(_T(読み込めないので\n画像の表示・印刷を中止します));
return;
}
f.Close();
::GlobalUnlock(h);
pstm = NULL;
hr = ::CreateStreamOnHGlobal(h, TRUE, &pstm);
if (SUCCEEDED(hr) == FALSE)
{
pstm->Release();
AfxMessageBox(_T(IStream失敗のため\n画像の表示・印刷を中止します));
return;
}
hr = ::OleLoadPicture(pstm, dwSize, FALSE, IID_IPicture, (LPVOID*)&m_pGazo);
if (SUCCEEDED(hr) == FALSE || m_pGazo == NULL)
{
pstm->Release();
AfxMessageBox(_T(画像ロード失敗のため\n画像の表示・印刷を中止します));
return;
}
pstm->Release();
HDC hDC = pDC->GetSafeHdc();
long hmWidth;
long hmHeight;
if (m_pGazo != NULL)
{
m_pGazo->get_Width(&hmWidth);
m_pGazo->get_Height(&hmHeight);
m_pGazo->Render(hDC, 0, 0,
(int)(hmWidth/25.40), -(int)(hmHeight/25.40),
0, hmHeight, hmWidth, -hmHeight, NULL);
}
訂正です。
Render 関数の第4,5引数の (int) は間違いで (long) です。
結果は同じです。
よろしくお願いいたします。
原因が判りました!
自分のPC環境は、Windows XP で、画面のプロパティの「DPI設定」を「大きなサイズ
(120DPI)」で使っていました。
これを「通常のサイズ(96DPI)」に変更したところ、画像の表示と印刷がほぼ同じ大きさ
となりました。
pDC->BitBlt() や pDC->TextOut() 等の場合、DPI設定には無関係に画像や文字の大きさ
は表示と印刷で一致します。
IPicture の場合は、Windows環境のDPI設定を取得して補正しなければならないのでしょ
う。
> m_pGazo->Render(hDC, 0, 0,
> (int)(hmWidth/25.40), -(int)(hmHeight/25.40),
サイズを固定値で割っているのが少しきになりまして・・・。
HIMETRIC 単位をデバイス単位に変換する
CDC::HIMETRICtoDP(CSize& size)
とかいう関数もあったりします。
私は実際にテストしてみていませんが、「JPEGファイルを表示する」サンプルコードを
作ってくれているサイトがありましたので、参考にされてみてはいかがでしょうか。
http://athomejp.com/goldfish/mfc/cobject/jpegview.asp
すみません、引数の型間違えてたので訂正です。
ついでに、ちゃんと書きました・・^^;
void CDC::HIMETRICtoDP(LPSIZE lpSize) const;
らら さん、レス有り難うございます。
「参考に・・・」とご提示戴いたサイトは、元々参考にしたサイトで、この質問の冒頭
に掲げております。
> void CDC::HIMETRICtoDP(LPSIZE lpSize) const;
もちろん、これでもやってみました。
CSise size;
pDC->HIMETRICtoDP(&size);
・・・・
m_pGazo->Render(hDC, 0, 0,
size.cx, -size.cy,
0, hmHeight, hmWidth, -hmHeight, NULL);
これでも、WindowsデスクトップのDPI設定によって、印刷される画像の大きさが違って
きます。
(int)(hmWidth/25.40), -(int)(hmHeight/25.40), と 25.40 で割っているのは、前に
も書いていますが、OnPrint() の中でマップモードを pDC->SetMapMode(LOENGLISH) と
しているためです。
hmWidth, hmHeight が HIMETRIC なので、LOENGLISH との比が 25.40 になります。
要するに、WindowsデスクトップのDPI設定によって印刷される画像の大きさが違ってく
るので、困っています。
DPI の取得の仕方を研究していますが、まだ判りません。
どなたかお解りでしたらご教授お願いします。
GetDeviceCaps に LOGPIXELSX / LOGPIXELSY を渡せば取得できたかも。
aetos さん、ありがとうございます。
やってみました。
pDC->GetDeviceCaps(LOGPIXELSX);
結果は、DPI=96 のときも、DPI=120 のときも「360」という値が取得されました。
自分の環境での値だと思うのですが、Capacity(能力)ということだろうと納得してお
る次第です。
やはり能力ですね。
画面のプロパティ → 設定 → 詳細設定 → DPI設定のコンボボックスで
「カスタム設定」を開くと、スケールが 360 まであります。
こんなページがありました。
http://msdn.microsoft.com/en-us/library/ms701681(VS.85).aspx
「DPIを意識したアプリケーショの作り方」というんでしょうか。
文中に、「GetSystemMetricsで好みのユーザーインターフェースのサイズを取得し、そ
れをスケールにしろ。」とあります。
そこで、
int cycaption = ::GetSystemMetrics(SM_CYCAPTION); //タイトルバーの高さを取得
double cyratio = cycaption / 27.08;
m_pGazo->Render(hDC, 0, 0,
(long)(hmWidth/25.40*cyratio), -(long)(hmHeight/25.40*cyratio),
0, hmHeight, hmWidth, -hmHeight, NULL);
としたら、デスクトップのDPI設定を変えても画像の印刷と表示の大きさが一致し、一定
となりました。
また、こんなページもありました。
http://msdn.microsoft.com/ja-jp/library/ms229605(VS.80).aspx
Win32 SDK 関数の GetDialogBaseUnits というのがあるそうです。
上記と同じことのようです。戻り値がlongでややこしい。
もっと直接的にデスクトップのDPI設定値を取得できないものでしょうか。