0

私のプログラムでは、基本的な画像エディターをコーディングしています。これにより、ユーザーは長方形の領域を描くことができ、その領域が 3 倍程度にズームされた表示がポップアップ表示されます (マウス ホイールでさらに調整できます)。この画像を右クリックしてドラッグすると、元の画像上でズーム領域が移動し、基本的に虫眼鏡のように機能します。

問題は、比較的小さなビットマップでも重大なパフォーマンスの問題が発生することです。ズームされた領域を示すビットマップが約 400x400 の場合、マウスが動くのと同じくらい速く更新され、完全に滑らかですが、マウス ホイールでズームを約 450x450 まで上げると、すぐにチャンクが開始され、1 秒あたり約 2 回の更新になります。もしそうなら。なぜこのようなわずかな増加が大きなパフォーマンスの問題を引き起こすのか理解できません...内部メモリの制限か何かに達したようです。ズームされているソースビットマップのサイズは問題ではないようです。ズームされたビットマップのサイズだけです。

問題は、Graphics.DrawImage と PictureBox を使用していることです。このサイトを読んでみると、通常、これらの両方のパフォーマンスがあまり良くないことがわかりますが、スピードを改善するための GDI の内部動作について十分に知りません。私のボトルネックがどこにあるかを知っている人がいるといいのですが、私はおそらくこれらのツールを不適切な方法で使用しているか、代わりに使用するより良いツールを知らないからです。

これは、私のマウス イベントと関連する関数の一部です。

private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {

        else if (e.Button == System.Windows.Forms.MouseButtons.Right)
        {
            // slide the zoomed part to look at a different area of the original image
            if (zoomFactor > 1)
            {
                isMovingZoom = true;
                // try saving the graphics object?? are these settings helping at all??
                zoomingGraphics = Graphics.FromImage(displayImage);
                zoomingGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
                zoomingGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
                zoomingGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
                zoomingGraphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
            }
        }
    }


private void pictureBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (isMovingZoom)
        {
            // some computation on where they moved mouse ommitted here

            zoomRegion.X = originalZoomRegion.X + delta.X;
            zoomRegion.Y = originalZoomRegion.Y + delta.Y;
            zoomRegionEnlarged = scaleToOriginal(zoomRegion);

            // overwrite the existing displayImage to prevent more Bitmaps being allocated
            createZoomedImage(image.Bitmap, zoomRegionEnlarged, zoomFactor, displayImage, zoomingGraphics);
        }
    }

private void createZoomedImage(Bitmap source, Rectangle srcRegion, float zoom, Bitmap output, Graphics outputGraphics)
    {
        Rectangle destRect = new Rectangle(0, 0, (int)(srcRegion.Width * zoom), (int)(srcRegion.Height * zoom));

            outputGraphics.DrawImage(source, destRect, srcRegion, GraphicsUnit.Pixel);

        if (displayImage != originalDisplayImage && displayImage != output)
            displayImage.Dispose();
        setImageInBox(output);
    }

// sets the picture box image, as well as resizes the window to fit
    void setImageInBox(Bitmap bmp)
    {
        pictureBox.Image = bmp;
        displayImage = bmp;
        this.Width = pictureBox.Width + okButton.Width + SystemInformation.FrameBorderSize.Width * 2 + 25;
        this.Height = Math.Max(450, pictureBox.Height) + SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height * 2 + 20;
    }

private void pictureBox_MouseUp(object sender, MouseEventArgs e)
    {
        else if (e.Button == System.Windows.Forms.MouseButtons.Right)
        {
            if (isMovingZoom)
            {
                isMovingZoom = false;
                zoomingGraphics.Dispose();
            }
        }
    }

ご覧のとおり、何かを描画するたびに新しい Bitmap を宣言するのではなく、古い Bitmap (および Bitmap のグラフィックス オブジェクト) を再利用していますが、Graphics. FromImage を繰り返し)。コードをベンチマークするためにストップウォッチを追加しようとしましたが、DrawImage は機能を別のスレッドに渡すため、関数は比較的迅速に実行されると主張しています。DisposeBitmap オブジェクトと Graphics オブジェクトを使用していないときはすべて使用しようとしており、MouseMoveイベント中にリソースの割り当て/割り当て解除を繰り返し呼び出さないようにしています。を使用してPictureBoxいますが、ここでは問題ないと思います。

このコードをスピードアップしたり、DrawImage で何が起こっているかを教えていただければ幸いです。見栄えを良くするために余分なコードを削除しましたが、誤って重要なものを削除したり、問題を引き起こしている可能性のあるものの使用方法を示していない場合は、お知らせください。役職。

4

1 に答える 1

2

このような問題を処理する方法は、Paint イベントを受け取るときに、イメージ全体をメモリ ビットマップに描画し、それをウィンドウに BLT することです。そうすれば、すべての視覚的なフラッシュが排除され、実際にはそうでなくても速く見えます。

より明確にするために、マウス イベント ハンドラー内からの描画は行いません。メインの Paint ハンドラーに必要なものをセットアップしてから、Invalidate を実行します。そのため、マウス イベントが完了した後に描画が行われます。


追加: コメントでトムの質問に答えるには、次のようにします。覚えておいてください、私はそれが速いと主張しているわけではありません。単に速く見える_e.Graphics.DrawImage(bmToDrawOn, new Point(0,0));というだけです。ある画像から次の画像へと移動するだけです。ユーザーは、ウィンドウがクリアされてから再描画されているのを見ません。ダブルバッファリングと同じ効果があります。

    Graphics grToDrawOn = null;
    Bitmap bmToDrawOn = null;

    private void DgmWin_Paint(object sender, PaintEventArgs _e){
        int w = ClientRectangle.Width;
        int h = ClientRectangle.Height;
        Graphics gr = _e.Graphics;

        // if the bitmap needs to be made, do so
        if (bmToDrawOn == null) bmToDrawOn = new Bitmap(w, h, gr);
        // if the bitmap needs to be changed in size, do so
        if (bmToDrawOn.Width != w || bmToDrawOn.Height != h){
            bmToDrawOn = new Bitmap(w, h, gr);
        }
        // hook the bitmap into the graphics object
        grToDrawOn = Graphics.FromImage(bmToDrawOn);
        // clear the graphics object before drawing
        grToDrawOn.Clear(Color.White);
        // paint everything
        DoPainting();
        // copy the bitmap onto the real screen
        _e.Graphics.DrawImage(bmToDrawOn, new Point(0,0));
    }

    private void DoPainting(){
        grToDrawOn.blahblah....
    }
于 2013-01-09T23:00:42.187 に答える