news 2026/9/12 4:56:42

MFC彩色正方形绘制实战:GDI绘图机制与双缓冲技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MFC彩色正方形绘制实战:GDI绘图机制与双缓冲技巧

MFC里画东西,第一反应多数人都是“在OnDraw里写两句FillRect不就完事了”,可真落到实际项目里,坐标怎么算、颜色怎么选、窗口一缩放就重绘、GDI对象一多就泄漏,样样都是坑。这篇文章就围绕“基于MFC绘制一个彩色正方形”这个入门题,把背后那套GDI绘图机制讲透,再给出可以直接抄走的代码和几个进阶玩法,适合刚接触MFC的人,也适合想要系统搞懂设备上下文、画刷画笔、双缓冲这些概念的老同学。你要是有VC++ 6.0或者VS2010以上版本,直接照着敲一遍,效果比看十遍文档都实在。

1. 先别急着写代码:MFC绘图的底层逻辑

1.1 设备上下文与“画什么、在哪画”

MFC绘图绕不开一个概念:设备上下文,英文简称DC。你可以把它理解成一块“虚拟画布”,所有画线、画圆、填充颜色的动作,最终都是在一个DC上完成的。MFC里的CDC类就是对Windows GDI(图形设备接口)的封装,它把画刷、画笔、字体、位图这些GDI对象和一系列绘图函数统一收在一起。

初学者最容易绕晕的是CPaintDC、CClientDC、CWindowDC这三兄弟。CPaintDC只能在OnPaint或者OnDraw这种响应WM_PAINT消息的地方用,它是从窗口客户区拿DC,并且在构造时自动BeginPaint,析构时自动EndPaint。CClientDC则更自由一点,它在你需要的时候直接GetDC客户区,常用于鼠标消息里画临时图形。CWindowDC连非客户区都能画,边框、标题栏都能动,但这属于“大力出奇迹”,日常项目里用得少,而且容易把人家的窗口画花。

对咱们这个彩色正方形项目来说,最简单的路子就是在视图类的OnDraw里用CPaintDC。为什么选OnDraw而不是自己写OnPaint?因为MFC的CView框架里,OnDraw会被框架在WM_PAINT时自动调用,而且和文档视图架构绑定得特别好,视图尺寸变化、窗口被遮挡后恢复,框架都会帮你触发必要的重绘。自己接管OnPaint也不是不行,但你需要处理很多事情,比如背景擦除、无效矩形区域优化,对新手来说增加理解成本和踩坑面。

1.2 OnDraw、窗口重绘与消息驱动

很多同学写了OnDraw,却发现“窗口被别的窗口挡住再拉回来,正方形不见了”,这就是没搞懂重绘机制。Windows窗口系统是一个消息驱动模型,当窗口需要重新绘制时,系统会给它发WM_PAINT消息。MFC把这件事封装得比较友好:Invalidate、InvalidateRect这些函数会向窗口发送“请重绘”的通知,框架收到WM_PAINT后,会调用你的OnDraw,并传入一个已经准备好的DC指针。

所以关键点是什么?是你的绘图代码必须写在OnDraw里,并且不能依赖上一次绘制的状态。窗口每次重绘,都是一次“推倒重来”,你要做的不是记住“之前画过多少”,而是在OnDraw里把整个正方形完整画出来。这一点和游戏开发里的“渲染循环”思路很像:每一帧都是全量绘制,而不是增量绘制。

还有一个小细节容易被忽略:如果你在鼠标消息(比如OnLButtonDown)里用CClientDC画了图形,然后窗口一刷新,画面就没了。因为消息处理函数里画的内容并不会被“记住”,重绘时只有OnDraw里的内容会被重新执行。这也是为什么MFC官方文档和多数教程都建议:把相对长期、需要保留的绘制内容统统放进OnDraw,临时性的交互反馈(比如拖拽过程中的橡皮筋框)才放在消息处理里画。

2. 从零搭建单文档项目,把彩色正方形画出来

2.1 创建MFC应用工程

我用的是Visual Studio 2019(VS2010、VS2015、VS2022操作基本一样),打开“新建项目”,选择“MFC应用”,项目名随意,例如“ColorSquareDemo”。到了向导界面,应用类型选“单文档”,项目样式选“MFC标准”,其余保持默认即可。这里要特别注意:如果你选的不是基于文档视图的架构,而是“对话框”,那后面OnDraw就没地方写了,得改成在对话框的OnPaint里画,逻辑类似但路径不同。

创建完成后,解决方案资源管理器里会生成一堆类。我们需要动的核心是视图类,名字一般是CColorSquareDemoView,里面已经有个OnDraw函数的空壳。这就是我们的主战场。

如果你是老派的VC++ 6.0用户,操作也差不多:文件 -> 新建 -> 工程 -> MFC AppWizard(exe),选单文档,完成。MFC的绘图机制二十多年没怎么变过,这套知识放到今天依然通用。

2.2 核心代码:在OnDraw中绘制彩色正方形

现在打开视图类的OnDraw函数,把下面的代码贴进去。完整代码我在这里一次性给全,每一段都有注释。

void CColorSquareDemoView::OnDraw(CDC* pDC) { CColorSquareDemoDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // 1. 获取客户区矩形 CRect rcClient; GetClientRect(&rcClient); // 2. 定义正方形边长,并计算居中位置 int nSideLength = 200; int nLeft = (rcClient.Width() - nSideLength) / 2; int nTop = (rcClient.Height() - nSideLength) / 2; CRect rcSquare(nLeft, nTop, nLeft + nSideLength, nTop + nSideLength); // 3. 用画刷填充正方形内部颜色 CBrush brFill(RGB(255, 100, 50)); // 橙红色 pDC->FillRect(rcSquare, &brFill); // 4. 用画笔绘制边界 CPen penFrame(PS_SOLID, 3, RGB(0, 0, 255)); // 蓝色边框,宽3像素 CPen* pOldPen = pDC->SelectObject(&penFrame); pDC->Rectangle(rcSquare); pDC->SelectObject(pOldPen); // 5. 在正方形中心绘制文字,验证DC的文本能力 pDC->SetBkMode(TRANSPARENT); // 背景透明,否则文字会有白底 pDC->SetTextColor(RGB(255, 255, 255)); CString strText = _T("MFC Color Square"); pDC->DrawText(strText, rcSquare, DT_CENTER | DT_VCENTER | DT_SINGLELINE); }

这里解释几个关键点。FillRect接收一个CRect和一个CBrush指针,作用是填充矩形区域;Rectangle则是画一个“空心矩形边框”,使用的是当前选中的画笔。所以整体效果就是:内部填充橙色,边框是蓝色粗线,看上去就是一个标准的彩色正方形。

CRect的右手系逻辑也要留意。在Windows屏幕坐标系里,y轴是向下增加的,所以CRect(left, top, right, bottom)中,bottom比top数值大才是正常朝向。很多人刚开始会写成“左上角坐标+宽高”的形式,结果发现正方形位置跑偏,多半是把bottom参数当高度用了。这个坑后面章节还会细说。

2.3 运行与效果验证,顺便聊聊CRect的坐标坑

按F5运行,你会看到一个居中显示的正方形,底色橙红,边框蓝色,中间有一行白色文字。这时候可以随便拉伸窗口,正方形会一直保持居中,因为每次重绘都会重新计算客户区大小。

这一步跑通之后,你就可以开始做实验了。比如把窗口拖到屏幕边缘,再拖回来,观察正方形是否会丢失。理论上MFC会自动重绘,正方形始终在。这里可以顺便验证一下之前讲的“重绘机制”:如果你把绘图代码移到一个普通成员函数里(比如菜单命令处理函数),然后调用Invalidate触发重绘,画面就会恢复成空白的,因为OnDraw里什么都没画。反过来,如果你只在OnDraw里画,不再调用任何重绘相关函数,画面则永远保持正常。这个简单的验证,能让你彻底理解WM_PAINT和OnDraw的关系。

关于坐标,还有个容易踩的点:客户区GetClientRect返回的矩形,right和bottom并不是坐标的最大值,而是“宽度”和“高度”。比如一个800x600的窗口,GetClientRect返回CRect(0, 0, 800, 600),这里的800和600实际是宽度和高度。当你在Rectangle里使用这个矩形时,画出来的实际宽高是正确的,因为GDI对right和bottom是“不包含”的。但如果你自己计算坐标,就容易绕晕。我的习惯是始终把CRect当“左上角+宽高”来理解,用right - left得到宽度,bottom - top得到高度,而不是直接拿right当x坐标去计算。

3. 让正方形“活”起来:渐变、交互与双缓冲

3.1 从纯色到渐变:让“彩色”名副其实

标题里写的是“彩色正方形”,如果只是填充单一颜色,多少有点辜负这个“彩色”。我建议做一个渐变效果,让正方形从左到右颜色连续变化,这样既锻炼颜色理解,又让成果更炫。

GDI没有直接提供渐变填充矩形的高层函数,但我们可以用逐行扫描的方式模拟。思路很简单:正方形内部区域,从上到下(或者从左到右)按像素行绘制一条条水平线,每条线的颜色根据位置实时计算。为了让代码简洁,我选择纵向渐变,也就是每条水平方向直线的颜色相同,行与行之间颜色变化。

void CColorSquareDemoView::DrawGradientSquare(CDC* pDC, CRect rc) { int nHeight = rc.Height(); int nWidth = rc.Width(); for (int y = 0; y < nHeight; y++) { // 颜色按当前行位置做线性变化 BYTE byRed = static_cast<BYTE>(255.0 * y / nHeight); BYTE byGreen = static_cast<BYTE>(255.0 * (nHeight - y) / nHeight); BYTE byBlue = static_cast<BYTE>(128 + 127 * sin(y * 3.14159 / nHeight)); CPen penLine(PS_SOLID, 1, RGB(byRed, byGreen, byBlue)); CPen* pOldPen = pDC->SelectObject(&penLine); pDC->MoveTo(rc.left, rc.top + y); pDC->LineTo(rc.right, rc.top + y); pDC->SelectObject(pOldPen); } }

这段代码里,红色的分量从0递增到255,绿色从255递减到0,蓝色用一个正弦函数做波动,这样得到的渐变不但有方向感,还会出现颜色的自然过渡。你把OnDraw里原来的FillRect换成调用DrawGradientSquare,再运行看效果,会明显比单色填充生动得多。

这里有一个性能问题值得说:逐行画线的效率并不高,正方形边长如果是200,就是循环200次,每次创建一支画笔再删除,开销不小。如果正方形边长变成2000,你再试试,刷新一下就能感受到明显的卡顿。这也是为什么大型绘图项目通常会用FillSolidRect或者直接操作DIB位图的像素数据,而不是一条线一条线地画。这属于后话,但你需要知道这个边界在哪里。

3.2 鼠标拖拽绘制正方形:消息处理与橡皮筋效果

静态的正方形演示到位后,可以加一点交互:用鼠标在窗口上拖拽,动态生成一个正方形。这就要用到视图类的鼠标消息了。

头文件里需要添加成员变量:

protected: BOOL m_bDrawing; // 是否正在拖拽 CPoint m_ptOrigin; // 按下鼠标时的起点

构造函数里初始化:

m_bDrawing = FALSE; m_ptOrigin = CPoint(0, 0);

然后通过类向导(或者手动添加)三个消息处理函数:

void CColorSquareDemoView::OnLButtonDown(UINT nFlags, CPoint point) { // 按下左键,记录起点,开始拖拽 m_bDrawing = TRUE; m_ptOrigin = point; CView::OnLButtonDown(nFlags, point); } void CColorSquareDemoView::OnMouseMove(UINT nFlags, CPoint point) { if (m_bDrawing) { // 这里使用异或绘制模式,达到“橡皮筋”效果 CClientDC dc(this); CRect rcClient; GetClientRect(&rcClient); // 计算正方形矩形:以起点和当前点为对角顶点 CRect rcSquare(m_ptOrigin, point); int nSide = min(rcSquare.Width(), rcSquare.Height()); if (point.x >= m_ptOrigin.x && point.y >= m_ptOrigin.y) rcSquare.SetRect(m_ptOrigin.x, m_ptOrigin.y, m_ptOrigin.x + nSide, m_ptOrigin.y + nSide); else if (point.x < m_ptOrigin.x && point.y >= m_ptOrigin.y) rcSquare.SetRect(m_ptOrigin.x - nSide, m_ptOrigin.y, m_ptOrigin.x, m_ptOrigin.y + nSide); else if (point.x >= m_ptOrigin.x && point.y < m_ptOrigin.y) rcSquare.SetRect(m_ptOrigin.x, m_ptOrigin.y - nSide, m_ptOrigin.x + nSide, m_ptOrigin.y); else rcSquare.SetRect(m_ptOrigin.x - nSide, m_ptOrigin.y - nSide, m_ptOrigin.x, m_ptOrigin.y); // 设置异或模式 int nOldRop = dc.SetROP2(R2_XORPEN); // 先画一个旧正方形来擦除(因为异或,同一个矩形画两次会消失) dc.Rectangle(m_rcLastSquare); m_rcLastSquare = rcSquare; dc.Rectangle(m_rcLastSquare); // 恢复绘制模式 dc.SetROP2(nOldRop); } CView::OnMouseMove(nFlags, point); } void CColorSquareDemoView::OnLButtonUp(UINT nFlags, CPoint point) { if (m_bDrawing) { m_bDrawing = FALSE; // 正式绘制:把最终的正方形固化到OnDraw的内容中 m_rcFinalSquare = m_rcLastSquare; Invalidate(FALSE); // 请求重绘 } CView::OnLButtonUp(nFlags, point); }

这个方案里用了R2_XORPEN异或模式。它的原理是:新像素颜色由画笔颜色和背景颜色按位异或得到。同一个矩形区域画两次,颜色会恢复到第一次画之前的状态。这样拖拽过程中,旧矩形就被“画第二次”的方式擦掉了,从而产生橡皮筋效果。这也是Windows程序员处理橡皮筋线的经典做法。

构造函数的初始化里,需要给m_rcLastSquare和m_rcFinalSquare一个初始值,比如SetRect(0, 0, 0, 0),避免第一次OnMouseMove时画一个未初始化的矩形导致乱画。

3.3 双缓冲绘图:彻底解决闪烁问题

拖拽交互加完,你会发现一个严重问题:经常拖拽时画面闪烁,尤其窗口尺寸大或者机器配置一般的时候,那个闪断真的让人怀疑人生。闪烁的根源是“背景擦除 + 重绘”被分开执行了:Windows先把旧背景抹成白色,再在当前DC上画新图形,这个过程中间有一个短暂的空档,人眼就会感知到闪烁。

解决办法是双缓冲。说白了就是在内存里建一块“后台画布”,把图形全部画到内存画布上,最后一次性把整块内存画布复制到屏幕。因为复制操作是原子性的,不会出现“先擦除再画”的中间态,闪烁自然就消失了。

MFC里实现双缓冲的标准套路是这样的:

void CColorSquareDemoView::OnDraw(CDC* pDC) { // 获取客户区 CRect rcClient; GetClientRect(&rcClient); // 创建内存DC和兼容位图 CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap bmp; bmp.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height()); CBitmap* pOldBmp = memDC.SelectObject(&bmp); // 用背景色填充内存DC,否则边缘会有杂色 CBrush brBack(RGB(255, 255, 255)); memDC.FillRect(rcClient, &brBack); // 在内存DC上完成所有绘图 // 1. 画渐变正方形 CRect rcSquare = CalcCenteredSquare(rcClient); // 自己封装个计算函数 DrawGradientSquare(&memDC, rcSquare); // 2. 画边框 CPen penFrame(PS_SOLID, 3, RGB(0, 0, 255)); CPen* pOldPen = memDC.SelectObject(&penFrame); memDC.Rectangle(rcSquare); memDC.SelectObject(pOldPen); // 3. 画文字 memDC.SetBkMode(TRANSPARENT); memDC.SetTextColor(RGB(255, 255, 255)); memDC.DrawText(_T("Double Buffered"), rcSquare, DT_CENTER | DT_VCENTER | DT_SINGLELINE); // 一次性把内存DC内容复制到屏幕DC pDC->BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &memDC, 0, 0, SRCCOPY); // 恢复内存DC的旧位图,然后局部变量会自动释放资源 memDC.SelectObject(pOldBmp); }

这里CreateCompatibleBitmap是关键字眼,它创建了一块兼容屏幕显示的位图。所有在memDC上的绘制操作看似在视图上,其实发生在内存里,速度飞快。BitBlt是“块复制”的意思,把内存里已经画好的完整画面一次性搬到屏幕DC。由于整个过程没有“擦除-画”的中间态,闪烁问题彻底解决。

在实际工程里,双缓冲还会配合OnEraseBkgnd重载。因为MFC默认会在WM_ERASEBKGND里用白色或背景画刷擦除窗口,这本身就是闪烁的来源之一。一个常见做法是在视图类里重载OnEraseBkgnd,直接返回TRUE,表示“背景擦除我包了”,把擦除工作合并到双缓冲里统一处理。

BOOL CColorSquareDemoView::OnEraseBkgnd(CDC* pDC) { // 屏蔽默认背景擦除,闪烁问题从源头消除 return TRUE; }

这样做以后,OnDraw里的双缓冲逻辑已经涵盖了背景填充,窗口客户区不会再出现“白色闪一下再画图”的问题。

4. MFC绘图最容易踩的坑与排查技巧

4.1 画不出来、颜色不对、坐标偏移:三大高频问题

先说最常见的:代码明明写了FillRect,但运行起来一片空白。这种情况99%是因为绘图代码没写在OnDraw里,而是写在了别的函数(比如OnCreate、OnInitialUpdate)中。OnCreate执行的时候,窗口还没有创建完成,客户区DC无效,画了也白画。OnInitialUpdate在视图创建后调用一次,但那里绘制的图形在窗口重绘时不会保留。正确的做法是:永久性内容进OnDraw,临时性内容放消息处理。

第二个问题是颜色看起来不对劲。比如RGB(255, 0, 0)画出来却是暗红色,或者FillRect填充后看不到文字。这通常是因为你忘了设置背景模式。文字绘制的默认背景是Opaque(不透明),文字背后会有一个矩形底色区域;如果你用FillRect填充了橙红色,再DrawText,文字背后就会出现一个白色的矩形块,观感很差。设置SetBkMode(TRANSPARENT)就能解决。

第三个问题是坐标偏移,尤其在做鼠标拖拽时。鼠标消息里的point坐标是相对于窗口客户区的,这一点没有问题。但如果你在OnDraw里画的时候用了整个窗口坐标系,或者用了屏幕坐标,图像就会偏离鼠标位置。建议统一使用客户区坐标,需要转换时用ScreenToClient / ClientToScreen。

还有一个细节不要忽略:画刷和画笔的选入与恢复。SelectObject会把一个新的GDI对象选进DC,同时返回旧的GDI对象。画完以后,必须把旧的选回去,否则这个DC销毁时可能因为GDI对象没有正确恢复而出现资源泄漏。MFC的CBrush、CPen对象有析构函数,会自动删除底层GDI对象,但如果你频繁创建新的画刷,在同一时刻有大量GDI对象存在,还是会撑爆系统资源。所以规范写法永远是“用完就选回老对象”。

4.2 高频问题速查表

现象根本原因解决方案
运行后一片空白,没有任何图形绘图代码没写在OnDraw,或窗口未重绘把永久性绘图逻辑移到OnDraw;临时绘制后用Invalidate触发重绘
图形被其他窗口遮挡后消失对WM_PAINT机制理解不到位确保全套绘图逻辑都在OnDraw里执行
窗口缩放时图形变形或位置偏移使用了固定坐标,没有计算客户区大小动态GetClientRect并按比例计算位置
拖拽时画面剧烈闪烁背景擦除与绘图分离重载OnEraseBkgnd返回TRUE,并用双缓冲
文字周围有白底背景模式是Opaque调用SetBkMode(TRANSPARENT)
颜色偏暗或发灰RGB参数顺序混淆或显示器色深问题RGB是(BYTE红, BYTE绿, BYTE蓝);检查显示器是否32位色
程序退出时报告GDI对象泄漏SelectObject后没有恢复旧对象绘图结束一定SelectObject回旧对象,CBrush/CPen实例不要长期持有
字体显示为方块乱码没有使用宽字符集或字体不支持中文用_Unicode宏;创建CString时用_T()宏,项目属性里字符集选“使用Unicode字符集”

4.3 这套思路还能用在哪儿:MFC绘图的影响范围

把彩色正方形画明白,你的能力并不是停留在“会画一个方块”,而是理解了整个GDI绘图体系。往后你做MFC项目,很多场景都能复用这套思路。

比如自定义控件:你可以在CStatic或CButton的子类里重写OnPaint,用双缓冲绘制进度条、仪表盘、图标按钮。核心步骤和画正方形一模一样:拿DC,创建内存DC,逐行或逐区域绘图,BitBlt一次性输出。

再比如数据可视化:你要画折线图、柱状图、波形图,本质上就是在一张CRect范围内计算坐标、画线和填充。很多项目的图表模块都是工程师自己用GDI手写的,用到的核心函数无非就是MoveTo、LineTo、Polygon、Rectangle这几样。

还有图像处理方向:MFC工程里读入一张图片,转成DIB位图,再在OnDraw里按像素处理,最后StretchBlt显示出来。这也就是热搜词里“mfc zxing-cpp 读取图片中的二维码”这类需求的底层逻辑——显示哪部分、怎么显示,全靠坐标和DC控制。甚至以后你用QT、用C#写图形界面,绘图模型的本质还是“拿DC、选GDI对象、画、释放”,只是API名字换了一茬。

所以这篇看似简单的小项目,其实是一把钥匙。你把单色、渐变、双缓冲、鼠标交互这些玩熟,后面遇到再复杂的界面需求,至少不会茫然无措。

根据我个人经验,从一个小正方形入手,比直接啃大型MFC工程要有效得多。我当年就是在会议室摸鱼的时候,拿这个题目练手,把OnDraw、DC、画刷、坐标计算这些概念一次性理清的。后来真到做实时曲线监控界面的时候,几乎没费什么劲就写完了,靠的就是这点底子。

这篇文章你可以当个脚本来敲,也可以当成“MFC绘图词典”随手翻。核心结论就这几句话:画布选对,坐标算对,GDI对象选进选回,长驻画面进OnDraw,闪烁就上双缓冲。把这五件事刻进脑子,你已经超过一半只会在对话框里拖按钮的人。下次再遇到需要“自己画点什么”的MFC需求,回来翻这篇就好。

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

Linux下ReadStat部署与数据格式转换实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:52:21

无锡南途科技:GEO优化如何重构企业内容与AI搜索的信任链

大模型搜索的普及正在改变一个根本问题&#xff1a;用户不再满足于十条蓝色链接&#xff0c;而是期待一个经过推理、整合、带有信源引用的直接答案。这种变化对内容生态的冲击是结构性的。过去围绕关键词密度和反向链接构建的排名逻辑&#xff0c;正在让位于以实体关系为核心的…

作者头像 李华
网站建设 2026/9/12 4:50:40

CTF Web入门指南:从BugKu靶场到通用解题方法论

刷 BugKu 的 Web 篇&#xff0c;是我带新人和自己重新梳理知识体系时都会干的一件事。这平台的好处在于&#xff0c;题目梯度拉得比较开&#xff0c;从送分题一路爬到需要静下心来做代码审计和逻辑绕过的题都有&#xff0c;而且不需要你自己搭靶场环境&#xff0c;浏览器打开就…

作者头像 李华
网站建设 2026/9/12 4:50:38

DDoS检测实战:从PCAP解析到低延迟XGBoost部署

简介&#xff1a;本资源是一套基于Python实现的DDoS网络入侵检测完整实践方案&#xff0c;面向网络安全初学者、机器学习入门者及本科毕业设计学生&#xff0c;聚焦于利用监督学习算法识别分布式拒绝服务攻击流量。资源包含可直接运行的源码、部署说明与数据集&#xff0c;覆盖…

作者头像 李华
网站建设 2026/9/12 4:48:49

2D角色PBR渲染实战:低成本实现3A级材质效果

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 4:48:26

整数对最小和问题的多语言实现与优化

1. 整数对最小和问题解析最近在技术社区看到一个挺有意思的算法题——"整数对最小和"&#xff0c;题目要求用Java、JS、Python和C四种语言分别实现。这个题目看似简单&#xff0c;但实际涉及不少算法优化的思考点&#xff0c;特别适合用来检验编程基本功和算法思维。…

作者头像 李华