PDA

View Full Version : [C#] Proverbiale lentezza di GDI+


stdecden
04-09-2008, 10:27
Salve a tutti,

sto scrivendo un Tilemap-editor e questo é il codice che uso per renderizzare la mappa:


void pnlCenter_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Graphics g = e.Graphics;

if (map != null)
{
for (int y = 0; y < map.TilesY; y++)
{
for (int x = 0; x < map.TilesX; x++)
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(map.Image, new Rectangle(x * map.Tileset.TileWidth, y * map.Tileset.TileHeight, map.Tileset.TileWidth, map.Tileset.TileHeight), new Rectangle(x * map.Tileset.TileWidth, y * map.Tileset.TileHeight, map.Tileset.TileWidth, map.Tileset.TileHeight), GraphicsUnit.Pixel);
}
}
}
}


L' immagine utilizzata é di dimensioni abbastanza grandi (4096 x 4096px), ed i tiles sono di 16x16px.
La visualizzazione é estremamente lenta. Circa 10sec per una finestra 200x200

Qualcuno mi puó suggerire un modo migliore...

cdimauro
04-09-2008, 20:41
Questo (http://www.getpaint.net/) è fatto in C# e non presenta i problemi che hai tu: dagli un'occhiata. ;)

Tommo
04-09-2008, 21:47
Hmm.. come mai renderizzi ogni volta TUTTI i tile? lo credo bene che è lento, sono più di 16 milioni :asd:

Dovresti fare un check per trovare solamente l'area visibile, in modo da disegnare solo i tiles visibili... sarebbe già un bel passo avanti. :D

Un'altra cosa che dovrebbe migliorare le prestazioni è usare una specie di "stencil buffer" per usare solo "num tipi" istruzioni di disegno:
nel ciclo, invece di disegnare, salvi in una matrice grande come la visuale dove un certo tipo è visibile, usando semplici float (che puoi anche usare per blendare più tipi :cool: ).

Quindi, copri la visuale delle tiles di un certo tipo con un solo full screen rectangle (leggasi una sola chiamata di disegno), e rendi trasparenti le tiles di troppo usando la matrice.

Cmq mai usato il C#, ma dovrebbe essere più o meno tutto fattibile :D

stdecden
04-09-2008, 22:06
Innanzitutto grazie per le risposte!!!

Ora invece di una immagine molto grande ho provvisto ogni elemento del Tileset di una sua piccola immagine e ho quindi riscritto la funzione per disegnare:

for (int y = 0; y < map.TilesY; y++)
{
for (int x = 0; x < map.TilesX; x++)
{
RectangleF rc = new RectangleF(x * map.TileWidth - scrollX, y * map.TileHeight - scrollY, map.TileWidth, map.TileHeight);
if (rc.IntersectsWith(g.ClipBounds))
{
g.DrawImage(map[x, y].Image, x * map.TileWidth - scrollX, y * map.TileHeight - scrollY);
}
}
}


Grazie a questi 2 accorgimenti il rendering é molto piú veloce, quasi fluido. Comunqze quello che influiva di piú secondo me non era disegnare tutti i tile dato che gdi+ é provvisto di un clipper, ma piuttosto accedere sezioni di immagini di grandi dimensioni...

P.S. Cosa strana che ho notato é che questo codice:

for (int tx = 0; tx < tileWidth; tx++)
{
for (int ty = 0; ty < tileHeight; ty++)
{
((Bitmap)ti.Image).SetPixel(tx, ty, bmp.GetPixel(x * tileWidth + tx, y * tileHeight + ty));
}
}


é piú veloce dell'apposita funzione clone dell'oggetto bitmap!!!