> 画面のプロパティの「DPI設定」を「大きなサイズ(120DPI)」...
ここで設定している値はスクリーンDC の dpi。
> pDC->GetDeviceCaps(LOGPIXELSX);
> 結果は、DPI=96 のときも、DPI=120 のときも「360」という値が取得されました。
pDC はプリンタDC。なので、この時の「360」はプリンタDCの dpi。
画面のプロパティで設定した dpi を取得したいのならば対象を間違っている。
> もっと直接的にデスクトップのDPI設定値を取得できないものでしょうか。
”スクリーンDC”に対して GetDeviceCaps() をかませば良い。
もっと言えば、今回の場合は”スクリーンDC”より
http://msdn.microsoft.com/en-us/library/ms690468(VS.85).aspx
で取得できる DC が適当か。
> DPI設定によって印刷される画像の大きさが違ってくるので、困っています。
JPEG 等の画像ファイルは pixel 単位(デバイス単位)でサイズ情報を持っている。
つまり、mm / inch のような、どんな環境でも同じ大きさを表現できる単位では無い。
例えば「1000 pixel」というサイズの場合、
「96 dpi」の環境では
1000 * 25.40 / 96 = 264.583mm
だが、「120 dpi」の環境では
1000 * 25.40 / 120 = 211.667mm
となり、同じ「1000 pixel」でも論理単位変換時に差異が出る。
> pDC->SetMapMode(MM_LOENGLISH);
> :
> m_pGazo->Render()
「JPEGオリジナルサイズの ○○mm で印刷して」という意図の処理だと思うが、↑で述べた
通りデバイス単位→論理単位の変換は環境に依存する。
つまり、○○の部分を(如何なる環境においても)一意に決める、という事は基本困難。
# それでも○○の部分を一意に決めたいケースはやはりある。
# なので、論理単位変換時に必要な情報(dpi 等)も一緒に保存する画像形式もある
#(JPEG も保存可能)のだが…、残念ながら IPicture はその情報を利用してなかった
# ので意味無しだったと思う
お休みモードに入るので、以降レス途絶えます…
> int cycaption = ::GetSystemMetrics(SM_CYCAPTION); //タイトルバーの高さを取得
> double cyratio = cycaption / 27.08;
デスクトップPCだと画面のプロパティからデザイン変更できますよね。
モバイルPC用のテクニックは使えないのでは?
HIMETRICtoDP → HIMETRICtoLP だったらどうなります?
#25.04なんてマジックナンバー使わずに済むと思う。
96dpiでも120dpiでも画面に描画されるJPEG画像の大きさは同じでしたっけ?
HIMETRICtoDPとHIMETRICtoLPの比を印刷時に考慮すれば良いかも。
出来ました!
gak様、ありがとうございました。
> ここで設定している値はスクリーンDC の dpi。
> pDC はプリンタDC。なので、この時の「360」はプリンタDCの dpi。
> 画面のプロパティで設定した dpi を取得したいのならば対象を間違っている。
その通りです。間違っていました。恥ずかしい。
CDC *pScreenDC = GetDC();
int dpi = pScreenDC->GetDeviceCaps(LOGPIXELSX);
m_pGazo->Render(hDC, 0, 0,
(long)(hmWidth/25.40*dpi/100), -(long)(hmHeight/25.40*dpi/100),
0, hmHeight, hmWidth, -hmHeight, NULL);
ReleaseDC(pScreenDC);
これでバッチリOKです。
画像の大きさが画面と印刷で一致しました。jpg, gif, bmp・・・確認しました。
> http://msdn.microsoft.com/en-us/library/ms690468(VS.85).aspx
こちらは、取得するのが HDC なので GetDeviceCaps(LOGPIXELSX) が出来ません。
>「JPEGオリジナルサイズの ○○mm で印刷して」という意図の処理だと思うが、
> ↑で述べた通りデバイス単位→論理単位の変換は環境に依存する。
> つまり、○○の部分を(如何なる環境においても)一意に決める、という事は
> 基本困難。
この作成中のアプリで、ダイアログ上に表示した画像・文字・線などを、
同じ配置関係で印刷出来ればいいんです。
解決です。有り難うございました。
ISLeさん、レスありがとうございました。
情報が断片的でわかりにくいけど、
IPicture::get_Width
IPicture::get_Height
がディスプレイの論理解像度に依存して報告してくるのが問題になっているということかな?
gakさんの、IPictureはJPEGの解像度を利用しないという情報から
画面側はピクセル単位での描画が正確なサイズと考えられます。
以下はVC6のOLESTD.Hに定義されているピクセル値への変換マクロです。(ppliは解像度)
#define HIMETRIC_PER_INCH 2540 // number HIMETRIC units per inch
#define MAP_LOGHIM_TO_PIX(x,ppli) MulDiv((ppli), (x), HIMETRIC_PER_INCH)
Ti Amoさんの
>CDC *pScreenDC = GetDC();
>int dpi = pScreenDC->GetDeviceCaps(LOGPIXELSX);
>m_pGazo->Render(hDC, 0, 0,
> (long)(hmWidth/25.40*dpi/100), -(long)(hmHeight/25.40*dpi/100),
> 0, hmHeight, hmWidth, -hmHeight, NULL);
>ReleaseDC(pScreenDC);
のコードと比べてみると実は全く同じことをしているようです。
ということは、画面の方は少なくともこの部分は結局MM_TEXTモードでの描画に変更して
ますね?
印刷側も同じコードだと解像度が高いプリンタではつらい気もするのですが、
参考までに印刷側もどのように修正して解決したのか教えていただけるとありがたいです。
>Ti Amoさんの
>>CDC *pScreenDC = GetDC();
>>int dpi = pScreenDC->GetDeviceCaps(LOGPIXELSX);
>>m_pGazo->Render(hDC, 0, 0,
>> (long)(hmWidth/25.40*dpi/100), -(long)(hmHeight/25.40*dpi/100),
>> 0, hmHeight, hmWidth, -hmHeight, NULL);
>>ReleaseDC(pScreenDC);
これが印刷部分です。
質問の冒頭に書いた目的です。
> 子ダイアログ上にJPEG画像を表示し、それを印刷しようと考えています。
改めて、印刷部分と子ダイアログ表示部分のコードを記します。
【印刷部分】
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();
if (m_pGazo != NULL)
{
CDC *pScreenDC = GetDC();
int dpi = pScreenDC->GetDeviceCaps(LOGPIXELSX);
ReleaseDC(pScreenDC);
long hmWidth, hmHeight;
m_pGazo->get_Width(&hmWidth);
m_pGazo->get_Height(&hmHeight);
HDC hDC = pDC->GetSafeHdc();
m_pGazo->Render(hDC, 0, 0,
(long)(hmWidth/25.40*dpi/100), -(long)(hmHeight/25.40*dpi/100),
0, hmHeight, hmWidth, -hmHeight, NULL);
}
【子ダイアログに表示している部分】
CFile f; から pstm->Release(); までは上記と同じコードを OnInitDialog() の中に
書いています。
void CDlgHyouji::OnPaint() の中です。
if (m_pGazo != NULL)
{
long hmWidth, hmHeight;
m_pGazo->get_Width(&hmWidth);
m_pGazo->get_Height(&hmHeight);
CSize size(hmWidth, hmHeight);
CDC* pDC = GetDC();
pDC->HIMETRICtoDP(&size);
ReleaseDC(pDC);
HDC hDC = ::GetDC(m_hWnd);
m_pGazo->Render(hDC, 0, 0,
size.cx, size.cy,
0, hmHeight, hmWidth, -hmHeight, NULL);
::ReleaseDC(m_hWnd, hDC);
}
お気づきのことがありましたら、教えてください。
ダイアログのコードの方の
pDC->HIMETRICtoDP(&size);
は内部的に印刷側と同じ計算が行われていると思います。
画面側のm_pGazo->Renderと印刷側のm_pGazo->Render
に渡している画像サイズは同じになっていませんか?
そうであれば単にピクセル値をMM_LOENGLISHモードで
描画しているということになります。
画面側の物理解像度がたまたま100DPIに近い値だから、
ほぼ同サイズで印刷されるのではないでしょうか。
> 画面側のm_pGazo->Renderと印刷側のm_pGazo->Render
> に渡している画像サイズは同じになっていませんか?
その通りです。同じ値となっています。
最初は印刷側でも
pDC->HIMETRICtoDP(&size);
として、これを Render に使いましたが、サイズが表示側と一致しないので
困った訳です。
そのときの pDC がプリンタDCであったことに気づいていなかったのです。
今や
pScreenDC->HIMETRICtoDP(&size);
とすれば何も問題ありません。
物凄い遠回りをしました。
お陰で勉強になりました。
subaru様のご指導で、すっきりしたコードになりました。
ありがとうございました。