Initial commit

This commit is contained in:
2026-09-16 16:06:51 +08:00
commit cac3990ad4
9 changed files with 370 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
*.exe
+99
View File
@@ -0,0 +1,99 @@
#include "image_proc.h"
#include <stdlib.h>
#include <string.h>
#include <windows.h>
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; y<h; ++y){
outImg->rows[y] = outImg->data + (size_t)y * w;
}
for(int y=0; y<h; ++y){
const uint8_t *srcLine = srcPixels + (size_t)(h - 1 - y) * srcStride;
uint8_t *dstLine = outImg->rows[y];
if(bpp == 8){
memcpy(dstLine, srcLine, (size_t)w);
} else if(bpp == 24 || bpp == 32){
for(int x=0; x<w; ++x){
const uint8_t *p = srcLine + x * srcBytesPerPixel;
uint8_t B = p[0], G = p[1], R = p[2];
uint8_t Y = (uint8_t)((R*77 + G*150 + B*29) >> 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; y<h; ++y){
uint8_t *dstLine = pixels + (size_t)(h - 1 - y) * dstStride;
const uint8_t *srcLine = img->rows[y];
memcpy(dstLine, srcLine, (size_t)w);
if(dstStride > w){
memset(dstLine + w, 0, dstStride - w);
}
}
*outBMI = bmi;
*outPixels = pixels;
return 1;
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef IMAGE_PROC_H
#define IMAGE_PROC_H
#include <stdint.h>
#include <windows.h>
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
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+205
View File
@@ -0,0 +1,205 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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;i<colors;i++){
g_bmi->bmiColors[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;i<colors;i++){
BYTE r=g_bmi->bmiColors[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;i<colors;i++){
g_bmi->bmiColors[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;
}
+27
View File
@@ -0,0 +1,27 @@
#include <stdint.h>
#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<w){
image[y][nx] = color;
}
}
for(int dy=-size;dy<=size;dy++){
int ny = y + dy;
if(ny>=0 && ny<h){
image[ny][x] = color;
}
}
}
void img_proc(GrayImage *img){
draw_cross(img, img->width/2, img->height/2, 20, 0);
}
+10
View File
@@ -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