GDI+でJPEG変換するには?

解決


うい  2009-06-03 21:44:05  No: 70246  IP: [192.*.*.*]

開発環境はVC++6 WindowsXPです。

DIBをjpeg変換してメモリ上に
(ディスクに持たずに)持ちたいと考えています。
GDI+でこういった実装は可能でしょうか。
ヒントでいいので、よろしくお願い致します。

編集 削除
うい  2009-06-03 22:27:58  No: 70247  IP: [192.*.*.*]

現在、ここまで作成しています。
pBmpはnewなどを入れてません。
FromBITMAPINFO関数だけで動くと考えています。
Save関数で落ちてしまいます。
方向性など間違っていたらご指摘お願い致します。

Bitmap pBmp;
BITMAPINFOHEADER* pvbmpInfoHeader;
BITMAPINFO* pvbmpInfo;
  :
(中略)
  :
//pvbmpInfoHeaderに画像データが入ってることは
//確認済です
pvbmpInfo = (BITMAPINFO*)pvbmpInfoHeader;
pBmp->FromBITMAPINFO(m_pvbmpInfo, m_pvbmpInfoHeader+1);
CLSID encoderClsid;
WCHAR wszFileName[256];
MultiByteToWideChar(CP_ACP, 0, "C:\\test.jpg", -1, wszFileName, MAX_PATH);

if ( 0 <= GetEncoderClsid(L"image/jpeg",&encoderClsid) ){
    //ここで落ちます。
    pBmp->Save(wszFileName, &encoderClsid );
}

編集 削除
うい  2009-06-03 22:28:53  No: 70248  IP: [192.*.*.*]

pBmp->FromBITMAPINFO(m_pvbmpInfo, m_pvbmpInfoHeader+1);

pBmp->FromBITMAPINFO(pvbmpInfo, pvbmpInfoHeader+1);
の間違いでした。

編集 削除
wclrp ( 'o')  2009-06-03 23:02:44  No: 70249  IP: [192.*.*.*]

>pBmpはnewなどを入れてません。
じゃあ何入れているの?

正しくないポインタでのpBmp->hogeは間違った使い方だよ。
メンバにアクセスしなかったとか不正な処理にならないこともあるけど。

FromBITMAPINFO等に関しては使ったことないしわかりません。

編集 削除
うい  2009-06-03 23:43:44  No: 70250  IP: [192.*.*.*]

返信ありがとうございます。

>じゃあ何入れているの?
何もいれずに、FromBITMAPINFOでビットマップのアドレスに
ポインタが移動するのかと考えてます。

C言語が正しく理解できてないのと、
FromBITMAPINFO関数に関する情報が探せてないので
詰まってしまってます。

編集 削除
aetos  2009-06-04 02:47:44  No: 70251  IP: [192.*.*.*]

> 何もいれずに、FromBITMAPINFOでビットマップのアドレスに
> ポインタが移動するのかと考えてます。

それはまずあり得ません。
というか、FromBITMAPINFO って static じゃないですか。
http://msdn.microsoft.com/en-us/library/ms536288.aspx

なので、
Bitmap * pBmp = Bitmap::FromBITMAPINFO( ... );
というのが正しい使い方です。

編集 削除
うい  2009-06-04 20:30:56  No: 70252  IP: [192.*.*.*]

pBmp->FromBITMAPINFO(pvbmpInfo, pvbmpInfoHeader+1);

Bitmap * pBmp = Bitmap::FromBITMAPINFO(pvbmpInfo, pvbmpInfoHeader+1);
に修正することで動作いたしました。
ありがとうございます。

編集 削除
うい  2009-06-04 21:42:00  No: 70253  IP: [192.*.*.*]

Save関数の引数でIStream型が指定できるようになっているのですが、
これはどういったものを指すのでしょうか。

下記に載せさせて頂いた他言語のサンプルだと
そういった型が存在していて、データを取得できるようなのですが、
VC++でcinやstreambufなど幾つか型を使用してみても
ビルドエラーになります。
IStreamとはどういった型を指すのか、
ヒントでもいいのでよろしくお願い致します。

Delphiのサンプル
http://blog.livedoor.jp/junki560/archives/22845646.html
.NETのサンプル
http://kait-field.spaces.live.com/Blog/cns!B90E9B4A3C4DFD66!401.entry

編集 削除
wclrp ( 'o')  2009-06-05 01:12:52  No: 70254  IP: [192.*.*.*]

IStreamの派生クラスを自分で作れば
メモリにデータを保存するとか自由にできるってことだよ。

C++のcinやstreambufなどはこういった用途には使えない。
IStreamの派生クラスじゃないから。

C++標準ライブラリにあるオブジェクトでこの手のものに使えるものは無い。
.NETやCOMで使えるように作ったライブラリじゃないから。

編集 削除
wclrp ( 'o')  2009-06-05 01:27:23  No: 70255  IP: [192.*.*.*]

CreateStreamOnHGlobalで
GlobalAllocによるメモリ用のIStreamのインスタンスが作れる。
面倒なので自作せずにこれで大抵済ませるね。
一旦メモリ置けば暗号化してファイル保存とか好き勝手できるから。

google検索してもMSDNの.NETのが見つかり
C++とは微妙に違うから不便だな。

編集 削除
aetos  2009-06-05 11:21:37  No: 70256  IP: [192.*.*.*]

ファイルを対象とするなら、IShellFolder::BindToObject でも IStream を取得できますね。

編集 削除
うい  2009-06-07 00:07:16  No: 70257  IP: [192.*.*.*]

CreateStreamOnHGlobalを試させて頂きました。
うまく実装できてないからなのか、取得した中身が
予想したものを違うものが入っています。

BOOL CTestGDIDlg::CreateOnHGlobal(IStream** ppIStream)
{
  HRESULT  hr;

  if(ppIStream == NULL)
    return  false;

  HGLOBAL h = ::GlobalAlloc(GMEM_MOVEABLE, 100000);
  hr = ::CreateStreamOnHGlobal(h,TRUE,ppIStream);
  if(SUCCEEDED(hr) && *ppIStream)
    return  true;

  *ppIStream = NULL;
  return  false;
}
上記の関数で作ったIStreamは
Save関数で落ちなくはなったのですが、
その後の処理で、
BYTE b[100];
ULONG  ul;
pIStream->Read(b, 100, &ul);
と呼んでも変数bの中は
173 186 13 240 173 186 13 ・・・
と同じ数値が連続してあるだけでした。
Read関数は
Read(コピーされた情報が入る配列、
コピーするバイト数、
実際にコピーされたバイト数)
と解釈しております。

CreateOnHGlobal関数の実装が間違っていますでしょうか。
よろしくお願い致します。

編集 削除
wclrp ( 'o')  2009-06-07 00:40:32  No: 70258  IP: [192.*.*.*]

IStream::Readなんてしないでメモリ直接見ればいいんじゃないの。
GlobalLock参照。

pIStream->Read(b, 100, &ul);
ulに読めたバイト数が入っているんだけど。

俺はCImage::Loadで使ったことしかないので推測だけど
先頭にIStream::SeekしてからIStream::Readするんじゃないの。

ところでSeekで相対移動0で現在位置を調べないと
データのサイズが判らないってことだろうか。

編集 削除
うい  2009-06-08 23:36:54  No: 70259  IP: [192.*.*.*]

>IStream::Readなんてしないでメモリ直接見ればいいんじゃないの。
>GlobalLock参照。
これはCopyMemory関数を使うということでしょうか。

下記のHPを参考にしました。
http://oshiete1.goo.ne.jp/qa4892351.html

>先頭にIStream::SeekしてからIStream::Readするんじゃないの。
Seek後にRead関数を呼ぶと正しい値が取得できました。

編集 削除
aetos  2009-06-09 17:29:07  No: 70260  IP: [192.*.*.*]

CreateStreamFromHGlobal に渡した HGLOBAL を GlobalLock すればポインタが取得できる。
今回は HGLOBAL が CreateStreamFromHGlobal のローカル変数なので内部から触れない。
HGLOBAL を外に出すか、GetHGlobalFromStream を使う。

編集 削除
aetos  2009-06-09 17:29:43  No: 70261  IP: [192.*.*.*]

> 内部から触れない。
外部から

編集 削除
aetos  2009-06-09 18:29:26  No: 70262  IP: [192.*.*.*]

# ぐだぐだやorz

> HGLOBAL が CreateStreamFromHGlobal のローカル変数なので内部から触れない。

HGLOBAL が CreateOnHGlobal のローカル変数なので外部から触れない。

編集 削除
うい  2009-06-17 21:39:00  No: 70263  IP: [192.*.*.*]

レスありがとうございます。
返信遅くなってすいません。

>CreateStreamFromHGlobal に渡した HGLOBAL を GlobalLock すればポインタが取得できる。

わかりやすいご説明、ありがとうございます。
戻り値にポインタが返ってきて、
それを操作する考え方が
なかったのでとてもためになりました。

メモリを操作する(動的配列など)ところは苦手だったのですが、
皆さんのおかげで、便利さがわかったので
今度から使えるように勉強していこうと思います。
ありがとうございました。

編集 削除