Scope
ChannelFrame.cpp
1 #include "stdafx.h"
2 #include "ChannelFrame.h"
3 #include "ChannelView.h"
4 #include "helpers/ScopeMultiImage.h"
5 #include "helpers/ScopeMultiImageResonanceSW.h"
6 #include "direct2d/D2ChannelRender.h"
7 #include "helpers/Lut.h"
8 #include "controllers/ScopeController.h"
9 #include "controls/ScopeColorComboCtrl.h"
10 #include "resource.h"
11 
12 namespace scope {
13  namespace gui {
14 
15 std::array<int32_t, 4> CChannelFrame::colorcombo_resources = { IDC_COLORCOMBO1, IDC_COLORCOMBO2, IDC_COLORCOMBO3, IDC_COLORCOMBO4 };
16 
17 CChannelFrame::CChannelFrame(const uint32_t& _area)
18  : area(_area)
19  , channels(scope_controller.GuiParameters.areas[_area]->daq.inputs->channels())
20  , attached(false)
21  , current_frame(std::make_shared<scope::SCOPE_MULTIIMAGE_T>(_area, channels, scope_controller.GuiParameters.areas[_area]->Currentframe().yres(), scope_controller.GuiParameters.areas[_area]->Currentframe().xres()))
22  , framecountstr(L"Frame ")
23  , mousepos(D2D1::Point2F(0.0f, 0.0f))
24  , mouseposstr(L"(0, 0)")
25  , statusstr(L"Stopped")
26  , channel_colors(channels, None)
27  , overlay(scope_controller.GuiParameters.areas[_area]->Currentframe().yres(), scope_controller.GuiParameters.areas[_area]->Currentframe().xres()) {
28 }
29 
31  // If we land here through an exception, OnDestroy is eventually not called thus we need to detach here to avoid
32  // an invalid pointer in the DisplayController
33  if ( attached ) {
34  try {
35  scope_controller.DetachFrame(this);
36  } catch (...) { ScopeExceptionHandler(__FUNCTION__); }
37  attached = false;
38  }
39  // We have to stop the Active's worker thread before member destruction starts, since stuff executed in that thread could want to access CChannelFrame member
40  // and these get destroyed before the Active and its thread gets destroyed!
41  Quit();
42 }
43 
44 void CChannelFrame::OnFinalMessage(HWND /*hWnd*/) {
45  delete this;
46 }
47 
49  // try-catch, since DetachFrame could throw if DisplayControllerImpl::DetachFrame(gui::CChannelFrame* const) does
50  // not find this CChannelFrame in its list
51  try {
52  scope_controller.DetachFrame(this);
53  } catch (...) { ScopeExceptionHandler(__FUNCTION__); }
54  attached = false;
55 }
56 
58  UIUpdateToolBar();
59  //Send(std::bind(&CChannelFrame::RunUpdateStatusbar, this, std::placeholders::_1));
60  return FALSE;
61 }
62 
63 bool CChannelFrame::RunLayOverAndRender(StopCondition* const sc, scope::SCOPE_MULTIIMAGECPTR_T const _multi) {
64  // Resize the renderer or the overlay if necessary
65  if ( (_multi->Linewidth() != view.GetRendererSize().width) || (_multi->Lines() != view.GetRendererSize().height) ) {
66  view.ResizeContent(_multi->Linewidth(), _multi->Lines());
67  mousepos.x = 0; // to be safe not to request a pixel value outside of the image
68  mousepos.y = 0;
69  // Also update the scale text
71  }
72  if ( (_multi->Linewidth() != overlay.Linewidth()) || (_multi->Lines() != overlay.Lines()) )
73  overlay.Resize(_multi->Lines(), _multi->Linewidth());
74 
75  overlay.Create(_multi, channel_colors);
76  overlay.ToD2Bitmap(view.GetBitmap());
77  view.Render();
78 
79  return true;
80 }
81 
82 bool CChannelFrame::RunUpdateStatusbar(StopCondition* const sc, scope::SCOPE_MULTIIMAGECPTR_T _multi) {
83  UISetText(0, statusstr.c_str());
84 
85  // Do not update mousepos at 0,0 (it is set to this values if the cursor leaves the window)
86  if ( (mousepos.x != 0.0) && (mousepos.y != 0.0) ) {
87  const uint32_t xui = static_cast<uint32_t>(std::floor(mousepos.x));
88  const uint32_t yui = static_cast<uint32_t>(std::floor(mousepos.y));
89  const std::vector<uint16_t> multipix(current_frame->GetMultiPixel(xui, yui));
90  std::wstringstream stream;
91  stream << L"(" << xui << L", " << yui << L") = (";
92  for ( const auto& pix : multipix )
93  stream << pix << L", ";
94  mouseposstr = stream.str();
95  mouseposstr.erase(std::end(mouseposstr)-2, std::end(mouseposstr)-1); // Delete trailing ", "
96  mouseposstr.append(L")"); // and replace with ")"
97  UISetText(1, mouseposstr.c_str());
98  }
99 
100  std::wostringstream stream;
101  stream << L"Frame " << current_frame->GetImageNumber() << L" (" << std::setprecision(1) << std::fixed << current_frame->PercentComplete();
102  stream << L" %, avg " << current_frame->GetAvgCount() << L"/" << current_frame->GetAvgMax() << L")";
103  framecountstr = stream.str();
104  UISetText(2, framecountstr.c_str());
105 
106  UIUpdateStatusBar();
107  return true;
108 }
109 
111  std::wostringstream stream;
112  stream << std::setprecision(1) << std::fixed;
113  stream << L"FOV " << std::setw(2) << scope_controller.GuiParameters.areas[area]->micronperpixelx() * scope_controller.GuiParameters.areas[area]->Currentframe().xres();
114  stream << L"µm x " << std::setw(2) << scope_controller.GuiParameters.areas[area]->micronperpixely()*scope_controller.GuiParameters.areas[area]->Currentframe().yres();
115  stream << L"µm (Offset " << std::setw(2) << scope_controller.GuiParameters.areas[area]->XOffsetInMicron();
116  stream << L"µm, " << std::setw(2) << scope_controller.GuiParameters.areas[area]->YOffsetInMicron() << L"µm)";
117  view.UpdateScaleText(stream.str());
118 }
119 
121  toolbar.CheckButton(IDC_SAMESIZE, FALSE);
122  toolbar.CheckButton(IDC_DOUBLESIZE, FALSE);
123  toolbar.CheckButton(IDC_HALFSIZE, FALSE);
124 }
125 
126 int CChannelFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
127  m_hWndClient = view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);
128 
129  SetMenu(HMENU(NULL));
130 
131  toolbar = CreateSimpleToolBarCtrl(m_hWnd, IDR_CHANNELFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE_EX);
132  CreateSimpleReBar(ATL_SIMPLE_REBAR_STYLE);
133  AddSimpleReBarBand(toolbar);
134  UIAddToolBar(toolbar);
135 
136  // Add buttons texts
137  AddToolbarButtonText(toolbar, IDC_SAMESIZE, L"x1");
138  AddToolbarButtonText(toolbar, IDC_DOUBLESIZE, L"x2");
139  AddToolbarButtonText(toolbar, IDC_HALFSIZE, L"x0.5");
140 
141  // Create toolbar combo box for every channel
142  assert(channels <= 4); // resource IDs for more than 4 channels not implemented, but easy to add if you need to
143  for ( uint32_t ch = 0 ; ch < channels ; ch++ ) {
144  CScopeColorComboCtrl combo;
145  combo.m_hWnd = CreateToolbarComboBoxEx(toolbar, colorcombo_resources[ch], 5);
146  combo.InitializeColors(); // Load color icons and text
147 
148  if ( ch == 0 )
149  combo.SetCurSel(1); // First channel gray
150  else
151  combo.SetCurSel(0); // Other channels 'None'
152  }
153  // First channel is gray
154  channel_colors.at(0).SetColor(Gray);
155 
156  //CreateSimpleStatusBar();
157  m_hWndStatusBar = m_wndStatusBar.Create(*this);
158  UIAddStatusBar (m_hWndStatusBar);
159  // Create the status bar panes.
160  int32_t anPanes[] = { IDPANE_STATUS , IDPANE_PIXELINFO, IDPANE_FRAMECOUNT };
161 
162  m_wndStatusBar.SetPanes ( anPanes, 3, false );
163  UISetText(0, statusstr.c_str());
164  //m_wndStatusBar.SetPaneText(0, L"Stopped");
165  UISetText(1, mouseposstr.c_str());
166  UISetText(2, framecountstr.c_str());
167  UIUpdateStatusBar();
168 
169  UIUpdateToolBar();
170  UpdateLayout();
171 
172  std::wostringstream stream;
173  stream << L"Image (Area " << area+1 << L")";
174  SetWindowText(stream.str().c_str());
175 
176  scope_controller.AttachFrame(this);
177  attached = true;
178 
179  RECT Rect; // Size correctly, regardless what parent window said
180  GetWindowRect(&Rect);
181  const double AspectRatio = static_cast<double>(scope_controller.GuiParameters.areas[area]->Currentframe().xres()) / static_cast<double>(scope_controller.GuiParameters.areas[area]->Currentframe().yres());
182  const double width = Rect.right-Rect.left; // Do no subtract 20 or 92 here, since GetWindowRect is without borders!
183  const double height = Rect.bottom - Rect.top;
184  const int32_t targetHeight = round2i32(width / AspectRatio)+92; // But add here, since MoveWindow wants total window size
185  const int32_t targetWidth = round2i32(height * AspectRatio)+20; // There must be a nicer way but I do not know enough Win32
186  MoveWindow(Rect.left, Rect.top, targetWidth, targetHeight);
187 
188  // Resize the bitmap in CChannelView's D2ChannelRender (because it is initially 0x0)
189  view.ResizeContent(scope_controller.GuiParameters.areas[area]->Currentframe().xres(), scope_controller.GuiParameters.areas[area]->Currentframe().yres());
190 
191  // Demagnify large frames
192  if ( scope_controller.GuiParameters.areas[area]->Currentframe().xres() >= 1024 )
193  OnHalfSize(0, 0, NULL);
194  else
195  OnSameSize(0, 0, NULL);
196 
197  UpdateScaleText();
198 
199  return 1;
200 }
201 
203 void CChannelFrame::OnToolBarCombo(HWND hWndCombo, UINT nID, int nSel, LPCTSTR lpszText, DWORD_PTR dwItemData) {
204  uint32_t selchannel = 0;
205  // Check which channel was clicked
206  for ( auto& cc : colorcombo_resources ) {
207  if (nID == cc) {
208  // Set which color was selected
209  channel_colors.at(selchannel).SetColor(ColorEnum(nSel));
210  DBOUT(L"Channel " << selchannel << L" selected color " << ColorEnum(nSel) << L" String: " << gColorStrings[nSel].GetString());
211  }
212  selchannel++;
213  }
214 }
215 
216 void CChannelFrame::OnSizing(UINT fwSide, LPRECT pRect) {
217  const double width = pRect->right - pRect->left - 20;
218  const double height = pRect->bottom - pRect->top - 92;
219  const double AspectRatio = static_cast<double>(scope_controller.GuiParameters.areas[area]->Currentframe().xres())
220  / static_cast<double>(scope_controller.GuiParameters.areas[area]->Currentframe().yres());
221  if (fwSide == WMSZ_LEFT || fwSide == WMSZ_RIGHT || fwSide == WMSZ_RIGHT + WMSZ_BOTTOM || fwSide == WMSZ_LEFT + WMSZ_BOTTOM ) {
222  const int targetHeight = round2ui32(width / AspectRatio) + 92; // Here we have to add the 92 again
223  pRect->bottom = pRect->top + targetHeight;
224  }
225  else if (fwSide == WMSZ_TOP || fwSide == WMSZ_BOTTOM || fwSide == WMSZ_LEFT + WMSZ_TOP || fwSide == WMSZ_RIGHT + WMSZ_TOP ) {
226  const int targetWidth = round2ui32(height * AspectRatio) + 20;
227  pRect->right = pRect->left + targetWidth;
228  }
229 }
230 
231 LRESULT CChannelFrame::OnUpdateMousePixel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
232  D2D1_POINT_2F dips(d2d::DPIScale::PixelsToDips(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
233  const D2D1_SIZE_F size(view.GetRendererSize());
234  dips.x = dips.x / size.width * current_frame->Linewidth();
235  dips.y = dips.y / size.height * current_frame->Lines();
236  mousepos = dips;
237  Send(std::bind(&CChannelFrame::RunUpdateStatusbar, this, std::placeholders::_1, current_frame));
238  bHandled = true;
239  return 0;
240 }
241 
242 BOOL CChannelFrame::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) {
243  if ( nFlags == MK_CONTROL )
244  scope_controller.GuiParameters.areas[area]->Currentframe().pockels += (zDelta/120) * 0.1;
245 
246  if ( nFlags == NULL ) {
247  const double oldzoom = scope_controller.GuiParameters.areas[area]->Currentframe().zoom();
248  scope_controller.GuiParameters.areas[area]->Currentframe().zoom.Set(oldzoom + (zDelta/120)*0.1, true, false); // Wait with update until offset set
249  scope_controller.GuiParameters.areas[area]->Currentframe().CoerceOffset(); // Force new offset limits
250 
251  const double offx = (2* mousepos.x / current_frame->Linewidth() - 1) / scope_controller.GuiParameters.areas[area]->Currentframe().zoom();
252  const double offy = (2* mousepos.y / current_frame->Lines() - 1) / scope_controller.GuiParameters.areas[area]->Currentframe().zoom();
253  scope_controller.GuiParameters.areas[area]->Currentframe().xoffset.Set(offx, true, false);
254  scope_controller.GuiParameters.areas[area]->Currentframe().yoffset.Set(offy, true, true); // Now request update
255  }
256 
257  return false;
258 }
259 
260 void CChannelFrame::OnSameSize(UINT uCode, int nID, HWND hwncCtrl) {
261  ResizeClient(scope_controller.GuiParameters.areas[area]->Currentframe().xres() + 20
262  , scope_controller.GuiParameters.areas[area]->Currentframe().yres() + 92);
264  toolbar.CheckButton(IDC_SAMESIZE);
265 }
266 
267 void CChannelFrame::OnDoubleSize(UINT uCode, int nID, HWND hwncCtrl) {
268  ResizeClient(scope_controller.GuiParameters.areas[area]->Currentframe().xres()*2 + 20
269  , scope_controller.GuiParameters.areas[area]->Currentframe().yres()*2 + 92);
271  toolbar.CheckButton(IDC_DOUBLESIZE);
272 }
273 
274 void CChannelFrame::OnHalfSize(UINT uCode, int nID, HWND hwncCtrl) {
275  ResizeClient(static_cast<int>(scope_controller.GuiParameters.areas[area]->Currentframe().xres()*0.5) + 20
276  , static_cast<int>(scope_controller.GuiParameters.areas[area]->Currentframe().yres()*0.5) + 92);
278  toolbar.CheckButton(IDC_HALFSIZE);
279 }
280 
281 void CChannelFrame::SetHistogramLimits(const uint32_t& _channel, const uint16_t& _lower, const uint16_t& _upper) {
282  channel_colors.at(_channel).SetLowerLimit(_lower);
283  channel_colors.at(_channel).SetUpperLimit(_upper);
284 
285  // Force rerender if we are not scanning (otherwise LayOverAndRender is called from DisplayControllerImpl::Run anyway)
286  if ( scope_controller.GuiParameters.run_state() == RunStateHelper::Stopped )
288 }
289 
291  statusstr = _rs;
292  Send(std::bind(&CChannelFrame::RunUpdateStatusbar, this, std::placeholders::_1, current_frame));
293 }
294 
295 void CChannelFrame::LayOverAndRender(scope::SCOPE_MULTIIMAGECPTR_T const _multi) {
296  current_frame = _multi;
297  // More than 6 elements in queue -> just throw away what is coming now...
298  if ( ptq.Size() < 7 ) {
299  Send(std::bind(&CChannelFrame::RunLayOverAndRender, this, std::placeholders::_1, current_frame));
300  Send(std::bind(&CChannelFrame::RunUpdateStatusbar, this, std::placeholders::_1, current_frame));
301  }
302  else {
303  DBOUT(L"CChannelFrame::LayOverAndRender dropping frame " << current_frame->GetImageNumber());
304  }
305 }
306 
307 void CChannelFrame::UpdateScaleText(const std::wstring& _text) {
308  view.UpdateScaleText(_text);
309 }
310 
311 
312 
313 }
314 
315 }
ID2D1Bitmap * GetBitmap()
Definition: ChannelView.cpp:54
void ResizeContent(const uint32_t &_xres, const uint32_t &_yres)
Resizes the bitmap of the renderer.
Definition: ChannelView.cpp:50
std::wstring mouseposstr
holds the current mouse position as string
Definition: ChannelFrame.h:59
Thread-safe lock-free bool to signal a requested stop to the worker function currently executed in th...
Definition: helpers.h:87
Simple class derivec from CComboBoxEx, added only method InitializeColors wich fills combo with color...
BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
Handles mouse wheel events to change zoom or pockels cell value (wheel+Ctrl)
void Quit()
Send lambda with done=true to the worker, join the worker thread, and clear the packaged task queue...
Definition: Active.h:80
std::wstring framecountstr
Holds the current frame count etc as string.
Definition: ChannelFrame.h:53
std::wstring statusstr
holds the current run status as string
Definition: ChannelFrame.h:62
std::array< std::unique_ptr< Area >, SCOPE_NAREAS > areas
holds AreaParameters for all areas.
Definition: Scope.h:231
bool RunLayOverAndRender(StopCondition *const sc, scope::SCOPE_MULTIIMAGECPTR_T const _multi)
Worker function which will run in the Active's thread.
D2D1_POINT_2F mousepos
current mouse position in device independent pixels (for the renderer)
Definition: ChannelFrame.h:56
size_t Size() const
Definition: SyncQueues.h:31
STL namespace.
const uint32_t channels
number of channels in this area
Definition: ChannelFrame.h:41
CChannelFrame(const uint32_t &_area)
Initialize everything.
scope::SCOPE_OVERLAY_T overlay
Overlay of channels of the currently displayed image, use shared_ptr because it is used from differen...
Definition: ChannelFrame.h:74
CToolBarCtrl toolbar
the toolbar
Definition: ChannelFrame.h:68
virtual BOOL OnIdle()
Updates toolbar and statusbar.
Base class for all Scope datatypes here, provides a uniform interface (and saves typing...).
virtual void UpdateScaleText(const std::wstring &_text)
Update the scale text.
Definition: ChannelView.cpp:58
void Render()
Calls renderer.Render.
Definition: ChannelView.cpp:46
LRESULT OnUpdateMousePixel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
Mouse movements are received in the client window (CChannelView) with the correct coordinates...
static std::array< int32_t, 4 > colorcombo_resources
The resource IDs of the channel color combo boxes.
Definition: ChannelFrame.h:77
const uint32_t area
area for this CChannelFrame
Definition: ChannelFrame.h:38
This is the include file for standard system include files, or project specific include files that ar...
void OnHalfSize(UINT uCode, int nID, HWND hwncCtrl)
Sets window size to half the real frame size.
std::vector< ColorProps > channel_colors
Ordered by channel number.
Definition: ChannelFrame.h:71
std::future< bool > Send(const Command &_cmd)
Sends a worker function/packaged task to the queue to be executed in the Active's thread...
Definition: Active.h:59
void UncheckScaleButtons()
Unchecks all scale buttons.
#define DBOUT(s)
A debug output to the debug console.
Definition: helpers.h:153
int OnCreate(LPCREATESTRUCT lpCreateStruct)
Create view, toolbar, and statusbar etc.
void ScopeExceptionHandler(const std::string &_origin, const bool &_log, const bool &_showmessagebox, const bool &_trace, const bool &_rethrow)
Handles all exceptions and does nice logging.
void OnDoubleSize(UINT uCode, int nID, HWND hwncCtrl)
Sets window size to double the real frame size.
CMultiPaneStatusBarCtrl m_wndStatusBar
the statusbar
Definition: ChannelFrame.h:65
static D2D1_POINT_2F PixelsToDips(T x, T y)
Convert screen pixels to device independent points.
Definition: d2wrap.h:31
~CChannelFrame()
Detach if not yet happened and quit.
virtual void OnFinalMessage(HWND)
delete this to free the memory
void UpdateScaleText()
Updates the scale text with current values from scope_controller::GuiParameters.
void OnSameSize(UINT uCode, int nID, HWND hwncCtrl)
Sets window size to match the real frame size.
virtual void LayOverAndRender(scope::SCOPE_MULTIIMAGECPTR_T const _multi)
Sends the worker function 'RunLayOverAndRender' to the ActiveObject.
ScopeValue< RunState > run_state
current RunState
Definition: Scope.h:276
void OnSizing(UINT fwSide, LPRECT pRect)
Keeps the aspect ratio.
bool attached
are we attached to ScopeController?
Definition: ChannelFrame.h:44
SynchronizedQueue< const std::function< void(StopCondition *const sc)> > ptq
synchronized queue of worker functions/packaged tasks that are executed in the Active' thread (see la...
Definition: Active.h:26
D2D1_SIZE_F GetRendererSize() const
Definition: ChannelView.cpp:42
bool RunUpdateStatusbar(StopCondition *const sc, scope::SCOPE_MULTIIMAGECPTR_T const _multi)
Worker function for all statusbar updates which will run in the Active's thread.
scope::SCOPE_MULTIIMAGECPTR_T current_frame
currently displayed image
Definition: ChannelFrame.h:50
following http://www.mochima.com/articles/LUT/lut_h.html
static parameters::Scope GuiParameters
The complete pseudo-global parameter set of the microscope.
virtual void SetHistogramLimits(const uint32_t &_channel, const uint16_t &_lower, const uint16_t &_upper)
Sets upper and lower limit of displayed colors for a channel.
virtual void UpdateStatus(const RunState &_rs)
Updates the statusstr and send RunUpdateStatusbar to the Active object.
scope::ScopeController scope_controller
the ScopeController kept handy here
Definition: ChannelFrame.h:35
CChannelView view
The view inside the client window.
Definition: ChannelFrame.h:47
void OnDestroy()
Detaches frame from ScopeController, because after OnDestroy the HWND is not valid anymore...