📄 bmpimage.cpp
字号:
* strange bitmap file that this can't handle, but the chances of finding such
* a file this are nearly zero.
*
* Each row of pixels is always padded to a 4-byte boundary.
*/
int CBMPImage::readBitsUncompressed(FILE *fp, RGB *image, int width, int height,
int depth, RGB *colorTable)
{
UINT8 temp;
int rc, padBytes, i;
long row, column, pixel, value;
switch (depth) {
case 1:
/*
* For 1 bit per pixel, each byte is 8 pixels. Each one is an index
* into the color table (1 or 0). Most significant byte first. All
* is padded to 32-bit boundaries as well.
*/
pixel = 0;
if (((width % 32) == 0) || ((width % 32) > 24))
padBytes = 0;
else if ((width % 32) <= 8)
padBytes = 3;
else if ((width % 32) <= 16)
padBytes = 2;
else
padBytes = 1;
for (row = height; row > 0; row--)
{
for (column = width; column > 0; column -= 8)
{
rc = readUINT8little(fp, &temp);
if (rc != 0)
return rc;
for (i=0; i < ((column < 8) ? column : 8); i++)
{
/*
* For each byte read, bit-decompose it. Note that the
* last byte on a row could have less than 8 bits used.
* Most significant bits come first.
*/
value = ((temp & (1 << (7-i))) == 0) ? 0 : 1;
image[pixel].red = colorTable[value].red;
image[pixel].green = colorTable[value].green;
image[pixel].blue = colorTable[value].blue;
pixel++;
}
}
if (padBytes != 0)
{
rc = fseek(fp, padBytes, SEEK_CUR);
if (rc != 0)
return rc;
}
}
break;
case 4:
/*
* For 4 bits per pixel, each byte is two pixels. The upper half go to
* the first pixel, and the lower half to the second.
*/
pixel = 0;
if (((width % 8) == 0) || ((width % 8) > 6))
padBytes = 0;
else if ((width % 8) <= 2)
padBytes = 3;
else if ((width % 8) <= 4)
padBytes = 2;
else
padBytes = 1;
for (row = height; row > 0; row--)
{
for (column = width; column > 0; column -= 2)
{
/*
* Each byte here is two pixels. Note that the last byte on a
* row may only contain one pixel.
*/
rc = readUINT8little(fp, &temp);
if (rc != 0)
return rc;
/*
* First pixel is the upper 4 bits
*/
value = temp >> 4;
image[pixel].red = colorTable[value].red;
image[pixel].green = colorTable[value].green;
image[pixel].blue = colorTable[value].blue;
pixel++;
/*
* Second pixel is lower 4 bits. If this is the last byte in
* the row, and there are an odd number of pixels per row, then
* this is not valid data.
*/
if (column == 1)
{
value = temp & 0x0f;
image[pixel].red = colorTable[value].red;
image[pixel].green = colorTable[value].green;
image[pixel].blue = colorTable[value].blue;
pixel++;
}
}
if (padBytes != 0)
{
rc = fseek(fp, padBytes, SEEK_CUR);
if (rc != 0)
return rc;
}
}
break;
case 8:
/*
* For 8 bits per pixel, each byte is one pixel.
*/
pixel = 0;
padBytes = ((width % 4) == 0) ? 0 : (4 - (width % 4));
for (row=height; row > 0; row--)
{
for (column=width; column > 0; column--)
{
rc = readUINT8little(fp, &temp);
if (rc != 0)
return rc;
image[pixel].red = colorTable[temp].red;
image[pixel].green = colorTable[temp].green;
image[pixel].blue = colorTable[temp].blue;
pixel++;
}
if (padBytes != 0)
{
rc = fseek(fp, padBytes, SEEK_CUR);
if (rc != 0)
return rc;
}
}
break;
case 16:
/*
* For 16 bits per pixel, you must read two bytes per pixel. But
* there's a catch. The data is big endian! This is because all pixel
* data (for all formats, actually) is stored as a packed array,
* stored in pixel order.
*/
pixel = 0;
padBytes = ((width % 2) == 0) ? 0 : 2;
for (row=height; row > 0; row--)
{
for (column=width; column > 0; column--)
{
/*
* Read a 16-bit integer as big endian. Do this by reading
* two bytes and mathematically combine them. After that,
* proceed as usual.
*/
rc = readUINT8little(fp, &temp);
if (rc != 0)
return rc;
value = ((long)temp) << 8;
rc = readUINT8little(fp, &temp);
if (rc != 0)
return rc;
value |= temp;
image[pixel].red = colorTable[value].red;
image[pixel].green = colorTable[value].green;
image[pixel].blue = colorTable[value].blue;
pixel++;
}
if (padBytes != 0)
{
rc = fseek(fp, padBytes, SEEK_CUR);
if (rc != 0)
return rc;
}
}
break;
case 24:
/*
* For 24 bits per pixel, it's an RGB structure. Note that the color
* table is ignore for bit depths greater than 24 bits.
*/
pixel = 0;
padBytes = width % 4;
for (row=height; row > 0; row--)
{
for (column=width; column > 0; column--)
{
rc = readRgb(fp, image+pixel, 3);
if (rc != 0 )
return rc;
pixel++;
}
if (padBytes != 0)
{
rc = fseek(fp, padBytes, SEEK_CUR);
if (rc != 0)
return rc;
}
}
break;
}
return 0;
}
/*
* ReadMaskBitsUncompressed. Reads a monochrome mask into an array of
* characters. It assmes that there is no compression. This is very similar
* (internally) to the readBitsUncompressed function. Note that if the data
* read isn't really one-bit-deep data, you'll probably get garbage back.
*/
int CBMPImage::readMaskBitsUncompressed(FILE *fp, char *image, int width, int height)
{
UINT8 temp;
int rc, padBytes, i;
long row, column, pixel;
char value;
/*
* see the one-bit-depth part of readBitsUncompressed for comments
*/
pixel = 0;
if (((width % 32) == 0) || ((width % 32) > 24))
padBytes = 0;
else if ((width % 32) <= 8)
padBytes = 3;
else if ((width % 32) <= 16)
padBytes = 2;
else
padBytes = 1;
for (row = height; row > 0; row--)
{
for (column = width; column > 0; column -= 8)
{
rc = readUINT8little(fp, &temp);
if (rc != 0)
return rc;
for (i=0; i < ((column < 8) ? column : 8); i++)
{
value = ((temp & (1 << (7-i))) == 0) ? (char) 0 : (char) 1;
image[pixel] = value;
pixel++;
}
}
if (padBytes != 0)
{
rc = fseek(fp, padBytes, SEEK_CUR);
if (rc != 0)
return rc;
}
}
return 0;
}
/*
* reflectYRGB takes an array of RGB vales and the dimensions they represent
* and flips it vertically. This will convert a bottom-left origin array to a
* top-left origin array.
*/
void CBMPImage::reflectYRGB(RGB *image, int width, int height)
{
int row, col;
RGB temp;
for (row = 0; row < (height / 2); row++)
{
for (col = 0; col < width; col++)
{
/* Swap pixels at (x,y) with (x,height-y) */
memcpy(&temp, image+(row * width + col), sizeof(RGB));
memcpy(image+(row * width + col),
image+((height - row - 1) * width + col), sizeof(RGB));
memcpy(image+((height - row - 1) * width + col), &temp,
sizeof(RGB));
}
}
}
/*
* reflectYchar takes an array of char values and the dimensions they
* represent and flips it vertically. This will convert a bottom-left origin
* array to a top-left origin array.
*/
void CBMPImage::reflectYchar(char *image, int width, int height)
{
int row, col;
char temp;
for (row = 0; row < (height / 2); row++)
{
for (col = 0; col < width; col++)
{
/* Swap values at (x,y) with (x,height-y) */
temp = image[row * width + col];
image[row * width + col]=image[(height - row - 1) * width + col];
image[(height - row - 1) * width + col] = temp;
}
}
}
/*****************************************************************************
*
* High-level functions
*
* These functions read in specific types of bitmap files. Each assumes that
* the file pointer is positioned at the appropriate place in a bitmap file.
* (At the start of a BITMAPFILEHEADER for all functions except
* readMultipleImages, which assumes the file pointer to be positioned on the
* start of a BITMAPARRAYHEADER. These functions will leave the file pointer
* on the byte after the image's color table.
*
* The coordinate speaces in the returned arrays will have an upper-left
* origin. As before, a non-zero return value indicates that something went
* wrong.
*
* Note that the BMP and mono-ICO functions will not return 1000 if the image
* is of type color-icon. This is because a color icon consists of a bitmap
* and a monochrome icon.
*
* return values:
* 0 - success
* 1000 - incorrect file type for the routine called
* 1001 - image data out of range or damaged file
* 1002 - good data, but the routine called can't handle it (yet)
* 1003 - out of memory allocating color table
* 1004 - out of memory allocating image
* 1005 - out of memory allocating image arrays
* 1006 - Illegal image type in a multi-image array
*
* other - I/O error of some kind
*/
/*
* readSingleImageBMP will read a single BMP image and generate an array of RGB
* triples that contain the RGB values for every pixel. It will also return
* the dimensions of the image.
*/
int CBMPImage::readSingleImageBMP(FILE *fp, RGB **argb, UINT32 *width, UINT32 *height)
{
BITMAPFILEHEADER bfh;
BITMAPHEADER bh;
RGB *colorTable=NULL, *image=NULL;
int rc, depth, inverted;
long numColors, numPixels, endPos;
/*
* First, get the file header and sanity check it. The type field must be
* TYPE_BMP or we're aborting.
*/
rc = readBitmapFileHeader(fp, &bfh);
if (rc != 0)
return rc;
if ((bfh.type != TYPE_BMP) &&
(bfh.type != TYPE_ICO_COLOR) &&
(bfh.type != TYPE_PTR_COLOR))
return 1000;
/*
* Immediately following a file header is always the bitmap header. Read
* it. Sanity check any values we might need. Specifically, less than
* 32-bit depth, known compression scheme, known origin, and known color
* encoding, and valid height/width. Note that negative height is OK,
* that indicates an upper-left origin for a Windows bitmap.
*/
rc = readBitmapHeader(fp, &bh);
if (rc != 0)
return rc;
depth = bh.numBitPlanes * bh.numBitsPerPlane;
if ((depth > 32) ||
(bh.compressionScheme > COMPRESSION_LAST) ||
(bh.origin > ORIGIN_LAST) ||
(bh.colorEncoding > COLOR_ENCODING_LAST) ||
(bh.width < 1) ||
(bh.height == 0))
return 1001;
/*
* If the height is negative, then this is a Windows bitmap whose origin
* is the upper-left corner and not the lower-left. The inverted flag
* indicates a lower-left origin. Our code will be outputting an
* upper-left origin pixel array.
*/
if (bh.height < 0)
{
inverted = 0;
bh.height = -bh.height;
}
else
inverted = 1;
/*
* Now, sanity check a few fields that are valid, but I don't have code to
* deal with them yet. This includes: more than one bit plane, any
* compression scheme, and bit depths that are not 1, 4, 8, 16, or 24.
*/
if ((bh.numBitPlanes > 1) ||
((bh.numBitsPerPlane != 1) &&
(bh.numBitsPerPlane != 4) &&
(bh.numBitsPerPlane != 8) &&
(bh.numBitsPerPlane != 16) &&
(bh.numBitsPerPlane != 24)) ||
(bh.compressionScheme != COMPRESSION_NONE))
return 1002;
/*
* Allocate and read the color table. The file pointer has been
* positioned in the right place by the readBitmapHeader function. Note
* that images with 24-bits or more color depth have no color table. They
* are already RGB. When reading the color table, be sure to check for
* old/new format bitmaps.
*/
if (depth < 24)
{
numColors = 1 << depth;
colorTable = new RGB[numColors]; // (RGB *)calloc(numColors, sizeof(RGB));
if (colorTable == NULL)
return 1003;
memset(colorTable, 0, numColors * sizeof(RGB));
if (bh.size <= 12)
rc = readColorTable(fp, colorTable, numColors, 3);
else
rc = readColorTable(fp, colorTable, numColors, 4);
if (rc != 0)
{
delete [] (colorTable);
return rc;
}
}
/*
* We're at the end of the color table. Preserve this position. We'll
* need to leave the file pointer there before returning from this
* function.
*/
endPos = ftell(fp);
/*
* Allocate the array of pixels and fill it in.
*/
numPixels = bh.width * bh.height;
image = new RGB[numPixels]; //(RGB *)calloc(numPixels, sizeof(RGB));
if (image == NULL)
{
delete [] (colorTable);
return 1004;
}
memset(image, 0, numPixels * sizeof(RGB));
/*
* Seek to the bits
*/
rc = fseek(fp, bfh.offsetToBits, SEEK_SET);
if (rc != 0)
{
delete [] (colorTable);
delete [] (image);
return rc;
}
/*
* Read the bits. If code for decompressing bits should be written,
* insert the call here.
*/
switch (bh.compressionScheme) {
case COMPRESSION_NONE:
rc = readBitsUncompressed(fp, image, bh.width, bh.height, depth,
colorTable);
break;
}
if (rc != 0)
{
delete [] (image);
return rc;
}
/*
* If the origin is lower-left, flip the image upside down
*/
if (inverted)
reflectYRGB(image, bh.width, bh.height);
/*
* Return the output values. Set the file pointer to the byte after the
* color table.
*/
*argb = image;
*width = bh.width;
*height = bh.height;
fseek(fp, endPos, SEEK_SET);
/*
* Clean up and return. Note that we don't return the color table. This
* is because we're returning an array of RGB values for the image - such
* a table would be redundant.
*/
if (colorTable != NULL)
delete [] (colorTable);
return 0;
}
bool CBMPImage::LoadFile(const char *pszBMPFile)
{
FILE* fp = fopen(pszBMPFile, "rb");
if (NULL == fp)
{
return false;
}
if (0 == readSingleImageBMP(fp,
(RGB**) &m_pRGBArray,
(UINT32*) &m_iWidth,
(UINT32*) &m_iHeight))
{
fclose(fp);
return true;
}
else
{
fclose(fp);
return false;
}
}
CBMPImage::CBMPImage()
{
}
CBMPImage::~CBMPImage()
{
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -