Files
GdCpp12/include/Mem/CImageBuf.h

489 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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