簡體   English   中英

在UserControl中渲染大畫布

[英]Render Large Canvases in UserControl

我幾天來一直很難實現這一點。 關於我想做的事情,我已經廣泛搜索了類似的問題,但是我沒有遇到直接幫助我解決問題的問題。

基本上,我是在UserControl類的網格上渲染圖塊。 這是針對我正在開發的基於Tile Engine的世界編輯器。 這是一個開放世界文檔的屏幕截圖,上面還刷了一些圖塊。

在此處輸入圖片說明

最初,我將在控件中使用一個Bitmap ,它將成為世界上的預覽畫布。 例如,使用畫筆工具,當您移動鼠標並向下按鼠標左鍵時,它將光標下方最近的圖塊設置為畫筆的圖塊,並將其繪制在layer位圖上。 控件的OnPaint方法將覆蓋到相對於繪畫事件的剪貼矩形繪制layer位圖的位置。

這種方法的問題在於,當處理大世界時,位圖將非常大。 我需要此應用程序具有通用的大小,並且很明顯,每次在控件上失效時將大位圖呈現到控件上時,都會出現性能問題。

目前,我正在控件的重寫的OnPaint事件中直接將切片繪制到控件上。 這很棒,因為它不需要很多內存。 例如,一個(1000, 1000) 1000,1000 (1000, 1000)世界在每個圖塊( (20, 20) (畫布總大小為(20000, 20000) 20000,20000 (20000, 20000) )下,整個應用程序的內存約為18mb。 雖然不占用大量內存,但是卻占用大量處理器,因為每次控件無效時,它都會迭代視口中的每個圖塊。 這會產生非常煩人的閃爍。

我想完成的是一種在內存使用和性能方面相會的方法。 本質上是雙緩沖世界,以便在重繪控件時不會閃爍(窗體調整大小,焦點和模糊,滾動等)。 以Photoshop為例-溢出容器視口時如何渲染打開的文檔?

作為參考,這是我的控件的OnPaint重寫,它使用上述直接繪制方法。

getRenderBounds返回一個相對於PaintEventArgs.ClipRectangle的矩形,該矩形用於呈現可見的圖塊,而不是遍歷世界上的所有圖塊並檢查其是否可見。

protected override void OnPaint(PaintEventArgs e)
{
    WorldSettings settings = worldSettings();

    Rectangle bounds = getRenderBounds(e.ClipRectangle),
        drawLocation = new Rectangle(Point.Empty, settings.TileSize);

    e.Graphics.InterpolationMode = 
        System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
    e.Graphics.SmoothingMode = 
        System.Drawing.Drawing2D.SmoothingMode.None;
    e.Graphics.PixelOffsetMode = 
        System.Drawing.Drawing2D.PixelOffsetMode.None;
    e.Graphics.CompositingQuality = 
        System.Drawing.Drawing2D.CompositingQuality.HighSpeed;

    for (int x = bounds.X; x < bounds.Width; x++)
    {
        for (int y = bounds.Y; y < bounds.Height; y++)
        {
            if (!inWorld(x, y))
                continue;

            Tile tile = getTile(x, y);

            if (tile == null)
                continue;

            drawLocation.X = x * settings.TileSize.Width;
            drawLocation.Y = y * settings.TileSize.Height;

            e.Graphics.DrawImage(img, 
                drawLocation, 
                tileRectangle, 
                GraphicsUnit.Pixel);
        }
    }
}

如果您需要我的代碼中的更多上下文,請發表評論。

訣竅是根本不使用大位圖。 您只需要一個覆蓋可見區域的位圖。 然后,您繪制可見的任何東西。

為此,您將需要與位圖分開維護數據。 它可以是一個簡單的數組,也可以是一個具有簡單類的數組/列表,其中包含用於每個塊的信息,例如世界位置。

當塊位於可見區域內時,您將其繪制。 您可能需要迭代整個數組,也可以不必遍歷整個數組,但這並不是真正的問題(您也可以在單獨的線程上計算可見數組)。 您還可以通過創建區域索引來使函數更智能,從而避免迭代所有塊。

要將新塊添加到數組,請計算其在世界坐標上的畫布位置,將其添加,然后再次渲染該數組(或繪制該塊的區域)。

這也是系統如何繪制具有可滾動區域的控件的方式。

啟用雙緩沖將使其清晰無閃爍。

在這種情況下,我還將使用帶有單獨滾動條的面板並計算滾動條的相對位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM