Lindenmayer System
A Simple L-system Image Generator Featuring Turtle Graphics
WindowsHelpers.cpp
Go to the documentation of this file.
1 
7 // MIT License
8 //
9 // Copyright (c) 2020 Ian Parberry
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to
13 // deal in the Software without restriction, including without limitation the
14 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
15 // sell copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
27 // IN THE SOFTWARE.
28 
29 #include <shobjidl_core.h>
30 #include <atlbase.h>
31 
32 #include "WindowsHelpers.h"
33 #include "resource.h"
34 
35 #include "Includes.h"
36 
38 // Initialization functions
39 
40 #pragma region Initialization
41 
46 
47 void InitWindow(HINSTANCE hInst, INT nShow, WNDPROC WndProc){
48  const char appname[] = "Lindenmayer";
49 
50  WNDCLASSEX wndClass = {0}; //extended window class structure
51 
52  wndClass.cbSize = sizeof(WNDCLASSEX);
53  wndClass.style = CS_HREDRAW | CS_VREDRAW;
54  wndClass.lpfnWndProc = WndProc;
55  wndClass.cbClsExtra = 0;
56  wndClass.cbWndExtra = 0;
57  wndClass.hInstance = hInst;
58  wndClass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1));
59  wndClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
60  wndClass.hbrBackground = (HBRUSH)GetStockObject(COLOR_WINDOW+1);
61  wndClass.lpszMenuName = nullptr;
62  wndClass.lpszClassName = appname;
63  wndClass.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON2));
64 
65  RegisterClassEx(&wndClass);
66 
67  const DWORD dwStyle = WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME | WS_SYSMENU;
68  const DWORD dwStyleEx = WS_EX_APPWINDOW | WS_EX_DLGMODALFRAME;
69 
70  const int w = 600; //window client area width.
71  const int h = 600; //window client area height.
72 
73  RECT r;
74  r.left = 0; r.right = w;
75  r.top = 0; r.bottom = h + GetSystemMetrics(SM_CYMENU);
76  AdjustWindowRectEx(&r, dwStyle, FALSE, dwStyleEx);
77 
78  const HWND hwnd = CreateWindowEx(dwStyleEx, appname, appname, dwStyle,
79  CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top,
80  nullptr, nullptr, hInst, nullptr);
81 
82  ShowWindow(hwnd, nShow);
83  UpdateWindow(hwnd);
84 } //InitWindow
85 
88 
89 ULONG_PTR InitGDIPlus(){
90  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
91  ULONG_PTR gdiplusToken;
92  Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
93  return gdiplusToken;
94 } //InitGDIPlus
95 
96 #pragma endregion Initialization
97 
99 // Rectangle functions
100 
101 #pragma region Rectangles
102 
109 
110 void AddPointToRect(RECT& r, Gdiplus::PointF point){
111  r.left = min(r.left, int(std::floor(point.X)));
112  r.right = max(r.right, int(std::ceil (point.X)));
113 
114  r.top = min(r.top, int(std::floor(point.Y)));
115  r.bottom = max(r.bottom, int(std::ceil (point.Y)));
116 } //AddPointToRect
117 
121 
122 Gdiplus::RectF GetClientRectF(HWND hwnd){
123  RECT r; //for client rectangle
124  GetClientRect(hwnd, &r); //get client rectangle
125 
126  return Gdiplus::RectF((float)r.left, (float)r.top,
127  (float)r.right - r.left, (float)r.bottom - r.top); //rock it
128 } //GetClientRectF
129 
136 
137 void MinDragRect(HWND hwnd, WPARAM wParam, RECT* pRect, int n){
138  RECT cr; //client rectangle
139  RECT wr; //window rectangle, includes client rectangle and borders
140 
141  GetClientRect(hwnd, &cr);
142  GetWindowRect(hwnd, &wr);
143 
144  //combined border width and height
145 
146  const int bw = (wr.right - wr.left) - (cr.right - cr.left); //border width
147  const int bh = (wr.bottom - wr.top) - (cr.bottom - cr.top); //border height
148 
149  //new drag window width and height
150 
151  const int dw = max(n, pRect->right - pRect->left - bw) + bw; //new width
152  const int dh = max(n, pRect->bottom - pRect->top - bh) + bh; //new height
153 
154  //enforce new drag window width and height
155 
156  switch(wParam){ //which edge are we dragging on?
157  case WMSZ_LEFT: //left edge
158  pRect->left = pRect->right - dw;
159  break;
160 
161  case WMSZ_RIGHT: //right edge
162  pRect->right = pRect->left + dw;
163  break;
164 
165  case WMSZ_TOP: //top edge
166  pRect->top = pRect->bottom - dh;
167  break;
168 
169  case WMSZ_BOTTOM: //bottom edge
170  pRect->bottom = pRect->top + dh;
171  break;
172 
173  case WMSZ_TOPRIGHT: //top right corner
174  pRect->top = pRect->bottom - dh;
175  pRect->right = pRect->left + dw;
176  break;
177 
178  case WMSZ_TOPLEFT: //top left corner
179  pRect->top = pRect->bottom - dh;
180  pRect->left = pRect->right - dw;
181  break;
182 
183  case WMSZ_BOTTOMRIGHT: //bottom right corner
184  pRect->bottom = pRect->top + dh;
185  pRect->right = pRect->left + dw;
186  break;
187 
188  case WMSZ_BOTTOMLEFT: //bottom left corner
189  pRect->bottom = pRect->top + dh;
190  pRect->left = pRect->right - dw;
191  break;
192  } //switch
193 } //MinDragRect
194 
195 #pragma endregion Rectangles
196 
198 // Save functions
199 
200 #pragma region Save
201 
206 
207 HRESULT GetEncoderClsid(const WCHAR* format, CLSID* pClsid){
208  UINT num = 0; //number of image encoders
209  UINT n = 0; //size of the image encoder array in bytes
210  HRESULT hr = E_FAIL; //return result
211 
212  Gdiplus::ImageCodecInfo* pCodecInfo = nullptr; //for codec info
213  if(FAILED(Gdiplus::GetImageEncodersSize(&num, &n)))return E_FAIL; //get sizes
214  if(n == 0)return E_FAIL; //there are no encoders
215 
216  pCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(n)); //allocate codec info memory
217  if(pCodecInfo == nullptr)return E_FAIL; //malloc failed (as if)
218  if(FAILED(GetImageEncoders(num, n, pCodecInfo)))return E_FAIL; //get encoders
219 
220  for(UINT j=0; j<num && hr!=S_OK; j++) //for each encoder, while not found
221  if(wcscmp(pCodecInfo[j].MimeType, format) == 0){ //found the codex we want
222  *pClsid = pCodecInfo[j].Clsid; //return it
223  hr = S_OK; //success
224  } //if
225 
226  free(pCodecInfo); //clean up
227  return hr;
228 } //GetEncoderClsid
229 
240 
241 HRESULT SaveBitmap(HWND hwnd, Gdiplus::Bitmap* pBitmap){
242  COMDLG_FILTERSPEC filetypes[] = { //png files only
243  {L"PNG Files", L"*.png"}
244  }; //filetypes
245 
246  std::wstring wstrFileName; //result
247  CComPtr<IFileSaveDialog> pDlg; //pointer to save dialog box
248  static int n = 0; //number of images saved in this run
249  std::wstring wstrName = L"Image" + std::to_wstring(n++); //default file name
250  CComPtr<IShellItem> pItem; //item pointer
251  LPWSTR pwsz = nullptr; //pointer to null-terminated wide string for result
252 
253  //fire up the save dialog box
254 
255  if(FAILED(pDlg.CoCreateInstance(__uuidof(FileSaveDialog))))return E_FAIL;
256 
257  pDlg->SetFileTypes(_countof(filetypes), filetypes); //set file types to png
258  pDlg->SetTitle(L"Save Image"); //set title bar text
259  pDlg->SetFileName(wstrName.c_str()); //set default file name
260  pDlg->SetDefaultExtension(L"png"); //set default extension
261 
262  if(FAILED(pDlg->Show(hwnd)))return E_FAIL; //show the dialog box
263  if(FAILED(pDlg->GetResult(&pItem)))return E_FAIL; //get the result item
264  if(FAILED(pItem->GetDisplayName(SIGDN_FILESYSPATH, &pwsz)))return E_FAIL; //get file name
265 
266  //wstrFileName should now contain the selected file name
267 
268  CLSID clsid; //for PNG class id
269  if(FAILED(GetEncoderClsid((WCHAR*)L"image/png", &clsid)))return E_FAIL; //get
270  pBitmap->Save(pwsz, &clsid, nullptr); //the actual save happens here
271 
272  CoTaskMemFree(pwsz); //clean up
273 
274  return S_OK;
275 } //SaveBitmap
276 
277 #pragma endregion Save
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Window procedure.
Definition: Main.cpp:41
void InitWindow(HINSTANCE hInst, INT nShow, WNDPROC WndProc)
Initialize window.
Useful includes.
void MinDragRect(HWND hwnd, WPARAM wParam, RECT *pRect, int n)
Enforce minimum drag rectangle.
HRESULT SaveBitmap(HWND hwnd, Gdiplus::Bitmap *pBitmap)
Save bitmap to file.
void AddPointToRect(RECT &r, Gdiplus::PointF point)
Add point to rectangle.
Interface for some helpful Windows-specific functions.
HRESULT GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
ULONG_PTR InitGDIPlus()
Initialize GDI+.
Gdiplus::RectF GetClientRectF(HWND hwnd)
Get client rectangle as a RectF.