bitmap display not correct - c++

tagBITMAPFILEHEADER bh;
BITMAPINFOHEADER bih;
char buf[3];
unsigned char bmp[200][600];
int width ,height;
ifstream fin(L"D:\\xx\\3.bmp",ios::_Nocreate|ios::binary);
fin.read((char*)&bh,sizeof(bh));
fin.read((char*)&bih,sizeof(bih));
width=bih.biWidth;
height=bih.biHeight;
HWND myconsole=GetConsoleWindow();
HDC mydc=GetDC(myconsole);
for(int i=height;i>=1;i--)
for(int j=1;j<=width;j++)
{
fin.read(buf,sizeof(buf));
bmp[i][j*3]=buf[0];
bmp[i][j*3+1]=buf[1];
bmp[i][j*3+2]=buf[2];
}
for(int i=1;i<=height;i++)
{
for(int j=1;j<=width;j++)
{
COLORREF color;
color=RGB(bmp[i][j*3],bmp[i][j*3+1],bmp[i][j*3+2]);
SetPixel(mydc,j,i,color);
}
//cout<<width<<endl;
}
ReleaseDC(myconsole,mydc);
fin.close();
return 0;
source bmp
display like this
i read pixel to an array, this bmp's bitcount is 24, so i use a char[3] to store the data,
but finaly display on the console like this,i can't figure it out.

Bitmap image data rows must be aligned on a 32-bit boundary. You therefore have to calculate the amount of padding that you need and skip that number of bytes at the end of every row.
In general the formula to calculate the number of bytes per row and the number you must skip is:
int rowBytes = ( (width * bytes_per_pixel) + 3 ) & ~3;
int skipBytes = rowBytes - (width * bytes_per_pixel);
Then skip skipBytes at the end of every row when reading the file.

Related

Reading BMP file into an array

I am writing a longer program and I found myself needing to read a .bmp file into an array in a specific way so that the rest of the program can use it without extensive rewrites. I failed to find older answers that would resolve my problem, and I am pretty much at the beginner stages.
The image I am trying to read is used to create a text font, so I want to read it character by character into an array, where the pixels belonging to one character are added in order to a 2d bool (true if pixel is not black) array [character_id] [pixel_n]. The dimensions of characters are predetermined and known, and the file is cropped so that they all appear in a single row with no unaccounted margins.
This is the specific file I am trying to read, though here it might not show up as .bmp
As an example, shown here, I want to read the pixels in the order of the yellow line, then jump to another character. For clarity each character is 5px wide and 11px high, with 1px of margin on both sides horizontally.
Based on what I was able to find, I have written a function to do it, but I fail to make it work as intended, as far as I can tell even the pixel values are not being read correctly:
void readBMP(char* filename)
{
int i;
FILE* f = fopen(filename, "rb");
unsigned char info[54];
// read the 54-byte header
fread(info, sizeof(unsigned char), 54, f);
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
// number of pixels in total
int size = 3 * width * height;
unsigned char* data = new unsigned char[size];
// number of characters to read
int counter1 = size / ((font_width + 2) * font_height) / 3 ;
// read the rest of the data at once
fread(data, sizeof(unsigned char), size, f);
fclose(f);
//loop that goes from character to character
for(int i = 0; i < counter1; i++)
{
int tmp = 0;
//loop that reads one character into font_ref array
for(int j = 0; j < font_height; j++)
{
//loop for each row of a character
for(int k = 0; k < font_width; k++)
{
int w = static_cast<int>(data[3*(j*(font_width+2)*(counter1) + i*(font_width + 2) + 1 + k + j*font_width + j)-1]);
if( w != 0 )
font_ref [i][(tmp)] = 1;
else
font_ref [i][(tmp)] = 0;
tmp++;
}
}
}
}
(bool font_ref [150][font_width*font_height]; is the array where the font is being loaded and stored)
this code reads something, but the result is a seemingly random mess and I am unable to resolve that. Here is an example of lowercase alphabet printed using another function in the program, where white pixels represent true bools. I am aware that some libraries exist to work with graphical files, however in this program I wanted to possibly avoid that to learn more lower-level things, and the goal is rather limited and specific.
Thank you in advance for any help with the issue.
The main errors are in the offset computation for a pixel in the bitmap data:
int w = static_cast<int>(data[3*(j*(font_width+2)*(counter1) + i*(font_width + 2) + 1 + k + j*font_width + j)-1]);
j*(font_width+2)*(counter1) - This doesn't take into account that
although you say the file is cropped, there is extra black space to the right of the last character cell, so the true width must be used;
(as drescherjm and user3386109 mentioned) padding bytes are appended to the rows so that their length is a multiple of four bytes.
+ j*font_width + j)-1 - This part makes no sense - perhaps you tried to compensate the above errors.
This would be correct:
int w = data[j*(3*width+3&~3)+3*(i*(font_width+2)+1+k)];

Optimize image buffer

Here is a code that decodes a WebM frame and put them in a buffer
image->planes[p] = pointer to the top left pixel
image->linesize[p] = strides betwen rows
framesArray = vector of unsigned char*
while ( videoDec->getImage(*image) == VPXDecoder::NO_ERROR)
{
const int w = image->getWidth(p);
const int h = image->getHeight(p);
int offset = 0;
for (int y = 0; y < h; y++)
{
// fwrite(image->planes[p] + offset, 1, w, pFile);
for(int i=0;i<w;i++){
framesArray.at(count)[i+(w*y)] = *(image->planes[p]+offset+ i) ;
}
offset += image->linesize[p];
}
}
.............................
How can I write intro buffer line by line not pixel by pixel or optimize the writing of frame intro buffer?
if the source image and destination buffer share the same Width, Height and bit per pixel, you can use std::copy to copy the whole image into it.
std::copy(image->planes[p] + offset, image->planes[p] + (image->getHeight(p) * image->linesize[p], framesArray.begin()) ;
if it is same bit per pixel but different width and height, you can use std::copy by line.

pad print line with white space C++

In C++ I am using unsigned char pointers to hold byte arrays so that I can fit 8 bit color codes in each element for a print line.
I have one array holding data, and one array holding white space, and I am using for loops to populate a third array so that the data is at the beginning and white space is at the end.
When the pointer is created, as I monitor memory at runtime, all elements have a default value of 0xCD, which is magenta on color chart. I use a for loop to populate the bytes I want with 0x00, but it will not write over the default array value of the third array.
So, I am stuck with my printer printing magenta instead of white space. Yet I can write over that array just fine with just the data. But not with the whitespace. Im unsure what is the reason for that. Can anyone give me any insight? Here is my code...
PrintLine(unsigned char* pbData, unsigned long ulDataSize, UINT xoffset)
{
if (xoffset > 0)
{ //create pointer to byte array for xoffset
unsigned char* offsetData;
offsetData = new unsigned char[(xoffset / 8)]; //x offset is divided by 8
//to convert pixels to bytes
//create pointer to byte array to hold image data and offset data
unsigned char* finalData;
finalData = new unsigned char[ulDataSize + (xoffset / 8)];
//begin final data with image data passed into the function
for (int count = 0; count < ulDataSize; count++)
{
finalData[count] = pbData[count];
}
//populate offset data with blank bytes
for (int count = 0; count < (xoffset / 8); count++)
{
offsetData[count] = 0x00;
}
//add blank data for offset to finalData
int position = 0;
for (int count = ulDataSize; count < ulDataSize + (xoffset / 8);count++)
{
finalData[ulDataSize] = offsetData[position];//also tried =0x00
position++;
}
//Send data to printer.
if (!(Write(finalData, ulDataSize + (xoffset / 8)))
{
return FALSE;
}
return TRUE;
}
}
At first glance your code don't have errors, but I see something that looks suspicious. I'm talking about the line:
finalData[ulDataSize] = offsetData[position]; //also tried =0x00
I think what you want is:
finalData[count] = offsetData[position];//also tried =0x00
On the other hand you could write your loop like this:
for (int count = 0; count < (xoffset / 8); count++)
{
finalData[ulDataSize + count] = offsetData[count];
}
Making the code much more readable.

c++ trouble with making a bitmap from scratch

I am trying to make a bitmap from scratch. I have a BYTE array (with known size) of RGB values and I would like to generate an HBITMAP.
For further clarification, the array of bytes I am working with is purely RGB values.
I have made sure that all variables are set and proper, and I believe that the issue has to do with lpvBits. I have been doing as much research for this in the past few days I have been unable to find anything that makes sense to me.
For testing purposes the width = 6 and height = 1
Code:
HBITMAP RayTracing::getBitmap(void){
BYTE * bytes = getPixels();
void * lpvBits = (void *)bytes;
HBITMAP hBMP = CreateBitmap(width, height, 1, 24, lpvBits);
return hBMP;
}
BYTE * RayTracing::getPixels(void){
Vec3 * vecs = display.getPixels();
BYTE * bytes;
bytes = new BYTE[(3 * width * height)];
for (unsigned int i = 0; i < (width * height); i++){
*bytes = static_cast<BYTE>(vecs->x);
bytes++;
*bytes = static_cast<BYTE>(vecs->y);
bytes++;
*bytes = static_cast<BYTE>(vecs->z);
bytes++;
vecs++;
}
return bytes;
}
You need to properly dword-align your array so each line is an even multiple of 4 bytes, and then skip those bytes when filling the array:
HBITMAP RayTracing::getBitmap(void)
{
BYTE * bytes = getPixels();
HBITMAP hBMP = CreateBitmap(width, height, 1, 24, bytes);
delete[] bytes;
return hBMP;
}
BYTE * RayTracing::getPixels(void)
{
Vec3 * vecs = display.getPixels(); // <-- don't forget to free if needed
int linesize = ((3 * width) + 3) & ~3; // <- 24bit pixels, width number of pixels, rounded to nearest dword boundary
BYTE * bytes = new BYTE[linesize * height];
for (unsigned int y = 0; y < height; y++)
{
BYTE *line = &bytes[linesize*y];
Vec3 *vec = &vecs[width*y];
for (unsigned int x = 0; x < width; x++)
{
*line++ = static_cast<BYTE>(vec->x);
*line++ = static_cast<BYTE>(vec->y);
*line++ = static_cast<BYTE>(vec->z);
++vec;
}
}
return bytes;
}
The third parameter of CreateBitmap should be 3, not 1. There are three color planes: Red, Green, and Blue.
Also, if you set the height to anything greater than one, you'll need to pad each row of pixels with zeroes to make the width a multiple of 4. So for a 6x2 image, after saving the 6*3 bytes for the first row, you'd need to save two zero bytes to make the row 20 bytes long.

C++: .bmp to byte array in a file

Yes i have been through the other questions that are related to this, but i found them not much help. They were some help but i am still a bit confused. So here what what i need to do:
We have a 132x65 screen. I have a 132x65 .bmp. I want to go through the .bmp and separate it into little 1x8 columns to get the binary of that 32-bit column. Then do that 132 times across, and do that 9 times down. Anything that is not white should be counted as a bit. example:
If the top left pixel of the picture is any color that is not white and the 7 pixels below that are white then that would be the first element of the array, the hex of that number, so the array would look like this:
array [] = { 0x01 } and then it would continue to fill through those 132 columns and then do it again for 9 "sections" of rows. And the file result would be ONLY that array in a separate file.
I understand the header format for this, i have read the wiki article on .bmp file formats, my main problem is i don't really know how to interact with the .bmp when i actually want it to go inside and interact with each pixel from the image. I really dont need the whole thing, but maybe just an example of grabbing each pixel from the .bmp and outputting the color of the pixel into a file or something. My c++ is a little rusty (been doing java and javscript lately).
If you want to read a known format BMP and don't care about how it's done (ie, internal-only thing) you can just take the BMP, ignore the header and use it as a pixel array. It is stored line by line starting at the bottom left. There are some detail snags for how it's packed but in my experience if you take a 32bpp image it can be completely ignored.
As a really simple example:
unsigned int *buffer;
void readfile() {
FILE *f = fopen("file.bmp", "rb");
buffer = new unsigned int[132*65];
fseek(f, 54);
fread(buffer, 132*65*4, 1, f);
fclose(f);
}
unsigned int getpixel(int x, int y) {
//assuming your x/y starts from top left, like I usually do
return buffer[(64 - y) * 132 + x];
}
I had the same problem, but by reading BMP file format description I wrote a function that reads a .BMP file and stores it into a array.
Maybe this function can help you:
unsigned int PIC::BinToNum(char *b,int bytes)
{
unsigned int tmpx = 0;
unsigned int pw = 1;
for(int i=0;i<bytes;i++)
{
tmpx += ((unsigned char)b[i]* pw);
pw = pw * 256;
}
return tmpx;
}
int PIC::Open(const char *path)
{
int pad = 0;
unsigned int sof = 0;
unsigned int tx = 0;
char tmp[4] = {0,0,0,0};
fstream file;
file.open(path,ios::in);
if(file.fail())
{
width=height=ColorBits=size=0;
return -1;
}
else
{
file.seekg(0,ios::beg);
file.read(tmp,2);
if(!(tmp[0] == 66 && tmp[1] == 77))
{
width=height=ColorBits=size=0;
return 0;
}
else
{
file.seekg(2,ios::beg); // 0x2 size
file.read(tmp,4);
size = BinToNum(tmp,4);
file.seekg(18,ios::beg); // 0x12 width
file.read(tmp,4);
width = BinToNum(tmp,4);
file.seekg(22,ios::beg); // 0x16 height
file.read(tmp,4);
height = BinToNum(tmp,4);
file.seekg(28,ios::beg); // 0x1C Bits per Pixel
file.read(tmp,2);
ColorBits = BinToNum(tmp,2);
file.seekg(10,ios::beg); // 0x0A start offset
file.read(tmp,4);
sof=BinToNum(tmp,4);
file.seekg(34,ios::beg); // 0x22 Padding
file.read(tmp,4);
pad = BinToNum(tmp,4);
pad = (int)(pad / height); // Compute Spacing in each row
pad = pad - (width*ColorBits/8);
// Initialize Matrix//
matrix = new(unsigned int[height*width]);
for(int h=height-1;h>=0;h--)
{
for(int w=0;w<=width-1;w++)
{
file.seekg(sof,ios::beg);
file.read(tmp,(int)(ColorBits/8));
tx = BinToNum(tmp,(int)(ColorBits/8));
matrix[(h*width)+w] = tx;
sof+=(int)(ColorBits/8);
}
sof +=pad;
}
}
}
file.close();
return 1;
}
Note:This functions is member of a class that i named it "PIC"...