1 /* qv4l2: a control panel controlling v4l2 devices.
2  *
3  * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 
20 #include "capture-win.h"
21 
22 #undef min
23 #undef max
24 
25 #include <QCloseEvent>
26 #include <QLabel>
27 #include <QImage>
28 #include <QVBoxLayout>
29 #include <QApplication>
30 #include <QDesktopWidget>
31 
32 #include <math.h>
33 
34 #define MIN_WIN_SIZE_WIDTH 16
35 #define MIN_WIN_SIZE_HEIGHT 12
36 
37 bool CaptureWin::m_enableScaling = true;
38 double CaptureWin::m_pixelAspectRatio = 1.0;
39 CropMethod CaptureWin::m_cropMethod = QV4L2_CROP_NONE;
40 
CaptureWin(ApplicationWindow * aw)41 CaptureWin::CaptureWin(ApplicationWindow *aw) :
42 	m_appWin(aw)
43 {
44 	setWindowTitle("V4L2 Capture");
45 	m_hotkeyClose = new QShortcut(Qt::CTRL+Qt::Key_W, this);
46 	connect(m_hotkeyClose, SIGNAL(activated()), this, SLOT(close()));
47 	connect(new QShortcut(Qt::Key_Q, this), SIGNAL(activated()), this, SLOT(close()));
48 	m_hotkeyScaleReset = new QShortcut(Qt::CTRL+Qt::Key_F, this);
49 	connect(m_hotkeyScaleReset, SIGNAL(activated()), this, SLOT(resetSize()));
50 	connect(aw->m_resetScalingAct, SIGNAL(triggered()), this, SLOT(resetSize()));
51 	m_hotkeyExitFullscreen = new QShortcut(Qt::Key_Escape, this);
52 	connect(m_hotkeyExitFullscreen, SIGNAL(activated()), this, SLOT(escape()));
53 	m_hotkeyToggleFullscreen = new QShortcut(Qt::Key_F, this);
54 	connect(m_hotkeyToggleFullscreen, SIGNAL(activated()), aw->m_makeFullScreenAct, SLOT(toggle()));
55 	m_exitFullScreen = new QAction(QIcon(":/fullscreenexit.png"), "Exit Fullscreen", this);
56 	connect(m_exitFullScreen, SIGNAL(triggered()), this, SLOT(escape()));
57 	m_enterFullScreen = new QAction(QIcon(":/fullscreen.png"), "Show Fullscreen", this);
58 	connect(m_enterFullScreen, SIGNAL(triggered()), this, SLOT(fullScreen()));
59 	m_frame.format = 0;
60 	m_frame.size.setWidth(0);
61 	m_frame.size.setHeight(0);
62 	m_frame.planeData[0] = NULL;
63 	m_frame.planeData[1] = NULL;
64 	m_frame.planeData[2] = NULL;
65 	m_crop.delta.setWidth(0);
66 	m_crop.delta.setHeight(0);
67 	m_crop.size.setWidth(0);
68 	m_crop.size.setHeight(0);
69 	m_crop.updated = 0;
70 	m_scaledSize.setWidth(0);
71 	m_scaledSize.setHeight(0);
72 	m_origFrameSize.setWidth(0);
73 	m_origFrameSize.setHeight(0);
74 	m_windowSize.setWidth(0);
75 	m_windowSize.setHeight(0);
76 }
77 
~CaptureWin()78 CaptureWin::~CaptureWin()
79 {
80 	if (layout() == NULL)
81 		return;
82 
83 	layout()->removeWidget(this);
84 	delete layout();
85 	delete m_hotkeyClose;
86 	delete m_hotkeyScaleReset;
87 }
88 
setFrame(int width,int height,uint32_t format,unsigned char * data,unsigned char * data2,unsigned char * data3)89 void CaptureWin::setFrame(int width, int height, uint32_t format,
90 	  unsigned char *data, unsigned char *data2, unsigned char *data3)
91 {
92 	m_frame.planeData[0] = data;
93 	m_frame.planeData[1] = data2;
94 	m_frame.planeData[2] = data3;
95 
96 	m_frame.updated = false;
97 	if (width != m_frame.size.width() || height != m_frame.size.height()
98 	    || format != m_frame.format) {
99 		m_frame.size.setHeight(height);
100 		m_frame.size.setWidth(width);
101 		m_frame.format       = format;
102 		m_frame.updated      = true;
103 		updateSize();
104 	}
105 
106 	setRenderFrame();
107 }
108 
buildWindow(QWidget * videoSurface)109 void CaptureWin::buildWindow(QWidget *videoSurface)
110 {
111 	int l, t, r, b;
112 	m_vboxLayout = new QVBoxLayout(this);
113 	m_vboxLayout->getContentsMargins(&l, &t, &r, &b);
114 	m_vboxLayout->setMargin(0);
115 	m_vboxLayout->addWidget(videoSurface, 1000, Qt::AlignCenter);
116 
117 	setContextMenuPolicy(Qt::CustomContextMenu);
118 	connect(this, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
119 }
120 
resetSize()121 void CaptureWin::resetSize()
122 {
123         // Force resize even if no size change
124 	QSize resetFrameSize = m_origFrameSize;
125 	m_origFrameSize.setWidth(0);
126 	m_origFrameSize.setHeight(0);
127 
128 	setWindowSize(resetFrameSize);
129 }
130 
cropSize(QSize size)131 QSize CaptureWin::cropSize(QSize size)
132 {
133 	QSize croppedSize = size;
134 	double realWidth = size.width() * m_pixelAspectRatio;
135 	double realHeight = size.height() / m_pixelAspectRatio;
136 	double aspectRatio = 1;
137 
138 	switch (m_cropMethod) {
139 	case QV4L2_CROP_P43:
140 		aspectRatio = 4.0 / 3.0;
141 		break;
142 	case QV4L2_CROP_W149:
143 		aspectRatio = 14.0 / 9.0;
144 		break;
145 	case QV4L2_CROP_W169:
146 		aspectRatio = 16.0 / 9.0;
147 		break;
148 	case QV4L2_CROP_C185:
149 		aspectRatio = 1.85;
150 		break;
151 	case QV4L2_CROP_C239:
152 		aspectRatio = 2.39;
153 		break;
154 	case QV4L2_CROP_TB:
155 		croppedSize.setHeight(size.height() - 2);
156 		break;
157 	default:
158 		break;  // No cropping
159 	}
160 
161 	if ((m_cropMethod != QV4L2_CROP_TB) && (m_cropMethod != QV4L2_CROP_NONE)) {
162 		if (realWidth / size.height() < aspectRatio)
163 			croppedSize.setHeight(realWidth / aspectRatio);
164 		else
165 			croppedSize.setWidth(realHeight * aspectRatio);
166 	}
167 
168 	if (croppedSize.width() >= size.width())
169 		croppedSize.setWidth(size.width());
170 	if (croppedSize.width() < MIN_WIN_SIZE_WIDTH)
171 		croppedSize.setWidth(MIN_WIN_SIZE_WIDTH);
172 	if (croppedSize.height() >= size.height())
173 		croppedSize.setHeight(size.height());
174 	if (croppedSize.height() < MIN_WIN_SIZE_HEIGHT)
175 		croppedSize.setHeight(MIN_WIN_SIZE_HEIGHT);
176 
177 	return croppedSize;
178 }
179 
updateSize()180 void CaptureWin::updateSize()
181 {
182 	m_crop.updated = false;
183 	if (m_frame.updated) {
184 		m_scaledSize = scaleFrameSize(m_windowSize, m_frame.size);
185 		m_crop.size = cropSize(m_frame.size);
186 		m_crop.delta = (m_frame.size - m_crop.size) / 2;
187 		m_crop.updated = true;
188 	}
189 }
190 
setCropMethod(CropMethod crop)191 void CaptureWin::setCropMethod(CropMethod crop)
192 {
193 	m_cropMethod = crop;
194 	resetSize();
195 }
196 
pixelAspectFrameSize(QSize size)197 QSize CaptureWin::pixelAspectFrameSize(QSize size)
198 {
199 	if (!m_enableScaling)
200 		return size;
201 
202 	if (m_pixelAspectRatio > 1)
203 		size.rwidth() *= m_pixelAspectRatio;
204 
205 	if (m_pixelAspectRatio < 1)
206 		size.rheight() /= m_pixelAspectRatio;
207 
208 	return size;
209 }
210 
getMargins()211 QSize CaptureWin::getMargins()
212 {
213 	int l, t, r, b;
214 	layout()->getContentsMargins(&l, &t, &r, &b);
215 	return QSize(l + r, t + b);
216 }
217 
enableScaling(bool enable)218 void CaptureWin::enableScaling(bool enable)
219 {
220 	if (!enable) {
221 		QSize margins = getMargins();
222 		QWidget::setMinimumSize(m_origFrameSize.width() + margins.width(),
223 					m_origFrameSize.height() + margins.height());
224 	} else {
225 		QWidget::setMinimumSize(MIN_WIN_SIZE_WIDTH, MIN_WIN_SIZE_HEIGHT);
226 	}
227 	m_enableScaling = enable;
228 	resetSize();
229 }
230 
setWindowSize(QSize frameSize)231 void CaptureWin::setWindowSize(QSize frameSize)
232 {
233 	// Dont resize window if the frame size is the same in
234 	// the event the window has been paused when beeing resized.
235 	if (frameSize == m_origFrameSize)
236 		return;
237 
238 	m_origFrameSize = frameSize;
239 
240 	QSize margins = getMargins();
241 	QDesktopWidget *screen = QApplication::desktop();
242 	QRect resolution = screen->screenGeometry();
243 
244 	QSize windowSize =  pixelAspectFrameSize(cropSize(frameSize)) + margins;
245 
246 	if (windowSize.width() > resolution.width())
247 		windowSize.setWidth(resolution.width());
248 	if (windowSize.width() < MIN_WIN_SIZE_WIDTH)
249 		windowSize.setWidth(MIN_WIN_SIZE_WIDTH);
250 
251 	if (windowSize.height() > resolution.height())
252 		windowSize.setHeight(resolution.height());
253 	if (windowSize.height() < MIN_WIN_SIZE_HEIGHT)
254 		windowSize.setHeight(MIN_WIN_SIZE_HEIGHT);
255 
256 	QWidget::setMinimumSize(MIN_WIN_SIZE_WIDTH, MIN_WIN_SIZE_HEIGHT);
257 
258 	m_frame.size = frameSize;
259 
260 	QWidget::resize(windowSize);
261 }
262 
scaleFrameSize(QSize window,QSize frame)263 QSize CaptureWin::scaleFrameSize(QSize window, QSize frame)
264 {
265 	QSize actualSize = pixelAspectFrameSize(cropSize(frame));
266 
267 	if (!m_enableScaling) {
268 		window.setWidth(actualSize.width());
269 		window.setHeight(actualSize.height());
270 	}
271 
272 	qreal newW, newH;
273 	if (window.width() >= window.height()) {
274 		newW = (qreal)window.width() / actualSize.width();
275 		newH = (qreal)window.height() / actualSize.height();
276 	} else {
277 		newH = (qreal)window.width() / actualSize.width();
278 		newW = (qreal)window.height() / actualSize.height();
279 	}
280 	qreal resized = (qreal)std::min(newW, newH);
281 
282 	return (actualSize * resized);
283 }
284 
setPixelAspectRatio(double ratio)285 void CaptureWin::setPixelAspectRatio(double ratio)
286 {
287 	m_pixelAspectRatio = ratio;
288 	resetSize();
289 }
290 
getHorScaleFactor()291 float CaptureWin::getHorScaleFactor()
292 {
293 	float ow, sw, wscale;
294 
295 	sw = m_scaledSize.width();
296 	ow = m_origFrameSize.width();
297 	wscale = floor(100 * (sw / ow)) / 100.0;
298 
299 	return wscale;
300 }
301 
getVertScaleFactor()302 float CaptureWin::getVertScaleFactor()
303 {
304 	float oh, sh, hscale;
305 
306 	sh = m_scaledSize.height();
307 	oh = m_origFrameSize.height();
308 	hscale = floor(100 * (sh / oh)) / 100.0;
309 
310 	return hscale;
311 }
312 
mouseDoubleClickEvent(QMouseEvent * e)313 void CaptureWin::mouseDoubleClickEvent(QMouseEvent *e)
314 {
315 	m_appWin->m_makeFullScreenAct->toggle();
316 }
317 
escape()318 void CaptureWin::escape()
319 {
320 	m_appWin->m_makeFullScreenAct->setChecked(false);
321 }
322 
fullScreen()323 void CaptureWin::fullScreen()
324 {
325 	m_appWin->m_makeFullScreenAct->setChecked(true);
326 }
327 
makeFullScreen(bool enable)328 void CaptureWin::makeFullScreen(bool enable)
329 {
330 	if (enable) {
331 		showFullScreen();
332 		setStyleSheet("background-color:#000000;");
333 	} else {
334 		showNormal();
335 		setStyleSheet("background-color:none;");
336 	}
337 	QSize resetFrameSize = m_origFrameSize;
338 	m_origFrameSize.setWidth(0);
339 	m_origFrameSize.setHeight(0);
340 
341 	setWindowSize(resetFrameSize);
342 }
343 
customMenuRequested(QPoint pos)344 void CaptureWin::customMenuRequested(QPoint pos)
345 {
346 	QMenu *menu = new QMenu(this);
347 
348 	if (isFullScreen()) {
349 		menu->addAction(m_exitFullScreen);
350 		menu->setStyleSheet("background-color:none;");
351 	} else {
352 		menu->addAction(m_enterFullScreen);
353 	}
354 
355 	menu->addAction(m_appWin->m_resetScalingAct);
356 	if (m_appWin->m_useBlendingAct)
357 		menu->addAction(m_appWin->m_useBlendingAct);
358 	if (m_appWin->m_useLinearAct)
359 		menu->addAction(m_appWin->m_useLinearAct);
360 	menu->addAction(m_appWin->m_snapshotAct);
361 	menu->addAction(m_appWin->m_showFramesAct);
362 	menu->addMenu(m_appWin->m_overrideColorspaceMenu);
363 	menu->addMenu(m_appWin->m_overrideXferFuncMenu);
364 	menu->addMenu(m_appWin->m_overrideYCbCrEncMenu);
365 	menu->addMenu(m_appWin->m_overrideQuantizationMenu);
366 
367 	menu->popup(mapToGlobal(pos));
368 }
369 
closeEvent(QCloseEvent * event)370 void CaptureWin::closeEvent(QCloseEvent *event)
371 {
372 	QWidget::closeEvent(event);
373 	emit close();
374 }
375