PictureControl und Bitmap



  • hm... ich stell mich offenbar recht dämlich an...

    GUI wird normalerwiese durch LabVIEW gemacht und dieses kleine Programm sollte nur zum Debuggen der Kamera bzw. Bilderbetachtung ohne Kamera dienen... aber offenbar ist das mit Bilddarstellung und PictureControl und Windows wensentlich schwieriger als ich dachte...

    //variables
    	RECT myRect;
    
    	// get coordinates for rectangle
    	GetDlgItem(IDC_IMG1)->GetClientRect(&myRect);
    	myRect.left;
    	myRect.right;
    	myRect.top;
    	myRect.bottom;
    
    	int width = myRect.right - myRect.left;
    	int height = myRect.bottom - myRect.top;
    
    	CDC *pDC = GetDC();
    	CDC mDC;
    	mDC.CreateCompatibleDC(pDC);
    
    	CBitmap scale_bitmap;
    	// Bitmap erzeugen
    	scale_bitmap.CreateCompatibleBitmap(pDC, width, height);
    	// Bits einfügen
    	scale_bitmap.SetBitmapBits(1280 * 1024, &m_pcImageMemory);
    	CBitmap *pob = mDC.SelectObject(&scale_bitmap);
    	CDC *pDC_CTRL = GetDlgItem(IDC_IMG1)->GetDC();
    	CDC mDC_CTRL;
    	mDC_CTRL.CreateCompatibleDC(pDC_CTRL);
    	mDC.SelectObject(pob);
    
    	BitBlt(mDC_CTRL.m_hDC, 0, 0, width, height, mDC.m_hDC, 0, 0, SRCCOPY);
    
    	((CStatic *) GetDlgItem(IDC_IMG1))->SetBitmap((HBITMAP) scale_bitmap);
    	ReleaseDC(pDC);
    

    gibt 'n schwarzes Bild... irgendwas ist da faul... oder ich habe n Knoten im Kopf...

    ich glaube ich gebe auf 😞



  • Das kann schon so gehen wenn du nicht gerade CBitmap scale_bitmap; auf dem Stack erzeugst und diese nach beenden der Funktion dann terminiert wird, was soll das das PictureCtrl anzeigen? Denn dieses macht keine kopie von deinem CBitmap. Also verschieb mal CBitmap scale_bitmap; in den Header und dann Versuch noch mal, wenn du dann net noch wo anders nen Fehler hast dann solltest du schon mal was anderes sehen als ein schwarzes Bild



  • Danke für eure Geduld mit mir.

    Ich denke, da ich mit DC und MFC nicht so den Durchblick habe, sollte ich es so einfach wie möglich halten.

    Also starte ich mal meine Idee ganz von vorne:

    1.) Daten werden von der Kamera aufgenommen bzw. aus Binärdatei eingelesen

    2.) Daten in CBitmap verpacken

    3.) CBitmap in CImage verpacken (attach)

    4.) CImage auch Größe von PictureControl stretchen/komprimieren

    5.) CImage entpacken und ausgeben (detach)

    CImage kann man ja auch direkt speichern und schauen, ob alles funktioniert

    h-File
        [...]
        CBitmap scale_bitmap;
        CImage  image1;
        [...]
    cpp-File
        [...]
        //  nur zur Info, damit der Datentyp klar ist
        //  char *m_pcImageMemory;  
        //  char *inBuf
        UINT sz = UINT(1280 * 1024);
        [...]
    
        // Daten sind eingelesen
        memcpy(m_pcImageMemory, inBuf, sz);
    
        //  Daten in CBitmap verpacken
        scale_bitmap.CreateBitmap(1280, 1024, 1, 8, &m_pcImageMemory);
    
        //  CBitmap in CImage verpacken
        image1.Attach(HBITMAP(scale_bitmap));
    
        //  CImage für Debugzwecke speichern
        //image1.Save(CString("D:\\temp\\test.bmp"));
    
        //  CIamge stretechen/komprimieren
        RECT myRect;
    
        GetDlgItem(IDC_IMG1)->GetClientRect(&myRect);
        int width = myRect.right - myRect.left;
        int height = myRect.bottom - myRect.top;
    
        CDC *pDC_CTRL = GetDlgItem(IDC_IMG1)->GetDC();
        CDC mDC_CTRL;
        mDC_CTRL.CreateCompatibleDC(pDC_CTRL);
    
        img1.StretchBlt(mDC_CTRL.m_hDC, 0, 0, width, height, SRCCOPY);
    
        //  CImage für Debugzwecke speichern
        //image1.Save(CString("D:\\temp\\test1.bmp"));
    
        //  CBitmap entpacken und in PictureControl ausgeben
        ((CStatic *) GetDlgItem(IDC_IMG1))->SetBitmap(HBITMAP(img1.Detach()));
    
        ReleaseDC(pDC_CTRL);
    
        [...]
    

    Ich flige allerdings mit Debug Assertion Fail! in Zeile 21 (Attach) raus.

    File: c:\...\atlimage.h
    Line: 758
    Expression: hBitmap!=0


  • Mod

    Callstack?

    Wie der ASSERT schon sagt ist die Bitmap nicht gültig. Einfach Wiederholen anklicken und im Callstack schauen was DU gerade machst.



  • > QCam_Test.exe!ATL::CImage::Attach(HBITMAP__ * hBitmap, ATL::CImage::DIBOrientation eOrientation) Zeile 758 C++

    inline void CImage::Attach(
    	_In_ HBITMAP hBitmap,
    	_In_ DIBOrientation eOrientation) throw()
    {
    	ATLASSUME( m_hBitmap == NULL );
    	ATLASSERT( hBitmap != NULL );
    
    	m_hBitmap = hBitmap;
    
    	UpdateBitmapInfo(eOrientation);
    }
    

    (in diesem Post ist es Zeile 6).


  • Mod

    Callstack sag ich....

    Also da Du in Deinem Code Zeile 21 einen Attach benutzt gehe ich davon aus, dass der Call zuvor fehlschlägt... Du prüfst ja auch nicht ob alles klappt...



  • sorry...

    ums Fehlerabfangen habe ich mir keine Gedanken gemacht bzw. die Rückgabewerte ignoriert.

    Ich habe Zeile 18 ersetzt durch:

    CDC *pDC = GetDC();
        CDC mDC;
        mDC.CreateCompatibleDC(pDC);
    
        //  Bitmap erzeugen
        scale_bitmap.CreateCompatibleBitmap(pDC, 1280, 1024);
        //  Bits einfügen
        scale_bitmap.SetBitmapBits(1280*1024, m_pcImageMemory);
    

    Nun läuft das Programm einmal ohne Fehler durch (beim 2. Mal tritt ein neuer Fehler auf), aber das Ergebnis ist noch nicht das Wahre:
    - Das Bild ist sichtbar, aber irgendwie sieht es aus als ob der Kontrast nicht stimmt (sieht aus wie tw ausgeblendet/transparent)...
    - Die Größe an sich stimmt nicht (viel größer als das PictureControl ich würde sagen immer noch 12801024), also scheint das Komprimieren noch nicht zu funktionieren
    - Urspründlich liegt ein Bild (1280
    1024) im Speicher nun ist das Bild aber gleich vier mal aneinandergereiht in einer Reihe da.

    Wenn ich die image1 vor und nach dem komprimieren Speichere sehe ich keine Veränderung und auch vier mal das gleiche Bild, aber da stimmt immerhin der Kontrast.

    😕 irgendwie gehts voran, aber nur in sehr kleinen Schritten... 😞

    Leider habe ich nicht die Zeit mich noch Tageweise damit zu beschäftigen. Ich verbuche die Idee dann also gescheitert.

    Sollte jemand ein Tutorial oder ein Beispiel kennen/haben, wo genau das gemacht wird, sprich ein Byte/char-Array in einem PictureControl anzuzeigen, dann kann er das gerne hier posten, ich lese das Thema/Forum weiterhin.

    @ Martin Richter danke für deine Antworten/Hinweise und deine Geduld


  • Mod

    Mal meine Vermutung. Du hast image1 in der Klasse definiert und nicht lokal.

    Dadurch ist das Objekt natürlich vorhanden und Attach schlägt natürlich beim zweiten Mal fehl. Attach geht nur an ein leeres Objekt.

    Das ist der Nachteil, wenn man nur rudimentären Code postet.



  • // ImageTestDlg.h: Headerdatei
    //
    
    #pragma once
    
    // CImageTestDlg-Dialogfeld
    class CImageTestDlg : public CDialog
    {
    private:
    	char *m_pcImageMemory[2];
    // Konstruktion
    public:
    	CImageTestDlg(CWnd* pParent = NULL);	// Standardkonstruktor
    
    // Dialogfelddaten
    	enum { IDD = IDD_IMAGETEST_DIALOG };
    
    	protected:
    	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV-Unterstützung
    
    // Implementierung
    protected:
    	HICON m_hIcon;
    
    	// Generierte Funktionen für die Meldungstabellen
    	virtual BOOL OnInitDialog();
    	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    	afx_msg void OnPaint();
    	afx_msg HCURSOR OnQueryDragIcon();
    	DECLARE_MESSAGE_MAP()
    public:
    	afx_msg void OnBnClickedFileopen();
    };
    
    // ImageTestDlg.cpp: Implementierungsdatei
    //
    
    #include "stdafx.h"
    #include "ImageTest.h"
    #include "ImageTestDlg.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    // CAboutDlg-Dialogfeld für Anwendungsbefehl "Info"
    
    class CAboutDlg : public CDialog
    {
    public:
    	CAboutDlg();
    
    // Dialogfelddaten
    	enum { IDD = IDD_ABOUTBOX };
    
    	protected:
    	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV-Unterstützung
    
    // Implementierung
    protected:
    	DECLARE_MESSAGE_MAP()
    };
    
    CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
    {
    }
    
    void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    {
    	CDialog::DoDataExchange(pDX);
    }
    
    BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    END_MESSAGE_MAP()
    
    // CImageTestDlg-Dialogfeld
    
    CImageTestDlg::CImageTestDlg(CWnd* pParent /*=NULL*/)
    	: CDialog(CImageTestDlg::IDD, pParent)
    {
    	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
    
    void CImageTestDlg::DoDataExchange(CDataExchange* pDX)
    {
    	CDialog::DoDataExchange(pDX);
    }
    
    BEGIN_MESSAGE_MAP(CImageTestDlg, CDialog)
    	ON_WM_SYSCOMMAND()
    	ON_WM_PAINT()
    	ON_WM_QUERYDRAGICON()
    	ON_BN_CLICKED(IDC_FILEOPEN, &CImageTestDlg::OnBnClickedFileopen)
    END_MESSAGE_MAP()
    
    // CImageTestDlg-Meldungshandler
    
    BOOL CImageTestDlg::OnInitDialog()
    {
    	CDialog::OnInitDialog();
    
    	// Hinzufügen des Menübefehls "Info..." zum Systemmenü.
    
    	// IDM_ABOUTBOX muss sich im Bereich der Systembefehle befinden.
    	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    	ASSERT(IDM_ABOUTBOX < 0xF000);
    
    	CMenu* pSysMenu = GetSystemMenu(FALSE);
    	if (pSysMenu != NULL)
    	{
    		BOOL bNameValid;
    		CString strAboutMenu;
    		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
    		ASSERT(bNameValid);
    		if (!strAboutMenu.IsEmpty())
    		{
    			pSysMenu->AppendMenu(MF_SEPARATOR);
    			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    		}
    	}
    
    	// Symbol für dieses Dialogfeld festlegen.  Wird automatisch erledigt
    	//  wenn das Hauptfenster der Anwendung kein Dialogfeld ist
    	SetIcon(m_hIcon, TRUE);			// Großes Symbol verwenden
    	SetIcon(m_hIcon, FALSE);		// Kleines Symbol verwenden
    
    	for (int i = 0; i < 2; i++) {
    		m_pcImageMemory[i] = (char *) malloc(1280 * 1024);
    	}
    
    	return TRUE;  // TRUE zurückgeben, wenn der Fokus nicht auf ein Steuerelement gesetzt wird
    }
    
    void CImageTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
    {
    	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    	{
    		CAboutDlg dlgAbout;
    		dlgAbout.DoModal();
    	}
    	else
    	{
    		CDialog::OnSysCommand(nID, lParam);
    	}
    }
    
    // Wenn Sie dem Dialogfeld eine Schaltfläche "Minimieren" hinzufügen, benötigen Sie
    //  den nachstehenden Code, um das Symbol zu zeichnen.  Für MFC-Anwendungen, die das 
    //  Dokument/Ansicht-Modell verwenden, wird dies automatisch ausgeführt.
    
    void CImageTestDlg::OnPaint()
    {
    	if (IsIconic())
    	{
    		CPaintDC dc(this); // Gerätekontext zum Zeichnen
    
    		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
    
    		// Symbol in Clientrechteck zentrieren
    		int cxIcon = GetSystemMetrics(SM_CXICON);
    		int cyIcon = GetSystemMetrics(SM_CYICON);
    		CRect rect;
    		GetClientRect(&rect);
    		int x = (rect.Width() - cxIcon + 1) / 2;
    		int y = (rect.Height() - cyIcon + 1) / 2;
    
    		// Symbol zeichnen
    		dc.DrawIcon(x, y, m_hIcon);
    	}
    	else
    	{
    		CDialog::OnPaint();
    	}
    }
    
    // Die System ruft diese Funktion auf, um den Cursor abzufragen, der angezeigt wird, während der Benutzer
    //  das minimierte Fenster mit der Maus zieht.
    HCURSOR CImageTestDlg::OnQueryDragIcon()
    {
    	return static_cast<HCURSOR>(m_hIcon);
    }
    
    void CImageTestDlg::OnBnClickedFileopen() {
    	char *inBuf;
    	CFile file;
    	CString path;
    	CString strFilter = CString("Bilddatei|*.img||");
    	CString Type = CString("*.img");
    	UINT sz = UINT(1280 * 1024);
    
    	CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER, strFilter);
    
    	if (dlg.DoModal() == IDOK) {
    		path = dlg.GetPathName();
    
    		if (file.Open(path, CFile::modeRead)) {
    			inBuf = (char *) malloc(sz);
    
    			for (int i = 0; i < 2; i++) {
    				if (file.Read(inBuf, sz) == sz) {
    
    					memcpy(m_pcImageMemory[i], inBuf, sz);
    
    				} else {
    					AfxMessageBox(L"Wrong imagesize!", MB_ICONERROR | MB_OK);
    					break;
    				}
    			}
    			file.Close();
    			free(inBuf);
    		} else {
    			AfxMessageBox(L"Failed to open file!", MB_ICONERROR | MB_OK);
    		}
    
    	} else {
    		return;
    	}
    
    	CBitmap scale_bitmap;
    	CImage	image1;
    	BOOL retCreBmp = scale_bitmap.CreateBitmap(1280, 1024, 1, 8, m_pcImageMemory[0]);
    
    	//  CBitmap in CImage verpacken
    	image1.Attach((HBITMAP) scale_bitmap);
    
    	//  CImage für Debugzwecke speichern
    	image1.Save(CString("D:\\temp\\test.bmp"));
    
    	//  CIamge stretechen/komprimieren
    	RECT myRect;
    
    	GetDlgItem(IDC_IMG1)->GetClientRect(&myRect);
    	int width = myRect.right - myRect.left;
    	int height = myRect.bottom - myRect.top;
    
    	CDC *pDC_CTRL = GetDlgItem(IDC_IMG1)->GetDC();
    	CDC mDC_CTRL;
    	mDC_CTRL.CreateCompatibleDC(pDC_CTRL);
    
    	BOOL retStrBlt=image1.StretchBlt(mDC_CTRL.m_hDC, 0, 0, width, height, SRCCOPY);
    
    	//  CImage für Debugzwecke speichern
    	image1.Save(CString("D:\\temp\\test1.bmp"));
    
    	//  CBitmap entpacken und in PictureControl ausgeben
    	((CStatic *) GetDlgItem(IDC_IMG1))->SetBitmap(HBITMAP(image1.Detach()));
    
    	ReleaseDC(pDC_CTRL);
    }
    

    Das ist der Quellcode den ich heute neu angelegt habe.

    den haut es bei StretchBlt raus, mit:
    Debug Assertion Failed!
    File: c:\...\atlimage.h
    Line: 1607
    Expression: hBitmap == m_hBitmap


  • Mod

    Und was soll der Quatsch mit StretchBit an dieser Stelle?
    In dieser Form wird der Inhalt des DCs in die Bitmap übertragen. Diese ist aber schon gefüllt. Deshalb der ASSERT.

    Was also bezweckst Du mit dem StretchBlt. Wenn DU nur das Image zuweisen willst, dann tu das und lass das was dazwischen liegt weg.

    Ich gehe mal davon aus, dass Du das Bild anpassen möchtest. Dann ist der Code aber falsch. Etwas Googlen und siehe da:
    http://www.codeproject.com/Questions/398007/How-to-use-CImage-class-to-resize-a-bitmap

    BZW: Deine Form mit Speicher in einem C++ Programm umzugehen ist sehr eigentümlich. Warum malloc? Warum keine smart pointer, keine vectoren?


Anmelden zum Antworten