392 lines
10 KiB
C++
392 lines
10 KiB
C++
#include "pch.h"
|
||
|
||
#include "Mem\CImageBuf.h"
|
||
#include <Windows.h>
|
||
#include "GdCpp.h"
|
||
//#include "json.h"
|
||
#include "aLog.h"
|
||
#include <cstdio> // FILE, _wfopen_s, fwrite, fclose
|
||
|
||
// 颜色类型与BPP的对应关系
|
||
std::map<uint32_t, uint32_t> sImageInfo::MapColor2BBP;
|
||
// 颜色类型与BPP的对应关系
|
||
std::map<uint32_t, uint32_t> sImageInfo::MapBBP2Color;
|
||
|
||
static RGBQUAD bmiColors[256] = { 0 };
|
||
|
||
bool CImageBuf::saveBMP(const wchar_t* filename)
|
||
{
|
||
if (!filename) return false;
|
||
|
||
FILE* f = nullptr;
|
||
errno_t err = _wfopen_s(&f, filename, L"wb"); // 二进制写入,覆盖创建
|
||
if (err != 0 || !f) {
|
||
return false;
|
||
}
|
||
|
||
bool ret = false;
|
||
|
||
do {
|
||
BITMAPFILEHEADER fileheader = {};
|
||
BITMAPINFOHEADER infoheader = {};
|
||
static const char _blank[2] = { 0 };
|
||
|
||
// 填充信息头
|
||
infoheader.biSize = sizeof(BITMAPINFOHEADER);
|
||
infoheader.biWidth = Width;
|
||
infoheader.biHeight = -Height; // top-down BMP
|
||
infoheader.biPlanes = 1;
|
||
infoheader.biBitCount = BPP * 8;
|
||
infoheader.biCompression = BI_RGB; // 0 = BI_RGB
|
||
infoheader.biSizeImage = LineSize * Height;
|
||
infoheader.biXPelsPerMeter = 3780;
|
||
infoheader.biYPelsPerMeter = 3780;
|
||
infoheader.biClrUsed = 0;
|
||
infoheader.biClrImportant = 0;
|
||
|
||
// 文件头
|
||
fileheader.bfType = 0x4D42; // 'BM' = 19778
|
||
fileheader.bfReserved1 = 0;
|
||
fileheader.bfReserved2 = 0;
|
||
|
||
// 计算像素数据偏移
|
||
if (BPP == 3) {
|
||
fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 2; // +2 for alignment
|
||
}
|
||
else if (BPP == 1) {
|
||
fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD) + 2;
|
||
}
|
||
else {
|
||
// 不支持的 BPP
|
||
break;
|
||
}
|
||
|
||
fileheader.bfSize = fileheader.bfOffBits + infoheader.biSizeImage;
|
||
|
||
// 写入文件头
|
||
if (fwrite(&fileheader, sizeof(fileheader), 1, f) != 1) break;
|
||
if (fwrite(&infoheader, sizeof(infoheader), 1, f) != 1) break;
|
||
|
||
// 写入调色板(仅 8-bit)
|
||
if (BPP == 1) {
|
||
if (bmiColors[255].rgbRed == 0) {
|
||
for (int i = 0; i < 256; i++) {
|
||
bmiColors[i].rgbRed = (BYTE)i;
|
||
bmiColors[i].rgbGreen = (BYTE)i;
|
||
bmiColors[i].rgbBlue = (BYTE)i;
|
||
bmiColors[i].rgbReserved = 0;
|
||
}
|
||
}
|
||
if (fwrite(bmiColors, sizeof(RGBQUAD), 256, f) != 256) break;
|
||
}
|
||
|
||
// 写入对齐填充(2 字节)
|
||
if (fwrite(_blank, sizeof(_blank), 1, f) != 1) break;
|
||
|
||
// 写入像素数据
|
||
if (fwrite(Addr(), 1, infoheader.biSizeImage, f) != infoheader.biSizeImage) break;
|
||
|
||
ret = true;
|
||
} while (0);
|
||
|
||
// 错误处理:记录失败原因(可选)
|
||
if (!ret) {
|
||
alog->error(L"Failed to write BMP file: {}", filename);
|
||
}
|
||
|
||
if (f) fclose(f);
|
||
return ret;
|
||
}
|
||
|
||
// 保存原始数据
|
||
bool CImageBuf::saveRaw(std::wstring& filename)
|
||
{
|
||
if (filename.empty()) return false;
|
||
|
||
FILE* f = nullptr;
|
||
errno_t err = _wfopen_s(&f, filename.c_str(), L"wb");
|
||
if (err != 0 || !f) {
|
||
return false;
|
||
}
|
||
|
||
size_t written = fwrite(Addr(), 1, ImageSize, f);
|
||
fclose(f);
|
||
|
||
return (written == ImageSize);
|
||
}
|
||
|
||
// 从 std::wstring 加载原始数据
|
||
bool CImageBuf::loadRaw(std::wstring& filename)
|
||
{
|
||
if (filename.empty()) return false;
|
||
|
||
FILE* f = nullptr;
|
||
errno_t err = _wfopen_s(&f, filename.c_str(), L"rb");
|
||
if (err != 0 || !f) {
|
||
return false;
|
||
}
|
||
|
||
size_t read = fread(Addr(), 1, ImageSize, f);
|
||
fclose(f);
|
||
|
||
return (read == ImageSize);
|
||
}
|
||
|
||
// 从 const wchar_t* 加载原始数据
|
||
bool CImageBuf::loadRaw(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;
|
||
}
|
||
|
||
size_t read = fread(Addr(), 1, ImageSize, f);
|
||
fclose(f);
|
||
|
||
return (read == ImageSize);
|
||
}
|
||
|
||
void CImageBuf::debugCopyErr(int err, const char* exinfo)
|
||
{
|
||
/*switch(err){
|
||
case Ok:
|
||
break;
|
||
case Err_DstWidth:
|
||
qsError() << exinfo << "dest w is larger than width";
|
||
break;
|
||
case Err_DstHeight:
|
||
qsError() << exinfo << "dest y is larger than height";
|
||
break;
|
||
case Warn_CropWidth:
|
||
qsWarn() << exinfo << "image is croped in width";
|
||
break;
|
||
case Warn_CropHeight:
|
||
qsWarn() << exinfo << "image is croped in height";
|
||
break;
|
||
}*/
|
||
}
|
||
///将来源目标图像缓冲区的(sx,sy,sw,sh)区域的数据复制到目标图像缓冲区的(dx,dy)处。两者的bpp必须相同
|
||
int CImageBuf::copy(CImageBuf* src, int sx, int sy, int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr)
|
||
{
|
||
int ret = 0;
|
||
uint8_t* sp = src->getAddr(sx, sy);
|
||
uint8_t* dp = dst->getAddr(dx, dy);
|
||
|
||
int dw = dst->Width - dx; //目标缓冲区可写的宽度
|
||
int dh = dst->Height - dy; //目标缓冲区可写的高度
|
||
do {
|
||
if (dw <= 0) {
|
||
ret = Err_DstWidth; //dx超出了目标缓冲区的范围
|
||
break;
|
||
}
|
||
if (dh <= 0) {
|
||
ret = Err_DstHeight; //dy超出了目标缓冲区的范围
|
||
break;
|
||
}
|
||
|
||
//判断宽度是否超出
|
||
if (sw > dw) {
|
||
sw = dw;
|
||
ret = Warn_CropWidth;
|
||
}
|
||
if (sh > dh) {
|
||
sh = dh;
|
||
ret = Warn_CropHeight;
|
||
}
|
||
size_t line = size_t(sw * src->BPP);
|
||
for (int h = 0; h < sh; h++) {
|
||
memcpy(dp, sp, line);
|
||
sp += src->LineSize;
|
||
dp += dst->LineSize;
|
||
}
|
||
} while (0);
|
||
|
||
if (debugstr) {
|
||
debugCopyErr(ret, debugstr);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
int CImageBuf::copy(CImageBuf* src, int sx, int sy, int sw, int sh, uint8_t* dst)
|
||
{
|
||
int ret = 0;
|
||
uint8_t* sp = src->getAddr(sx, sy);
|
||
uint8_t* dp = dst;
|
||
|
||
|
||
do {
|
||
size_t line = size_t(sw * src->BPP);
|
||
for (int h = 0; h < sh; h++) {
|
||
memcpy(dp, sp, line);
|
||
sp += src->LineSize;
|
||
dp += line;
|
||
}
|
||
} while (0);
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
/// buf1,buf2数值相加平均写到dst,长度为line
|
||
static void meanline(uint8_t* buf1, uint8_t* buf2, uint8_t* dst, int line)
|
||
{
|
||
for (int i = 0; i < line; i++) {
|
||
*dst++ = uint8_t((uint32_t(*buf1++) + uint32_t(*buf2++)) / 2);
|
||
}
|
||
}
|
||
|
||
///叠加图片,颜色值各取1/2
|
||
int CImageBuf::overlay(CImageBuf* src, int sx, int sy, int sw, int sh, CImageBuf* dst, int dx, int dy, const char* debugstr)
|
||
{
|
||
int ret = 0;
|
||
uint8_t* sp = src->getAddr(sx, sy);
|
||
uint8_t* dp = dst->getAddr(dx, dy);
|
||
|
||
int dw = dst->Width - dx; //目标缓冲区可写的宽度
|
||
int dh = dst->Height - dy; //目标缓冲区可写的高度
|
||
do {
|
||
if (dw <= 0) {
|
||
ret = Err_DstWidth; //dx超出了目标缓冲区的范围
|
||
break;
|
||
}
|
||
if (dh <= 0) {
|
||
ret = Err_DstHeight; //dy超出了目标缓冲区的范围
|
||
break;
|
||
}
|
||
|
||
//判断宽度是否超出
|
||
if (sw > dw) {
|
||
sw = dw;
|
||
ret = Warn_CropWidth;
|
||
}
|
||
if (sh > dh) {
|
||
sh = dh;
|
||
ret = Warn_CropHeight;
|
||
}
|
||
size_t line = size_t(sw * src->BPP);
|
||
// 创建临时行
|
||
uint8_t* tmpline = new uint8_t[size_t(line)];
|
||
for (int h = 0; h < sh; h++) {
|
||
meanline(dp, sp, dp, int(line));
|
||
sp += src->LineSize;
|
||
dp += dst->LineSize;
|
||
}
|
||
delete[] tmpline;
|
||
} while (0);
|
||
|
||
if (debugstr) {
|
||
debugCopyErr(ret, debugstr);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
void CImageBuf::fill4(uint32_t val)
|
||
{
|
||
auto* pline = Addr();
|
||
switch (BPP) {
|
||
case 1: { //一次算4个像素
|
||
val &= 0xFF;
|
||
uint32_t v = val | val << 8 | val << 16 | val << 24;
|
||
for (int h = 0; h < Height; h++) {
|
||
auto p = (uint32_t*)pline;
|
||
for (int i = 0; i < Width / 4; i++) *p++ = v;
|
||
|
||
pline += LineSize;
|
||
}
|
||
break;
|
||
}
|
||
case 3: { //一次算4个像素
|
||
val &= 0xFFFFFF;
|
||
uint32_t v1 = val | val << 24;
|
||
uint32_t v2 = (val >> 8) | (val << 16);
|
||
uint32_t v3 = (val >> 16) | (val << 8);
|
||
for (int h = 0; h < Height; h++) {
|
||
auto p = (uint32_t*)pline;
|
||
for (int i = 0; i < Width / 4; i++) {
|
||
*p++ = v1;
|
||
*p++ = v2;
|
||
*p++ = v3;
|
||
}
|
||
pline += LineSize;
|
||
}
|
||
break;
|
||
}
|
||
case 4: {//一次算1个像素
|
||
for (int h = 0; h < Height; h++) {
|
||
auto p = (uint32_t*)pline;
|
||
for (int i = 0; i < Width; i++) *p++ = val;
|
||
pline += LineSize;
|
||
}
|
||
break;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/// 填充颜色,要求4像素对齐
|
||
void CImageBuf::fill4(uint32_t val, int x, int y, int W, int H)
|
||
{
|
||
auto* pline = getAddr(x, y);
|
||
switch (BPP) {
|
||
case 1: { //一次算4个像素
|
||
val &= 0xFF;
|
||
uint32_t v = val | val << 8 | val << 16 | val << 24;
|
||
for (int h = 0; h < H; h++) {
|
||
auto p = (uint32_t*)pline;
|
||
for (int i = 0; i < W / 4; i++) *p++ = v;
|
||
|
||
pline += LineSize;
|
||
}
|
||
break;
|
||
}
|
||
case 3: { //一次算4个像素
|
||
val &= 0xFFFFFF;
|
||
uint32_t v1 = val | val << 24;
|
||
uint32_t v2 = (val >> 8) | (val << 16);
|
||
uint32_t v3 = (val >> 16) | (val << 8);
|
||
for (int h = 0; h < H; h++) {
|
||
auto p = (uint32_t*)pline;
|
||
for (int i = 0; i < W / 4; i++) {
|
||
*p++ = v1;
|
||
*p++ = v2;
|
||
*p++ = v3;
|
||
}
|
||
pline += LineSize;
|
||
}
|
||
break;
|
||
}
|
||
case 4: {//一次算1个像素
|
||
for (int h = 0; h < H; h++) {
|
||
auto p = (uint32_t*)pline;
|
||
for (int i = 0; i < W; i++) *p++ = val;
|
||
pline += LineSize;
|
||
}
|
||
break;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
//cv::Mat toMat(const CImageBuf& img)
|
||
//{
|
||
// switch (img.BytePerPix)
|
||
// {
|
||
// case 1:
|
||
// return cv::Mat(img.Height, img.Width, CV_8UC1, img.Addr, img.LineSize);
|
||
// case 2:
|
||
// return cv::Mat(img.Height, img.Width, CV_8UC2, img.Addr, img.LineSize);
|
||
// case 3:
|
||
// return cv::Mat(img.Height, img.Width, CV_8UC3, img.Addr, img.LineSize);
|
||
// case 4:
|
||
// return cv::Mat(img.Height, img.Width, CV_8UC4, img.Addr, img.LineSize);
|
||
// default:
|
||
// return cv::Mat();
|
||
// }
|
||
//}
|
||
|