通过BYTE数组创建Gdiplus::Bitmap

本文介绍如何在VC++中使用GDI+从BYTE数组创建Bitmap对象的过程。该过程涉及使用IStream辅助创建Bitmap,并确保正确释放内存避免泄漏。

VC中利用GDI+进行图形编程,已知图片数据保存在BYTE数组中,需要通过这个BYTE创建一个Bitmap再进行相关处理,这个过程中间需要通过IStream来实现,代码如下:

void ByteToBitmap( BYTE* pImageData, int nSize )
{
        HRESULT hr;
        Status sr;

        HGLOBAL hGlobal = GlobalAlloc( GMEM_MOVEABLE, nSize );
        LPVOID pvData = NULL;
        pvData = GlobalLock( hGlobal );
        memcpy( pvData, pImageData, nSize );
        GlobalUnlock( hGlobal );

        IStream *pStream = NULL;
        hr = CreateStreamOnHGlobal( hGlobal,  TRUE,  &pStream );

        Bitmap bmp( pStream );
        sr = bmp.GetLastStatus();  // sr = Ok,表示Bitmap创建成功。

        ……
        ……

        GlobalFree( hGlobal ); // 使用Bitmap完后,需要释放资源,以免造成内存泄漏。
}

//绘制图片 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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值