What should be a BITMAPINFOHEADER construction If biCompression is BI_PNG? - c++

I need suggestions about the BITMAPINFOHEADER construction for PNG images. This bitmap info header will be used to save a buffer in png file format using GdiPlus::Bitmap. My image buffer is a 32 bit PNG image.
I have done the following but GdiPlus::Bitmap::GetLastStatus() says "Invalid Parameter" and I can't save the buffer to a png file format.
void CreateBitmapInfo(long lWidth, long lHeight, bool bColor, CComHeapPtr<BITMAPINFO>& apBitmapInfo)
{
const auto bitCount = 32;
const auto colorUsed = 2;
long lBitmapInfoSize = sizeof(BITMAPINFOHEADER);
if (!bColor)
lBitmapInfoSize += colorUsed * sizeof(RGBQUAD);
TRY_CONDITION(apBitmapInfo.AllocateBytes(lBitmapInfoSize));
::memset(apBitmapInfo, 0, lBitmapInfoSize);
apBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
apBitmapInfo->bmiHeader.biWidth = lWidth;
apBitmapInfo->bmiHeader.biHeight = lHeight;
apBitmapInfo->bmiHeader.biPlanes = 1;
apBitmapInfo->bmiHeader.biBitCount = bitCount;
apBitmapInfo->bmiHeader.biCompression = BI_PNG;
apBitmapInfo->bmiHeader.biSizeImage = gsl::narrow_cast<uint32_t>(((lWidth + 7) & 0xFFFFFFF8) * lHeight / 8); // zero for BI_RGB
apBitmapInfo->bmiHeader.biXPelsPerMeter = 0;
apBitmapInfo->bmiHeader.biYPelsPerMeter = 0;
apBitmapInfo->bmiHeader.biClrUsed = bColor ? 0 : colorUsed;
apBitmapInfo->bmiHeader.biClrImportant = 0;
if (!bColor)
{
// create a gray scale color table
for (int i = 0; i < colorUsed; i++)
{
RGBQUAD& rgb = *(apBitmapInfo->bmiColors + i);
rgb.rgbRed = rgb.rgbGreen = rgb.rgbBlue = gsl::narrow_cast<BYTE>(i);
}
}
}

Related

how do i take a grayscale screenshot with c++?

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";
}

Can I output a one channel image acquired from camera into a winAppi window?

I have a pointer to an image acquired from acamera using a third party SDK. The image is one band (Mono 8). I want to output it as a bittmap into a winAppi window. What i do is
HBITMAP hBitmap = NULL;
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = 1280;
bmih.biHeight = 960;
bmih.biPlanes = 1;
bmih.biBitCount = 8;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
BITMAPINFO dbmi;
ZeroMemory(&dbmi, sizeof(dbmi));
dbmi.bmiHeader = bmih;
dbmi.bmiColors->rgbBlue = 0;
dbmi.bmiColors->rgbGreen = 0;
dbmi.bmiColors->rgbRed = 0;
dbmi.bmiColors->rgbReserved = 0;
void* bits = &aquiredImageCPU.m_sMemory.ptr()[0];
hBitmap = CreateDIBitmap(dc, &bmih, CBM_INIT, bits, &dbmi, DIB_RGB_COLORS);
src = CreateCompatibleDC(dc);
SelectObject(src, hBitmap);
BitBlt(dc, 10,10,512, 512, src, 0, 0, SRCCOPY);
If i output the image as matrix and previou it using my library (a library where i use imsave similiar to matlabs imsave) i can see that the image is ok(grayscale image). But when i output it to winAppi window it tranforms it to RGB. I think it has to do with
HDC dc = GetDC(hwnd);
src = CreateCompatibleDC(dc);
Any suggestions?
Thank you
What i did in a similiar case is to take every 8 bits and to copy them 2 more times after the chunk of 8 bits. That works but it is costly and i need to have a real tile application.
auto outputImageHight = 1280;
auto outputImageWidth = 960;
unsigned char *myArray = new unsigned char[3 * outputImageHight * outputImageWidth];
for (int i = 0; i < outputImageHight; i++)
for (int j = 0; j < outputImageWidth; j++)
{
unsigned char ucTmp = (unsigned char)(image(i, j));
myArray[3 * (i + outputImageHight * j) + 0] = ucTmp;
myArray[3 * (i + outputImageHight * j) + 1] = ucTmp;
myArray[3 * (i + outputImageHight * j) + 2] = ucTmp;
}
An 8-bit bitmap requires a color table. Since you want grayscale, you have to set up the color table to have 256 levels of gray. You've set the first one to black, which is correct, but you haven't set the rest.
BITMAPINFO is actually a variably sized structure. The bmiColors field is just a placeholder for the first color in the color table. You have to allocate extra space for the entire color table and fill it out.
std::size_t size = sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD);
std::vector<char> buffer(size);
BITMAPINFO *dbmi = reinterpret_cast<BITMAPINFO *>(buffer.data());
ZeroMemory(dbmi, size); // probably unnecessary
dbmi->bmiHeader = bmih;
for (int i = 0; i < 256; ++i) {
dbmi->bmiColors[i].rgbBlue = i;
dbmi->bmiColors[i].rgbGreen = i;
dbmi->bmiColors[i].rgbRed = i;
dbmi->bmiColors[i].rgbReserved = 0;
}

Creating a DIB from a buffer and blitting it to the screen

I'm trying to create my own image buffer filled with red pixels and then use that with a DIB to display it on a screen DC. The result is that I'm getting a dialog full of lines, almost like what would happen if the stride was off... maybe someone here can point out my mistake. The lines in the image are difficult to see, but they alternate as a line of red, then green, then blue.. repeating until the bottom of the dialog. Much appreciated if someone can help me out on this!
//Filling the buffer with all red pixels
uint8_t *buffer = new uint8_t[IMAGEX * IMAGEY * 3];
for (int y = 0; y < IMAGEY; y++)
{
for (int x = 0; x < IMAGEX * 3; x += 3)
{
buffer[y*IMAGEX*3 + x] = 0;
buffer[y*IMAGEX*3 + x + 1] = 0;
buffer[y*IMAGEX*3 + x + 2] = 255;
}
}
pCam->pTestQ->push_back(buffer);
//Displaying the buffer on a DC
BITMAPINFOHEADER bmih = {0};
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = IMAGEX;
bmih.biHeight = -IMAGEY;
bmih.biPlanes = 1;
bmih.biBitCount = 24;
bmih.biCompression = BI_RGB;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
bmih.biSizeImage = IMAGEX * IMAGEY * 3;
BITMAPINFO bmi = {0};
bmi.bmiHeader = bmih;
pDC = GetDC();
mDC.CreateCompatibleDC(pDC);
CBitmap img;
img.CreateCompatibleBitmap(pDC, IMAGEX, IMAGEY);
CBitmap* pOldBmp = mDC.SelectObject(&img);
int iRet = SetDIBits(mDC, img, 0, IMAGEY, pCamera->pTestQ->front(), &bmi, DIB_RGB_COLORS);
pDC->BitBlt(0, 0, IMAGEX, IMAGEY, &mDC, 0, 0, SRCCOPY);
mDC.SelectObject(pOldBmp);
ReleaseDC(pDC);
Resulting dc blit:
http://i.imgur.com/qtImSFE.png

How to make 8-bit bitmap appear as monochrome in C++?

When I set up and create a 24-bit bitmap like this:
//fileheader
BITMAPFILEHEADER* bf = new BITMAPFILEHEADER;
bf->bfType = 0x4d42;
bf->bfSize = 6054400 + 54;
bf->bfOffBits = 54;
//infoheader
BITMAPINFOHEADER* bi = new BITMAPINFOHEADER;
bi->biSize = 40;
bi->biWidth = 2752;
bi->biHeight = -733;
bi->biPlanes = 1;
bi->biBitCount = 24;
bi->biCompression = 0;
//bi->biSizeImage = 6054400;
bi->biXPelsPerMeter = 2835;
bi->biYPelsPerMeter = 2835;
bi->biClrUsed = 0;
bi->biClrImportant = 0;
pFrame->GetImage(m_imageData);
//
//create bitmap...
//(hbit is a global variable)
BITMAPINFO* bmi;
bmi = (BITMAPINFO*)bi;
HDC hdc = ::GetDC(NULL);
hbit = CreateDIBitmap(hdc, bi, CBM_INIT, m_imageData, bmi, DIB_RGB_COLORS);
I get an output image like this:
But when I change bitcount from 24 to 8 (which also allows for 3x image size, allowing me to go from 733 width to the image's natural width of 2200), I get an image like this (along with a lot of instability):
My output looks like this:
BITMAP* bi = new BITMAP;
CBitmap bmp;
bmp.Attach(hbit);
CClientDC dc(pWnd);
CDC bmDC;
bmDC.CreateCompatibleDC(&dc);
CBitmap *pOldbmp = bmDC.SelectObject(&bmp);
bmp.GetBitmap(bi);
dc.BitBlt(384,26,bi->bmWidth/3,bi->bmHeight,&bmDC,0,0,SRCCOPY);
//note: if bitcount is 8, height and width need to be /3,
//if 24, only width gets /3
bmDC.SelectObject(pOldbmp);
//explicitly delete everything just to be safe
delete bi;
DeleteObject(bmp);
DeleteObject(dc);
DeleteObject(pOldbmp);
DeleteObject(bmDC);
So my questions are:
Why is this happening when I switch from 24 to 8?
Is there an easy way to output the image as monochrome rather than color?
One last thing:
My coworker wrote this function a long time ago for a similar issue, but he said I may be able to use it. I can't get it to work, unfortunately:
void CopyMono8ToBgrx(byte* pDestBlue, byte* pDestGreen, byte *pDestRed, byte* pDestAlpha)
{
byte* pSrc;
byte* pSrcEnd;
pSrc = ( byte* ) m_imageData;
pSrcEnd = pSrc + ( 2752*2200 );
while ( pSrc < pSrcEnd )
{
byte data = *pSrc;
*pDestBlue = data;
*pDestGreen = data;
*pDestRed = data;
*pDestAlpha = 255; // alpha is always 255 (fully opaque)
pSrc++;
pDestBlue += 4;
pDestGreen += 4;
pDestRed += 4;
pDestAlpha += 4;
}
}
You should create a color pallete. Try this:
struct BITMAPINFO256 {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} bmi;
memset(&bmi, 0, sizeof(BITMAPINFO256));
bmi.bmiHeader.biSize = 40;
bmi.bmiHeader.biWidth = 2752;
bmi.bmiHeader.biHeight = -733;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 8;
bmi.bmiHeader.biCompression = 0;
bmi.bmiHeader.biXPelsPerMeter = 2835;
bmi.bmiHeader.biYPelsPerMeter = 2835;
bmi.bmiHeader.biClrUsed = 256;
bmi.bmiHeader.biClrImportant = 0;
for (unsigned int i = 0; i < 256; i++) {
bmi.bmiColors[i].rgbRed = i;
bmi.bmiColors[i].rgbGreen = i;
bmi.bmiColors[i].rgbBlue = i;
}
And then when you call CreateDIBitmap it will become:
hbit = CreateDIBitmap(hdc, &bmi.bmiHeader, CBM_INIT, m_imageData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
Also note that you should be careful to also increase the offset in your BITMAPFILEHEADER so that it it expresses that there is color pallete defined before the actual pixels data (yesterday I was having hard time because of this, see Creating 8bpp bitmap with GDI and saving it as a file):
bf->bfOffBits = 54 + sizeof(RGBQUAD)*256;
And to that function that your coworker wrote: It's better to use Luminance to convert colors to gray-scale equivalents:
Hope this helps :)
8 bit per pixel images are assuming a color palette following BITMAPINFOHEADER structure (see BITMAPINFO::bmiColors). If you make the palette to be 256 gray shades, the image is going to me 8 bpp grayscale. Now it's color with random colors on it.
The function CopyMono8ToBgrx you quoted creates full color bitmap, with gray individual pixels (R=G=B).

raw bitmap data to jpeg or png C++

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);