API Question Writing an Orbiter Device Context to a File

Swatch

Addon Developer
Addon Developer
Joined
Apr 24, 2008
Messages
15
Reaction score
0
Points
0
Hello all,

I'm trying to write a DC that is handled by orbiter out to a file and am having some troubles, hope you guys can help.

I have a surface that is being used in an Orbiter 2D panel that I would like to draw on and then output to a bitmap file.

I can use oapiGetDC to get the actual Device Context that I need to draw to, and I know how to actually draw to the Device Context, however I then want to output the resulting memory DC as a bitmap file for future reference. So far all I've been able to do is write a bitmap file that is the proper resolution but is all black. I can't get any proper image out. Any help is greatly appreciated, as I'm trying to get this working for ProjectApollo's Entry Monitoring System for debugging purposes.
 

Artlav

Aperiodic traveller
Addon Developer
Beta Tester
Joined
Jan 7, 2008
Messages
5,790
Reaction score
780
Points
203
Location
Earth
Website
orbides.org
Preferred Pronouns
she/her
Seeing what you actually tried to do when you "want to output the resulting memory DC as a bitmap file" as code could help.

Why can't you just take a screenshot?
 
E

ex-orbinaut

Guest
Hi,

Can't tell you exactly for your case, but I am working on something similar, so maybe I can give you a pointer. You will probably need to use some WINAPI functions; make a bitmap structure and then use CreateCompatibleBitmap() to get a Handle on the bitmap you created from the bitmap DC, so you can then save it. It will be something like that.

However I fail to see, if you already have the made bitmap you are using for the panel, why you want to save another copy of it? Anyway, that is your secret...;)

Have a look at MicroSoft WINAPI MSDN on the net, for those bitmap and file IO function usages. Good luck.
 

Swatch

Addon Developer
Addon Developer
Joined
Apr 24, 2008
Messages
15
Reaction score
0
Points
0
No secrets here... the last post just ended up being short because it was late at night here.

Artlav: let me show you what I'm trying to do....

Here is the bitmap that is loaded into orbiter that I want to use as a background to draw upon.
moz-screenshot.jpg

scrollsm.PNG


Below is a picture of how that bitmap is bitblt onto the Apollo control panel. (surrounded by red box)

moz-screenshot-1.jpg
EMSPanel.PNG


This is the EMS scroll and it scrolls from right to left to provide a reference for manual reentry control. Meanwhile, a 'scribe' would draw a line on this scroll in the vertical axis relating to the current G-loading. Shown below is an example of this using the test settings. See the black line that has been drawn?

EMSPanel_samplescribe.PNG


This line is a Polyline GDI object drawn onto the background bitmap (the large scroll) each PanelRefresh. The necessary areas of the background bitmap are then bitblt onto the Panel itself. This function is working perfectly, however I would like to be able to output the full scroll with trace included on it for post-mission reference and debugging.

Currently the only way to get a representative line is how Gunner_CAF did for me below. but this required him pausing and starting the simulation and taking numbers down.
scroll_dottedline.PNG


It is my goal to make it easier on him by just automatically writing the scroll+trace to a bitmap.

Now... on to the code I have...

Code:
bool EMS::WriteScrollToFile() {return WriteScrollToFile(NULL);}
bool EMS::WriteScrollToFile(SURFHANDLE surf) {

    if (sat->EMSScrollSurf == 0) return false;

    /////////////////////////////////////////////////////////
    //Get the drawing surface, apply the scribe line and create a corresponding 
    //bitmap with the same dimensions

    SURFHANDLE bkgrndsurf;

    if (surf == NULL){
        bkgrndsurf = sat->EMSScrollSurf;
    } else {
        bkgrndsurf = surf;
    }
    HDC hDCsrc = oapiGetDC (bkgrndsurf);

    SetBkMode (hDCsrc, TRANSPARENT);            
    HGDIOBJ oldObj = SelectObject(hDCsrc, g_Param.pen[2]);

    Polyline(hDCsrc, ScribePntArray, ScribePntCnt);
    
    SelectObject(hDCsrc, oldObj);
    
    int width = (int)ScrollBitmapLength;
    int height = (int)ScrollBitmapHeight;

    HDC hDCdst = CreateCompatibleDC(hDCsrc); 
    HBITMAP hCbmp = CreateCompatibleBitmap(hDCdst, width, height); 
    BITMAP Cbmp;

    SelectObject(hDCdst, hCbmp);

    BitBlt(hDCdst, 0, 0, width, height, hDCsrc, 0, 0, SRCCOPY); 

    // Get the BITMAP from the HBITMAP
    GetObject(hCbmp,sizeof(BITMAP),&Cbmp);
     
    BITMAPFILEHEADER   bmfHeader;    
    BITMAPINFOHEADER   bi;
     
    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = Cbmp.bmWidth;    
    bi.biHeight = Cbmp.bmHeight;  
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((Cbmp.bmWidth * bi.biBitCount + 31) / 32) * 4 * Cbmp.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize+sizeof(BITMAPINFOHEADER)); 
    LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);    
    *lpbi = bi;

    // Gets the "bits" from the bitmap and copies them into a buffer 
    // which is pointed to by lpbi
    GetDIBits(hDCsrc,hCbmp,0,
        (UINT)Cbmp.bmHeight, 
        lpbi,
        (BITMAPINFO *)lpbi, DIB_RGB_COLORS);    

    // A file is created, this is where we will save the screen capture.
    HANDLE hFile = CreateFile((LPCTSTR)"EMS_Scroll.bmp",
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, NULL);   
    
    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 
    //Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); 
    
    //Size of the file
    bmfHeader.bfSize = dwSizeofDIB; 
    
    //bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM   
 
    DWORD dwBytesWritten = 0;
    WriteFile(hFile,(LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile,(LPSTR)&bi,sizeof(BITMAPINFOHEADER),&dwBytesWritten,NULL);
    WriteFile(hFile,(LPSTR)lpbi,dwBmpSize,&dwBytesWritten,NULL);
    
    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);    
    GlobalFree(hDIB);

    //Close the handle for the file that was created
    CloseHandle(hFile);
       
    //Clean up
    oapiReleaseDC(bkgrndsurf, hDCsrc);
    DeleteObject(hCbmp);

    return true;
}

The meat of the code was built from example code from within Microsoft's MSDN. At the top of the method is the code I use in the PanelRedraw to write the scribe line (the polyline command) to the orbiter surface. The result of this code IS a bitmap, but the bitmap is all black. The file can be found at the link below.

http://swatch.homeip.net/Orbiter/SDK Question/EMS_Scroll.bmp

What I'm having trouble figureing out is why I can't get any proper bitmap image out... even if I comment out the polyline part and try to just write out the scroll by itself. Hope this is enough information, but if not, I can supply more :).
 
E

ex-orbinaut

Guest
Swatch, my apologies...

With absolutely no intention of any hijacking of your thread, may I add that the problem is not only related to saving the bmp file. My more simplistic approach has run into the same "black blitting" problem, while stretching a panel bitmap.

Code:
bool Test_Vessel::clbkLoadPanel(int id)
{
 HBITMAP h_Panel, h_Sized_Panel;
 BITMAP sbmp;
 SURFHANDLE s_sbmp, s_dbmp;
 HDC hDC_sMem, hDC_dMem;
 
 s_sbmp = oapiCreateSurface(800, 375); // test values
 s_dbmp = oapiCreateSurface(1024, 480);
 hDC_sMem = oapiGetDC(s_sbmp);
 hDC_dMem = oapiGetDC(s_dbmp);
 h_Panel = LoadBitmap(hDLL, MAKEINTRESOURCE(IDB_PANEL_800));
 GetObject(h_Panel, sizeof(sbmp), &sbmp);
 SelectObject(hDC_sMem, h_Panel);
 SetStretchBltMode(hDC_dMem, HALFTONE);
 StretchBlt(hDC_dMem, 0, 0, 1024, 480, 
  hDC_sMem, 0, 0, 800, 375, SRCCOPY);
 
 h_Sized_Panel = CreateCompatibleBitmap(hDC_dMem, 1024, 480);
 
 oapiRegisterPanelBackground(h_Sized_Panel,  
  PANEL_ATTACH_BOTTOM | PANEL_MOVEOUT_BOTTOM, 
  0xFFFFFF);
 
 return true;
}

Maybe the same solution?

Again, my apologies, and will not intrude on your discussion again. :tiphat:

All the best...
 

Swatch

Addon Developer
Addon Developer
Joined
Apr 24, 2008
Messages
15
Reaction score
0
Points
0
Not a problem, hopefully the solution is one in the same.
 

computerex

Addon Developer
Addon Developer
Joined
Oct 16, 2007
Messages
1,282
Reaction score
17
Points
0
Location
Florida
Hi. I haven't read the entire thread so forgive me if this is not what you are looking for. The below code will take a snapshot of the orbiter window, draw on it, then save it as a bitmap file.

Code:
#define STRICT
#define ORBITER_MODULE

#include <orbitersdk.h>
#include <iostream>

HDC hOrbWindow=0;
DWORD width=0, height=0;

DLLCLBK void opcOpenRenderViewport(HWND hWnd, DWORD w, DWORD h, BOOL fc)
{
	hOrbWindow=GetDC(hWnd);
	width=w;height=h;
}
// below two functions copied from MSDN
PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp)
{ 
	BITMAP      bmp; 
	PBITMAPINFO pbmi; 
	WORD        cClrBits; 

	// Retrieve the bitmap color format, width, and height. 
	if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) {
		return NULL;
	}

	cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 
	if (cClrBits == 1) 
		cClrBits  = 1; 
	else if (cClrBits <= 4) 
		cClrBits  = 4; 
	else if (cClrBits <= 8) 
		cClrBits  = 8; 
	else if (cClrBits <= 16) 
		cClrBits  = 16; 
	else if (cClrBits <= 24) 
		cClrBits  = 24; 
	else cClrBits = 32; 

	if (cClrBits != 24) 
		pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
		sizeof(BITMAPINFOHEADER) + 
		sizeof(RGBQUAD) * (1<< cClrBits)); 
	else 
		pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
		sizeof(BITMAPINFOHEADER)); 

	pbmi->bmiHeader.biSize     = sizeof(BITMAPINFOHEADER); 
	pbmi->bmiHeader.biWidth    = bmp.bmWidth; 
	pbmi->bmiHeader.biHeight   = bmp.bmHeight; 
	pbmi->bmiHeader.biPlanes   = bmp.bmPlanes; 
	pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 
	if (cClrBits < 24) 
		pbmi->bmiHeader.biClrUsed = (1<<cClrBits); 
	pbmi->bmiHeader.biCompression = BI_RGB; 
	pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
		* pbmi->bmiHeader.biHeight; 
	pbmi->bmiHeader.biClrImportant = 0; 
	return pbmi; 
}

bool CreateBMPFile(LPTSTR pszFile, PBITMAPINFO pbi, 
				   HBITMAP hBMP, HDC hDC) { 
					   HANDLE hf;                 // file handle 
					   BITMAPFILEHEADER hdr;       // bitmap file-header 
					   PBITMAPINFOHEADER pbih;     // bitmap info-header 
					   LPBYTE lpBits;              // memory pointer 
					   DWORD dwTotal;              // total count of bytes 
					   DWORD cb;                   // incremental count of bytes 
					   BYTE *hp;                   // byte pointer 
					   DWORD dwTmp; 

					   pbih = (PBITMAPINFOHEADER) pbi; 
					   lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

					   if (!lpBits) {
						   return false;
					   }

					   // Retrieve the color table (RGBQUAD array) and the bits 
					   // (array of palette indices) from the DIB. 
					   if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, 
						   DIB_RGB_COLORS)) {
							   return false;
					   }

					   // Create the .BMP file. 
					   hf = CreateFile(pszFile, 
						   GENERIC_READ | GENERIC_WRITE, 
						   (DWORD) 0, 
						   NULL, 
						   CREATE_ALWAYS, 
						   FILE_ATTRIBUTE_NORMAL, 
						   (HANDLE) NULL); 
					   if (hf == INVALID_HANDLE_VALUE) {
						   return false;
					   }
					   hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M" 
					   // Compute the size of the entire file. 
					   hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + 
						   pbih->biSize + pbih->biClrUsed 
						   * sizeof(RGBQUAD) + pbih->biSizeImage); 
					   hdr.bfReserved1 = 0; 
					   hdr.bfReserved2 = 0; 

					   // Compute the offset to the array of color indices. 
					   hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + 
						   pbih->biSize + pbih->biClrUsed 
						   * sizeof (RGBQUAD); 

					   // Copy the BITMAPFILEHEADER into the .BMP file. 
					   if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), 
						   (LPDWORD) &dwTmp,  NULL)) 
					   {
						   return false;
					   }

					   // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. 
					   if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) 
						   + pbih->biClrUsed * sizeof (RGBQUAD), 
						   (LPDWORD) &dwTmp, NULL)) {
							   return false;
					   }

					   // Copy the array of color indices into the .BMP file. 
					   dwTotal = cb = pbih->biSizeImage; 
					   hp = lpBits; 
					   if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) {
						   return false;
					   }

					   // Close the .BMP file. 
					   if (!CloseHandle(hf)) {
						   return false;
					   }

					   // Free memory. 
					   GlobalFree((HGLOBAL)lpBits);
					   return true;
}

DLLCLBK void opcPreStep(double simt, double simdt, double mjd)
{
	static double ltime = simt;

	if ( GetAsyncKeyState(VK_F10) && simt-ltime > 0.4)
	{
		HDC hMemDC = CreateCompatibleDC(hOrbWindow);
		HBITMAP hBitmap = CreateCompatibleBitmap(hOrbWindow, width, height);
		HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);
		BitBlt(hMemDC, 0, 0, width, height, hOrbWindow, 
			0, 0, SRCCOPY);

		// drawing stuff
		for ( int i = 0; i < 100; i++ ){
			MoveToEx(hMemDC, rand()%width, rand()%height, NULL);
			LineTo(hMemDC, rand()%width, rand()%height);
		}
        SelectObject(hMemDC, hOld);
        DeleteDC(hMemDC);

		PBITMAPINFO bitmapInfo = CreateBitmapInfoStruct(hBitmap);
		CreateBMPFile("screenie.bmp", bitmapInfo, hBitmap, hOrbWindow);
		DeleteObject(hBitmap);
		ltime=simt;
	}
}
 

Swatch

Addon Developer
Addon Developer
Joined
Apr 24, 2008
Messages
15
Reaction score
0
Points
0
That might be helpful computerex, but I think my problem stems from getting the original bitmap information from oapiGetDC instead of the MSDN GetDC. I'm wondering if orbiter has some special way of storing these bitmaps.

I will definitely look through your code to figure out if mine has a problem with its image output code though. Thanks for the help, hopefully its what I need.
 

Swatch

Addon Developer
Addon Developer
Joined
Apr 24, 2008
Messages
15
Reaction score
0
Points
0
Finally got around to testing that... and whatever in that code works, it works well! :)

Thanks a bunch computerex! When I was diggin through the MSDN originally I never found those functions, but yours did the trick.
 
Last edited:
Top