#include "HGBmp.h" #include "../base/HGInc.h" #include "../base/HGInfo.h" 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) { HGBase_WriteInfo(HGBASE_INFOTYPE_ERROR, "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; }