InflateRect

InflateRect函数详解
本文详细介绍了InflateRect函数的功能及使用方法。InflateRect函数能够通过调整dx和dy参数来扩大或缩小指定矩形的宽度和高度。dx用于调整矩形左右两侧的宽度,而dy则用于调整矩形上下的高度。

 InflateRect

  InflateRect函数增大或减小指定矩形的宽和高。InflateRect函数在矩形的左和右增加dx,在矩形的上下增加dy。 dx和dy参数是由符号值。正数增加宽和高,负数减小。

  BOOL InflateRect(

  LPRECT lprc, //矩形

  int dx, // amount to adjust width

  int dy // amount to adjust height

  );

//绘制图片 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(); }
06-28
BOOL CMyPS2Dlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != nullptr) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); // IDR_MAINFRAME为资源ID // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 m_nSaveTimer = 0; m_bStateDirty = false; //light litslider.SetRange(0, 200); litslider.SetPos(100); m_nlightValue = 0; // 更新编辑框的值 litedit.SetWindowText(_T("0")); //saturation saturslider.SetRange(0, 200); saturslider.SetPos(100); saturslider.SetTicFreq(10); m_nsatureValue = 1.0f; // 更新编辑框的值 saturedit.SetWindowText(_T("0.0")); //msk mskslider.SetRange(1, 50); mskslider.SetPos(1); m_nmskValue = 1; // 更新编辑框的值 mskedit.SetWindowText(_T("1")); m_sliderLay1.SetRange(0, 100); // 0-100% 透明度 m_sliderLay1.SetPos(100); // 默认不透明 m_sliderLay2.SetRange(0, 100); m_sliderLay2.SetPos(50); // 默认半透明 m_bDoodleMode = false; m_doodleColor = RGB(255, 0, 0); // 默认 m_doodleThickness = 3; // 默认3像素 m_bDoodling = false; CSliderCtrl* pColorSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDERCOL); if (pColorSlider) { pColorSlider->SetRange(0, 360); pColorSlider->SetPos(0); // 对应红色 } // 涂鸦粗细滑块 CSliderCtrl* pThicknessSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDERTHI); if (pThicknessSlider) { pThicknessSlider->SetRange(1, 20); pThicknessSlider->SetPos(3); } // TODO: 在此添加额外的初始化代码 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CMyPS2Dlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } void CMyPS2Dlg::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(); } m_memDC.FillSolidRect(clientRect, GetSysColor(COLOR_BTNFACE)); Gdiplus::Graphics graphics(m_memDC); // 2. 绘制底层(未选中的图层) ImageLayer* bottomLayer = m_layer1.isSelected ? &m_layer2 : &m_layer1; if (bottomLayer->isVisible && !bottomLayer->displayImage.IsNull()) { DrawLayer(graphics, bottomLayer->displayImage, bottomLayer->alpha); } // 3. 绘制上层(选中的图层) ImageLayer* topLayer = m_layer1.isSelected ? &m_layer1 : &m_layer2; if (topLayer->isVisible && !topLayer->displayImage.IsNull()) { DrawLayer(graphics, topLayer->displayImage, topLayer->alpha); // 4. 在选中的图层上绘制选区 if (!m_rcSelection.IsRectNull() || !m_freeFormPoints.empty()) { DrawSelection(m_memDC, topLayer); } } if (m_bDoodleMode && !m_doodleTempImage.IsNull()) { CRect imgRect = GetImageClientRect(topLayer->image); std::unique_ptr<Gdiplus::Bitmap> gdiBitmap(CImageToGdiPlusBitmap(m_doodleTempImage)); if (gdiBitmap) { graphics.DrawImage( gdiBitmap.get(), Gdiplus::Rect(imgRect.left, imgRect.top, imgRect.Width(), imgRect.Height()), 0, 0, m_doodleTempImage.GetWidth(), m_doodleTempImage.GetHeight(), Gdiplus::UnitPixel ); } } // 5. 输出到屏幕 dc.BitBlt(0, 0, clientRect.Width(), clientRect.Height(), &m_memDC, 0, 0, SRCCOPY); } void CMyPS2Dlg::DrawLayer(Gdiplus::Graphics& graphics, CImage& image, float alpha) { CRect imgRect = GetImageClientRect(image); std::unique_ptr<Gdiplus::Bitmap> gdiBitmap(CImageToGdiPlusBitmap(image)); if (gdiBitmap) { Gdiplus::ColorMatrix matrix = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, alpha, 0, 0, 0, 0, 0, 1 }; Gdiplus::ImageAttributes attr; attr.SetColorMatrix(&matrix); graphics.DrawImage( gdiBitmap.get(), Gdiplus::Rect(imgRect.left, imgRect.top, imgRect.Width(), imgRect.Height()), 0, 0, image.GetWidth(), image.GetHeight(), Gdiplus::UnitPixel, &attr ); } } void CMyPS2Dlg::DrawSelection(CDC& dc, ImageLayer* layer) { 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 && !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; } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CMyPS2Dlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } //打开 void CMyPS2Dlg::OnBnClickedButton2() { CFileDialog dlg(TRUE, _T("*.bmp;*.jpg;*.png"), NULL, OFN_FILEMUSTEXIST, _T("Image Files|*.bmp;*.jpg;*.png||"), this); if (dlg.DoModal() == IDOK) { // 释放旧资源 m_layer1.history.clear(); m_layer1.currentHistoryIndex = -1; m_layer1.isSelected = true; if (!m_layer1.image.IsNull()) { m_layer1.image.Destroy(); } if (!m_layer1.displayImage.IsNull()) { m_layer1.displayImage.Destroy(); } if (m_layer1.image.Load(dlg.GetPathName()) != S_OK) { AfxMessageBox(L"图像加载失败!\n请检查格式支持(BMP/JPG/PNG)"); return; } else { // 加载新图像 ImageState initialState; initialState.brightness = 0; initialState.saturation = 1.0f; initialState.mosaic = 1; initialState.alpha = 1.0f; if (!m_layer1.image.IsNull()) { if (!initialState.image.IsNull()) { initialState.image.Destroy(); } initialState.image.Create(m_layer1.image.GetWidth(), m_layer1.image.GetHeight(), m_layer1.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(m_layer1.image.GetDC()); CDC* pDstDC = CDC::FromHandle(initialState.image.GetDC()); pDstDC->BitBlt(0, 0, m_layer1.image.GetWidth(), m_layer1.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); m_layer1.image.ReleaseDC(); initialState.image.ReleaseDC(); } m_layer1.history.push_back(initialState); m_layer1.history.push_back(initialState); m_layer1.currentHistoryIndex = 1; // 重置所有参数 m_nlightValue = 0; m_nsatureValue = 1.0f; m_nmskValue = 1; m_layer1.alpha = 1.0f; // 重置底层透明度为不透明 m_layer2.alpha = 0.5f; // 重置顶层透明度为半透明 // 初始化UI控件 litslider.SetPos(100); // 假设滑块范围0-200,100对应亮度0 saturslider.SetPos(100); // 假设滑块范围0-200,100对应饱和度1.0 mskslider.SetPos(1); // 马赛克块大小(1~20) m_sliderLay1.SetPos(100); // 新增:底层透明度滑块(0-100) m_sliderLay2.SetPos(50); // 新增:顶层透明度滑块(0-100) // 生成显示图像 UpdateDisplayImage(m_layer1); } } } //保存 void CMyPS2Dlg::OnBnClickedButton3() { CFileDialog dlg(FALSE, _T("*.jpg"), _T("output.jpg"), OFN_OVERWRITEPROMPT, _T("JPEG Files|*.jpg||"), this); if (dlg.DoModal() == IDOK) { if (m_layer1.isSelected) { if (m_layer1.displayImage.IsNull()) { // 改为检查 m_displayImage AfxMessageBox(_T("请先打开图片!")); return; } if (m_layer1.currentHistoryIndex < 0) { AfxMessageBox(_T("图片失效")); return; } else { m_layer1.displayImage.Save(dlg.GetPathName()); } } // 保存处理后的图像 else { if (m_layer2.displayImage.IsNull()) { // 改为检查 m_displayImage AfxMessageBox(_T("请先打开图片!")); return; } if (m_layer2.currentHistoryIndex < 0) { AfxMessageBox(_T("图片失效")); return; } else { m_layer2.displayImage.Save(dlg.GetPathName()); } } } } //退出 void CMyPS2Dlg::OnBnClickedButton4() { UINT i; i = MessageBox(_T("图片未保存,确定要退出吗?"), _T("提示"), MB_YESNO | MB_ICONQUESTION); if (i == IDYES) { exit(0); } } //亮度滑块 /*void CMyPS2Dlg::OnNMCustomdrawSliderlit(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); *pResult = 0; }*/ //亮度编辑框 void CMyPS2Dlg::OnEnChangeEditlit() { if (m_bStateDirty) return; CString strValue; litedit.GetWindowText(strValue); int brightness = _ttoi(strValue)+100 ; // 检查输入值是否在有效范围内 (-100 到 100) brightness = max(0, min(200, brightness)); litslider.SetPos(brightness); // 应用亮度调整 OnParameterChanged(); } void CMyPS2Dlg::ApplyBrightnessToPixels(BYTE* pBits, int width, int height, int pitch, int channels, int brightness) { for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { for (int c = 0; c < 3; c++) // 处理BGR通道 { int newVal = pRow[x * channels + c] + brightness; pRow[x * channels + c] = (BYTE)min(255, max(0, newVal)); } } } } void CMyPS2Dlg::ApplySaturationToPixels(BYTE* pBits, int width, int height, int pitch, int channels, float saturation) { // 确保饱和度范围合理(0.0~2.0) saturation = min(max(saturation, 0.0f), 2.0f); for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { BYTE& b = pRow[x * channels]; BYTE& g = pRow[x * channels + 1]; BYTE& r = pRow[x * channels + 2]; // 计算灰度值(更精确的权重) float gray = 0.299f * r + 0.587f * g + 0.114f * b; // 调整饱和度(非线性插值,避免过度失真) float blendFactor = (saturation < 1.0f) ? saturation : 1.0f + (saturation - 1.0f) * 0.5f; r = (BYTE)min(255.0f, max(0.0f, gray + blendFactor * (r - gray))); g = (BYTE)min(255.0f, max(0.0f, gray + blendFactor * (g - gray))); b = (BYTE)min(255.0f, max(0.0f, gray + blendFactor * (b - gray))); } } } void CMyPS2Dlg::ApplyMosaicToPixels(BYTE* pBits, int width, int height, int pitch, int channels, int blockSize) { for (int y = 0; y < height; y += blockSize) { for (int x = 0; x < width; x += blockSize) { // 计算当前块的边界 int blockEndX = min(x + blockSize, width); int blockEndY = min(y + blockSize, height); // 计算块内像素的平均颜色 long sumR = 0, sumG = 0, sumB = 0; int count = 0; for (int by = y; by < blockEndY; by++) { BYTE* pRow = pBits + by * pitch; for (int bx = x; bx < blockEndX; bx++) { sumB += pRow[bx * channels]; sumG += pRow[bx * channels + 1]; sumR += pRow[bx * channels + 2]; count++; } } BYTE avgR = (BYTE)(sumR / count); BYTE avgG = (BYTE)(sumG / count); BYTE avgB = (BYTE)(sumB / count); // 将整个块设为平均颜色 for (int by = y; by < blockEndY; by++) { BYTE* pRow = pBits + by * pitch; for (int bx = x; bx < blockEndX; bx++) { pRow[bx * channels] = avgB; pRow[bx * channels + 1] = avgG; pRow[bx * channels + 2] = avgR; } } } } } /*void CMyPS2Dlg::OnNMCustomdrawSlidersatur(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; }*/ //饱和度编辑框 void CMyPS2Dlg::OnEnChangeEditsatur() { if (m_bStateDirty) return; CString strValue; saturedit.GetWindowText(strValue); float sat = _ttof(strValue) * 100.0f + 100.0f; // 限制范围并同步滑块 sat = max(0.0f, min(200.0f, sat)); saturslider.SetPos((int)sat); // 统一计算为浮点数(0.0~2.0) m_nsatureValue = sat / 100.0f; // 新增此行 OnParameterChanged(); } void CMyPS2Dlg::OnNMCustomdrawSlidermsk(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; } //马赛克编辑框 void CMyPS2Dlg::OnEnChangeEditmsk() { if (m_bStateDirty) return; CString strValue; mskedit.GetWindowText(strValue); int size = _ttoi(strValue); // 限制范围并同步滑块 size = max(1, min(50, size)); mskslider.SetPos(size); // 应用马赛克效果 OnParameterChanged(); } //滑块 void CMyPS2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (m_bStateDirty)return; int id = pScrollBar->GetDlgCtrlID(); if (id == IDC_SLIDERLIT) { // 更新当前值(区分全局和选区) if (Selection* pSel = GetActiveSelection()) { pSel->brightness = litslider.GetPos() - 100; CString strValue; strValue.Format(_T("%d"), pSel->brightness); litedit.SetWindowText(strValue); } else { m_nlightValue = litslider.GetPos() - 100; CString strValue; strValue.Format(_T("%d"), m_nlightValue); litedit.SetWindowText(strValue); } } else if (id == IDC_SLIDERSATUR) { // 更新当前值(区分全局和选区) if (Selection* pSel = GetActiveSelection()) { pSel->saturation = litslider.GetPos() - 100; CString strValue; strValue.Format(_T("%d"), pSel->saturation); litedit.SetWindowText(strValue); } else { m_nsatureValue = (saturslider.GetPos()) / 100.0f; // 范围:0.0 ~ 2.0 float sat = m_nsatureValue - 1.0f; CString strValue; strValue.Format(_T("%.1f"), sat); saturedit.SetWindowText(strValue); } } else if (id == IDC_SLIDERMSK) { // 马赛克滑块 m_nmskValue = mskslider.GetPos(); // 范围:1 ~ 50 CString strValue; strValue.Format(_T("%d"), m_nmskValue); mskedit.SetWindowText(strValue); //UpdateDisplayImage(); } else if (id == IDC_SLIDER1) { m_layer1.alpha = (float)m_sliderLay1.GetPos() / 100.0f; // 范围:0.0 ~ 1.0 InvalidateRect(GetImageClientRect(m_layer1.image)); // 重绘图片区域 } // 顶层图片透明度滑块 else if (id == IDC_SLIDER2) { m_layer2.alpha = (float)m_sliderLay2.GetPos() / 100.0f; // 范围:0.0 ~ 1.0 InvalidateRect(m_overlayRect); // 只重绘叠加层区域 } OnParameterChanged(); CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar); } void CMyPS2Dlg::OnBnClickedButtonReset() { //if (!m_originalImage.IsNull()) { // m_nlightValue = 0; // m_nsatureValue = 0.0f; // m_nmskValue = 1; // litslider.SetPos(50); // saturslider.SetPos(50); // litslider.SetPos(1); // litedit.SetWindowText(_T("0")); // saturedit.SetWindowText(_T("0.0")); // mskedit.SetWindowText(_T("1")); // UpdateDisplayImage(); //} } //黑白 void CMyPS2Dlg::ApplyBlackWhiteToPixels(BYTE* pBits, int width, int height, int pitch, int channels) { for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { BYTE& b = pRow[x * channels]; BYTE& g = pRow[x * channels + 1]; BYTE& r = pRow[x * channels + 2]; // 计算灰度值(加权平均) BYTE gray = (BYTE)(0.299f * r + 0.587f * g + 0.114f * b); // 所有通道设为灰度值 b = g = r = gray; } } } //怀旧 void CMyPS2Dlg::ApplySepiaToPixels(BYTE* pBits, int width, int height, int pitch, int channels) { for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { BYTE& b = pRow[x * channels]; BYTE& g = pRow[x * channels + 1]; BYTE& r = pRow[x * channels + 2]; // 怀旧色计算公式 int newR = min(255, (int)(0.393f * r + 0.769f * g + 0.189f * b)); int newG = min(255, (int)(0.349f * r + 0.686f * g + 0.168f * b)); int newB = min(255, (int)(0.272f * r + 0.534f * g + 0.131f * b)); r = (BYTE)newR; g = (BYTE)newG; b = (BYTE)newB; } } } //定时器 void CMyPS2Dlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == SAVE_DELAY_TIMER) { KillTimer(m_nSaveTimer); m_nSaveTimer = 0; if (m_bStateDirty) { if (m_layer1.isSelected) { SaveCurrentState(m_layer1); } else { SaveCurrentState(m_layer2); } m_bStateDirty = false; } } CDialogEx::OnTimer(nIDEvent); } //新增状态 void CMyPS2Dlg::SaveCurrentState(ImageLayer& layer) { if (!m_bStateDirty) return; if (layer.currentHistoryIndex < 0) return; if (!m_rcSelection.IsRectNull() && !layer.modified) { return; } ImageState currentState; if (layer.modified) { currentState.brightness = layer.history[layer.currentHistoryIndex].brightness; currentState.saturation = layer.history[layer.currentHistoryIndex].saturation; currentState.mosaic = layer.history[layer.currentHistoryIndex].mosaic; currentState.alpha = layer.history[layer.currentHistoryIndex].alpha; layer.modified = false; } else { currentState.brightness = m_nlightValue; currentState.saturation = m_nsatureValue; currentState.mosaic = m_nmskValue; currentState.alpha = layer.alpha; } if (!layer.image.IsNull()) { if (!currentState.image.IsNull()) { currentState.image.Destroy(); } currentState.image.Create(layer.image.GetWidth(), layer.image.GetHeight(), layer.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(layer.image.GetDC()); CDC* pDstDC = CDC::FromHandle(currentState.image.GetDC()); pDstDC->BitBlt(0, 0, layer.image.GetWidth(), layer.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); layer.image.ReleaseDC(); currentState.image.ReleaseDC(); } if (layer.currentHistoryIndex >= 0 && layer.history[layer.currentHistoryIndex] == currentState) { return; } for (int i = layer.currentHistoryIndex + 1; i <= layer.history.size() - 1; i++) { layer.history.erase(layer.history.begin() + i); } layer.history.push_back(currentState); layer.currentHistoryIndex += 1; // 限制历史记录数量 const int MAX_HISTORY = 20; if (layer.history.size() > MAX_HISTORY) { layer.history.erase(layer.history.begin()); layer.currentHistoryIndex--; } m_bStateDirty = false; } //销毁 void CMyPS2Dlg::OnDestroy() { CDialogEx::OnDestroy(); // 调用基类实现 // 清理定时器资源 if (m_nSaveTimer != 0) { KillTimer(m_nSaveTimer); m_nSaveTimer = 0; } // 清理图像资源 if (!m_layer1.image.IsNull()) m_layer1.image.Destroy(); if (!m_layer2.image.IsNull()) m_layer2.image.Destroy(); if (!m_layer1.displayImage.IsNull()) m_layer1.displayImage.Destroy(); if (!m_layer2.displayImage.IsNull()) m_layer2.displayImage.Destroy(); if (!m_doodleTempImage.IsNull()) m_doodleTempImage.Destroy(); } //撤回 void CMyPS2Dlg::OnUndo() { if (m_layer1.isSelected) { if (m_layer1.currentHistoryIndex > 0) { m_layer1.currentHistoryIndex--; ApplyHistoryState(m_layer1.currentHistoryIndex, m_layer1); } else { AfxMessageBox(_T("不可撤回。")); } } else { if (m_layer2.currentHistoryIndex > 0) { m_layer2.currentHistoryIndex--; ApplyHistoryState(m_layer2.currentHistoryIndex, m_layer2); } else { AfxMessageBox(_T("不可撤回。")); } } } //重做 void CMyPS2Dlg::OnRedo() { if (m_layer1.isSelected) { if (m_layer1.currentHistoryIndex < static_cast<int>(m_layer1.history.size()) - 1) { m_layer1.currentHistoryIndex++; ApplyHistoryState(m_layer1.currentHistoryIndex, m_layer1); } else { AfxMessageBox(_T("不可重做。")); } } else { if (m_layer2.currentHistoryIndex < static_cast<int>(m_layer2.history.size()) - 1) { m_layer2.currentHistoryIndex++; ApplyHistoryState(m_layer2.currentHistoryIndex, m_layer2); } else { AfxMessageBox(_T("不可重做。")); } } } void CMyPS2Dlg::ApplyHistoryState(int index, ImageLayer& layer) { if (index < 0 || index >= static_cast<int>(layer.history.size())) return; const ImageState& state = layer.history[index]; if (!state.image.IsNull()) { if (!layer.image.IsNull()) { layer.image.Destroy(); } layer.image.Create(state.image.GetWidth(), state.image.GetHeight(), state.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(state.image.GetDC()); CDC* pDstDC = CDC::FromHandle(layer.image.GetDC()); pDstDC->BitBlt(0, 0, state.image.GetWidth(), state.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); state.image.ReleaseDC(); layer.image.ReleaseDC(); } // 更新UI控件 litslider.SetPos(state.brightness + 100); saturslider.SetPos((int)(state.saturation * 100.0f)); mskslider.SetPos(state.mosaic); if (m_layer1.isSelected) { m_sliderLay1.SetPos((int)(state.alpha * 100.0f)); } else { m_sliderLay2.SetPos((int)(state.alpha * 100.0f)); } CString str; str.Format(_T("%d"), state.brightness); litedit.SetWindowText(str); str.Format(_T("%.1f"), state.saturation); saturedit.SetWindowText(str); str.Format(_T("%d"), state.mosaic); mskedit.SetWindowText(str); // 更新当前参数 m_nlightValue = state.brightness; m_nsatureValue = state.saturation; m_nmskValue = state.mosaic; layer.alpha = state.alpha; // 更新显示 UpdateDisplayImage(layer); } //撤回 void CMyPS2Dlg::OnBnClickedButton1() { OnUndo(); // TODO: 在此添加控件通知处理程序代码 } //黑白滤镜 void CMyPS2Dlg::OnBnClickedButton6() { m_isBlackWhite = !m_isBlackWhite; m_isSepia = false; // 确保二者互斥 if (m_layer1.isSelected) { UpdateDisplayImage(m_layer1); } else { UpdateDisplayImage(m_layer2); } // TODO: 在此添加控件通知处理程序代码 } //怀旧滤镜 void CMyPS2Dlg::OnBnClickedButton7() { m_isSepia = !m_isSepia; m_isBlackWhite = false; // 确保二者互斥 if (m_layer1.isSelected) { UpdateDisplayImage(m_layer1); } else { UpdateDisplayImage(m_layer2); } // TODO: 在此添加控件通知处理程序代码 } CRect CMyPS2Dlg::GetImageClientRect(const CImage& image) { CRect ctrlRect; m_staticImage.GetWindowRect(&ctrlRect); ScreenToClient(&ctrlRect); int imgW = image.GetWidth(); int imgH = image.GetHeight(); double ratio = min( (double)ctrlRect.Width() / imgW, (double)ctrlRect.Height() / imgH ); int drawW = (int)(imgW * ratio); int drawH = (int)(imgH * ratio); int x = ctrlRect.left + (ctrlRect.Width() - drawW) / 2; int y = ctrlRect.top + (ctrlRect.Height() - drawH) / 2; return CRect(x, y, x + drawW, y + drawH); } std::unique_ptr<Gdiplus::Bitmap> CMyPS2Dlg::CImageToGdiPlusBitmap(CImage& image) { if (image.IsNull()) return nullptr; // 直接通过HBITMAP转换 return std::unique_ptr<Gdiplus::Bitmap>( Gdiplus::Bitmap::FromHBITMAP( (HBITMAP)image, // CImage可隐式转换为HBITMAP NULL // 不使用调色板 ) ); } void CMyPS2Dlg::OnLButtonDown(UINT nFlags, CPoint point) { if (m_bDoodleMode) { CRect imgRect = GetImageClientRect(m_layer1.isSelected ? m_layer1.image : m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_doodleTempImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_doodleTempImage.GetHeight() / imgRect.Height(); m_ptDoodleStart = imgPoint; m_bDoodling = true; } return; // 涂鸦模式下不进行其他选择操作 } if (m_layer1.isSelected) { CRect imgRect = GetImageClientRect(m_layer1.image); if (imgRect.PtInRect(point)) { if (m_selectionMode == SELECT_RECTANGLE) { // 原有矩形选区代码 m_ptStart.x = (point.x - imgRect.left) * m_layer1.displayImage.GetWidth() / imgRect.Width(); m_ptStart.y = (point.y - imgRect.top) * m_layer1.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_layer1.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer1.displayImage.GetHeight() / imgRect.Height(); m_freeFormPoints.push_back(imgPoint); SetCapture(); Invalidate(); } } } else { CRect imgRect = GetImageClientRect(m_layer2.image); if (imgRect.PtInRect(point)) { if (m_selectionMode == SELECT_RECTANGLE) { // 原有矩形选区代码 m_ptStart.x = (point.x - imgRect.left) * m_layer2.displayImage.GetWidth() / imgRect.Width(); m_ptStart.y = (point.y - imgRect.top) * m_layer2.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_layer2.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer2.displayImage.GetHeight() / imgRect.Height(); m_freeFormPoints.push_back(imgPoint); SetCapture(); Invalidate(); } } } select = false; CDialogEx::OnLButtonDown(nFlags, point); } void CMyPS2Dlg::OnMouseMove(UINT nFlags, CPoint point) { if (m_bDoodleMode && m_bDoodling) { CRect imgRect = GetImageClientRect(m_layer1.isSelected ? m_layer1.image : m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_doodleTempImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_doodleTempImage.GetHeight() / imgRect.Height(); // 绘制线段 DrawDoodleLine(m_ptDoodleStart, imgPoint); m_ptDoodleStart = imgPoint; // 刷新显示 Invalidate(FALSE); } return; } if (m_layer1.isSelected) { if (m_bSelecting && !m_layer1.displayImage.IsNull() && m_selectionMode == SELECT_RECTANGLE) { // 1. 获取图像显示区域 CRect imgRect = GetImageClientRect(m_layer1.image); // 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_layer1.displayImage.GetWidth() / imgRect.Width(); m_ptEnd.y = (point.y - imgRect.top) * m_layer1.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_layer1.displayImage.IsNull()) { CRect imgRect = GetImageClientRect(m_layer1.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_layer1.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer1.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); } } } } } else { if (m_bSelecting && !m_layer2.displayImage.IsNull() && m_selectionMode == SELECT_RECTANGLE) { // 1. 获取图像显示区域 CRect imgRect = GetImageClientRect(m_layer2.image); // 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_layer2.displayImage.GetWidth() / imgRect.Width(); m_ptEnd.y = (point.y - imgRect.top) * m_layer2.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_layer2.displayImage.IsNull()) { CRect imgRect = GetImageClientRect(m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_layer2.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer2.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 CMyPS2Dlg::OnLButtonUp(UINT nFlags, CPoint point) { if (m_bDoodleMode && m_bDoodling) { m_bDoodling = false; return; } if (m_bSelecting || m_bFreeFormSelecting) { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (m_selectionMode == SELECT_RECTANGLE && !m_rcSelection.IsRectNull()) { // 创建新矩形选区并激活 CreateNewSelection(m_rcSelection); m_pCurrentSelection->isActive = true; } else if (m_selectionMode == SELECT_FREE_FORM && m_freeFormPoints.size() > 2) { // 闭合自由选区路径 m_freeFormPoints.push_back(m_freeFormPoints[0]); // 创建新自由选区并激活 CreateNewFreeFormSelection(m_freeFormPoints); m_pCurrentSelection->isActive = true; // 创建区域对象用于后续绘制 if (m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.DeleteObject(); } m_rgnFreeForm.CreatePolygonRgn( m_freeFormPoints.data(), (int)m_freeFormPoints.size(), WINDING); } m_bSelecting = false; m_bFreeFormSelecting = false; ReleaseCapture(); Invalidate(); } CDialogEx::OnLButtonUp(nFlags, point); } void CMyPS2Dlg::RedrawSelectionArea(const CRect& imageRect) { // 1. 检查输入有效性 if (imageRect.IsRectEmpty() || (m_layer1.displayImage.IsNull() && m_layer2.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 CMyPS2Dlg::ConvertToScreenRect(const CRect& imageRect) { if (m_layer1.isSelected) { if (m_layer1.displayImage.IsNull()) return CRect(0, 0, 0, 0); CRect imgClientRect = GetImageClientRect(m_layer1.image); return CRect( imgClientRect.left + imageRect.left * imgClientRect.Width() / m_layer1.displayImage.GetWidth(), imgClientRect.top + imageRect.top * imgClientRect.Height() / m_layer1.displayImage.GetHeight(), imgClientRect.left + imageRect.right * imgClientRect.Width() / m_layer1.displayImage.GetWidth(), imgClientRect.top + imageRect.bottom * imgClientRect.Height() / m_layer1.displayImage.GetHeight() ); } else { if (m_layer2.displayImage.IsNull()) return CRect(0, 0, 0, 0); CRect imgClientRect = GetImageClientRect(m_layer2.image); return CRect( imgClientRect.left + imageRect.left * imgClientRect.Width() / m_layer2.displayImage.GetWidth(), imgClientRect.top + imageRect.top * imgClientRect.Height() / m_layer2.displayImage.GetHeight(), imgClientRect.left + imageRect.right * imgClientRect.Width() / m_layer2.displayImage.GetWidth(), imgClientRect.top + imageRect.bottom * imgClientRect.Height() / m_layer2.displayImage.GetHeight() ); } } BOOL CMyPS2Dlg::OnEraseBkgnd(CDC* pDC) { return TRUE; // 完全禁用背景擦除 } // 获取位图实际尺寸的辅助函数 CSize CMyPS2Dlg::GetBitmapDimension(CBitmap& bitmap) { BITMAP bm; if (bitmap.GetSafeHandle() && bitmap.GetBitmap(&bm)) return CSize(bm.bmWidth, bm.bmHeight); return CSize(0, 0); } //规则选区 void CMyPS2Dlg::OnBnClickedButton9() { m_selectionMode = SELECT_RECTANGLE; ResetSelection(); // 重置选区状态 // TODO: 在此添加控件通知处理程序代码 } //不规则选区 void CMyPS2Dlg::OnBnClickedButton10() { m_selectionMode = SELECT_FREE_FORM; ResetSelection(); // 添加调试输出 TRACE(_T("不规则选区模式已激活\n")); // 强制重绘以确保状态更新 Invalidate(); // TODO: 在此添加控件通知处理程序代码 } //添加图层 void CMyPS2Dlg::OnBnClickedButton11() { CFileDialog dlg(TRUE, _T("*.bmp;*.jpg;*.png"), NULL, OFN_FILEMUSTEXIST, _T("Image Files|*.bmp;*.jpg;*.png||"), this); if (dlg.DoModal() == IDOK) { // 释放旧资源 m_layer2.history.clear(); m_layer2.currentHistoryIndex = -1; m_layer1.isSelected = false; m_layer2.isSelected = true; if (!m_layer2.image.IsNull()) { m_layer2.image.Destroy(); } if (!m_layer2.displayImage.IsNull()) { m_layer2.displayImage.Destroy(); } if (m_layer2.image.Load(dlg.GetPathName()) != S_OK) { AfxMessageBox(L"图像加载失败!\n请检查格式支持(BMP/JPG/PNG)"); return; } else { // 加载新图像 ImageState initialState; initialState.brightness = 0; initialState.saturation = 1.0f; initialState.mosaic = 1; initialState.alpha = 0.5f; if (!m_layer2.image.IsNull()) { if (!initialState.image.IsNull()) { initialState.image.Destroy(); } initialState.image.Create(m_layer2.image.GetWidth(), m_layer2.image.GetHeight(), m_layer2.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(m_layer2.image.GetDC()); CDC* pDstDC = CDC::FromHandle(initialState.image.GetDC()); pDstDC->BitBlt(0, 0, m_layer2.image.GetWidth(), m_layer2.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); m_layer2.image.ReleaseDC(); initialState.image.ReleaseDC(); } m_layer2.history.push_back(initialState); m_layer2.history.push_back(initialState); m_layer2.currentHistoryIndex = 1; // 重置所有参数 m_nlightValue = 0; m_nsatureValue = 1.0f; m_nmskValue = 1; m_layer1.alpha = 1.0f; // 重置底层透明度为不透明 m_layer2.alpha = 0.5f; // 重置顶层透明度为半透明 // 初始化UI控件 litslider.SetPos(100); // 假设滑块范围0-200,100对应亮度0 saturslider.SetPos(100); // 假设滑块范围0-200,100对应饱和度1.0 mskslider.SetPos(1); // 马赛克块大小(1~20) m_sliderLay1.SetPos(100); // 新增:底层透明度滑块(0-100) m_sliderLay2.SetPos(50); // 新增:顶层透明度滑块(0-100) // 生成显示图像 UpdateDisplayImage(m_layer2); } } // TODO: 在此添加控件通知处理程序代码 } void CMyPS2Dlg::OnNMCustomdrawSliderl1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; } void CMyPS2Dlg::ResetSelection() { m_bSelecting = false; m_bFreeFormSelecting = false; m_rcSelection.SetRectEmpty(); // 清理不规则选区资源 if (m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.DeleteObject(); } m_freeFormPoints.clear(); Invalidate(); } BOOL CMyPS2Dlg::HasSelection() const { if (m_selectionMode == SELECT_RECTANGLE) { return !m_rcSelection.IsRectNull(); } else { return m_rgnFreeForm.GetSafeHandle() != NULL; } } void CMyPS2Dlg::CleanUpResources() { // 清理双缓冲资源 if (m_memDC.GetSafeHdc()) { m_memDC.SelectObject((HBITMAP)NULL); m_memDC.DeleteDC(); } if (m_memBitmap.GetSafeHandle()) { m_memBitmap.DeleteObject(); } // 清理其他GDI资源 if (m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.DeleteObject(); } } CPoint CMyPS2Dlg::ConvertToScreenPoint(CPoint imagePoint) { if (m_layer1.isSelected) { CRect imgRect = GetImageClientRect(m_layer1.image); return CPoint( imgRect.left + imagePoint.x * imgRect.Width() / m_layer1.displayImage.GetWidth(), imgRect.top + imagePoint.y * imgRect.Height() / m_layer1.displayImage.GetHeight() ); } else { CRect imgRect = GetImageClientRect(m_layer2.image); return CPoint( imgRect.left + imagePoint.x * imgRect.Width() / m_layer2.displayImage.GetWidth(), imgRect.top + imagePoint.y * imgRect.Height() / m_layer2.displayImage.GetHeight() ); } } void CMyPS2Dlg::SelectLayer(bool selectLayer1) { m_layer1.isSelected = selectLayer1; m_layer2.isSelected = !selectLayer1; // 更新滑块显示当前图层的透明度 if (selectLayer1) { m_sliderLay1.SetPos(static_cast<int>(m_layer1.alpha * 100.0f)); } else { m_sliderLay2.SetPos(static_cast<int>(m_layer2.alpha * 100.0f)); } Invalidate(); // 刷新显示 } //选择图层 void CMyPS2Dlg::OnBnClickedButton12() { SelectLayer(!m_layer1.isSelected); if (m_layer1.isSelected) { if (m_layer1.displayImage.IsNull()) { AfxMessageBox(_T("请先打开图片")); } else { AfxMessageBox(_T("已选 图层1")); } } else { if (m_layer2.displayImage.IsNull()) { AfxMessageBox(_T("请先打开图片")); } else { AfxMessageBox(_T("已选 图层2")); } } // TODO: 在此添加控件通知处理程序代码 } //颜色滑块 void CMyPS2Dlg::OnNMCustomdrawSlidercol(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDERCOL); if (pSlider) { int pos = pSlider->GetPos(); // 将滑块位置转换为颜色 (0-360度色相) m_doodleColor = HSLtoRGB(pos, 1.0, 0.5); } *pResult = 0; } // HSL转RGB辅助函数 COLORREF CMyPS2Dlg::HSLtoRGB(float H, float S, float L) { float C = (1 - fabs(2 * L - 1)) * S; float X = C * (1 - fabs(fmod(H / 60.0, 2) - 1)); float m = L - C / 2; float R, G, B; if (H >= 0 && H < 60) { R = C; G = X; B = 0; } else if (H >= 60 && H < 120) { R = X; G = C; B = 0; } else if (H >= 120 && H < 180) { R = 0; G = C; B = X; } else if (H >= 180 && H < 240) { R = 0; G = X; B = C; } else if (H >= 240 && H < 300) { R = X; G = 0; B = C; } else { R = C; G = 0; B = X; } BYTE r = (BYTE)((R + m) * 255); BYTE g = (BYTE)((G + m) * 255); BYTE b = (BYTE)((B + m) * 255); return RGB(r, g, b); } //粗细滑块 void CMyPS2Dlg::OnNMCustomdrawSliderthi(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDERTHI); if (pSlider) { m_doodleThickness = pSlider->GetPos(); } *pResult = 0; } void CMyPS2Dlg::StartDoodleMode() { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (activeLayer.displayImage.IsNull()) { AfxMessageBox(_T("请先打开图片")); return; } // 创建临时位图 if (!m_doodleTempImage.IsNull()) m_doodleTempImage.Destroy(); int width = activeLayer.displayImage.GetWidth(); int height = activeLayer.displayImage.GetHeight(); m_doodleTempImage.Create(width, height, activeLayer.displayImage.GetBPP()); // 复制当前显示图像到临时位图 CDC* pSrcDC = CDC::FromHandle(activeLayer.displayImage.GetDC()); CDC* pDstDC = CDC::FromHandle(m_doodleTempImage.GetDC()); pDstDC->BitBlt(0, 0, width, height, pSrcDC, 0, 0, SRCCOPY); activeLayer.displayImage.ReleaseDC(); m_doodleTempImage.ReleaseDC(); // 重置涂鸦数据 m_doodleLines.clear(); m_bDoodleMode = true; m_bDoodling = false; // 设置鼠标光标 SetCursor(LoadCursor(NULL, IDC_CROSS)); } // 结束涂鸦模式 void CMyPS2Dlg::EndDoodleMode(bool apply) { if (!m_bDoodleMode) return; if (apply) { ApplyDoodleToImage(); } // 清理资源 m_doodleTempImage.Destroy(); m_doodleLines.clear(); m_bDoodleMode = false; m_bDoodling = false; // 恢复鼠标光标 SetCursor(LoadCursor(NULL, IDC_ARROW)); // 刷新显示 Invalidate(FALSE); } // 将涂鸦应用到当前图层 void CMyPS2Dlg::ApplyDoodleToImage() { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (activeLayer.image.IsNull()) return; // 获取设备上下文 CDC* pDC = CDC::FromHandle(activeLayer.image.GetDC()); CPen pen(PS_SOLID, m_doodleThickness, m_doodleColor); CPen* pOldPen = pDC->SelectObject(&pen); // 绘制所有涂鸦线段 for (const auto& line : m_doodleLines) { pDC->MoveTo(line.first); pDC->LineTo(line.second); } pDC->SelectObject(pOldPen); activeLayer.image.ReleaseDC(); // 更新显示图像并保存状态 UpdateDisplayImage(activeLayer); SaveCurrentState(activeLayer); } // 绘制一条涂鸦线段 void CMyPS2Dlg::DrawDoodleLine(CPoint start, CPoint end) { if (m_doodleTempImage.IsNull()) return; CDC* pDC = CDC::FromHandle(m_doodleTempImage.GetDC()); CPen pen(PS_SOLID, m_doodleThickness, m_doodleColor); CPen* pOldPen = pDC->SelectObject(&pen); pDC->MoveTo(start); pDC->LineTo(end); pDC->SelectObject(pOldPen); m_doodleTempImage.ReleaseDC(); // 将线段添加到列表 m_doodleLines.push_back(std::make_pair(start, end)); } //开始涂鸦 void CMyPS2Dlg::OnBnClickedButton13() { StartDoodleMode(); } //应用涂鸦 void CMyPS2Dlg::OnBnClickedButton14() { EndDoodleMode(true); } //取消涂鸦 void CMyPS2Dlg::OnBnClickedButton15() { EndDoodleMode(false); } //重做 void CMyPS2Dlg::OnBnClickedButtonA() { OnRedo(); } void CMyPS2Dlg::UpdateDisplayImage(ImageLayer& layer) { if (layer.currentHistoryIndex < 0) return; if (layer.image.IsNull()) return; // 创建显示图像(不变) if (layer.displayImage.IsNull() || layer.displayImage.GetWidth() != layer.image.GetWidth() || layer.displayImage.GetHeight() != layer.image.GetHeight()) { layer.displayImage.Destroy(); layer.displayImage.Create(layer.image.GetWidth(), layer.image.GetHeight(), layer.image.GetBPP()); } // 复制原始图像到显示图像 HDC hdcSrc = layer.image.GetDC(); HDC hdcDst = layer.displayImage.GetDC(); BitBlt(hdcDst, 0, 0, layer.image.GetWidth(), layer.image.GetHeight(), hdcSrc, 0, 0, SRCCOPY); layer.image.ReleaseDC(); layer.displayImage.ReleaseDC(); // 获取像素数据 BYTE* pBits = (BYTE*)layer.displayImage.GetBits(); if (!pBits) return; int pitch = layer.displayImage.GetPitch(); int width = layer.displayImage.GetWidth(); int height = layer.displayImage.GetHeight(); int bpp = layer.displayImage.GetBPP(); int channels = (bpp == 32) ? 4 : 3; // 1. 首先应用全局效果 if (m_nmskValue > 1) ApplyMosaicToPixels(pBits, width, height, pitch, channels, m_nmskValue); if (m_nlightValue != 0) ApplyBrightnessToPixels(pBits, width, height, pitch, channels, m_nlightValue); if (m_nsatureValue != 1.0f) ApplySaturationToPixels(pBits, width, height, pitch, channels, m_nsatureValue); if (m_isBlackWhite) ApplyBlackWhiteToPixels(pBits, width, height, pitch, channels); else if (m_isSepia) ApplySepiaToPixels(pBits, width, height, pitch, channels); // 2. 然后应用选区效果(叠加在全局效果之上) for (auto& selection : m_selections) { if (!selection.rect.IsRectNull()) { // 矩形选区 ApplySelectionEffect(pBits, width, height, pitch, channels, selection.rect, selection.brightness, selection.saturation, selection.mosaic, selection.isBlackWhite, selection.isSepia); } else if (!selection.freeFormPoints.empty()) { // 自由选区 ApplyFreeFormSelectionEffect(pBits, width, height, pitch, channels, selection.freeFormPoints, selection.brightness, selection.saturation, selection.mosaic, selection.isBlackWhite, selection.isSepia); } } InvalidateRect(NULL, FALSE); UpdateWindow(); } void CMyPS2Dlg::ApplySelectionEffect(BYTE* pBits, int width, int height, int pitch, int channels, const CRect& rect, int brightness, float saturation, int mosaic, bool isBlackWhite, bool isSepia) { // 确保选区在图像范围内 CRect imageRect(0, 0, width, height); CRect effectRect; effectRect.IntersectRect(rect, imageRect); if (effectRect.IsRectEmpty()) return; // 直接处理选区内的每个像素 for (int y = effectRect.top; y < effectRect.bottom; y++) { BYTE* pPixel = pBits + y * pitch + effectRect.left * channels; for (int x = 0; x < effectRect.Width(); x++) { // 应用亮度 if (brightness != 0) { for (int c = 0; c < 3; c++) { // 只处理RGB通道 int val = pPixel[x * channels + c] + brightness; pPixel[x * channels + c] = (BYTE)max(0, min(255, val)); } } // 应用饱和度 if (saturation != 1.0f) { BYTE& b = pPixel[x * channels]; BYTE& g = pPixel[x * channels + 1]; BYTE& r = pPixel[x * channels + 2]; float gray = 0.299f * r + 0.587f * g + 0.114f * b; r = (BYTE)(gray + saturation * (r - gray)); g = (BYTE)(gray + saturation * (g - gray)); b = (BYTE)(gray + saturation * (b - gray)); } // 应用滤镜效果 if (isBlackWhite) { BYTE& b = pPixel[x * channels]; BYTE& g = pPixel[x * channels + 1]; BYTE& r = pPixel[x * channels + 2]; BYTE gray = (BYTE)(0.299f * r + 0.587f * g + 0.114f * b); r = g = b = gray; } else if (isSepia) { BYTE& b = pPixel[x * channels]; BYTE& g = pPixel[x * channels + 1]; BYTE& r = pPixel[x * channels + 2]; int newR = min(255, (int)(0.393f * r + 0.769f * g + 0.189f * b)); int newG = min(255, (int)(0.349f * r + 0.686f * g + 0.168f * b)); int newB = min(255, (int)(0.272f * r + 0.534f * g + 0.131f * b)); r = (BYTE)newR; g = (BYTE)newG; b = (BYTE)newB; } } } } // 添加自由选区效果应用函数 void CMyPS2Dlg::ApplyFreeFormSelectionEffect(BYTE* pBits, int width, int height, int pitch, int channels, const std::vector<CPoint>& points, int brightness, float saturation, int mosaic, bool isBlackWhite, bool isSepia) { if (points.size() < 3) return; // 创建区域(修正后的版本) CRgn rgn; if (!rgn.CreatePolygonRgn((LPPOINT)points.data(), (int)points.size(), WINDING)) { TRACE(_T("Failed to create polygon region\n")); return; } // 创建临时缓冲区用于选区效果 std::vector<BYTE> tempBuffer(width * height * channels); int tempPitch = width * channels; // 1. 提取选区像素(只处理选区内的像素) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (rgn.PtInRegion(x, y)) { BYTE* pSrc = pBits + y * pitch + x * channels; BYTE* pDst = tempBuffer.data() + y * tempPitch + x * channels; memcpy(pDst, pSrc, channels); } } } // 2. 在临时缓冲区上应用效果 if (mosaic > 1) ApplyMosaicToPixels(tempBuffer.data(), width, height, tempPitch, channels, mosaic); if (brightness != 0) ApplyBrightnessToPixels(tempBuffer.data(), width, height, tempPitch, channels, brightness); if (saturation != 1.0f) ApplySaturationToPixels(tempBuffer.data(), width, height, tempPitch, channels, saturation); if (isBlackWhite) ApplyBlackWhiteToPixels(tempBuffer.data(), width, height, tempPitch, channels); else if (isSepia) ApplySepiaToPixels(tempBuffer.data(), width, height, tempPitch, channels); // 3. 将处理后的像素写回原图(只写入选区部分) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (rgn.PtInRegion(x, y)) { BYTE* pDst = pBits + y * pitch + x * channels; BYTE* pSrc = tempBuffer.data() + y * tempPitch + x * channels; memcpy(pDst, pSrc, channels); } } } } // 修改参数变化处理函数 void CMyPS2Dlg::OnParameterChanged() { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; Selection* pActiveSel = GetActiveSelection(); if (pActiveSel) { // 只调整活动选区参数 pActiveSel->brightness = litslider.GetPos() - 100; pActiveSel->saturation = (saturslider.GetPos()) / 100.0f; pActiveSel->mosaic = mskslider.GetPos(); pActiveSel->isBlackWhite = m_isBlackWhite; pActiveSel->isSepia = m_isSepia; } else { // 没有活动选区时调整全局参数 m_nlightValue = litslider.GetPos() - 100; m_nsatureValue = (saturslider.GetPos()) / 100.0f; m_nmskValue = mskslider.GetPos(); } // 标记状态已改变 m_bStateDirty = true; // 重置保存定时器 if (m_nSaveTimer) { KillTimer(m_nSaveTimer); } m_nSaveTimer = SetTimer(SAVE_DELAY_TIMER, SAVE_DELAY, NULL); // 立即更新显示 if (m_layer1.isSelected) { UpdateDisplayImage(m_layer1); } else { UpdateDisplayImage(m_layer2); } } // 修改退出选区函数 void CMyPS2Dlg::OnBnClickedButton8() { m_pCurrentSelection = nullptr; // 清除当前选区指针 UpdateSelectionControls(); // 切换回显示全局参数 Invalidate(); } // 添加创建新选区函数 void CMyPS2Dlg::CreateNewSelection(const CRect& rect) { Selection newSelection; newSelection.rect = rect; newSelection.brightness = m_nlightValue; newSelection.saturation = m_nsatureValue; newSelection.mosaic = m_nmskValue; newSelection.isBlackWhite = m_isBlackWhite; newSelection.isSepia = m_isSepia; m_selections.push_back(newSelection); m_pCurrentSelection = &m_selections.back(); m_bStateDirty = true; UpdateSelectionControls(); // 更新控件显示选区参数 UpdateDisplayImage(m_layer1.isSelected ? m_layer1 : m_layer2); } // 添加创建自由选区函数 void CMyPS2Dlg::CreateNewFreeFormSelection(const std::vector<CPoint>& points) { Selection newSelection; newSelection.freeFormPoints = points; newSelection.brightness = m_nlightValue; newSelection.saturation = m_nsatureValue; newSelection.mosaic = m_nmskValue; newSelection.isBlackWhite = m_isBlackWhite; newSelection.isSepia = m_isSepia; m_selections.push_back(newSelection); m_pCurrentSelection = &m_selections.back(); m_bStateDirty = true; UpdateSelectionControls(); // 更新控件显示选区参数 UpdateDisplayImage(m_layer1.isSelected ? m_layer1 : m_layer2); } Selection* CMyPS2Dlg::GetActiveSelection() { for (auto& sel : m_selections) { if (sel.isActive) return &sel; } return nullptr; } // 实现更新选区控件状态的函数 void CMyPS2Dlg::UpdateSelectionControls() { Selection* pSel = GetActiveSelection(); CString strValue; if (!pSel) { // 没有活动选区时显示全局参数 // 亮度控件 litslider.SetPos(m_nlightValue + 100); strValue.Format(_T("%d"), m_nlightValue); SetDlgItemText(IDC_EDITLIT, strValue); // 饱和度控件 saturslider.SetPos((int)(m_nsatureValue * 100)); strValue.Format(_T("%.1f"), m_nsatureValue); SetDlgItemText(IDC_EDITSATUR, strValue); // 马赛克控件 mskslider.SetPos(m_nmskValue); strValue.Format(_T("%d"), m_nmskValue); SetDlgItemText(IDC_EDITMAK, strValue); // 滤镜按钮 ((CButton*)GetDlgItem(IDC_BUTTON6))->SetCheck(m_isBlackWhite); ((CButton*)GetDlgItem(IDC_BUTTON7))->SetCheck(m_isSepia); } else { // 有活动选区时显示选区参数 // 亮度控件 litslider.SetPos(pSel->brightness + 100); strValue.Format(_T("%d"), pSel->brightness); SetDlgItemText(IDC_EDITLIT, strValue); // 饱和度控件 saturslider.SetPos((int)(pSel->saturation * 100)); strValue.Format(_T("%.1f"), pSel->saturation); SetDlgItemText(IDC_EDITSATUR, strValue); // 马赛克控件 mskslider.SetPos(pSel->mosaic); strValue.Format(_T("%d"), pSel->mosaic); SetDlgItemText(IDC_EDITMAK, strValue); // 滤镜按钮 ((CButton*)GetDlgItem(IDC_BUTTON6))->SetCheck(pSel->isBlackWhite); ((CButton*)GetDlgItem(IDC_BUTTON7))->SetCheck(pSel->isSepia); } }为什么全局调整亮度等编辑框与滑条互相影响,子区域调整没变化
06-29
void CMyPS2Dlg::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(); } m_memDC.FillSolidRect(clientRect, GetSysColor(COLOR_BTNFACE)); Gdiplus::Graphics graphics(m_memDC); // 2. 绘制底层(未选中的图层) ImageLayer* bottomLayer = m_layer1.isSelected ? &m_layer2 : &m_layer1; if (bottomLayer->isVisible && !bottomLayer->displayImage.IsNull()) { DrawLayer(graphics, bottomLayer->displayImage, bottomLayer->alpha); } // 3. 绘制上层(选中的图层) ImageLayer* topLayer = m_layer1.isSelected ? &m_layer1 : &m_layer2; if (topLayer->isVisible && !topLayer->displayImage.IsNull()) { DrawLayer(graphics, topLayer->displayImage, topLayer->alpha); // 4. 在选中的图层上绘制选区 if (!m_rcSelection.IsRectNull() || !m_freeFormPoints.empty()) { DrawSelection(m_memDC, topLayer); } } if (m_bDoodleMode && !m_doodleTempImage.IsNull()) { CRect imgRect = GetImageClientRect(topLayer->image); std::unique_ptr<Gdiplus::Bitmap> gdiBitmap(CImageToGdiPlusBitmap(m_doodleTempImage)); if (gdiBitmap) { graphics.DrawImage( gdiBitmap.get(), Gdiplus::Rect(imgRect.left, imgRect.top, imgRect.Width(), imgRect.Height()), 0, 0, m_doodleTempImage.GetWidth(), m_doodleTempImage.GetHeight(), Gdiplus::UnitPixel ); } } // 绘制克隆区域 ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (activeLayer.clonedRegion.active && activeLayer.clonedRegion.visible) { CRect srcRect(0, 0, activeLayer.clonedRegion.image.GetWidth(), activeLayer.clonedRegion.image.GetHeight()); CRect screenRect = ConvertToScreenRect(activeLayer.clonedRegion.destRect); std::unique_ptr<Gdiplus::Bitmap> gdiBitmap( CImageToGdiPlusBitmap(activeLayer.clonedRegion.image)); if (gdiBitmap) { graphics.DrawImage(gdiBitmap.get(), Gdiplus::Rect(screenRect.left, screenRect.top, screenRect.Width(), screenRect.Height()), 0, 0, srcRect.Width(), srcRect.Height(), Gdiplus::UnitPixel); } } // 5. 输出到屏幕 dc.BitBlt(0, 0, clientRect.Width(), clientRect.Height(), &m_memDC, 0, 0, SRCCOPY); } void CMyPS2Dlg::DrawLayer(Gdiplus::Graphics& graphics, CImage& image, float alpha) { CRect imgRect = GetImageClientRect(image); std::unique_ptrGdiplus::Bitmap gdiBitmap(CImageToGdiPlusBitmap(image)); if (gdiBitmap) { Gdiplus::ColorMatrix matrix = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, alpha, 0, 0, 0, 0, 0, 1 }; Gdiplus::ImageAttributes attr; attr.SetColorMatrix(&matrix); graphics.DrawImage( gdiBitmap.get(), Gdiplus::Rect(imgRect.left, imgRect.top, imgRect.Width(), imgRect.Height()), 0, 0, image.GetWidth(), image.GetHeight(), Gdiplus::UnitPixel, &attr ); } } void CMyPS2Dlg::DrawSelection(CDC& dc, ImageLayer* layer) { 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 && !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; } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CMyPS2Dlg::OnQueryDragIcon() { return static_cast(m_hIcon); } //打开 void CMyPS2Dlg::OnBnClickedButton2() { if (!m_layer1.image.IsNull()) { UINT i; i = MessageBox(_T(“确认重新打开图片吗?”), _T(“提示”), MB_YESNO | MB_ICONQUESTION); if (i != IDYES) { return; } } CFileDialog dlg(TRUE, _T(“.bmp;.jpg;.png"), NULL, OFN_FILEMUSTEXIST, _T("Image Files|.bmp;.jpg;.png||”), this); if (dlg.DoModal() == IDOK) { // 释放旧资源 m_layer1.history.clear(); m_layer1.currentHistoryIndex = -1; m_layer1.isSelected = true; if (!m_layer1.image.IsNull()) { m_layer1.image.Destroy(); } if (!m_layer1.displayImage.IsNull()) { m_layer1.displayImage.Destroy(); } if (m_layer1.image.Load(dlg.GetPathName()) != S_OK) { AfxMessageBox(L"图像加载失败!\n请检查格式支持(BMP/JPG/PNG)"); return; } else { // 加载新图像 ImageState initialState; initialState.brightness = 0; initialState.saturation = 1.0f; initialState.mosaic = 1; initialState.alpha = 1.0f; if (!m_layer1.image.IsNull()) { if (!initialState.image.IsNull()) { initialState.image.Destroy(); } initialState.image.Create(m_layer1.image.GetWidth(), m_layer1.image.GetHeight(), m_layer1.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(m_layer1.image.GetDC()); CDC* pDstDC = CDC::FromHandle(initialState.image.GetDC()); pDstDC->BitBlt(0, 0, m_layer1.image.GetWidth(), m_layer1.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); m_layer1.image.ReleaseDC(); initialState.image.ReleaseDC(); } m_layer1.history.push_back(initialState); m_layer1.history.push_back(initialState); m_layer1.currentHistoryIndex = 1; // 重置所有参数 m_nlightValue = 0; m_nsatureValue = 1.0f; m_nmskValue = 1; m_layer1.alpha = 1.0f; // 重置底层透明度为不透明 m_layer2.alpha = 0.5f; // 重置顶层透明度为半透明 // 初始化UI控件 litslider.SetPos(100); // 假设滑块范围0-200,100对应亮度0 saturslider.SetPos(100); // 假设滑块范围0-200,100对应饱和度1.0 mskslider.SetPos(1); // 马赛克块大小(1~20) m_sliderLay1.SetPos(100); // 新增:底层透明度滑块(0-100) m_sliderLay2.SetPos(50); // 新增:顶层透明度滑块(0-100) // 生成显示图像 UpdateDisplayImage(m_layer1); } } } //保存 void CMyPS2Dlg::OnBnClickedButton3() { CFileDialog dlg(FALSE, _T(“.jpg"), _T(“output.jpg”), OFN_OVERWRITEPROMPT, _T("JPEG Files|.jpg||”), this); if (dlg.DoModal() == IDOK) { if (m_layer1.isSelected) { if (m_layer1.displayImage.IsNull()) { // 改为检查 m_displayImage AfxMessageBox(_T("请先打开图片!")); return; } if (m_layer1.currentHistoryIndex < 0) { AfxMessageBox(_T("图片失效")); return; } else { m_layer1.displayImage.Save(dlg.GetPathName()); } } // 保存处理后的图像 else { if (m_layer2.displayImage.IsNull()) { // 改为检查 m_displayImage AfxMessageBox(_T("请先打开图片!")); return; } if (m_layer2.currentHistoryIndex < 0) { AfxMessageBox(_T("图片失效")); return; } else { m_layer2.displayImage.Save(dlg.GetPathName()); } } } // TODO: 在此添加控件通知处理程序代码 } //退出 void CMyPS2Dlg::OnBnClickedButton4() { UINT i; i = MessageBox(_T(“请确认图片保存再退出。”), _T(“提示”), MB_YESNO | MB_ICONQUESTION); if (i == IDYES) { exit(0); } // TODO: 在此添加控件通知处理程序代码 } //亮度滑块 void CMyPS2Dlg::OnNMCustomdrawSliderlit(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); *pResult = 0; } //亮度编辑框 void CMyPS2Dlg::OnEnChangeEditlit() { if (m_bStateDirty) return; CString strValue; litedit.GetWindowText(strValue); int brightness = _ttoi(strValue)+100; // 检查输入值是否在有效范围内 (-100 到 100) brightness = max(0, min(200, brightness)); litslider.SetPos(brightness); // 应用亮度调整 OnParameterChanged(); // TODO: 如果该控件是 RICHEDIT 控件,它将不 // 发送此通知,除非重写 CDialogEx::OnInitDialog() // 函数并调用 CRichEditCtrl().SetEventMask(), // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // TODO: 在此添加控件通知处理程序代码 } void CMyPS2Dlg::ApplyBrightnessToPixels(BYTE* pBits, int width, int height, int pitch, int channels, int brightness) { for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { for (int c = 0; c < 3; c++) // 处理BGR通道 { int newVal = pRow[x * channels + c] + brightness; pRow[x * channels + c] = (BYTE)min(255, max(0, newVal)); } } } } void CMyPS2Dlg::ApplySaturationToPixels(BYTE* pBits, int width, int height, int pitch, int channels, float saturation) { // 确保饱和度范围合理(0.0~3.0) saturation = min(max(saturation, 0.0f), 2.0f); for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { BYTE& b = pRow[x * channels]; BYTE& g = pRow[x * channels + 1]; BYTE& r = pRow[x * channels + 2]; // 计算灰度值(更精确的权重) float gray = 0.299f * r + 0.587f * g + 0.114f * b; // 调整饱和度(非线性插值,避免过度失真) float blendFactor = saturation ; r = (BYTE)min(255.0f, max(0.0f, gray + blendFactor * (r - gray))); g = (BYTE)min(255.0f, max(0.0f, gray + blendFactor * (g - gray))); b = (BYTE)min(255.0f, max(0.0f, gray + blendFactor * (b - gray))); } } } void CMyPS2Dlg::ApplyMosaicToPixels(BYTE* pBits, int width, int height, int pitch, int channels, int blockSize) { for (int y = 0; y < height; y += blockSize) { for (int x = 0; x < width; x += blockSize) { // 计算当前块的边界 int blockEndX = min(x + blockSize, width); int blockEndY = min(y + blockSize, height); // 计算块内像素的平均颜色 long sumR = 0, sumG = 0, sumB = 0; int count = 0; for (int by = y; by < blockEndY; by++) { BYTE* pRow = pBits + by * pitch; for (int bx = x; bx < blockEndX; bx++) { sumB += pRow[bx * channels]; sumG += pRow[bx * channels + 1]; sumR += pRow[bx * channels + 2]; count++; } } BYTE avgR = (BYTE)(sumR / count); BYTE avgG = (BYTE)(sumG / count); BYTE avgB = (BYTE)(sumB / count); // 将整个块设为平均颜色 for (int by = y; by < blockEndY; by++) { BYTE* pRow = pBits + by * pitch; for (int bx = x; bx < blockEndX; bx++) { pRow[bx * channels] = avgB; pRow[bx * channels + 1] = avgG; pRow[bx * channels + 2] = avgR; } } } } } void CMyPS2Dlg::OnNMCustomdrawSlidersatur(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; } //饱和度编辑框 void CMyPS2Dlg::OnEnChangeEditsatur() { if (m_bStateDirty) return; CString strValue; saturedit.GetWindowText(strValue); float sat = _ttof(strValue)*100.0f+100.0f; // 限制范围并同步滑块 sat = max(0.0f, min(200.0f, sat)); saturslider.SetPos((int)sat); // 统一计算为浮点数(0.0~2.0) m_nsatureValue = sat / 100.0f; // 新增此行 OnParameterChanged(); // TODO: 如果该控件是 RICHEDIT 控件,它将不 // 发送此通知,除非重写 CDialogEx::OnInitDialog() // 函数并调用 CRichEditCtrl().SetEventMask(), // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // TODO: 在此添加控件通知处理程序代码 } void CMyPS2Dlg::OnNMCustomdrawSlidermsk(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; } //马赛克编辑框 void CMyPS2Dlg::OnEnChangeEditmsk() { if (m_bStateDirty) return; CString strValue; mskedit.GetWindowText(strValue); int size = _ttoi(strValue); // 限制范围并同步滑块 size = max(1, min(50, size)); mskslider.SetPos(size); // 应用马赛克效果 OnParameterChanged(); // TODO: 如果该控件是 RICHEDIT 控件,它将不 // 发送此通知,除非重写 CDialogEx::OnInitDialog() // 函数并调用 CRichEditCtrl().SetEventMask(), // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // TODO: 在此添加控件通知处理程序代码 } //滑块 void CMyPS2Dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (m_bStateDirty)return; int id = pScrollBar->GetDlgCtrlID(); if (id == IDC_SLIDERLIT) { // 亮度滑块 m_nlightValue = litslider.GetPos() - 100; // 范围:-100 ~ +100 CString strValue; strValue.Format(_T("%d"), m_nlightValue); litedit.SetWindowText(strValue); } else if (id == IDC_SLIDERSATUR) { // 饱和度滑块 m_nsatureValue = (saturslider.GetPos()) / 100.0f; // 范围:0.0 ~ 2.0 float sat = m_nsatureValue - 1.0f; CString strValue; strValue.Format(_T("%.1f"), sat); saturedit.SetWindowText(strValue); //UpdateDisplayImage(); } else if (id == IDC_SLIDERMSK) { // 马赛克滑块 m_nmskValue = mskslider.GetPos(); // 范围:1 ~ 50 CString strValue; strValue.Format(_T("%d"), m_nmskValue); mskedit.SetWindowText(strValue); //UpdateDisplayImage(); } else if (id == IDC_SLIDERL1) { m_layer1.alpha = (float)m_sliderLay1.GetPos() / 100.0f; // 范围:0.0 ~ 1.0 InvalidateRect(GetImageClientRect(m_layer1.image)); // 重绘图片区域 } // 顶层图片透明度滑块 else if (id == IDC_SLIDERL2) { m_layer2.alpha = (float)m_sliderLay2.GetPos() / 100.0f; // 范围:0.0 ~ 1.0 InvalidateRect(m_overlayRect); // 只重绘叠加层区域 } OnParameterChanged(); CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar); } void CMyPS2Dlg::OnBnClickedButtonReset() { //if (!m_originalImage.IsNull()) { // m_nlightValue = 0; // m_nsatureValue = 0.0f; // m_nmskValue = 1; // litslider.SetPos(50); // saturslider.SetPos(50); // litslider.SetPos(1); // litedit.SetWindowText(_T("0")); // saturedit.SetWindowText(_T("0.0")); // mskedit.SetWindowText(_T("1")); // UpdateDisplayImage(); //} } //更新1 void CMyPS2Dlg::UpdateDisplayImage(ImageLayer& layer) { if (layer.currentHistoryIndex < 0) return; if(layer.image.IsNull()) return; if (layer.displayImage.IsNull() || layer.displayImage.GetWidth() != layer.image.GetWidth() || layer.displayImage.GetHeight() != layer.image.GetHeight()) { layer.displayImage.Destroy(); layer.displayImage.Create(layer.image.GetWidth(), layer.image.GetHeight(), layer.image.GetBPP()); } // 复制原图到显示图像 CDC* pSrcDC = CDC::FromHandle(layer.image.GetDC()); CDC* pDstDC = CDC::FromHandle(layer.displayImage.GetDC()); pDstDC->BitBlt(0, 0, layer.image.GetWidth(), layer.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); layer.image.ReleaseDC(); layer.displayImage.ReleaseDC(); // 应用当前参数效果 BYTE* pBits = (BYTE*)layer.displayImage.GetBits(); if (!pBits) return; int pitch = layer.displayImage.GetPitch(); int width = layer.displayImage.GetWidth(); int height = layer.displayImage.GetHeight(); int bpp = layer.displayImage.GetBPP(); int channels = (bpp == 32) ? 4 : 3; if (select) { if (layer.history[layer.currentHistoryIndex].mosaic > 1) { ApplyMosaicToPixels(pBits, width, height, pitch, channels, layer.history[layer.currentHistoryIndex].mosaic); } if (layer.history[layer.currentHistoryIndex].brightness != 0) { ApplyBrightnessToPixels(pBits, width, height, pitch, channels, layer.history[layer.currentHistoryIndex].brightness); } if (layer.history[layer.currentHistoryIndex].saturation != 1.0f) { ApplySaturationToPixels(pBits, width, height, pitch, channels, layer.history[layer.currentHistoryIndex].saturation); } ApplyAdvancedEffectsToSelection(m_nlightValue, m_nsatureValue, m_isBlackWhite, m_isSepia, m_nmskValue, layer); } else { // 全局应用效果 if (m_nmskValue > 1) { ApplyMosaicToPixels(pBits, width, height, pitch, channels, m_nmskValue); } if (m_nlightValue != 0) { ApplyBrightnessToPixels(pBits, width, height, pitch, channels, m_nlightValue); } if (m_nsatureValue != 1.0f) { ApplySaturationToPixels(pBits, width, height, pitch, channels, m_nsatureValue); } if (m_isBlackWhite) ApplyBlackWhiteToPixels(pBits, width, height, pitch, channels); else if (m_isSepia) ApplySepiaToPixels(pBits, width, height, pitch, channels); } InvalidateRect(NULL, FALSE); UpdateWindow(); } //黑白 void CMyPS2Dlg::ApplyBlackWhiteToPixels(BYTE* pBits, int width, int height, int pitch, int channels) { for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { BYTE& b = pRow[x * channels]; BYTE& g = pRow[x * channels + 1]; BYTE& r = pRow[x * channels + 2]; // 计算灰度值(加权平均) BYTE gray = (BYTE)(0.299f * r + 0.587f * g + 0.114f * b); // 所有通道设为灰度值 b = g = r = gray; } } } //怀旧 void CMyPS2Dlg::ApplySepiaToPixels(BYTE* pBits, int width, int height, int pitch, int channels) { for (int y = 0; y < height; y++) { BYTE* pRow = pBits + y * pitch; for (int x = 0; x < width; x++) { BYTE& b = pRow[x * channels]; BYTE& g = pRow[x * channels + 1]; BYTE& r = pRow[x * channels + 2]; // 怀旧色计算公式 int newR = min(255, (int)(0.393f * r + 0.769f * g + 0.189f * b)); int newG = min(255, (int)(0.349f * r + 0.686f * g + 0.168f * b)); int newB = min(255, (int)(0.272f * r + 0.534f * g + 0.131f * b)); r = (BYTE)newR; g = (BYTE)newG; b = (BYTE)newB; } } } //定时器 void CMyPS2Dlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == SAVE_DELAY_TIMER) { KillTimer(m_nSaveTimer); m_nSaveTimer = 0; if (m_bStateDirty) { if (m_layer1.isSelected) { SaveCurrentState(m_layer1); } else { SaveCurrentState(m_layer2); } m_bStateDirty = false; } } CDialogEx::OnTimer(nIDEvent); } //参数变化 void CMyPS2Dlg::OnParameterChanged() { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; // 更新当前参数值 m_nlightValue = litslider.GetPos() - 100; m_nsatureValue = (saturslider.GetPos()) / 100.0f; m_nmskValue = mskslider.GetPos(); // 标记状态已改变 m_bStateDirty = true; // 重置保存定时器 if (m_nSaveTimer) { KillTimer(m_nSaveTimer); } m_nSaveTimer = SetTimer(SAVE_DELAY_TIMER, SAVE_DELAY, NULL); // 立即更新显示 if (m_layer1.isSelected) { UpdateDisplayImage(m_layer1); } else { UpdateDisplayImage(m_layer2); } } //新增状态 void CMyPS2Dlg::SaveCurrentState(ImageLayer& layer) { if (!m_bStateDirty) return; if (layer.currentHistoryIndex < 0) return; if (select) { return; } ImageState currentState; if (layer.modified) { currentState.brightness = layer.history[layer.currentHistoryIndex].brightness; currentState.saturation = layer.history[layer.currentHistoryIndex].saturation; currentState.mosaic = layer.history[layer.currentHistoryIndex].mosaic; currentState.alpha = layer.history[layer.currentHistoryIndex].alpha; } else { currentState.brightness = m_nlightValue; currentState.saturation = m_nsatureValue; currentState.mosaic = m_nmskValue; currentState.alpha = layer.alpha; } if (!layer.image.IsNull()) { if (!currentState.image.IsNull()) { currentState.image.Destroy(); } currentState.image.Create(layer.image.GetWidth(), layer.image.GetHeight(), layer.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(layer.image.GetDC()); CDC* pDstDC = CDC::FromHandle(currentState.image.GetDC()); pDstDC->BitBlt(0, 0, layer.image.GetWidth(), layer.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); layer.image.ReleaseDC(); currentState.image.ReleaseDC(); } if (layer.currentHistoryIndex >= 0 && layer.history[layer.currentHistoryIndex] == currentState&&(!layer.modified)&&(!doodend)) { return; } for (int i = layer.currentHistoryIndex +1; i <= layer.history.size() - 1; i++) { layer.history.erase(layer.history.begin() + i); } layer.history.push_back(currentState); layer.currentHistoryIndex +=1; layer.modified = false; doodend = false; // 限制历史记录数量 const int MAX_HISTORY = 40; if (layer.history.size() > MAX_HISTORY) { layer.history.erase(layer.history.begin()); layer.currentHistoryIndex--; } m_bStateDirty = false; } //销毁 void CMyPS2Dlg::OnDestroy() { CDialogEx::OnDestroy(); // 调用基类实现 // 清理定时器资源 if (m_nSaveTimer != 0) { KillTimer(m_nSaveTimer); m_nSaveTimer = 0; } // 清理图像资源 if (!m_layer1.image.IsNull()) m_layer1.image.Destroy(); if (!m_layer2.image.IsNull()) m_layer2.image.Destroy(); if (!m_layer1.displayImage.IsNull()) m_layer1.displayImage.Destroy(); if (!m_layer2.displayImage.IsNull()) m_layer2.displayImage.Destroy(); if (!m_doodleTempImage.IsNull()) m_doodleTempImage.Destroy(); //GdiplusShutdown(m_gdiplusToken); CDialogEx::OnDestroy(); } //撤回 void CMyPS2Dlg::OnUndo() { if (m_layer1.isSelected) { if (m_layer1.currentHistoryIndex > 0) { m_layer1.currentHistoryIndex–; ApplyHistoryState(m_layer1.currentHistoryIndex,m_layer1); } else { AfxMessageBox(_T(“不可撤回。”)); } } else { if (m_layer2.currentHistoryIndex > 0) { m_layer2.currentHistoryIndex–; ApplyHistoryState(m_layer2.currentHistoryIndex, m_layer2); } else { AfxMessageBox(_T(“不可撤回。”)); } } } //重做 void CMyPS2Dlg::OnRedo() { if (m_layer1.isSelected) { if (m_layer1.currentHistoryIndex < static_cast(m_layer1.history.size()) - 1) { m_layer1.currentHistoryIndex++; ApplyHistoryState(m_layer1.currentHistoryIndex,m_layer1); } else { AfxMessageBox(_T(“不可重做。”)); } } else { if (m_layer2.currentHistoryIndex < static_cast(m_layer2.history.size()) - 1) { m_layer2.currentHistoryIndex++; ApplyHistoryState(m_layer2.currentHistoryIndex, m_layer2); } else { AfxMessageBox(_T(“不可重做。”)); } } } void CMyPS2Dlg::ApplyHistoryState(int index, ImageLayer& layer) { if (index < 0 || index >= static_cast(layer.history.size())) return; const ImageState& state = layer.history[index]; if (!state.image.IsNull()) { if (!layer.image.IsNull()) { layer.image.Destroy(); } layer.image.Create(state.image.GetWidth(), state.image.GetHeight(), state.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(state.image.GetDC()); CDC* pDstDC = CDC::FromHandle(layer.image.GetDC()); pDstDC->BitBlt(0, 0, state.image.GetWidth(), state.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); state.image.ReleaseDC(); layer.image.ReleaseDC(); } // 更新UI控件 litslider.SetPos(state.brightness + 100); saturslider.SetPos((int)(state.saturation * 100.0f)); mskslider.SetPos(state.mosaic); if (m_layer1.isSelected) { m_sliderLay1.SetPos((int)(state.alpha * 100.0f)); } else { m_sliderLay2.SetPos((int)(state.alpha * 100.0f)); } CString str; str.Format(_T("%d"), state.brightness); litedit.SetWindowText(str); str.Format(_T("%.1f"), state.saturation); saturedit.SetWindowText(str); str.Format(_T("%d"), state.mosaic); mskedit.SetWindowText(str); // 更新当前参数 m_nlightValue = state.brightness; m_nsatureValue = state.saturation; m_nmskValue = state.mosaic; layer.alpha = state.alpha; // 更新显示 UpdateDisplayImage(layer); } //撤回 void CMyPS2Dlg::OnBnClickedButton1() { OnUndo(); // TODO: 在此添加控件通知处理程序代码 } void CMyPS2Dlg::OnBnClickedButton5() { OnRedo(); // TODO: 在此添加控件通知处理程序代码 } //黑白滤镜 void CMyPS2Dlg::OnBnClickedButton6() { m_isBlackWhite = !m_isBlackWhite; m_isSepia = false; // 确保二者互斥 if (m_layer1.isSelected) { UpdateDisplayImage(m_layer1); } else { UpdateDisplayImage(m_layer2); } // TODO: 在此添加控件通知处理程序代码 } //怀旧滤镜 void CMyPS2Dlg::OnBnClickedButton7() { m_isSepia = !m_isSepia; m_isBlackWhite = false; // 确保二者互斥 if (m_layer1.isSelected) { UpdateDisplayImage(m_layer1); } else { UpdateDisplayImage(m_layer2); } // TODO: 在此添加控件通知处理程序代码 } //退出选区 void CMyPS2Dlg::OnBnClickedButton8() { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (activeLayer.clonedRegion.active && activeLayer.clonedRegion.visible) { // 将克隆区域绘制到目标位置 CDC* pDstDC = CDC::FromHandle(activeLayer.image.GetDC()); CDC* pSrcDC = CDC::FromHandle(activeLayer.clonedRegion.image.GetDC()); pDstDC->BitBlt( activeLayer.clonedRegion.destRect.left, activeLayer.clonedRegion.destRect.top, activeLayer.clonedRegion.destRect.Width(), activeLayer.clonedRegion.destRect.Height(), pSrcDC, 0, 0, SRCCOPY); activeLayer.image.ReleaseDC(); activeLayer.clonedRegion.image.ReleaseDC(); } // 重置克隆区域 activeLayer.clonedRegion.active = false; activeLayer.clonedRegion.visible = false; select = false; if (m_layer1.isSelected) { if (HasSelection() && !m_layer1.displayImage.IsNull() && !m_layer1.image.IsNull()) { CRect validRect; if (m_selectionMode == SELECT_RECTANGLE) { validRect = m_rcSelection; validRect.IntersectRect(validRect, CRect(0, 0, m_layer1.image.GetWidth(), m_layer1.image.GetHeight())); } else if (m_selectionMode == SELECT_FREE_FORM && m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.GetRgnBox(&validRect); validRect.IntersectRect(validRect, CRect(0, 0, m_layer1.image.GetWidth(), m_layer1.image.GetHeight())); } if (!validRect.IsRectEmpty()) { // 只复制选区内的修改回原图 CDC* pDispDC = CDC::FromHandle(m_layer1.displayImage.GetDC()); CDC* pOrigDC = CDC::FromHandle(m_layer1.image.GetDC()); // 设置裁剪区域(只复制选区内的像素) if (m_selectionMode == SELECT_FREE_FORM) { pOrigDC->SelectClipRgn(&m_rgnFreeForm); } pOrigDC->BitBlt( validRect.left, validRect.top, validRect.Width(), validRect.Height(), pDispDC, validRect.left, validRect.top, SRCCOPY); // 重置裁剪区域 pOrigDC->SelectClipRgn(NULL); m_layer1.image.ReleaseDC(); m_layer1.displayImage.ReleaseDC(); // 保存当前状态到历史记录 m_layer1.modified = true; m_bStateDirty = true; m_nlightValue = m_layer1.history[m_layer1.currentHistoryIndex].brightness; m_nsatureValue = m_layer1.history[m_layer1.currentHistoryIndex].saturation; m_nmskValue = m_layer1.history[m_layer1.currentHistoryIndex].mosaic; SaveCurrentState(m_layer1); } } } else { if (HasSelection() && !m_layer2.displayImage.IsNull() && !m_layer2.image.IsNull()) { CRect validRect; if (m_selectionMode == SELECT_RECTANGLE) { validRect = m_rcSelection; validRect.IntersectRect(validRect, CRect(0, 0, m_layer2.image.GetWidth(), m_layer2.image.GetHeight())); } else if (m_selectionMode == SELECT_FREE_FORM && m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.GetRgnBox(&validRect); validRect.IntersectRect(validRect, CRect(0, 0, m_layer2.image.GetWidth(), m_layer2.image.GetHeight())); } if (!validRect.IsRectEmpty()) { // 只复制选区内的修改回原图 CDC* pDispDC = CDC::FromHandle(m_layer2.displayImage.GetDC()); CDC* pOrigDC = CDC::FromHandle(m_layer2.image.GetDC()); // 设置裁剪区域(只复制选区内的像素) if (m_selectionMode == SELECT_FREE_FORM) { pOrigDC->SelectClipRgn(&m_rgnFreeForm); } pOrigDC->BitBlt( validRect.left, validRect.top, validRect.Width(), validRect.Height(), pDispDC, validRect.left, validRect.top, SRCCOPY); // 重置裁剪区域 pOrigDC->SelectClipRgn(NULL); m_layer2.image.ReleaseDC(); m_layer2.displayImage.ReleaseDC(); // 保存当前状态到历史记录 m_layer2.modified = true; m_bStateDirty = true; m_nlightValue = m_layer2.history[m_layer2.currentHistoryIndex].brightness; m_nsatureValue = m_layer2.history[m_layer2.currentHistoryIndex].saturation; m_nmskValue = m_layer2.history[m_layer2.currentHistoryIndex].mosaic; SaveCurrentState(m_layer2); } } } /*m_bStateDirty = true;*/ litslider.SetPos(m_nlightValue); saturslider.SetPos((int)(m_nsatureValue * 100.0f)); mskslider.SetPos(m_nmskValue); CString strValue; strValue.Format(_T("%d"), m_nlightValue); litedit.SetWindowText(strValue); strValue.Format(_T("%.1f"), m_nsatureValue); saturedit.SetWindowText(strValue); strValue.Format(_T("%d"), m_nmskValue); mskedit.SetWindowText(strValue); m_rcSelection.SetRectEmpty(); m_ptStart = m_ptEnd = CPoint(0, 0); if (m_rgnFreeForm.GetSafeHandle()) { // 2. 清除不规则选区数据 m_rgnFreeForm.DeleteObject(); // 删除区域对象 } m_freeFormPoints.clear(); // 清空点集 m_bSelecting = false;//// 3. 重置状态标志 m_bFreeFormSelecting = false; InvalidateRect(NULL, FALSE); UpdateWindow(); // TODO: 在此添加控件通知处理程序代码 } CRect CMyPS2Dlg::GetImageClientRect(const CImage& image) { CRect ctrlRect; m_staticImage.GetWindowRect(&ctrlRect); ScreenToClient(&ctrlRect); int imgW = image.GetWidth(); int imgH = image.GetHeight(); double ratio = min( (double)ctrlRect.Width() / imgW, (double)ctrlRect.Height() / imgH ); int drawW = (int)(imgW * ratio); int drawH = (int)(imgH * ratio); int x = ctrlRect.left + (ctrlRect.Width() - drawW) / 2; int y = ctrlRect.top + (ctrlRect.Height() - drawH) / 2; return CRect(x, y, x + drawW, y + drawH); } std::unique_ptrGdiplus::Bitmap CMyPS2Dlg::CImageToGdiPlusBitmap(CImage& image) { if (image.IsNull()) return nullptr; // 直接通过HBITMAP转换 return std::unique_ptr<Gdiplus::Bitmap>( Gdiplus::Bitmap::FromHBITMAP( (HBITMAP)image, // CImage可隐式转换为HBITMAP NULL // 不使用调色板 ) ); } void CMyPS2Dlg::OnLButtonDown(UINT nFlags, CPoint point) { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; // 检查是否点击在克隆区域上 if (activeLayer.clonedRegion.active && activeLayer.clonedRegion.visible) { CRect screenRect = ConvertToScreenRect(activeLayer.clonedRegion.destRect); if (screenRect.PtInRect(point)) { // 记录拖动起始点 m_ptDragStart = point; m_ptCloneStart = activeLayer.clonedRegion.destRect.TopLeft(); m_bDragging = true; SetCapture(); // 捕获鼠标 TRACE(_T("开始拖动克隆区域\n")); TRACE(_T("原始位置: (%d, %d)\n"), m_ptCloneStart.x, m_ptCloneStart.y); TRACE(_T("屏幕位置: (%d, %d)\n"), point.x, point.y); return; // 不处理其他逻辑 } } if (m_bDoodleMode) { CRect imgRect = GetImageClientRect(m_layer1.isSelected?m_layer1.image:m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_doodleTempImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_doodleTempImage.GetHeight() / imgRect.Height(); m_ptDoodleStart = imgPoint; m_bDoodling = true; } return; // 涂鸦模式下不进行其他选择操作 } if (m_layer1.isSelected) { CRect imgRect = GetImageClientRect(m_layer1.image); if (imgRect.PtInRect(point)) { if (m_selectionMode == SELECT_RECTANGLE) { // 原有矩形选区代码 m_ptStart.x = (point.x - imgRect.left) * m_layer1.displayImage.GetWidth() / imgRect.Width(); m_ptStart.y = (point.y - imgRect.top) * m_layer1.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_layer1.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer1.displayImage.GetHeight() / imgRect.Height(); m_freeFormPoints.push_back(imgPoint); SetCapture(); Invalidate(); } } } else { CRect imgRect = GetImageClientRect(m_layer2.image); if (imgRect.PtInRect(point)) { if (m_selectionMode == SELECT_RECTANGLE) { // 原有矩形选区代码 m_ptStart.x = (point.x - imgRect.left) * m_layer2.displayImage.GetWidth() / imgRect.Width(); m_ptStart.y = (point.y - imgRect.top) * m_layer2.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_layer2.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer2.displayImage.GetHeight() / imgRect.Height(); m_freeFormPoints.push_back(imgPoint); SetCapture(); Invalidate(); } } } select = false; CDialogEx::OnLButtonDown(nFlags, point); } void CMyPS2Dlg::OnMouseMove(UINT nFlags, CPoint point) { #ifdef _DEBUG if (m_nDebugCounter++ % 10 == 0) // 每10次移动输出一次 { TRACE(_T(“鼠标位置: (%d, %d)\n”), point.x, point.y); if (m_bDragging) { TRACE(_T(“正在拖动克隆区域\n”)); } else if (m_bSelecting) { TRACE(_T(“正在选区操作\n”)); } } #endif if (m_bDragging) { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; // 计算移动距离(转换为图像坐标) CRect imgRect = GetImageClientRect(activeLayer.image); double scaleX = (double)activeLayer.image.GetWidth() / imgRect.Width(); double scaleY = (double)activeLayer.image.GetHeight() / imgRect.Height(); int dx = static_cast<int>((point.x - m_ptDragStart.x) * scaleX); int dy = static_cast<int>((point.y - m_ptDragStart.y) * scaleY); // 更新克隆区域位置(限制在图像范围内) int newX = max(0, min(activeLayer.image.GetWidth() - activeLayer.clonedRegion.destRect.Width(), (int)m_ptCloneStart.x + dx)); int newY = max(0, min(activeLayer.image.GetHeight() - activeLayer.clonedRegion.destRect.Height(), (int)m_ptCloneStart.y + dy)); activeLayer.clonedRegion.destRect.MoveToXY(newX, newY); Invalidate(); // 刷新显示 return; } if (m_bDoodleMode && m_bDoodling) { CRect imgRect = GetImageClientRect(m_layer1.isSelected?m_layer1.image:m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_doodleTempImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_doodleTempImage.GetHeight() / imgRect.Height(); // 绘制线段 DrawDoodleLine(m_ptDoodleStart, imgPoint); m_ptDoodleStart = imgPoint; // 刷新显示 Invalidate(FALSE); } return; } if (m_layer1.isSelected) { if (m_bSelecting && !m_layer1.displayImage.IsNull() && m_selectionMode == SELECT_RECTANGLE) { // 1. 获取图像显示区域 CRect imgRect = GetImageClientRect(m_layer1.image); // 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_layer1.displayImage.GetWidth() / imgRect.Width(); m_ptEnd.y = (point.y - imgRect.top) * m_layer1.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_layer1.displayImage.IsNull()) { CRect imgRect = GetImageClientRect(m_layer1.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_layer1.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer1.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); } } } } } else { if (m_bSelecting && !m_layer2.displayImage.IsNull() && m_selectionMode == SELECT_RECTANGLE) { // 1. 获取图像显示区域 CRect imgRect = GetImageClientRect(m_layer2.image); // 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_layer2.displayImage.GetWidth() / imgRect.Width(); m_ptEnd.y = (point.y - imgRect.top) * m_layer2.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_layer2.displayImage.IsNull()) { CRect imgRect = GetImageClientRect(m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_layer2.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer2.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 CMyPS2Dlg::OnLButtonUp(UINT nFlags, CPoint point) { if (m_bDragging) { m_bDragging = false; ReleaseCapture(); return; // 不处理其他逻辑 } if (m_bDoodleMode && m_bDoodling) { m_bDoodling = false; return; } if (m_layer1.isSelected) { 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(m_layer1.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_layer1.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer1.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; } select = true; ReleaseCapture(); Invalidate(); } } else { if (m_bSelecting || m_bFreeFormSelecting) { if (m_selectionMode == SELECT_RECTANGLE) { // 原有矩形选区结束代码 m_bSelecting = false; select = true; } else if (m_bFreeFormSelecting && m_selectionMode == SELECT_FREE_FORM) { CRect imgRect = GetImageClientRect(m_layer2.image); if (imgRect.PtInRect(point)) { // 转换为图像坐标 CPoint imgPoint; imgPoint.x = (point.x - imgRect.left) * m_layer2.displayImage.GetWidth() / imgRect.Width(); imgPoint.y = (point.y - imgRect.top) * m_layer2.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; select = true; } ReleaseCapture(); Invalidate(); } } CDialogEx::OnLButtonUp(nFlags, point); } void CMyPS2Dlg::ApplyAdvancedEffectsToSelection(int brightness, float saturation, bool bBlackWhite, bool bSepia, int mosaicSize, ImageLayer& layer) { if (layer.image.IsNull()) return; // 1. 准备选区数据 CRect validRect; bool hasSelection = false; if (m_selectionMode == SELECT_RECTANGLE && !m_rcSelection.IsRectNull()) { // 矩形选区 validRect.SetRect( max(0, (int)m_rcSelection.left), max(0, (int)m_rcSelection.top), min(layer.image.GetWidth(), (int)m_rcSelection.right), min(layer.image.GetHeight(), (int)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 (layer.displayImage.GetBits() == nullptr || layer.displayImage.GetWidth() != layer.image.GetWidth() || layer.displayImage.GetHeight() != layer.image.GetHeight()) { layer.displayImage.Destroy(); layer.displayImage.Create(layer.image.GetWidth(), layer.image.GetHeight(), layer.image.GetBPP()); } // 3. 获取像素数据 BYTE* pDest = (BYTE*)layer.displayImage.GetBits(); BYTE* pSrc = (BYTE*)layer.image.GetBits(); int destPitch = layer.displayImage.GetPitch(); int srcPitch = layer.image.GetPitch(); int channels = (layer.displayImage.GetBPP() == 32) ? 4 : 3; // 4. 处理选区像素(包含马赛克效果) #pragma omp parallel for for (int y = validRect.top; y < validRect.bottom; y++) { BYTE* pDestRow = pDest + y * destPitch; BYTE* pSrcRow = pSrc + y * srcPitch; for (int x = validRect.left; x < validRect.right; x++) { // 检查是否在选区内(不规则选区需要额外检查) bool inSelection = true; if (m_selectionMode == SELECT_FREE_FORM) { inSelection = m_rgnFreeForm.PtInRegion(x, y); } if (inSelection) { 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, (int)validRect.right); int blockEndY = min(blockStartY + mosaicSize, (int)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 (!layer.displayImage.IsNull()) { // // 方法1:使用BitBlt复制(推荐) // layer.image.Destroy(); // 先销毁旧图像 // layer.image.Create(layer.displayImage.GetWidth(), // layer.displayImage.GetHeight(), // layer.displayImage.GetBPP()); // HDC hdcSrc = layer.displayImage.GetDC(); // HDC hdcDst = layer.image.GetDC(); // BitBlt(hdcDst, 0, 0, layer.displayImage.GetWidth(), layer.displayImage.GetHeight(), // hdcSrc, 0, 0, SRCCOPY); // layer.displayImage.ReleaseDC(); // layer.image.ReleaseDC(); //} } void CMyPS2Dlg::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]; // 亮度调整(基于原始值的非线性变换) // brightness=100 → 2.0倍 float r = min(255.0f, max(0.0f, (float)(origR + brightness))); float g = min(255.0f, max(0.0f, (float)(origG + brightness))); float b = min(255.0f, max(0.0f, (float)(origB + brightness))); // 饱和度调整(保持基于原始值) if (saturation != 1.0f) { float gray = 0.299f * origR + 0.587f * origG + 0.114f * origB; r = min(255.0f,max(0.0f,(float)(gray + saturation * (r - gray)))); g = min(255.0f, max(0.0f, (float)(gray + saturation * (g - gray)))); b = min(255.0f, max(0.0f, (float)(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 CMyPS2Dlg::RedrawSelectionArea(const CRect& imageRect) { // 1. 检查输入有效性 if (imageRect.IsRectEmpty() ||( m_layer1.displayImage.IsNull()&&m_layer2.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 CMyPS2Dlg::ConvertToScreenRect(const CRect& imageRect) { if (m_layer1.isSelected) { if (m_layer1.displayImage.IsNull()) return CRect(0, 0, 0, 0); CRect imgClientRect = GetImageClientRect(m_layer1.image); return CRect( imgClientRect.left + imageRect.left * imgClientRect.Width() / m_layer1.displayImage.GetWidth(), imgClientRect.top + imageRect.top * imgClientRect.Height() / m_layer1.displayImage.GetHeight(), imgClientRect.left + imageRect.right * imgClientRect.Width() / m_layer1.displayImage.GetWidth(), imgClientRect.top + imageRect.bottom * imgClientRect.Height() / m_layer1.displayImage.GetHeight() ); } else { if (m_layer2.displayImage.IsNull()) return CRect(0, 0, 0, 0); CRect imgClientRect = GetImageClientRect(m_layer2.image); return CRect( imgClientRect.left + imageRect.left * imgClientRect.Width() / m_layer2.displayImage.GetWidth(), imgClientRect.top + imageRect.top * imgClientRect.Height() / m_layer2.displayImage.GetHeight(), imgClientRect.left + imageRect.right * imgClientRect.Width() / m_layer2.displayImage.GetWidth(), imgClientRect.top + imageRect.bottom * imgClientRect.Height() / m_layer2.displayImage.GetHeight() ); } } BOOL CMyPS2Dlg::OnEraseBkgnd(CDC* pDC) { return TRUE; // 完全禁用背景擦除 } // 获取位图实际尺寸的辅助函数 CSize CMyPS2Dlg::GetBitmapDimension(CBitmap& bitmap) { BITMAP bm; if (bitmap.GetSafeHandle() && bitmap.GetBitmap(&bm)) return CSize(bm.bmWidth, bm.bmHeight); return CSize(0, 0); } //规则选区 void CMyPS2Dlg::OnBnClickedButton9() { m_selectionMode = SELECT_RECTANGLE; ResetSelection(); // 重置选区状态 // TODO: 在此添加控件通知处理程序代码 } //不规则选区 void CMyPS2Dlg::OnBnClickedButton10() { m_selectionMode = SELECT_FREE_FORM; ResetSelection(); // 添加调试输出 TRACE(_T("不规则选区模式已激活\n")); // 强制重绘以确保状态更新 Invalidate(); // TODO: 在此添加控件通知处理程序代码 } //添加图层 void CMyPS2Dlg::OnBnClickedButton11() { if (!m_layer2.image.IsNull()) { UINT i; i = MessageBox(_T(“确认重新打开图片吗?”), _T(“提示”), MB_YESNO | MB_ICONQUESTION); if (i != IDYES) { return; } } CFileDialog dlg(TRUE, _T(“.bmp;.jpg;.png"), NULL, OFN_FILEMUSTEXIST, _T("Image Files|.bmp;.jpg;.png||”), this); if (dlg.DoModal() == IDOK) { // 释放旧资源 m_layer2.history.clear(); m_layer2.currentHistoryIndex = -1; m_layer1.isSelected = false; m_layer2.isSelected = true; if (!m_layer2.image.IsNull()) { m_layer2.image.Destroy(); } if (!m_layer2.displayImage.IsNull()) { m_layer2.displayImage.Destroy(); } if (m_layer2.image.Load(dlg.GetPathName()) != S_OK) { AfxMessageBox(L"图像加载失败!\n请检查格式支持(BMP/JPG/PNG)"); return; } else { // 加载新图像 ImageState initialState; initialState.brightness = 0; initialState.saturation = 1.0f; initialState.mosaic = 1; initialState.alpha = 0.5f; if (!m_layer2.image.IsNull()) { if (!initialState.image.IsNull()) { initialState.image.Destroy(); } initialState.image.Create(m_layer2.image.GetWidth(), m_layer2.image.GetHeight(), m_layer2.image.GetBPP()); CDC* pSrcDC = CDC::FromHandle(m_layer2.image.GetDC()); CDC* pDstDC = CDC::FromHandle(initialState.image.GetDC()); pDstDC->BitBlt(0, 0, m_layer2.image.GetWidth(), m_layer2.image.GetHeight(), pSrcDC, 0, 0, SRCCOPY); m_layer2.image.ReleaseDC(); initialState.image.ReleaseDC(); } m_layer2.history.push_back(initialState); m_layer2.history.push_back(initialState); m_layer2.currentHistoryIndex = 1; // 重置所有参数 m_nlightValue = 0; m_nsatureValue = 1.0f; m_nmskValue = 1; m_layer1.alpha = 1.0f; // 重置底层透明度为不透明 m_layer2.alpha = 0.5f; // 重置顶层透明度为半透明 // 初始化UI控件 litslider.SetPos(100); // 假设滑块范围0-200,100对应亮度0 saturslider.SetPos(100); // 假设滑块范围0-200,100对应饱和度1.0 mskslider.SetPos(1); // 马赛克块大小(1~20) m_sliderLay1.SetPos(100); // 新增:底层透明度滑块(0-100) m_sliderLay2.SetPos(50); // 新增:顶层透明度滑块(0-100) // 生成显示图像 UpdateDisplayImage(m_layer2); } } // TODO: 在此添加控件通知处理程序代码 } void CMyPS2Dlg::OnNMCustomdrawSliderl1(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; } void CMyPS2Dlg::ResetSelection() { m_bSelecting = false; m_bFreeFormSelecting = false; m_rcSelection.SetRectEmpty(); // 清理不规则选区资源 if (m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.DeleteObject(); } m_freeFormPoints.clear(); Invalidate(); } BOOL CMyPS2Dlg::HasSelection() const { if (m_selectionMode == SELECT_RECTANGLE) { return !m_rcSelection.IsRectNull(); } else { return m_rgnFreeForm.GetSafeHandle() != NULL; } } void CMyPS2Dlg::CleanUpResources() { // 清理双缓冲资源 if (m_memDC.GetSafeHdc()) { m_memDC.SelectObject((HBITMAP)NULL); m_memDC.DeleteDC(); } if (m_memBitmap.GetSafeHandle()) { m_memBitmap.DeleteObject(); } // 清理其他GDI资源 if (m_rgnFreeForm.GetSafeHandle()) { m_rgnFreeForm.DeleteObject(); } } CPoint CMyPS2Dlg::ConvertToScreenPoint(CPoint imagePoint) { if (m_layer1.isSelected) { CRect imgRect = GetImageClientRect(m_layer1.image); return CPoint( imgRect.left + imagePoint.x * imgRect.Width() / m_layer1.displayImage.GetWidth(), imgRect.top + imagePoint.y * imgRect.Height() / m_layer1.displayImage.GetHeight() ); } else { CRect imgRect = GetImageClientRect(m_layer2.image); return CPoint( imgRect.left + imagePoint.x * imgRect.Width() / m_layer2.displayImage.GetWidth(), imgRect.top + imagePoint.y * imgRect.Height() / m_layer2.displayImage.GetHeight() ); } } void CMyPS2Dlg::SelectLayer(bool selectLayer1) { m_layer1.isSelected = selectLayer1; m_layer2.isSelected = !selectLayer1; // 更新滑块显示当前图层的透明度 if (selectLayer1) { m_sliderLay1.SetPos(static_cast<int>(m_layer1.alpha * 100.0f)); } else { m_sliderLay2.SetPos(static_cast<int>(m_layer2.alpha * 100.0f)); } Invalidate(); // 刷新显示 } //选择图层 void CMyPS2Dlg::OnBnClickedButton12() { SelectLayer(!m_layer1.isSelected); if (m_layer1.isSelected) { if (m_layer1.displayImage.IsNull()) { AfxMessageBox(_T(“请先打开图片”)); } else { AfxMessageBox(_T(“已选 图层1”)); } } else { if (m_layer2.displayImage.IsNull()) { AfxMessageBox(_T(“请先打开图片”)); } else { AfxMessageBox(_T(“已选 图层2”)); } } // TODO: 在此添加控件通知处理程序代码 } //开始涂鸦 void CMyPS2Dlg::OnBnClickedButton13() { StartDoodleMode(); // TODO: 在此添加控件通知处理程序代码 } //应用涂鸦 void CMyPS2Dlg::OnBnClickedButton14() { EndDoodleMode(true); // TODO: 在此添加控件通知处理程序代码 } //取消涂鸦 void CMyPS2Dlg::OnBnClickedButton15() { EndDoodleMode(false); // TODO: 在此添加控件通知处理程序代码 } //颜色滑块 void CMyPS2Dlg::OnNMCustomdrawSlidercol(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDERCOL); if (pSlider) { int pos = pSlider->GetPos(); // 将滑块位置转换为颜色 (0-360度色相) m_doodleColor = HSLtoRGB(pos, 1.0, 0.5); } *pResult = 0; } // HSL转RGB辅助函数 COLORREF CMyPS2Dlg::HSLtoRGB(float H, float S, float L) { float C = (1 - fabs(2 * L - 1)) * S; float X = C * (1 - fabs(fmod(H / 60.0, 2) - 1)); float m = L - C / 2; float R, G, B; if (H >= 0 && H < 60) { R = C; G = X; B = 0; } else if (H >= 60 && H < 120) { R = X; G = C; B = 0; } else if (H >= 120 && H < 180) { R = 0; G = C; B = X; } else if (H >= 180 && H < 240) { R = 0; G = X; B = C; } else if (H >= 240 && H < 300) { R = X; G = 0; B = C; } else { R = C; G = 0; B = X; } BYTE r = (BYTE)((R + m) * 255); BYTE g = (BYTE)((G + m) * 255); BYTE b = (BYTE)((B + m) * 255); return RGB(r, g, b); } //粗细滑块 void CMyPS2Dlg::OnNMCustomdrawSliderthi(NMHDR* pNMHDR, LRESULT* pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); CSliderCtrl* pSlider = (CSliderCtrl*)GetDlgItem(IDC_SLIDERTHI); if (pSlider) { m_doodleThickness = pSlider->GetPos(); } *pResult = 0; } void CMyPS2Dlg::StartDoodleMode() { ImageLayer& activeLayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (activeLayer.displayImage.IsNull()) { AfxMessageBox(_T(“请先打开图片”)); return; } // 创建临时位图 if (!m_doodleTempImage.IsNull()) m_doodleTempImage.Destroy(); int width = activeLayer.displayImage.GetWidth(); int height = activeLayer.displayImage.GetHeight(); m_doodleTempImage.Create(width, height, activeLayer.displayImage.GetBPP()); // 复制当前显示图像到临时位图 CDC* pSrcDC = CDC::FromHandle(activeLayer.displayImage.GetDC()); CDC* pDstDC = CDC::FromHandle(m_doodleTempImage.GetDC()); pDstDC->BitBlt(0, 0, width, height, pSrcDC, 0, 0, SRCCOPY); activeLayer.displayImage.ReleaseDC(); m_doodleTempImage.ReleaseDC(); // 重置涂鸦数据 m_doodleLines.clear(); m_bDoodleMode = true; m_bDoodling = false; // 设置鼠标光标 SetCursor(LoadCursor(NULL, IDC_CROSS)); } // 结束涂鸦模式 void CMyPS2Dlg::EndDoodleMode(bool apply) { if (!m_bDoodleMode) return; if (apply) { ApplyDoodleToImage(); } // 清理资源 m_doodleTempImage.Destroy(); m_doodleLines.clear(); m_bDoodleMode = false; m_bDoodling = false; // 恢复鼠标光标 SetCursor(LoadCursor(NULL, IDC_ARROW)); // 刷新显示 Invalidate(FALSE); } // 将涂鸦应用到当前图层(有改) void CMyPS2Dlg::ApplyDoodleToImage() { ImageLayer* activeLayer = m_layer1.isSelected ? &m_layer1 : &m_layer2; if (activeLayer->image.IsNull()) return; // 获取设备上下文 CDC* pDC = CDC::FromHandle(activeLayer->image.GetDC()); CPen pen(PS_SOLID, m_doodleThickness, m_doodleColor); CPen* pOldPen = pDC->SelectObject(&pen); // 绘制所有涂鸦线段 for (const auto& line : m_doodleLines) { pDC->MoveTo(line.first); pDC->LineTo(line.second); } pDC->SelectObject(pOldPen); activeLayer->image.ReleaseDC(); doodend = true; m_bStateDirty = true; // 更新显示图像并保存状态 UpdateDisplayImage(*activeLayer); SaveCurrentState(*activeLayer); } // 绘制一条涂鸦线段 void CMyPS2Dlg::DrawDoodleLine(CPoint start, CPoint end) { if (m_doodleTempImage.IsNull()) return; CDC* pDC = CDC::FromHandle(m_doodleTempImage.GetDC()); CPen pen(PS_SOLID, m_doodleThickness, m_doodleColor); CPen* pOldPen = pDC->SelectObject(&pen); pDC->MoveTo(start); pDC->LineTo(end); pDC->SelectObject(pOldPen); m_doodleTempImage.ReleaseDC(); // 将线段添加到列表 m_doodleLines.push_back(std::make_pair(start, end)); } //复制选区 void CMyPS2Dlg::OnBnClickedButton16() { ImageLayer& activelayer = m_layer1.isSelected ? m_layer1 : m_layer2; if (!HasSelection()) { AfxMessageBox(_T(“请先创建选区”)); return; } if (!activelayer.clonedRegion.active) { // 首次复制:创建克隆区域 CRect selRect = GetSelectionRect(); activelayer.clonedRegion.sourceRect = selRect; activelayer.clonedRegion.destRect = selRect; activelayer.clonedRegion.active = true; activelayer.clonedRegion.visible = true; // 复制图像数据 - 从displayImage复制而不是image activelayer.clonedRegion.image.Destroy(); activelayer.clonedRegion.image.Create(selRect.Width(), selRect.Height(), activelayer.displayImage.GetBPP()); // 使用displayImage作为源 CDC* pSrcDC = CDC::FromHandle(activelayer.displayImage.GetDC()); CDC* pDstDC = CDC::FromHandle(activelayer.clonedRegion.image.GetDC()); pDstDC->BitBlt(0, 0, selRect.Width(), selRect.Height(), pSrcDC, selRect.left, selRect.top, SRCCOPY); activelayer.displayImage.ReleaseDC(); activelayer.clonedRegion.image.ReleaseDC(); TRACE(_T("创建克隆区域(带效果)\n")); TRACE(_T("选区位置: (%d, %d) - (%d, %d)\n"), selRect.left, selRect.top, selRect.right, selRect.bottom); } else { // 切换显示状态 activelayer.clonedRegion.visible = !activelayer.clonedRegion.visible; // 添加调试输出 TRACE(_T("切换克隆区域可见性: %s\n"), activelayer.clonedRegion.visible ? _T("可见") : _T("隐藏")); } Invalidate(); // 刷新显示 // 添加状态栏提示 /*CString msg; msg.Format(_T("克隆区域已%s"), activelayer.clonedRegion.visible ? _T("显示") : _T("隐藏")); GetDlgItem(IDC_STATIC)->SetWindowText(msg);*/ // 切换显示状态 //activeLayer.clonedRegion.visible = !activeLayer.clonedRegion.visible; //Invalidate(); // TODO: 在此添加控件通知处理程序代码 } CRect CMyPS2Dlg::GetSelectionRect() { if (m_selectionMode == SELECT_RECTANGLE && !m_rcSelection.IsRectNull()) { return m_rcSelection; } else if (m_selectionMode == SELECT_FREE_FORM && m_rgnFreeForm.GetSafeHandle()) { CRect rect; m_rgnFreeForm.GetRgnBox(&rect); return rect; } return CRect(0, 0, 0, 0); // 无选区 }为什么这段函数能实现在同一图层,出现多个统一类型的选区,且相互独立
最新发布
07-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值