Files
GdCpp12/source/Mem/CImageBuf.cpp

392 lines
10 KiB
C++
Raw Permalink 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.
#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;
}
/// buf1buf2数值相加平均写到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();
// }
//}