File indexing completed on 2024-05-12 16:23:32
0001 /*************************************************************************** 0002 * Copyright (C) 2005-2017 by Linuxstopmotion contributors; * 0003 * see the AUTHORS file for details. * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 0019 ***************************************************************************/ 0020 0021 #include "frameview.h" 0022 0023 #include <algorithm> 0024 #include <cstring> 0025 0026 #include <QApplication> 0027 #include <QMessageBox> 0028 #include <QPainter> 0029 0030 #include "imagecache.h" 0031 #include "logger.h" 0032 #include "frontends/frontend.h" 0033 #include "workspacefile.h" 0034 #include "technical/grabber/imagegrabber.h" 0035 #include "src/domain/domainfacade.h" 0036 #include "src/foundation/preferencestool.h" 0037 #include "src/presentation/frontends/qtfrontend/imagegrabthread.h" 0038 #include "src/technical/grabber/commandlinegrabber.h" 0039 0040 enum { IMAGE_CACHE_SIZE = 10 }; 0041 0042 namespace { 0043 void getMaximumScaledRectangle(QRect& out, int w, int h, const QRect& window) { 0044 int vw = window.width(); 0045 int vh = window.height(); 0046 int dw = vw; 0047 int dh = vh; 0048 // Find out whether to scale by width or height. 0049 // Width if w/dw < h/dh <=> w*dh < h*dw 0050 if (w*dh < h*dw) { 0051 dw = (w * dh + h/2)/h; 0052 int left = (vw - dw)/2; 0053 out.setLeft(left); 0054 out.setRight(left + dw); 0055 out.setTop(window.top()); 0056 out.setBottom(window.bottom()); 0057 } else { 0058 dh = (h * dw + w/2)/w; 0059 int top = (vh - dh)/2; 0060 out.setTop(top); 0061 out.setBottom(top + dh); 0062 out.setLeft(window.left()); 0063 out.setRight(window.right()); 0064 } 0065 } 0066 void drawBorders(QPainter& painter, const QRect& inner, const QRect& outer) { 0067 int topBorder = inner.top() - outer.top(); 0068 if (0 < topBorder) { 0069 painter.drawRect(outer.left(), outer.top(), outer.width(), topBorder); 0070 } 0071 int bottomBorder = outer.bottom() - inner.bottom(); 0072 if (0 < bottomBorder) { 0073 painter.drawRect(outer.left(), inner.bottom(), outer.width(), bottomBorder); 0074 } 0075 int leftBorder = inner.left() - outer.left(); 0076 if (0 < leftBorder) { 0077 painter.drawRect(outer.left(), inner.top(), leftBorder, inner.height()); 0078 } 0079 int rightBorder = outer.right() - inner.right(); 0080 if (0 < rightBorder) { 0081 painter.drawRect(inner.right(), inner.top(), rightBorder, inner.height()); 0082 } 0083 } 0084 } 0085 0086 FrameView::FrameView(QWidget *parent, const char *name, int playbackSpeed) 0087 : QWidget(parent), 0088 imageCache(IMAGE_CACHE_SIZE), grabThread(0), grabber(0), 0089 capturedFile(WorkspaceFile::capturedImage) { 0090 facade = DomainFacade::getFacade(); 0091 0092 isPlayingVideo = false; 0093 mode = imageModeMix; 0094 this->playbackSpeed = PreferencesTool::get()->getPreference("fps", 0095 playbackSpeed); 0096 activeScene = -1; 0097 activeFrame = -1; 0098 mixCount = 2; 0099 playbackModeFrame = -1; 0100 0101 connect(&grabTimer, SIGNAL(timeout()), this, SLOT(redraw())); 0102 connect(&playbackTimer, SIGNAL(timeout()),this, SLOT(nextPlayBack())); 0103 0104 setMinimumSize(400, 300); 0105 setAttribute(Qt::WA_NoSystemBackground); 0106 setObjectName(name); 0107 update(); 0108 0109 Logger::get().logDebug("FrameView is attached to the model and the model to FrameView"); 0110 } 0111 0112 0113 FrameView::~FrameView() { 0114 // Turn off camera if it's on 0115 if (isPlayingVideo) { 0116 off(); 0117 } 0118 delete grabber; 0119 grabber = 0; 0120 } 0121 0122 0123 void FrameView::initCompleted() { 0124 emit cameraReady(); 0125 } 0126 0127 void FrameView::updateNewActiveFrame(int sceneNumber, int frameNumber) { 0128 setActiveFrame(sceneNumber, frameNumber); 0129 } 0130 0131 0132 void FrameView::updatePlayFrame(int sceneNumber, int frameNumber) { 0133 if (frameNumber > -1) { 0134 setActiveFrame(sceneNumber, frameNumber); 0135 } else { 0136 this->update(); 0137 } 0138 } 0139 0140 0141 void FrameView::workspaceCleared() { 0142 imageCache.clear(); 0143 } 0144 0145 0146 void FrameView::resizeEvent(QResizeEvent*) { 0147 update(); 0148 } 0149 0150 void FrameView::drawOnionSkins() { 0151 int w = cameraOutput.width(); 0152 int h = cameraOutput.height(); 0153 QRect dst; 0154 getMaximumScaledRectangle(dst, w, h, rect()); 0155 QPainter painter(this); 0156 painter.setBackgroundMode(Qt::OpaqueMode); 0157 painter.setBrush(QColor::fromRgb(0, 0, 0, 255)); 0158 drawBorders(painter, dst, rect()); 0159 DomainFacade* anim = DomainFacade::getFacade(); 0160 if (isPlayingVideo && 0 <= activeScene) { 0161 switch (mode) { 0162 case imageModeMix: 0163 { 0164 painter.drawPixmap(dst, cameraOutput); 0165 const int frameCount = std::min(mixCount, activeFrame + 1); 0166 for (int i = 0; i != frameCount; ++i) { 0167 const char* path = anim->getImagePath( 0168 activeScene, activeFrame - i); 0169 QPixmap* image = imageCache.get(path); 0170 if (image) { 0171 painter.setOpacity(qreal(1)/(i + 2)); 0172 painter.drawPixmap(dst, *image); 0173 } 0174 } 0175 break; 0176 } 0177 case imageModeDiff: 0178 { 0179 QSize differenceSize(cameraOutput.size()); 0180 QRect differenceRect(QPoint(0, 0), differenceSize); 0181 QImage difference(differenceSize, QImage::Format_RGB888); 0182 QPainter differencePainter(&difference); 0183 differencePainter.drawPixmap(differenceRect, cameraOutput); 0184 if (0 <= activeFrame) { 0185 const char* path = anim->getImagePath(activeScene, activeFrame); 0186 QPixmap* image = imageCache.get(path); 0187 if (image) { 0188 differencePainter.setCompositionMode( 0189 QPainter::CompositionMode_Difference); 0190 differencePainter.drawPixmap(differenceRect, *image); 0191 } 0192 } 0193 differencePainter.end(); 0194 painter.drawImage(dst, difference); 0195 break; 0196 } 0197 case imageModePlayback: 0198 if (playbackModeFrame < 0) { 0199 painter.drawPixmap(dst, cameraOutput); 0200 } else { 0201 const char* path = anim->getImagePath(activeScene, playbackModeFrame); 0202 QPixmap* image = imageCache.get(path); 0203 if (image) { 0204 painter.drawPixmap(dst, *image); 0205 } 0206 } 0207 break; 0208 default: 0209 painter.drawPixmap(dst, cameraOutput); 0210 break; 0211 } 0212 } else { 0213 painter.drawPixmap(dst, cameraOutput); 0214 } 0215 } 0216 0217 void FrameView::paintEvent(QPaintEvent *) { 0218 if (isPlayingVideo) { 0219 if (!cameraOutput.isNull()) { 0220 drawOnionSkins(); 0221 } 0222 } else { 0223 QPainter painter(this); 0224 painter.setBackgroundMode(Qt::OpaqueMode); 0225 painter.setBrush(QColor::fromRgb(0, 0, 0, 255)); 0226 DomainFacade* anim = DomainFacade::getFacade(); 0227 if (0 <= activeScene && 0 <= activeFrame) { 0228 const char* path = anim->getImagePath(activeScene, activeFrame); 0229 QPixmap* image = imageCache.get(path); 0230 if (image) { 0231 QRect destination; 0232 getMaximumScaledRectangle(destination, image->width(), image->height(), rect()); 0233 drawBorders(painter, destination, rect()); 0234 painter.drawPixmap(destination, *image); 0235 return; 0236 } 0237 } 0238 painter.drawRect(rect()); 0239 } 0240 } 0241 0242 void FrameView::setActiveFrame(int sceneNumber, int frameNumber) { 0243 activeScene = sceneNumber; 0244 activeFrame = frameNumber; 0245 Logger::get().logDebug("Setting new active frame %d and scene %d in FrameView", 0246 activeFrame, activeScene); 0247 update(); 0248 } 0249 0250 0251 // TODO: Refactor this terrible ugly method. This one is really bad!! 0252 bool FrameView::on() { 0253 const char* DEVICE_TOKEN = "$VIDEODEVICE"; 0254 PreferencesTool *prefs = PreferencesTool::get(); 0255 int activeCmd = prefs->getPreference("activedevice", 0); 0256 0257 Preference prepoll(QString("importprepoll%1") 0258 .arg(activeCmd).toLatin1().constData(), ""); 0259 Preference startDaemon(QString("importstartdaemon%1") 0260 .arg(activeCmd).toLatin1().constData(), ""); 0261 Preference stopDaemon(QString("importstopdaemon%1") 0262 .arg(activeCmd).toLatin1().constData(), ""); 0263 0264 bool prepollRequiresDevice = strstr(prepoll.get(), DEVICE_TOKEN); 0265 bool startDaemonRequiresDevice = strstr(startDaemon.get(), DEVICE_TOKEN); 0266 QString device; 0267 if (prepollRequiresDevice || startDaemonRequiresDevice) { 0268 int activeDev = prefs->getPreference("activeVideoDevice", -1); 0269 if (0 <= activeDev) { 0270 Preference deviceP(QString("device%1") 0271 .arg(activeDev).toLatin1().constData(), ""); 0272 device = QString(deviceP.get()).trimmed(); 0273 } 0274 if (device.isEmpty()) { 0275 QMessageBox::warning(this, tr("Warning"), tr( 0276 "No video device selected in the preferences menu."), 0277 QMessageBox::Ok, 0278 Qt::NoButton, 0279 Qt::NoButton); 0280 return false; 0281 } 0282 } 0283 QString pre = QString(prepoll.get()).replace(DEVICE_TOKEN, device); 0284 bool isProcess = startDaemon.get() && *startDaemon.get() != '\0'; 0285 0286 bool isCameraReady = true; 0287 grabber = new CommandLineGrabber(capturedFile.path()); 0288 if ( !grabber->setPrePollCommand(pre.toLatin1().constData()) ) { 0289 QMessageBox::warning(this, tr("Warning"), tr( 0290 "Pre poll command does not exists"), 0291 QMessageBox::Ok, 0292 Qt::NoButton, 0293 Qt::NoButton); 0294 //return false; 0295 isCameraReady = false; 0296 } 0297 0298 if (isProcess) { 0299 QString sd = QString(startDaemon.get()).replace(DEVICE_TOKEN, device); 0300 if ( !grabber->setStartCommand(sd.toLatin1().constData()) ) { 0301 DomainFacade::getFacade()->getFrontend()->hideProgress(); 0302 QMessageBox::warning(this, tr("Warning"), tr( 0303 "You do not have the given grabber installed on your system"), 0304 QMessageBox::Ok, 0305 Qt::NoButton, 0306 Qt::NoButton); 0307 isCameraReady = false; 0308 //return false; 0309 } 0310 } 0311 0312 grabber->setStopCommand(stopDaemon.get()); 0313 0314 if (!isCameraReady) { 0315 return false; 0316 } 0317 initCompleted(); 0318 isPlayingVideo = true; 0319 0320 if ( prefs->getPreference("numberofimports", 1) > 0 ) { 0321 // If the grabber is running in it's own process we use a timer. 0322 if (grabber->isGrabberProcess() == true) { 0323 if ( grabber->init() ) { 0324 grabTimer.start(150); 0325 } 0326 else { 0327 QMessageBox::warning(this, tr("Warning"), tr( 0328 "Grabbing failed. This may happen if you try\n" 0329 "to grab from an invalid device. Please check\n" 0330 "your grabber settings in the preferences menu."), 0331 QMessageBox::Ok, 0332 Qt::NoButton, 0333 Qt::NoButton); 0334 return false; 0335 } 0336 } 0337 // Otherwise a thread is needed 0338 else { 0339 grabThread = new ImageGrabThread(this, grabber); 0340 connect(grabThread, SIGNAL(grabbed()), this, SLOT(redraw())); 0341 grabThread->start(); 0342 grabThread->wait(500); 0343 0344 if (grabThread->wasGrabbingSuccess() == false) { 0345 QMessageBox::warning(this, tr("Warning"), tr( 0346 "Grabbing failed. This may happen if you try\n" 0347 "to grab from an invalid device. Please check\n" 0348 "your grabber settings in the preferences menu."), 0349 QMessageBox::Ok, 0350 Qt::NoButton, 0351 Qt::NoButton); 0352 return false; 0353 } 0354 } 0355 } 0356 else { 0357 QMessageBox::warning(this, tr("Warning"), tr( 0358 "You have to define an image grabber to use.\n" 0359 "This can be set in the preferences menu."), 0360 QMessageBox::Ok, 0361 Qt::NoButton, 0362 Qt::NoButton); 0363 return false; 0364 } 0365 return true; 0366 } 0367 0368 0369 void FrameView::off() { 0370 if ( grabber != 0 ) { 0371 if ( grabber->isGrabberProcess() ) { 0372 grabber->tearDown(); 0373 grabTimer.stop(); 0374 playbackTimer.stop(); 0375 } 0376 } 0377 if (grabThread != 0) { 0378 grabThread->terminate(); 0379 grabThread->wait(); 0380 delete grabThread; 0381 grabThread = 0; 0382 } 0383 delete grabber; 0384 grabber = 0; 0385 0386 isPlayingVideo = false; 0387 setActiveFrame(activeScene, activeFrame); 0388 update(); 0389 } 0390 0391 0392 void FrameView::redraw() { 0393 cameraOutput.load(capturedFile.path()); 0394 update(); 0395 } 0396 0397 0398 void FrameView::nextPlayBack() { 0399 if (0 <= activeScene && 0 <= activeFrame) { 0400 int firstFrame = std::max(activeFrame - mixCount + 1, 0); 0401 if (playbackModeFrame < firstFrame) { 0402 // restart playback 0403 playbackModeFrame = firstFrame; 0404 } else { 0405 ++playbackModeFrame; 0406 } 0407 if (activeFrame < playbackModeFrame) { 0408 // negative number means that the camera output should be shown 0409 playbackModeFrame = -1; 0410 } 0411 update(); 0412 return; 0413 } 0414 // Set display image to camera output 0415 playbackModeFrame = -1; 0416 update(); 0417 } 0418 0419 0420 bool FrameView::setViewMode(ImageMode mode) { 0421 if (mode == this->mode) 0422 return true; 0423 if (!grabber) 0424 return true; 0425 if (mode == imageModePlayback) { 0426 if ( grabber->isGrabberProcess() ) { 0427 playbackTimer.start(1000/playbackSpeed); 0428 } else { 0429 return false; 0430 } 0431 } else if (mode != imageModePlayback) { 0432 if ( grabber->isGrabberProcess() ) { 0433 playbackTimer.stop(); 0434 } 0435 } 0436 this->mode = mode; 0437 return true; 0438 } 0439 0440 void FrameView::setMixCount(int mixCount) { 0441 this->mixCount = mixCount; 0442 } 0443 0444 int FrameView::getViewMode() const { 0445 return mode; 0446 } 0447 0448 void FrameView::setPlaybackSpeed(int playbackSpeed) { 0449 this->playbackSpeed = playbackSpeed; 0450 if ( playbackTimer.isActive() ) { 0451 playbackTimer.setInterval(1000 / playbackSpeed); 0452 } 0453 } 0454 0455 int FrameView::getPlaybackSpeed() const { 0456 return playbackSpeed; 0457 } 0458 0459 void FrameView::fileChanged(const QString& path) { 0460 const char* p = path.toLocal8Bit(); 0461 imageCache.drop(p); 0462 if (isPlayingVideo && 0 <= activeScene) { 0463 update(); 0464 } 0465 }