使用WinAPI 双缓冲 画图在窗口中,按Esc关闭,其他的参考官方使用键盘输入 - Win32 apps | Microsoft Docs
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<math.h>
#include<windows.h>
#include<iostream>
using namespace std;
//BMP文件头、信息头等,已在windows.h中定义了
/*
typedef struct tagBITMAPFILEHEADER { //文件头
unsigned short bfType;
unsigned long bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER { //信息头
unsigned long biSize;
long biWidth;
long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD { //颜色表
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
*/
//指定文件信息头
typedef struct tagINFOHEADER {
WORD label; //标志位 0x5250
DWORD linesub; //从0开始计数
DWORD pixnum; //信息域像素数 与图像宽一致
BYTE datatype; //存储方式
DWORD reserve;
} INFOHEADER;
typedef struct tagPIXEL { //像素信息结构体
unsigned char B;
unsigned char G;
unsigned char R;
}PIXEL;
typedef struct tagBMP { //图片信息结构体
unsigned int height;
unsigned int width;
unsigned short bitcount;
PIXEL* data;
}BMP;
wchar_t* to_wchar(const char* str) //宽字符转换函数工具
{
int length = strlen(str) + 1;
wchar_t* t = (wchar_t*)malloc(sizeof(wchar_t) * length);
if (t != 0)
memset(t, 0, length * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, 0, str, strlen(str), t, length);
return t;
}
BYTE getbit(BYTE n, int head, int bitcount) //提取一个字节的某几位的数据
{ //head为 从高位到低位数 开始的位数,bitcount为长度
BYTE x = 0;
int offset = 7 - head;
for (int i = 0; i < bitcount; i++)
{
x += 1 << (offset - i);
}
return (x & n) >> (8 - head - bitcount);
}
BMP* bmpread(const char* file_path, int readtype = 1)
{
BMP* dst = new BMP;
FILE* fp = fopen(file_path, "rb"); //打开文件
if (fp == NULL) //判断文件是否成功打开
{
MessageBox(NULL, L"file not exist", NULL, MB_OK);
return 0;
}
BITMAPFILEHEADER file_head; //读取文件头
fread(&file_head, sizeof(BITMAPFILEHEADER), 1, fp);
if (file_head.bfType != 'MB') //判断是否是bmp文件
{
fclose(fp);
MessageBox(NULL, L"not bmp file", NULL, MB_OK);
return 0;
}
BITMAPINFOHEADER info_head; //读取信息头
fread(&info_head, sizeof(BITMAPINFOHEADER), 1, fp);
int height = info_head.biHeight; //图片信息
int width = info_head.biWidth;
int bitcount = info_head.biBitCount;
int line_byte = (width * bitcount * 8 + 255) / 64 / 4 * 4; //每行字节数,采取了更高精度的算法,当bitcount较小时不至于算错
PIXEL* data = new PIXEL[(long long)height * (long long)width]; //像素信息
dst->data = data;
dst->height = height;
dst->width = width;
dst->bitcount = bitcount;
//开始读像素数据
if (bitcount <= 8)//有颜色表
{
//颜色表数组长度
int quad_num = (file_head.bfOffBits - sizeof(file_head) - sizeof(info_head)) / 4;
//创建颜色表
RGBQUAD* color_table = new RGBQUAD[quad_num];
//读颜色表
fread(color_table, sizeof(RGBQUAD), quad_num, fp);
//读数据
BYTE bt;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < line_byte; j++) //一个字节一个字节读
{
fread(&bt, 1, 1, fp);
for (int k = 0; k < 8 / bitcount; k++) //一个字节内包含的信息取决于bitcount
{
int sub = j * 8 / bitcount + k; //一行一行的读,sub是一行的像素的下标,
if (sub + 1 > width) //一行的像素下标大于width,也就是一行读完了
break;
BYTE t = getbit(bt, k * bitcount, bitcount); //取一个字节内对应位数的信息,因为一个字节8位,BMP有1,4,8位的,总是能装满一个字节
data[i * width + sub].B = color_table[t].rgbBlue;
data[i * width + sub].G = color_table[t].rgbGreen;
data[i * width + sub].R = color_table[t].rgbRed;
}
}
}
delete[]color_table;
}
else//没有颜色表,真彩色图24位,直接读bgr
{
BYTE bt;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
fread(&bt, 1, 1, fp);
data[i * width + j].B = bt;
fread(&bt, 1, 1, fp);
data[i * width + j].G = bt;
fread(&bt, 1, 1, fp);
data[i * width + j].R = bt;
}
fseek(fp, line_byte - width * 3, SEEK_CUR);//跳过为了补齐的空数据
}
}
if (readtype == 0)
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
BYTE B_ = data[i * width + j].B;
BYTE G_ = data[i * width + j].G;
BYTE R_ = data[i * width + j].R;
BYTE gray_ = 0.3 * R_ + 0.59 * G_ + 0.11 * B_;
data[i * width + j].B = data[i * width + j].G = data[i * width + j].R = gray_;
}
}
}
fclose(fp);//关闭文件
return dst;
}
LRESULT CALLBACK winproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
//按下Esc退出 其他的参考官网
case WM_CHAR:
if(wParam == 0x1B)
PostQuitMessage(0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
int WINAPI bmpshow(const char* winname, BMP src, HINSTANCE hInstance = NULL)
{
wchar_t* WinClass = to_wchar("ImageShow_window");
wchar_t* WinTitle = to_wchar(winname);
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW | CS_BYTEALIGNWINDOW;
wc.lpfnWndProc = winproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_CROSS);
wc.hbrBackground = (HBRUSH)(COLOR_GRAYTEXT);
wc.lpszMenuName = NULL;
wc.lpszClassName = WinClass;
RegisterClass(&wc);
DWORD defstyle = WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU | WS_OVERLAPPED;
HWND hWnd = CreateWindow(
WinClass,
WinTitle,
defstyle,
CW_USEDEFAULT, CW_USEDEFAULT,
src.width + 15, src.height + 40, //加上了菜单栏的高度和边界的宽度
0, 0, hInstance, 0);
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
HDC hdc, memdc;
HBITMAP bmp, oldbmp;
int height = src.height;
int width = src.width;
int r, g, b, pix;
hdc = GetDC(hWnd);
memdc = CreateCompatibleDC(hdc);
bmp = CreateCompatibleBitmap(hdc, width, height);
SelectObject(memdc, bmp);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
b = src.data[i * width + j].B;
g = src.data[i * width + j].G;
r = src.data[i * width + j].R;
pix = RGB(r, g, b);
SetPixel(memdc, j, height - i - 1, pix);
}
}
BitBlt(hdc, 0, 0, width, height, memdc, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdc);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
代码不完善 请多指教