commit d9ac634069cde77df2eaeda54c4c80f1101cb49e Author: lixiangrui11 <2177013634@qq.com> Date: Sun Sep 13 20:57:03 2026 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..adb36c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.exe \ No newline at end of file diff --git a/image_proc.c b/image_proc.c new file mode 100644 index 0000000..1b767c3 --- /dev/null +++ b/image_proc.c @@ -0,0 +1,99 @@ +#include "image_proc.h" +#include +#include +#include + +int ExtractGray(const uint8_t *srcPixels, int w, int h, int bpp, GrayImage *outImg){ + if(!srcPixels || w<=0 || h<=0) return 0; + int srcBytesPerPixel = bpp / 8; + int srcStride = ((w * srcBytesPerPixel + 3) / 4) * 4; + outImg->width = w; + outImg->height = h; + outImg->stride = w; + size_t total = (size_t)w * h; + outImg->data = (uint8_t*)malloc(total); + if(!outImg->data) return 0; + outImg->rows = (uint8_t**)malloc(sizeof(uint8_t*) * h); + if(!outImg->rows){ + free(outImg->data); + outImg->data = NULL; + return 0; + } + for(int y=0; yrows[y] = outImg->data + (size_t)y * w; + } + + for(int y=0; yrows[y]; + if(bpp == 8){ + memcpy(dstLine, srcLine, (size_t)w); + } else if(bpp == 24 || bpp == 32){ + for(int x=0; x> 8); + dstLine[x] = Y; + } + } else { + free(outImg->rows); free(outImg->data); + memset(outImg,0,sizeof(*outImg)); + return 0; + } + } + return 1; +} + +void FreeGray(GrayImage *img){ + if(!img) return; + free(img->rows); + free(img->data); + memset(img,0,sizeof(*img)); +} + +int Build8BitDIB(const GrayImage *img, BITMAPINFO **outBMI, uint8_t **outPixels){ + if(!img || !img->data) return 0; + int w = img->width; + int h = img->height; + int bpp = 8; + int dstStride = ((w * (bpp/8) + 3)/4)*4; + size_t pixelBytes = (size_t)dstStride * h; + + size_t bmiSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD); + BITMAPINFO *bmi = (BITMAPINFO*)calloc(1, bmiSize); + if(!bmi) return 0; + + bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi->bmiHeader.biWidth = w; + bmi->bmiHeader.biHeight = h; + bmi->bmiHeader.biPlanes = 1; + bmi->bmiHeader.biBitCount = 8; + bmi->bmiHeader.biCompression = BI_RGB; + bmi->bmiHeader.biSizeImage = (DWORD)pixelBytes; + bmi->bmiHeader.biClrUsed = 256; + + for(int i=0;i<256;i++){ + bmi->bmiColors[i].rgbBlue = (BYTE)i; + bmi->bmiColors[i].rgbGreen = (BYTE)i; + bmi->bmiColors[i].rgbRed = (BYTE)i; + bmi->bmiColors[i].rgbReserved = 0; + } + + uint8_t *pixels = (uint8_t*)malloc(pixelBytes); + if(!pixels){ + free(bmi); + return 0; + } + for(int y=0; yrows[y]; + memcpy(dstLine, srcLine, (size_t)w); + if(dstStride > w){ + memset(dstLine + w, 0, dstStride - w); + } + } + + *outBMI = bmi; + *outPixels = pixels; + return 1; +} \ No newline at end of file diff --git a/image_proc.h b/image_proc.h new file mode 100644 index 0000000..3346c54 --- /dev/null +++ b/image_proc.h @@ -0,0 +1,28 @@ +#ifndef IMAGE_PROC_H +#define IMAGE_PROC_H +#include +#include + +typedef struct { + int width; + int height; + int stride; // 连续灰度数据每行字节数 (无 4 字节对齐,= width) + uint8_t *data; // 连续内存 (height * width) + uint8_t **rows; // 行指针 (rows[0] 顶行) +} GrayImage; + +// 从已加载的 BMP 像素提取灰度 (支持 8/24/32 bpp),源为 bottom-up DIB,输出 top-down +// srcPixels: 原始像素 (bottom-up) +// bpp: 8 / 24 / 32 +// 返回 1 成功 0 失败 +int ExtractGray(const uint8_t *srcPixels, int w, int h, int bpp, GrayImage *outImg); + + +// 释放 +void FreeGray(GrayImage *img); + +// 把灰度图转换成可直接 StretchDIBits 的 8 位 DIB (bottom-up + 调色板) +// 返回 1 成功 +int Build8BitDIB(const GrayImage *img, BITMAPINFO **outBMI, uint8_t **outPixels); + +#endif \ No newline at end of file diff --git a/img_files/0.bmp b/img_files/0.bmp new file mode 100644 index 0000000..8fac5a6 Binary files /dev/null and b/img_files/0.bmp differ diff --git a/img_files/1.bmp b/img_files/1.bmp new file mode 100644 index 0000000..53bca27 Binary files /dev/null and b/img_files/1.bmp differ diff --git a/img_files/2.bmp b/img_files/2.bmp new file mode 100644 index 0000000..c2fe0a7 Binary files /dev/null and b/img_files/2.bmp differ diff --git a/main.c b/main.c new file mode 100644 index 0000000..599d496 --- /dev/null +++ b/main.c @@ -0,0 +1,205 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include "image_proc.h" +#include "user_proc.h" + +#pragma pack(push,1) +typedef struct { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +} BMPFILEHEADER; +#pragma pack(pop) + +static uint8_t* g_pixels = NULL; +static BITMAPINFO* g_bmi = NULL; +static int g_w=0, g_h=0, g_bpp=0; +static const char* g_title = "BMP Viewer"; + +static void FreeImage() { + free(g_pixels); g_pixels = NULL; + free(g_bmi); g_bmi = NULL; + g_w = g_h = g_bpp = 0; +} + +static int LoadBMP(const char* path) { + FreeImage(); + FILE* fp = fopen(path, "rb"); + if(!fp){ fprintf(stderr,"无法打开: %s\n", path); return 0; } + + BMPFILEHEADER fh; + BITMAPINFOHEADER ih; + if(fread(&fh,sizeof(fh),1,fp)!=1){ fclose(fp); return 0; } + if(fh.bfType != 0x4D42){ fclose(fp); fprintf(stderr,"非BMP\n"); return 0; } + if(fread(&ih,sizeof(ih),1,fp)!=1){ fclose(fp); return 0; } + if(ih.biCompression != BI_RGB){ fclose(fp); fprintf(stderr,"仅支持未压缩 BI_RGB\n"); return 0; } + if(!(ih.biBitCount==8 || ih.biBitCount==24 || ih.biBitCount==32)){ + fclose(fp); fprintf(stderr,"不支持位数: %u\n", ih.biBitCount); return 0; + } + + g_w = ih.biWidth; + g_h = ih.biHeight; + g_bpp = ih.biBitCount; + + int bytesPerPixel = g_bpp / 8; + int stride = ((g_w * bytesPerPixel + 3) / 4) * 4; + size_t imgSize = (size_t)stride * g_h; + + uint32_t colors = 0; + if(g_bpp == 8){ + colors = ih.biClrUsed ? ih.biClrUsed : 256; + if(colors > 256) colors = 256; + } + size_t bmiSize = sizeof(BITMAPINFOHEADER) + colors * sizeof(RGBQUAD); + g_bmi = (BITMAPINFO*)calloc(1, bmiSize); + if(!g_bmi){ fclose(fp); return 0; } + g_bmi->bmiHeader = ih; + + if(g_bpp == 8){ + if(colors){ + size_t readPaletteBytes = colors * sizeof(RGBQUAD); + if(fread(g_bmi->bmiColors, 1, readPaletteBytes, fp) != readPaletteBytes){ + for(uint32_t i=0;ibmiColors[i].rgbRed = + g_bmi->bmiColors[i].rgbGreen = + g_bmi->bmiColors[i].rgbBlue = (BYTE)i; + } + } + } else { + } + } + + g_pixels = (uint8_t*)malloc(imgSize); + if(!g_pixels){ fclose(fp); FreeImage(); return 0; } + + if(fseek(fp, (long)fh.bfOffBits, SEEK_SET)!=0){ + fclose(fp); FreeImage(); return 0; + } + if(fread(g_pixels,1,imgSize,fp)!=imgSize){ + fclose(fp); FreeImage(); fprintf(stderr,"像素读取失败\n"); return 0; + } + fclose(fp); + + if(g_bpp == 8){ + int isGray = 1; + for(uint32_t i=0;ibmiColors[i].rgbRed; + BYTE g=g_bmi->bmiColors[i].rgbGreen; + BYTE b=g_bmi->bmiColors[i].rgbBlue; + if(!(r==g && g==b)){ isGray=0; break; } + } + if(!isGray){ + for(uint32_t i=0;ibmiColors[i].rgbRed = + g_bmi->bmiColors[i].rgbGreen = + g_bmi->bmiColors[i].rgbBlue = (BYTE)i; + } + } + } + + return 1; +} + +static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ + switch(msg){ + case WM_ERASEBKGND: + return 1; + case WM_PAINT:{ + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd,&ps); + if(g_pixels && g_bmi){ + int dstW = g_w * IMAGE_SCALE; + int dstH = g_h * IMAGE_SCALE; + SetStretchBltMode(hdc, COLORONCOLOR); + StretchDIBits( + hdc, + 0,0,dstW,dstH, + 0,0,g_w,g_h, + g_pixels, + g_bmi, + DIB_RGB_COLORS, + SRCCOPY + ); + } else { + TextOutA(hdc,10,10,"未加载BMP",12); + } + EndPaint(hWnd,&ps); + } break; + case WM_DESTROY: + FreeImage(); + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd,msg,wParam,lParam); + } + return 0; +} + +int APIENTRY WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpCmdLine,int nShow){ + (void)hPrev; (void)lpCmdLine; + if(!LoadBMP(BMP_PATH)){ + char msg[256]; + snprintf(msg,sizeof(msg),"加载失败: %s", BMP_PATH); + MessageBoxA(NULL,msg,"错误",MB_OK|MB_ICONERROR); + } else { + g_title = BMP_PATH; + + GrayImage gray = {0}; + if(ExtractGray(g_pixels, g_w, g_h, g_bpp, &gray)){ + + img_proc(&gray);//这里调用了图像处理函数 + + BITMAPINFO *newBMI = NULL; + uint8_t *newPixels = NULL; + if(Build8BitDIB(&gray, &newBMI, &newPixels)){ + free(g_pixels); + free(g_bmi); + g_pixels = newPixels; + g_bmi = newBMI; + g_bpp = 8; + } + } + FreeGray(&gray); + } + + const char* cls = "BMP_VIEWER_CLASS"; + WNDCLASSA wc = (WNDCLASSA){0}; + wc.lpfnWndProc = WndProc; + wc.hInstance = hInst; + wc.lpszClassName = cls; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + if(!RegisterClassA(&wc)) return 0; + + int winW = g_w ? g_w * IMAGE_SCALE : 640; + int winH = g_h ? g_h * IMAGE_SCALE : 480; + + RECT rc = {0,0, winW, winH}; + DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + AdjustWindowRect(&rc, style, FALSE); + + HWND hWnd = CreateWindowExA( + 0, cls, g_title, + style, + CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, + rc.bottom - rc.top, + NULL,NULL,hInst,NULL); + if(!hWnd) return 0; + + ShowWindow(hWnd,nShow); + UpdateWindow(hWnd); + + MSG msg; + while(GetMessage(&msg,NULL,0,0)>0){ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return (int)msg.wParam; +} \ No newline at end of file diff --git a/user_proc.c b/user_proc.c new file mode 100644 index 0000000..99788ae --- /dev/null +++ b/user_proc.c @@ -0,0 +1,27 @@ +#include +#include "user_proc.h" +//绘制十字交叉线 +void draw_cross(GrayImage *img, int x, int y, int size, uint8_t color){ + if(!img || !img->data) return; + //获取图像宽度和高度 + int w = img->width; + int h = img->height; + if(x<0 || x>=w || y<0 || y>=h) return; + //定义一个二维数组指针方便访问像素 + uint8_t **image = img->rows; + for(int dx=-size;dx<=size;dx++){ + int nx = x + dx; + if(nx>=0 && nx=0 && nywidth/2, img->height/2, 20, 0); +} \ No newline at end of file diff --git a/user_proc.h b/user_proc.h new file mode 100644 index 0000000..d79f2e7 --- /dev/null +++ b/user_proc.h @@ -0,0 +1,10 @@ +#ifndef USER_PROC_H +#define USER_PROC_H + +#include "image_proc.h" + +#define BMP_PATH ".\\img_files\\0.bmp" // 更改这里即可切换要加载的 BMP +#define IMAGE_SCALE 3 // 放大倍数,可改成 1 / 2 / 3 等 + +void img_proc(GrayImage *img); +#endif \ No newline at end of file