2011年09月

C#で、GraphicsのDrawImageメソッドがOutOfMemoryExceptionを出す

今、C#でプログラムを書いています。
そのプログラムでは、読み込んだ画像をマウスで拡大したり縮小したり移動したり出来ます。
が、画像の表示に使用しているGraphicsクラスのDrawImageメソッドが
OutOfMemoryExceptionを吐き出しました。
物理メモリは余ってるし、使っているBitmapGraphicsもちゃんとDisposeしてます。
しばらく悩んで解決したのでメモしておきます。

まず、具体的な処理を説明すると・・・

画像の表示はPictureBoxを使って行っています。
PictureBoxImageプロパティに読み込んだBitmapなりを入れると表示してくれるものです。

最初に、PictureBoxImageプロパティに、
PictureBoxと同じ大きさのBitmapを入れておきます。
絵は入ってなくて白いままです。
読み込んだオリジナル画像は別のBitmapとして保持しておきます。

マウスのドラッグで位置や拡大率を変更できるようにします。
で、位置や拡大率を変更したときに次の処理を必ず行います。

GraphicsクラスのFromImageメソッドに、
PictureBoxImageプロパティを指定して、Graphicsのインスタンスを取得します。

で、このGraphicsDrawImageメソッドに、オリジナル画像のBitmapと、
マウスのドラッグで変更した位置や、拡大・縮小後の画像のサイズを指定します。
拡大縮小後のサイズは、拡大率×オリジナル画像のサイズで計算しています。

すると、PictureBoxImageプロパティに、
オリジナル画像を指定した位置、サイズで移動・縮小・拡大したものが書き込まれます。

PictureBoxは、Imageプロパティをそのまま表示するので、
オリジナル画像を指定した位置、サイズで移動・縮小・拡大したものが表示されるわけです。

最後に使用したGraphicsDisposeして終わります。
これで世界は平和になったのだ・・もとい、画像の拡大縮小ができるようになりました。

・・・で、あればよかったのですが。

どうも拡大・縮小に利用したGraphicsDrawImageメソッドが、
OutOfMemoryExceptionを吐き出してしまうときがあるのです。
最初に言ったとおり物理メモリは余ってますので、メモリ不足って意味ではないようです。

ではなんだろうと思って調べました。
どうやらGraphicsDrawImageメソッドは、
書き込むBitmapが適切な形式じゃなかったらOutOfMemoryExceptionを出すとかなんとか。

でも、読み込んだオリジナル画像は特殊な形式でもないし、
ちゃんとBitmapとして読み込めてるしなぁ。

じゃあ、もう1つの引数である位置や画像のサイズはどうだろう?
ってことでやったらBingo!でした。

実は、位置や画像サイズの指定は、RectangleFという構造体を使っていました。
RectangleFは、矩形の位置やサイズをfloat型で保持するものです。

スクリーン座標は1280x1024とかの整数値なので、int型で指定すればいいのですが、
縮小・拡大の計算の関係でサイズはfloat型の結果になります。
キャストが面倒なのでそのまま入れられるRectangleFに入れたのですが、これがいけなかった。

OutOfMemoryExceptionを吐き出すときにRectangleFの中身を見たところ、
サイズが0.000012345といったすごく細かくて小さいになっていました。

え?これが原因?普通に考えれば0とかになるよね?
と思ったけど、ずばりこれが原因でした。

矩形の位置やサイズをint型で保持するRectangle構造体で指定するようにしたところ、
OutOfMemoryExceptionが出なくなりました。

ちゃんちゃん。

その部分なソース↓
        /// <summary>
        /// オリジナル画像を拡大・縮小したものをpictureBoxにセットする
        /// </summary>
        private void SetPasteImage()
        {
            // pictureBox.Imageプロパティ←pictureBoxのサイズのBitmapが入っている
            // originalImage←オリジナル画像のBitmap
           
            // オリジナル画像が読み込まれていなかった場合なにもしない
            if (originalImage == null)
            {
                return;
            }

            // 位置とサイズの計算
            Rectangle rect = new Rectangle(imageX, imageY, (int)(scaleX * originalImage.Width), (int)(scaleY * originalImage.Height));

            // pictureBoxのImageから、Graphicsを取得
            Graphics g = Graphics.FromImage(picPasteImage.Image);

            // pictureBoxのImageをクリア
            g.Clear(Color.White);

            // pictureBoxのImageにオリジナル画像を拡大縮小して書き込み
            g.DrawImage(originalImage, rect);

            // Graphicsを破棄
            g.Dispose();

            // pictureBoxの表示を更新(Imageに画像が書き込まれているので表示も変わる)
            picPasteImage.Invalidate();
        }

GeForce6600のポリゴン欠け・伸びについて

最近、今は昔の旧世代GPUのGeForce6600GTをお安く手に入れたわけですよ。
古くてもGPUということで喜んでベンチマークを動かしたんですが(もちろんその当時の)、
ポリゴンが伸びる・欠けるといった現象が発生して見れたもんじゃない。
手に入れた当初(1ヶ月前?)はそんなに酷くなかったとおもうんだけど、
ここ数日ベンチを動かしてみたら酷いのなんのって。

巨大な戦艦(母艦?)のはずなのに巨大なポリゴンが移動してるようにしか見えなかったり、そこから飛び出てくる多数の揚陸艇はポリゴンが伸びてとげだらけになってたり。お前らはウニかっ。
(宇宙の話です)

いろいろ設定してみたけど直らない。
熱暴走かと思って温度計測してみても、最大84度くらいしかいかない。十分高いけどそこまで高いか?と。
もしかして前に熱上がってぶっ壊れたか?と絶望感に苛まれたりしました。

が、治りましたよ!
ドライバを入れなおして

最新版からバージョンにして186.45低いドライバを。
最新版バージョンは280.26ですが、入れたのはバージョン93.81です。

GPULoadやVRAM使用率などが見れなくなるといった機能低下があったり、NVIDIA設定・コントロールパネルが英語になったりしてしまいましたが、ポリゴンの欠け・伸びは解消しましたヽ(´◉◞౪◟◉)ノ

公式では古くてもバージョン197.13までしか配布されてないので、
バージョン93.81は以下のページからダウンロードしました。
http://www.nodevice.jp/driver/GeForce_6600_GT/get37228.html

もしかしたら設定で治ったのかもしれませんが、治ったのでいいです(
もし古いGPUで表示がおかしかったりしたら、最新ドライバーではなくて、古いドライバーを入れてみるといいかもしれませんねっ。

追記:
以下のページにちょっと新しいバージョン94.24がありました。
http://www.nvidia.co.jp/object/winxp_2k_94.24_jp.html
こちらを入れても、ポリゴンの欠けや伸びは発生しませんでした。
コントロールパネルが日本語になっていますので、入れるならこっちが良いかもしれません。
記事検索
livedoor プロフィール
QRコード
QRコード

トップに戻る