Table of contents
1. Introduction
2. The CImageCtrl class
3. PAN and ZOOM
4. GDI+
5. GDI: In-memory Device Context
6. Resources
6.1 Resource-Only DLL
6.2 ResourceList module
6.3 Icon module
6.4 Load image from resource DLL or EXE
7. Resizable Dialog
8. Background
9. References
10. Using the code
11. History
Introduction
This article describes Image Control (derived from CStatic), primary used to import and view following image formats: BMP, DIB, JPG, JPEG, JPE, JFIF, GIF, TIF, TIFF, PNG, ICO, WMF, EMF. What is more, there are also some other handy features integrated like Resize, Position, Pan, Zoom, Export Image and Extract Resource Icon.
I took the basics from Tobias Eiseler's solution1 and improved it even more for purposes of importing all mentioned image types from dll or exe resources with additional resizing and positioning (inside client area) option.
The goal was to make an advanced image control, where image can be obtained from one of these four sources:
1) Image file path,
2) Image IStream interface,
3) Image BYTE(unsigned char)
array,
4) Image DLL or EXE resource.
The CImageCtrl class
Now, have a quick look at CImageCtrl
class, which encapsulates this behaviour:
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; } double getLeft() { return m_left; } double getTop() { return m_top; } void setWidth(double width) { m_width = width; } double getWidth() { return m_width; } double getWidthOriginal() { return m_widthOriginal; } void setHeight(double height) { m_height = height; } double getHeight() { return m_height; } double getHeightOriginal() { return m_heightOriginal; }
void setMaintainAspectRatio(bool maintainAspectRatio)
{
m_maintainAspectRatio = maintainAspectRatio;
}
double setAspectRatio(double aspectRatio) { return m_aspectRatio = aspectRatio; }
double getAspectRatio() { return m_aspectRatio; } void setAllignmentType(int allignmentType) { m_allignmentType = allignmentType; }
void setPanMode(bool isPanMode) { m_isPanMode = isPanMode; } void setZoomMode(bool isZoomMode) { m_isZoomMode = isZoomMode; }
bool isImageShown() { return m_isImageShown > 0; } void update(); void erase();
BOOL load(CString szFilePath); BOOL load(IStream* piStream); BOOL load(BYTE* pData, size_t nSize);
BOOL load(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType, WORD wlan);
BOOL convert(CString pathName, CString imageType);
BOOL iconResourceToFile(CString resPathName, LPCTSTR lpName, WORD wlan, CString icoPathName);
private:
void release(bool calcFrameAspectRatio = true);
int m_sizeType; double m_left; double m_top; double m_width; double m_height; double m_widthOriginal; double m_heightOriginal; bool m_maintainAspectRatio; double m_aspectRatio; int m_allignmentType; bool m_isPanMode; bool m_isZoomMode; double m_zoomMin; double m_zoomMax; BOOL m_isImageShown;
BOOL m_isInitialShow; CPoint m_panAtPt; CPoint m_panOffset; CPoint m_zoomAtPt; double m_zoomFactor;
+ Bitmap* m_pBmp; + ULONG_PTR m_gdiplusToken;
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()
};
PAN and ZOOM
At first, you have to enable PAN or ZOOM mode by selecting appropriate checkbox on dialog window.
What PAN is and how it works?
Click left mouse button, while mouse over image client area, then move mouse and finally release left mouse button. The result is image displaced by "mouse-move" vector.
What ZOOM is and how it works?
Position mouse over the image (or at least inside image client area). Use mouse middle (wheel) button to zoom in or zoom out. The result is scaled image with mouse position point fixed.
GDI+
The technology used for viewing images is GDI+, which was first introduced in Windows XP and Windows Server 2003 (GDI+ functionality is denoted by plus character at the start of the code line).
+
I initialized it in CimageCtrl
constructor like this:
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;
}
and the clean up was made in destructor like this:
CImageCtrl::~CImageCtrl(void)
{
+ release(false); GdiplusShutdown(m_gdiplusToken);
}
The variable CImageCtrl::m_pBmp
is pointer to the Bitmap, which was previously obtained by one of four CImageCtrl::load()
methods, and the CImageCtrl::DrawItem()
procedure does the OnPaint()
"job", which is triggered by CImageCtrl::Invalidate()
and CImageCtrl::UpdateWindow()
methods.
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)
{
dcMem.CreateCompatibleDC(pDC);
GetClientRect(rect); bmpMem.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
oldBmp = dcMem.SelectObject(&bmpMem);
+ Graphics graphics(dcMem.m_hDC);
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; } else if (m_zoomFactor > 1e-6) {
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: In-memory Device Context
This technique was used to eliminate "flickering" effect during PAN or ZOOM action. The trick is to create in-memory device context (which is compatible with painting device context), where all the painting is done. At the end, you just copy a portion of in-memory device context to the painting device context. This was achieved by following statements.
CDC *pDC = NULL, dcMem; CBitmap bmpMem, *oldBmp = NULL;
pDC = GetDC();
dcMem.CreateCompatibleDC(pDC);
GetClientRect(rect); bmpMem.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
oldBmp = dcMem.SelectObject(&bmpMem);
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
dcMem.SelectObject(oldBmp); bmpMem.DeleteObject(); dcMem.DeleteDC(); ReleaseDC(pDC);
If you would like to learn more about working with device contexts, here 2 is an excellent article by Marius Bancila.
Resource-Only DLL
I have decided to create a resource-only DLL3 (containing all images) in order to test "Load image from Resource DLL or EXE" option.
Resource-only DLL is a DLL that contains nothing but resources, such as images, strings, dialog boxes,...
How can you create it in Microsoft Visual Studio?
1) Create "New Project->WIn32 Project->DLL" application type.
2) Select "Project->Properties->Configuration Properties->Linker->Advanced->No Entry Point" and set option
"Yes (/NOENTRY)".
3) Add resources.
I added images of type BMP, DIB, JPG, JPEG, JPE, JFIF, GIF, TIF, TIFF, PNG, ICO, WMF, EMF:
*.ico image under "Icon" resource type,
*.bmp images under "Bitmap" and "RCDATA" resource type,
*.jpg image under "JPG" custom resource type,
*.png image under "PNG" custom resource type, etc.
ResourceList module
ResourceList module, containing "ResourceList.h
" and "ResourceList.cpp
" (project ImageControl
), was implemented to list ALL resources, defined by resource language, resource type and resource name. I would like to highlight, that resource type and name could be string or integer string (for example MAKEINTRESOURCE(
153)
, RT_BITMAP
).
And how to get resources that might be image-type?
#include "ResourceList.h"
CResourceList rl; HMODULE hModule;
CArray<tResourceLCID*> *pRes;
if(hModule = LoadLibrary(_T("...")))
{
rl.setResourceType(CResourceList::resourceType::RESOURCE_IMAGE);
pRes = rl.getResourceList(hModule);
FreeLibrary(hModule);
}
Icon module
Icon module, containing "Icon.h
" and "Icon.cpp
" (project ImageControl
), was developed to provide importIcon()
(from resource module or *.ico file) and exportIcon()
(to *.ico file) methods.
And how to load resource icon and export it to *.ico file?
#include "Icon.h"
CIconExtractor ie; HMODULE hModule;
LPCTSTR lpName = ...; WORD wlan = ...; CString icoPathName = _T("...");
if(hModule = LoadLibrary(_T("...")))
{
if(ie.importIcon(hModule, lpName, wlan)) ie.exportIcon(icoPathName);
FreeLibrary(hModule);
}
Load image from a resource DLL or EXE
Using CImageCtrl
class, you can accomplish that in 3 steps.
1) Load appropriate resource dll or exe by calling function hModule = LoadLibrary()
.
2) Call m_image.load(hModule, MAKEINTRESOURCE(.), ., .)
.
3) Unload the resource by calling FreeLibrary(hModule)
.
This is realized in CImageControlDlg::OnCbnSelchangeComboResname()
event.
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(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());
}
Remark:
Pay attention on loading icon. The resource type is RT_GROUP_ICON. Icon resource can contain multiple images, each of them of type RT_ICON.
Now, let us have a look at CImageCtrl::load(hModule, MAKEINTRESOURCE(.), ., .)
method more precisely. Basically, the code is divided into two branches based on resource type (non-icon and icon resource).
1) "Non-icon resource" passes this code snippet.
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)
"Icon resource" is more complicated.
Here4 you can find out, that icon resource (RT_GROUP_ICON) is simply GRPICONDIR structure, defined below.
typedef struct
{
WORD idReserved; WORD idType; WORD idCount; GRPICONDIRENTRY idEntries[1]; } GRPICONDIR, *LPGRPICONDIR;
Meanwhile, all icon images, which are of type RT_ICON, are located in the array of GRPICONDIRENTRY structures.
typedef struct
{
BYTE bWidth; BYTE bHeight; BYTE bColorCount; BYTE bReserved; WORD wPlanes; WORD wBitCount; DWORD dwBytesInRes; WORD nID; } GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
In "code language" that would require these additional code lines.
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)))
{
FreeResource(hGlobal3);
}
UnlockResource(hGlobal);
}
Resizable Dialog
We would like to define appropriate actions, when dialog is resized, like repositioning and/or resizing dialog controls. For this purposes "StandardLibrary" (static library project) with module "ResizableDlg" was developed.
One approach is to describe these actions relatively to dialog size change from anchor point. If we would like to perform only repositions, then one anchor point would be enough to describe all possible repositions (let it be top-left dialog point). Resize action is more tricky. If we observe resize in x-direction, it could be done with left edge fixed and right edge moved or vice versa. There is similar case with resize in y-direction. But with anchor point top-left,the corresponding resize in x-direction would be to move right edge and in y-direction would be to move bottom edge. Similary we can define resize actions in both directions for remaining three anchor points: bottom-left, top-right, bottom-right.
There are four basic actions relative to dialog size change:
- repositioning in x-direction (parameter
dxMoveRelative
),
- repositioning in y-direction (parameter
dyMoveRelative
),
- resize in x-direction (parameter
dxResizeRelative
),
- resize in y-direction (parameter
dyResizeRelative
).
All four actions can be described by a parameter from interval [0,1]. 0 means no action, 1 means action in x- or y-direction for the same ammount as dialog's x- or y-direction change, 0.3 means action in x- or y-direction for the 30% of the ammount ...
It is a good idea to be
dxMoveRelative + dxResizeRelative <= 1
and dyMoveRelative + dyResizeRelative <= 1
,
otherwise the control could leave the dialog window area, which is not supposed to happen.
If we choose all four parameters to be zero, then control is anchored at fixed x- and y-distance from anchor point.
Finally, there is a method CResizableDlg::moveResizeControl(...)
in "ResizableDlg" module with parameters:
int nID
(control ID),
int anchor
(anchor point: TOP_LEFT
, BOTTOM_LEFT
, TOP_RIGHT
, BOTTOM_RIGHT
),
double dxMoveRelative
(repositioning in x-direction),
double dyMoveRelative
(repositioning in y-direction),
double dxResizeRelative
(resize in x-direction),
double dyResizeRelative
(resize in y-direction),
bool isTransparent
(true
for group boxes),
which defines actions to perform on control at dialog resize.
If you would like to use ResizableDlg module, proceed here.
Background
- GDI+ (Graphics, Bitmap, Metafile)
- GDI (In-memory Device Context, Client Device Context)
- Resource-Only DLL
- Resizable Dialog
References
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
Using the code
File "Source.zip" contains "ImageControl" static library project.
You can use this library in your project as follows.
1) Under "Project->Properties->Configuration Properties->C/C++->General->Additional Include Directories"
add appropriate relative path "..\ImageControl\inc" , targeting "ImageCtrl.h", "ResourceList.h" and "Icon.h" file.
2) Under "Project->Properties->Configuration Properties->Linker->General->Additional Library Directories"
add appropriate relative path "..\ImageControl\x64\Release", targeting "ImageControl.lib" file.
3) Under "Project->Properties->Configuration Properties->Linker->Input->Additional Dependencies"
add "ImageControl.lib" file.
4)
4.1) If you would like to use ImageCtrl module ...
4.1.1) Include header file "ImageCtrl.h" and declare a variable of type CImageCtrl
in your dialog class.
#include "ImageCtrl.h"
class CImageControlDlg : public CDialogEx
{
public:
CImageControlDlg(CWnd* pParent = NULL); enum { IDD = IDD_IMAGECONTROL_DIALOG };
private:
CImageCtrl m_image;
HICON m_hIcon;
protected:
virtual void DoDataExchange(CDataExchange* pDX); 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) Subclass your CStatic control, which will hold the image.
void CImageControlDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATIC, m_image);
}
4.1.3) Call one of four overloaded methods m_image.load()
to load image into control.
4.2) If you would like to use only ResourceList module, proceed here.
4.3) If you would like to use only Icon module, proceed here.
File "Source.zip" contains "StandardLibrary" static library project.
You can use this library in your project as follows.
1) Under "Project->Properties->Configuration Properties->C/C++->General->Additional Include Directories"
add appropriate relative path "..\StandardLibrary\inc" , targeting "ResizableDlg.h".
2) Under "Project->Properties->Configuration Properties->Linker->General->Additional Library Directories"
add appropriate relative path "..\StandardLibrary\x64\Release", targeting "StandardLibrary.lib" file.
3) Under "Project->Properties->Configuration Properties->Linker->Input->Additional Dependencies"
add "StandardLibrary.lib" file.
4) Include header file "ResizableDlg.h" and derive your dialog class from CResizableDlg
.
#include "ImageCtrl.h"
#include "ResizableDlg.h"
class CImageControlDlg : public CResizableDlg
{
public:
CImageControlDlg(CWnd* pParent = NULL); enum { IDD = IDD_IMAGECONTROL_DIALOG };
private:
CImageCtrl m_image;
HICON m_hIcon;
protected:
virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog();
afx_msg void OnCbnSelchangeComboLoadOption();
afx_msg void OnCbnSelchangeComboResize();
afx_msg void OnBnClickedButtonLoadimage();
afx_msg void OnBnClickedButtonAbout();
DECLARE_MESSAGE_MAP()
};
5) Define constructor like this:
CImageControlDlg::CImageControlDlg(CWnd* pParent )
: 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); }
where 400 means minimal dialog width and 490 minimal dialog height. If any of these parameters is 0, the minimal dialog width/height is set to initial dialog width/height values.
6) Add method CResizableDlg::
moveResizeControl(...)
for each dialog control, which should be repositioned and/or resized, at the beginning of OnInitDialog()
and call CResizableDlg::OnInitDialog()
afterwards.
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;
}
File "Demo.zip" contains 4 projects:
- [LIB]/ImageControl (static library project),
- [LIB]/StandardLibrary (static library project),
- [Resource]/ImageControlRes (resource-only DLL, containing all images),
- ImageControl Demo (demonstrative project).
How to use it?
1) Unzip "Demo.zip" file.
2) Build all four projects.
3) Run the application "ImageControl Demo.exe". Remember that all image files are located under "[Resource]/ImageControlRes/res" folder and in resource-only "ImageControlRes.dll" under "[Resource]/ImageControlRes/x64/Release".
History
Version 1.5: Released on 2016-06-18
New additional feature:
- Resizable dialog support with minimal dialog window size defined and last window placement remembered.
Added "StandardLibrary" (static library project), which supports repositioning and/or resizing dialog controls
on dialog resize. CImageControlDlg
is now derived from CResizableDlg (CDialogEx)
in order to
support these resizable dialog features.
Version 1.4: Released on 2015-11-12
- Image object
m_pImage
from CImageCtrl
class has been found redundant and therefore removed, meanwhile Bitmap object m_pBmp
took over its place.
New additional features:
- Create custom-sized 32-bit icon from any image,
- Export resource images,
- PAN and ZOOM resource images.
Version 1.3: Released on 2015-10-28
- Zoom operation has been redefined with higher rate of accurancy. Now, it is also possible to execute it, if mouse positioned outside image rectangle (but inside image client area).
Version 1.2: Released on 2015-10-24
New additional features:
- Import "Bitmap" resource,
- Import images from arbitrary multi-language (resource) dll or exe,
- Custom Resize,
- Pan,
- Zoom,
- Export Image,
- Extract Resource Icon.
Version 1.1 Released on 2015-10-08
- Image object
image
from CImageCtrl::DrawItem()
has been replaced by m_pImage
pointer to Image and moved to CImageCtrl
class. Now it is initialized only once in each image cycle - no matter how many times CImageCtrl::DrawItem()
is called!
- Function
load(CString szFilePath)
has been optimized by removing Read()/Write()
calls.
- Function
load(IStream* piStream)
has been optimized by removing IStream-copy.
Version 1.0: Released on 2015-10-06