簡単なグラフをビットマップ形式で保存したいのですが・・・

ActiveBasicでのプログラミングでわからないこと、困ったことなどがあったら、ここで質問してみましょう(質問を行う場合は、過去ログやWeb上であらかじめ問題を整理するようにしましょう☆)。
返信する
メッセージ
作成者
アラタシ
記事: 6
登録日時: 2007年2月17日(土) 16:17

簡単なグラフをビットマップ形式で保存したいのですが・・・

#1 投稿記事 by アラタシ »

初めて質問させていただきます初心者です。アラタシと申します。よろしくお願いいたします。

エクセルなどで作ったカンマ区切りの数値データファイルを開き、それをもとにWindow内に簡単なグラフを描き、それを印刷するというアプリケーションを作っています。グラフの描画と印刷はできたのですが、今度はそのグラフをビットマップ形式の画像ファイルとして保存したいと考えています。参考書やヘルプを見ているのですが、具体的に何をすればいいのか分からず、悩んでいます。どなたか、ご教示願えないでしょうか?以下に、プログラムの流れのみを示します。わざわざコードにするまでもないのですが・・・。

Windowを二つ用意しています。一つはMainWndで、ここでデータファイルのオープンをし、カンマ区切りの数値データを読み込みます。ファイルには複数のデータセットが含まれており、データセット毎に配列に数値を格納していきます。MainWndに配置したリストボックスにデータセット名が羅列されるようになっており、データセットのうちどれか一つを選択すると、PlotWndという別ウィンドウを開いてそこにそのデータセットのグラフを描画します。

コード: 全て選択


'====Openボタンを押したとき====
Sub MainWnd_IDM_OPEN_MenuClick()
    '(略)ファイルを開くダイアログでファイルを選択
    '(略)Open fileName$ For Input As 1 などとしてカンマ区切りの数値データをデータセット毎に配列に読み込み
    '(略)MainWndに作ったリストボックスにデータセット名を羅列して表示
End Sub

'====データリストでいずれかを選択(クリック)したとき====
Sub MainWnd_DataList_SelChange()
    '(略)データリストで何番目のデータセットを選択したかを取得
    DataNo=SendDlgItemMessage(hMainWnd,DataList,LB_GETCURSEL,0,0)
    '(略)グラフ描画用ウィンドウPlotWndを開く
    ShowWindow(hPlotWnd,SW_SHOWNORMAL)
    '(略)PlotWndに再描画要求を出す
    InvalidateRect(hPlotWnd,ByVal 0,1)
End Sub
ここからグラフ描画です。以下はPlotWndに対するイベントプロシージャです。なお、PlotWndには「印刷」というメニューを配してあります。

コード: 全て選択


'====PlotWndにグラフを描画====
Sub PlotWnd_Paint(hDC As HDC)
    '(略)ペンやブラシを用いて、上で選択したDataNoで指定された数値データ配列についてLineToやEllipse命令語で折れ線グラフのようなものを描きます。
End Sub

'====印刷メニューが押されたとき====
Sub PlotWnd_IDM_PRINT_MenuClick()
    '(略)印刷ダイアログを開いてプリンタを選択
    hDC=pd.hDC
    '(略)
    '印刷ジョブ開始
    StartDoc(hDC, docinfo)
        StartPage(hDC)
             '下のPlotWnd_Printプロシージャを呼ぶ
             PlotWnd_Print(hDC)
        EndPage(hDC)
    EndDoc(hDC)
    '(略)
End Sub

'====プリンタのデバイスコンテキストに描画====
Sub PlotWnd_Print(hDC As DWord)
    '(略)Sub PlotWnd_Paintでウィンドウに描画したときとほぼ同じ要領で、ペンやブラシを用いて、上で選択したDataNoで指定された数値データ配列についてLineToやEllipse命令後で折れ線グラフグラフを描画。PlotWndに描画したときとは少しだけグラフの仕様が異なっている。
End Sub
流れは以上の通りですが、ここまではうまくいっています。やりたいことは、プリンタに描画したものと同じものをビットマップ形式で画像ファイルとして保存できるようにすることです。PlotWndに「印刷」に加えて「ビットマップを保存」というメニューを配し、それをクリックすると、データと同じフォルダに適当なファイル名のビットマップファイルが保存されるようにしたいと考えています。

長々と書いてしまいましたが、最後までお読みいただきどうもありがとうございました。アドバイスいただければ大変嬉しいです。どうぞよろしくお願い申し上げます。
yama
記事: 58
登録日時: 2005年5月31日(火) 21:11
お住まい: 新潟市
連絡する:

#2 投稿記事 by yama »

直接的なアドバイスは皆さんに譲りますが、私は困ったときは猫頼み・くめい様頼みです。

画面や印刷が出来ていますなら、もう一息では?
私は出来ませんが・・

http://www.kumei.ne.jp/c_lang/sdk2/sdk_174.htm

なんぞを参考にされてはどうでしょう。
イグトランス
記事: 899
登録日時: 2005年5月31日(火) 17:59
お住まい: 東京都
連絡する:

#3 投稿記事 by イグトランス »

CreateDIBSection関数でDIBセクションを作ればよいと思います。
これははHBITMAPを返すので,メモリデバイスコンテキストに対してSelectObjectします。こうすればPlotWnd_Paintへ描画させることができます。
それと同時にCreateDIBSectionではDIBのメモリが得られます。多少のヘッダを付けてからDIBを書き出せば,よくあるビットマップファイルの構造になります。
NoWest
記事: 264
登録日時: 2005年5月31日(火) 10:52
お住まい: 高知
連絡する:

Re: 簡単なグラフをビットマップ形式で保存したいのですが・・・

#4 投稿記事 by NoWest »

過去に同じような質問があって、誰だったかビットマップを保存する関数を作成していたはずです。

SaveBitmap?? SaveBMP???
検索してみてください。
ゲスト

#5 投稿記事 by ゲスト »

質問者のアタラシです。

yamaさん
イグトランスさん
NoWestさん

早速お返事いただき、ありがとうございました!
CreateDIBSection関数というのを使いこなすにはABの勉強だけしていてもダメな感じですね・・・。ビットマップファイルのヘッダ作成などにもまったく自信がないのですが、何を参考にすればいいのでしょう・・・?WinAPIの参考書などでしょうか?
NoWestさんに教えていただいたSaveBMPという関数も見つけることができました!
konisiさんが作られたものですね。 早速検討してみます!ありがとうございました。
ゲスト

#6 投稿記事 by ゲスト »

質問者のアラタシです。
CreateDIBSection関数はABでサポートされていないのでしょうか?
以下のコードをコンパイルしようとすると、
“CreateDIBSection(NULL,VarPtr(biBMP),DIB_RGB_COLORS,VarPtr(pixel),NULL,0)”の全体が「無効な識別子です」と言われてしまいます。

コード: 全て選択


    '1900*3100ピクセル、32ビットDIB用BITMAPINFO設定
    Dim biBMP As BITMAPINFO
    Dim hbmpBMP As HBITMAP
    Dim pixel As DWordPtr

    ZeroMemory(VarPtr(biBMP),Len(biBMP))
    
    biBMP.bmiHeader.biSize=SizeOf(BITMAPINFOHEADER)
    biBMP.bmiHeader.biBitCount=32
    biBMP.bmiHeader.biPlanes=1
    biBMP.bmiHeader.biWidth=1900
    biBMP.bmiHeader.biHeight=-3100

    hbmpBMP=CreateDIBSection(NULL,VarPtr(biBMP),DIB_RGB_COLORS,VarPtr(pixel),NULL,0)

konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

#7 投稿記事 by konisi »

多分これでいいと思うんですが、どうでしょうか?

コード: 全て選択

Declare Function CreateDIBSection Lib "gdi32.dll" (hdc As Long,ByRef pbmi As BITMAPINFO,iUsage As Long, ByRef ppvBits As Long,hSection As Long,dwOffset As Long) As Long
参照元

SaveBMP・・・覚えてなかった・・・。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
アラタシ
記事: 6
登録日時: 2007年2月17日(土) 16:17

#8 投稿記事 by アラタシ »

質問者のアラタシです。

konisiさん、お返事ありがとうございました。そうですね・・・私もあの後調べて
Declare Functionをしなければならないことに気付きました。早速試してみます。
どうもありがとうございました。

ところで、別トピックを立てるべき質問かもしれませんが・・・別の質問です。

DeleteObject や DeleteDC をおこなう意味というのは何なのでしょうか?
これを怠るとどのような不具合が起こるのでしょうか?

例えば私は、ウィンドウにグラフを描画して、それを印刷したり、拡張メタファイルと
してクリップボードにコピーしたりするプログラムを作っていますが、プリンタをデバ
イスコンテキストにした場合、印刷が終わるとDeleteDCしていますし、メタファイル
をクリップボードにコピーし終わったら、使用したメモリデバイスコンテキストを破棄
しています。グラフの描画のためにCreateしたペンやブラシについても使用後は
毎回DeleteObjectしています。しかし、これは参考にしたコードを真似ただけで、
意味はわかっていません。ウィンドウにグラフを描画した後は、デバイスコンテキス
トを破棄してませんし、リストボックスやエディットボックス類のハンドルも破棄して
ません。何を破棄し、何を破棄する必要がないのか、ポイントをアドバイスいただけ
れば非常に嬉しいです。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

#9 投稿記事 by konisi »

僕の頭ではメモリが有限だから、と理解しています。

Windowsに、「こんなデータのペンを使うよ」等と教えてあげる事でペンやブラシを使えるようになるのですが、こいつらを作る時には、そのデータを保存する領域が必要です。
ようするにメモリが消費されるわけですね。

いつもペンをWindowsに返さなければWindowsは「ペンを作るだけのメモリが足りないよ」という事にいずれなってしまいます。
また、メモリがなければ新しいウインドウも作れないし、それの描画もできない。新しい実行ファイルを動かす事すらできない。そんな事になってしまいます。
だから、「もう使い終わったから、ペンは廃棄して、使ってたメモリは再利用していいよ」と教えてあげる事が必要なんです。

ちなみに、エディットボックスやリストボックスのハンドルもウインドウが閉じる時に一緒に破棄されてますよ。
CreateWindowExで作られてるのでDestroyWindowが必要な気もしますけど・・・実行ファイルが終了しちゃうので、大丈夫なんでしょう。

間違ってたらすみません。
最後に編集したユーザー konisi [ 2007年2月28日(水) 17:37 ], 累計 1 回
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
アラタシ
記事: 6
登録日時: 2007年2月17日(土) 16:17

#10 投稿記事 by アラタシ »

konisiさん、ありがとうございます。

ということは、また疑問が出てきてしまったのですが、プログラムの終了時
(終了という概念もよくわかってませんが、要はWM_CLOSEをMainWnd
に送ったとき?)には、デバイスコンテキストやオブジェクトに占有されてい
たメモリは全て開放されるのでしょうか?だとすればさほど心配しなくていい
のかもしれませんが・・・。

それとも、プログラム終了を受け持つプロシージャに、全てのメモリを開放
する命令を記述していないと、プログラムを終了した後もメモリが占有され
続け、プログラムの起動、終了を繰り返すうちに、だんだんWindowsの
動作が遅くなるなどの障害が出るのでしょうか?

そう考えると、配列や変数も空にしてから終了しないといけないのか、とか
心配事が増えるばかりです・・・・。
konisi
記事: 893
登録日時: 2005年7月25日(月) 13:27
お住まい: 埼玉県東松山市
連絡する:

#11 投稿記事 by konisi »

変数やその配列は、alloc系の関数で確保したものでなければ、グローバル変数はdata領域、ローカル変数はstack領域にそれぞれ確保されているはずなので気にしなくてもいいと思います。

メモリの開放について心配なら、次のプログラムを、タスクマネージャ等でメモリ使用量を監視している状態で実行し、そのメモリ使用量の推移を見てください。

コード: 全て選択

malloc(1024*1024*1024)
Sleep(5000)
メモリを1GB確保し、5秒間停止し、その後メモリを開放せずに終了するプログラムです。

data領域というのは、実行ファイルが読み込まれた時には既に展開されている領域で、実行ファイルの終了とともに消滅します。
stack領域はすごくいろいろな用途があるんですが、関数から抜ける時にローカル変数の存在自体が消滅するので大丈夫です。(だから関数内のローカル変数が終了時にどんな値を取っていたかを追跡できないんですが。)

プログラム終了の概念は、今の時代の人間には説明が難しいです。誰か頼みます。

まぁ、Windowsはなぜか長時間起動しっぱなしだと動作が不安定になったりしますし、Java系のソフトが異常終了するとなぜかメモリの開放がうまくいかない事もあるんですが。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。

に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
マティ
記事: 161
登録日時: 2005年8月23日(火) 00:15
お住まい: 沖縄県
連絡する:

#12 投稿記事 by マティ »

プログラムの終了に関しては説明が難しいので、他の人に説明していただくとして
ペンやブラシについて、DeleteObjectする必要がある事を説明したいと思います。

Win9x系では、システムリソース領域が64KBしかなくメモリーを増設してもリソース不足でアプリケーションが複数起動出来ない事があります。
この現象は、画面の色数が増えると発生しやすくなります。

実は、グラフィック系オブジェクトの大部分はシステムリソースを食いつぶすので、使用したオブジェクトが不要になった時点で開放する事を推奨しています。
Win9x系でオブジェクトを開放しないプログラムを作成して描画を行い続けるとリソース不足でシステムが不安定になります。

昔のATI系のドライバーでは、高速化の為にブラシやペンのリソースに独自の拡張をしていたので、他のドライバーに比べて起動時点で10%以上リソースの残りが少なかったと記憶しています。

(システムリソースでは無くユーザリソースだったかも)
アラタシ
記事: 6
登録日時: 2007年2月17日(土) 16:17

#13 投稿記事 by アラタシ »

konisiさま
マティさま

お返事どうもありがとうございました。変数や配列に関しては安心しました。
グラフィック系のオブジェクトは使い終わったらメモリ開放を心がけて今後も
プログラミングに取り組みたいと思います。
いろいろとアドバイスいただき、ありがとうございました。
返信する