//绘制图片
void CMyPSDlg::OnPaint()
{
CPaintDC dc(this);
CRect clientRect;
GetClientRect(&clientRect);
// 双缓冲初始化
if (m_memDC.GetSafeHdc() == NULL ||
m_memBitmap.GetSafeHandle() == NULL ||
GetBitmapDimension(m_memBitmap) != clientRect.Size())
{
// 先删除旧对象
if (m_memDC.GetSafeHdc()) {
m_memDC.SelectObject((HBITMAP)NULL); // 必须先取消选择位图
m_memDC.DeleteDC();
}
if (m_memBitmap.GetSafeHandle()) {
m_memBitmap.DeleteObject();
}
// 创建新对象
CDC dcScreen;
dcScreen.CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
m_memDC.CreateCompatibleDC(&dcScreen);
m_memBitmap.CreateCompatibleBitmap(&dcScreen, clientRect.Width(), clientRect.Height());
CBitmap* pOldBitmap = m_memDC.SelectObject(&m_memBitmap);
if (pOldBitmap) {
pOldBitmap->DeleteObject(); // 删除默认的位图对象
}
dcScreen.DeleteDC();
}
// 1. 绘制背景
m_memDC.FillSolidRect(clientRect, GetSysColor(COLOR_BTNFACE));
// 创建 GDI+ Graphics 对象
Gdiplus::Graphics graphics(m_memDC);
// 2. 绘制底层图像(带透明度)
if (!m_displayImage.IsNull())
{
CRect imgRect = GetImageClientRect();
// 转换为 GDI+ Bitmap
std::unique_ptr<Gdiplus::Bitmap> gdiBg(CImageToGdiPlusBitmap(m_displayImage));
if (gdiBg)
{
// 设置透明度矩阵
Gdiplus::ColorMatrix bgMatrix = {
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, m_bgAlpha, 0, // 底层透明度
0, 0, 0, 0, 1
};
Gdiplus::ImageAttributes bgAttr;
bgAttr.SetColorMatrix(&bgMatrix);
// 绘制图像
graphics.DrawImage(
gdiBg.get(),
Gdiplus::Rect(imgRect.left, imgRect.top, imgRect.Width(), imgRect.Height()),
0, 0, m_displayImage.GetWidth(), m_displayImage.GetHeight(),
Gdiplus::UnitPixel, &bgAttr
);
}
}
// 3. 绘制上层图像(带透明度,与底层完全重合)
if (!m_overlayImage.IsNull())
{
CRect imgRect = GetImageClientRect(); // 使用与底层相同的区域
// 转换为 GDI+ Bitmap
std::unique_ptr<Gdiplus::Bitmap> gdiOverlay(CImageToGdiPlusBitmap(m_overlayImage));
if (gdiOverlay)
{
// 设置透明度矩阵
Gdiplus::ColorMatrix overlayMatrix = {
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, m_overlayAlpha, 0, // 上层透明度
0, 0, 0, 0, 1
};
Gdiplus::ImageAttributes overlayAttr;
overlayAttr.SetColorMatrix(&overlayMatrix);
// 绘制图像(使用与底层完全相同的区域)
graphics.DrawImage(
gdiOverlay.get(),
Gdiplus::Rect(imgRect.left, imgRect.top, imgRect.Width(), imgRect.Height()),
0, 0,
m_overlayImage.GetWidth(),
m_overlayImage.GetHeight(),
Gdiplus::UnitPixel,
&overlayAttr
);
}
}
// 3. 绘制涂鸦
if (m_bDoodleMode || m_doodleStrokes.size() > 0)
{
Gdiplus::Graphics graphics(m_memDC);
// 绘制已完成的涂鸦笔画
for (int i = 0; i < m_doodleStrokes.size(); i++)
{
DrawDoodleStroke(graphics, m_doodleStrokes[i]);
}
// 绘制当前正在进行的涂鸦笔画
if (m_pCurrentStroke && m_pCurrentStroke->points.size() > 0)
{
DrawDoodleStroke(graphics, *m_pCurrentStroke);
}
}
// 4. 绘制选区
if (m_selectionMode == SELECT_RECTANGLE && !m_rcSelection.IsRectNull())
{
// 将图像坐标的选区转换为屏幕坐标
CRect selRect = ConvertToScreenRect(m_rcSelection);
// 区分选中状态和保持状态
if (m_bSelecting)
{
// 动态选区:使用XOR模式(无闪烁拖动)
CPen pen(PS_DOT, 1, RGB(255, 0, 0));
CPen* pOldPen = m_memDC.SelectObject(&pen);
m_memDC.SelectStockObject(NULL_BRUSH);
int oldROP = m_memDC.SetROP2(R2_XORPEN);
m_memDC.Rectangle(selRect);
m_memDC.SetROP2(oldROP);
m_memDC.SelectObject(pOldPen);
pen.DeleteObject();
}
else
{
// 静态选区:使用常规绘制(保持可见)
CPen pen(PS_SOLID, 1, RGB(0, 255, 0)); // 绿色实线
CPen* pOldPen = m_memDC.SelectObject(&pen);
m_memDC.SelectStockObject(NULL_BRUSH);
m_memDC.Rectangle(selRect);
m_memDC.SelectObject(pOldPen);
pen.DeleteObject();
}
}
else if (m_selectionMode == SELECT_FREE_FORM)
{
// 绘制不规则选区
if (m_selectionMode == SELECT_FREE_FORM && !m_freeFormPoints.empty())
{
// 转换为屏幕坐标数组
CPoint* screenPoints = new CPoint[m_freeFormPoints.size()];
for (size_t i = 0; i < m_freeFormPoints.size(); ++i)
{
screenPoints[i] = ConvertToScreenPoint(m_freeFormPoints[i]);
}
// 绘制路径
CPen pen(PS_SOLID, 1, m_bFreeFormSelecting ? RGB(255, 0, 0) : RGB(0, 255, 0));
CPen* pOldPen = m_memDC.SelectObject(&pen);
m_memDC.SelectStockObject(NULL_BRUSH);
// 使用Polyline绘制连续线条
m_memDC.Polyline(screenPoints, (int)m_freeFormPoints.size());
// 如果是闭合路径,绘制填充区域
if (!m_bFreeFormSelecting && m_freeFormPoints.size() > 2)
{
// 绘制闭合提示标记
CRect markRect(screenPoints[0].x - 3, screenPoints[0].y - 3,
screenPoints[0].x + 3, screenPoints[0].y + 3);
CBrush markBrush(RGB(255, 255, 0));
m_memDC.FillRect(&markRect, &markBrush);
}
m_memDC.SelectObject(pOldPen);
pen.DeleteObject();
delete[] screenPoints;
}
}
// 最终将内存DC内容绘制到屏幕DC
dc.BitBlt(0, 0, clientRect.Width(), clientRect.Height(), &m_memDC, 0, 0, SRCCOPY);
}
std::unique_ptr<Gdiplus::Bitmap> CMyPSDlg::CImageToGdiPlusBitmap(CImage& image)
{
if (image.IsNull())
return nullptr;
// 直接通过HBITMAP转换
return std::unique_ptr<Gdiplus::Bitmap>(
Gdiplus::Bitmap::FromHBITMAP(
(HBITMAP)image, // CImage可隐式转换为HBITMAP
NULL // 不使用调色板
)
);
}
void CMyPSDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_bDoodleMode) {
// 开始新的涂鸦笔画
m_pCurrentStroke = new DoodleStroke;
m_pCurrentStroke->color = m_doodleColor;
m_pCurrentStroke->width = m_doodleWidth;
CRect imgRect = GetImageClientRect();
if (imgRect.PtInRect(point)) {
// 转换为图像坐标存储
CPoint imgPoint = ConvertToImagePoint(point);
m_pCurrentStroke->points.push_back(imgPoint);
}
SetCapture();
return;
}
if (!m_displayImage.IsNull())
{
CRect imgRect = GetImageClientRect();
if (imgRect.PtInRect(point))
{
if (m_selectionMode == SELECT_RECTANGLE) {
// 原有矩形选区代码
m_ptStart.x = (point.x - imgRect.left) * m_displayImage.GetWidth() / imgRect.Width();
m_ptStart.y = (point.y - imgRect.top) * m_displayImage.GetHeight() / imgRect.Height();
m_ptEnd = m_ptStart;
m_bSelecting = true;
SetCapture();
}
else if (m_selectionMode == SELECT_FREE_FORM)
{
m_bFreeFormSelecting = true;
m_freeFormPoints.clear();
// 转换为图像坐标存储
CPoint imgPoint;
imgPoint.x = (point.x - imgRect.left) * m_displayImage.GetWidth() / imgRect.Width();
imgPoint.y = (point.y - imgRect.top) * m_displayImage.GetHeight() / imgRect.Height();
m_freeFormPoints.push_back(imgPoint);
SetCapture();
Invalidate();
}
}
}
CDialogEx::OnLButtonDown(nFlags, point);
}
void CMyPSDlg::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDoodleMode && (nFlags & MK_LBUTTON) && m_pCurrentStroke) {
CRect imgRect = GetImageClientRect();
if (imgRect.PtInRect(point)) {
// 转换为图像坐标
CPoint imgPoint = ConvertToImagePoint(point);
// 避免重复添加相近点
if (m_pCurrentStroke->points.empty() ||
abs(imgPoint.x - m_pCurrentStroke->points.back().x) > 2 ||
abs(imgPoint.y - m_pCurrentStroke->points.back().y )> 2)
{
m_pCurrentStroke->points.push_back(imgPoint);
// 计算需要刷新的区域
if (m_pCurrentStroke->points.size() > 1) {
CPoint prevPoint = ConvertToScreenPoint(m_pCurrentStroke->points[m_pCurrentStroke->points.size() - 2]);
CPoint currPoint = ConvertToScreenPoint(imgPoint);
CRect invalidRect(
min(prevPoint.x, currPoint.x) - m_doodleWidth - 2,
min(prevPoint.y, currPoint.y) - m_doodleWidth - 2,
max(prevPoint.x, currPoint.x) + m_doodleWidth + 2,
max(prevPoint.y, currPoint.y) + m_doodleWidth + 2
);
RedrawWindow(invalidRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
}
}
return;
}
if (m_bSelecting && !m_displayImage.IsNull() && m_selectionMode == SELECT_RECTANGLE)
{
// 1. 获取图像显示区域
CRect imgRect = GetImageClientRect();
// 2. 约束鼠标在图像范围内
point.x = max(imgRect.left, min(imgRect.right, point.x));
point.y = max(imgRect.top, min(imgRect.bottom, point.y));
// 3. 保存旧选区用于刷新
CRect oldSelRect = ConvertToScreenRect(m_rcSelection);
// 4. 转换为图像坐标并更新选区
m_ptEnd.x = (point.x - imgRect.left) * m_displayImage.GetWidth() / imgRect.Width();
m_ptEnd.y = (point.y - imgRect.top) * m_displayImage.GetHeight() / imgRect.Height();
m_rcSelection = CRect(
min(m_ptStart.x, m_ptEnd.x),
min(m_ptStart.y, m_ptEnd.y),
max(m_ptStart.x, m_ptEnd.x),
max(m_ptStart.y, m_ptEnd.y)
);
// 5. 计算需要刷新的区域(新旧选区合并)
CRect newSelRect = ConvertToScreenRect(m_rcSelection);
CRect invalidRect = oldSelRect | newSelRect;
invalidRect.InflateRect(2, 2); // 扩大2像素避免残影
// 6. 使用临时DC绘制动态选区
CClientDC dc(this);
CPen pen(PS_DOT, 1, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&pen);
dc.SelectStockObject(NULL_BRUSH);
int oldROP = dc.SetROP2(R2_XORPEN);
dc.Rectangle(newSelRect); // 使用已定义的 newSelRect
dc.SetROP2(oldROP);
dc.SelectObject(pOldPen);
pen.DeleteObject();
// 6. 精准刷新
RedrawWindow(invalidRect, NULL,
RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE);
}
else if (m_bFreeFormSelecting && !m_displayImage.IsNull())
{
CRect imgRect = GetImageClientRect();
if (imgRect.PtInRect(point))
{
// 转换为图像坐标
CPoint imgPoint;
imgPoint.x = (point.x - imgRect.left) * m_displayImage.GetWidth() / imgRect.Width();
imgPoint.y = (point.y - imgRect.top) * m_displayImage.GetHeight() / imgRect.Height();
// 检查是否闭合(当前点接近起始点)
if (m_freeFormPoints.size() > 10) // 至少10个点才开始检测闭合
{
const CPoint& firstPoint = m_freeFormPoints[0];
double distance = sqrt(pow(imgPoint.x - firstPoint.x, 2) +
pow(imgPoint.y - firstPoint.y, 2));
// 如果距离小于10像素认为闭合
if (distance < 10.0)
{
// 自动闭合路径
m_freeFormPoints.push_back(firstPoint);
// 创建多边形区域
if (m_rgnFreeForm.GetSafeHandle())
m_rgnFreeForm.DeleteObject();
m_rgnFreeForm.CreatePolygonRgn(
m_freeFormPoints.data(),
(int)m_freeFormPoints.size(),
ALTERNATE);
m_bFreeFormSelecting = false;
ReleaseCapture();
Invalidate();
return;
}
}
// 避免重复添加相近点
if (m_freeFormPoints.empty() ||
abs(imgPoint.x - m_freeFormPoints.back().x) > 2 ||
abs(imgPoint.y - m_freeFormPoints.back().y) > 2)
{
m_freeFormPoints.push_back(imgPoint);
// 计算需要刷新的区域
CRect invalidRect;
if (m_freeFormPoints.size() > 1)
{
CPoint prevScreen = ConvertToScreenPoint(m_freeFormPoints[m_freeFormPoints.size() - 2]);
CPoint currScreen = ConvertToScreenPoint(imgPoint);
invalidRect.SetRect(
min(prevScreen.x, currScreen.x) - 2,
min(prevScreen.y, currScreen.y) - 2,
max(prevScreen.x, currScreen.x) + 2,
max(prevScreen.y, currScreen.y) + 2
);
}
// 智能刷新
if (m_freeFormPoints.size() <= 2)
{
InvalidateRect(imgRect);
}
else if (!invalidRect.IsRectEmpty())
{
RedrawWindow(invalidRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
}
}
}
__super::OnMouseMove(nFlags, point);
}
void CMyPSDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bDoodleMode && m_pCurrentStroke) {
// 完成当前笔画
if (m_pCurrentStroke->points.size() > 0) {
m_doodleStrokes.push_back(*m_pCurrentStroke);
}
delete m_pCurrentStroke;
m_pCurrentStroke = nullptr;
ReleaseCapture();
return;
}
if (m_bSelecting || m_bFreeFormSelecting)
{
if (m_selectionMode == SELECT_RECTANGLE) {
// 原有矩形选区结束代码
m_bSelecting = false;
}
else if (m_bFreeFormSelecting && m_selectionMode == SELECT_FREE_FORM)
{
CRect imgRect = GetImageClientRect();
if (imgRect.PtInRect(point))
{
// 转换为图像坐标
CPoint imgPoint;
imgPoint.x = (point.x - imgRect.left) * m_displayImage.GetWidth() / imgRect.Width();
imgPoint.y = (point.y - imgRect.top) * m_displayImage.GetHeight() / imgRect.Height();
// 确保至少3个点才闭合
if (m_freeFormPoints.size() > 2)
{
// 闭合路径:添加第一个点作为终点
m_freeFormPoints.push_back(m_freeFormPoints[0]);
// 删除旧区域(如果存在)
if (m_rgnFreeForm.GetSafeHandle())
m_rgnFreeForm.DeleteObject();
// 创建多边形区域
m_rgnFreeForm.CreatePolygonRgn(
m_freeFormPoints.data(),
(int)m_freeFormPoints.size(),
ALTERNATE);
TRACE(_T("创建多边形选区,点数: %d\n"), m_freeFormPoints.size());
}
else
{
// 点数不足,清除选区
m_freeFormPoints.clear();
}
}
m_bFreeFormSelecting=false;
}
ReleaseCapture();
Invalidate();
CDialogEx::OnLButtonUp(nFlags, point);
}
}
void CMyPSDlg::ApplyAdvancedEffectsToSelection(int brightness, float saturation,
bool bBlackWhite, bool bSepia, int mosaicSize)
{
// 0. 参数检查和初始化
if (m_originalImage.IsNull())
return;
// 1. 准备选区数据
CRect validRect;
bool hasSelection = false;
if (m_selectionMode == SELECT_RECTANGLE && !m_rcSelection.IsRectNull())
{
// 矩形选区
validRect.SetRect(
max(0, m_rcSelection.left),
max(0, m_rcSelection.top),
min(m_originalImage.GetWidth(), m_rcSelection.right),
min(m_originalImage.GetHeight(), m_rcSelection.bottom)
);
hasSelection = true;
}
else if (m_selectionMode == SELECT_FREE_FORM && m_rgnFreeForm.GetSafeHandle())
{
// 不规则选区
m_rgnFreeForm.GetRgnBox(&validRect);
hasSelection = true;
}
if (!hasSelection)
return;
// 2. 准备目标图像
if (m_displayImage.GetBits() == nullptr ||
m_displayImage.GetWidth() != m_originalImage.GetWidth() ||
m_displayImage.GetHeight() != m_originalImage.GetHeight())
{
m_displayImage.Destroy();
m_displayImage.Create(m_originalImage.GetWidth(),
m_originalImage.GetHeight(),
m_originalImage.GetBPP());
}
// 3. 获取像素数据
BYTE* pDest = (BYTE*)m_displayImage.GetBits();
BYTE* pSrc = (BYTE*)m_originalImage.GetBits();
int destPitch = m_displayImage.GetPitch();
int srcPitch = m_originalImage.GetPitch();
int channels = (m_displayImage.GetBPP() == 32) ? 4 : 3;
// 4. 处理选区像素(包含马赛克效果)
#pragma omp parallel for
for (int y = validRect.top; y < validRect.bottom; y++)
{
for (int x = validRect.left; x < validRect.right; x++)
{
// 检查是否在选区内(不规则选区需要额外检查)
if (m_selectionMode == SELECT_FREE_FORM &&
!m_rgnFreeForm.PtInRegion(x, y))
{
continue;
}
BYTE* pDestPixel = &pDest[y * destPitch + x * channels];
// 计算马赛克块的平均颜色(直接从原图获取)
if (mosaicSize > 1)
{
int blockStartX = (x / mosaicSize) * mosaicSize;
int blockStartY = (y / mosaicSize) * mosaicSize;
int blockEndX = min(blockStartX + mosaicSize, validRect.right);
int blockEndY = min(blockStartY + mosaicSize, validRect.bottom);
long sumR = 0, sumG = 0, sumB = 0;
int count = 0;
for (int by = blockStartY; by < blockEndY; by++) {
for (int bx = blockStartX; bx < blockEndX; bx++) {
// 不规则选区需要检查马赛克块内每个点
if (m_selectionMode != SELECT_FREE_FORM ||
m_rgnFreeForm.PtInRegion(bx, by))
{
BYTE* pSrcPixel = &pSrc[by * srcPitch + bx * channels];
sumB += pSrcPixel[0];
sumG += pSrcPixel[1];
sumR += pSrcPixel[2];
count++;
}
}
}
if (count > 0) {
BYTE avgR = sumR / count;
BYTE avgG = sumG / count;
BYTE avgB = sumB / count;
// 使用ProcessSinglePixel处理(基于马赛克平均颜色)
BYTE mosaicPixel[3] = { avgB, avgG, avgR };
ProcessSinglePixel(pDestPixel, brightness, saturation,
bBlackWhite, bSepia, mosaicPixel);
}
}
else
{
// 无马赛克效果,直接使用原图像素
BYTE* pSrcPixel = &pSrc[y * srcPitch + x * channels];
ProcessSinglePixel(pDestPixel, brightness, saturation,
bBlackWhite, bSepia, pSrcPixel);
}
}
}
// 5. 刷新显示
RedrawSelectionArea(validRect);
if (!m_displayImage.IsNull()) {
// 方法1:使用BitBlt复制(推荐)
m_originalImage.Destroy(); // 先销毁旧图像
m_originalImage.Create(m_displayImage.GetWidth(),
m_displayImage.GetHeight(),
m_displayImage.GetBPP());
HDC hdcSrc = m_displayImage.GetDC();
HDC hdcDst = m_originalImage.GetDC();
BitBlt(hdcDst, 0, 0, m_displayImage.GetWidth(), m_displayImage.GetHeight(),
hdcSrc, 0, 0, SRCCOPY);
m_displayImage.ReleaseDC();
m_originalImage.ReleaseDC();
}
}
void CMyPSDlg::ProcessSinglePixel(BYTE* pPixel, int brightness, float saturation, bool bBlackWhite, bool bSepia, const BYTE* pOriginalPixel = nullptr) // 新增原始像素参数
{
// 默认使用当前像素作为原始值(兼容旧代码)
BYTE origB = pOriginalPixel ? pOriginalPixel[0] : pPixel[0];
BYTE origG = pOriginalPixel ? pOriginalPixel[1] : pPixel[1];
BYTE origR = pOriginalPixel ? pOriginalPixel[2] : pPixel[2];
// 亮度调整(基于原始值的非线性变换)
float brightFactor = 1.0f + brightness / 100.0f; // brightness=100 → 2.0倍
float r = origR * brightFactor;
float g = origG * brightFactor;
float b = origB * brightFactor;
// 饱和度调整(保持基于原始值)
if (saturation != 1.0f) {
float gray = 0.299f * origR + 0.587f * origG + 0.114f * origB;
r = gray + saturation * (r - gray);
g = gray + saturation * (g - gray);
b = gray + saturation * (b - gray);
}
// 写入目标像素(限幅)
pPixel[0] = static_cast<BYTE>(min(255, max(0, static_cast<int>(b))));
pPixel[1] = static_cast<BYTE>(min(255, max(0, static_cast<int>(g))));
pPixel[2] = static_cast<BYTE>(min(255, max(0, static_cast<int>(r))));
// 滤镜处理(互斥)
if (bBlackWhite || bSepia) {
BYTE gray = static_cast<BYTE>(0.299f * pPixel[2] + 0.587f * pPixel[1] + 0.114f * pPixel[0]);
if (bBlackWhite) {
pPixel[0] = pPixel[1] = pPixel[2] = gray;
}
else if (bSepia) {
pPixel[2] = min(255, static_cast<int>(0.393 * pPixel[2] + 0.769 * pPixel[1] + 0.189 * pPixel[0]));
pPixel[1] = min(255, static_cast<int>(0.349 * pPixel[2] + 0.686 * pPixel[1] + 0.168 * pPixel[0]));
pPixel[0] = min(255, static_cast<int>(0.272 * pPixel[2] + 0.534 * pPixel[1] + 0.131 * pPixel[0]));
}
}
}
void CMyPSDlg::RedrawSelectionArea(const CRect& imageRect)
{
// 1. 检查输入有效性
if (imageRect.IsRectEmpty() || m_displayImage.IsNull())
return;
// 2. 精确坐标转换
CRect screenRect = ConvertToScreenRect(imageRect);
// 3. 验证屏幕区域有效性
CRect clientRect;
GetClientRect(&clientRect);
if (!screenRect.IntersectRect(screenRect, clientRect))
return;
// 4. 动态调整扩大范围
int inflate = max(2, min(10, screenRect.Width() / 20));
screenRect.InflateRect(inflate, inflate);
// 5. 安全刷新
if (!screenRect.IsRectEmpty()) {
RedrawWindow(screenRect, NULL,
RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE | RDW_NOCHILDREN);
}
}
// 将图像坐标系中的矩形转换为屏幕客户区坐标
CRect CMyPSDlg::ConvertToScreenRect(const CRect& imageRect)
{
if (m_displayImage.IsNull())
return CRect(0, 0, 0, 0);
CRect imgClientRect = GetImageClientRect();
return CRect(
imgClientRect.left + imageRect.left * imgClientRect.Width() / m_displayImage.GetWidth(),
imgClientRect.top + imageRect.top * imgClientRect.Height() / m_displayImage.GetHeight(),
imgClientRect.left + imageRect.right * imgClientRect.Width() / m_displayImage.GetWidth(),
imgClientRect.top + imageRect.bottom * imgClientRect.Height() / m_displayImage.GetHeight()
);
}
BOOL CMyPSDlg::OnEraseBkgnd(CDC* pDC)
{
return TRUE; // 完全禁用背景擦除
}
// 获取位图实际尺寸的辅助函数
CSize CMyPSDlg::GetBitmapDimension(CBitmap& bitmap)
{
BITMAP bm;
if (bitmap.GetSafeHandle() && bitmap.GetBitmap(&bm))
return CSize(bm.bmWidth, bm.bmHeight);
return CSize(0, 0);
}
void CMyPSDlg::OnBnClickedButton7() {
CFileDialog dlg(TRUE, _T("*.bmp;*.jpg;*.png"), NULL, OFN_FILEMUSTEXIST,
_T("Image Files|*.bmp;*.jpg;*.png||"), this);
if (dlg.DoModal() == IDOK) {
m_overlayImage.Destroy();
if (m_overlayImage.Load(dlg.GetPathName()) == S_OK) {
// 设置上层图片显示区域(例如:Picture Control下半部分)
CRect imgRect = GetImageClientRect();
m_overlayRect = imgRect; // 默认全区域叠加
Invalidate(); // 触发重绘
}
}
}
//规则选区
void CMyPSDlg::OnBnClickedButton8()
{
m_selectionMode = SELECT_RECTANGLE;
ResetSelection(); // 重置选区状态
}
//不规则选区
void CMyPSDlg::OnBnClickedButton9()
{
m_selectionMode = SELECT_FREE_FORM;
ResetSelection();
// 强制重绘以确保状态更新
Invalidate();
}
void CMyPSDlg::ResetSelection()
{
m_bSelecting = false;
m_bFreeFormSelecting = false;
m_rcSelection.SetRectEmpty();
// 清理不规则选区资源
if (m_rgnFreeForm.GetSafeHandle()) {
m_rgnFreeForm.DeleteObject();
}
m_freeFormPoints.clear();
Invalidate();
}
BOOL CMyPSDlg::HasSelection() const
{
if (m_selectionMode == SELECT_RECTANGLE) {
return !m_rcSelection.IsRectNull();
}
else {
return m_rgnFreeForm.GetSafeHandle() != NULL;
}
}
void CMyPSDlg::CleanUpResources()
{
// 确保释放DC
if (m_doodleLayer.GetDC() != NULL) {
m_doodleLayer.ReleaseDC();
}
// 销毁涂鸦层
if (!m_doodleLayer.IsNull()) {
m_doodleLayer.Destroy();
}
}
// 在对话框销毁时调用
void CMyPSDlg::OnDestroy()
{
CleanUpResources();
if (m_doodleLayer.GetBits()) {
m_doodleLayer.Destroy();
}
// ... 其他清理代码 ...
CDialogEx::OnDestroy();
}
CPoint CMyPSDlg::ConvertToScreenPoint(CPoint imagePoint)
{
CRect imgRect = GetImageClientRect();
return CPoint(
imgRect.left + imagePoint.x * imgRect.Width() / m_displayImage.GetWidth(),
imgRect.top + imagePoint.y * imgRect.Height() / m_displayImage.GetHeight()
);
}
void CMyPSDlg::OnBnClickedButton10() // 开始涂鸦
{
if (m_originalImage.IsNull()) {
AfxMessageBox(_T("请先打开图片再开始涂鸦"), MB_ICONWARNING);
return;
}
InitDoodleLayer(); // 直接调用
if (m_doodleLayer.IsNull()) {
AfxMessageBox(_T("涂鸦层初始化失败"));
return;
}
m_bDoodleMode = true;
m_bIsDrawing = false;
ResetSelection();
GetDlgItem(IDC_BUTTON10)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON11)->EnableWindow(TRUE);
}
void CMyPSDlg::OnBnClickedButton11() // 结束涂鸦
{
m_bDoodleMode = false;
// 确保释放所有资源
if (m_bIsDrawing) {
OnLButtonUp(0, CPoint(0, 0));
}
Invalidate();
}
void CMyPSDlg::InitDoodleLayer()
{
// 清理旧资源
if (m_doodleLayer.GetDC()) {
m_doodleLayer.ReleaseDC();
}
m_doodleLayer.Destroy();
if (!m_originalImage.IsNull()) {
// 创建32位带Alpha通道的位图
if (!m_doodleLayer.Create(m_originalImage.GetWidth(),
m_originalImage.GetHeight(),
32, CImage::createAlphaChannel)) {
AfxMessageBox(_T("无法创建涂鸦图层"));
return;
}
// 初始化透明背景
HDC hDC = m_doodleLayer.GetDC();
if (hDC) {
// 使用GDI+确保透明背景
Gdiplus::Graphics graphics(hDC);
graphics.Clear(Gdiplus::Color::Transparent);
m_doodleLayer.ReleaseDC();
}
}
}
// 坐标转换
CPoint CMyPSDlg::ConvertToImagePoint(CPoint screenPoint)
{
CRect imgRect = GetImageClientRect();
return CPoint(
(screenPoint.x - imgRect.left) * m_originalImage.GetWidth() / imgRect.Width(),
(screenPoint.y - imgRect.top) * m_originalImage.GetHeight() / imgRect.Height()
);
}
// 局部重绘涂鸦区域
void CMyPSDlg::RedrawDoodleArea(const CRect& imageRect)
{
CRect screenRect = ConvertToScreenRect(imageRect);
screenRect.InflateRect(m_doodleWidth * 2, m_doodleWidth * 2);
RedrawWindow(screenRect, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
//红色
void CMyPSDlg::OnBnClickedRadio1()
{
m_doodleColor = RGB(255, 0, 0); // 红色
TRACE(_T("颜色切换为红色\n"));
}
//蓝
void CMyPSDlg::OnBnClickedRadio2()
{
m_doodleColor = RGB(0, 0, 255); // 蓝色
TRACE(_T("颜色切换为蓝色\n"));
}
//黄
void CMyPSDlg::OnBnClickedRadio3()
{
m_doodleColor = RGB(255, 255, 0); // 黄色
TRACE(_T("颜色切换为黄色\n"));
}
void CMyPSDlg::OnBnClickedButton4()
{
// 添加有效性检查
}
CMyPSDlg::~CMyPSDlg()
{
CleanUpResources();
// 确保释放所有GDI资源
if (m_rgnFreeForm.GetSafeHandle()) {
m_rgnFreeForm.DeleteObject();
}
if (m_memDC.GetSafeHdc()) {
m_memDC.SelectObject((HBITMAP)NULL);
m_memDC.DeleteDC();
}
if (m_memBitmap.GetSafeHandle()) {
m_memBitmap.DeleteObject();
}
}
void CMyPSDlg::DrawDoodleStroke(Gdiplus::Graphics& graphics, const DoodleStroke& stroke)
{
if (stroke.points.size() < 1) return;
Gdiplus::Pen pen(Gdiplus::Color(GetRValue(stroke.color),
GetGValue(stroke.color),
GetBValue(stroke.color)),
(Gdiplus::REAL)stroke.width);
pen.SetLineJoin(Gdiplus::LineJoinRound);
std::vector<Gdiplus::Point> gdiPoints;
for (int i = 0; i < stroke.points.size(); i++)
{
CPoint screenPoint = ConvertToScreenPoint(stroke.points[i]);
gdiPoints.push_back(Gdiplus::Point(screenPoint.x, screenPoint.y));
}
if (gdiPoints.size() > 1)
{
graphics.DrawCurve(&pen, &gdiPoints[0], (INT)gdiPoints.size());
}
else
{
Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(stroke.color),
GetGValue(stroke.color),
GetBValue(stroke.color)));
graphics.FillEllipse(&brush,
gdiPoints[0].X - stroke.width / 2,
gdiPoints[0].Y - stroke.width / 2,
stroke.width,
stroke.width);
}
}
//撤销最后一笔涂鸦
void CMyPSDlg::OnBnClickedButton12()
{
if (!m_doodleStrokes.empty()) {
m_doodleStrokes.pop_back(); // 直接删除最后一个元素
Invalidate();
}
}
// 清空所有涂鸦
void CMyPSDlg::OnBnClickedButton13()
{
m_doodleStrokes.clear();
if (m_pCurrentStroke) {
delete m_pCurrentStroke;
m_pCurrentStroke = nullptr;
}
Invalidate();
}