Sign in to follow this  
timaer

help! odd bitmap loading

Recommended Posts

I've tried to load a 24bit bitmap to the primary surface,but whenever I set the bitmap_width odd number,the bitmap would be disordered.To the contrary ,after I set the bitmap_width even,It can be display correctly. I've tested that the bitmap_height seems no relation with this problem.Anyone could give me some advice? thanks! some crucial code is below: lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); // get video pointer to primary surfce DWORD *primary_buffer = (DWORD *)ddsd.lpSurface; // process each line and copy it into the primary buffer for (int index_y = 0; index_y < bitmap_height; index_y++) { for (int index_x = 0; index_x < bitmap_width; index_x++) { // get BGR values UCHAR blue = (bitmap.buffer[index_y*bitmap_width*3 + index_x*3 + 0]), green = (bitmap.buffer[index_y*bitmap_width*3 + index_x*3 + 1]), red = (bitmap.buffer[index_y*bitmap_width*3 + index_x*3 + 2]); // this builds a 32 bit color value in A.8.8.8 format (8-bit alpha mode) DWORD pixel = _RGB32BIT(0,red,green,blue); // write the pixel primary_buffer[index_x + (index_y*ddsd.lPitch >> 2)] = pixel; } // end for index_x } // end for index_y // now unlock the primary surface if (FAILED(lpddsprimary->Unlock(NULL))) return(0);

Share this post


Link to post
Share on other sites
Did you take the line padding in account?

The bitmap data in Windows bitmaps is padded to full 32 bits per line. If the line with (in bytes) is not divisible by 4 the data is padded. This data can be discarded on loading.

Also, when you're using D3DFMT_A8R8G8B8 consider setting alpha to 255 instead of 0.

Your copy code looks ok so far.

Share this post


Link to post
Share on other sites
Thank you so much,Endurion.I know your meaning,and I guess that's the key to resolve this problem.but I don't how to do it.

As you mentioned,windows need one line's total bytes be divisible by 4,so I can set the 24bit bitmap_width such as 20,40,60 pixel,since one line 20*3byte ,40*3byte,60*3byte all can be divisible by 4.

so,what if I need setting the bitmap_width such as 10,30,50,in that case,10*3byte ,30*3byte and 50*3byte can't be divisible by 4,what should I do to avoid losing data?

Share this post


Link to post
Share on other sites
You do the changes in the loading routine. If your 24bit bitmap has a width of 10 pixels, the bitmap data in one line equals 3 byte * 10 pixel = 30 bytes.

So you read 30 bytes into your bitmap buffer. Following in the file are 2 padding bytes (to make 32 bytes). Simply skip them in the loading routine (read 2 bytes but don't do anything with them). Continue with the next line.

This boils down that you may have to change the reading from one big block of memory into the single lines.

Share this post


Link to post
Share on other sites
thanks ,Endurion.I tried to skip reading the padding 2 byte,but the bitmap was still displayed disorderly.

my revised sample code is below:(Assume that I need to draw a 10*10 pixel bitmap)

for (int index_y = 0; index_y < 10; index_y++)
{
for (int index_x = 0; index_x < 10; index_x++)
{

UCHAR blue = (bitmap.buffer[index_y*(10*3+2) + index_x*3 + 0]),
green = (bitmap.buffer[index_y*(10*3+2) + index_x*3 + 1]),
red = (bitmap.buffer[index_y*(10*3+2) + index_x*3 + 2]);


DWORD pixel = _RGB32BIT(0,red,green,blue);


primary_buffer[index_x + (index_y*ddsd.lPitch >> 2)] = pixel;

}

}

Is there anything wrong with my code?:(

Share this post


Link to post
Share on other sites
It looks ok. I would separate the drawing from the loading even more though (right now your drawing code needs to be specialized for the bitmap size).

How do you fill your bitmap.buffer? A full block read?

If you did skip the bytes on reading into bitmap.buffer already you do not need to skip them again in the drawing code.

Share this post


Link to post
Share on other sites
Your message is so helpful.Now I think the problem probably caused by the loading procedure.

I'm reading a book written by Andre Lamoth,and I've been using the Load_Bitmap_File function in the book's CD.Since I'm not aware that how windows loading bitmap file.

the function is below ,could you tell me how to revised it?

int Load_Bitmap_File(BITMAP_FILE_PTR bitmap, char *filename)
{
// this function opens a bitmap file and loads the data into bitmap

int file_handle, // the file handle
index; // looping index

UCHAR *temp_buffer = NULL; // used to convert 24 bit images to 16 bit
OFSTRUCT file_data; // the file data information

// open the file if it exists
if ((file_handle = OpenFile(filename,&file_data,OF_READ))==-1)
return(0);

// now load the bitmap file header
_lread(file_handle, &bitmap->bitmapfileheader,sizeof(BITMAPFILEHEADER));

// test if this is a bitmap file
if (bitmap->bitmapfileheader.bfType!=BITMAP_ID)
{
// close the file
_lclose(file_handle);

// return error
return(0);
} // end if

// now we know this is a bitmap, so read in all the sections

// first the bitmap infoheader

// now load the bitmap file header
_lread(file_handle, &bitmap->bitmapinfoheader,sizeof(BITMAPINFOHEADER));

// now load the color palette if there is one
if (bitmap->bitmapinfoheader.biBitCount == 8)
{
_lread(file_handle, &bitmap->palette,MAX_COLORS_PALETTE*sizeof(PALETTEENTRY));

// now set all the flags in the palette correctly and fix the reversed
// BGR RGBQUAD data format
for (index=0; index < MAX_COLORS_PALETTE; index++)
{
// reverse the red and green fields
int temp_color = bitmap->palette[index].peRed;
bitmap->palette[index].peRed = bitmap->palette[index].peBlue;
bitmap->palette[index].peBlue = temp_color;

// always set the flags word to this
bitmap->palette[index].peFlags = PC_NOCOLLAPSE;
} // end for index

} // end if

// finally the image data itself
_lseek(file_handle,-(int)(bitmap->bitmapinfoheader.biSizeImage),SEEK_END);

// now read in the image, if the image is 8 or 16 bit then simply read it
// but if its 24 bit then read it into a temporary area and then convert
// it to a 16 bit image

if (bitmap->bitmapinfoheader.biBitCount==8 || bitmap->bitmapinfoheader.biBitCount==16 ||
bitmap->bitmapinfoheader.biBitCount==24)
{
// delete the last image if there was one
if (bitmap->buffer)
free(bitmap->buffer);

// allocate the memory for the image
if (!(bitmap->buffer = (UCHAR *)malloc(bitmap->bitmapinfoheader.biSizeImage)))
{
// close the file
_lclose(file_handle);

// return error
return(0);
} // end if

// now read it in
_lread(file_handle,bitmap->buffer,bitmap->bitmapinfoheader.biSizeImage);

} // end if
else
{
// serious problem
return(0);

} // end else

#if 0
// write the file info out
printf("\nfilename:%s \nsize=%d \nwidth=%d \nheight=%d \nbitsperpixel=%d \ncolors=%d \nimpcolors=%d",
filename,
bitmap->bitmapinfoheader.biSizeImage,
bitmap->bitmapinfoheader.biWidth,
bitmap->bitmapinfoheader.biHeight,
bitmap->bitmapinfoheader.biBitCount,
bitmap->bitmapinfoheader.biClrUsed,
bitmap->bitmapinfoheader.biClrImportant);
#endif

// close the file
_lclose(file_handle);

// flip the bitmap
Flip_Bitmap(bitmap->buffer,
bitmap->bitmapinfoheader.biWidth*(bitmap->bitmapinfoheader.biBitCount/8),
bitmap->bitmapinfoheader.biHeight);

// return success
return(1);

} // end Load_Bitmap_File

plus:By the way,you metioned "A full block read",I guess that means in the loading procedure we can read data by block.Anyway,I can't figure out a method in drawing code to make it possible writing bitmap's data into direct surface by block(since before writting,data have to be encoded 32 bit format?)If you have any idea to make the drawing procedure faster ,I'll be glad to hear that.Thanks so much.

Share this post


Link to post
Share on other sites
I'm having a problem loading my bitmaps too. I'm not using the same language as timaer. I'm using c++ and windows API. I have the resource done correctly and the pictures are bmp format. Everything in the code seems fine, but I think I may have to have the the size of the bitmap a certain width/height. Any idea what I did wrong? If you need to see the code I'll post it.

Share this post


Link to post
Share on other sites
My idea would be along those lines.

A few comments: biSizeImage can be set to 0, it is a valid value. You could simply calculate the needed size yourself. Also note that biHeight might be negative to indicate flipped top-down (Therefore the abs).


// allocate the memory for the image
size_t iNeededMemoryPerLine = bitmap->bitmapinfoheader.biWidth * bitmap->bitmapinfoheader.biBitCount / 8;
size_t iNeededMemory = iNeededMemoryPerLine * abs( bitmap->bitmapinfoheader.biHeight );

// Calculate padding bytes (this is not optimized but shows the way to think)
size_t iTemp = iNeededMemoryPerLine;
size_t iPaddingBytes = 0;

while ( iTemp % 4 )
{
++iPaddingBytes;
++iTemp;
}

if ( !( bitmap->buffer = (UCHAR*)malloc( iNeededMemory ) ) )
{
// close the file
_lclose(file_handle);
// return error
return 0;
}

// now read it in
for ( int i = 0; i < abs( bitmap->bitmapinfoheader.biHeight ); ++i )
{
// read one line
_lread( file_handle, bitmap->buffer + i * iNeededMemoryPerLine, iNeededMemoryPerLine );
// skip padding if needed
_lseek( file_handle, iPaddingBytes, SEEK_CUR );
}



Now that the padding is taken care of you can make the drawing code generic again:


lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);

// get video pointer to primary surfce
DWORD *primary_buffer = (DWORD *)ddsd.lpSurface;

// process each line and copy it into the primary buffer
for (int index_y = 0; index_y < bitmap_height; index_y++)
{
for (int index_x = 0; index_x < bitmap_width; index_x++)
{
// get BGR values
UCHAR blue = (bitmap.buffer[index_y*bitmap_width*3 + index_x*3 + 0]),
green = (bitmap.buffer[index_y*bitmap_width*3 + index_x*3 + 1]),
red = (bitmap.buffer[index_y*bitmap_width*3 + index_x*3 + 2]);

// this builds a 32 bit color value in A.8.8.8 format (8-bit alpha mode)
DWORD pixel = _RGB32BIT(0,red,green,blue);

// write the pixel
primary_buffer[index_x + (index_y*ddsd.lPitch >> 2)] = pixel;
} // end for index_x
} // end for index_y


If you want to optimize for direct screen copying you have to change the image data on loading to 32 bit already. Then you could do memcpy for the single lines which will speed the drawing up tremendously.

This is typed off my head, there may be errors inside (although i hope not).

Share this post


Link to post
Share on other sites
Hi,Endurion,you are so fantabulous.You code works perfectly.The problem has been resolved.

Plus:It seems that if I need to load a odd width pixel bitmap,I have to read the bitmap's data line by line,is that means,in most cases,we probably use the even width pixel bitmap such as 40*40 ,160*160 etc..

Or do you have any better way to load the bitmap of any size? In addtion,you mentioned that if I want to speed up the drawing procedure,I have to "change the image data on loading to 32 bit already",How can I do it ,could you explain more detailedly.

At last,appreciate so much,I learned so much from you.

Share this post


Link to post
Share on other sites
If you use the loading code like i showed you can load odd and even sized images. The image data will be appended in memory without the padding.

A second step to get the faster drawing would be to convert the 24bit bitmap.buffer to a 32bit bitmap.buffer. You modify the data the same way you do in your drawing code right now.



// allocate 32 bit bitmap buffer image
bitmap32->buffer = (UCHAR*)malloc( bitmap->width * bitmap->height * 4 );

for ( int i = 0; i < bitmap->height; ++i )
{
for ( int j = 0; j < bitmap->width; ++j )
{
UCHAR blue = (bitmap.buffer[i * bitmap->width * 3 + j * 3 + 0]),
green = (bitmap.buffer[i * bitmap->width * 3 + j * 3 + 1]),
red = (bitmap.buffer[i * bitmap_width*3 + j * 3 + 2]);

// this builds a 32 bit color value in A.8.8.8 format (8-bit alpha mode)
// set alpha to 255 (fully opaque)
DWORD pixel = _RGB32BIT( 255, red, green, blue );

// set the modified pixel into the 32bit bitmap
*(DWORD*)( bitmap32->buffer + i * bitmap->width * 4 + j * 4 ) = pixel;
}
}


The drawing can be modified like this then:


// process each line and copy it into the primary buffer
for (int index_y = 0; index_y < bitmap_height; index_y++)
{
memcpy( primary_buffer + index_y * ( ddsd.lPitch >> 2 ),
bitmap32.buffer + index_y * bitmap32.width * 4,
bitmap32.width * 4 );

} // end for index_y


That's as fast as you can get without using ddraw surface blits. And with recent ddraw driver problems you're actually better off.

Note that that drawing code only works for 32bit buffer to 32bit surface!


Note: The code snippets will not compile like this, you have to create a bitmap32 buffer and also insert the correct addressing of the variable. Should be easy though ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this