code_app/modules/imgfmt/HGBmp.cpp

560 lines
12 KiB
C++

#include "HGBmp.h"
#include "../base/HGInc.h"
#include "log/log.h"
extern HLOG g_hLog;
extern "C"
{
#include "libnsbmp.h"
};
HGResult HGAPI HGImgFmt_CheckBmpFile(const HGChar* fileName, HGBool* isBmp)
{
if (NULL == fileName || NULL == isBmp)
{
return HGBASE_ERR_INVALIDARG;
}
HGBmpLoadInfo info;
HGResult ret = HGImgFmt_LoadBmpImage(fileName, &info, 0, 0, NULL);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
*isBmp = HGTRUE;
return ret;
}
static void* bitmap_create(int width, int height, unsigned int state)
{
if (width <= 0 || height <= 0)
{
return NULL;
}
(void)state; /* unused */
return calloc((intptr_t)width * (intptr_t)height, 4);
}
static unsigned char* bitmap_get_buffer(void* bitmap)
{
assert(NULL != bitmap);
return (unsigned char*)bitmap;
}
static size_t bitmap_get_bpp(void* bitmap)
{
(void)bitmap; /* unused */
return 4;
}
static void bitmap_destroy(void* bitmap)
{
assert(NULL != bitmap);
free(bitmap);
}
HGResult HGAPI HGImgFmt_LoadBmpImage(const HGChar* fileName, HGBmpLoadInfo* info,
HGUInt imgType, HGUInt imgOrigin, HGImage* image)
{
if (NULL == fileName)
{
return HGBASE_ERR_INVALIDARG;
}
if (NULL == image)
{
if (0 != imgType || 0 != imgOrigin)
{
return HGBASE_ERR_INVALIDARG;
}
}
else
{
if (0 != imgType && HGBASE_IMGTYPE_BINARY != imgType && HGBASE_IMGTYPE_GRAY != imgType
&& HGBASE_IMGTYPE_BGR != imgType && HGBASE_IMGTYPE_RGB != imgType
&& HGBASE_IMGTYPE_BGRA != imgType && HGBASE_IMGTYPE_RGBA != imgType)
{
return HGBASE_ERR_INVALIDARG;
}
if (0 != imgOrigin && HGBASE_IMGORIGIN_TOP != imgOrigin && HGBASE_IMGORIGIN_BOTTOM != imgOrigin)
{
return HGBASE_ERR_INVALIDARG;
}
}
#if defined(HG_CMP_MSC)
if (0 != _access(fileName, 0))
#else
if (0 != access(fileName, 0))
#endif
{
return HGBASE_ERR_FILENOTEXIST;
}
FILE* file = fopen(fileName, "rb");
if (NULL == file)
{
return HGBASE_ERR_ACCESSDENIED;
}
#if defined(HG_CMP_MSC)
_fseeki64(file, 0, SEEK_END);
uint64_t size = _ftelli64(file);
#else
fseeko64(file, 0, SEEK_END);
uint64_t size = ftello64(file);
#endif
if (0 == size)
{
fclose(file);
file = NULL;
return HGBASE_ERR_FILEERROR;
}
uint8_t* buffer = (uint8_t*)malloc((size_t)size);
if (NULL == buffer)
{
fclose(file);
file = NULL;
return HGBASE_ERR_OUTOFMEMORY;
}
#if defined(HG_CMP_MSC)
_fseeki64(file, 0, SEEK_SET);
#else
fseeko64(file, 0, SEEK_SET);
#endif
size_t readSize = fread(buffer, 1, (size_t)size, file);
if (readSize != size)
{
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return HGBASE_ERR_FAIL;
}
bmp_image bmp;
bmp_bitmap_callback_vt bitmap_callbacks = { bitmap_create, bitmap_destroy, bitmap_get_buffer, bitmap_get_bpp };
bmp_create(&bmp, &bitmap_callbacks);
if (BMP_OK != bmp_analyse(&bmp, (size_t)size, buffer))
{
bmp_finalise(&bmp);
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return HGIMGFMT_ERR_FAIL;
}
if (NULL != info)
{
info->width = bmp.width;
info->height = bmp.height;
info->bitCount = bmp.bpp;
info->compression = (uint32_t)bmp.encoding;
info->xPelsPerMeter = bmp.x_pels_per_meter;
info->yPelsPerMeter = bmp.y_pels_per_meter;
}
if (NULL != image)
{
if (BMP_OK != bmp_decode(&bmp))
{
bmp_finalise(&bmp);
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return HGIMGFMT_ERR_FAIL;
}
if (0 == imgType)
{
imgType = HGBASE_IMGTYPE_RGB;
if (1 == bmp.bpp || 4 == bmp.bpp || 8 == bmp.bpp)
{
bool bGray = true;
bool bBinary = true;
for (uint32_t i = 0; i < bmp.colours; ++i)
{
HGByte red = (bmp.colour_table[i] >> 16) & 0xff;
HGByte green = (bmp.colour_table[i] >> 8) & 0xff;
HGByte blue = (bmp.colour_table[i]) & 0xff;
if (red != green || red != blue || green != blue)
{
bGray = false;
bBinary = false;
break;
}
if (red != 0 && red != 255)
{
bBinary = false;
}
}
if (bBinary)
imgType = HGBASE_IMGTYPE_BINARY;
else if (bGray)
imgType = HGBASE_IMGTYPE_GRAY;
}
else if (32 == bmp.bpp)
{
imgType = HGBASE_IMGTYPE_RGBA;
}
}
if (imgOrigin == 0)
{
imgOrigin = HGBASE_IMGORIGIN_TOP;
}
HGImageInfo bmpImageInfo;
bmpImageInfo.width = bmp.width;
bmpImageInfo.height = bmp.height;
bmpImageInfo.type = HGBASE_IMGTYPE_RGBA;
bmpImageInfo.widthStep = bmp.width * 4;
bmpImageInfo.origin = HGBASE_IMGORIGIN_TOP;
HGImage image2 = NULL;
HGResult ret = HGBase_CreateImageWithData((HGByte*)bmp.bitmap, &bmpImageInfo, &image2);
if (HGBASE_ERR_OK != ret)
{
bmp_finalise(&bmp);
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return ret;
}
uint32_t xDpi = (uint32_t)((double)bmp.x_pels_per_meter / 39.3700787 + 0.5);
uint32_t yDpi = (uint32_t)((double)bmp.y_pels_per_meter / 39.3700787 + 0.5);
HGBase_SetImageDpi(image2, xDpi, yDpi);
ret = HGBase_CloneImage(image2, imgType, imgOrigin, image);
HGBase_DestroyImage(image2);
if (HGBASE_ERR_OK != ret)
{
bmp_finalise(&bmp);
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return ret;
}
}
bmp_finalise(&bmp);
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return HGBASE_ERR_OK;
}
HGResult HGAPI HGImgFmt_SaveBmpImage(HGImage image, const HGBmpSaveInfo* info, const HGChar* fileName)
{
if (NULL == image || NULL == fileName)
{
return HGBASE_ERR_INVALIDARG;
}
if (NULL != info)
{
// 检查合法性
}
HGImageInfo imgInfo;
HGBase_GetImageInfo(image, &imgInfo);
uint32_t width = imgInfo.width;
uint32_t height = imgInfo.height;
uint32_t widthStep = imgInfo.widthStep;
uint32_t type = imgInfo.type;
uint32_t origin = imgInfo.origin;
HGUInt bpp = 0;
if (HGBASE_IMGTYPE_BINARY == type)
bpp = 1;
else if (HGBASE_IMGTYPE_GRAY == type)
bpp = 8;
else if (HGBASE_IMGTYPE_BGR == type || HGBASE_IMGTYPE_RGB == type)
bpp = 24;
else if (HGBASE_IMGTYPE_BGRA == type || HGBASE_IMGTYPE_RGBA == type)
bpp = 32;
assert(0 != bpp);
HGUInt stdWidthStep = ((width * bpp + 31) & ~31) >> 3;
if (widthStep != stdWidthStep)
{
HGImage imgTemp = NULL;
HGImageRoi imageRoi;
HGBase_GetImageROI(image, &imageRoi);
HGBase_ResetImageROI(image);
HGResult ret = HGBase_CloneImage(image, 0, 0, &imgTemp);
HGBase_SetImageROI(image, &imageRoi);
if (HGBASE_ERR_OK != ret)
{
return ret;
}
ret = HGImgFmt_SaveBmpImage(imgTemp, info, fileName);
HGBase_DestroyImage(imgTemp);
return ret;
}
FILE* file = fopen(fileName, "wb");
if (NULL == file)
{
ErrorLog(g_hLog, "HGImgFmt_SaveBmpImage: fopen fail, %s errno=%d", fileName, errno);
return HGBASE_ERR_ACCESSDENIED;
}
uint8_t* buffer = (uint8_t*)malloc(widthStep * height);
if (NULL == buffer)
{
fclose(file);
file = NULL;
return HGBASE_ERR_OUTOFMEMORY;
}
uint8_t fh[14];
*(uint16_t*)fh = 0x4D42;
*(uint16_t*)(fh + 6) = 0;
*(uint16_t*)(fh + 8) = 0;
uint8_t ih[40];
*(uint32_t*)ih = 40;
*(int32_t*)(ih + 4) = (int32_t)width;
*(int32_t*)(ih + 8) = (int32_t)height;
*(uint16_t*)(ih + 12) = 1;
*(uint32_t*)(ih + 16) = 0;
*(uint32_t*)(ih + 20) = (widthStep * height);
*(int32_t*)(ih + 24) = 0;
*(int32_t*)(ih + 28) = 0;
*(uint32_t*)(ih + 32) = 0;
*(uint32_t*)(ih + 36) = 0;
if (NULL != info)
{
*(int32_t*)(ih + 24) = (int32_t)info->xPelsPerMeter;
*(int32_t*)(ih + 28) = (int32_t)info->yPelsPerMeter;
}
else
{
HGUInt xDpi, yDpi;
HGBase_GetImageDpi(image, &xDpi, &yDpi);
*(int32_t*)(ih + 24) = (uint32_t)((double)xDpi * 39.3700787 + 0.5);
*(int32_t*)(ih + 28) = (uint32_t)((double)yDpi * 39.3700787 + 0.5);
}
uint8_t* data;
HGBase_GetImageData(image, &data);
if (HGBASE_IMGTYPE_BINARY == type)
{
*(uint32_t*)(fh + 2) = 54 + 4 * 2 + widthStep * height;
*(uint32_t*)(fh + 10) = 54 + 4 * 2;
*(uint16_t*)(ih + 14) = 1;
uint32_t colorTable[2];
colorTable[0] = 0x00000000;
colorTable[1] = 0x00FFFFFF;
fwrite(fh, 14, 1, file);
fwrite(ih, 40, 1, file);
fwrite(colorTable, 4, 2, file);
if (HGBASE_IMGORIGIN_BOTTOM == origin)
{
fwrite(data, 1, widthStep * height, file);
}
else
{
uint8_t* dataEx = data + (height - 1) * widthStep;
while (dataEx >= data)
{
fwrite(dataEx, 1, widthStep, file);
dataEx -= widthStep;
}
}
}
else if (HGBASE_IMGTYPE_GRAY == type)
{
*(uint32_t*)(fh + 2) = 54 + 4 * 256 + widthStep * height;
*(uint32_t*)(fh + 10) = 54 + 4 * 256;
*(uint16_t*)(ih + 14) = 8;
uint32_t colorTable[256];
//#pragma omp parallel for
for (int32_t i = 0; i < 256; ++i)
{
uint32_t v = (i & 0x000000FF) | ((i << 8) & 0x0000FF00) | ((i << 16) & 0x00FF0000);
colorTable[i] = v;
}
fwrite(fh, 14, 1, file);
fwrite(ih, 40, 1, file);
fwrite(colorTable, 4, 256, file);
if (HGBASE_IMGORIGIN_BOTTOM == origin)
{
fwrite(data, 1, widthStep * height, file);
}
else
{
uint8_t* dataEx = data + (height - 1) * widthStep;
while (dataEx >= data)
{
fwrite(dataEx, 1, widthStep, file);
dataEx -= widthStep;
}
}
}
else if (HGBASE_IMGTYPE_RGB == type)
{
*(uint32_t*)(fh + 2) = 54 + widthStep * height;
*(uint32_t*)(fh + 10) = 54;
*(uint16_t*)(ih + 14) = 24;
fwrite(fh, 14, 1, file);
fwrite(ih, 40, 1, file);
//#pragma omp parallel for
for (int32_t i = 0; i < (int32_t)height; ++i)
{
uint8_t* pEx = data + (uintptr_t)i * (uintptr_t)widthStep;
uint8_t* pExEnd = pEx + width * 3;
uint8_t* pDestEx = buffer + (uintptr_t)i * (uintptr_t)widthStep;
while (pEx < pExEnd)
{
pDestEx[0] = pEx[2];
pDestEx[1] = pEx[1];
pDestEx[2] = pEx[0];
pEx += 3;
pDestEx += 3;
}
}
if (HGBASE_IMGORIGIN_BOTTOM == origin)
{
fwrite(buffer, 1, widthStep * height, file);
}
else
{
uint8_t* bufferEx = buffer + (height - 1) * widthStep;
while (bufferEx >= buffer)
{
fwrite(bufferEx, 1, widthStep, file);
bufferEx -= widthStep;
}
}
}
else if (HGBASE_IMGTYPE_BGR == type)
{
*(uint32_t*)(fh + 2) = 54 + widthStep * height;
*(uint32_t*)(fh + 10) = 54;
*(uint16_t*)(ih + 14) = 24;
fwrite(fh, 14, 1, file);
fwrite(ih, 40, 1, file);
if (HGBASE_IMGORIGIN_BOTTOM == origin)
{
fwrite(data, 1, widthStep * height, file);
}
else
{
uint8_t* dataEx = data + (height - 1) * widthStep;
while (dataEx >= data)
{
fwrite(dataEx, 1, widthStep, file);
dataEx -= widthStep;
}
}
}
else if (HGBASE_IMGTYPE_BGRA == type)
{
*(uint32_t*)(fh + 2) = 54 + widthStep * height;
*(uint32_t*)(fh + 10) = 54;
*(uint16_t*)(ih + 14) = 32;
fwrite(fh, 14, 1, file);
fwrite(ih, 40, 1, file);
if (HGBASE_IMGORIGIN_BOTTOM == origin)
{
fwrite(data, 1, widthStep * height, file);
}
else
{
uint8_t* dataEx = data + (height - 1) * widthStep;
while (dataEx >= data)
{
fwrite(dataEx, 1, widthStep, file);
dataEx -= widthStep;
}
}
}
else
{
assert(HGBASE_IMGTYPE_RGBA == type);
*(uint32_t*)(fh + 2) = 54 + widthStep * height;
*(uint32_t*)(fh + 10) = 54;
*(uint16_t*)(ih + 14) = 32;
fwrite(fh, 14, 1, file);
fwrite(ih, 40, 1, file);
//#pragma omp parallel for
for (int32_t i = 0; i < (int32_t)height; ++i)
{
uint8_t* pEx = data + (uintptr_t)i * (uintptr_t)widthStep;
uint8_t* pExEnd = pEx + width * 4;
uint8_t* pDestEx = buffer + (uintptr_t)i * (uintptr_t)widthStep;
while (pEx < pExEnd)
{
pDestEx[0] = pEx[2];
pDestEx[1] = pEx[1];
pDestEx[2] = pEx[0];
pDestEx[3] = pEx[3];
pEx += 4;
pDestEx += 4;
}
}
if (HGBASE_IMGORIGIN_BOTTOM == origin)
{
fwrite(buffer, 1, widthStep * height, file);
}
else
{
uint8_t* bufferEx = buffer + (height - 1) * widthStep;
while (bufferEx >= buffer)
{
fwrite(bufferEx, 1, widthStep, file);
bufferEx -= widthStep;
}
}
}
free(buffer);
buffer = NULL;
fclose(file);
file = NULL;
return HGBASE_ERR_OK;
}