Scope
D2HistogramRender.cpp
1 #include "StdAfx.h"
2 #include "D2HistogramRender.h"
3 #include "helpers/hresult_exception.h"
4 #include "helpers/ScopeMultiHistogram.h"
5 
6 namespace d2d {
7 
8 D2HistogramRender::D2HistogramRender(const uint32_t& _width, const uint32_t& _channels, scope::ScopeMultiHistogramPtr _hist)
9  : width(_width)
10  , channels(_channels)
11  , render_target(nullptr)
12  , brushwhite(nullptr)
13  , brushred(nullptr)
14  , brushyellow(nullptr)
15  , hwnd(NULL)
16  , llpos(channels, 0.0)
17  , ulpos(channels, 512.0)
18  , hist(_hist) {
19 }
20 
23 }
24 
29 }
30 
31 void D2HistogramRender::Create(const HWND& _hwnd) {
32  std::lock_guard<std::mutex> lock(mutex);
33  hwnd = _hwnd;
34 
35  // Ensure the client area/HWND has the width we need for the histogram (because RenderTarget uses GetClientRect)
36  RECT rect;
37  GetClientRect(hwnd, &rect);
38  assert(( rect.right-rect.left) == width );
39 
40  render_target.reset(new RenderTarget(hwnd));
41  render_target->CreateSolidColorBrush(&brushwhite, D2D1::ColorF(D2D1::ColorF::White));
42  render_target->CreateSolidColorBrush(&brushred, D2D1::ColorF(D2D1::ColorF::Red));
43  render_target->CreateSolidColorBrush(&brushyellow, D2D1::ColorF(D2D1::ColorF::Yellow));
44  llpos.assign(channels, 0);
45  // make sure we don't get in trouble when window has size 0
46  ulpos.assign(channels, (render_target->GetSize().width==0)?1.0f:render_target->GetSize().width-1.0f);
47 }
48 
49 void D2HistogramRender::SetHistogram(scope::ScopeMultiHistogramPtr _hist) {
50  std::lock_guard<std::mutex> lock(mutex);
51  hist = _hist;
52 }
53 
55  std::lock_guard<std::mutex> lock(mutex);
56  HResult hr;
57  if ( 0 == (D2D1_WINDOW_STATE_OCCLUDED & render_target->CheckWindowState()) ) {
58  render_target->BeginDraw();
59  render_target->SetTransform(D2D1::Matrix3x2F::Identity());
60  render_target->Clear(D2D1::ColorF(D2D1::ColorF::Black));
61 
62  D2D1_SIZE_F size = render_target->GetSize();
63  std::vector<uint32_t> maxvals(hist->MaxCounts());
64  float channelheight = size.height/channels;
65  float multiplier = 1;
66  for ( uint32_t c = 0 ; c < channels ; c++ ) {
67  // scale factor from counts to screen pixels, +1 for maxvals in case of 0
68  multiplier = channelheight / (maxvals[c]+1.0f);
69  // Get a pointer to that channels histogram, call lock that histogram
70  const std::vector<uint32_t>* h = hist->GetHistConst(c);
71  assert(h->size() >= size.width);
72  // Draw histogram out of single lines
73  for ( float pos = 0.0f ; pos < size.width ; pos += 1.0f ) {
74  render_target->DrawLine( D2D1::Point2F(pos, channelheight*(c+1.0f))
75  , D2D1::Point2F(pos, channelheight*(c+1.0f) - static_cast<float>(h->at(static_cast<size_t>(pos))) * multiplier)
76  , brushwhite, 2 );
77  }
78  // Release the histogram lock of that channel
79  hist->ReleaseHistConst(c);
80  // Draw the limit lines, lower in red, upper in yellow
81  render_target->DrawLine( D2D1::Point2F(ulpos[c], channelheight*c)
82  , D2D1::Point2F(ulpos[c], channelheight*(c+1.0f))
83  , brushyellow, 3);
84  render_target->DrawLine( D2D1::Point2F(llpos[c], channelheight*c)
85  , D2D1::Point2F(llpos[c], channelheight*(c+1.0f))
86  , brushred, 3);
87  }
88  // Errors from non-hresult-returning function end up here
89  hr(__FUNCTION__) = render_target->Flush();
90  hr(__FUNCTION__) = render_target->EndDraw();
91  // "A presentation error has occurred that may be recoverable. The caller needs to re-create the render target then attempt to render the frame again."
92  if ((HRESULT)hr == D2DERR_RECREATE_TARGET) {
93  hr(__FUNCTION__) = S_OK;
95  }
96  }
97 }
98 
99 bool D2HistogramRender::ClickedAt(const CPoint _clickpoint, uint32_t& _channel, bool& _uplo) {
100  std::lock_guard<std::mutex> lock(mutex); // be safe here
101  ID2D1RectangleGeometry* hitTestlower = nullptr;
102  ID2D1RectangleGeometry* hitTestupper = nullptr;
103  FLOAT channelheight = render_target->GetSize().height/channels;
104  BOOL hit = false;
105  D2D1_POINT_2F point = D2D1::Point2F(static_cast<FLOAT>(_clickpoint.x), static_cast<FLOAT>(_clickpoint.y));
106  for ( uint32_t c = 0 ; c < channels ; c++ ) {
107  // Create rectangle around limit line (widht +/- 5 pixels)
108  render_target->CreateRectangleGeometry(D2D1::RectF(llpos[c]-5.0f, channelheight*c
109  , llpos[c]+5.0f, channelheight*(c+1.0f)), &hitTestlower);
110  render_target->CreateRectangleGeometry(D2D1::RectF(ulpos[c]-5.0f, channelheight*c
111  , ulpos[c]+5.0f, channelheight*(c+1.0f)), &hitTestupper);
112  // Test hit on lower limit
113  hitTestlower->FillContainsPoint(point, NULL, &hit);
114  if ( hit ) {
115  _channel = c;
116  _uplo = false;
117  break;
118  }
119  else { // Avoid dragging both when (nearly) overlying
120  // Test hit on upper limit
121  hitTestupper->FillContainsPoint(point, NULL, &hit);
122  if ( hit ) {
123  _channel = c;
124  _uplo = true;
125  break;
126  }
127  }
128  }
129  SafeRelease(&hitTestlower);
130  SafeRelease(&hitTestupper);
131  return (hit!=0);
132 }
133 
134 void D2HistogramRender::SetLowerLimitPosition(const uint32_t& _channel, const FLOAT& _pos) {
135  std::lock_guard<std::mutex> lock(mutex);
136  llpos[_channel] = _pos;
137 }
138 
139 void D2HistogramRender::SetUpperLimitPosition(const uint32_t& _channel, const FLOAT& _pos) {
140  std::lock_guard<std::mutex> lock(mutex);
141  ulpos[_channel] = _pos;
142 }
143 
144 void D2HistogramRender::Size(const CSize& size) {
145  std::lock_guard<std::mutex> lock(mutex);
146  if ( render_target != nullptr ) {
147  DBOUT(L"New size x: " << size.cx << L" old size x: " << render_target->GetSize().width);
148  // Fix size for calculations if it is zero (windows start with size zero!)
149  FLOAT s = ((size.cx==0)?1.0f:static_cast<FLOAT>(size.cx));
150  FLOAT r = ((render_target->GetSize().width==0)?1.0f:render_target->GetSize().width);
151  // Recalculate the limit positions
152  for ( FLOAT& p : llpos )
153  p *= s / r;
154  for ( FLOAT& p : ulpos )
155  p *= s / r;
156  render_target->Resize(D2D1::SizeU(size.cx, size.cy));
157  }
158 }
159 
160 }
std::unique_ptr< RenderTarget > render_target
Direct2D render target.
const uint32_t width
constant width of the renderer
scope::ScopeMultiHistogramPtr hist
Pointer to the currently rendered multi histogram.
Wrappers around the Direct2D interface.
void SetHistogram(scope::ScopeMultiHistogramPtr _hist)
Sets the pointer to the current multi histogram.
void Render()
Draws the current multi histogram with the limit lines.
void SafeRelease(Interface **ppInterfaceToRelease)
A safe release for COM objects.
Definition: helpers.h:25
void DiscardDeviceResources()
Safely release all Direct2D resources.
bool ClickedAt(const CPoint _clickpoint, uint32_t &_channel, bool &_uplo)
Checks if click hit a limit (+-5 pixel) and if yes, on which channel and upper or lower limit line...
D2HistogramRender(const D2HistogramRender &)
Disable copy.
This is the include file for standard system include files, or project specific include files that ar...
HWND hwnd
windows handle
std::vector< FLOAT > ulpos
Keeps track of the upper limit positions in screen coordinates.
ID2D1SolidColorBrush * brushwhite
Direct2D white brush.
void Create(const HWND &_hwnd)
Creates the render target for a hwnd, the brushes for painting, and sets the limit position to the wi...
void SetLowerLimitPosition(const uint32_t &_channel, const FLOAT &_pos)
Set position of a lower limit line.
std::vector< FLOAT > llpos
Keeps track of the lower limit positions in screen coordinates.
#define DBOUT(s)
A debug output to the debug console.
Definition: helpers.h:153
ID2D1SolidColorBrush * brushyellow
Direct2D yellow brush.
const uint32_t channels
number of channels
void Size(const CSize &size)
Resizes the renderer and the limit positions.
Wrapper around a Direct2D render target and the underlying Direct2D factory and IDWriteFactory.
Definition: d2wrap.h:71
std::mutex mutex
mutex for protection
void SetUpperLimitPosition(const uint32_t &_channel, const FLOAT &_pos)
Set position of an upper limit line.
~D2HistogramRender()
Discard device resources.
ID2D1SolidColorBrush * brushred
Direct2D red brush.