I'm calling the following function every 100ms. It's goal is to take an image from renderBuffer, resize it and display it in the dialog's CStatic control with SetBitmap().
Problem is that I'm observing a rather huge spike of memory usage every second when this function is executed. It displays the resized image in CStatis control without a problem, but I see in task manager that each second process allocates additional 4 megabytes of memory and doesn't ever stop until the process runs out of memory.
Here is the code, let me know if you have any idea what could be the problem.
void CAppDlg::UpdatePreview( const RenderBuffer* renderBuffer )
{
HBITMAP hbmReturn = NULL;
Gdiplus::Bitmap *bmPhoto = NULL;
Gdiplus::Bitmap bmPhoto( THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT );
CBitmap Bmp1;
Gdiplus::Bitmap image( 780, 780, 4*780, PixelFormat32bppARGB, renderBuffer->buffer );
int sourceWidth = image.GetWidth();
int sourceHeight = image.GetHeight();
int destX = 0,
destY = 0;
float nPercent = 0;
float nPercentW = ((float)THUMBNAIL_WIDTH/(float)sourceWidth);;
float nPercentH = ((float)THUMBNAIL_HEIGHT/(float)sourceHeight);
if(nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = (int)((THUMBNAIL_WIDTH - (sourceWidth * nPercent))/2);
}
else
{
nPercent = nPercentW;
destY = (int)((THUMBNAIL_HEIGHT - (sourceHeight * nPercent))/2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
bmPhoto.SetResolution( image.GetHorizontalResolution(), image.GetVerticalResolution() );
Gdiplus::Graphics *grPhoto = Gdiplus::Graphics::FromImage( &bmPhoto );
Gdiplus::Color colorW(255, 255, 255, 255);
grPhoto->Clear( colorW );
grPhoto->SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBicubic );
grPhoto->DrawImage( &image, Gdiplus::Rect(destX, destY, destWidth, destHeight) );
bmPhoto.GetHBITMAP( colorW, &hbmReturn );
m_BitmapPreview.SetBitmap( hbmReturn ); // ---- without this line memory usage doesn't go up rapidly every second.
DeleteObject( hbmReturn ); // ---- returns non-zero which would point out that it was freed properly.
delete grPhoto;
}
Thanks for any help!
Regards.
I guess you should use DeleteObject.
Here is how the code should look like in my opinion:
// ..............
bmPhoto.GetHBITMAP( colorW, &hbmReturn );
HBITMAP prev = m_BitmapPreview.SetBitmap( hbmReturn ); // ---- without this line memory usage doesn't go up rapidly every second.
if (NULL != prev)
{
DeleteObject(prev); // *** do not forget to delete the previously associated bitmap
}
DeleteObject( hbmReturn ); // ---- returns non-zero which would point out that it was freed properly.
// .........
Related
I am trying to create a program that will capture a full screen directx application, look for a specific set of pixels on the screen and if it finds it then draw an image on the screen.
I have been able to set up the application to capture the screen the directx libraries using the code the answer for this question Capture screen using DirectX
In this example the code saves to the harddrive using the IWIC libraries. I would rather manipulate the pixels instead of saving it.
After I have captured the screen and have a LPBYTE of the entire screen pixels I am unsure how to crop it to the region I want and then being able to manipulate the pixel array. Is it just a multi dimensional byte array?
The way I think I should do it is
Capture screen to IWIC bitmap (done).
Convert IWIC bitmap to ID2D1 bitmap using ID2D1RenderTarget::CreateBitmapFromWicBitmap
Create new ID2D1::Bitmap to store partial image.
Copy region of the ID2D1 bitmap to a new bitmap using ID2D1::CopyFromBitmap.
Render back onto screen using ID2D1 .
Any help on any of this would be so much appreciated.
Here is a modified version of the original code that only captures a portion of the screen into a buffer, and also gives back the stride. Then it browses all the pixels, dumps their colors as a sample usage of the returned buffer.
In this sample, the buffer is allocated by the function, so you must free it once you've used it:
// sample usage
int main()
{
LONG left = 10;
LONG top = 10;
LONG width = 100;
LONG height = 100;
LPBYTE buffer;
UINT stride;
RECT rc = { left, top, left + width, top + height };
Direct3D9TakeScreenshot(D3DADAPTER_DEFAULT, &buffer, &stride, &rc);
// In 32bppPBGRA format, each pixel is represented by 4 bytes
// with one byte each for blue, green, red, and the alpha channel, in that order.
// But don't forget this is all modulo endianness ...
// So, on Intel architecture, if we read a pixel from memory
// as a DWORD, it's reversed (ARGB). The macros below handle that.
// browse every pixel by line
for (int h = 0; h < height; h++)
{
LPDWORD pixels = (LPDWORD)(buffer + h * stride);
for (int w = 0; w < width; w++)
{
DWORD pixel = pixels[w];
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
}
}
// get pixel at 50, 50 in the buffer, as #ARGB
DWORD pixel = GetBGRAPixel(buffer, stride, 50, 50);
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
SavePixelsToFile32bppPBGRA(width, height, stride, buffer, L"test.png", GUID_ContainerFormatPng);
LocalFree(buffer);
return 0;;
}
#define GetBGRAPixelBlue(p) (LOBYTE(p))
#define GetBGRAPixelGreen(p) (HIBYTE(p))
#define GetBGRAPixelRed(p) (LOBYTE(HIWORD(p)))
#define GetBGRAPixelAlpha(p) (HIBYTE(HIWORD(p)))
#define GetBGRAPixel(b,s,x,y) (((LPDWORD)(((LPBYTE)b) + y * s))[x])
int main()
HRESULT Direct3D9TakeScreenshot(UINT adapter, LPBYTE *pBuffer, UINT *pStride, const RECT *pInputRc = nullptr)
{
if (!pBuffer || !pStride) return E_INVALIDARG;
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr;
IDirect3DDevice9 *device = nullptr;
IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = { 0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
*pBuffer = NULL;
*pStride = 0;
// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));
LONG width = pInputRc ? (pInputRc->right - pInputRc->left) : mode.Width;
LONG height = pInputRc ? (pInputRc->bottom - pInputRc->top) : mode.Height;
parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = height;
parameters.BackBufferWidth = width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = NULL;
// create device & capture surface (note it needs desktop size, not our capture size)
HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device));
HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));
// get pitch/stride to compute the required buffer size
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
*pStride = rc.Pitch;
HRCHECK(surface->UnlockRect());
// allocate buffer
*pBuffer = (LPBYTE)LocalAlloc(0, *pStride * height);
if (!*pBuffer)
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));
// copy it into our buffer
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
CopyMemory(*pBuffer, rc.pBits, rc.Pitch * height);
HRCHECK(surface->UnlockRect());
cleanup:
if (FAILED(hr))
{
if (*pBuffer)
{
LocalFree(*pBuffer);
*pBuffer = NULL;
}
*pStride = 0;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}
I am new with MFC and try to learn it with a project of MFC dialog base on VS2008. Here are the archivements I have done:
First, I have managed to display a list of pictures from a folder to a Listbox Control. After that, I also handled the click event on each line of the listbox to load and show the picture to the Picture Control(type Bitmap) on the right side. You can see the image below for easy understanding: Please click here for the image of my MFC dialog
Here is the code. Note m_ListCtrl and static_picture are variables of the listbox and the picture control:
void CMyClientDlg::OnLbnSelchangeList1(){
CString imagePath;
m_ListCtrl.GetText(m_ListCtrl.GetCurSel(),imagePath);
CImage picture;
picture.Load(imagePath);
if (!picture.IsNull())
{
float screenWidth = 200, screenHeight = 200;
float imageWidth = picture.GetWidth();
float imageHeight = picture.GetHeight();
//scaling:
float pictureRatio = imageWidth/ imageHeight;
float newImageWidth;
float newImageHeight;
int aligmentX = 0;
int aligmentY = 0;
if (pictureRatio <= 1)
{
newImageWidth = imageWidth*(screenHeight/imageHeight);
newImageHeight = screenHeight;
aligmentX = (screenWidth-newImageWidth)/2;
}
else
{
newImageWidth = screenWidth;
newImageHeight = imageHeight*(screenWidth/imageWidth);
aligmentY = (screenHeight - newImageHeight)/2;
}
//end scaling.
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
CBitmap bitMap;
bitMap.CreateCompatibleBitmap(screenDC, screenWidth, screenHeight);
CBitmap *pob = mDC.SelectObject(&bitMap);
mDC.SetStretchBltMode(HALFTONE);
picture.StretchBlt(mDC.m_hDC, aligmentX, aligmentY, newImageWidth, newImageHeight, 0, 0, imageWidth, imageHeight, SRCCOPY);
mDC.SelectObject(pob);
/*.......code to convert bitmap to BYTE* ........*/
/*.......code to send BYTE* over socket........*/
//display the bit map
static_picture.SetBitmap((HBITMAP)bitMap.Detach());
//clean up
ReleaseDC(screenDC);
}
}
So now I would like to advance one more step, and tried to work with socket... and yes, I successfully sent and received simple char* or CString over socket.
What I want to do is: instead showing the picture on this dialog, it shows the image on the other dialog(server).
Somehow I learned that there are 2 funtions that sound work: SetBitmapBits() and GetBitmapBits() (I honestly just read it on some source and have no idead if they suitable for my goal here).
So, I added this piece of code to turn the above bitmap into array of BYTE bmpBuffer:
BITMAP bmpProperties;
bitMap.GetBitmap(&bmpProperties);
int bmpDemension = bmpProperties.bmWidthBytes*bmpProperties.bmHeight;
BYTE* bmpBuffer=(BYTE*)GlobalAlloc(GPTR, bmpDemension);
bitMap.GetBitmapBits(bmpDemension,bmpBuffer);
Then send that array over socket:
UpdateData(TRUE);
char *socketBuffer = reinterpret_cast<char*>(bmpBuffer);
send(m_ClientSocket, socketBuffer, sizeof(socketBuffer), 0);
//clean up after send
GlobalFree((HGLOBAL)bmpBuffer);
On the other dialog. Note: I have hardcoded the demension of the bitmap to 160000, just to simplify the problem:
void CMyServer2Dlg::OnReceive(){
char *socketBuffer = new char [1025];
int iLen;
iLen = recv(m_sConnected, socketBuffer, 1025, NULL);
if(iLen==SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
BYTE* bmpBuffer = reinterpret_cast<BYTE*>(socketBuffer);
//re-construct the bitmap
CBitmap clone;
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
clone.CreateCompatibleBitmap(screenDC, 200, 200);
clone.SetBitmapBits(160000,bmpBuffer);
//Picture control(type bitmap) has variable "static_picture"
static_picture.SetBitmap((HBITMAP)clone.Detach());
UpdateData(FALSE);
ReleaseDC(screenDC);
GlobalFree((HGLOBAL)bmpBuffer);
}
delete socketBuffer;
And, it just doesn't work... Please tell me where did I mess it up? And sorry for the long post.....
I think the most possible reason is that your receiver doesn't get all data of the picture. I suggest you put a size of the bitmap into the package while sending it, for receiver to get correct size.
Here are some sample code. Be aware they are just for showing the idea, you may need some debugging to make sure they work.
step 1: Pack the size of bitmap. I suppose here the size is less than 64K, so a int is used. If size may be bigger than 64k, you may want to use INT64.
int bmpDemension = bmpProperties.bmWidthBytes*bmpProperties.bmHeight;
int bufferSize = bmpDemension + sizeof(int);
BYTE* bmpBuffer=(BYTE*)GlobalAlloc(GPTR, bufferSize );
bitMap.GetBitmapBits(bmpDemension,bmpBuffer + sizeof(int));
memcpy(bmpBuffer, &bmpDemension, sizeof(int)); // put the size into the head of package.
step 2: Send it out
Be aware, I use bufferSize here, because sizeof(bmpBuffer) returns the pointer size, which is 4, not the space size.
UpdateData(TRUE);
char *socketBuffer = reinterpret_cast<char*>(bmpBuffer);
send(m_ClientSocket, socketBuffer, bufferSize , 0);
//clean up after send
GlobalFree((HGLOBAL)bmpBuffer);
At the receiver side:
First, you read the size of the bitmap, then do receive according to the size of data.
void CMyServer2Dlg::OnReceive(){
char socketBuffer[1025];
int iLen;
iLen = recv(m_sConnected, socketBuffer, sizeof(int), NULL); //read the bigmap size
if(iLen==SOCKET_ERROR)
{
AfxMessageBox("Could not Receive");
}
else
{
int dimension = *((int *) socketBuffer);
char * bitmapBuffer = new char[dimension];
int readSize = dimension;
char * pBuffer = bitmapBuffer;
while (readSize > 0)
{
int sizeToRead = readSize > sizeof(socketBuffer) ? sizeof(socketBuffer) : readSize;
iLen = recv(m_sConnected, socketBuffer, sizeToRead , NULL);
memcpy(pBuffer, socketBuffer, iLen);
pBuffer += iLen;
readSize -= iLen;
}
// when the loop done, you shall have all data in bitmapBuffer.
....
// I leave the remaining code to you.
Again, these code is just to demo the idea.
I am searching for the memory leak(s) in this code.
I am new to GDI+ and I am not sure what I am doing wrong.
The class you see below gets called in a loop in my main function.
Each loop iteration I push an other vector to the function.
Everything is working fine except there is a memory leak.
I tried the program cppCheck to find the leak but it found no memory leaks :/
My last chance to fix the problem is to ask someone who has more experience than me with GDI+
Thank you very much for the help and sorry for the long code :)
#include "helper.h"
Gui::Gui(const TCHAR* fileName) {
this->fileName = fileName;
}
void Gui::drawGui(Gdiplus::Bitmap* image, std::vector<std::wstring> &vec) {
// Init graphics
Gdiplus::Graphics* graphics = Gdiplus::Graphics::FromImage(image);
Gdiplus::Pen penWhite (Gdiplus::Color::White);
Gdiplus::Pen penRed (Gdiplus::Color::Red);
Gdiplus::SolidBrush redBrush(Gdiplus::Color(255, 255, 0, 0));
penRed.SetWidth(8);
unsigned short marginTop = 15;
unsigned short marginLeft = 5;
unsigned short horizontalBarsizeStart = marginLeft + 60;
for (unsigned short iter = 0; iter < 8; iter++) {
// Draw text
std::wstring coreLabel = L"Core " + std::to_wstring(iter) + L':';
Gdiplus::Font myFont(L"Arial", 12);
Gdiplus::PointF origin(marginLeft, marginTop - 10);
graphics->DrawString(coreLabel.c_str(), coreLabel.length(), &myFont, origin, &redBrush);
// Draw CPU lines
unsigned short horizontalBarsizeEnd = horizontalBarsizeStart + std::stoi(vec.at(iter)); // 100 == Max cpu load
graphics->DrawLine(&penRed, horizontalBarsizeStart, marginTop, horizontalBarsizeEnd, marginTop);
// Draw border
Gdiplus::Rect rect(horizontalBarsizeStart, marginTop - 5, 100, 8);
graphics->DrawRectangle(&penWhite, rect);
// Next element
marginTop += 17;
}
}
bool Gui::SetColorBackgroundFromFile(std::vector<std::wstring> &vec) {
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+.
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HDC hdc = GetDC(NULL);
// Load the image. Any of the following formats are supported: BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
Gdiplus::Bitmap* image = Gdiplus::Bitmap::FromFile(this->fileName, false);
if (image == NULL) {
return false;
}
// Draw the gui
this->drawGui(image, vec);
// Get the bitmap handle
HBITMAP hBitmap = NULL;
Gdiplus::Status status = image->GetHBITMAP(RGB(0, 0, 0), &hBitmap);
if (status != Gdiplus::Ok) {
return false;
}
BITMAPINFO bitmapInfo = { 0 };
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Check what we got
int ret = GetDIBits(hdc, hBitmap, 0, 0, NULL, &bitmapInfo, DIB_RGB_COLORS);
if (LOGI_LCD_COLOR_WIDTH != bitmapInfo.bmiHeader.biWidth || LOGI_LCD_COLOR_HEIGHT != bitmapInfo.bmiHeader.biHeight) {
std::cout << "Oooops. Make sure to use a 320 by 240 image for color background." << std::endl;
return false;
}
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biHeight = -bitmapInfo.bmiHeader.biHeight; // this value needs to be inverted, or else image will show up upside/down
BYTE byteBitmap[LOGI_LCD_COLOR_WIDTH * LOGI_LCD_COLOR_HEIGHT * 4]; // we have 32 bits per pixel, or 4 bytes
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by byteBitmap.
ret = GetDIBits(hdc, hBitmap, 0,
-bitmapInfo.bmiHeader.biHeight, // height here needs to be positive. Since we made it negative previously, let's reverse it again.
&byteBitmap,
(BITMAPINFO *)&bitmapInfo, DIB_RGB_COLORS);
LogiLcdColorSetBackground(byteBitmap); // Send image to LCD
// delete the image when done
if (image) {
delete image;
image = NULL;
Gdiplus::GdiplusShutdown(gdiplusToken); // Shutdown GDI+
}
return true;
}
In drawGui() you're leaking the graphics object. This line creates a new Gdiplus::Graphics object:
Gdiplus::Graphics* graphics = Gdiplus::Graphics::FromImage(image);
But nowhere do you call delete graphics to delete it once you're done with it.
In SetColorBackgroundFromFile, you're leaking the DC.
HDC hdc = GetDC(NULL);
This gets a DC for the screen, but nowhere do you call ReleaseDC(NULL, hdc); to free it.
In the same function, you are creating an HBITMAP using the following call:
Gdiplus::Status status = image->GetHBITMAP(RGB(0, 0, 0), &hBitmap);
But nowhere do you call DeleteObject(hBitmap); to free it.
You also have the problem that in case of errors, your code can return without doing necessary cleanup. E.g. if the GetHBITMAP call fails, you return immediately and will leak the image object that you created a few lines above.
I don't have any trouble when I declare
SDL_Surface *dot = NULL;
globally, but if the SDL_Surface is unique to the class I cant set it to NULL, so thought it would be fine if I declare it in the constructor like
dot = load_image( "dot.bmp" );
but I still get a
Unhandled exception at 0x1002b195 in Uber Mario.exe: 0xC0000005: Access violation reading location 0x0000013c.
on the load_image which returns a SDL_Surface*, sometimes that happened to be because the image was bad or a certain img filetype so i tried another image that does work elsewhere but it still errors like this.
i think im just not using the pointers correctly, even though i studied pointers in school and have read facts on them, for some reason i always have trouble with them. load_image returns a *SDL_Surface so i need to use a pointer...i think.
here is the class:
class Character
{
private:
int yVel, xVel;
int xAcc, yAcc;
int spd, maxV;
int JumpPower;
int FacingRight, FacingLeft;//directing status 0 or 1
bool Flying, onGround;
//Type of particle
SDL_Surface *type;
public:
Shine *myShine;
Animation *walking;
SDL_Surface *dot;
//Offsets
SDL_Rect Rect;
Character();
void handle_input();
void move();
void show();
void togglefly();
void jump();
void whereami();// check and set various characters statuses
};
Character::Character()
{
//Set offsets
Rect.x = 150;
Rect.y = 150;
Rect.w = 20;
Rect.h = 20;
yVel = 0;
xVel = 0;
yAcc = 0;
xAcc = 0;
maxV = 30;
spd = 2;
JumpPower = 40;
Flying = true;
myShine = new Shine(Rect.x, Rect.y);
// walking = new Animation("mario.bmp", 3, 0, 0, Rect.w, Rect.h);
dot = new SDL_Surface();
dot = load_image( "dot.bmp" );
myShine->setpos(Rect);
myShine->setRange(Rect.h*1.5);
}
the load image function:
SDL_Surface *load_image( std::string filename )
{
//The image that's loaded
SDL_Surface* loadedImage = NULL;
//The optimized surface that will be used
SDL_Surface* optimizedImage = NULL;
//Load the image
loadedImage = IMG_Load( filename.c_str() );
//If the image loaded
if( loadedImage != NULL )
{
//Create an optimized surface
optimizedImage = SDL_DisplayFormat( loadedImage ); //EXCEPTION OCCURES HERE
//Free the old surface
SDL_FreeSurface( loadedImage );
//If the surface was optimized
if( optimizedImage != NULL )
{
//Color key surface
SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF ) );
}
}
//Return the optimized surface
return optimizedImage;
init function
bool init()
{
//Initialize all SDL subsystems
if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
{
return false;
}
//Set up the screen
screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
//If there was an error in setting up the screen
if( screen == NULL )
{
return false;
}
//Set the window caption
SDL_WM_SetCaption( "Particle Test", NULL );
//Seed random
srand( SDL_GetTicks() );
//If everything initialized fine
return true;
}
By the way, aren't you leaking resources executing that code:
dot = new SDL_Surface();
dot = load_image( "dot.bmp" );
Doing load_image causes you to loose pointer to SDL_Surface() object hence you cannot delete it later.
Answer to your main issue
Call SDL_Init before using SDL_DisplayFormay and it should work. Citation from SDL documentation.
Newbie hint
You have to call SDL_Init before using the SDL_DisplayFormat function. If you don't, your program will crash with an access violation.
You know, dot is a pointer,and it was allocated dynamiclly.
SDL_Surface *dot=new SDL_Surface();means that you allocate a storage space in heap,and dot index this space.
And,the function load_image(string) return a SDL_Surface type value,that is an object,the return object was assignmented to the pointer dot,then pointer dot changed it's direction,then this makes a memory leak.
You can modify the program by this:
SDL_Surface *dot;
dot = load_image("dot.bmp");
thank you.
I would like to display in a MFC application, frames that I capture from an avi file with OpenCV (cvCaptureFromAVI function).
I'm new to MFC but feel like I'm close to making it work. But instead of the frames being displayed in the picture box they are displayed in a new window.
cvGetWindowName returns always a null value.
There is my code:
CWnd* hPic = 0;
hPic = GetDlgItem(IDC_STATICPIC1);
const char* szWindName = cvGetWindowName(hPic->GetSafeHwnd());
cvShowImage(szWindName, frame_copy);
So I found something to make it work after long researches.
The solution is to create the window and then insert it inside the picture box. I'm not sure it's good practice but I haven't found anything better for now.
cvNamedWindow("IDC_STATIC_OUTPUT", 0);
cvResizeWindow("IDC_STATIC_OUTPUT", 420, 240);
HWND hWnd = (HWND) cvGetWindowHandle("IDC_STATIC_OUTPUT");
HWND hParent = ::GetParent(hWnd);
::SetParent(hWnd, GetDlgItem(IDC_PIC1)->m_hWnd);
::ShowWindow(hParent, SW_HIDE);
cvShowImage("IDC_STATIC_OUTPUT", frame_copy);
In this case the picture box is called IDC_PIC1 and frame_copy is a OpenCV IplImage.
Hope this helps somebody.
Using the following code you can convert Mat to CImage and then display CImage everywhere you want:
int Mat2CImage(Mat *mat, CImage &img){
if(!mat || mat->empty())
return -1;
int nBPP = mat->channels()*8;
img.Create(mat->cols, mat->rows, nBPP);
if(nBPP == 8)
{
static RGBQUAD pRGB[256];
for (int i = 0; i < 256; i++)
pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
img.SetColorTable(0, 256, pRGB);
}
uchar* psrc = mat->data;
uchar* pdst = (uchar*) img.GetBits();
int imgPitch = img.GetPitch();
for(int y = 0; y < mat->rows; y++)
{
memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
psrc += mat->step;
pdst += imgPitch;
}
return 0;
}
NOTE: If you use the StretchDIBits() method with the BITMAPINFO approach, you MUST be aware that StretchDIBits() expects the raw OpenCV Mat::data pointer to have row lengths in even multiples of 4 bytes! If not, you'll get freaky shearing when you try to copy the data to the DC via StretchDIBits() - where the image is not only sheered along an angle, but the colors are all be trashed as well.
Here is my completely working edition of the code, which also supports maintaining image aspect ratio in the target control's rectangle. It can probably be made a bit faster, but it works for now:
void AdjustAspectImageSize( const Size& imageSize,
const Size& destSize,
Size& newSize )
{
double destAspectRatio = float( destSize.width ) / float( destSize.height );
double imageAspectRatio = float( imageSize.width ) / float( imageSize.height );
if ( imageAspectRatio > destAspectRatio )
{
// Margins on top/bottom
newSize.width = destSize.width;
newSize.height = int( imageSize.height *
( double( destSize.width ) / double( imageSize.width ) ) );
}
else
{
// Margins on left/right
newSize.height = destSize.height;
newSize.width = int( imageSize.width *
( double( destSize.height ) / double( imageSize.height ) ) );
}
}
void DrawPicToHDC( Mat cvImg,
UINT nDlgID,
bool bMaintainAspectRatio /* =true*/ )
{
// Get the HDC handle information from the ID passed
CDC* pDC = GetDlgItem(nDlgID)->GetDC();
HDC hDC = pDC->GetSafeHdc();
CRect rect;
GetDlgItem(nDlgID)->GetClientRect(rect);
Size winSize( rect.right, rect.bottom );
// Calculate the size of the image that
// will fit in the control rectangle.
Size origImageSize( cvImg.cols, cvImg.rows );
Size imageSize;
int offsetX;
int offsetY;
if ( ! bMaintainAspectRatio )
{
// Image should be the same size as the control's rectangle
imageSize = winSize;
}
else
{
Size newSize;
_AdjustAspectImageSize( origImageSize,
winSize,
imageSize );
}
offsetX = ( winSize.width - imageSize.width ) / 2;
offsetY = ( winSize.height - imageSize.height ) / 2;
// Resize the source to the size of the destination image if necessary
Mat cvImgTmp;
resize( cvImg,
cvImgTmp,
imageSize,
0,
0,
INTER_AREA );
// To handle our Mat object of this width, the source rows must
// be even multiples of a DWORD in length to be compatible with
// SetDIBits(). Calculate what the correct byte width of the
// row should be to be compatible with SetDIBits() below.
int stride = ( ( ( ( imageSize.width * 24 ) + 31 ) & ~31 ) >> 3 );
// Allocate a buffer for our DIB bits
uchar* pcDibBits = (uchar*) malloc( imageSize.height * stride );
if ( pcDibBits != NULL )
{
// Copy the raw pixel data over to our dibBits buffer.
// NOTE: Can setup cvImgTmp to add the padding to skip this.
for ( int row = 0; row < cvImgTmp.rows; ++row )
{
// Get pointers to the beginning of the row on both buffers
uchar* pcSrcPixel = cvImgTmp.ptr<uchar>(row);
uchar* pcDstPixel = pcDibBits + ( row * stride );
// We can just use memcpy
memcpy( pcDstPixel,
pcSrcPixel,
stride );
}
// Initialize the BITMAPINFO structure
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = cvImgTmp.cols;
bitInfo.bmiHeader.biHeight = -cvImgTmp.rows;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0; //winSize.height * winSize.width * * 3;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
// Add header and OPENCV image's data to the HDC
StretchDIBits( hDC,
offsetX,
offsetY,
cvImgTmp.cols,
cvImgTmp.rows,
0,
0,
cvImgTmp.cols,
cvImgTmp.rows,
pcDibBits,
& bitInfo,
DIB_RGB_COLORS,
SRCCOPY );
free(pcDibBits);
}
ReleaseDC(pDC);
}
int DrawImageToHDC(IplImage* img, HDC hdc, int xDest, int yDest, UINT iUsage, DWORD rop)
char m_chBmpBuf[2048];
BITMAPINFO *m_pBmpInfo = 0;
m_pBmpInfo = (BITMAPINFO*)m_chBmpBuf;
m_pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_pBmpInfo->bmiHeader.biWidth = img->width;
m_pBmpInfo->bmiHeader.biHeight = -img->height;
m_pBmpInfo->bmiHeader.biBitCount = 24;
m_pBmpInfo->bmiHeader.biPlanes = 1;
m_pBmpInfo->bmiHeader.biCompression = BI_RGB;
m_pBmpInfo->bmiHeader.biSizeImage = 0;
m_pBmpInfo->bmiHeader.biXPelsPerMeter = 0;
m_pBmpInfo->bmiHeader.biYPelsPerMeter = 0;
m_pBmpInfo->bmiHeader.biClrUsed = 0;
m_pBmpInfo->bmiHeader.biClrImportant = 0;
return StretchDIBits(hdc, xDest, yDest, img->width, img->height, 0, 0,
img->width, img->height, img->imageData, m_pBmpInfo, DIB_RGB_COLORS, SRCCOPY);
Usage: DrawImageToHDC(img, pDC->m_hDC, Area.left, Area.top, DIB_RGB_COLORS, SRCCOPY);