# [WIN32]-PNG einbinden und verwenden



## Mr Apfelkuchen (10. Juni 2008)

Also hi erstmal,
ich bin beim schreiben eines Programms im Eifer auf ein Problem gestoßen. Unzwar hab ich zuviele Bitmaps benutzt so das das Programm sehr groß geworden ist und ausserdem lassen sich Bitmaps nur schwer an gewissen Stellen durchsichtig machen.

Daher meine Frage: Kann man PNG-Bilder einbinden und wenn ja wie? Irgendwie wurde die Frage beim Surfen im Inet nicht so richtig klar beantwortet...

Freue mich über Antworten oder auch Anregungen


----------



## devDevil (10. Juni 2008)

Gibt da ne Lib: http://members.gamedev.net/lode/projects/LodePNG/ wäre evtl. genau das richtige. Sonst einfach GDI+.


----------



## Mr Apfelkuchen (10. Juni 2008)

Danke für den Link  aber wenn das so kompliziert is ein PNG-Bild einzubinden dann glaub ich ist das nichts für mich... dachte das geht so ähnlich wie bei Bitmaps, die man als Visual Studio Ressourcen hinzufügt und mit 

```
hBmp = LoadBitmap (hInstance, TEXT ("BITMAPNAME"));
GetObject (hBmp, sizeof (BITMAP), &Bmp);

SelectObject (hdcMem, hBmp);
BitBlt (hdc,  x,  y,  größe x,  größe y,  hdcMem,  0,  0,  SRCPAINT) ;
```
ausgibt... weil der Code ist erstmal sehr kompliziert (für mich^^) und sehr lang daher nochmal die frage... gibt es einen weg der einfacher ist?

mfg Apfelkuchen


----------



## devDevil (10. Juni 2008)

Na nimm doch einfach von der Seite http://members.gamedev.net/lode/projects/LodePNG/picopng.cpp so, da übergibst du als Parameter einfach folgendes:

```
std::ifstream file_stream("C:\\sample.png", std::ios_base::binary);
if (!file_stream) { std::cerr << "FEHLER: Datei nicht gefunden!"; return 1; }

std::vector<unsigned char> image(file_stream.seekg(0, std::ios_base::end).tellg());
file_stream.seekg(0, std::ios_base::beg).read(&image[0], image.size());

std::pair<unsigned long, unsigned long> size;
decodePNG(image, size.first, size.second, &image[0], image.size());
```
 dann solltest du die Daten in image einfach wie normale BitmapPixel benutzen können


----------



## Mr Apfelkuchen (11. Juni 2008)

Hmm dann is doch aber das Problem da das der keine Transparenz mehr darstellen kann oder?


----------



## MCoder (11. Juni 2008)

Du könntest auch mit GDI+ arbeitest, das kann PNGs.

Gruß
MCoder


----------



## Mr Apfelkuchen (11. Juni 2008)

hmm was is denn gdi+? oder besser wie geht das oder wo kann ich mir ngucken wie das geht?


----------



## MCoder (11. Juni 2008)

GDI+ ist Bestandteil der Windows-API. Infos sollten also in der MSDN oder im Netz nicht allzu schwer zu finden sein.
Hier mal ein paar Code-Snippets:

```
// Header und Library
#include <gdiplus.h>
#pragma comment (lib, "Gdiplus.lib")

// Starten (einmal am Anfang der App)
ULONG_PTR token;
Gdiplus::GdiplusStartupInput startupInput;
Gdiplus::GdiplusStartup(&token, &startupInput, NULL);

// Stoppen (am Ende der App); 
Gdiplus::GdiplusShutdown(token);

// Bild laden und anzeigen
LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch( message ) 
    { 
        case WM_PAINT:
            { 
                PAINTSTRUCT ps; 
                HDC hdc = BeginPaint(hwnd, &ps); 
            
                Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromFile(L"C:\\Bild.png");

                if( pBitmap )
                {
                    Gdiplus::Graphics g(hdc);
                    g.DrawImage(pBitmap, Gdiplus::PointF(0.0f, 0.0f));
                }

                delete oBitmap;
                EndPaint(hwnd, &ps); 
            }
            return 0L; 

        // ...
    } 
}
```
Gruß
MCoder


----------



## Mr Apfelkuchen (11. Juni 2008)

Hmm also  ich probier das mal wenn das geht wär natürlich cool ;-) muss ich die header oder die lib noch iwo runterladen oder is die bei msvs 2005 express dabei?
Kann ich das erstellen und stoppen mit WM_CREAT bzw. WM_DESTROY erledigen?


----------



## Mr Apfelkuchen (11. Juni 2008)

Also wenn ich diesen Quelltext kompilieren will kommen 26 Fehler heraus (siehe unten):

```
#include <windows.h>
#include <gdiplus.h>
#pragma comment (lib, "Gdiplus.lib")

LRESULT CALLBACK WndProc (HWND,UINT,WPARAM,LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPrewInstance,
					PSTR szCmdLine,int iCmdShow)
{
	static TCHAR szAppName[] = TEXT ("HelloWin") ;
	HWND		 hwnd ;
	MSG			 msg ;
	WNDCLASS	 wndclass ;

	wndclass.style			= CS_HREDRAW | CS_VREDRAW ;
	wndclass.lpfnWndProc	= WndProc ;
	wndclass.cbClsExtra		= 0 ;
	wndclass.cbWndExtra		= 0 ;
	wndclass.hInstance		= hInstance ;
	wndclass.hIcon			= LoadIcon (NULL,IDI_APPLICATION) ;
	wndclass.hCursor		= LoadCursor (NULL,IDC_ARROW) ;
	wndclass.hbrBackground	= (HBRUSH) GetStockObject (WHITE_BRUSH) ;
	wndclass.lpszMenuName	= NULL ;
	wndclass.lpszClassName	= szAppName ;

	if (!RegisterClass (&wndclass))
	{	//UNICODE-Compilierung ist die einzige realistische Fehlermöglichkeit
		MessageBox (NULL, TEXT ("Programm arbeitet mit Unicode!"),
					szAppName,MB_ICONERROR) ;
		return 0;
	}

	hwnd = CreateWindow (szAppName,				// Name der Fensterklasse
		TEXT ("Counter-Strike Befehle V 1.0"),	// Fenstertitel
		WS_OVERLAPPEDWINDOW,					// Fensterstil
		CW_USEDEFAULT,							// X-Position des Fensters
		CW_USEDEFAULT,							// Y-Position des Fensters
		CW_USEDEFAULT,							// Fensterbreite
		CW_USEDEFAULT,							// Fensterhöhe
		NULL,									// übergeordnetes Fenster
		NULL,									// Menü
		hInstance,								// Programm-Kopiezähler (Programm-ID)
		NULL) ;									// zusätzliche Parameter

	ShowWindow (hwnd,iCmdShow) ;
	UpdateWindow (hwnd) ;

	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg) ;
		DispatchMessage (&msg) ;
	}
	return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC			hdc ;
	PAINTSTRUCT	ps ;

	switch (message)
	{
	case WM_CREATE:
		ULONG_PTR token;
		Gdiplus::GdiplusStartupInput startupInput;
		Gdiplus::GdiplusStartup(&token, &startupInput, NULL);
		return 0 ;

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hwnd, &ps);
			
			Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromFile(L"C:\\00NewGame.png");
			
			if( pBitmap ) {
				Gdiplus::Graphics g(hdc);
				g.DrawImage(pBitmap, Gdiplus::PointF(0.0f, 0.0f));
			}
			delete oBitmap;
			EndPaint(hwnd, &ps);
		}
		return 0L;

	case WM_DESTROY:
		Gdiplus::GdiplusShutdown(token);
		PostQuitMessage (0) ;
		return 0;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}
```


----------



## MCoder (11. Juni 2008)

Mache deine Sourcen mal als ".cpp" (C++), statt als  ".c", sonst passt das nicht mit dem Gdiplus-Zeugs zusammen.
Die Variable "ULONG_PTR token;" musst du global deklarieren.

Gruß
MCoder


----------



## Mr Apfelkuchen (12. Juni 2008)

Also ich weiss nich was ich sagen soll.....

Fettes Dankeschön an euch Beide   
Weiss nich wo ich wär ohne euch ;-) wahrscheinlich immernoch am suchen 

Also Alles Gute euch noch

mfg Mr Apfelkuchen


----------



## Mr Apfelkuchen (13. Juni 2008)

Da is jetzt noch ein Problem... ich hab mein origramm bisher in c geschrieben und werde jetzt mit Fehlermeldungen zugeschmissen... gdi+ gibt es nicht in c oder?


----------



## MCoder (13. Juni 2008)

Wie man am besten mit gemischten Sourcen umgeht, kann ich dir auch nicht so genau sagen. Versuche erst mal, allen Sourcefiles die Endung ".cpp" zu geben. Bei hartnäckigen Fällen kannst du den betreffenden Sourcecode auch kapseln:

```
extern "C"
{
    /* C-Code */
}
```
Da die SDK-Schnittstelle für GDI+ objektorientiert ist, bekommst du das nicht in ein reines C-Projekt herein.

Gruß
MCoder


----------



## Mr Apfelkuchen (13. Juni 2008)

Ist jetzt auch egal ich hab die 345 Fehlermeldungen abgearbeitet... Also das Programm funktioniert und holt sich auch die PNGs. Das dumme ist jetzt das es zum Neuzeichnen des Anwendungsbereichs einige Sekunden braucht und die Auslaustung im Taskmanager auf 99% steht... Belegen die PNGS soviel speicher oder hab ich was falsch gemacht?


----------



## MCoder (14. Juni 2008)

Also einige Sekunden sollte das nun wirklich nicht dauern und auch nicht allzuviel Resourcen verbrauchen. 
In meinem Beispielcode habe ich der Einfachheit habe das Laden der PNG-Datei innerhalb der WM_PAINT Message ausgeführt. Das solltest du ändern, indem du (wenn möglich) die benötigten Bilder beim Programmstart in die Bitmap-Objekte lädst und bei WM_PAINT nur das Draw() ausführst.

Gruß
MCoder


----------



## Mr Apfelkuchen (15. Juni 2008)

Das habe ich bereits getan. Ich lade die Bilder Beim Programmstart und lösche sie mit WM_DESTROY. In WM_PAINT lasse ich nur diesen Code ausführen:

```
if( Bmp ) {
	Gdiplus::Graphics g(hdc);
	g.DrawImage(Bmp, Gdiplus::PointF(0, 0));
}
```
Was auch auffällig ist: Manche Bilder erscheinen kleiner und manche größer als ihre eigendliche Größe!


----------



## MCoder (15. Juni 2008)

Mr Apfelkuchen hat gesagt.:


> Was auch auffällig ist: Manche Bilder erscheinen kleiner und manche größer als ihre eigendliche Größe!


Mit dem angegebenen Code sollten sie eigentlich in Originalgröße angezeigt und ggf. abgeschnitten werden. Für DrawImage gibt es verschiedene Überladungen, wo man neben der Ausgabeposition auch die Zeichengröße mit angeben kann. Evt. probierst du mal damit herum.
Für das Zeit- und Resourcenproblem habe ich keine Erklärung. Vielleicht zeigst du nochmal den aktuellen Code.

Gruß
MCoder


----------



## Mr Apfelkuchen (15. Juni 2008)

Der aktuelle Code ist 5000 Zeilen lang... 
Kann das auch an den Bildern liegen? Ich hab die mit Transperenz gemacht aber das kann doch nicht der Fehler sein oder?

Antwort: Doch Ich hatte die Bilder mit Photoshop gemacht... wenn ich die danach nochmal smit Pain öffne und die da speicher dann geht das... aber warum haben die mit Photoshop erzeugten so ein Bug?


----------



## MCoder (16. Juni 2008)

Photoshop kenne ich nicht, aber bei Corel Photopaint ist der PNG-Support auch eine Zumutung. Ich nehme dann für sowas immer GIMP (kann übrigens auch psd-Dateien öffnen).

Gruß
MCoder


----------



## Mr Apfelkuchen (16. Juni 2008)

Naja das funktioniert jetzt, also werden in der richtigen Größe dargestellt... Das Problem bleibt aber die lange Ladezeit....
Ich hab es halt so gamcht das das Programm sich die Bilder alle am Anfang unter ("data\\image\\name.png") holt und in die Variabeln einspeichert.
In WM_PAINT soll es dann die Bilder darstellen per:

```
if( Variabelnname ) {
	Gdiplus::Graphics g(hdc);
	g.DrawImage(Variabelnname, Gdiplus::PointF(0, 0));
}
```
Und am Schluss sollen die Variabeln wieder freigegeben werden mit:

```
delete Variabelnname;
```
Aber irgendwie ist das wohl zuviel denn das Programm belegt laut Taskmanager 822.645 K Speicher und das obwohl alle Bilder zusammen gerademal 1.21 MB groß sind... Also was ist da falsch?


----------



## MCoder (16. Juni 2008)

Um wieviel Bilder mit welcher Auflösung geht es denn? Die reine Dateigröße hat nicht viel zu sagen, da PNGs komprimiert sind, im Speicher aber die unkomprimierten Pixeldaten abgelegt werden.

Zeigst du denn alle Bilder gleichzeitig an? Ansonsten könntest du vielleicht doch nur diejenigen laden, die unmittellbar verwendet werden.

Gruß
MCoder


----------



## Mr Apfelkuchen (16. Juni 2008)

Das Problem dabei ist das mir der Kompiler dann sagt:
Inizialisierung durch 'case' übersprungen...


----------



## MCoder (16. Juni 2008)

Wenn du innerhalb einer case-Anweisung Variablen deklarierst, musst du den ganzen Block in geschweifte Klammern einschließen:

```
case 1:
{
    int x = 0; 
    // ...
}
break;
```
Gruß
MCoder


----------



## Mr Apfelkuchen (18. Juni 2008)

Das mit den geschweiften Klammern geht auch nicht weil dann die Meldung kommt, das die Variable nicht deklariert ist... was mir aber noch aufgefallen ist: Das Programm läuft eigendlich ganz gut bis auf kleine Ladungsstörungen "überlädt" sich aber nach 15 sekunden im eigendlich programm ( vorher nur einstellungen vornehmen). Kann das daran liegen das ich per WM_TIMER alle 15 Sekunden einen Bereich aktualisiere der das PNG schneidet und nur Teilweise erneuern müsste?


----------



## MCoder (18. Juni 2008)

Mr Apfelkuchen hat gesagt.:


> Das mit den geschweiften Klammern geht auch nicht weil dann die Meldung kommt, das die Variable nicht deklariert ist...


Dann musst du sie außerhalb des switch/case-Blocks oder möglicherweise global deklarieren.

Zu deinem anderen Problem: Die 15 Sekunden sind ein langes Intervall, da sollte es eigentlich eine Konflikte geben. Wenn du generell das ganze Fenster aktualisierst, sollte es auch keine Darstellungsprobleme geben. Was genau meinst du eigentlich mit "das Programm überlädt sich"?

Gruß
MCoder


----------



## Mr Apfelkuchen (19. Juni 2008)

Îch mein damit das es für die aktualiesierung des gesammten oder auch nur eines Teilbereichs um die 3 Sekunden braucht und PNGs nichtmehr darstellt.
Mir ist aber aufgefallen das das nich an dem 15 Sekunden Interval liegt sondern viel mehr an einer generellen Programmlaufzeit von 15-20 Sekunden. Ich versteh das einfach nicht... Warum nach dieser Zeit? Warum funktioniert das vorher (egal wie oft ich den Berreich erneuern lasse und dann nicht mehr?


----------



## Mr Apfelkuchen (20. Juni 2008)

Warum muss mein verdammter PC wieder sowas machen? Ist doch nciht normal oder?


----------



## MCoder (22. Juni 2008)

Ich denke, jetzt wird's schwierig, hier noch was dazu zu sagen. Könntest du relevante Codeteile (wo auch das Problem auftritt) aus deinem Projekt herauslösen und zeigen?

Gruß
MCoder


----------



## Mr Apfelkuchen (24. Juni 2008)

Hmm ich denke das ist mehr der Code zum Ausgeben der Bilder.
Also ich hab da folgendes festgestellt. 
Wenn das Programm läuft beginnt die Speicherauslastung um die 5.000K. Dann mit jedem Mausklick oder Tastatureingabe die das Programm verarbeiten steigt diese um 20.000 - 50.000K! Sobald ein Wetr um die 600.000K überschritten ist werden die PNGs nichtmehr angezeigt. Bei einer minimierung des Fensters geht die Speicherauslastung auf 4.000K zurück aber die Bilder werden trotzdem nichtmehr gezeigt, daher glaube ich das diese aus dem Speicher genommen wurden.
Das gleiche passiert bei nur einem Bild (nur im geringeren Maßstab).
Also irgendwie muss der Code den ich benutze einen Fehler aufweisen was die Speicherung der Bilder angeht...

Ich habe die Bilder vor dem switch in WndProc geladen... Kann das der Fehler sein? Das die Bilder immerwieder, bei jeder Nachricht neu geladen werden?


----------



## MCoder (24. Juni 2008)

Hallo, 

sofern es immer die gleichen Bilder sind, reicht das einmalige Laden beim Programmstart (spart Rechenzeit), ansonsten nur im WM_PAINT - case.
Vor dem Laden sollten evt. schon vorhandende Bitmaps unbedingt mit "delete" gelöscht werden. Andernfalls explodiert der Speicher, wie du ja schon festgestellt hast 

Gruß
MCoder


----------



## Mr Apfelkuchen (24. Juni 2008)

Da liegt das Problem... ich weiss nich wie ich es hinbekomm die in den Casezweigen zu deklarieren weil ich dann immer die Meldung bekomme das die Deklarierung durch case übersprungen wurde bzw. wenn ich sie in { / } packe sie garnicht als definiert gelten..


----------



## Mr Apfelkuchen (24. Juni 2008)

Also es liegt daran das der alle Bilder bei jeder Nachricht die an das Programm geht ( selbst Mausbewegungen) neu eingespeichert werden. Daher meine Frage wenn ich aus diesem Code

```
Gdiplus::Bitmap *BmpBild = Gdiplus::Bitmap::FromFile(L"Bild.png");
```
einen Code machen will der am Anfang die Variable deklariert und bei WM_CREATE das Bild einliest, wie sehe das aus?


----------



## MCoder (24. Juni 2008)

So etwa:

```
// globale Deklaration und Initialisierung mit NULL am Anfang der .cpp
Gdiplus::Bitmap *BmpBild = NULL;

...

// Zweisung bei WM_CREATE
BmpBild = Gdiplus::Bitmap::FromFile(L"Bild.png"); 

...

// Verwendung bei WM_PAINT
if( BmpBild )
{
    g.DrawImage( BmpBild, ...
}

...

// Aufräumen bei WM_DESTROY
if( BmpBild ) { delete BmpBild; }
```
Gruß
MCoder


----------



## Mr Apfelkuchen (24. Juni 2008)

ist das if(BmpBild) nötig?


----------



## MCoder (24. Juni 2008)

Mr Apfelkuchen hat gesagt.:


> ist das if(BmpBild) nötig?


Sicher ist sicher 
Falls beim Laden irgendwas schiefgeht, ersparst du dir damit einen Programmcrash und es wird halt nur das Bild nicht angezeigt. Damit wird das Programm robuster.

Gruß
MCoder


----------

