so what I am trying to do is to have my program take a screenshot and save it on the computer. The part of actually taking the screenshot I will program later, and I am first trying to solve the problem of how to actually save a bmp file on the computer. I found the following code that would help me out with that:
// szPathName : Specifies the pathname
// lpBits : Specifies the bitmap bits
// w : Specifies the image width
// h : Specifies the image height
bool SaveImage(char* szPathName, void* lpBits, int w, int h)
{
//Create a new file for writing
FILE *pFile = fopen(szPathName, "wb");
if(pFile == NULL)
{
return false;
}
BITMAPINFOHEADER BMIH;
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biSizeImage = w * h * 3;
// Create the bitmap for this OpenGL context
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biWidth = w;
BMIH.biHeight = h;
BMIH.biPlanes = 1;
BMIH.biBitCount = 24;
BMIH.biCompression = BI_RGB;
BMIH.biSizeImage = w * h* 3;
BITMAPFILEHEADER bmfh;
int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize;
LONG lImageSize = BMIH.biSizeImage;
LONG lFileSize = nBitsOffset + lImageSize;
bmfh.bfType = 'B'+('M'<<8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
//Write the bitmap file header
UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1,
sizeof(BITMAPFILEHEADER), pFile);
//And then the bitmap info header
UINT nWrittenInfoHeaderSize = fwrite(&BMIH,
1, sizeof(BITMAPINFOHEADER), pFile);
//Finally, write the image data itself
//-- the data represents our drawing
UINT nWrittenDIBDataSize =
fwrite(lpBits, 1, lImageSize, pFile);
fclose(pFile);
return true;
}
So what is the issue.... Well I do not understand the varialbe IpBits. there is a brief explanation of the lpBits in the comments of the code (lpBits: Specifies the bitmap bits)... but I don't know what that actually means. I tried going into msdn and looking into the fopen and fclose functions since fclose is the function that will eventually use the lpbits that I pass to the SaveImage function.... and well it seemed that the lpBits variable in the fclose function varies depending on what variable was passed in the fopen function. I tried to find out what the "wb" of the fopen function means but was unsuccessful (even searching on msdn).
QUESTION: in the case that I use "wb" as the second variable in the fopen function in my previous code, what exactly would the lpBits in the fclose function be? When I ask what exactly would it be, I mean... what type of variable is it (in the code it is placed as void* which basically allows it to be any variable) and I would appriciate any feedback you could give.
Thanks guys!
lpBits refers to an array of bytes with size lImageSize.
Each byte of the array will contain a single color component, in this order: B, G and R: each pixel takes three bytes, one for each color component.
Please note that the code you posted doesn't take into consideration the 4 bytes alignment of each image's row. Each image's row must be aligned on a 4 bytes boundary, so the correct formula for lImageSize is:
lImageSize = h * ((w * 3 + 3) & 0xfffffffc);
You can create the lpbits by yourself:
lpbits = new BYTE[lImageSize];
or by using CreateDIBSection() as stated in the answer from Logicrat
Commenting the code:
// lpBits stand for long pointer bits
// szPathName : Specifies the pathname -> the file path to save the image
// lpBits : Specifies the bitmap bits -> the buffer (content of the) image
// w : Specifies the image width
// h : Specifies the image height
bool SaveImage(char* szPathName, void* lpBits, int w, int h) {
// Create a new file for writing
FILE* pFile = fopen(szPathName, "wb"); // wb -> w: writable b: binary, open as writable and binary
if (pFile == NULL) {
return false;
}
BITMAPINFOHEADER BMIH; // BMP header
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biSizeImage = w * h * 3;
// Create the bitmap for this OpenGL context
BMIH.biSize = sizeof(BITMAPINFOHEADER);
BMIH.biWidth = w;
BMIH.biHeight = h;
BMIH.biPlanes = 1;
BMIH.biBitCount = 24;
BMIH.biCompression = BI_RGB;
BMIH.biSizeImage = w * h * 3;
BITMAPFILEHEADER bmfh; // Other BMP header
int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize;
LONG lImageSize = BMIH.biSizeImage;
LONG lFileSize = nBitsOffset + lImageSize;
bmfh.bfType = 'B' + ('M' << 8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
// Write the bitmap file header // Saving the first header to file
UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile);
// And then the bitmap info header // Saving the second header to file
UINT nWrittenInfoHeaderSize = fwrite(&BMIH, 1, sizeof(BITMAPINFOHEADER), pFile);
// Finally, write the image data itself
//-- the data represents our drawing // Saving the file content in lpBits to file
UINT nWrittenDIBDataSize = fwrite(lpBits, 1, lImageSize, pFile);
fclose(pFile); // closing the file.
return true;
}
Some improvement to substitute the C code with C++:
The improvement were:
Using std::string instead of char* that originally need to be const char*
Using vector instead of void* (could be a problem in the original code, if the width and height provided was wrong or miscalculated the program will read invalid memory because there is no notion of size of lpBits. The content of the file not need to change when saving, adding const-correctness
Using std::ofstream instead of FILE.
Code:
// lpBits stand for long point bits
// szPathName : Specifies the pathname -> the file path to save the image
// lpBits : Specifies the bitmap bits -> the buffer (content of the) image
// w : Specifies the image width
// h : Specifies the image height
bool SaveImage(const std::string& szPathName, const std::vector<char>& lpBits, int w, int h) {
// Create a new file for writing
std::ofstream pFile(szPathName, std::ios_base::binary);
if (!pFile.is_open()) {
return false;
}
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = w;
bmih.biHeight = h;
bmih.biPlanes = 1;
bmih.biBitCount = 24;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = w * h * 3;
BITMAPFILEHEADER bmfh;
int nBitsOffset = sizeof(BITMAPFILEHEADER) + bmih.biSize;
LONG lImageSize = bmih.biSizeImage;
LONG lFileSize = nBitsOffset + lImageSize;
bmfh.bfType = 'B' + ('M' << 8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
// Write the bitmap file header
pFile.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER));
UINT nWrittenFileHeaderSize = pFile.tellp();
// And then the bitmap info header
pFile.write((const char*)&bmih, sizeof(BITMAPINFOHEADER));
UINT nWrittenInfoHeaderSize = pFile.tellp();
// Finally, write the image data itself
//-- the data represents our drawing
pFile.write(&lpBits[0], lpBits.size());
UINT nWrittenDIBDataSize = pFile.tellp();
pFile.close();
return true;
}
Study the Windows API CreateDIBSection(). With this API, Windows will allocate the memory for the pixels you need. When it allocates the memory, it will give you the memory address in a long pointer. That is what "lpbits" refers to - a long pointer to the bits that were allocated.
The "wb" in fopen() means "Write Binary". Without the "b" (i.e., if you use only a "w" in the second parameter), fopen will open the file in text mode, which will cause the file to be written as though it were text, which may change the '\n' character in a system-dependent way.
Here is a constructor I use for class PixMapAny, which is primarily used for off-screen drawing but can also be used to read pixmaps.
PixMapAny::PixMapAny(int width, int height, int depth)
{
m_dc.CreateCompatibleDC(NULL);
m_width = width;
m_height = height;
m_depth = depth;
// The declaration of 'fake' creates a storage area big enough to
// contain a BITMAPINFO structure composed of a BITMAPINFOHEADER
// and a 256-element array of RGBQUAD values.
long fake[266];
LPBITMAPINFO pbmi = (LPBITMAPINFO) fake;
// Initialize the area to all zeros
for(int x = 0; x < 266; x++) fake[x] = 0;
// Fill in the header with the characteristics of the bitmap we want
// to write.
pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader);
pbmi->bmiHeader.biWidth = m_width;
pbmi->bmiHeader.biHeight = -m_height;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 24;
pbmi->bmiHeader.biCompression = BI_RGB;
// Tell the system to allocate room for the pixmap.
// 'ppvbits' receives a pointer to the pixmap memory.
m_dib = CreateDIBSection(m_dc.m_hDC, pbmi, DIB_RGB_COLORS, &m_ppvbits, NULL, 0);
// ____________________________________________________________________________
// Select the bitmap into the device context
m_prev = (CBitmap *) m_dc.SelectObject(m_dib);
// ____________________________________________________________________________
}
In this example, the height is negative because the rows in the pixmap will be ordered in a top-down fashion, i.e., the address of the top row is less than the address of the bottom row.
Once a pixmap has been constructed in this manner, copying into an area of an open windows is easy. Here, pDC is a pointer to the target window's device context, and x and y are coordinates within that window:
void PixMapAny::Blit(int x, int y, CDC * pDC)
{
pDC->BitBlt(x,y,m_width,m_height,&m_dc,0,0,SRCCOPY);
}
Related
I have this piece of code here that takes a screenshot and as bitmaps and saves it as a BMP file. it works just fine but I want to be able to modify the bits and make it grayscale instead of an RGB map.
I found a way to do this but I had to save the BMPINFOHEADER and RGBQUAD array, then read it with the grayscale function it kind of worked, but the size stays the same.
I am very noob at this but I think the grayscale image should be a lot smaller than an RGB one.
is there a way of removing color from the bitmap right away without having to re-read it and modify it?
int CaptureBMP(LPCTSTR szFile)
{
// Source[1]
HDC hdcScr, hdcMem;
HBITMAP hbmScr;
BITMAP bmp;
int iXRes, iYRes;
// Create a normal DC and a memory DC for the entire screen. The
// normal DC provides a "snapshot" of the screen contents. The
// memory DC keeps a copy of this "snapshot" in the associated
// bitmap.
hdcScr = CreateDC("DISPLAY", NULL, NULL, NULL);
hdcMem = CreateCompatibleDC(hdcScr);
iXRes = GetDeviceCaps(hdcScr, HORZRES);
iYRes = GetDeviceCaps(hdcScr, VERTRES);
// Create a compatible bitmap for hdcScreen.
hbmScr = CreateCompatibleBitmap(hdcScr, iXRes, iYRes);
if (hbmScr == 0) return 0;
// Select the bitmaps into the compatible DC.
if (!SelectObject(hdcMem, hbmScr)) return 0;
// Copy color data for the entire display into a
// bitmap that is selected into a compatible DC.
if (!StretchBlt(hdcMem,0, 0, iXRes, iYRes,hdcScr,0, 0, iXRes, iYRes,SRCCOPY)) return 0;
// Source[2]
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap's color format, width, and height.
if (!GetObject(hbmScr, sizeof(BITMAP), (LPSTR)&bmp)) return 0;
// Convert the color format to a count of bits.
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;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits != 24)
pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits));
// There is no RGBQUAD array for the 24-bit-per-pixel format.
else
pbmi = (PBITMAPINFO)LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
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);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) / 8 * pbmi->bmiHeader.biHeight * cClrBits;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
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)pbmi;
lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits) return 0;
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hdcMem, hbmScr, 0, (WORD)pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS)) return 0;
// Create the .BMP file.
hf = CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, (DWORD)0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if (hf == INVALID_HANDLE_VALUE) return 0;
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 0;
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID)pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof(RGBQUAD),
(LPDWORD)&dwTmp, NULL))
return 0;
// 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 0;
// Close the .BMP file.
if (!CloseHandle(hf)) return 0;
// Free memory.
GlobalFree((HGLOBAL)lpBits);
ReleaseDC(0, hdcScr);
ReleaseDC(0, hdcMem);
return 1;
}
I used array conversion, which is mainly from the rgbRed, rgbGreen and rgbBlue components of the original true color map to the gray value Y of the gray image.
It can be obtained by using the following formula:
Y=0.299 * rgbRed+0.587 * rgbGreen+0.114 * rgbBlue
The processing code has been added to your code, you can refer to the following code, the output file size is 1/4 of the original.
#pragma pack(1)
typedef struct tag_color_32 {
BYTE Red;
BYTE Green;
BYTE Blue;
BYTE Alpha;
}color_32;
int CaptureBMP(LPCTSTR szFile)
{
// Source[1]
HDC hdcScr, hdcMem;
HBITMAP hbmScr;
BITMAP bmp;
.................
.................
// 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 0;
// Close the .BMP file.
if (!CloseHandle(hf)) return 0;
/*********************edit 2022/1/7******************************/
FILE* originImg;
fopen_s(&originImg, "lena-32.bmp", "rb");
if (originImg == NULL) return 0;
int sizeFileHeader = sizeof(BITMAPFILEHEADER);
int sizeInfoHeader = sizeof(BITMAPINFOHEADER);
BITMAPFILEHEADER* bitmapFileHeader = new BITMAPFILEHEADER[sizeFileHeader + 1];
BITMAPINFOHEADER* bitmapInfoHeader = new BITMAPINFOHEADER[sizeInfoHeader + 1];
memset(bitmapFileHeader, 0, sizeFileHeader + 1);
memset(bitmapInfoHeader, 0, sizeInfoHeader + 1);
fread(bitmapFileHeader, sizeof(char), sizeFileHeader, originImg);
fseek(originImg, sizeFileHeader, 0);
fread(bitmapInfoHeader, sizeof(char), sizeInfoHeader, originImg);
int srcImageLineByteCount = (((bitmapInfoHeader->biWidth * 32) + 31) / 32) * 4; //Calculates the number of bytes of pixels per line of the original 32 bitmap
int grayImageLineByteCount = (((bitmapInfoHeader->biWidth) * 8 + 31) / 32) * 4; //Calculate the number of bytes of pixels in each row of 8-bit grayscale map
//************Bitmap header**********************
//Creates a two-dimensional array with high biHeight and width srcImageLineByteCount, and initializes the array
color_32* origImgData = new color_32[bitmapInfoHeader->biHeight * bitmapInfoHeader->biWidth];
for (int i = 0; i < bitmapInfoHeader->biHeight * bitmapInfoHeader->biWidth; i++)
{
fread(origImgData + i, 4, 1, originImg);
}
fclose(originImg);
// palette
RGBQUAD* pRgbQuards = new RGBQUAD[256];
for (int i = 0; i < 256; i++)
{
pRgbQuards[i].rgbBlue = i;
pRgbQuards[i].rgbRed = i;
pRgbQuards[i].rgbGreen = i;
pRgbQuards[i].rgbReserved = 0;
}
//Modify headers
bitmapInfoHeader->biBitCount = 8;
bitmapInfoHeader->biClrUsed = 256;
bitmapInfoHeader->biSizeImage = (bitmapInfoHeader->biHeight) * grayImageLineByteCount;
//8 is a grayscale image with 256 RGBQUAD data structures. A color palette takes up 4 bytes of data, so the color palette length of 256 color images is 256*4 and 1024 bytes
bitmapFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256; //The offset plus the size of the palette
bitmapFileHeader->bfSize = bitmapFileHeader->bfOffBits + bitmapInfoHeader->biSizeImage;
//Write the data
FILE* grayImg;
fopen_s(&grayImg, "lena-gray.bmp", "wb");
//Written to the file
fwrite(bitmapFileHeader, sizeof(char), sizeof(BITMAPFILEHEADER), grayImg);
fwrite(bitmapInfoHeader, sizeof(char), sizeof(BITMAPINFOHEADER), grayImg);
fwrite(pRgbQuards, sizeof(RGBQUAD), 256, grayImg);
//Grayscale map a two-dimensional array of bitmap data
for (int i = 0; i < bitmapInfoHeader->biHeight; i++)
{
for (int j = 0; j < bitmapInfoHeader->biWidth; j++) //The number of bytes of pixels per row of a grayscale image
{
float pr = origImgData[i * bitmapInfoHeader->biWidth + j].Red;
float pg = origImgData[i * bitmapInfoHeader->biWidth + j].Green;
float pb = origImgData[i * bitmapInfoHeader->biWidth + j].Blue;
BYTE data = pr * 0.299 + pg * 0.587 + pb * 0.114;
fwrite(&data, 1, 1, grayImg);
for (int j = bitmapInfoHeader->biWidth; j < grayImageLineByteCount; j++) {
BYTE data = 0;
fwrite(&data, 1, 1, grayImg);
}
}
}
fclose(grayImg);
//Free memory.
delete[]origImgData;
/*******************edit 2022/1/7****************************/
// Free memory.
GlobalFree((HGLOBAL)lpBits);
ReleaseDC(0, hdcScr);
ReleaseDC(0, hdcMem);
return 1;
}
int main()
{
CaptureBMP(L"lena-32");
std::cout << "Hello World!\n";
}
I am trying to create a C++ application which actually captures the bitmap from magnifier and render it using Direct 2d.
I am currently having the code to save the bitmap from magnifier to a file. but what I need is to do is to draw that bitmap to my window using direct 2d instead of saving it to file.
The magnifier returns image as struct in the form of MAGIMAGEHEADER and I was able to obtain BITMAPINFOHEADER and byte from it. I need to render it to a window using direct 2D.
Here is the code used to obtain the BITMAPINFOHEADER and bytes from Magnifier API
BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader,RECT unclipped, RECT clipped, HRGN dirty)
{
// Setup the bitmap info header
bmif.biSize = sizeof(BITMAPINFOHEADER);
bmif.biHeight = srcheader.height;
bmif.biWidth = srcheader.width;
bmif.biSizeImage = srcheader.cbSize;
bmif.biPlanes = 1;
bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8);
bmif.biCompression = BI_RGB;
// Prepare the buffer
if (pData != NULL)
{
delete pData;
pData = NULL;
}
pData = (BYTE*)malloc(bmif.biSizeImage);
memcpy(pData, srcdata, bmif.biSizeImage);
// The data bit is in top->bottom order, so we convert it to bottom->top order
LONG lineSize = bmif.biWidth * bmif.biBitCount / 8;
BYTE* pLineData = new BYTE[lineSize];
BYTE* pStart;
BYTE* pEnd;
LONG lineStart = 0;
LONG lineEnd = bmif.biHeight - 1;
while (lineStart < lineEnd)
{
// Get the address of the swap line
pStart = pData + (lineStart * lineSize);
pEnd = pData + (lineEnd * lineSize);
// Swap the top with the bottom
memcpy(pLineData, pStart, lineSize);
memcpy(pStart, pEnd, lineSize);
memcpy(pEnd, pLineData, lineSize);
// Adjust the line index
lineStart++;
lineEnd--;
}
delete pLineData;
// Set the flag to say that the callback function is finished
bCallbacked = TRUE;
return TRUE;
}
Here the variable bmif is BITMAPINFOHEADER and pData is the Bytes
Is there any way to achieve this?
if you have the HBITMAP handle, you can do this:
The the size of your image using: ::GetObject(hBmp, sizeof(BITMAP), &bmpSizeInfo);
fill a BITMAPINFO like this:
memset(&bmpData, 0, sizeof(BITMAPINFO));
bmpData.bmiHeader.biSize = sizeof(bmpData.bmiHeader);
bmpData.bmiHeader.biHeight = -bmpSizeInfo.bmHeight;
bmpData.bmiHeader.biWidth = bmpSizeInfo.bmWidth;
bmpData.bmiHeader.biPlanes = bmpSizeInfo.bmPlanes;
bmpData.bmiHeader.biBitCount = bmpSizeInfo.bmBitsPixel;
create enough heap memory to hold the data for your bitmap:
pBuff = new char[bmpSizeInfo.bmWidth * bmpSizeInfo.bmHeight * 4];
Get the bitmap data like this:
::GetDIBits(hDc, hBmp, 0, bmpSizeInfo.bmHeight, (void*)pBuff, &bmpData, DIB_RGB_COLORS);
Create a D2D1_BITMAP_PROPERTIES and fill it like this:
bmpPorp.dpiX = 0.0f;
bmpPorp.dpiY = 0.0f;
bmpPorp.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
bmpPorp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
Using your render target turn the data into ID2D1Bitmap
pRT->CreateBitmap(bmpSize, pBuff, 4 * bmpSizeInfo.bmWidth, bmpPorp, &pBmpFromH);
Here is how you can use mag's bitmap with Direct2D. You don't need BITMAPINFOHEADER since mag format is the same as DXGI_FORMAT_B8G8R8A8_UNORM:
BOOL MagImageScaling(HWND hwnd, void* srcdata, MAGIMAGEHEADER srcheader, void* destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
{
// note: all this (dc, surface, targte) can be created only once as long as the D3D device isn't reset
ComPtr<ID2D1DeviceContext> dc;
HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, dc.GetAddressOf()));
ComPtr<IDXGISurface2> surface;
HR(swapChain->GetBuffer(0, IID_PPV_ARGS(&surface)));
ComPtr<ID2D1Bitmap1> target;
HR(dc->CreateBitmapFromDxgiSurface(surface.Get(), NULL, target.GetAddressOf()));
dc->SetTarget(target.Get());
D2D1_BITMAP_PROPERTIES properties = {};
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
// note: this is ok as srcheader.format (GUID_WICPixelFormat32bppRGBA) is compatible
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
D2D1_SIZE_U size = {};
size.width = srcheader.width;
size.height = srcheader.height;
ComPtr<ID2D1Bitmap> bitmap;
HR(dc->CreateBitmap(size, properties, bitmap.GetAddressOf()));
HR(bitmap->CopyFromMemory(NULL, srcdata, srcheader.stride));
dc->BeginDraw();
// note: we don't call this because we draw on the whole render target
//dc->Clear();
dc->DrawBitmap(bitmap.Get());
HR(dc->EndDraw());
HR(swapChain->Present(1, 0));
return TRUE;
}
I have got a hard time to display jpg file at VIEW class by ImageMagicK in MFC.
The following code are my code. the problem is looking as following
image
void CTestview::DoDisplayImage(void)
{
CDC *pDC = GetDC();
if (fileposition != NULL)
AfxMessageBox(fileposition);
char m_szAppPath[255];
//m_szAppPath = (char) * fileposition;
(void)MagickCore::SetClientPath(fileposition);
InitializeMagick(fileposition);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
Image m_Image;
m_Image.read(fileposition);
/*
char m_szAppPath[255];
(void) MagickCore::SetClientPath(m_szAppPath);
InitializeMagick(m_szAppPath);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
Image master;
master.read("D:\\work\\mfc_test5\\q1.jpg");
*/
if (pDC != NULL && m_Image.isValid())
{
CRect rectClient;
GetClientRect(rectClient);
// Clear the background
pDC->FillSolidRect(rectClient, pDC->GetBkColor());
// Set up the Windows bitmap header
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER); // Size of structure
bmi.biWidth = m_Image.columns(); // Bitmaps width in pixels
bmi.biHeight = (-1)*m_Image.rows(); // Bitmaps height n pixels
bmi.biPlanes = 1; // Number of planes in the image
bmi.biBitCount = 32; // The number of bits per pixel
bmi.biCompression = BI_RGB; // The type of compression used
bmi.biSizeImage = 0; // The size of the image in bytes
bmi.biXPelsPerMeter = 0; // Horizontal resolution
bmi.biYPelsPerMeter = 0; // Veritical resolution
bmi.biClrUsed = 0; // Number of colors actually used
bmi.biClrImportant = 0; // Colors most important
// Extract the pixels from Magick++ image object and convert to a DIB section
PixelPacket *pPixels = m_Image.getPixels(0, 0, m_Image.columns(), m_Image.rows());
RGBQUAD *prgbaDIB = 0;
HBITMAP hBitmap = CreateDIBSection
(
pDC->m_hDC, // handle to device context
(BITMAPINFO *)&bmi, // pointer to structure containing bitmap size, format, and color data
DIB_RGB_COLORS, // color data type indicator: RGB values or palette indices
(void**)&prgbaDIB, // pointer to variable to receive a pointer to the bitmap's bit values
NULL, // optional handle to a file mapping object
0 // offset to the bitmap bit values within the file mapping object
);
if (!hBitmap)
return;
unsigned long nPixels = m_Image.columns() * m_Image.rows();
RGBQUAD *pDestPixel = prgbaDIB;
// Transfer pixels, scaling to Quantum
/*
for( unsigned long nPixelCount = nPixels; nPixelCount ; nPixelCount-- )
{
pDestPixel->rgbRed = MagickCore::GetPixelRed(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbGreen = MagickCore::GetPixelGreen(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbBlue = MagickCore::GetPixelBlue(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbReserved = 0;
++pDestPixel;
pPixels+=MagickCore::GetPixelChannels(m_Image.constImage());
}
*/
for (int i = 0; i < 256; i++)
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
// Now copy the bitmap to device.
HDC hMemDC = CreateCompatibleDC(pDC->m_hDC);
SelectObject(hMemDC, hBitmap);
BitBlt(pDC->m_hDC, 0, 0, m_Image.columns(), m_Image.rows(), hMemDC, 0, 0, SRCCOPY);
DeleteObject(hMemDC);
}
}
Especially, at the following code does not sure.
What should I do for solving this problem?
for( int i = 0 ; i < 256 ; i++ )
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
Would you please help me how do I do for solving this problem?
As you can see, the output image has black.
This has been bugging me for some time, I want to make sure that my understanding of bitmaps is correct and get some help spotting errors. Basically what I am trying to do is to save an 8 bit bitmap file and at the same time display it in a picture box in the MFC application. I want to avoid the cumbersome method of saving the bitmap then loading it up again.
The saving file operation was mostly successful, however I altered my code and now what used to be white in the file (in this instance its a black and white image) is coming out usually green but it changes. I'm guessing this is because my data is referencing information in the color table possibly, which value would be white?
HBITMAP ReadWrite::SaveFile(LPCTSTR file, double* data) {
BYTE* bmp = ConvertData(data);
HANDLE hf;
BITMAPINFO* pbmi = WriteHeader(file);
BITMAPFILEHEADER* bmfh = (BITMAPFILEHEADER*)alloca(sizeof(BITMAPFILEHEADER));
bmfh->bfType = 0x4d42; // 'BM'
bmfh->bfReserved1 = 0;
bmfh->bfReserved2 = 0;
bmfh->bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbmi->bmiHeader.biSize + 256 * sizeof(RGBQUAD);
bmfh->bfSize = (DWORD)(bmfh->bfOffBits + pbmi->bmiHeader.biSizeImage);
hf = CreateFile(file, GENERIC_READ | GENERIC_WRITE, (DWORD) 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL );
if (hf == NULL) // error creating
{
CloseHandle (hf);
return NULL;
}
// write header
unsigned long bwritten;
if (!WriteFile(hf, (LPCVOID)bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL))
{
CloseHandle (hf);
return NULL;
}
// write infoheader
if (!WriteFile(hf, (LPCVOID)pbmi, sizeof (BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD), &bwritten, NULL ))
{
CloseHandle (hf);
return NULL;
}
// write image data
if (!WriteFile(hf, (LPCVOID)bmp_data, (int) pbmi->bmiHeader.biSizeImage, &bwritten, NULL ))
{
CloseHandle (hf);
return NULL;
}
// Close
CloseHandle(hf);
// Send back a file to display
return CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, (void**)bmp_data, NULL, 0);
}
Code to write the infoheader + palette (which should be values ranging from black to white??)
BITMAPINFO* ReadWrite::WriteHeader(LPCTSTR fn)
{
int R = ReadWrite::getR();
int C = ReadWrite::getC();
BITMAPINFO* pbmi = (BITMAPINFO*)alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = R;
pbmi->bmiHeader.biHeight = -C;
pbmi->bmiHeader.biPlanes = 1;
pbmi->bmiHeader.biBitCount = 8;
pbmi->bmiHeader.biCompression = BI_RGB;
pbmi->bmiHeader.biSizeImage = (((R * pbmi->bmiHeader.biBitCount + 31) & ~31) >> 3) * C;
pbmi->bmiHeader.biClrUsed = 256;
pbmi->bmiHeader.biClrImportant = 0;
for(int i=0; i<256; i++)
{
pbmi->bmiColors[i].rgbRed = i;
pbmi->bmiColors[i].rgbGreen = i;
pbmi->bmiColors[i].rgbBlue = i;
pbmi->bmiColors[i].rgbReserved = 0;
}
//return true;
return pbmi;
}
And finally converting my number array into unsigned char 'Bytes':
BYTE* ReadWrite::ConvertData(double* data) {
BYTE* bmp_data;
int R = ReadWrite::getR();
int C = ReadWrite::getC();
bool binary = ReadWrite::getBinary();
bmp_data = new BYTE [R*C];
// convert the values to unsigned char (BYTE)
for(int i=0; i<R*C; i++){
if (data[i] == 1){
data[i] = 255;
}
bmp_data[i] = (unsigned char)data[i];
}
delete [] data;
return bmp_data;
}
So a recap of the issues/questions:
White comes out lime green.
The HBITMAP is unable to display inside a picturebox, the box goes solid black after setimage called (yes its set to SS_BITMAP)
I believe I may be missing some information from the creation of the bitmap and I think I need to implement a device context though I'm unsure. I can open the file in windows but if I try to upload it online it doesnt like the format.
I don't know how to manage the memory of the objects, so I'm getting leaks, how do I clean up with DeleteObject just before the application closes? (destructor of dialog maybe?) The leaks are 262144 bytes which is around the size of the image.
Thanks for reading this stupid long post.
EDIT I managed to fix the green issue (number 1), I don't know how, I think it was to do with incorrect sizes for memory on the headers. It still isn't able to upload to the internet or display in the program so something must be wrong with it.
So I found the issue with my code that was causing errors across other applications/the internet.
In it I assign a negative value to the height of the image in order to flip the data, however this is a completely incorrect solution. Instead:
// convert the integer values to unsigned char (BYTE) BACKWARDS
for(int i=0; i<R; i++){
for(int j=0; j<C; j++){
if (data[i*R + j] == 1 && binary){
data[i*R + j] = 255;
}
bmp_data[(R-i)*C + (j-C)] = (unsigned char)data[i*C + j];
}
}
What this does is copies the original values into the bmp_data both backwards and flipped. This ensures the data is stored correctly and will open in the application. My current solution to DIBSection and memory management was to delete all the data and reload the image from file instead. Passing a HBITMAP was too much of a chore. I would welcome input on that part, but for now all my problems are fixed.
I have bytearray where every three bytes describes 1 pixel (RGB). The task is to convert it to jpeg or png.
Actually, I am using Zint (open source lib for generating barcodes) that uses libpng to generate image file and save it to file system, but in Zintthe function png_plot() except generating image also save it on disk which is undesirable.
As result I think there two ways:
1. from bitmap bytearray to bmp -> jpeg / png (using some other lib)
2. writing hook or some similar to png_plot()
Can you give me some advices?
Thank you.
Upd: for #peacemaker
FILE *f;
zint_symbol *my_symbol;
my_symbol = ZBarcode_Create();
ZBarcode_Encode_and_Buffer(my_symbol, (unsigned char *)argv[1], 0, 0);
f = fopen("bitmap.bmp", "w");
fwrite(my_symbol->bitmap, sizeof(*(my_symbol->bitmap)), my_symbol->bitmap_height * my_symbol->bitmap_width, f);
ZBarcode_Delete(my_symbol);
fclose(f);
In order to convert between image formats, the easiest way would be using the class CImage shared by MFC and ATL and defined in the header file atlimage.h.
CImage image;
HRESULT res = image.Load("in.bmp");
image.Save("out.jpg");
image.Save("out.gif");
image.Save("out.png");
image.Save("out.tif");
If you have a RGB buffer and want to create a bitmap: just create and save a bitmap header into a file and add the RGB buffer to it.
To create the header you can use the BITMAPFILEHEADER, BITMAPINFOHEADER and RGBQUAD structures from GDI defined in the header WinGDI.h
Here is an example on how to fill the header data:
BITMAPINFOHEADER bmpInfoHdr;
bmpInfoHdr.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHdr.biHeight = nHeight;
bmpInfoHdr.biWidth = nWidthPadded;
bmpInfoHdr.biPlanes = 1;
bmpInfoHdr.biBitCount = bitsPerPixel;
bmpInfoHdr.biSizeImage = nHeight * nWidthPadded * nSPP;
bmpInfoHdr.biCompression = BI_RGB;
bmpInfoHdr.biClrImportant = 0;
bmpInfoHdr.biClrUsed = 0;
bmpInfoHdr.biXPelsPerMeter = 0;
bmpInfoHdr.biYPelsPerMeter = 0;
bmpFileHdr.bfType = BITMAP_FORMAT_BMP;
bmpFileHdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + bmpInfoHdr.biSize +
sizeof(RGBQUAD)*numColors + bmpInfoHdr.biSizeImage);
bmpFileHdr.bfReserved1 = 0;
bmpFileHdr.bfReserved2 = 0;
bmpFileHdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + bmpInfoHdr.biSize +
sizeof(RGBQUAD)*numColors);
Keep into account that the bitmaps are stored upside-down and that the width of the image must be aligned on a DWORD except for RLE-compressed bitmaps.(they must be multiple of 4 bytes, add a padding if necessary).
if ((nWidth%4) != 0)
nPadding = ((nWidth/4) + 1) * 4;
When saving your buffer, add the needed padding to each row...
Summarizing, these are the needed steps to create a bitmap file from a rgb buffer:
//1. create bmp header
//2. save header to file:
write(file, &bmpFileHdr, sizeof(BITMAPFILEHEADER));
write(file, &bmpInfoHdr, sizeof(BITMAPINFOHEADER));
write(file, &colorTable, numColors * sizeof(RGBQUAD));
//3. add rgb buffer to file:
for(int h=0; h<nHeight; h++) {
for(int w=0; w<nWidth; w++) {
//3.a) add row to file
//3.b) add padding for this row to file
}
}
I used the CImage Class from ATL.
int width=0, height=0;
char * val = "9788994774480";
zint_symbol *my_symbol;
my_symbol = ZBarcode_Create();
//ZBarcode_Encode_and_Buffer(my_symbol,(unsigned char *) val, 0, 0);
ZBarcode_Encode(my_symbol, (unsigned char *) val, 0);
ZBarcode_Buffer(my_symbol, 0);
height = my_symbol->bitmap_height;
width = my_symbol->bitmap_width;
char * imgBits = my_symbol->bitmap;
CImage img;
img.Create(width, height, 24 /* bpp */, 0 /* No alpha channel */);
int nPixel = 0;
for(int row = 0; row < height; row++)
{
for(int col = 0; col < width; col++)
{
BYTE r = (BYTE)imgBits[nPixel];
BYTE g = (BYTE)imgBits[nPixel+1];
BYTE b = (BYTE)imgBits[nPixel+2];
img.SetPixel(col, row , RGB(r, g, b));
nPixel += 3;
}
}
img.Save("CImage.bmp", Gdiplus::ImageFormatBMP);
ZBarcode_Delete(my_symbol);
is there anyway to do this other than using SetPixel? I am experiencing major performance issues with SetPixel and need an alternative method... I have tried using CreateDIBSection to no avail. The barcode displays slanted and is unusable. here is my code for that:
void *bits = (unsigned char*)(my_symbol->bitmap);
HBITMAP hBitmap = CreateDIBSection(pDC->GetSafeHdc(), &info, DIB_RGB_COLORS, (void **)&pDestData, NULL, 0);
memcpy(pDestData, my_symbol->bitmap, info.bmiHeader.biSizeImage);
img.Attach(hBitmap);
Another option that produces the same result is this:
BITMAPINFO info;
BITMAPINFOHEADER BitmapInfoHeader;
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoHeader.biWidth = my_symbol->bitmap_width;
BitmapInfoHeader.biHeight = -(my_symbol->bitmap_height);
BitmapInfoHeader.biPlanes = 1;
BitmapInfoHeader.biBitCount = 24;
BitmapInfoHeader.biCompression = BI_RGB;
BitmapInfoHeader.biSizeImage = 0;
BitmapInfoHeader.biXPelsPerMeter = 0;
BitmapInfoHeader.biYPelsPerMeter = 0;
BitmapInfoHeader.biClrUsed = 0;
BitmapInfoHeader.biClrImportant = 0;
info.bmiHeader = BitmapInfoHeader;
HBITMAP hbmp = CreateDIBitmap(dc, &BitmapInfoHeader, CBM_INIT, (LPVOID *)my_symbol->bitmap, (LPBITMAPINFO)&info, DIB_RGB_COLORS);
img.Attach(hbmp);