GDI+ Bitmap im Header?



  • Thilo87 schrieb:

    Ja, das habe ich jetzt auch schon rausgekriegt, aber wenn ich Bitmap *bmpGraph; und dann im Constructor bmpGraph = new Bitmap(w,h) schreibe, dann kann ich irgendwie später nicht mehr dort reinzeichnen.

    Und? Ist es eine Frage?



  • Ja, die Frage lautet: Warum?



  • Das klappt einfach nicht...



  • Also ich habe jetzt im Headerfile

    Bitmap * bmpGraph;
    

    dann im Constructor

    this->bmpGraph = new Bitmap(500,500,PixelFormat32bppRGB );
    

    und später, wenn ich zeichnen möchte

    Graphics gGraphGraphics(bmpGraph);
    
    	gGraphGraphics.DrawLine(new Pen(Color(0,0,0)),0, 0, 100, 200);
    
    	gGraphics.DrawImage(bmpGraph,0,0,500,500);
    

    . Aber er zeichnet einfach nichts.



  • // Graphics-Objekt mit bmpGraph als Zeichenfläche erzeugen
     Graphics gGraphGraphics(bmpGraph);
    
    // Linie in Bitmap bmpGraph zeichnen   
        gGraphGraphics.DrawLine(new Pen(Color(0,0,0)),0, 0, 100, 200);
    
    // bmpGraph in bmpGraph zeichnen <-- Warum?
    gGraphics.DrawImage(bmpGraph,0,0,500,500);
    

    ...ich denke deine letzte Zeile ist nicht so ganz richtig 😉
    Du wirst deine Bitmap wo du die Linie rein gezeichnet hast normalerweise als *.bmp speichern wollen oder irgendwo in ein Fenster zeichnen wollen.



  • Ne, daran liegts leider nicht. Das ist echt ulkig. Wenn ich im Constructor

    bmpGraph = new Bitmap(500,500);
    if (bmpGraph == NULL) MessageBox(0,"null","null",MB_OK);
    

    schreibe, dann kommt immer die MessageBox. Also die Bitmap ist immer NULL. Wenn ich das aber zu einem späteren Zeitpunkt schreibe, dann ist die Bitmap nicht mehr NULL und alles funktioniert so wie es sollte. Warum darf ich im Constructor meine Bitmap nicht erstellen? Ich würde das wirklich gerne wissen.

    Hier mal der ganze Code, für den, der mir helfen will (ich bin C++ Anfänger, also entschuldigung wegen der Übersicht und so 😉 Fehler oder Optimierungen könnt ihr mir aber gerne aufzeigen).

    //wavepointer.h
    #include <Windows.h>
    #include <GdiPlus.h>
    using namespace Gdiplus;
    
    class WAVEPOINTER{
    private:
    	WAVEPOINTER *wpWavepointerPointer;
    	HWND *hWnd;
    	Bitmap *bmpGraph;
    
    	INT32 iPosX;					// x-Position der gesamten Abbildung (Mittelpunkt der Kreise)
    	INT32 iPosY;					// y-Position der gesamten Abbildung (Mittelpunkt der Kreise)
    	double dTime;
    
    	double dY[1000];
    	INT32 iXCounter;
    
    	double dPeriod[100];				// Umdrehungen pro Sekunde
    
    	INT32 iMidPointRadius;			// Radius des Mittelpunktes
    	Color cMidPointColor;	// Farbe des Mittelpunktes
    
    	INT32 iCurves;					// Anzahl der Zeiger/Kreise
    	INT32 iRadius[100];				// Radius-Array der Zeiger
    	Color cLineColor[100];	// Farben-Array der Linien der Zeiger
    
    	//TODO: FontFamily
    	INT32 iTextSize;
    
    	// AKTUALISIERUNGS-TIMER
    	HANDLE hTimer;
    	HANDLE hTimerQueue;
    	void CreateTimer();				// Erstellt einen Aktualisierungs-Timer für diese Klasse
    	void TimerCallback();			// Wird ausgeführt, wenn der Timer ausgelöst wird
    	INT32 iUpdateRate;				// in Millisekunden; gibt an, wie oft der Wavepointer aktualisiert wird
    
    public:
    	static VOID CALLBACK TimerRoutine(PVOID *lpParam, BOOLEAN TimerOrWaitFired);
    
    	WAVEPOINTER(HWND *hWnd, INT32 iRadius, INT32 iPosX, INT32 iPosY, INT32 iMidPointRadius, double dPeriod, Gdiplus::Color cLineColor, Gdiplus::Color cMidPointColor);
    	~WAVEPOINTER();
    
    	void AddCurve(INT32 iRadius, Gdiplus::Color cLineColor, INT32 iPeriod);		// Fügt einen Zeiger hinzu
    	void Draw();						// Zeichnet alles in das hDC
    	void Run();
    	void SethWnd(HWND *hWnd);
    };
    
    // wavepointer.cpp
    
    #include "stdafx.h"
    #include <math.h>
    
    WAVEPOINTER::WAVEPOINTER(HWND *hWnd, INT32 iRadius, INT32 iPosX, INT32 iPosY, INT32 iMidPointRadius, double dPeriod, Gdiplus::Color cLineColor, Gdiplus::Color cMidPointColor){
    	// Ersten Zeiger hinzufügen, weitere werden mit AddCurve() hinzugefügt
    	this->iRadius[0] = iRadius;
    	this->iPosX = iPosX;
    	this->iPosY = iPosY;
    	this->cLineColor[0] = cLineColor;
    	this->iMidPointRadius = iMidPointRadius;
    	this->cMidPointColor = cMidPointColor;
    	this->dPeriod[0] = dPeriod;
    	this->dTime = 0.00;
    	this->iUpdateRate = 20;
    	this->iXCounter = 0;
    
    	this->iTextSize = 8;
    
    	this->iCurves = 1;
    
    	if (!hWnd == NULL) this->hWnd = hWnd;
    };
    
    void WAVEPOINTER::SethWnd(HWND *hWnd){
    	this->hWnd = hWnd;
    };
    
    void WAVEPOINTER::Run(){
    	if (hWnd == NULL) {
    		MessageBox(NULL,"No hWnd. Use SethWnd(HWND *hWnd).","Error",MB_ICONEXCLAMATION);
    	} else {
    		CreateTimer();
    		bmpGraph = new Bitmap(500,500);
    	};
    };
    
    void WAVEPOINTER::TimerCallback(){
    	dTime += 1.0/(1000.0/iUpdateRate);
    	if (bmpGraph == NULL) MessageBox(0,"null","null",MB_OK);
    	Draw();
    };
    
    void WAVEPOINTER::Draw(){
    	InvalidateRect(*hWnd,NULL,true);
    
    	Graphics gGraphGraphics(bmpGraph);
    
    	gGraphGraphics.DrawLine(new Pen(Color(0,0,0)),0, 0, 100, 200);
    
    	HDC hDC;
    	PAINTSTRUCT ps;
    
    	hDC = BeginPaint(*hWnd, &ps);	
    
    	Graphics gGraphics(hDC);
    	gGraphics.SetSmoothingMode(SmoothingModeHighQuality);
    
    	// AUßENLINIEN ZEICHNEN
    	for (INT32 i = 0; i < iCurves; i++) {
    		Pen			pPen(cLineColor[i]);
    		SolidBrush	sbBrush(cLineColor[i]);
    
    		gGraphics.DrawEllipse(&pPen,iPosX - iRadius[i]/2,iPosY - iRadius[i]/2,iRadius[i],iRadius[i]);
    	}
    
    	gGraphics.DrawLine(new Pen(Color(0,0,0)), iPosX + 150, iPosY - 100, iPosX + 150, iPosY + 100); 
    
    	// ZEIGER ZEICHNEN
    	for (INT32 i = 0; i < iCurves; i++) {
    		Pen	pPen(cLineColor[i]);
    
    		Point pt1, pt2;			// pt1 ist der Ausgangspunkt, pt2 der Endpunkt des Zeigers
    		pt1.X = iPosX;
    		pt1.Y = iPosY;
    
    		// (DegToRad(360)/iPeriod[i]) * iTime bedeutet, dass er eine Umdrehung pro iPeriod macht
    		pt2.X = iPosX + iRadius[i]/2 * cos( (DegToRad(360)/dPeriod[i]) * dTime );
    		pt2.Y = iPosY + iRadius[i]/2 * sin( (DegToRad(360)/dPeriod[i]) * dTime );
    
    		dY[iXCounter] = pt2.Y;
    
    		gGraphics.DrawLine(&pPen,pt1,pt2);
    	}
    
    	iXCounter++;
    
    	// GRAFEN ZEICHNEN
    
    	//gGraphGraphics.DrawLine(new Pen(Color(0,0,0)),iXCounter-1, dY[iXCounter-1], iXCounter, dY[iXCounter]);
    
    	gGraphics.DrawImage(bmpGraph,0,0,500,500);
    
    	// INFORMATIONEN ZEICHNEN
    	/*
    double x = 99.12;
    wchar_t str[128];
    swprintf_s(str, L"%f\0", x);
    
    	SolidBrush  brush(Color(255, 0, 0, 255));
       FontFamily  fontFamily(L"Times New Roman");
       Font        font(&fontFamily, 24, FontStyleRegular, UnitPixel);
       PointF      pointF(10.0f, 20.0f);
    
       gGraphics.DrawString(str, 10, &font, pointF, &brush);
    	*/
    
    	// PFEILE ZEICHNEN
    	/*
    	for (INT32 i = 0; i < iCurves; i++) {
    		Bitmap bmpTemp(60,60);
    		Graphics g(&bmpTemp);
    		g.TranslateTransform(10.0f,10.0f);
    		g.RotateTransform(RadToDeg((DegToRad(360)/iPeriod[i]) * iTime));
    		g.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
    		g.DrawLine(new Pen(cLineColor[i]),0,0,4,8);
    		g.DrawLine(new Pen(cLineColor[i]),0,0,8,0);
    		g.DrawLine(new Pen(cLineColor[i]),8,0,4,8);
    
    		gGraphics.DrawImage(&bmpTemp,(float)(iPosX + iRadius[i]/2 * cos( (DegToRad(360)/iPeriod[i]) * iTime ),iPosY + iRadius[i]/2 * sin( (DegToRad(360)/iPeriod[i]) * iTime ));
    	}
    	*/
    
    	{ // MITTELPUNKT ZEICHNEN
    	SolidBrush	sbBrush(cMidPointColor);
    
    	gGraphics.FillEllipse(&sbBrush,iPosX - iMidPointRadius/2, iPosY - iMidPointRadius/2,iMidPointRadius,iMidPointRadius);
    	};
    
    	EndPaint(*hWnd,&ps);
    };
    
    // Fügt einen neuen Zeiger hinzu
    void WAVEPOINTER::AddCurve(INT32 iRadius, Gdiplus::Color cLineColor, INT32 iPeriod){
    	this->iRadius[iCurves] = iRadius;
    	this->cLineColor[iCurves] = cLineColor;
    	this->dPeriod[iCurves] = iPeriod;
    
    	iCurves++;
    };
    
    void WAVEPOINTER::CreateTimer(){
    	hTimer = NULL;
    	hTimerQueue = CreateTimerQueue();
    	CreateTimerQueueTimer( &hTimer, hTimerQueue, (WAITORTIMERCALLBACK)TimerRoutine, &wpWavepointerPointer, 0, iUpdateRate, 0);
    };
    
    VOID CALLBACK WAVEPOINTER::TimerRoutine(PVOID *lpParam, BOOLEAN TimerOrWaitFired){
    	((WAVEPOINTER *)lpParam)->TimerCallback();
    ;}
    
    WAVEPOINTER::~WAVEPOINTER() {
    
    };
    
    // main.cpp
    
    #pragma comment(lib, "gdiplus.lib")
    
    #include "stdafx.h"
    WAVEPOINTER wpWavePointer(NULL,200,200,200,7,5.0,Color(0,0,255),Color(0,0,0));
    
    //---------------------------------------------------------------------------
    HWND hWnd;
    LPCTSTR ClsName = "GDIFund";
    LPCTSTR WindowCaption = "GDI Fundamentals";
    LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    //---------------------------------------------------------------------------
    
    INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine, int nCmdShow)
    {
    	GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR           gdiplusToken;  
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
        MSG         Msg;
        WNDCLASSEX  WndClsEx;
    
        WndClsEx.cbSize        = sizeof(WNDCLASSEX);
        WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
        WndClsEx.lpfnWndProc   = WndProc;
        WndClsEx.cbClsExtra    = NULL;
        WndClsEx.cbWndExtra    = NULL;
        WndClsEx.hInstance     = hInstance;
        WndClsEx.hIcon         = LoadIcon(hInstance, IDI_APPLICATION);
        WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
        WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        WndClsEx.lpszMenuName  = NULL;
        WndClsEx.lpszClassName = ClsName;
        WndClsEx.hIconSm       = LoadIcon(hInstance, IDI_APPLICATION);
    
        RegisterClassEx(&WndClsEx);
    
        hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                              ClsName,
                              WindowCaption,
                              WS_OVERLAPPEDWINDOW,
                              100,
                              120,
                              640,
                              480,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);
    
    	wpWavePointer.AddCurve(100,Color(255,0,0),2.0);
    	wpWavePointer.AddCurve(150,Color(0,255,0),3.0);
    	wpWavePointer.SethWnd(&hWnd);
    	wpWavePointer.Run();
    
        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);
    
        while( GetMessage(&Msg, NULL, 0, 0) )
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    
    	GdiplusShutdown(gdiplusToken);
    
        return 0;
    }
    
    double dTime;
    //---------------------------------------------------------------------------
    LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        switch(Msg)
        {
        case WM_DESTROY:
    		wpWavePointer.~WAVEPOINTER();
            PostQuitMessage(WM_QUIT);
            break;
    	case WM_PAINT:
    		break;
        default:
            return DefWindowProc(hWnd, Msg, wParam, lParam);
        }
        return 0;
    }
    


  • Hmm, spontan fällt mir nur auf:
    BeginPaint und EndPaint sollte man nur in WM_PAINT verwenden, GetDC() und ReleaseDC() wäre in der .Draw() wohl eher geeignet.



  • wpWavePointer.~WAVEPOINTER();
    

    Hat nichts mit dem Thema zu tun, aber den expliziten Aufruf des Destruktors solltest du dir wirklich schenken.
    Auch sonst solltest du dir vielleicht einmal Container der stl (std::vector) anschauen.
    Ansonsten:

    void SethWnd(HWND *hWnd);
    ...
    HWND *hWnd;
    

    Handle, nicht Zeiger darauf übergeben und Handle, nicht Zeiger darauf speichern.

    double dTime;
    

    Was soll die globale Variablendeklaration vor der WndProc?
    Gibst noch einiges Anderes.



  • @Thilo87
    wpWavePointer ist global.
    Konstruktoren von globalen Objekten laufen vor WinMain().
    GdiplusStartup rufst du erst in WinMain() auf, d.h. der Konstruktor von wpWavePointer läuft vor GdiplusStartup, und daher kannst du dort GDI+ noch nicht verwenden.



  • geeky schrieb:

    Hmm, spontan fällt mir nur auf:
    BeginPaint und EndPaint sollte man nur in WM_PAINT verwenden, GetDC() und ReleaseDC() wäre in der .Draw() wohl eher geeignet.

    Nach einigen Widrigkeiten konnte ich das ganze mit VS2012 kompilieren.
    Das Fenster blieb (erwartungsgemäss) zunächst leer.

    Es fehlt:

    PAINTSTRUCT ps;
    HDC hdc;
    
    switch(Msg)
    {
    case WM_PAINT:
    	hdc = BeginPaint(hWnd, &ps);
    	// TODO: Hier den Zeichnungscode hinzufügen.
    	wpWavePointer.Draw();
    	EndPaint(hWnd, &ps);
    	break;
    

    In der math.h scheint folgendes zu fehlen

    #define M_PI  ((double)3.14159265358979323846 )
    #define DegToRad(grad)  (grad * M_PI / 180)
    

    Und beim Pen fehlt im Code ein Parameter

    // Create a Pen object.
    Pen     bluePen(Color( 255, 0, 0, 255), 1.0f);
    

Anmelden zum Antworten