news 2026/10/28 2:00:16

曼德博分形图的简单绘制和保存

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
曼德博分形图的简单绘制和保存

/*
在窗口中绘制分形图,并可以保存为图像文件

*/

#define _USE_MATH_DEFINES //定义此宏就可以使用其它预定义的宏,比如 M_PI
#include <math.h> //使用数学函数,比如 sqrt
#include <stdio.h>
#include <windows.h>
#include "resource.h"

#define COUNTOF(a) (sizeof(a)/sizeof((a)[0])) //获得数组单元数量
// 高彩颜色格式:16位:555格式,二进制表示 0RRRRRGGGGGBBBBBb
#define BGR16(r,g,b) (WORD)((((BYTE)(r)>>3)<<10) | (((BYTE)(g)>>3)<<5) | ((BYTE)(b)>> 3))
// 真彩颜色格式:24位:BGR格式,字节序为BB GG RR (0x00RRGGBB)
// 真彩颜色格式:32位:BGRA格式,字节序为BB GG RR FF(0xFFRRGGBB)
#define BGR(r,g,b) ((COLORREF)(((BYTE)(b)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(r))<<16)))
// 默认的初始数据
#define WINDOW_W 640 //初始视口宽度
#define WINDOW_H 480 //初始视口高度
#define BITSPERPIXEL 16 //初始位图位深度:16位表现力还可以
#define MAX_ITERATIONS (1<<9) // 迭代的次数
#define ZOOM_IN 500 // 放大倍数
#define ABS_X 1.7
#define ABS_Y 1.5

const char szClassName[8] = "Fractal";
const char szWindowName[10] = "混沌系列";
const char szClass2[12] = "FractalView";
const char szWindow2[22] = "分形局部";

HINSTANCE ghInstance;
HWND ghwnd, hwndView;
HDC ghdc, hdcView;
int g_bmp_w = WINDOW_W, g_bmp_h = WINDOW_H, g_bmp_bpp = BITSPERPIXEL;
int g_max_iter = MAX_ITERATIONS; // 迭代的次数
double g_zoomin = 1.0 / ZOOM_IN;

DWORD gMouseX, gMouseY;

// 保存图像为文件时需要使用 GDI plus
#include "gdiplus.h"
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")

BOOL bInitGDIPlus = 0;
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;

void init_gdiplus () {
if (bInitGDIPlus == 0) {
GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
bInitGDIPlus = 1;
}
}

void free_gdiplus () {
if (bInitGDIPlus == 1) {
GdiplusShutdown (gdiplusToken);
bInitGDIPlus = 0;
}
}

int GetEncoderClsid (const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // 图像编码器的数量
UINT size = 0; // 图像编码器数组的字节数
GetImageEncodersSize (&num, &size);
if (size == 0)
return -1; // 失败
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc (size));
if (pImageCodecInfo == NULL)
return -1; // 失败
GetImageEncoders (num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j) {
if (wcscmp (pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free (pImageCodecInfo);
return j; // 成功找到匹配
}
}
free (pImageCodecInfo);
return -1; // 失败
}

void save_fractal (HDC hdc)
{
if (hdc == NULL)return;
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc, OBJ_BITMAP);
BITMAP bm;
if (!GetObject (hbm, sizeof (BITMAP), &bm)) {
return;
}
HPALETTE hPal = NULL;
if (bm.bmBitsPixel <= 8) {// 有调色板
int colorN = (1 << bm.bmBitsPixel);
RGBQUAD pal[256];
GetDIBColorTable (hdc, 0, colorN, pal);
// 分配颜色表内存空间并初始化
UINT palSize = sizeof (WORD) * 2 + sizeof (PALETTEENTRY)*colorN;
LOGPALETTE *pLogPal = (LOGPALETTE *)LocalAlloc (LMEM_FIXED, palSize);
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = (WORD)colorN;
for (int i = 0; i < colorN; i++) {// 填充颜色表
pLogPal->palPalEntry[i].peBlue = (BYTE)(pal[i].rgbBlue);
pLogPal->palPalEntry[i].peGreen = (BYTE)(pal[i].rgbGreen);
pLogPal->palPalEntry[i].peRed = (BYTE)(pal[i].rgbRed);
pLogPal->palPalEntry[i].peFlags = (BYTE)0;
}
hPal = CreatePalette (pLogPal);
LocalFree (pLogPal);
}
char szFile[MAX_PATH];
SYSTEMTIME ldt;
GetLocalTime (&ldt);// 获得本地日期时间字符串
sprintf (szFile, "%04d_%02d_%02d_%02d%02d%02d.jpg",
ldt.wYear, ldt.wMonth, ldt.wDay, ldt.wHour, ldt.wMinute, ldt.wSecond);
WCHAR szUnicode[MAX_PATH];
MultiByteToWideChar (0, 0, szFile, -1, szUnicode, COUNTOF (szUnicode));//转换为宽字节字符
Bitmap bmp (hbm, hPal);//建立位图对象
CLSID pngClsid;
/* GDI plus库支持的图像格式:
image/bmp
image/jpeg
image/gif
image/x-emf
image/x-wmf
image/tiff
image/png
image/x-icon */
GetEncoderClsid (L"image/jpeg", &pngClsid);
bmp.Save (szUnicode, &pngClsid, NULL);
if (hPal)DeleteObject (hPal);
}

// 调色板:1,4,8位:RGB格式,字节序为RR GG BB FF (0xFFBBGGRR)
COLORREF gColors2[2] = { 0, 0xffffff };// 黑色和白色
COLORREF gColors16[16];
COLORREF gColors256[256];
// 装入调色板:用户可以自定义配色,会产生不同效果的图像
// *.pal 调色板文件可用VS IDE来制作
void load_pal (COLORREF *pal, int color_n, const char *palFile)
{
if (!pal || !palFile)return;
FILE *fp = fopen (palFile, "rb");
if (fp) {
fseek (fp, 24, SEEK_SET);// 跳过文件描述头
fread (pal, 4, color_n, fp);
fclose (fp);
}
else{if (color_n == 256) { //256色位图颜色表:红3绿3蓝2
for (int i = 0; i < 256; i++) {
RGBQUAD *P = pal + i;
P->rgbBlue = (BYTE)((i & 3) * 255 / 3);
P->rgbGreen = (BYTE)(((i >> 2) & 7) * 255 / 7);
P->rgbRed = (BYTE)(((i >> 5) & 7) * 255 / 7);
P->rgbReserved = 0;
}

}
}
}
void set_pixel (BITMAP *bm, int x, int y, int iter)
{
if (!bm || !bm->bmBits)//注意!只有使用CreateDIBSection建立的位图其bmBits成员才有效
return;
BYTE* pPixel = (BYTE*)bm->bmBits + y * bm->bmWidthBytes + (x*bm->bmBitsPixel / 8);
DWORD color;
if (bm->bmBitsPixel > 8) {//平滑地产生真彩色像素的颜色
float t = (float)((double)iter / g_max_iter);
float ot = 1 - t;
BYTE r = (BYTE)(255 * (9 * ot * t * t * t));
BYTE g = (BYTE)(255 * (15 * ot * ot * t * t));
BYTE b = (BYTE)(255 * (8.5 * ot * ot * ot * t));
if (bm->bmBitsPixel == 16)
color = BGR16 (r, g, b);
else
color = BGR (r, g, b);
}

switch (bm->bmBitsPixel)
{
case 1://字节的每一位对应图像的一个像素:位76543210对应坐标值递增
*pPixel &= ~(1 << (7 - (x & 7)));//清除目标位
*pPixel |= (iter & 1) << (7 - (x & 7));//设置目标位
break;

case 4://字节的每4位对应图像的一个像素:若位7654对应坐标值x,则位3210对应坐标值x+1
*pPixel &= 15 << ((x & 1) << 2);//保护字节的另4位,清除像素使用的4位
*pPixel |= (iter & 15) << ((1 - (x & 1)) << 2);//像素使用的4位设置为颜色索引
break;

case 8: *pPixel = (BYTE)(iter & 255); break;//一个字节对应图像的一个像素的颜色索引

case 16: *(WORD*)pPixel = (WORD)(color & 0xffff); break;
case 24: CopyMemory (pPixel, &color, 3); break;
case 32: *(DWORD*)pPixel = color; break;
}
}

void create_dib (HDC& hdc, int w, int h, int bpp)
{
if (hdc == NULL)
hdc = CreateCompatibleDC (0);//建立与桌面窗口一致的DC
if (hdc == NULL)return;
BITMAPINFO bmi;
memset (&bmi, 0, sizeof (bmi));
bmi.bmiHeader.biSize = sizeof (BITMAPINFO);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;//负值表示像素行自上而下
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = bpp;
void *ppvBits;//像素位地址
HBITMAP hbm = CreateDIBSection (hdc, &bmi, 0, &ppvBits, 0, 0);//建立DIB并分配像素空间
if (hbm == NULL)return;
HBITMAP hbmPrev = (HBITMAP)SelectObject (hdc, hbm);//选入DC并取出以前的位图
if (hbmPrev)
DeleteObject (hbmPrev);//删除不再使用的位图
if (bpp <= 8) {//像素位数不大于8的位图需要设置调色板
RGBQUAD* pal = NULL;
if (bpp == 1)pal = (RGBQUAD*)gColors2;
else if (bpp == 4)pal = (RGBQUAD*)gColors16;
else if (bpp == 8)pal = (RGBQUAD*)gColors256;
if (pal)SetDIBColorTable (hdc, 0, (1 << bpp), pal);
}
}

LRESULT WINAPI view_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdcV = hdcView;

switch (uMsg)
{
case WM_PAINT: {// 重绘窗口
PAINTSTRUCT P;
BeginPaint (hWnd, &P);
PRECT pR = &P.rcPaint;
BitBlt (P.hdc, 0, 0, pR->right, pR->bottom, hdcV, 0, 0, SRCCOPY);
EndPaint (hWnd, &P);
break; }

case WM_SIZE: {RECT R;
GetClientRect (hWnd, &R);
HDC hdc = GetDC (hWnd);
BitBlt (hdc, 0, 0, R.right, R.bottom, hdcV, 0, 0, SRCCOPY);
ReleaseDC (hWnd, hdc);
break; }
}
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}

HWND create_view (HWND parent, const char *name, HDC& hdc)
{
HWND hwnd = CreateWindow (szClass2, name,
WS_BORDER | WS_CAPTION,//此子窗口只是用来观看图像,没有滚动条,但不影响图像保存
0, 0, 0, 0,
parent, NULL, ghInstance, NULL);
if (hwnd == NULL) {
return NULL;
}

create_dib (hdc, g_bmp_w, g_bmp_h, g_bmp_bpp);
SetBkColor (hdc, 0x7f0000);// 设置背景颜色:类型COLORREF:0x00BBGGRR
SetBkMode (hdc, OPAQUE); // 填充背景为设置的颜色(TRANSPARENT 透明方式)

RECT WR, CR;
GetWindowRect (hwnd, &WR);// 取得窗口矩形
GetClientRect (hwnd, &CR);// 取得窗口的客户区矩形
int newW = WINDOW_W + (WR.right - WR.left) - (CR.right - CR.left);// 窗口的新宽度
int newH = WINDOW_H + (WR.bottom - WR.top) - (CR.bottom - CR.top);// 窗口的新高度
int SW = GetSystemMetrics (SM_CXSCREEN);// 屏宽
GetWindowRect (parent, &WR);// 取得主窗口矩形
MoveWindow (hwnd, WR.left - WINDOW_W - 1, WR.top, newW, newH, FALSE);// 设置窗口
ShowWindow (hwnd, 1);

return hwnd;
}

/*
曼德博集和茱莉亚集使用的迭代公式:
Zₙ₊₁ = Zₙ² + C
其中,C 是复数平面上的点(x为实部,y为虚部)
Z₀ = 0
如果迭代过程中 |Z| 超过某个阈值(通常是2),则认为该点不属于集合;
如果迭代固定次数后仍未发散(逃逸),则认为该点属于集合。
集合外的点的迭代次数呈现规律性,若将迭代次数用颜色表示就形成了分形图
*/

struct Complex {//自定义的复数结构
double real;//复数的实部
double imag;//复数的虚部
Complex (double r = 0.0, double i = 0.0) :real (r), imag (i) {}//构造函数
Complex operator+(const Complex& c) const {//复数的加法运算符重载
return Complex (real + c.real, imag + c.imag);
}
Complex operator*(const Complex& c) const {//复数的乘法运算符重载
return Complex (real * c.real - imag * c.imag,
real * c.imag + imag * c.real);
}
};

inline int iter (Complex C, Complex Z) {// 迭代
int iter_cnt = 0;// 迭代计数
while (iter_cnt < g_max_iter) {
Z = Z * Z + C;
iter_cnt++;
if (sqrt (Z.real * Z.real + Z.imag * Z.imag) >= 2.0)break;
}
return iter_cnt;
}

void mandelbrot (HDC hdc, double x, double xmax, double y, double ymax)
{//x, xmax和y, ymax 的值域为 [ -2, 2 ]
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc, OBJ_BITMAP);
if (hbm == NULL)return;
BITMAP bm;
if (!GetObject (hbm, sizeof (BITMAP), &bm))return;
LONG max_W = bm.bmWidth;
LONG max_H = bm.bmHeight;
double Creal = (xmax - x) / (max_W - 1);//不随循环改变的值
double Cimag = (ymax - y) / (max_H - 1);
Complex C, Z;
int i, j;
for (j = 0; j < max_H; j++) {
for (i = 0; i < max_W; i++) {
C.real = x + Creal * i;
C.imag = y + Cimag * j;
Z.real = Z.imag = 0;
set_pixel (&bm, i, j, iter (C, Z));// 迭代计数转换成颜色
}
}
char szDesc[80];
sprintf (szDesc, "曼德博分形图 %dx%d %d位 (%f, %f),最大迭数 %d", max_W, max_H, bm.bmBitsPixel, x, y, g_max_iter);
SetTextColor (hdc, 0xcccccc);
TextOut (hdc, 8, max_H - 30, szDesc, lstrlen (szDesc));
TextOut (hdc, max_W - 108, max_H - 30, "编制:赵利龙", 12);
}

void julia (HDC hdc, double x, double y)// 茱莉亚集
{
if (hdc == NULL)return;
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc, OBJ_BITMAP);
if (hbm == NULL)return;
BITMAP bm;
if (!GetObject (hbm, sizeof (BITMAP), &bm))return;
LONG max_W = bm.bmWidth;
LONG max_H = bm.bmHeight;
double xmin = -ABS_X, xmax = ABS_X, ymin = -ABS_Y, ymax = ABS_Y;
double Creal = (xmax - xmin) / (max_W - 1);//不随循环改变的值
double Cimag = (ymax - ymin) / (max_H - 1);
Complex C (x, y);
Complex Z;
int i, j;
for (j = 0; j < max_H; j++) {
for (i = 0; i < max_W; i++) {
Z.real = xmin + Creal * i;
Z.imag = ymin + Cimag * j;
set_pixel (&bm, i, j, iter (C, Z));// 迭代计数转换成颜色
}
}
char szDesc[80];
sprintf (szDesc, "茱莉亚分形图 %dx%d %d位 (%f, %f)", max_W, max_H, bm.bmBitsPixel, x, y);
SetTextColor (hdc, 0xcccccc);
TextOut (hdc, 8, max_H - 30, szDesc, lstrlen (szDesc));
}
// 绘制sierpinski 地毯
void draw_sierpinski_carpet (HDC hdc, int depth, int x, int y, int w, int h)
{
if (depth <= 0 || w <= 1 || h <= 1) return;
// 将矩形均分为 3X3 (九宫格)
int w_3 = w / 3;
int h_3 = h / 3;
if (w_3 < 1)w_3 = 1;
if (h_3 < 1)h_3 = 1;

// 中间的矩形 直接绘制
if (w_3 == 1 && h_3 == 1) {// 当宽高都是1时 Rectangle会摇头,因此使用画点函数
SetPixel (hdc, x + 1, y + 1, 0xff7777);//此函数因为要考虑颜色位数和画点方式,故此很慢
}
else {// 绘制矩形,并填充
HBRUSH hbr = CreateHatchBrush (rand () % 6, rand () % 0x777788);// 随机画刷样式、随机颜色
HBRUSH hbr0 = (HBRUSH)SelectObject (hdc, hbr);
Rectangle (hdc, x + w_3, y + h_3, x + 2 * w_3, y + 2 * h_3);
hbr = (HBRUSH)SelectObject (hdc, hbr0);
//Hatch类型画刷使用了预定义的1位8x8位图位数据,画刷本身由用户建立,因此用完后需要删除
if (hbr)DeleteObject (hbr);
}
int pos[8][2] = { 0, 0, w_3, 0, 2 * w_3, 0, 2 * w_3, h_3,
2 * w_3, 2 * h_3, w_3, 2 * h_3, 0, 2 * h_3, 0, h_3 };
// 递归其他8个小矩形:顺时针方向
depth--;
for (int i = 0; i < 8; i++)
draw_sierpinski_carpet (hdc, depth, x + pos[i][0], y + pos[i][1], w_3, h_3);
}

void sierpinski_carpet (HDC hdc)
{
if (hdc == NULL)return;
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc, OBJ_BITMAP);
if (hbm == NULL)return;
BITMAP bm;
if (!GetObject (hbm, sizeof (BITMAP), &bm))return;
HPEN hpen = CreatePen (PS_SOLID, 1, 0xcf3333);
HPEN hpen0 = (HPEN)SelectObject (hdc, hpen);
SetBkMode (hdc, TRANSPARENT); // 透明方式背景:适用于文本绘制和画刷填充
draw_sierpinski_carpet (hdc, 8, 0, 0, bm.bmWidth, bm.bmHeight);
DeleteObject (SelectObject (hdc, hpen0));
}

BOOL WINAPI set_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG: {
SetDlgItemInt (hWnd, IDCE_WIDTH, g_bmp_w, TRUE);
SetDlgItemInt (hWnd, IDCE_HEIGHT, g_bmp_h, TRUE);
SetDlgItemInt (hWnd, IDCE_BPP, g_bmp_bpp, TRUE);
SetDlgItemInt (hWnd, IDCE_ITER, g_max_iter, TRUE);
SetDlgItemInt (hWnd, IDCE_ZOOMIN, (int)(1.0 / g_zoomin), TRUE);
break; }

case WM_COMMAND: {
BOOL fTranslated;
const char *tip = "请输入一个有效的数值。";
switch (LOWORD (wParam))
{
case IDOK: {
int cx = GetDlgItemInt (hWnd, IDCE_WIDTH, &fTranslated, FALSE); cx &= 0xfffffffc;
if (!fTranslated) {
MessageBox (hWnd, tip, "宽度错误", MB_OK | MB_ICONINFORMATION);
break;
}
if (cx < 16 || cx > 2400) {
MessageBox (hWnd, tip, "宽度", MB_OK | MB_ICONINFORMATION);
break;
}
int cy = GetDlgItemInt (hWnd, IDCE_HEIGHT, &fTranslated, FALSE);
if (!fTranslated) {
MessageBox (hWnd, tip, "高度错误", MB_OK | MB_ICONINFORMATION);
break;
}
if (cy < 16 || cy > 2400) {
MessageBox (hWnd, tip, "高度", MB_OK | MB_ICONINFORMATION);
break;
}
int bpp = GetDlgItemInt (hWnd, IDCE_BPP, &fTranslated, FALSE);
if (!fTranslated) {
MessageBox (hWnd, tip, "位深度错误", MB_OK | MB_ICONINFORMATION);
break;
}
if (bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32) {
MessageBox (hWnd, tip, "位深度错误", MB_OK | MB_ICONINFORMATION);
break;
}
int iter = GetDlgItemInt (hWnd, IDCE_ITER, &fTranslated, FALSE);
if (!fTranslated) {
MessageBox (hWnd, tip, "迭代次数错误", MB_OK | MB_ICONINFORMATION);
break;
}
if (iter < 99 || iter > 3999) {
MessageBox (hWnd, tip, "迭代次数", MB_OK | MB_ICONINFORMATION);
break;
}
int zoom = GetDlgItemInt (hWnd, IDCE_ZOOMIN, &fTranslated, FALSE);
if (!fTranslated) {
MessageBox (hWnd, tip, "放大倍数错误", MB_OK | MB_ICONINFORMATION);
break;
}
if (zoom < 10 || zoom > 10000) {
MessageBox (hWnd, tip, "放大倍数", MB_OK | MB_ICONINFORMATION);
break;
}
if (cx != g_bmp_w || cy != g_bmp_h || bpp != g_bmp_bpp) {//尺寸和位深度有一个改变时才重建位图
create_dib (hdcView, cx, cy, bpp);
g_bmp_w = cx;
g_bmp_h = cy;
g_bmp_bpp = bpp;
}
g_max_iter = iter;
g_zoomin = 1.0 / zoom;

EndDialog (hWnd, IDOK);
break; }

case IDCANCEL:
EndDialog (hWnd, IDCANCEL);
break;
}
break; }

case WM_PAINT: {
HDC hdc = GetDC (hWnd), hdc2 = NULL;
RECT rc;
GetClientRect (hWnd, &rc);
create_dib (hdc2, rc.right, rc.bottom, 8);
sierpinski_carpet (hdc2);// 填充背景
BitBlt (hdc, 0, 0, rc.right, rc.bottom, hdc2, 0, 0, SRCCOPY);
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc2, OBJ_BITMAP);
if (hbm)DeleteObject (hbm);
DeleteDC (hdc2);
ReleaseDC (hWnd, hdc);
break; }
}

return FALSE;
}

LONG WINAPI wnd_proc (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
LONG result = 0;
HDC hdc = ghdc;
switch (Msg)
{
case WM_COMMAND://菜单命令
switch (LOWORD (wParam))
{
case ID_40001:save_fractal (hdcView); break;//保存分形图像
case ID_40003://设置位图的尺寸、位深、迭代次数、放大倍数
DialogBox (ghInstance, (LPCTSTR)IDD_SIZE, hWnd, (DLGPROC)set_proc);
break;
}
break;

case WM_PAINT: {
PAINTSTRUCT P;
BeginPaint (hWnd, &P);
BitBlt (P.hdc, 0, 0, P.rcPaint.right, P.rcPaint.bottom, hdc, 0, 0, SRCCOPY);
EndPaint (hWnd, &P);
break; }

case WM_MOUSEMOVE:
gMouseX = (lParam & 0xffff);
gMouseY = ((lParam >> 16) & 0xffff);
break;

case WM_LBUTTONUP: {
RECT R; GetClientRect (hWnd, &R);
double x1 = (double)gMouseX / R.right*(2 * ABS_X) - ABS_X;
double y1 = (double)gMouseY / R.bottom*(2 * ABS_Y) - ABS_Y;
mandelbrot (hdcView, x1, x1 + g_zoomin, y1, y1 + g_zoomin); // 曼德博集合,放大局部
SendMessage (hwndView, WM_SIZE, NULL, NULL);
break; }

case WM_MBUTTONUP: {// 若迭代次数较小则不会迭出海马谷
double x1 = -0.74877, y1 = 0.06505;//海马谷(Seahorse Valley)
mandelbrot (hdcView, x1, x1 + 0.00005, y1, y1 + 0.00005); // 曼德博集合
SendMessage (hwndView, WM_SIZE, NULL, NULL);
break; }

case WM_RBUTTONUP: {
RECT R; GetClientRect (hWnd, &R);
double x1 = (double)gMouseX / R.right*(2 * ABS_X) - ABS_X;
double y1 = (double)gMouseY / R.bottom*(2 * ABS_Y) - ABS_Y;
julia (hdcView, x1, y1); // 茱莉亚集合
SendMessage (hwndView, WM_SIZE, NULL, NULL);
break; }

case WM_SIZE: {RECT R;
GetClientRect (hWnd, &R);
int width = R.right & 0xfffffffc;
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc, OBJ_BITMAP);
if (hbm) {
BITMAP bm;
if (GetObject (hbm, sizeof (BITMAP), &bm)) {
if (width == bm.bmWidth&& R.bottom == bm.bmHeight)
break;//避免主窗口的尺寸没有改变时无意义的重建位图
}
}
if (R.bottom - R.top >= 16) {//避免使用无意义的窗口尺寸来建立位图
create_dib (hdc, width, R.bottom, 4);// 新建位图,作为分形地图
ghdc = hdc;
if (hdc == NULL)break;
SetBkMode (hdc, TRANSPARENT); // 透明方式背景
// 绘制分形图,作为母地图
mandelbrot (hdc, -ABS_X, ABS_X, -ABS_Y, ABS_Y); // 曼德博集合
SetTextColor (hdc, 0xff5555);
TextOut (hdc, 8, 8, "在此图上点击鼠标键:", 20);
TextOut (hdc, 24, 28, "[左] 迭代局部", 14);
TextOut (hdc, 24, 48, "[中] 呈现海马谷", 16);
TextOut (hdc, 24, 68, "[右] 茱莉亚图", 14);
HDC hdc2 = GetDC (hWnd);
BitBlt (hdc2, 0, 0, width, R.bottom, hdc, 0, 0, SRCCOPY);
ReleaseDC (hWnd, hdc2);
}
break; }

case WM_DESTROY: {
HBITMAP hbm = (HBITMAP)GetCurrentObject (hdc, OBJ_BITMAP);
if (hbm)DeleteObject (hbm);
DeleteDC (hdc);
hbm = (HBITMAP)GetCurrentObject (hdcView, OBJ_BITMAP);
if (hbm)DeleteObject (hbm);
DeleteDC (hdcView);

PostQuitMessage (0);
break; }

default:
result = DefWindowProc (hWnd, Msg, wParam, lParam);
break;
}
return result;
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, int nCmdShow)
{
load_pal (gColors16, 16, "4bits.pal");
load_pal (gColors256, 256, "8bits.pal");

WNDCLASS WC;
memset (&WC, 0, sizeof (WC));
ghInstance = hInstance;
WC.hInstance = hInstance;
WC.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
WC.lpszClassName = szClassName;
WC.lpfnWndProc = wnd_proc;
WC.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON1));
WC.hCursor = LoadCursorA (0, (LPCSTR)IDC_ARROW);
WC.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);;
WC.lpszMenuName = MAKEINTRESOURCE (IDR_MENU1);
if (!RegisterClass (&WC)) {
return 0;
}

ghwnd = CreateWindowExA (0, WC.lpszClassName,
szWindowName, WS_OVERLAPPEDWINDOW,
0, 0, 0, 0,
0, 0, hInstance, 0);
RECT WR, CR;
GetWindowRect (ghwnd, &WR);// 取得窗口的大小
GetClientRect (ghwnd, &CR);// 取得窗口客户区大小
int SW = GetSystemMetrics (SM_CXSCREEN);// 屏宽
int SH = GetSystemMetrics (SM_CYSCREEN);// 屏高
int MH = GetSystemMetrics (SM_CYMENU);// 菜单条高
int newW = WINDOW_W + (WR.right - WR.left) - (CR.right - CR.left);// 窗口的新宽度
int newH = WINDOW_H + MH + (WR.bottom - WR.top) - (CR.bottom - CR.top);// 窗口的新高度
MoveWindow (ghwnd, SW / 2, (SH - newH) / 2, newW, newH, FALSE);
ShowWindow (ghwnd, 1);
UpdateWindow (ghwnd);

WC.lpfnWndProc = view_proc;
WC.lpszMenuName = NULL;
WC.lpszClassName = szClass2;
if (!RegisterClass (&WC)) {
return 0;
}
hwndView = create_view (ghwnd, szWindow2, hdcView);

SetFocus (ghwnd);
init_gdiplus ();
srand (GetTickCount ());

MSG msg, *pMsg = &msg;
while (GetMessageA (pMsg, 0, 0, 0))
{
TranslateMessage (pMsg);
DispatchMessageA (pMsg);
}

DestroyWindow (hwndView);
DestroyWindow (ghwnd);
UnregisterClass (szClass2, hInstance);
UnregisterClass (szClassName, hInstance);
free_gdiplus ();
return (pMsg->wParam);
}


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 1:59:50

棉花采摘棉铃检测数据集 | 棉花成熟度 棉铃检测 智慧农业 目标检测 YOLO格式9252期

棉花采摘棉铃检测数据集 | 棉花成熟度 棉铃检测 智慧农业 目标检测 YOLO格式9252期 数据集概述 本数据集专注于农田场景下棉花棉铃成熟度与采摘状态的视觉检测&#xff0c;服务于智慧农业、自动化采摘及棉花产量评估。数据涵盖四种典型的棉铃状态&#xff0c;适配目标检测模型…

作者头像 李华
网站建设 2026/10/28 1:58:49

如何写 Skill 番外,你的第一个自定义 agent

摘要&#xff1a;同一段盲测提示词&#xff0c;手写到第三遍&#xff0c;我把它固化成了自己的第一个自定义 agent。这篇讲造岗的全套手艺&#xff1a;什么时候该把重复劳动抽象成岗&#xff0c;一份 agent 定义文件的三个读者各读什么&#xff0c;字段拼错和方言差异怎么防&am…

作者头像 李华
网站建设 2026/10/28 1:58:08

高通SA8295P与SA8775P深度对比:智能座舱平台升级,真正改变了什么?

高通SA8295P与SA8775P深度对比:智能座舱平台升级,真正改变了什么? 一、SA8295P和SA8775P为什么值得对比? 高通SA8295P和SA8775P是智能座舱及融合计算领域值得关注的两款汽车级处理器。 SA8295P主要面向高性能智能座舱,包括多屏显示、3D图形渲染、视频编解码、摄像头处理…

作者头像 李华
网站建设 2026/10/28 1:57:38

端子线和线束到底有什么区别?三问分清,报价不跑偏

- **端子线**&#xff1d;一根&#xff08;或几根&#xff09;线&#xff0c;两端压接端子&#xff0c;做成可直接插接的成品线&#xff1b; - **线束**&#xff1d;按图纸把多根线组合起来&#xff0c;可能有分支、护套、包扎、多个连接器的**总成**。一句话&#xff1a;**端子…

作者头像 李华
网站建设 2026/10/28 1:57:24

# 微信小程序 video视频弹幕案例

微信小程序 video视频弹幕案例 一、实验目的 掌握小程序video视频组件的使用&#xff0c;熟悉弹幕发送接口sendDanmu&#xff0c;实现自定义弹幕发送功能&#xff0c;理解视频上下文createVideoContext。 二、核心知识点 <video>组件&#xff1a;danmu-list、enable-danm…

作者头像 李华