65.9K
CodeProject 正在变化。 阅读更多。
Home

高级图像控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (37投票s)

2015年10月6日

CPOL

9分钟阅读

viewsIcon

60610

downloadIcon

20630

用于查看大多数常见图像格式的高级图像控件,包含导入图像、预览、调整大小、定位、平移、缩放、导出图像、提取资源图标等高级功能。


目录

  1. 引言
  2. CImageCtrl 类
  3. 平移和缩放
  4. GDI+
  5. GDI:内存设备上下文
  6. 资源
       6.1 仅资源 DLL
       6.2 ResourceList 模块
       6.3 Icon 模块
       6.4 从资源 DLL 或 EXE 加载图像
  7. 可调整大小的对话框
  8. 背景
  9. 参考
10. 使用代码
11. 历史记录

 

引言

本文描述了图像控件(派生自 CStatic),主要用于导入和查看以下图像格式:BMP、DIB、JPG、JPEG、JPE、JFIF、GIF、TIF、TIFF、PNG、ICO、WMF、EMF。此外,还集成了一些其他方便的功能,如调整大小定位平移缩放导出图像提取资源图标

我借鉴了Tobias Eiseler 的解决方案1 的基础,并进一步改进,以支持从 dll 或 exe 资源导入所有提到的图像类型,并提供额外的调整大小和定位(在客户区内)选项。

目标是创建一个高级图像控件,图像可以从以下四种来源之一获得:

1) 图像文件路径,
2) 图像 IStream 接口
3) 图像 BYTE(unsigned char) 数组,
4) 图像 DLL 或 EXE 资源。

 

CImageCtrl 类

现在,快速看一下封装此行为的 CImageCtrl 类。

class CImageCtrl : public CStatic
{
public:
    CImageCtrl();
    ~CImageCtrl();
    enum sizeType { SIZE_ORIGINAL, SIZE_SCALETOFIT, SIZE_CUSTOM };
    enum allignmentType { ALLIGN_TOPLEFT, ALLIGN_TOPCENTER, ALLIGN_TOPRIGHT, ALLIGN_MIDDLELEFT, 
                          ALLIGN_MIDDLECENTER, ALLIGN_MIDDLERIGHT, ALLIGN_BOTTOMLEFT, 
                          ALLIGN_BOTTOMCENTER, ALLIGN_BOTTOMRIGHT };    

    void setSizeType(int sizeType) { m_sizeType = sizeType; } // Size type: SIZE_ORIGINAL,...
    double getLeft() { return m_left; }            // x-coordinate of the image top-left point.
    double getTop() { return m_top; }              // y-coordinate of the image top-left point.
    void setWidth(double width) { m_width = width; }          // Set image width (pixels).
    double getWidth() { return m_width; }                     // Image width (pixels).
    double getWidthOriginal() { return m_widthOriginal; }     // Image original width (pixels).
    void setHeight(double height) { m_height = height; }      // Set image height (pixels).
    double getHeight() { return m_height; }                   // Image height (pixels).
    double getHeightOriginal() { return m_heightOriginal; }   // Image original height (pixels).

    void setMaintainAspectRatio(bool maintainAspectRatio) 
    { 
        m_maintainAspectRatio = maintainAspectRatio; 
    }
    double setAspectRatio(double aspectRatio) { return m_aspectRatio = aspectRatio; } 
    double getAspectRatio() { return m_aspectRatio; }         // Case "image": m_height / m_width.
    void setAllignmentType(int allignmentType) { m_allignmentType = allignmentType; }  
    void setPanMode(bool isPanMode) { m_isPanMode = isPanMode; }     // Enable/disable PAN mode.
    void setZoomMode(bool isZoomMode) { m_isZoomMode = isZoomMode; } // Enable/disable ZOOM mode.

    bool isImageShown() { return m_isImageShown > 0; } // Is image shown in the control?
    void update();                                     // Update image in the control.
    void erase();                                      // Erase image from the control.

    BOOL load(CString szFilePath);                     // Loads an image from file.
    BOOL load(IStream* piStream);                      // Loads an image from IStream interface.
    BOOL load(BYTE* pData, size_t nSize);              // Loads an image from BYTE array.
    
    // Loads image from resource.
    BOOL load(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType, WORD wlan); 
    
    BOOL convert(CString pathName, CString imageType); // Converts image to specified image type.
    
    // Loads icon from resource EXE or DLL and saves it to *.ico file.
    BOOL iconResourceToFile(CString resPathName, LPCTSTR lpName, WORD wlan, CString icoPathName); 

private:
    void release(bool calcFrameAspectRatio = true);  // Release allocated memory and initialize.

    int m_sizeType;             // Size type: SIZE_ORIGINAL, SIZE_SCALETOFILL, SIZE_CUSTOM.
    double m_left;              // x-coordinate of the image top-left point.
    double m_top;               // y-coordinate of the image top-left point.
    double m_width;             // Image width (pixels).
    double m_height;            // Image height (pixels).
    double m_widthOriginal;     // Image original width (pixels).
    double m_heightOriginal;    // Image original height (pixels).
    bool m_maintainAspectRatio; // Maintain aspect ratio or not.
    double m_aspectRatio;       // Aspect ratio factor: Case "image": m_height / m_width.
    int m_allignmentType;       // Image alignment type inside control: ALLIGN_TOPLEFT,...
    bool m_isPanMode;           // Enabled/disabled PAN mode.
    bool m_isZoomMode;          // Enabled/disabled ZOOM mode.
    double m_zoomMin;           // Minimal rectangle side value (in pixels) on ZOOM action.
    double m_zoomMax;           // Maximal rectangle side value (in pixels) on ZOOM action.
    BOOL m_isImageShown;        // Is image shown in the control?
    
    BOOL m_isInitialShow; // Initial image show? True, if not derived from PAN/ZOOM mode action.
    CPoint m_panAtPt;           // Origin point of PAN action.
    CPoint m_panOffset;         // Offset distances at PAN action.
    CPoint m_zoomAtPt;    // Point at zoom event, triggered by mouse wheel scrolling ON image.
    double m_zoomFactor;        // Zoom factor: Case > 1: zoom in, Case < 1: zoom out.
    
+   Bitmap* m_pBmp;             // Pointer to GDI+ bitmap.
+   ULONG_PTR m_gdiplusToken;   // GDI+ Token.

protected:
    virtual void PreSubclassWindow();
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
    DECLARE_MESSAGE_MAP()
};

 

平移和缩放

首先,您需要通过选择对话框窗口上的相应复选框来启用平移或缩放模式。

 

什么是平移及其工作原理?

在图像客户区上方单击鼠标左键,然后移动鼠标,最后释放鼠标左键。结果是将图像按照“鼠标移动”向量进行位移。


什么是缩放及其工作原理?

将鼠标光标置于图像上(或至少在图像客户区内)。使用鼠标中键(滚轮)进行放大或缩小。结果是缩放后的图像,以鼠标位置点为固定点。

 

GDI+

用于查看图像的技术是 GDI+,它首次出现在 Windows XP 和 Windows Server 2003 中(GDI+ 功能用代码行开头的加号表示)。

+


我在 CimageCtrl 构造函数中这样初始化它:

CImageCtrl::CImageCtrl(void)
    : CStatic(), m_pBmp(NULL), m_gdiplusToken(0), m_sizeType(sizeType::SIZE_SCALETOFIT), 
                 m_maintainAspectRatio(true), m_aspectRatio(1), 
                 m_allignmentType(allignmentType::ALLIGN_MIDDLECENTER), 
                 m_isPanMode(FALSE), m_isZoomMode(FALSE)
{
+   GdiplusStartupInput startupInput; GdiplusStartup(&m_gdiplusToken, &startupInput, NULL);
    
    m_isImageShown = FALSE; m_panAtPt.SetPoint(-1, -1); m_panOffset.SetPoint(0, 0); 
    m_zoomAtPt.SetPoint(-1, -1); m_zoomFactor = 1.0; m_zoomMin = 1; m_zoomMax = 99999;
}


并在析构函数中进行清理:

CImageCtrl::~CImageCtrl(void)
{
+   release(false); GdiplusShutdown(m_gdiplusToken);
}


变量 CImageCtrl::m_pBmp 是指向 Bitmap 的指针,该指针之前已通过四个 CImageCtrl::load() 方法之一获取,而 CImageCtrl::DrawItem() 过程执行 OnPaint() “工作”,该工作由 CImageCtrl::Invalidate()CImageCtrl::UpdateWindow() 方法触发。

void CImageCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    double w0, h0, sx, sy, s, dx, dy; CRect rect; 
    CDC *pDC = NULL, dcMem; CBitmap bmpMem, *oldBmp = NULL;

    if (pDC = GetDC())
    {
+       if (m_pBmp)
        {
            // Create an in-memory device context, compatible with the painting device context.
            dcMem.CreateCompatibleDC(pDC);        
            // Create bitmap compatible with the painting device context.
            GetClientRect(rect); bmpMem.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
            // Select the bitmap into this in-memory device context.
            oldBmp = dcMem.SelectObject(&bmpMem);  

+           Graphics graphics(dcMem.m_hDC);
            // Paint with dialog-background color.
            dcMem.FillSolidRect(rect, GetSysColor(COLOR_3DFACE)); 

            if (m_isInitialShow)
            {
                m_widthOriginal = m_pBmp->GetWidth(); m_heightOriginal = m_pBmp->GetHeight();
                m_aspectRatio = m_heightOriginal / m_widthOriginal;

                if (!m_maintainAspectRatio)
                {
                    if (m_sizeType == sizeType::SIZE_SCALETOFIT) 
                        { m_width = rect.Width(); m_height = rect.Height(); }
                    else if (m_sizeType == sizeType::SIZE_ORIGINAL) 
                        { m_width = m_widthOriginal; m_height = m_heightOriginal; }
                    m_left = m_top = 0;
                }
                else
                {
                    if (m_sizeType == sizeType::SIZE_SCALETOFIT)
                    {
                        sx = rect.Width() / m_widthOriginal; 
                        sy = rect.Height() / m_heightOriginal;
                        s = (sx > sy) ? sy : sx; 
                        m_height = m_aspectRatio * (m_width = s * m_widthOriginal);
                    }
                    else if (m_sizeType == sizeType::SIZE_CUSTOM)
                    {
                        sx = m_width / m_widthOriginal; 
                        sy = m_height / m_heightOriginal;
                        s = (sx > sy) ? sy : sx; 
                        m_height = m_aspectRatio * (m_width = s * m_widthOriginal);
                    }
                    else if (m_sizeType == sizeType::SIZE_ORIGINAL) 
                        { m_width = m_widthOriginal; m_height = m_heightOriginal; }
                }

                if (m_allignmentType == allignmentType::ALLIGN_TOPLEFT) m_left = m_top = 0;
                else if (m_allignmentType == allignmentType::ALLIGN_TOPCENTER) 
                    { m_left = (rect.Width() - m_width) / 2; m_top = 0; }
                else if (m_allignmentType == allignmentType::ALLIGN_TOPRIGHT) 
                    { m_left = rect.Width() - m_width; m_top = 0; }
                else if (m_allignmentType == allignmentType::ALLIGN_MIDDLELEFT) 
                    { m_left = 0; m_top = (rect.Height() - m_height) / 2; }
                else if (m_allignmentType == allignmentType::ALLIGN_MIDDLECENTER) 
                { 
                    m_left = (rect.Width() - m_width) / 2; m_top = (rect.Height() - m_height) / 2;
                }
                else if (m_allignmentType == allignmentType::ALLIGN_MIDDLERIGHT) 
                    { m_left = rect.Width() - m_width; m_top = (rect.Height() - m_height) / 2; }
                else if (m_allignmentType == allignmentType::ALLIGN_BOTTOMLEFT) 
                    { m_left = 0; m_top = rect.Height() - m_height; }
                else if (m_allignmentType == allignmentType::ALLIGN_BOTTOMCENTER) 
                    { m_left = (rect.Width() - m_width) / 2; m_top = rect.Height() - m_height; }
                else if (m_allignmentType == allignmentType::ALLIGN_BOTTOMRIGHT) 
                    { m_left = rect.Width() - m_width; m_top = rect.Height() - m_height; }
            }
            else if (m_zoomAtPt.x < 0) { m_left += m_panOffset.x; m_top += m_panOffset.y; }// PAN
            else if (m_zoomFactor > 1e-6)                                                  // ZOOM
            {
                ScreenToClient(&m_zoomAtPt);
                if ((dx = (m_zoomAtPt.x - m_left)) < 1e-6) { m_zoomAtPt.x = (LONG)m_left;dx = 0; }
                else if (m_zoomAtPt.x > m_left + m_width - 1e-6) 
                    { m_zoomAtPt.x = (LONG)(m_left + (dx = m_width)); }
                if ((dy = (m_zoomAtPt.y - m_top)) < 1e-6) { m_zoomAtPt.y = (LONG)m_top; dy = 0; }
                else if (m_zoomAtPt.y > m_top + m_height - 1e-6) 
                    { m_zoomAtPt.y = (LONG)(m_top + (dy = m_height)); }

                w0 = m_width * m_zoomFactor; h0 = m_height * m_zoomFactor;
                if (w0 >= m_zoomMin && w0 <= m_zoomMax && h0 >= m_zoomMin && h0 <= m_zoomMax) 
                {
                    dx *= (w0 / m_width); dy *= (h0 / m_height);
                    m_left = m_zoomAtPt.x - dx; m_top = m_zoomAtPt.y - dy; 
                    m_height = m_aspectRatio * (m_width = w0);
                    GetParent()->SendMessage(WM_APP + 1, 12345, 0); 
                }
            }

+           m_isImageShown = (graphics.DrawImage(m_pBmp, 
                             (int)(m_left + 1e-6), (int)(m_top + 1e-6), 
                             (int)(m_width + 1e-6), (int)(m_height + 1e-6)) == Status::Ok);
            pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
            dcMem.SelectObject(oldBmp); bmpMem.DeleteObject(); dcMem.DeleteDC();
        }
        else 
        { 
            GetClientRect(rect); pDC->FillSolidRect(rect, GetSysColor(COLOR_3DFACE));
            m_isImageShown = FALSE; 
        }
        ReleaseDC(pDC); m_zoomAtPt.SetPoint(-1, -1);
    }
}

 

GDI:内存设备上下文

此技术用于消除平移或缩放操作期间的“闪烁”效果。技巧是创建一个内存设备上下文(与绘图设备上下文兼容),所有绘图都在其中完成。最后,只需将内存设备上下文的一部分复制到绘图设备上下文。这通过以下语句实现:

CDC *pDC = NULL, dcMem; CBitmap bmpMem, *oldBmp = NULL;

pDC = GetDC();

// Create an in-memory device context, compatible with the painting device context.
dcMem.CreateCompatibleDC(pDC);  

// Create bitmap compatible with the painting device context.
GetClientRect(rect); bmpMem.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());

// Select the bitmap into this in-memory device context.
oldBmp = dcMem.SelectObject(&bmpMem);                                                

// Do all the paintings with dcMem in-memory device context here.

// Copy a portion of the in-memory device context to the painting device context.
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY); 

dcMem.SelectObject(oldBmp); bmpMem.DeleteObject(); dcMem.DeleteDC(); ReleaseDC(pDC);

如果您想了解更多关于设备上下文的信息,这里 2 是 Marius Bancila 的一篇优秀文章。
 

仅资源 DLL

我决定创建一个仅资源 DLL3(包含所有图像),以测试“从资源 DLL 或 EXE 加载图像”选项。

仅资源 DLL 是一个只包含资源的 DLL,如图像、字符串、对话框等。


如何在Microsoft Visual Studio中创建它?

1) 创建“新建项目->WIn32 项目->DLL”应用程序类型。
2) 选择“项目->属性->配置属性->链接器->高级->无入口点”并将选项设置为
   是 (/NOENTRY)”。
3) 添加资源。


我添加了 BMP、DIB、JPG、JPEG、JPE、JFIF、GIF、TIF、TIFF、PNG、ICO、WMF、EMF 类型的图像。

*.ico 图像在“图标”资源类型下;
*.bmp 图像在“位图”和“RCDATA”资源类型下;
*.jpg 图像在“JPG自定义资源类型下;
*.png 图像在“PNG自定义资源类型下,依此类推。

 

ResourceList 模块

ResourceList 模块包含“ResourceList.h”和“ResourceList.cpp”(项目 ImageControl),用于列出所有资源,由资源语言、资源类型和资源名称定义。我想强调的是,资源类型和名称可以是字符串或整数字符串(例如 MAKEINTRESOURCE(153)RT_BITMAP)。

如何获取可能是图像类型的资源?

#include "ResourceList.h"

CResourceList rl; HMODULE hModule;
CArray<tResourceLCID*> *pRes;      // Array of resource "Locale Culture Identifier"-s.

if(hModule = LoadLibrary(_T("...")))
{
   rl.setResourceType(CResourceList::resourceType::RESOURCE_IMAGE);
   pRes = rl.getResourceList(hModule); // Get the resource list. Do something with it.

   FreeLibrary(hModule);
}

 

Icon 模块

Icon 模块包含“Icon.h”和“Icon.cpp”(项目 ImageControl),用于提供 importIcon() (从资源模块或 *.ico 文件)和 exportIcon() (导出到 *.ico 文件)方法。

如何加载资源图标并将其导出为 *.ico 文件?

#include "Icon.h"

CIconExtractor ie; HMODULE hModule;
LPCTSTR lpName = ...;                     // Icon resource name.
WORD wlan = ...;                          // Icon resource language.
CString icoPathName = _T("...");          // Output icon path.

// You can get resource list as mentioned in previous section, and filter only 
// RT_GROUP_ICON resources by calling setResourceType(RT_GROUP_ICON) preliminary.
// Thus you will get all resource icons listed.

if(hModule = LoadLibrary(_T("...")))
{
   if(ie.importIcon(hModule, lpName, wlan)) ie.exportIcon(icoPathName);

   FreeLibrary(hModule);
}

 

从资源 DLL 或 EXE 加载图像

使用 CImageCtrl 类,您可以分 3 步完成此操作。

1) 调用函数 hModule = LoadLibrary() 加载相应的资源 dll 或 exe。
2) 调用 m_image.load(hModule, MAKEINTRESOURCE(.), ., .)
3) 调用 FreeLibrary(hModule) 卸载资源。

这在 CImageControlDlg::OnCbnSelchangeComboResname() 事件中实现。

void CImageControlDlg::OnCbnSelchangeComboResname()
{
    int i, j, k; bool isTypeInt, isNameInt; CString type, name, buff; HMODULE hModule;

    if ((i = ((CComboBox *)GetDlgItem(IDC_COMBO_RESLOCALE))->GetCurSel()) != CB_ERR 
        && (j = ((CComboBox *)GetDlgItem(IDC_COMBO_RESTYPE))->GetCurSel()) != CB_ERR 
        && (k = ((CComboBox *)GetDlgItem(IDC_COMBO_RESNAME))->GetCurSel()) != CB_ERR)
        if (hModule = LoadLibrary(m_resFilePath))
        {
            isTypeInt = m_pRes->GetAt(i)->resTypes.GetAt(j)->isInteger; 
            type = m_pRes->GetAt(i)->resTypes.GetAt(j)->type;
            isNameInt = m_pRes->GetAt(i)->resTypes.GetAt(j)->resNames.GetAt(k)->isInteger; 
            name = m_pRes->GetAt(i)->resTypes.GetAt(j)->resNames.GetAt(k)->name;

      // if(m_image.load(m_hModule, MAKEINTRESOURCE(164), RT_GROUP_ICON, m_pRes->GetAt(i)->lcid)) 
      // if(m_image.load(m_hModule, MAKEINTRESOURCE(153), _T("GIF"), m_pRes->GetAt(i)->lcid)) 
            if(m_image.load(hModule, isNameInt ? MAKEINTRESOURCE(_wtoi(name)) : name,
                        isTypeInt ? MAKEINTRESOURCE(_wtoi(type)) : type, m_pRes->GetAt(i)->lcid))
            {
                m_isOnEnChangeEditWidthHeight = false;
                buff.Format(_T("%d"), m_image.getWidth()); 
                GetDlgItem(IDC_EDIT_WIDTH)->SetWindowText(buff);
                buff.Format(_T("%d"), m_image.getHeight()); 
                GetDlgItem(IDC_EDIT_HEIGHT)->SetWindowText(buff);
                m_isOnEnChangeEditWidthHeight = true;

                ((CComboBox*)GetDlgItem(IDC_COMBO_RESTYPE))->GetLBText(j, buff);
            }
            FreeLibrary(hModule);
        }

    GetDlgItem(IDC_BUTTON_SAVEICONAS)->EnableWindow(buff == _T("Icon"));
    GetDlgItem(IDC_BUTTON_SAVEAS)->EnableWindow(m_image.isImageShown());
}

备注

注意加载图标。资源类型为 RT_GROUP_ICON。图标资源可以包含多个图像,每个图像的类型为 RT_ICON。

 

现在,让我们更精确地看一下 CImageCtrl::load(hModule, MAKEINTRESOURCE(.), ., .) 方法。基本上,代码根据资源类型(非图标和图标资源)分为两个分支。

1) “非图标资源”通过此代码片段。

BOOL CImageCtrl::load(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType, WORD wlan)
{
    HRSRC hRes; DWORD resSize; HGLOBAL hGlobal, hGlobal2, hGlobal3; HICON hIcon;
    void *pRes = NULL, *pRes2 = NULL; IStream *pStream = NULL; GRPICONDIR *pIconDir = NULL;
    
    release(); m_isImageFromResource = true;
    if ((hRes = FindResourceEx(hModule, lpType, lpName, wlan)) 
       && (resSize = SizeofResource(hModule, hRes)) && (hGlobal = LoadResource(hModule, hRes)))
    {
         if (lpType != RT_GROUP_ICON)
         {
             if (lpType == RT_BITMAP)
             {
               m_pBmp = new Bitmap((HBITMAP)LoadImage(hModule, lpName, IMAGE_BITMAP, 0, 0, 0), 0);
               m_isInitialShow = TRUE; Invalidate(); UpdateWindow(); m_isInitialShow = FALSE; 
             }
             else if (pRes = LockResource(hGlobal))
             {
                 if (hGlobal2 = GlobalAlloc(GMEM_MOVEABLE, resSize))
                 {
                     if (pRes2 = GlobalLock(hGlobal2))
                     {
                         CopyMemory(pRes2, pRes, resSize);
                         if (CreateStreamOnHGlobal(hGlobal2, FALSE, &pStream) == S_OK)  
                         { 
                             if (pBmp = Bitmap::FromStream(pStream))
                             {
                      Graphics g(m_pBmp = new Bitmap(pBmp->GetWidth(), pBmp->GetHeight()));
                      g.DrawImage(pBmp, 0, 0, pBmp->GetWidth(), pBmp->GetHeight()); delete pBmp;
                             }
                             pStream->Release();  
                         }
                         UnlockResource(hGlobal2);
                     }
                     GlobalFree(hGlobal2);
                 }
                 UnlockResource(hGlobal);
             }
         }
         FreeResource(hGlobal);
     }
     
     m_isImageFromResource = true; return m_isImageShown;
}
 
2) “图标资源”更复杂。

这里4,您可以找到图标资源(RT_GROUP_ICON)就是一个 GRPICONDIR 结构,如下定义。
typedef struct
{
    WORD            idReserved;   // Reserved (must be 0).
    WORD            idType;       // Resource type (1 for icons).
    WORD            idCount;      // Number of icon images.
    GRPICONDIRENTRY idEntries[1]; // An entry for each icon image.
} GRPICONDIR, *LPGRPICONDIR;

同时,所有类型为 RT_ICON 的图标图像都位于 GRPICONDIRENTRY 结构数组中。
typedef struct
{
    BYTE    bWidth;               // Width (in pixels) of the image.
    BYTE    bHeight;              // Height (in pixels )of the image.
    BYTE    bColorCount;          // Number of colors in image (0 if >= 8bpp).
    BYTE    bReserved;            // Reserved (must be 0).
    WORD    wPlanes;              // Color Planes.
    WORD    wBitCount;            // Bits per pixel.
    DWORD   dwBytesInRes;         // How many bytes in this resource?
    WORD    nID;                  // The ID.
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
 
用“代码语言”来说,这需要以下附加代码行。
GRPICONDIR *pIconDir = NULL;

if (pIconDir = (GRPICONDIR*)LockResource(hGlobal))
{
    if (pIconDir->idCount && 
       (hRes = FindResource(hModule, MAKEINTRESOURCE(pIconDir->idEntries[0].nID), RT_ICON)) &&
       (resSize = SizeofResource(hModule, hRes)) && (hGlobal3 = LoadResource(hModule, hRes)))
    {
       //
       // Same as in non-icon resource images.
       //

       FreeResource(hGlobal3);
    }
    UnlockResource(hGlobal);
}

 

可调整大小的对话框

我们希望在调整对话框大小时执行适当的操作,例如重新定位和/或调整对话框控件的大小。为此,开发了“StandardLibrary”(静态库项目),其中包含“ResizableDlg”模块。

一种方法是相对于对话框大小的变化,从锚点描述这些操作。如果我们只想执行重新定位,那么一个锚点就足以描述所有可能的重新定位(让它成为对话框的左上角点)。调整大小的操作更棘手。如果我们观察 x 方向的调整大小,可以固定左边缘并移动右边缘,反之亦然。y 方向的调整大小也有类似的情况。但是,以左上角为锚点,x 方向的相应调整大小是移动右边缘,y 方向是移动底部边缘。类似地,我们可以为其余三个锚点定义 x 和 y 方向的调整大小操作:左下角、右上角、右下角。

相对于对话框大小变化有四种基本操作:

  • x 方向的重新定位(参数 dxMoveRelative),
  • y 方向的重新定位(参数 dyMoveRelative),
  • x 方向的调整大小(参数 dxResizeRelative),
  • y 方向的调整大小(参数 dyResizeRelative)。

所有这四种操作都可以用区间 [0,1] 中的参数来描述。0 表示无操作,1 表示 x 或 y 方向的操作与对话框的 x 或 y 方向变化量相同,0.3 表示 x 或 y 方向的操作为变化量的 30%……

最好是
dxMoveRelative + dxResizeRelative <= 1dyMoveRelative + dyResizeRelative <= 1
否则控件可能会离开对话框区域,这是不允许的。

如果我们选择所有四个参数为零,则控件将固定在距离锚点的 x 和 y 距离处。

最后,“ResizableDlg”模块中有一个方法 CResizableDlg::moveResizeControl(...),其参数为:

    int nID (控件 ID),
    int anchor (锚点:TOP_LEFTBOTTOM_LEFTTOP_RIGHTBOTTOM_RIGHT),
    double dxMoveRelative (x 方向的重新定位),
    double dyMoveRelative (y 方向的重新定位),
    double dxResizeRelative (x 方向的调整大小),
    double dyResizeRelative (y 方向的调整大小),
    bool isTransparent (组合框为 true),

该方法定义了在对话框调整大小时对控件执行的操作。

如果您想使用 **ResizableDlg 模块**,请点击这里

 

背景

  • GDI+(图形、位图、图元文件)
  • GDI(内存设备上下文、客户设备上下文)
  • 仅资源 DLL
  • 可调整大小的对话框

 

参考

1. Picture Control - Tobias Eiseler - CodeProject
2. Working with Device Contexts - Marius Bancila - CodeGuru
3. Creating Resource-Only DLL - Microsoft - MSDN
4. Icons - John Hornick - MSDN
 

使用代码


文件“Source.zip”包含“ImageControl”静态库项目。


您可以在您的项目中这样使用此库:

1) 在“项目->属性->配置属性->C/C++->常规->附加包含目录”下
    添加适当的相对路径“..\ImageControl\inc”,以定位“ImageCtrl.h”、“ResourceList.h”和“Icon.h”文件。

2)  在“项目->属性->配置属性->链接器->常规->附加库目录”下
    添加适当的相对路径“..\ImageControl\x64\Release”,以定位“ImageControl.lib”文件。

3)  在“项目->属性->配置属性->链接器->输入->附加依赖项”下
    添加“ImageControl.lib”文件。

4)

     4.1) 如果您想使用 **ImageCtrl** 模块……

          4.1.1) 包含头文件“ImageCtrl.h”并在您的对话框类中声明一个 CImageCtrl 类型的变量。

#include "ImageCtrl.h"

class CImageControlDlg : public CDialogEx
{
public:
    CImageControlDlg(CWnd* pParent = NULL);    // standard constructor
    enum { IDD = IDD_IMAGECONTROL_DIALOG };

private:
    CImageCtrl m_image;
    HICON m_hIcon;

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    virtual BOOL OnInitDialog();
    afx_msg void OnCbnSelchangeComboLoadOption();
    afx_msg void OnCbnSelchangeComboResize();
    afx_msg void OnBnClickedButtonLoadimage();
    afx_msg void OnBnClickedButtonAbout();
    DECLARE_MESSAGE_MAP()    
};

          4.1.2) 子类化您的 CStatic 控件,它将用于显示图像。

void CImageControlDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_STATIC, m_image);
}

          4.1.3) 调用四个重载方法之一 m_image.load() 将图像加载到控件中。

     4.2) 如果您只想使用 **ResourceList 模块**,请点击这里

     4.3) 如果您只想使用 **Icon 模块**,请点击这里

 


文件“Source.zip”包含“StandardLibrary”静态库项目。


 

您可以在您的项目中这样使用此库:

1) 在“项目->属性->配置属性->C/C++->常规->附加包含目录”下
    添加适当的相对路径“..\StandardLibrary\inc”,以定位“ResizableDlg.h”。

2)  在“项目->属性->配置属性->链接器->常规->附加库目录”下
    添加适当的相对路径“..\StandardLibrary\x64\Release”,以定位 StandardLibrary.lib”文件。

3)  在“项目->属性->配置属性->链接器->输入->附加依赖项”下
    添加“StandardLibrary.lib”文件。

 4) 包含头文件“ResizableDlg.h”并将您的对话框类派生自 CResizableDlg

#include "ImageCtrl.h" 
#include "ResizableDlg.h"

class CImageControlDlg : public CResizableDlg
{
public:
    CImageControlDlg(CWnd* pParent = NULL);    // standard constructor
    enum { IDD = IDD_IMAGECONTROL_DIALOG };

private:
    CImageCtrl m_image;
    HICON m_hIcon;

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    virtual BOOL OnInitDialog();
    afx_msg void OnCbnSelchangeComboLoadOption();
    afx_msg void OnCbnSelchangeComboResize();
    afx_msg void OnBnClickedButtonLoadimage();
    afx_msg void OnBnClickedButtonAbout();
    DECLARE_MESSAGE_MAP()    
};

5) 像这样定义构造函数:

CImageControlDlg::CImageControlDlg(CWnd* pParent /*=NULL*/)
    : CResizableDlg(CImageControlDlg::IDD, 400, 490, pParent), m_pRes(NULL), m_isOnEnChangeEditWidthHeight(true)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON_OCEANWAVE); m_rl.setResourceType(CResourceList::resourceType::RESOURCE_IMAGE); // m_rl.setResourceType((UINT)RT_GROUP_ICON);
}

其中 400 表示最小对话框宽度,490 表示最小对话框高度。如果这些参数中的任何一个为 0,则最小对话框宽度/高度将设置为初始对话框宽度/高度值。

6) 在 OnInitDialog() 开始时,为每个需要重新定位和/或调整大小的对话框控件添加方法 CResizableDlg::moveResizeControl(...),然后在之后调用 CResizableDlg::OnInitDialog()

BOOL CImageControlDlg::OnInitDialog()
{
    moveResizeControl(IDC_STATIC_IMAGEFRAME, tControl::anchor::TOP_LEFT, 0, 0, 1, 1);
    moveResizeControl(IDC_STATIC_LOADOPTION, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_COMBO_LOADOPTION, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_GROUP_RESOURCE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0, true);
    moveResizeControl(IDC_STATIC_RESFILE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_EDIT_RESFILE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_BUTTON_RESFILE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_RESLOCALE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_COMBO_RESLOCALE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_RESTYPE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_COMBO_RESTYPE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_RESNAME, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_COMBO_RESNAME, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_BUTTON_LOADIMAGE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_GDIPLUS, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_GROUP_SIZEPOSITION, tControl::anchor::TOP_LEFT, 1, 0, 0, 0, true);
    moveResizeControl(IDC_STATIC_SIZETYPE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_COMBO_SIZETYPE, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_WIDTH, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_EDIT_WIDTH, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_WIDTHUNITS, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_HEIGHT, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_EDIT_HEIGHT, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_HEIGHTUNITS, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_CHECK_MAR, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_STATIC_ALIGNMENT, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_COMBO_ALIGNMENT, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_CHECK_PAN, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_CHECK_ZOOM, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_BUTTON_SAVEICONAS, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDC_BUTTON_SAVEAS, tControl::anchor::TOP_LEFT, 1, 0, 0, 0);
    moveResizeControl(IDOK, tControl::anchor::TOP_LEFT, 1, 1, 0, 0);
    moveResizeControl(IDC_BUTTON_ABOUT, tControl::anchor::TOP_LEFT, 1, 1, 0, 0);

    CResizableDlg::OnInitDialog();

    return TRUE;
}

 


文件“Demo.zip”包含 4 个项目:

  • [LIB]/ImageControl(静态库项目),
  • [LIB]/StandardLibrary(静态库项目),
  • [Resource]/ImageControlRes(仅资源 DLL,包含所有图像),
  • ImageControl Demo(演示项目)。

 如何使用它?

1) 解压“Demo.zip”文件。
2) 生成所有四个项目。
3) 运行应用程序“ImageControl Demo.exe”。请记住,所有图像文件都位于“[Resource]/ImageControlRes/res”文件夹下,并且仅资源文件“ImageControlRes.dll”位于“[Resource]/ImageControlRes/x64/Release”下。
 

历史记录

版本 1.5:发布于 2016-06-18

新增附加功能

  • 支持可调整大小的对话框,定义了最小对话框窗口尺寸并记住最后一个窗口位置。

          添加了“StandardLibrary”(静态库项目),支持在对话框大小调整时重新定位和/或调整对话框控件的大小。
          CImageControlDlg 现在派生自 CResizableDlg (CDialogEx),以
          支持这些可调整大小的对话框功能。
 

版本 1.4:发布于 2015-11-12

  • CImageCtrl 类中的图像对象 m_pImage 被发现是多余的,因此被移除,同时 Bitmap 对象 m_pBmp 取代了它的位置。

新增附加功能

  • 从任何图像创建自定义大小的 32 位图标,
  • 导出资源图像,
  • 平移和缩放资源图像。
     

版本 1.3:发布于 2015-10-28

  • 缩放操作已重新定义,精度更高。现在,即使鼠标位于图像矩形之外(但在图像客户区内)也可以执行。


版本 1.2:发布于 2015-10-24

新增附加功能

  • 导入“位图”资源,
  • 从任意多语言(资源)dll 或 exe 导入图像,
  • 自定义调整大小,
  • 平移,
  • 缩放,
  • 导出图像,
  • 提取资源图标。
     

版本 1.1 发布于 2015-10-08

  • CImageCtrl::DrawItem() 中的图像对象 image 已被替换为指向 Imagem_pImage 指针,并移至 CImageCtrl 类。现在它仅在每个图像周期中初始化一次 - 无论调用 CImageCtrl::DrawItem() 多少次!
  • 通过移除 Read()/Write() 调用优化了函数 load(CString szFilePath)
  • 通过移除 IStream 副本优化了函数 load(IStream* piStream)。                   


版本 1.0:发布于 2015-10-06

© . All rights reserved.