抛弃GdCpp*.dll/pdb历史重新建库。libhv和Sqlite的dll保留
This commit is contained in:
488
include/Mem/CImageBuf.h
Normal file
488
include/Mem/CImageBuf.h
Normal file
@@ -0,0 +1,488 @@
|
||||
#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
|
||||
370
include/Mem/CMem.h
Normal file
370
include/Mem/CMem.h
Normal file
@@ -0,0 +1,370 @@
|
||||
#ifndef CMem_H
|
||||
#define CMem_H
|
||||
|
||||
#include "GdCPP_Exports.h"
|
||||
#include <stdint.h>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* @addtogroup Memory
|
||||
** @brief
|
||||
** @details
|
||||
* @{
|
||||
*
|
||||
*/
|
||||
///
|
||||
/// \brief 内存管理的基类,实现:<br>
|
||||
///
|
||||
/// 需要了解的基本概念:
|
||||
/// - 物理内存:内存条 + 内存交换文件
|
||||
/// - 虚拟内存:应用程序认为它拥有连续的可用的内存,win10 64位有128T
|
||||
/// - 内存交换文件paging file: 内存不够用了,系统将最近没使用的内存保存到文件中,用到再交换回来。
|
||||
/// - page size:系统管理内存的单元,x86、x64系统使用4-KB, IA-64用8-KB.
|
||||
/// - 内存分配粒度:windows从虚拟内存分配一个range时的最小单位,win10是64k
|
||||
/// - large page: 大内存的应用为优化内存访问以比page大的多的单位使用物理内存(好像是2M).
|
||||
/// 刚开机的时候分配没问题,电脑运行一段时间就没有足够连续的物理内存来分配了。
|
||||
///
|
||||
/// 需要了解的windows内存操作:reserve/commit/decommit/release/lock/unlock
|
||||
///
|
||||
/// 参考资料:
|
||||
/// - 《windows核心编程》第五版
|
||||
/// - [windows memory management](https://docs.microsoft.com/en-us/windows/win32/memory/memory-management)<br>
|
||||
/// - [windows memory api](https://docs.microsoft.com/en-us/windows/win32/api/memoryapi)<br>
|
||||
/// </pre>
|
||||
|
||||
|
||||
/// \brief CMem 定义了一块内存,包括逻辑、物理两个方面。
|
||||
/// - 内存的逻辑属性:包括内存地址、可用内存大小、外部容器大小。
|
||||
/// - 内存的物理方法:包括内存如何分配、释放、调整大小的函数接口。
|
||||
/// CMem作为所有内存的基类,物理层定义了一个在现有内存中取一部分使用。
|
||||
class GDCPP_API CMem
|
||||
{
|
||||
public:
|
||||
virtual ~CMem()
|
||||
{
|
||||
//dealloc(); // 基类析构没必要清空属性变量了
|
||||
};
|
||||
|
||||
// ----------- 逻辑内存 -----------
|
||||
|
||||
/// \brief 内存起始地址。可以引用,但不要直接更改,通过alloc()、dealloc()来修改
|
||||
uint8_t* Addr = 0;
|
||||
|
||||
/// \brief 可用内存大小
|
||||
size_t memSize = 0;
|
||||
|
||||
/// 容器大小,containerSize>=memSize
|
||||
/// 不同子类有不同的容器。
|
||||
/// - 基类容器就是已经分配好的一块内存
|
||||
/// - CWinMem的容器是AllocVirtual函数分配的虚拟内存空间
|
||||
size_t containerSize = 0;
|
||||
|
||||
bool selfAlloc = false; // 是否自己分配的内存,如果是,析构时需要释放
|
||||
|
||||
// ----------- 物理内存:分配、释放、调整大小 -----------
|
||||
|
||||
/// @brief 基类在指定容器中分配内存。子类需要重载。
|
||||
/// @param addr 容器所在地址
|
||||
/// @param container 容器大小
|
||||
/// @param size 实际使用大小。
|
||||
/// @return 是否分配成功
|
||||
virtual bool alloc(void* addr, size_t container, size_t size) {
|
||||
// 注意Addr当前不知道时可以赋0,container不能为0
|
||||
ASSERT(container != 0);
|
||||
ASSERT(size <= container);
|
||||
Addr = static_cast<uint8_t*>(addr);
|
||||
containerSize = container;
|
||||
memSize = size;
|
||||
selfAlloc = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief 如果子类没有重载,默认调用alloc(nullptr, container, size)
|
||||
virtual bool alloc(size_t container, size_t size)
|
||||
{
|
||||
return alloc(nullptr, container, size);
|
||||
}
|
||||
|
||||
/// @brief 如果子类没有重载,默认调用alloc(addr, size, size),而不是alloc(addr, size, 0)
|
||||
virtual bool alloc(void* addr, size_t size) {
|
||||
return alloc(addr, size, size);
|
||||
};
|
||||
|
||||
/// @brief 如果子类没有重载,默认调用alloc(nullptr, size, size),而不是alloc(nullptr, size, 0)
|
||||
virtual bool alloc(size_t size)
|
||||
{
|
||||
return alloc(nullptr, size, size);
|
||||
}
|
||||
|
||||
/// \brief 基类不自动释放内存。子类需要重载实现释放内存。
|
||||
virtual void dealloc() {
|
||||
Addr = nullptr;
|
||||
memSize = 0;
|
||||
containerSize = 0; //如果不想containerSize清0,使用resize()函数
|
||||
selfAlloc = false;
|
||||
}
|
||||
|
||||
// 下面两个函数调整内存大小,区别在于如果原先的内存有多的,一个释放掉,一个不释放只减小memSize。
|
||||
|
||||
/// 在容器范围内调整可用内存的大小,多余的释放。
|
||||
/// 如果containerSize不够时,基类只返回false
|
||||
/// 子类需要重载,决定是重新申请,还是返回false。
|
||||
virtual bool resize(size_t size) {
|
||||
if (containerSize == 0) //不知道容器大小
|
||||
return false;
|
||||
if (size <= containerSize) {
|
||||
memSize = size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/// 在容器范围内调整可用内存的大小,多余的不释放只减小memSize,不释放。
|
||||
/// 如果containerSize不够时,基类只返回false
|
||||
/// 子类需要重载,决定是重新申请,还是返回false。
|
||||
virtual bool reserve(size_t size) {
|
||||
if (containerSize == 0) //不知道容器大小
|
||||
return false;
|
||||
if (size <= containerSize) {
|
||||
memSize = size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief 从操作系统的的虚拟空间分配内存,不分配物理内存
|
||||
/// \param addr 分配到指定地址。如果不指定地址,请用后面的同名函数更方便,并增加确认内存之前是空的。
|
||||
/// \param size 空间大小,会保存到PhysicSize,并设置freeFunc指针为freeInVirtual
|
||||
/// \param commit 是否立即分配物理内存
|
||||
virtual bool _allocVirtual(void* addr, size_t size, bool commit);
|
||||
|
||||
static uint8_t* allocVirtual(size_t size);
|
||||
static void freeVirtual(uint8_t* addr);
|
||||
protected:
|
||||
/// 有些虚拟内存空间需要临时释放,再分拆、合并。
|
||||
/// 为避时释放时被别的线程占了,采用以下两个措施:<br>
|
||||
/// - 需要分拆的内存规定地址空间从后向前分配。<br>
|
||||
/// - 临时释放前加锁,重新占用后解锁
|
||||
static std::mutex uplocker;
|
||||
|
||||
/// \brief 从操作系统的的虚拟空间分配内存,不分配物理内存
|
||||
/// \param addr 分配到指定地址。如果不指定地址,请用后面的同名函数更方便,并增加确认内存之前是空的。
|
||||
/// \param size 空间大小,会保存到PhysicSize,并设置freeFunc指针为freeInVirtual
|
||||
/// \param commit 是否立即分配物理内存
|
||||
virtual bool _allocUpperVirtual(size_t size, bool commit);
|
||||
};
|
||||
|
||||
/// 统计内存使用的接口类。需要统计内存使用情况的子类继承此类。
|
||||
/// 内存统计功能没有合并到CMem的原因:
|
||||
/// 内存的分配、释放时必须功能,而内存统计是个辅助功能。
|
||||
/// 基类是在现有内存中划一块使用,是不需要统计内存使用量,使其更轻量一点。
|
||||
class GDCPP_API CMemUsage
|
||||
: virtual public CMem // 虚继承CMem,这样在统计表中访问CMemUsage指针,就可以获取其Addr、containerSize和memSize
|
||||
{
|
||||
public:
|
||||
CMemUsage()
|
||||
{
|
||||
// 构造时就自动添加到统计表,但未命名
|
||||
std::lock_guard guard(MemListLock); // 索引表加锁
|
||||
MemList.push_back(this);
|
||||
}
|
||||
|
||||
virtual ~CMemUsage()
|
||||
{
|
||||
// 析构时从统计表中删除
|
||||
std::lock_guard guard(MemListLock); // 加锁
|
||||
for (auto it = MemList.begin(); it != MemList.end(); it++) {
|
||||
if (*it == this) {
|
||||
MemList.erase(it);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 不允许复制构造
|
||||
CMemUsage(const CMemUsage&) = delete;
|
||||
|
||||
/// @brief 被统计的内存块需要起一个名称。构造时为空,需要用户层指定名称,最好不要重名,可以是中文。
|
||||
std::wstring memName;
|
||||
|
||||
/// @brief 内存类型名称,通常在子类构造时设定,方便分类统计。
|
||||
std::wstring memType;
|
||||
|
||||
/// @brief 统计已经分配的内存ContainerSize总和,调用updateUsage()、report()时更新最新值
|
||||
static size_t totalContainerSize;
|
||||
|
||||
/// @brief 统计已经分配的内存memSize总和,调用updateUsage()、report()时更新最新值
|
||||
static size_t totalMemSize;
|
||||
|
||||
/// @brief 分配的同时命名
|
||||
// virtual bool allocNamed(const wchar_t* name, size_t container, size_t size) = 0;
|
||||
virtual bool allocNamed(const std::wstring& name, size_t container, size_t size) = 0;
|
||||
|
||||
protected:
|
||||
/// @brief 所有已经内配内存索引表,用于统计所有分配的内存
|
||||
static std::list<CMemUsage*> MemList;
|
||||
|
||||
/// @brief 索引表添加、删除时加锁
|
||||
static std::mutex MemListLock;
|
||||
|
||||
/// @brief 更新统计值。内部调用,未加锁
|
||||
static void _updateUsage()
|
||||
{
|
||||
totalContainerSize = 0;
|
||||
totalMemSize = 0;
|
||||
for (auto mem : MemList)
|
||||
{
|
||||
totalContainerSize += mem->containerSize;
|
||||
totalMemSize += mem->memSize;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// @brief 更新统计值。外部调用,加锁
|
||||
static void updateUsage()
|
||||
{
|
||||
std::lock_guard guard(MemListLock); // 加锁
|
||||
_updateUsage();
|
||||
}
|
||||
|
||||
/// @brief 输出报表到str
|
||||
static void report(std::wstring& str);
|
||||
|
||||
virtual std::wstring details()
|
||||
{
|
||||
return std::wstring();
|
||||
}
|
||||
};
|
||||
|
||||
class GDCPP_API CWinMem
|
||||
: virtual public CMemUsage // 继承内存统计接口
|
||||
{
|
||||
public:
|
||||
CWinMem()
|
||||
{
|
||||
memType = L"Win"; // 标记内存内存类型
|
||||
}
|
||||
~CWinMem() override
|
||||
{
|
||||
dealloc();
|
||||
}
|
||||
using CMem::alloc; // 防止基类的同名函数被子类掩盖
|
||||
|
||||
/// @brief 分配虚拟内存
|
||||
/// \param addr 可以为0,由win自动分配一个对齐64k的地址;可以为指定地址,但必须是未分配的。
|
||||
/// \param container 虚拟内存大小,必须大于0。内部会对齐到64k
|
||||
/// \param size 提交的物理内存大小。size <= container
|
||||
/// 可以等于0,之后再用reserve()、resize()提交物理内存。
|
||||
/// \return 是否分配成功。
|
||||
virtual bool alloc(void* addr, size_t container, size_t size) override;
|
||||
|
||||
/// @brief 分配虚拟内存成功后给它命名为name
|
||||
virtual bool allocNamed(const std::wstring& name, size_t container, size_t size) override
|
||||
{
|
||||
const bool ret = alloc(nullptr, container, size);
|
||||
if (ret) memName = name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// @brief 特殊应用场景从虚拟内存的高端地址往下分配
|
||||
bool allocUpper(size_t container, size_t size);
|
||||
|
||||
virtual bool resize(size_t size) override;
|
||||
|
||||
virtual bool reserve(size_t size) override;
|
||||
|
||||
virtual void dealloc() override;
|
||||
|
||||
std::wstring details() override;
|
||||
};
|
||||
|
||||
/// 一段内存映射到一个内存映射文件,当访问超出文件顶部时,自动翻转到文件底部;超出底部时自动翻转到顶部。
|
||||
///
|
||||
/// <pre>
|
||||
/// 如下图所示:
|
||||
/// 内存 映像文件
|
||||
/// -------------
|
||||
/// | 2 |
|
||||
/// ============== --- ==============
|
||||
/// | 1 | | 1 |
|
||||
/// -------------- --------------
|
||||
/// | 2 | | 2 |
|
||||
/// ============== --- ==============
|
||||
/// | 1 |
|
||||
/// --------------
|
||||
/// </pre>
|
||||
class GDCPP_API CFileMapMem2
|
||||
: virtual public CMemUsage
|
||||
{
|
||||
public:
|
||||
CFileMapMem2()
|
||||
{
|
||||
memType = L"FileMap"; // 标记内存内存类型
|
||||
}
|
||||
~CFileMapMem2()
|
||||
{
|
||||
dealloc();
|
||||
}
|
||||
using CMem::alloc; // 防止基类的同名函数被子类掩盖
|
||||
|
||||
/// @brief 执行了以下操作:
|
||||
/// - 分配2*container大小的虚拟空间,未分配物理内存。
|
||||
/// - 创建了内存映射文件,但未映射到内存
|
||||
/// @param name 名字保存到memName,同时加了一个前缀用于创建内存映射文件
|
||||
/// @param container 内存映射文件的大小。
|
||||
/// @param size 本类此参数为0
|
||||
/// @return
|
||||
virtual bool allocNamed(const std::wstring& name, size_t container, size_t size) override final;
|
||||
|
||||
virtual void dealloc() override final;
|
||||
|
||||
/// @brief 调整大小,必须在0和containerSize两个之间调整,不支持其它数值。
|
||||
virtual bool resize(size_t size) override final;
|
||||
|
||||
/// @brief 调整大小,必须在0和containerSize两个之间调整,不支持其它数值。
|
||||
virtual bool reserve(size_t size) override final
|
||||
{
|
||||
return resize(size);
|
||||
};
|
||||
|
||||
/// @brief 本类必须使用带名称的分配函数,不带名字的直接返回false
|
||||
bool alloc(void* addr, size_t container, size_t size) override final { return false; }
|
||||
|
||||
protected:
|
||||
// 本类自己的Addr、containerSize、memSize保存供外部访问的地址空间
|
||||
// 以下3个成员保存整个内存空间、顶部、底部只读访问的映射空间
|
||||
CMem allMem; // 保存整个虚拟内存空间
|
||||
CMem TopMem; // 地址空间在顶部,映射到文件的底部
|
||||
CMem BottomMem; // 地址空间在底部,映射到文件的顶部
|
||||
|
||||
/// 内存镜像文件的句柄
|
||||
HANDLE hand=nullptr;
|
||||
|
||||
/// 共享文件的名称
|
||||
std::wstring _nativeKey;
|
||||
|
||||
/// 出错码
|
||||
int _error;
|
||||
/// 出错信息
|
||||
std::wstring _errorString;
|
||||
|
||||
bool mapFile();
|
||||
|
||||
void unmapFile(bool realloc);
|
||||
|
||||
public:
|
||||
/// @brief 验证内存映射是否正确
|
||||
/// @return
|
||||
bool verifyMap(bool compare_content=false);
|
||||
|
||||
std::wstring details() override;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @} //group
|
||||
*/
|
||||
|
||||
#endif // CMem_H
|
||||
101
include/Mem/CStack.h
Normal file
101
include/Mem/CStack.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "GdCPP_Exports.h"
|
||||
|
||||
template<typename T>
|
||||
class CStack
|
||||
{
|
||||
public:
|
||||
/// 构造函数指定预先分配的空间,和实际使用的数量
|
||||
CStack(uint32_t maxnum = 0, uint32_t num = 0)
|
||||
: _Num(num)
|
||||
, _MaxNum(maxnum)
|
||||
, _Data(nullptr)
|
||||
{
|
||||
ASSERT(_MaxNum >= _Num);
|
||||
if (_MaxNum) {
|
||||
_Data = new T[_MaxNum];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~CStack()
|
||||
{
|
||||
clearArray();
|
||||
}
|
||||
|
||||
/// 调整实际使用的数量
|
||||
bool resizeCnt(uint32_t num)
|
||||
{
|
||||
ASSERT(_MaxNum >= num);
|
||||
if (_Num != num && num <= _MaxNum) {
|
||||
_Num = num;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 调整预留空间
|
||||
bool reserveCnt(uint32_t maxnum, bool useall = false)
|
||||
{
|
||||
if (maxnum > _MaxNum) {
|
||||
clearArray();
|
||||
_MaxNum = maxnum;
|
||||
_Data = new T[_MaxNum];
|
||||
if (_Data == nullptr) return false;
|
||||
if (useall) {
|
||||
_Num = _MaxNum;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 重载[]操作符简化图像的引用,调用必须保证camid在[0, camNum-1]范围内
|
||||
inline T& operator [](size_t id)
|
||||
{
|
||||
return _Data[id];
|
||||
}
|
||||
|
||||
inline const T& operator [](size_t id) const
|
||||
{
|
||||
return _Data[id];
|
||||
}
|
||||
|
||||
inline T& at(size_t id)
|
||||
{
|
||||
return _Data[id];
|
||||
}
|
||||
|
||||
inline const T& at(size_t id) const
|
||||
{
|
||||
return _Data[id];
|
||||
}
|
||||
|
||||
inline uint32_t Num() const { return _Num; }
|
||||
inline uint32_t MaxNum() const { return _MaxNum; }
|
||||
|
||||
void clearArray() {
|
||||
delete[] _Data;
|
||||
_Data = nullptr;
|
||||
_MaxNum = 0;
|
||||
_Num = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
/// 禁用复制构造函数
|
||||
CStack(const CStack& r);
|
||||
|
||||
protected:
|
||||
/// 图形数组长度
|
||||
uint32_t _Num;
|
||||
uint32_t _MaxNum;
|
||||
|
||||
|
||||
|
||||
/// 动态分配的CImageBuf数组。数量没有使用宏CAM_MAX_NUM,因为CAM_MAX_NUM在不同应用可能发生变化。
|
||||
/// 为了通用性不在构造时动态分配,camNum指定数量;
|
||||
/// 或者默认构造空数组,使用resize函数分配。
|
||||
T* _Data=nullptr;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user