#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 MapColor2BBP; // 颜色类型与BPP的对应关系 static std::map MapBBP2Color; // 添加一个颜色、字节对 template static void addColor2BBP(ColorT c, ByteT b) { MapColor2BBP.insert({ uint32_t(c), uint32_t(b) }); } // 添加一个颜色、字节对 template static void addBBP2Color(ByteT b, ColorT c) { MapBBP2Color.insert({ uint32_t(b), uint32_t(c) }); } // 根据颜色查字节数 template 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 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 // 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