3

私は地図を表示するプログラムを書いており、その上にカメラの位置とその視線方向が表示される別のレイヤーがあります。マップ自体は、ズームおよびパンできます。問題は、マップ ファイルのサイズが大きく、ズームがスムーズに行われないことです。

class ZoomablePictureBox : PictureBoxズームとパン機能を追加するために作成しました。ズームとパンのために、このフォーラムや他のフォーラムからさまざまな方法を試したところ、次のOnPaintイベントで発火しましたZoomablePictureBox

  private void DrawImgZoomed(PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            if (imgZoomed != null)
                e.Graphics.DrawImage(imgZoomed, new Rectangle(-ShiftX, -ShiftY, imgZoomed.Width, imgZoomed.Height), 0, 0, imgZoomed.Width, imgZoomed.Height, GraphicsUnit.Pixel);

    }

ShiftX と ShiftY は適切なマップ パンを提供します (この問題には計算は関係ありません)。

imgZoomedズームが変更されるたびに BackgroundWorker で計算された元のマップのズーム バージョンです。

private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {

        Bitmap workerImage = e.Argument as Bitmap;
        Bitmap result;

        result = new Bitmap(workerImage, new Size((int)(workerImage.Width * Zoom), (int)(workerImage.Height * Zoom)));

        e.Result = result;
    }

したがって、現在のアプローチは、ユーザーがマウスホイールをスクロールするたびに、imgZoomed現在のズームに基づいて新しいものが計算されるというものです。マップ サイズが 30 MB 以下の場合、最大で 0.5 秒かかるので面倒ですが、パンはスムーズに実行されます。

これが最善の考えではないかもしれないことは承知しています。以前のアプローチでは、マウスがスクロールされるたびにズームされた画像のコピーを作成しませんでしたが、代わりにこれを行いました:

e.Graphics.DrawImage(Image, new Rectangle(-ShiftX, -ShiftY, (int)(this._image.Width * Zoom), (int)(this._image.Height * Zoom)), 0, 0, Image.Width, Image.Height, GraphicsUnit.Pixel);

私が理解していることから、ズームは元の画像を引き伸ばしただけなので、はるかにスムーズでした。一方、パンは激しくスキップしていました。

私は考えていました:

  • ズームごとに元のマップのコピーをメモリ/ハード ドライブに作成する - メモリ/HDD スペースを大量に消費します。
  • 次/実際/前のズーム用に元のマップのコピーを作成するので、次のステップを計算する時間が増えます - ユーザーが一度に複数のステップをスクロールすると役に立ちません

マトリックス変換も試しましたが、実際のパフォーマンスの向上はなく、パンの計算は本当に骨の折れる作業でした。

私はここでぐるぐる回っていますが、その方法がわかりません。デフォルトの Windows 画像ビューアーでマップを開くと、ズームとパンがスムーズになります。彼らはどのようにそれをしますか?

ズームとパンを同時にスムーズに行うにはどうすればよいですか?

4

1 に答える 1

2

長くなってすみません、要点だけ残しました。

p/invoke のほとんどは、pinvoke.netから取得したものです。

大きな画像をスムーズにパンするための私の解決策は、 のStretchBlt代わりに gdi メソッドを使用することですGraphics.DrawImage。すべてのネイティブ ブリッティング操作をグループ化する静的クラスを作成しました。

もう 1 つ大いに役立つのは、 のHBitmapおよびGraphicsオブジェクトをキャッシュすることBitmapです。

public class ZoomPanWindow
{
    private Bitmap map;
    private Graphics bmpGfx;
    private IntPtr hBitmap;

    public Bitmap Map
    {
        get { return map; }
        set 
        {
            if (map != value)
            {
                map = value;
                //dispose/delete any previous caches
                if (bmpGfx != null) bmpGfx.Dispose();
                if (hBitmap != null) StretchBltHelper.DeleteObject(hBitmap);
                if (value == null) return;
                //cache the new HBitmap and Graphics.
                bmpGfx = Graphics.FromImage(map);
                hBitmap = map.GetHbitmap();
             }
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (map == null) return;
        //finally, the actual painting!
        Rectangle mapRect = //whatever zoom/pan logic you implemented.
        Rectangle thisRect = new Rectangle(0, 0, this.Width, this.Height);
        StretchBltHelper.DrawStretch(
            hBitmap,
            bmpGfx,
            e.Graphics,
            mapRect,
            thisRect);
    }
}

public static class StretchBltHelper
{
    public static void DrawStretch(IntPtr hBitmap, Graphics srcGfx, Graphics destGfx,
        Rectangle srcRect, Rectangle destRect)
    {
        IntPtr pTarget = destGfx.GetHdc();
        IntPtr pSource = CreateCompatibleDC(pTarget);
        IntPtr pOrig = SelectObject(pSource, hBitmap);
        if (!StretchBlt(pTarget, destRect.X, destRect.Y, destRect.Width, destRect.Height,
            pSource, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height,
            TernaryRasterOperations.SRCCOPY))
        throw new Win32Exception(Marshal.GetLastWin32Error());

        IntPtr pNew = SelectObject(pSource, pOrig);
        DeleteDC(pSource);
        destGfx.ReleaseHdc(pTarget);
    }

    [DllImport("gdi32.dll", EntryPoint = "SelectObject")]
    public static extern System.IntPtr SelectObject(
        [In()] System.IntPtr hdc,
        [In()] System.IntPtr h);

    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    static extern bool DeleteDC(IntPtr hdc);

    [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteObject(
        [In()] System.IntPtr ho);

    [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
    static extern IntPtr CreateCompatibleDC(IntPtr hdc);

    [DllImport("gdi32.dll")]
    static extern bool StretchBlt(IntPtr hdcDest, int nXOriginDest, int nYOriginDest,
        int nWidthDest, int nHeightDest,
        IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
        TernaryRasterOperations dwRop);

    public enum TernaryRasterOperations : uint
    {
        SRCCOPY = 0x00CC0020
        //there are many others but we don't need them for this purpose, omitted for brevity
    }
}
于 2012-11-29T11:06:09.443 に答える