-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathScreenHelper.h
More file actions
208 lines (179 loc) · 6.37 KB
/
ScreenHelper.h
File metadata and controls
208 lines (179 loc) · 6.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#pragma once
#include <QRect> // QRect
#include <QPoint> // QPoint
#include "PanZoomState.h"
#include "math.h"
/**
* Screen helper functions
*/
namespace BioTracker {
namespace Core {
namespace ScreenHelper {
/**
* @brief calculate_viewport
* @param im_w width of the image
* @param im_h height of the image
* @param w width of the videoview
* @param h height of the videoview
* @param window OUT parameter
* @param viewport OUT parameter
*/
inline float calculate_viewport(
const int im_w, const int im_h,
const int w, const int h,
QRect &window, QRect &viewport) {
// We use setWindow and setViewport to fit the video into the
// given video widget frame (with width "w" and height "h")
// we later need to adjust an offset caused the use of different
// dimensions for window and viewport.
const float im_wf = static_cast<float>(im_w);
const float im_hf = static_cast<float>(im_h);
const float w_f = static_cast<float>(w);
const float h_f = static_cast<float>(h);
float ratio = 0;
int offset_top = 0;
int offset_left = 0;
if ((im_wf/im_hf) > (w_f/h_f)) {
// image ratio is bigger (wider image than window)
ratio = im_wf / w_f;
const float px = w_f / im_wf;
const float px_im_hf = px * im_hf;
offset_top = h/2 - static_cast<int>(px_im_hf / 2);
} else {
// window ratio is bigger (narrow image)
ratio = im_hf / h_f;
const float px = h_f / im_hf;
const float px_im_wf = px * im_wf;
offset_left = w/2 - static_cast<int>(px_im_wf / 2);
}
const int screen_w = static_cast<int>(im_wf * ratio);
const int screen_h = static_cast<int>(im_hf * ratio);
window.setX(0);
window.setY(0);
window.setWidth(screen_w);
window.setHeight(screen_h);
viewport.setX(offset_left);
viewport.setY(offset_top);
viewport.setWidth(im_w);
viewport.setHeight(im_h);
// adjust the panning as the viewport is potentially scewed
// and mouse movements given by the window are not translated
// one-to-one anymore
return screen_w / im_wf;
}
/**
* @brief getImDimsInScreenCoords
* Calculates the actual dimension of the image with all zoom, pan and
* viewport transformations applied
* @return a rectangle that represents the position and dimension of the
* image in the videoview element
*/
inline QRect getImDimsInScreenCoords(
const PanZoomState zoomState,
const int im_w, const int im_h,
const int w, const int h) {
QRect viewport, window;
const float viewportRatio = calculate_viewport(im_w, im_h, w, h, window, viewport);
const float zoom = 1 + zoomState.zoomFactor;
// back-translate the pan to non-zoomed coordinate space
float realPanX = -zoomState.panX;
float realPanY = -zoomState.panY;
realPanX += (viewport.x());
realPanY += (viewport.y());
// The image in screen coords
QRect actualIm(
static_cast<int>(realPanX), static_cast<int>(realPanY),
static_cast<int>((im_w / viewportRatio) * zoom),
static_cast<int>((im_h / viewportRatio) * zoom)
);
return actualIm;
}
/**
* @brief imageToScreenCoords
*/
inline QPoint imageToScreenCoords(
const PanZoomState zoomState,
const int im_w, const int im_h,
const int w, const int h,
const QPoint poi) {
QPoint result;
const QRect actualIm = getImDimsInScreenCoords(zoomState, im_w, im_h, w, h);
const float one_step_x = actualIm.width() / static_cast<float>(im_w);
const float one_step_y = actualIm.height() / static_cast<float>(im_h);
const int actualPosXInIm = static_cast<int>(round(poi.x() * one_step_x));
const int actualPosYInIm = static_cast<int>(round(poi.y() * one_step_y));
result.setX(actualPosXInIm + actualIm.x());
result.setY(actualPosYInIm + actualIm.y());
return result;
}
/**
* @brief screenToImageCoords
* Translate the window coordinates to the actual image coords
* @param im_w image width
* @param im_h image height
* @param w width of the videoview
* @param h height of the videoview
* @param poi Point in Screen Coords
*/
inline QPoint screenToImageCoords(
const BioTracker::Core::PanZoomState zoomState,
const int im_w, const int im_h,
const int w, const int h,
QPoint poi) {
QPoint result(0, 0);
// The image in screen coords
QRect actualIm = getImDimsInScreenCoords(zoomState, im_w, im_h, w, h);
const float im_wf = static_cast<float>(im_w);
const float im_hf = static_cast<float>(im_h);
const float one_step_x = im_wf / actualIm.width();
const float one_step_y = im_hf / actualIm.height();
const float transformedX = (-actualIm.x() + poi.x()) * one_step_x;
const float transformedY = (-actualIm.y() + poi.y()) * one_step_y;
result.setX(static_cast<int>(round(transformedX)));
result.setY(static_cast<int>(round(transformedY)));
return result;
}
/**
* @brief zoomTo
* Modifies the panzoomstate so that the cursor (zoomCenter) will point to the same
* location in the image as prior to the zoom
* @param im_w width of the image (in px)
* @param im_h height of the image (in px)
* @param w width of the videoview element
* @param h height of the videoview element
*/
inline PanZoomState zoomTo(
PanZoomState state,
const int im_w,
const int im_h,
const int w,
const int h,
const float deltaZoom,
const QPoint zoomCenter) {
const QPoint imPos = screenToImageCoords(state, im_w, im_h, w, h, zoomCenter);
const float oldZoomFactor = state.zoomFactor;
const float newZoomFactor = state.zoomFactor - (deltaZoom/2000);
if (newZoomFactor <= -1.0f) {
// MAX VALUE
return state;
}
const float zoom = 1 + newZoomFactor;
const float oldPanX = state.panX / (1 + oldZoomFactor);
const float oldPanY = state.panY / (1 + oldZoomFactor);
// zoom with origin in (0/0)
state.panX = oldPanX * zoom;
state.panY = oldPanY * zoom;
state.zoomFactor = newZoomFactor;
// As we do not want to zoom to (0/0) but rather keep the focus on the chosen
// image position (zoomCenter) we now have to "back-translate" to our initial
// position
const QPoint translatedZoomCenter = imageToScreenCoords(state, im_w, im_h, w, h, imPos);
const QPoint translate = translatedZoomCenter - zoomCenter;
state.panX += translate.x();
state.panY += translate.y();
state.isChanged = true;
return state;
}
}
}
}