I'm encountering the following problem. I wolud very much appreciate your help.
Description of the problem
MFC Desktop Application
VisualStudio 2010
Icons are supposed to be displayed in a CListCtrl as a CImageList.
It usually works fine except when using RemoteDesktop with the OS Windows 2008 / 2003.
In these particular cases the icons are just displayed as a black square.
Connecting with VMware vSphere (alternative to RemoteDesktop) -> Icons are shown appropriately
Remotdesktop on Windows XP / 7 / 8 -> Icons are shown appropriately
Code:
OnInitDialog()
{
// Setting the symbol for this dialog-field. This is done automatically
// if the mainwindow of the application is no dialog-field.
SetIcon(m_hIcon, TRUE); // using big symbol
SetIcon(m_hIcon, FALSE); // using small symbol
// TODO: appending additional initialization
InitList(); //
m_list.SetImageList(imageList, LVSIL_NORMAL);
//captions
CString buf = _T("testitem");
m_list.InsertItem(0, buf, 0);
m_list.SetItemData(0, (DWORD) 0);
m_list.SetItemText(0, 0, _T("0"));
m_list.SetItemState(0, 2, LVIS_SELECTED);
m_list.EnsureVisible(0, FALSE);
}
void CDispomarkierungTestDlg::InitList()
{
imageList = new CImageList();
int err = imageList->Create(16, 16, ILC_COLOR32 | ILC_MASK, 1, 1); //ILC_COLOR16 , ILC_COLOR8 , ... getested
int id = 40;
int requiredLength = 3154;
LPCSTR picInCharsFromDB = GetBitmapAsText();
int width = 24;
int heigth = 24;
int pixel = 32;
int planes = 1;
BYTE *picInBytes = new BYTE[requiredLength];
int lengthVariable = requiredLength;
int* lengthPointer;
lengthPointer = &lengthVariable;
Base64Decode(picInCharsFromDB, requiredLength, picInBytes, lengthPointer);
HANDLE hBitMap = CreateBitmap(width, heigth, planes, pixel, picInBytes);
BITMAP bitmap;
GetObject(hBitMap,sizeof(BITMAP),&bitmap);
CImage image;
image.Attach((HBITMAP)hBitMap);
CBitmap icon;
icon.Attach((HBITMAP)hBitMap);
CBitmap* bitmappointer;
bitmappointer = &icon;
BITMAP bmp;
icon.GetBitmap(&bmp);
CClientDC dcClient(this);
CDC memDC;
memDC.CreateCompatibleDC(&dcClient);
HBITMAP hbmMask = ::CreateCompatibleBitmap(memDC.GetSafeHdc(), width, heigth);
ICONINFO ii = {0};
ii.fIcon = TRUE;
ii.hbmColor = icon;
ii.hbmMask = hbmMask;
HICON hIcon = ::CreateIconIndirect(&ii);
::DeleteObject(hbmMask);
err = imageList->Add(hIcon);
}
In the real application, the source is a database which saves the Bitmap as a string. That’s the reason for the complicated code during the loading. Loading the images as a resource is possible, but in this case no option.
Drivers of server and client are updated to latest version.
I added the project here https://www.hidrive.strato.com/lnk/gRuMg38R. Thanks for helping out
Related
I have the following method to print a bitmap which did work perfectly but now it prints the area of the bitmap all in black. I've tested my test app which was compiled on my PC on another PC and it prints the bitmap perfectly. I've debugged it and it is opening the bitmap file because its reading the correct dimensions. I'm at a loss to see what has happen, Any advice would be greatly appreciated. Thanks in advance.
void CTestAppPrintDlg::OnBnClickedButton1()
{
CString path;
path = "Test1.bmp";
PrintBitmap(path);
}
void CTestAppPrintDlg::PrintBitmap(LPCTSTR filename) {
CPrintDialog printDlg(FALSE);
printDlg.GetDefaults();
return;
CDC dc;
if (!dc.Attach(printDlg.GetPrinterDC())) {
AfxMessageBox(_T("No printer found!")); return;
}
dc.m_bPrinting = TRUE;
DOCINFO di;
// Initialise print document details
::ZeroMemory(&di, sizeof(DOCINFO));
di.cbSize = sizeof(DOCINFO);
di.lpszDocName = filename;
BOOL bPrintingOK = dc.StartDoc(&di); // Begin a new print job
// Get the printing extents
// and store in the m_rectDraw field of a
// CPrintInfo object
CPrintInfo Info;
Info.SetMaxPage(1); // just one page
int maxw = dc.GetDeviceCaps(HORZRES);
int maxh = dc.GetDeviceCaps(VERTRES);
Info.m_rectDraw.SetRect(0, 0, maxw, maxh);
for (UINT page = Info.GetMinPage(); page <=
Info.GetMaxPage() && bPrintingOK; page++) {
dc.StartPage(); // begin new page
Info.m_nCurPage = page;
CBitmap bitmap;
// LoadImage does the trick here, it creates a DIB section
// You can also use a resource here
// by using MAKEINTRESOURCE() ... etc.
if (!bitmap.Attach(::LoadImage(
::GetModuleHandle(NULL), filename, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE))) {
AfxMessageBox(_T("Error loading bitmap!")); return;
}
BITMAP bm;
bitmap.GetBitmap(&bm);
int w = bm.bmWidth;
int h = bm.bmHeight;
// create memory device context
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap *pBmp = memDC.SelectObject(&bitmap);
memDC.SetMapMode(dc.GetMapMode());
dc.SetStretchBltMode(HALFTONE);
// now stretchblt to maximum width on page
dc.StretchBlt(0, 0, w, h, &memDC, 0, 0, w, h, SRCCOPY);
// clean up
memDC.SelectObject(pBmp);
bPrintingOK = (dc.EndPage() > 0); // end page
}
if (bPrintingOK)
dc.EndDoc(); // end a print job
else dc.AbortDoc(); // abort job.
}
Thanks for the person who gave me a negative rating. This was very helpful!
I've found that it was nothing to do with my code and the cause was the Windows Update KB5000802. I uninstalled this update and it now works.
This is my method for loading a transparent PNG file into a buffer:
/* static */ void CRibbonButton::LoadImageFromRelativeFilespec(HGLOBAL& rhDIB, bool bLarge,
const CString& rstrImageRelFilespec, UINT32& ruDIBW, int& ruDIBH)
{
USES_CONVERSION;
using namespace RibbonBar ;
// Clear any existing image away.
if (rhDIB != NULL)
::GlobalFree(rhDIB);
// Build the correct filespec.
CString strThisEXE = _T("");
::GetModuleFileName(AfxGetInstanceHandle(),
strThisEXE.GetBuffer(_MAX_PATH + 1),_MAX_PATH);
strThisEXE.ReleaseBuffer();
LPCTSTR lpszPath = (LPCTSTR)strThisEXE ;
LPTSTR lpszFilename = ::PathFindFileName(lpszPath);
CString strPath = strThisEXE.Left( (int)(lpszFilename - lpszPath) );
CString strFilespec = strPath ;
::PathAppend(strFilespec.GetBuffer(_MAX_PATH + 1), rstrImageRelFilespec);
strFilespec.ReleaseBuffer();
HISSRC hSrc = is6_OpenFileSource(CT2A((LPCTSTR)strFilespec));
if (hSrc)
{
// read it
UINT32 w, h;
rhDIB = is6_ReadImage(hSrc, &w, &h, 2, 0); // the "2" = load directly to DIB, in the lowest bit depth possible.
if (rhDIB)
{
// get the dimensions
is6_DIBWidth((BITMAPINFOHEADER *)rhDIB, &ruDIBW);
is6_DIBHeight((BITMAPINFOHEADER *)rhDIB, &ruDIBH);
UINT32 bc;
is6_DIBBitCount((BITMAPINFOHEADER *)rhDIB, &bc);
is6_ClearJPGInputMarkers();
}
else
{
AfxMessageBox(_T("Can't read that image"));
}
is6_CloseSource(hSrc);
}
}
And this is the rendering code:
/* virtual */ void CRibbonButton::PaintData(CDC& rDC)
{
CDC dcMem ;
dcMem.CreateCompatibleDC(NULL); // Screen.
const CRect& rrctImage = GetImageBounds();
if (m_hDIB)
{
// draw to a memory DC
CDC memDC;
if (memDC.CreateCompatibleDC(&rDC))
{
CBitmap bmp;
if (bmp.CreateCompatibleBitmap(&rDC, rrctImage.Width(), rrctImage.Height()))
{
CBitmap *ob = memDC.SelectObject(&bmp);
if (ob)
{
// dark red background
memDC.FillSolidRect(CRect(rrctImage.left, rrctImage.top, rrctImage.Width(), rrctImage.Height()), RibbonBar::kBackColour);
// stretchDrawDIB is typically the fastest way to draw an image from ImgSource.
BOOL ok = is6_StretchDrawDIB(memDC.m_hDC, (BITMAPINFOHEADER *)m_hDIB, 0, 0, m_uDIBW, m_uDIBH);
if (!ok)
{
memDC.SetBkMode(TRANSPARENT);
memDC.SetTextColor(RGB(255, 255, 255));
memDC.TextOut(rrctImage.left, rrctImage.top, _T("X"));
}
// copy this to the window
rDC.BitBlt(rrctImage.left, rrctImage.top, rrctImage.Width(), rrctImage.Height(), &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(ob);
}
}
}
}
dcMem.DeleteDC();
}
It is not drawing the transparent PNG file correctly. I always end up with a black background.
I am using the ISSource libraries for rendering. But the company is now out of business. I am using version 6 library.
Update
Based on the answer I am now loading and rendering the image like this:
CRect rct;
CImage img;
img.Load(_T("d:\\Publishers.png"));
rct.SetRect(rrctImage.left, rrctImage.top, rrctImage.left + img.GetWidth(), rrctImage.top + img.GetHeight());
img.TransparentBlt(rDC.GetSafeHdc(), rct, RGB(255,255,255));
But why do I still get black for where the transparency was set?
If I don't pass RGB(255,255,255) as the last parameter
and use the default I get an exception.
Update
According to the documentation for TransparentBit:
TransparentBlt is supported for source bitmaps of 4 bits per pixel and 8 bits per pixel. Use CImage::AlphaBlend to specify 32 bits-per-pixel bitmaps with transparency.
So, I have stopped using:
img.TransparentBlt(rDC.GetSafeHdc(), rct);
Now I am using:
img.AlphaBlend(rDC.GetSafeHdc(), rct.left, rct.top, rct.Width(), rct.Height(), rct.left, rct.top, rct.Width(), rct.Height(), 0xff, AC_SRC_OVER);
I don't see anything. I confirm the coordinates are right by doing:
CBrush br;
br.CreateStockObject(BLACK_BRUSH);
rDC.FrameRect(rct, &br);
Why do I not see anything?
This is much to complicate. There are existing methods in CImage.
Check out CImage::AlphaBlend or CImage::TransparentBlt.
AlphaBlend: The Dst fields are the coordinates in your DC. the Src values are inside your picture. Usually they start with 0,0 and have the width and height as values. Is xSrc/ySrc are not 0 you have an offset in the source.
I would like to create 32 bit color icons programmatically using C++ and Win API. For this purpose I use the following code which I found here.
HICON CreateSolidColorIcon(COLORREF iconColor, int width, int height)
{
// Obtain a handle to the screen device context.
HDC hdcScreen = GetDC(NULL);
// Create a memory device context, which we will draw into.
HDC hdcMem = CreateCompatibleDC(hdcScreen);
// Create the bitmap, and select it into the device context for drawing.
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, width, height);
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmp);
// Draw your icon.
//
// For this simple example, we're just drawing a solid color rectangle
// in the specified color with the specified dimensions.
HPEN hpen = CreatePen(PS_SOLID, 1, iconColor);
HPEN hpenOld = (HPEN)SelectObject(hdcMem, hpen);
HBRUSH hbrush = CreateSolidBrush(iconColor);
HBRUSH hbrushOld = (HBRUSH)SelectObject(hdcMem, hbrush);
Rectangle(hdcMem, 0, 0, width, height);
SelectObject(hdcMem, hbrushOld);
SelectObject(hdcMem, hpenOld);
DeleteObject(hbrush);
DeleteObject(hpen);
// Create an icon from the bitmap.
//
// Icons require masks to indicate transparent and opaque areas. Since this
// simple example has no transparent areas, we use a fully opaque mask.
HBITMAP hbmpMask = CreateCompatibleBitmap(hdcScreen, width, height);
ICONINFO ii;
ii.fIcon = TRUE;
ii.hbmMask = hbmpMask;
ii.hbmColor = hbmp;
HICON hIcon = CreateIconIndirect(&ii);
DeleteObject(hbmpMask);
// Clean-up.
SelectObject(hdcMem, hbmpOld);
DeleteObject(hbmp);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
// Return the icon.
return hIcon;
}
In principle the code works and I can use it to create colored icons at runtime using the Win API. However, I have some problems and questions about that code (and creating icons in general) which I would like to discuss.
The icons created with this function don't seem to be of 32 bit color depth. If I use a color like RGB(218, 112, 214) I would expect it to be some light purple. However, the actual displayed color is gray. How can I change the code such that the color is really 32 bit RGB?
The icon created is completly filled with the color, I would like to have a thin black boarder around it... how can this be achieved?
In the MSDN documentation (a bit downwards) it is mentioned that "Before closing, your application must use DestroyIcon to destroy any icon it created by using CreateIconIndirect. It is not necessary to destroy icons created by other functions. " However, in the documentation for e.g. CreateIcon in MSDN it is said that "When you are finished using the icon, destroy it using the DestroyIcon function." which is pretty much a contradiction. When do I actually have to destroy the icon?
Do these rules then also apply when I add the icon to an image list and this list to a combobox? I.e. do I have to clean up the image list and each associated icon?
Any help is highly appreciated.
When do I actually have to destroy the icon?
read about DestroyIcon
It is only necessary to call DestroyIcon for icons and cursors
created with the following functions: CreateIconFromResourceEx (if
called without the LR_SHARED flag), CreateIconIndirect, and
CopyIcon. Do not use this function to destroy a shared icon. A
shared icon is valid as long as the module from which it was loaded
remains in memory. The following functions obtain a shared icon.
LoadIcon
LoadImage (if you use the LR_SHARED flag)
CopyImage (if you use the LR_COPYRETURNORG flag and the hImage parameter is a shared icon)
CreateIconFromResource
CreateIconFromResourceEx (if you use the LR_SHARED flag)
so you need call DestroyIcon for not shared icon, when you are finished using it
ComboBoxEx not destroy image list which you assign to it with CBEM_SETIMAGELIST - so this image list must be valid until ComboBoxEx valid and you must destroy it yourself later.
ImageList_AddIcon
Because the system does not save hicon, you can destroy it after the
macro returns
in other words ImageList_AddIcon make copy of your icon, and you can destroy your original icon, after macro return
for create 32 bit color icon try code like this:
HICON CreateGradientColorIcon(COLORREF iconColor, int width, int height)
{
HICON hIcon = 0;
ICONINFO ii = { TRUE };
ULONG n = width * height;
if (PULONG lpBits = new ULONG[n])
{
PULONG p = lpBits;
ULONG x, y = height, t;
do
{
x = width, t = --y << 8;
do
{
*p++ = iconColor | ((t * --x) / n << 24);
} while (x);
} while (y);
if (ii.hbmColor = CreateBitmap(width, height, 1, 32, lpBits))
{
if (ii.hbmMask = CreateBitmap(width, height, 1, 1, 0))
{
hIcon = CreateIconIndirect(&ii);
DeleteObject(ii.hbmMask);
}
DeleteObject(ii.hbmColor);
}
delete [] lpBits;
}
return hIcon;
}
when I draw (DrawIconEx(, DI_IMAGE|DI_MASK)) this icon over green mesh I view next:
To everyone who has stumbled upon this solution, I am simply posting a little bit more of a documented solution to RbMm's answer. This is basically the same as his solution (maybe not as performant, I'm not sure):
static HICON CreateIconFromBytes(HDC DC, int width, int height, uint32* bytes) {
HICON hIcon = NULL;
ICONINFO iconInfo = {
TRUE, // fIcon, set to true if this is an icon, set to false if this is a cursor
NULL, // xHotspot, set to null for icons
NULL, // yHotspot, set to null for icons
NULL, // Monochrome bitmap mask, set to null initially
NULL // Color bitmap mask, set to null initially
};
uint32* rawBitmap = new uint32[width * height];
ULONG uWidth = (ULONG)width;
ULONG uHeight = (ULONG)height;
uint32* bitmapPtr = rawBitmap;
for (ULONG y = 0; y < uHeight; y++) {
for (ULONG x = 0; x < uWidth; x++) {
// Bytes are expected to be in RGB order (8 bits each)
// Swap G and B bytes, so that it is in BGR order for windows
uint32 byte = bytes[x + y * width];
uint8 A = (byte & 0xff000000) >> 24;
uint8 R = (byte & 0xff0000) >> 16;
uint8 G = (byte & 0xff00) >> 8;
uint8 B = (byte & 0xff);
*bitmapPtr = (A << 24) | (R << 16) | (G << 8) | B;
bitmapPtr++;
}
}
iconInfo.hbmColor = CreateBitmap(width, height, 1, 32, rawBitmap);
if (iconInfo.hbmColor) {
iconInfo.hbmMask = CreateCompatibleBitmap(DC, width, height);
if (iconInfo.hbmMask) {
hIcon = CreateIconIndirect(&iconInfo);
if (hIcon == NULL) {
Log::Warning("Failed to create icon.");
}
DeleteObject(iconInfo.hbmMask);
} else {
Log::Warning("Failed to create color mask.");
}
DeleteObject(iconInfo.hbmColor);
} else {
Log::Warning("Failed to create bitmap mask.");
}
delete[] rawBitmap;
return hIcon;
}
This solution will work with STB image library for loading images. So you can literally just load an image with stb, then pass the byte data to this function, and you will get an icon as a result. I had a little bit of trouble setting the icon as well, and eventually did this to get that to work:
HICON icon = CreateIconFromBytes(DC, image.m_Width, image.m_Height, image.m_Pixels);
SendMessage(WND, WM_SETICON, ICON_SMALL, (LPARAM)icon);
SendMessage(WND, WM_SETICON, ICON_BIG, (LPARAM)icon);
SendMessage(WND, WM_SETICON, ICON_SMALL2, (LPARAM)icon);
The only thing you should note about this is that you should probably use 3 different sized icons for the SendMessage() functions, but other than that this worked good for me :)
Edit:
Here's the links to official MSDN documentation as well.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createiconindirect
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createcompatiblebitmap
https://learn.microsoft.com/en-us/windows/win32/menurc/using-icons
I have several child window on the main window, and some are GDI windows, and some are opengl rendered window, one function is to capture the image with a rect (may cover different combination of windows). This function works fine under windows xp. However, under windows 7, all opengl rendered windows are black. I did some research and someone said that the gdi cannot directly access the frame buffer via the window DC, and has to use glReadPixels to combine the bitmap. This approach however is awkward since I have to combine each window in that rect separately. Anyone has a better option for me?
Here is my code for catching a bmp:
void MainWndClass::catchBmp(const char* path_fn, bool drawAreaOnly /*=0*/)
{
CDC *pDC=GetDC();
int BitPerPixel = pDC->GetDeviceCaps(BITSPIXEL);
int Left,Top,Width,Height;
if (drawAreaOnly)
{
Left = rBDWin.left;
Top = rBDWin.top;
Width = rBDWin.right-rBDWin.left;
Width = Width/4*4;
Height = rBDWin.bottom-rBDWin.top;
Height = Height/4*4;
}
else
{
Left=rbmpWin.left;
Top=rbmpWin.top;
Width=rbmpWin.right-rbmpWin.left;
Width=Width/4*4;
Height=rbmpWin.bottom-rbmpWin.top;
Height=Height/4*4;
}
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap memBitmap, *oldmemBitmap;
memBitmap.CreateCompatibleBitmap(pDC, Width, Height);
//it seems does no work
//short bpp=24;
if(BitPerPixel>24) BitPerPixel=24;
memBitmap.SetBitmapBits(2,&BitPerPixel);
oldmemBitmap = memDC.SelectObject(&memBitmap);
//copy the bitmap from the pDC (source)
memDC.BitBlt(0, 0, Width, Height, pDC, Left, Top, SRCCOPY);
/*
CString title;
GetWindowText(title);
memDC.SetBkMode(TRANSPARENT);
memDC.TextOut(64,4,title);
*/
BITMAP bmp;
memBitmap.GetBitmap(&bmp);
if(bmp.bmBitsPixel>24)
{
bmp.bmBitsPixel=24;
//bmp.bmWidthBytes=bmp.bmWidth*3;
}
bmp.bmWidthBytes=bmp.bmWidth*(bmp.bmBitsPixel/8);
FILE *fp=NULL;
//path_fn+=".bmp";
fp=fopen((LPCTSTR)path_fn,"w+b");
BITMAPINFOHEADER bih = {0};
bih.biBitCount = bmp.bmBitsPixel;
bih.biCompression = BI_RGB;
bih.biHeight = bmp.bmHeight;
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;
bih.biWidth = bmp.bmWidth;
BITMAPFILEHEADER bfh = {0};
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight;
bfh.bfType = (WORD)0x4d42;
if(fp)
{
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
}
byte * p = new byte[bmp.bmWidthBytes * bmp.bmHeight];
//copy the bits to the buffer
int ret=GetDIBits(memDC.m_hDC, (HBITMAP) memBitmap.m_hObject, 0, Height, p,
(LPBITMAPINFO) &bih, DIB_RGB_COLORS);
if(fp)
fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp);
delete [] p;
if(fp)
fclose(fp);
memDC.SelectObject(oldmemBitmap);
}
The opengl window is configured as:
PIXELFORMATDESCRIPTOR pixelDesc =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0,0,0,0,0,0,
0,
0,
0,
0,0,0,0,
32,//
0,
0,
PFD_MAIN_PLANE,
0,
0,0,0
};
I want to emphysis the fact again:
it works under xp, but not under win7 (the opengl window part is black)
Hello I finally got a perfect solution for this. According to information given by Mats Pertersson, and I am pretty sure that is the reason since it matches the facts. Windows 7 introduces the transparent window appearance, and each window is not the final results. Final results (the screen outputs) are composed from all the windows. So I came the solution, capture the final screen instead of capture the main window. And it works perfect under both xp and win 7.
Main changes: all DC comes from the screen instead of the window, hence relating functions are all changed to global gdi functions.
Here is the code:
catchBmp(const char* path_fn, bool drawAreaOnly /*=0*/)
{
//CDC *pDC=GetDC();
HDC hdcScreen;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
hdcScreen=::GetDC(NULL);
int BitPerPixel = ::GetDeviceCaps(hdcScreen,BITSPIXEL);
int Left,Top,Width,Height;
if (drawAreaOnly)
{
Left = rBDWin.left;
Top = rBDWin.top;
Width = rBDWin.right-rBDWin.left;
Width = Width/4*4;
Height = rBDWin.bottom-rBDWin.top;
Height = Height/4*4;
}
else
{
Left=rbmpWin.left;
Top=rbmpWin.top;
Width=rbmpWin.right-rbmpWin.left;
Width=Width/4*4;
Height=rbmpWin.bottom-rbmpWin.top;
Height=Height/4*4;
}
hdcMemDC=::CreateCompatibleDC(hdcScreen);
hbmScreen=::CreateCompatibleBitmap(hdcScreen,Width,Height);
if(BitPerPixel>24) BitPerPixel=24;
::SetBitmapBits(hbmScreen,2,&BitPerPixel);
::SelectObject(hdcMemDC,hbmScreen);
BitBlt(hdcMemDC,
0,0,Width,Height,hdcScreen,Left,Top,SRCCOPY);
::GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
if(bmpScreen.bmBitsPixel>24)
{
bmpScreen.bmBitsPixel=24;
}
bmpScreen.bmWidthBytes=bmpScreen.bmWidth*(bmpScreen.bmBitsPixel/8);
FILE *fp=NULL;
fp=fopen((LPCTSTR)path_fn,"w+b");
BITMAPINFOHEADER bih = {0};
bih.biBitCount = bmpScreen.bmBitsPixel;
bih.biCompression = BI_RGB;
bih.biHeight = bmpScreen.bmHeight;
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = bmpScreen.bmWidthBytes * bmpScreen.bmHeight;
bih.biWidth = bmpScreen.bmWidth;
BITMAPFILEHEADER bfh = {0};
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfSize = bfh.bfOffBits + bmpScreen.bmWidthBytes * bmpScreen.bmHeight;
bfh.bfType = (WORD)0x4d42;
if(fp)
{
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
}
byte * p = new byte[bmpScreen.bmWidthBytes * bmpScreen.bmHeight];
GetDIBits(hdcScreen, hbmScreen, 0, Height, p, (LPBITMAPINFO) &bih, DIB_RGB_COLORS);
if(fp)
fwrite(p, 1, bmpScreen.bmWidthBytes * bmpScreen.bmHeight, fp);
delete [] p;
if(fp)
fclose(fp);
::DeleteObject(hbmScreen);
::DeleteObject(hdcMemDC);
::ReleaseDC(NULL,hdcScreen);
//memDC.SelectObject(oldmemBitmap);
}
As a developer working with GPU's on and off (currently "on", but not doing graphigs) for the past ten or so years, I'll try to explain what is going on:
The GPU often have more than one "layer" that it can output to - for example, on modern graphics cards, the mouse cursor lives in a layer of it's own, so that we don't have to redraw things (as used to be the case, the video card/driver would "remember" what was under the mouse-cursor, and the redraw that when you moved the mouse). That layer is on top of the actual graphics on the screen, and they are combined when the frame buffer memory is scanned out - that is, when pixel-colours are sent to the display itself - each layer is read in a defined order, and the colour of the different layers are combined according to their respective alpha-value.
Some OpenGL drivers & hardware find it much easier to draw the 3D to a separate layer, and then combine the two during "scanning out" phase. This sometimes gives better performance, because the GL driver "owns" this layer, and doesn't have to fight with GDI trying to draw to the screen at the same time.
Of course, when GDI reads back the content, it can only read the content that GDI knows about [this is also why the mouse cursor is typically not present in a screen-copy]
I have to find the correct way to attach a scrollbar to a set of images that are created dynamically by the application.
In my code I create the scrollbar but it is not working properly. The main window does not scroll to view all the images.
int currentLength = iImage * (WIDTH + SPACER);
picName.Format(_T("Image %d"),iImage);
CPoint topLeft(currentLength,0);
CPoint bottomRigth(currentLength + (WIDTH), HEIGHT);
CRect miniCRect(topLeft, bottomRigth);
Miniature[iImage] = new CStatic();
Miniature[iImage]->Create(picName, WS_CHILD|WS_VISIBLE|SS_BITMAP, miniCRect, this);
if((bottomRigth.x > 500) && (currentLength <= 500))
{
//creo la scrool bar
CPoint ptnrigin(0,210);
CPoint endptn(bottomRigth.x,230);
CRect workingArea(ptnrigin,endptn);
cs.Create(WS_VISIBLE,workingArea,this,0);
cs.EnableScrollBar(ESB_ENABLE_BOTH);
SCROLLINFO info;
info.cbSize = sizeof(SCROLLINFO);
info.fMask = SIF_ALL;
info.nMin = 1;
info.nMax = 10;
info.nPage = 2;
info.nPos = 5;
info.nTrackPos = 5;
cs.SetScrollInfo(&info);
}
HDC hDCScreen_mini = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
HDC hDCMem_mini = ::CreateCompatibleDC(hDCScreen_mini);
// create a compatible bitmap and select it in the memory DC
HBITMAP hBitmap_mini = ::CreateCompatibleBitmap(hDCScreen_mini, miniCRect.Width(), miniCRect.Height());
HBITMAP hBmpOld_mini = (HBITMAP)::SelectObject(hDCMem_mini, hBitmap_mini);
BitBlt(hDCMem_mini, 0, 0, desktopRect.Width(), desktopRect.Height(), hDCScreen_mini, desktopRect.left, desktopRect.top, dwRop);
Miniature[iImage]->SetBitmap(hBitmap_mini);
Invalidate();
// restore the memory DC and perform cleanup
SelectObject(hDCMem_mini, hBmpOld_mini);
DeleteDC(hDCMem_mini);
DeleteDC(hDCScreen_mini);
//end capture
Can someone help me?
A scrollbar does not scroll your window or its contents. It only provides input from the user to code that you must write to reposition the images. Such code typically uses ScrollWindow to move the visible portion, followed by adding the newly-visible portion in WM_PAINT.
A different approach (that does do the scrolling for you) would be to put the images in an owner-drawn list box.