489 lines
14 KiB
C++
489 lines
14 KiB
C++
#ifndef CImageBuf_H
|
||
#define CImageBuf_H
|
||
#include "GdCPP_Exports.h"
|
||
|
||
#include "CMem.h"
|
||
#include "GdCpp.h"
|
||
#include "alog.h"
|
||
|
||
#pragma warning(disable:4250)
|
||
//#include "opencv2/opencv.hpp"
|
||
|
||
/**
|
||
* @addtogroup Memory
|
||
* @{
|
||
*
|
||
*/
|
||
|
||
// 图形基本信息,不包含内存
|
||
struct GDCPP_API sImageInfo
|
||
{
|
||
sImageInfo() = default;
|
||
|
||
int Width = 0; ///< 图像宽度
|
||
int Height = 0; ///< 图像宽高
|
||
int ColorType = 0; ///< 颜色类型。仅支持有限的颜色类型
|
||
// 由基本信息计算出来的
|
||
int BPP = 0; ///< 单像素的字节数
|
||
int LineSize = 0; ///< 一行像素的长度,大多数下等于BytePerPix*Width。由于对齐的原因,或者只截取图像一部分的原因,可能大于。
|
||
size_t ImageSize = 0; ///< 单个图像大小,<= PhysicSize
|
||
|
||
// 颜色类型与BPP的对应关系,光纤相机与dalsa相机不相同
|
||
static std::map<uint32_t, uint32_t> MapColor2BBP;
|
||
|
||
// 颜色类型与BPP的对应关系
|
||
static std::map<uint32_t, uint32_t> MapBBP2Color;
|
||
|
||
// 添加一个颜色、字节对
|
||
template<typename ColorT, typename ByteT>
|
||
static void addColor2BBP(ColorT c, ByteT b) {
|
||
MapColor2BBP.insert({ uint32_t(c), uint32_t(b) });
|
||
}
|
||
|
||
// 添加一个颜色、字节对
|
||
template<typename ColorT, typename ByteT>
|
||
static void addBBP2Color(ByteT b, ColorT c) {
|
||
MapBBP2Color.insert({ uint32_t(b), uint32_t(c) });
|
||
}
|
||
|
||
// 根据颜色查字节数
|
||
template<typename ColorT>
|
||
static uint32_t bppFromColorType(const ColorT c) {
|
||
ASSERT(c != 8);//RAW
|
||
auto it = MapColor2BBP.find(uint32_t(c));
|
||
// 没找到的认为是单字节,应用层负责确保颜色存在
|
||
if (it == MapColor2BBP.end()) return 1;
|
||
return it->second;
|
||
}
|
||
|
||
// 根据颜色查字节数
|
||
template<typename ByteT>
|
||
static uint32_t ColorTypeFromBpp(const ByteT b) {
|
||
auto it = MapBBP2Color.find(uint32_t(b));
|
||
if (it == MapBBP2Color.end()) return 0;
|
||
return it->second;
|
||
}
|
||
|
||
// 清除颜色对照表
|
||
static void clearColorBppMap() {
|
||
MapColor2BBP.clear();
|
||
MapBBP2Color.clear();
|
||
}
|
||
|
||
/// 已知图像信息计算所需要的内存大,用于预先分配足够的缓冲区
|
||
static inline size_t calcImageSize(CSize s, int t, int bpp = 0, int linealgin = 4)
|
||
{
|
||
return calcImageSize(s.cx, s.cy, t, bpp, linealgin);
|
||
}
|
||
|
||
void initImageSize(int w, int h, int t, int bpp = 0, int linealgin = 4)
|
||
{
|
||
Width = w;
|
||
Height = h;
|
||
ColorType = t;
|
||
if (bpp == 0)
|
||
BPP = bppFromColorType(t);
|
||
else
|
||
BPP = bpp;
|
||
LineSize = w * BPP;
|
||
if (linealgin != 0) AlignUp(LineSize, linealgin);
|
||
ImageSize = LineSize * h;
|
||
}
|
||
|
||
///复制宽、高、颜色、linesize, 不复制Addr、PhysicSize
|
||
void copyImageInfo(const sImageInfo* src)
|
||
{
|
||
Width = src->Width;
|
||
Height = src->Height;
|
||
BPP = src->BPP;
|
||
LineSize = src->LineSize;
|
||
ColorType = src->ColorType;
|
||
ImageSize = src->ImageSize;
|
||
}
|
||
|
||
/// 已知图像信息计算所需要的内存大,用于预先分配足够的缓冲区
|
||
static size_t calcImageSize(int w, int h, int t, int bpp = 0, int linealgin = 4)
|
||
{
|
||
if (bpp == 0) bpp = bppFromColorType(t);
|
||
size_t size = w * bpp;
|
||
if (linealgin != 0) AlignUp(size, linealgin);
|
||
size *= h;
|
||
return size;
|
||
}
|
||
};
|
||
|
||
|
||
/// 图像缓冲区通用的定义。
|
||
class GDCPP_API CImageBuf
|
||
: public sImageInfo
|
||
{
|
||
public:
|
||
CImageBuf(CMem* alloctor=nullptr)
|
||
{
|
||
allocedMem = alloctor;
|
||
}
|
||
/// 复制构造函数禁掉了
|
||
CImageBuf(const CImageBuf& src) = delete;
|
||
|
||
virtual ~CImageBuf()
|
||
{
|
||
if (allocedMem) {
|
||
allocedMem->dealloc();
|
||
delete allocedMem;
|
||
allocedMem = nullptr; // 防止重复释放
|
||
}
|
||
}
|
||
|
||
CMem* allocedMem=nullptr;
|
||
|
||
|
||
///// 指定外部缓冲区和图像基本属性。构造时addr可以为0,但必须在后续赋值后才能使用
|
||
//CImageBuf(CMem* alloctor, void* exinfo, void* addr, int w, int h, int t, int bpp = 0, int linealgin = 4)
|
||
//{
|
||
// ASSERT(alloctor !=nullptr);
|
||
// allocedMem = alloctor;
|
||
// pExInfo = exinfo;
|
||
// initImageSize(w, h, t, bpp, linealgin);
|
||
// allocedMem->alloc(addr, 0, ImageSize);
|
||
//}
|
||
|
||
//// 构造时直接分配内存。
|
||
//// 注意谨慎用于构造静态变量,因为颜色类型-字节数对照表还没初始化
|
||
//CImageBuf(CMem* alloctor, void* exinfo, int w, int h, int t, int bpp = 0, int linealgin = 4)
|
||
//{
|
||
// ASSERT(alloctor != nullptr);
|
||
// allocedMem = alloctor;
|
||
// pExInfo = exinfo;
|
||
// initImageSize(w, h, t, bpp, linealgin);
|
||
// allocedMem->alloc(ImageSize);
|
||
//}
|
||
|
||
/// 复制图像数据时的错误码
|
||
enum {
|
||
Ok = 0,
|
||
Err_DstWidth = -1,
|
||
Err_DstHeight = -2,
|
||
Warn_CropWidth = 1,
|
||
Warn_CropHeight = 2,
|
||
};
|
||
|
||
uint8_t* Addr() const { return allocedMem->Addr; } ///< 返回分配的内存地址
|
||
/// 图像缓冲区结束地址。注意只有图像是满宽的时候才有效
|
||
uint8_t* endAddr() const { return allocedMem->Addr + ImageSize; }
|
||
|
||
|
||
/// 获取(x,y)处的地址
|
||
inline uint8_t* getAddr(int x, int y)
|
||
{
|
||
return allocedMem->Addr + y * LineSize + x * BPP;
|
||
}
|
||
|
||
// ---------- 复制图像相关 ----------
|
||
|
||
/// 将来源图像缓冲区的(sx,sy,sw,sh)区域的数据复制到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同
|
||
/// 其它几个copy函数也是调用本函数。
|
||
/// @return 0表示复制ok,没有裁剪。负值表示dx目标宽度或dy大于目标高度,无法复制;正值表示能复制但有超出,超出部分截掉了。
|
||
static int copy(CImageBuf* src, int sx, int sy, int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr);
|
||
|
||
static int copy(CImageBuf* src, int sx, int sy, int sw, int sh, uint8_t* dst);
|
||
|
||
inline void copyTo(void* dst)
|
||
{
|
||
memcpy(dst, allocedMem->Addr, ImageSize);
|
||
}
|
||
|
||
|
||
/// 将来源图像缓冲区的数据全部复制到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同。
|
||
inline static int copy(CImageBuf* src, CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr)
|
||
{
|
||
return copy(src, 0, 0, src->Width, src->Height, dst, dx, dy, debugstr);
|
||
}
|
||
|
||
/// 将本源图像缓冲区的数据全部复制到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同。
|
||
/// 复制整个图像。
|
||
inline int copyTo(CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr)
|
||
{
|
||
return copy(this, 0, 0, Width, Height, dst, dx, dy, debugstr);
|
||
}
|
||
/// 将本源图像缓冲区的数据全部复制到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同。
|
||
/// 复制整个图像。
|
||
inline int copyTo(CImageBuf* dst, const char* debugstr = nullptr)
|
||
{
|
||
return copy(this, 0, 0, Width, Height, dst, 0, 0, debugstr);
|
||
}
|
||
|
||
/// 将本源图像缓冲区指定区域的数据全部到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同。
|
||
/// 复制本图像指定矩形(sx,xy, sw, wh)
|
||
inline int copyTo(int sx, int sy, int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr)
|
||
{
|
||
return copy(this, sx, sy, sw, sh, dst, dx, dy, debugstr);
|
||
}
|
||
|
||
/// 将本源图像缓冲区指定区域的数据全部到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同。
|
||
/// 复制本图像指定矩形(0,0, sw, wh)
|
||
inline int copyTo(int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr)
|
||
{
|
||
return copy(this, 0, 0, sw, sh, dst, dx, dy, debugstr);
|
||
}
|
||
|
||
///叠加图片,颜色值各取1/2
|
||
static int overlay(CImageBuf* src, int sx, int sy, int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr);
|
||
inline int overlayTo(int sx, int sy, int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr = nullptr)
|
||
{
|
||
return overlay(this, sx, sy, sw, sh, dst, dx, dy, debugstr);
|
||
}
|
||
|
||
///复制错误调试输出
|
||
static void debugCopyErr(int err, const char* exinfo);
|
||
|
||
// ---------- 保存相关 ----------
|
||
bool saveBMP(const wchar_t* filename);
|
||
bool saveRaw(std::wstring& filename);
|
||
bool loadRaw(std::wstring& filename);
|
||
bool loadRaw(const wchar_t* filename);
|
||
|
||
/// 连同图像数据一起复制
|
||
// void deepCopy(const CImageBuf & src)
|
||
// {
|
||
// uint8_t *backAddr = Addr;
|
||
// memcpy(this, &src, sizeof(CImageBuf));
|
||
// Addr = backAddr;
|
||
// memcpy(Addr, src.Addr, size_t(Height*LineSize));
|
||
// }
|
||
|
||
/// 填充颜色,要求4像素对齐
|
||
void fill4(uint32_t val);
|
||
|
||
/// 填充颜色,要求4像素对齐
|
||
void fill4(uint32_t val, int x, int y, int W, int H);
|
||
|
||
|
||
|
||
/// 从一个大图中截取一部分创建一个小图
|
||
CImageBuf *subImage(int x, int y, int w, int h)
|
||
{
|
||
auto p = new CImageBuf(new CMem);
|
||
p->initImageSize(w, h, ColorType, BPP, 0);
|
||
p->allocedMem->alloc(getAddr(x, y), LineSize * h, LineSize * h);
|
||
p->LineSize = LineSize;
|
||
return p;
|
||
}
|
||
|
||
bool initImg(int w, int h, int t, int bpp = 0, int linealgin = 4)
|
||
{
|
||
ASSERT(allocedMem != nullptr);
|
||
initImageSize(w, h, t, bpp, linealgin);
|
||
return allocedMem->alloc(ImageSize);
|
||
}
|
||
|
||
// 不改变类型和像素字节数
|
||
bool resizeImg(int w, int h, int align)
|
||
{
|
||
ASSERT(allocedMem != nullptr);
|
||
initImageSize(w, h, ColorType, BPP, align);
|
||
return allocedMem->resize(ImageSize);
|
||
}
|
||
|
||
bool resizeImg(int w, int h, int t, int bpp, int align)
|
||
{
|
||
ASSERT(allocedMem != nullptr);
|
||
initImageSize(w, h, t, bpp, align);
|
||
return allocedMem->resize(ImageSize);
|
||
}
|
||
|
||
bool reserveImg(int w, int h, int align)
|
||
{
|
||
ASSERT(allocedMem != nullptr);
|
||
initImageSize(w, h, ColorType, BPP, align);
|
||
return allocedMem->reserve(ImageSize);
|
||
}
|
||
|
||
bool reserveImg(int w, int h, int t, int bpp, int align)
|
||
{
|
||
ASSERT(allocedMem != nullptr);
|
||
initImageSize(w, h, t, bpp, align);
|
||
return allocedMem->reserve(ImageSize);
|
||
}
|
||
|
||
|
||
bool loadBMP(const wchar_t* filename)
|
||
{
|
||
if (!filename) return false;
|
||
|
||
FILE* f = nullptr;
|
||
errno_t err = _wfopen_s(&f, filename, L"rb"); // 二进制读取,支持宽字符路径
|
||
if (err != 0 || !f) {
|
||
return false;
|
||
}
|
||
|
||
bool success = false;
|
||
|
||
BITMAPFILEHEADER fileheader = {};
|
||
BITMAPINFOHEADER infoheader = {};
|
||
|
||
// 读取文件头和信息头
|
||
if (fread(&fileheader, sizeof(fileheader), 1, f) != 1 ||
|
||
fread(&infoheader, sizeof(infoheader), 1, f) != 1) {
|
||
goto cleanup;
|
||
}
|
||
|
||
do {
|
||
if (infoheader.biBitCount > BPP * 8) {
|
||
alog->error("biBitCount({}) >= BytePerPix({}) *8", infoheader.biBitCount, BPP);
|
||
break;
|
||
}
|
||
if ((infoheader.biWidth & 0x03) != 0) {
|
||
alog->error(L"Width should be multiply of 4: {}", filename);
|
||
break;
|
||
}
|
||
|
||
if (!(infoheader.biWidth == Width && (infoheader.biHeight == Height || infoheader.biHeight == -Height))) {
|
||
int h = infoheader.biHeight;
|
||
if (h < 0) h = -h;
|
||
size_t size = calcImageSize(infoheader.biWidth, h, ColorType, 0);
|
||
if (!allocedMem->reserve(size)) {
|
||
alog->error("BMP size mismatch with buffer");
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 定位到像素数据起始位置
|
||
if (_fseeki64(f, fileheader.bfOffBits, SEEK_SET) != 0) {
|
||
break;
|
||
}
|
||
|
||
if (infoheader.biHeight < 0) {
|
||
// BMP 从上到下存储(top-down)
|
||
if (fread(allocedMem->Addr, 1, infoheader.biSizeImage, f) != infoheader.biSizeImage) {
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
// BMP 从下到上存储(bottom-up)
|
||
int h = infoheader.biHeight;
|
||
char* p = (char*)(allocedMem->Addr) + LineSize * (h - 1);
|
||
for (; h > 0; h--) {
|
||
if (fread(p, 1, LineSize, f) != LineSize) {
|
||
break;
|
||
}
|
||
p -= LineSize;
|
||
}
|
||
if (h > 0) break; // 说明中途出错
|
||
}
|
||
|
||
success = true;
|
||
} while (0);
|
||
|
||
cleanup:
|
||
if (f) fclose(f);
|
||
return success;
|
||
}
|
||
|
||
#include <cstdio> // FILE, fread, fclose, _wfopen_s, _fseeki64
|
||
|
||
bool loadBMPex(const wchar_t* filename)
|
||
{
|
||
if (!filename) return false;
|
||
|
||
FILE* f = nullptr;
|
||
errno_t err = _wfopen_s(&f, filename, L"rb");
|
||
if (err != 0 || !f) {
|
||
return false;
|
||
}
|
||
|
||
bool success = false;
|
||
|
||
BITMAPFILEHEADER fileheader = {};
|
||
BITMAPINFOHEADER infoheader = {};
|
||
|
||
// 读取文件头和信息头
|
||
if (fread(&fileheader, sizeof(fileheader), 1, f) != 1 ||
|
||
fread(&infoheader, sizeof(infoheader), 1, f) != 1) {
|
||
goto cleanup;
|
||
}
|
||
|
||
do {
|
||
if ((infoheader.biWidth & 0x03) != 0) {
|
||
alog->error(L"Width should be multiply of 4: {}", filename);
|
||
break;
|
||
}
|
||
|
||
int h = infoheader.biHeight;
|
||
if (h < 0) h = -h;
|
||
|
||
// 设置颜色类型
|
||
if (infoheader.biBitCount == 8) {
|
||
ColorType = ColorTypeFromBpp(1); // 8/8 = 1
|
||
}
|
||
else if (infoheader.biBitCount == 24) {
|
||
ColorType = ColorTypeFromBpp(3); // 24/8 = 3
|
||
}
|
||
else {
|
||
alog->error("Invalid Color type (biBitCount={})", infoheader.biBitCount);
|
||
break;
|
||
}
|
||
|
||
// 初始化图像尺寸参数(你原有的逻辑)
|
||
initImageSize(infoheader.biWidth, h, ColorType, infoheader.biBitCount / 8, 4);
|
||
|
||
// 调整缓冲区大小
|
||
if (!allocedMem->resize(ImageSize)) {
|
||
alog->error("Buffer size {} < ImageSize {}", allocedMem->containerSize, ImageSize);
|
||
break; // 注意:原代码此处缺少 break,会导致继续执行!已修正
|
||
}
|
||
|
||
// 定位到像素数据起始位置
|
||
if (_fseeki64(f, fileheader.bfOffBits, SEEK_SET) != 0) {
|
||
break;
|
||
}
|
||
|
||
// 读取像素数据
|
||
if (infoheader.biHeight < 0) {
|
||
// top-down BMP:从上到下存储
|
||
size_t bytesToRead = infoheader.biSizeImage ? infoheader.biSizeImage : ImageSize;
|
||
if (fread(allocedMem->Addr, 1, bytesToRead, f) != bytesToRead) {
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
// bottom-up BMP:从下到上存储
|
||
int lines = infoheader.biHeight; // 正数
|
||
char* p = (char*)(allocedMem->Addr) + LineSize * (lines - 1);
|
||
for (int i = 0; i < lines; ++i) {
|
||
if (fread(p, 1, LineSize, f) != LineSize) {
|
||
break;
|
||
}
|
||
p -= LineSize;
|
||
}
|
||
// 检查是否完整读取
|
||
if (p != (char*)(allocedMem->Addr) - LineSize) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
success = true;
|
||
} while (0);
|
||
|
||
cleanup:
|
||
if (f) fclose(f);
|
||
return success;
|
||
}
|
||
|
||
|
||
};
|
||
|
||
|
||
|
||
#ifdef OPENCV_CORE_HPP
|
||
/// @brief 从CImageBuf生成一个cv::Mat
|
||
/// @param img
|
||
/// @return
|
||
//GDCPP_API cv::Mat toMat(const CImageBuf& img);
|
||
#endif
|
||
|
||
|
||
/** @} //end of group */
|
||
|
||
|
||
#endif // CImageBuf_H
|