void TiePointLayerImp::drawLabels(const vector<TiePoint>& points, double sceneSymbolSize, const int viewableBounds[4]) const { double dRotation = 0.0; ViewImp* pView = dynamic_cast<ViewImp*>(getView()); VERIFYNRV(pView != NULL); PerspectiveView* pPerspectiveView = dynamic_cast<PerspectiveView*>(pView); if (pPerspectiveView != NULL) { dRotation = pPerspectiveView->getRotation() * PI / 180.0; } double textScale = sceneSymbolSize / mSymbolSize; LocationType offset; sceneSymbolSize *= 1.2; // push the label off the symbol a little offset.mX = cos(dRotation) * sceneSymbolSize + 0.5; offset.mY = sin(dRotation) * sceneSymbolSize + 0.5; QFont font = QApplication::font(); font.setBold(false); font.setPointSize(12); vector<TiePoint>::const_iterator pPoint; for (pPoint = points.begin(); pPoint != points.end(); ++pPoint) { LocationType point = getPoint(*pPoint); if (isInBounds(point, viewableBounds)) { point += offset; QString strText; strText.sprintf("%d", pPoint-points.begin()+1); LocationType screenCoord; translateDataToScreen(point.mX, point.mY, screenCoord.mX, screenCoord.mY); int screenX = static_cast<int>(screenCoord.mX); int screenY = pView->height() - static_cast<int>(screenCoord.mY); pView->renderText(screenX, screenY, strText, font); } } }
QFont TextObjectImp::getScaledFont(double minSize, double maxSize) { QFont scaledFont = getFont(); GraphicLayer* pLayer = getLayer(); if (pLayer != NULL) { // Scale the point size double pointSize = scaledFont.pointSizeF(); PerspectiveView* pPerspectiveView = dynamic_cast<PerspectiveView*>(pLayer->getView()); if (pPerspectiveView != NULL) { // Zoom percentage double zoomPercent = pPerspectiveView->getZoomPercentage(); pointSize *= zoomPercent / 100.0; // Product DPI ProductView* pProductView = dynamic_cast<ProductView*>(pPerspectiveView); if (pProductView != NULL) { int dpi = pProductView->getDpi(); pointSize *= dpi / 72.0; } } // Restrict to the minimum size if (minSize > 0.0) { pointSize = max(pointSize, minSize); } // Restrict to the maximum size if (maxSize > 0.0) { pointSize = min(pointSize, maxSize); } // Set the scaled point size in the font scaledFont.setPointSizeF(pointSize); } return scaledFont; }
void Kml::generateGroundOverlayLayer(Layer* pLayer, bool visible, int order, const Layer* pGeoLayer, int frame) { VERIFYNRV(pGeoLayer != NULL && pLayer != NULL); PerspectiveView* pView = dynamic_cast<PerspectiveView*>(pLayer->getView()); VERIFYNRV(pView != NULL); // block adding current actions to the view's undo buffer UndoLock lock(pView); double originalAngle = pView->getRotation(); // get rotation for north up double angle(0.0); RasterElement* pRaster = dynamic_cast<RasterElement*>(pGeoLayer->getDataElement()); // get the primary raster element VERIFYNRV(GeoAlgorithms::getAngleToNorth(pRaster, pView, angle) != false); pView->rotateTo(angle); // since north up only rotates, check for left-right reversed LocationType ll, ul, ur, lr; pView->getVisibleCorners(ll, ul, ur, lr); bool imageFlipped(ll.mX > ur.mX); if (imageFlipped) { pView->flipHorizontal(); } pView->refresh(); XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* pGroundOverlay = mXml.addElement("GroundOverlay"); mXml.pushAddPoint(pGroundOverlay); string name = pLayer->getDisplayName(); if (name.empty()) { name = pLayer->getName(); } if (frame >= 0) { name += " frame " + StringUtilities::toDisplayString(frame+1); } mXml.addText(name, mXml.addElement("name")); mXml.addText(pLayer->getDisplayText(), mXml.addElement("description")); mXml.addText(visible ? "1" : "0", mXml.addElement("visibility")); mXml.addText(QString::number(order).toAscii().data(), mXml.addElement("drawOrder")); QString layerId = QString::fromStdString(pLayer->getId()); if (mExportImages) { QByteArray bytes(5 * 1024 * 1024, '\0'); QBuffer buffer(&bytes); QString fileName = layerId; if (frame >= 0) { fileName += QString("/frame%1").arg(frame); mXml.pushAddPoint(mXml.addElement("TimeStamp")); QDateTime dt = QDateTime::currentDateTime(); dt.setTime(QTime(0, 0, frame)); mXml.addText(dt.toString(Qt::ISODate).toStdString(), mXml.addElement("when")); mXml.popAddPoint(); } fileName += ".png"; if (ImageHandler::getSessionItemImage(pLayer, buffer, "PNG", frame)) { mImages[fileName] = bytes; mXml.pushAddPoint(mXml.addElement("Icon")); mXml.addText(fileName.toStdString(), mXml.addElement("href")); mXml.popAddPoint(); } } else { layerId = QUrl::toPercentEncoding(layerId); QString imageUrl(QString("http://localhost:%1/images/%2.png").arg(KMLServer::getSettingKmlServerPort()).arg( layerId)); if (frame >= 0) { imageUrl += QString("?frame=%1").arg(frame); mXml.pushAddPoint(mXml.addElement("TimeStamp")); QDateTime dt = QDateTime::currentDateTime(); dt.setTime(QTime(0, 0, frame)); mXml.addText(dt.toString(Qt::ISODate).toStdString(), mXml.addElement("when")); mXml.popAddPoint(); } mXml.pushAddPoint(mXml.addElement("Icon")); mXml.addText(imageUrl.toStdString(), mXml.addElement("href")); mXml.popAddPoint(); } generateBoundingBox(pGeoLayer); // add ExtendedData const DynamicObject* pMeta = pLayer->getDataElement()->getMetadata(); VERIFYNRV(pMeta); const DataVariant& ked(pMeta->getAttributeByPath("KML/ExtendedData")); if (ked.isValid()) { std::string kedString = ked.toXmlString(); XmlReader reader(Service<MessageLogMgr>()->getLog(), false); XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* kedDoc = reader.parseString(ked.toXmlString()); if (kedDoc != NULL) { XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pKedNode = kedDoc->getDocumentElement(); if (pKedNode != NULL) { bool extraPop = false; if (std::string(A(pKedNode->getNodeName())) != "ExtendedData") { mXml.pushAddPoint(mXml.addElement("ExtendedData")); extraPop = true; } pKedNode = mXml.peekAddPoint()->getOwnerDocument()->importNode(pKedNode, true); mXml.peekAddPoint()->appendChild(pKedNode); if (extraPop) { mXml.popAddPoint(); } } } } mXml.popAddPoint(); // GroundOverlay // restore original rotation if (imageFlipped) { pView->flipHorizontal(); } pView->rotateTo(originalAngle); pView->refresh(); }
void TextObjectImp::updateBoundingBox() { // Get the width and height of the bounding box in screen pixels based on the scaled text image size int iWidth = 0; int iHeight = 0; string text = getSubstitutedText(); if (text.empty() == false) { QString strMessage = QString::fromStdString(text); QFont scaledFont = getScaledFont(); int iMaxSize = 0; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &iMaxSize); int iAlignment = getTextAlignment(); QFontMetrics ftMetrics(scaledFont); QRect boundingBox = ftMetrics.boundingRect(0, 0, iMaxSize, iMaxSize, iAlignment | Qt::TextWordWrap, strMessage); iWidth = boundingBox.width(); iHeight = boundingBox.height(); } // Get the current bounding box LocationType llCorner = getLlCorner(); LocationType urCorner = getUrCorner(); // Use the lower left corner as the anchor and compute the data coordinate of the upper right corner GraphicLayer* pLayer = getLayer(); if (pLayer != NULL) { // Compute the upper right coordinate PerspectiveView* pView = dynamic_cast<PerspectiveView*>(pLayer->getView()); if (pView != NULL) { double zoomFactor = 100.0 / pView->getZoomPercentage(); double xScale = zoomFactor / pLayer->getXScaleFactor(); double yScale = zoomFactor / pLayer->getYScaleFactor(); urCorner.mX = llCorner.mX + (iWidth * xScale); urCorner.mY = llCorner.mY + (iHeight * yScale); } if (dynamic_cast<OrthographicView*>(pLayer->getView()) != NULL) { double dScreenX = 0.0; double dScreenY = 0.0; pLayer->translateDataToScreen(llCorner.mX, llCorner.mY, dScreenX, dScreenY); pLayer->translateScreenToData(dScreenX + iWidth, dScreenY + iHeight, urCorner.mX, urCorner.mY); } } else { urCorner.mX = llCorner.mX + iWidth; urCorner.mY = llCorner.mY + iHeight; } // Update the bounding box and selection handles setBoundingBox(llCorner, urCorner); updateHandles(); mUpdateBoundingBox = false; }
ValidationResultType MovieExporter::validate(const PlugInArgList* pArgList, string& errorMessage) const { /* validate some arguments */ ValidationResultType result = ExporterShell::validate(pArgList, errorMessage); if (result != VALIDATE_SUCCESS) { return result; } View* pView = pArgList->getPlugInArgValue<View>(Exporter::ExportItemArg()); if (pView == NULL) { errorMessage = "No view specified. Nothing to export."; return VALIDATE_FAILURE; } AnimationController* pController = pView->getAnimationController(); if (pController == NULL) { errorMessage = "No animation is associated with this view. Nothing to export."; return VALIDATE_FAILURE; } /* validate the framerate */ boost::rational<int> expectedFrameRate; // first, get the framerate from the arg list // next, try the option widget // next, get from the animation controller // finally, default to the config settings int framerateNum = 0; int framerateDen = 0; if (pArgList->getPlugInArgValue("Framerate Numerator", framerateNum) && pArgList->getPlugInArgValue("Framerate Denominator", framerateDen)) { try { expectedFrameRate.assign(framerateNum, framerateDen); } catch (const boost::bad_rational&) { errorMessage = "Invalid framerate specified"; return VALIDATE_FAILURE; } } if (expectedFrameRate == 0) { if (mpOptionWidget.get() != NULL) { FramerateWidget* pFramerateWidget = mpOptionWidget->getFramerateWidget(); VERIFYRV(pFramerateWidget != NULL, VALIDATE_FAILURE); expectedFrameRate = pFramerateWidget->getFramerate(); } if (expectedFrameRate == 0) { expectedFrameRate = getFrameRate(pController); } if (expectedFrameRate == 0) { errorMessage = "No framerate specified"; return VALIDATE_FAILURE; } } boost::rational<int> actualFrameRate = convertToValidFrameRate(expectedFrameRate); if (actualFrameRate != 0) { if (actualFrameRate < getFrameRate(pController)) { errorMessage = "The selected output frame rate may not encode all the frames in the movie. " "Frames may be dropped."; result = VALIDATE_INFO; } if (actualFrameRate != expectedFrameRate) { QString msg = QString("The selected animation frame rate (%1/%2 fps) can not be represented in the " "selected movie format. A frame rate of %3/%4 fps is being used instead.") .arg(expectedFrameRate.numerator()) .arg(expectedFrameRate.denominator()) .arg(actualFrameRate.numerator()) .arg(actualFrameRate.denominator()); if (!errorMessage.empty()) { errorMessage += "\n\n"; } errorMessage += msg.toStdString(); result = VALIDATE_INFO; } } // check the frame resolution int resolutionX(-1); int resolutionY(-1); bool fullResolution(false); bool fixedResolution(false); if (!pArgList->getPlugInArgValue("Resolution X", resolutionX) || !pArgList->getPlugInArgValue("Resolution Y", resolutionY)) { if (mpOptionWidget.get() != NULL) { ViewResolutionWidget* pResolutionWidget = mpOptionWidget->getResolutionWidget(); VERIFYRV(pResolutionWidget != NULL, VALIDATE_FAILURE); switch(pResolutionWidget->getResolutionType()) { case OptionsMovieExporter::VIEW_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = false; break; case OptionsMovieExporter::FULL_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = true; break; case OptionsMovieExporter::FIXED_RESOLUTION: { QSize resolution = pResolutionWidget->getResolution(); resolutionX = resolution.width(); resolutionY = resolution.height(); fullResolution = false; fixedResolution = true; break; } default: break; // nothing } } else { switch(StringUtilities::fromXmlString<OptionsMovieExporter::ResolutionType>( OptionsMovieExporter::getSettingResolutionType())) { case OptionsMovieExporter::VIEW_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = false; break; case OptionsMovieExporter::FULL_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = true; break; case OptionsMovieExporter::FIXED_RESOLUTION: resolutionX = OptionsMovieExporter::getSettingWidth(); resolutionY = OptionsMovieExporter::getSettingHeight(); fullResolution = false; fixedResolution = true; break; default: break; // nothing } } } if (resolutionX <= 0 || resolutionY <= 0) { QWidget* pWidget = pView->getWidget(); if (pWidget != NULL) { resolutionX = pWidget->width(); resolutionY = pWidget->height(); if (fullResolution) { PerspectiveView* pPersp = dynamic_cast<PerspectiveView*>(pView); if (pPersp != NULL && pPersp->getZoomPercentage() > 100.) { fullResolution = false; if (mpProgress != NULL) { errorMessage += "Full resolution export will be smaller than " "view export, export resolution will be changed to current view size.\n"; if (result == VALIDATE_SUCCESS) { result = VALIDATE_INFO; } } } else { // translate to data coordinate double x1 = 0.0; double y1 = 0.0; double x2 = 0.0; double y2 = 0.0; pView->translateScreenToWorld(0.0, 0.0, x1, y1); pView->translateScreenToWorld(resolutionX, resolutionY, x2, y2); resolutionX = (x2 > x1) ? (x2 - x1 + 0.5) : (x1 - x2 + 0.5); resolutionY = (y2 > y1) ? (y2 - y1 + 0.5) : (y1 - y2 + 0.5); } } } else { errorMessage += "Can't determine output resolution.\n"; return VALIDATE_FAILURE; } } int oldResX = resolutionX; int oldResY = resolutionY; if (!convertToValidResolution(resolutionX, resolutionY) || (fixedResolution && (resolutionX != oldResX || resolutionY != oldResY))) { if (mpOptionWidget.get() != NULL) { ViewResolutionWidget* pResolutionWidget = mpOptionWidget->getResolutionWidget(); VERIFYRV(pResolutionWidget != NULL, VALIDATE_FAILURE); pResolutionWidget->setResolution(QSize(resolutionX, resolutionY), pResolutionWidget->getResolutionType()); } errorMessage += "The export resolution does not meet the requirements of the the selected CODEC and will be adjusted.\n"; if (result != VALIDATE_FAILURE) { result = VALIDATE_INFO; } } return result; }
bool MovieExporter::execute(PlugInArgList* pInArgList, PlugInArgList* pOutArgList) { FileDescriptor* pFileDescriptor(NULL); string filename; View* pView(NULL); AnimationController* pController(NULL); FrameType eType; AVOutputFormat* pOutFormat(NULL); int resolutionX(-1); int resolutionY(-1); rational<int> framerate(0); unsigned int bitrate(0); double startExport(0.0); double stopExport(0.0); bool fullResolution(false); mpProgress = NULL; mpStep = StepResource("Export movie", "app", "2233BFC9-9C51-4e31-A8C5-2512925CBE6D"); // get input arguments and log some useful info about them { // scope the MessageResource MessageResource pMsg("Input arguments", "app", "4551F478-E182-4b56-B88F-6682F0E3A2CF"); mpProgress = pInArgList->getPlugInArgValue<Progress>(Executable::ProgressArg()); pMsg->addBooleanProperty("Progress Present", (mpProgress != NULL)); pFileDescriptor = pInArgList->getPlugInArgValue<FileDescriptor>(Exporter::ExportDescriptorArg()); if (pFileDescriptor == NULL) { log_error("No file specified"); return false; } pMsg->addProperty("Destination", pFileDescriptor->getFilename()); filename = pFileDescriptor->getFilename().getFullPathAndName(); pView = pInArgList->getPlugInArgValue<View>(Exporter::ExportItemArg()); if (pView == NULL) { log_error("No view specified"); return false; } pController = pView->getAnimationController(); if (pController == NULL) { log_error("No animation controller specified"); return false; } pMsg->addProperty("Animation Controller Name", pController->getName()); pOutFormat = getOutputFormat(); if (pOutFormat == NULL) { log_error("Can't determine output format or format not supported."); return false; } pMsg->addProperty("Format", pOutFormat->long_name); bool resolutionFromInputArgs(false); if (!pInArgList->getPlugInArgValue("Resolution X", resolutionX) || !pInArgList->getPlugInArgValue("Resolution Y", resolutionY)) { if (mpOptionWidget.get() != NULL) { ViewResolutionWidget* pResolutionWidget = mpOptionWidget->getResolutionWidget(); VERIFY(pResolutionWidget != NULL); switch(pResolutionWidget->getResolutionType()) { case OptionsMovieExporter::VIEW_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = false; break; case OptionsMovieExporter::FULL_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = true; break; case OptionsMovieExporter::FIXED_RESOLUTION: { QSize resolution = pResolutionWidget->getResolution(); resolutionX = resolution.width(); resolutionY = resolution.height(); fullResolution = false; break; } default: break; // nothing } } else { switch(StringUtilities::fromXmlString<OptionsMovieExporter::ResolutionType>( OptionsMovieExporter::getSettingResolutionType())) { case OptionsMovieExporter::VIEW_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = false; break; case OptionsMovieExporter::FULL_RESOLUTION: resolutionX = -1; resolutionY = -1; fullResolution = true; break; case OptionsMovieExporter::FIXED_RESOLUTION: resolutionX = OptionsMovieExporter::getSettingWidth(); resolutionY = OptionsMovieExporter::getSettingHeight(); fullResolution = false; break; default: break; // nothing } } } else { resolutionFromInputArgs = true; } if (resolutionX <= 0 || resolutionY <= 0) { QWidget* pWidget = pView->getWidget(); if (pWidget != NULL) { resolutionX = pWidget->width(); resolutionY = pWidget->height(); resolutionFromInputArgs = false; if (fullResolution) { PerspectiveView* pPersp = dynamic_cast<PerspectiveView*>(pView); if (pPersp != NULL && pPersp->getZoomPercentage() > 100.) { fullResolution = false; if (mpProgress != NULL) { mpProgress->updateProgress("Full resolution export will be smaller than " "view export, changing export resolution to current view size.", 0, WARNING); } } else { // translate to data coordinate double x1 = 0.0; double y1 = 0.0; double x2 = 0.0; double y2 = 0.0; pView->translateScreenToWorld(0.0, 0.0, x1, y1); pView->translateScreenToWorld(resolutionX, resolutionY, x2, y2); resolutionX = (x2 > x1) ? (x2 - x1 + 0.5) : (x1 - x2 + 0.5); resolutionY = (y2 > y1) ? (y2 - y1 + 0.5) : (y1 - y2 + 0.5); } } } else { log_error("Can't determine output resolution."); return false; } } int oldResX = resolutionX; int oldResY = resolutionY; if (!convertToValidResolution(resolutionX, resolutionY) || (resolutionFromInputArgs && (resolutionX != oldResX || resolutionY != oldResY))) { stringstream msg; msg << "The export resolution does not meet the requirements of the the selected CODEC. " "The input arguments were X resolution of " << oldResX << " and Y resolution of " << oldResY << "." << "The adjusted resolution was (" << resolutionX << ", " << resolutionY << ")"; log_error(msg.str()); return false; } pMsg->addProperty("Resolution", QString("%1 x %2").arg(resolutionX).arg(resolutionY).toStdString()); int framerateNum = 0; int framerateDen = 0; // first, get the framerate from the arg list // next, try the option widget // next, get from the animation controller if (pInArgList->getPlugInArgValue("Framerate Numerator", framerateNum) && pInArgList->getPlugInArgValue("Framerate Denominator", framerateDen)) { try { framerate.assign(framerateNum, framerateDen); } catch (const boost::bad_rational&) { // Do nothing; the code below handles this case } } if (framerate == 0) { if (mpOptionWidget.get() != NULL) { FramerateWidget* pFramerateWidget = mpOptionWidget->getFramerateWidget(); VERIFY(pFramerateWidget != NULL); framerate = pFramerateWidget->getFramerate(); } else { framerate = getFrameRate(pController); } } if (framerate == 0) { log_error("No framerate specified"); return false; } // Validate the framerate boost::rational<int> validFrameRate = convertToValidFrameRate(framerate); if (validFrameRate != framerate) { QString msg = QString("The selected animation frame rate (%1/%2 fps) can not be represented in the " "selected movie format. A frame rate of %3/%4 fps is being used instead.") .arg(framerate.numerator()) .arg(framerate.denominator()) .arg(validFrameRate.numerator()) .arg(validFrameRate.denominator()); mpProgress->updateProgress(msg.toStdString(), 0, WARNING); framerate = validFrameRate; } pMsg->addProperty("Framerate", QString("%1/%2").arg(framerate.numerator()).arg(framerate.denominator()).toStdString()); if (!pInArgList->getPlugInArgValue("Bitrate", bitrate)) { if (mpOptionWidget.get() != NULL) { BitrateWidget* pBitrateWidget = mpOptionWidget->getBitrateWidget(); VERIFY(pBitrateWidget != NULL); bitrate = pBitrateWidget->getBitrate(); } else { bitrate = OptionsMovieExporter::getSettingBitrate(); } } pMsg->addProperty("Bitrate", QString::number(bitrate).toStdString()); eType = pController->getFrameType(); if (!pInArgList->getPlugInArgValue("Start Export", startExport)) { if (mpOptionWidget.get() != NULL) { AnimationFrameSubsetWidget* pSubsetWidget = mpOptionWidget->getSubsetWidget(); VERIFY(pSubsetWidget != NULL); startExport = pSubsetWidget->getStartFrame(); } else { if (pController->getBumpersEnabled()) { startExport = pController->getStartBumper(); } else { startExport = pController->getStartFrame(); } } } else { // adjust to 0-based since the input arg uses 1-based --startExport; } if (!pInArgList->getPlugInArgValue("Stop Export", stopExport)) { if (mpOptionWidget.get() != NULL) { AnimationFrameSubsetWidget* pSubsetWidget = mpOptionWidget->getSubsetWidget(); VERIFY(pSubsetWidget != NULL); stopExport = pSubsetWidget->getStopFrame(); } else { if (pController->getBumpersEnabled()) { stopExport = pController->getStopBumper(); } else { stopExport = pController->getStopFrame(); } } } else { // adjust to 0-based since the input arg users 1-based --stopExport; } string valueType("Time"); if (eType == FRAME_ID) { valueType = "Frame"; } pMsg->addProperty("Start "+valueType, QString::number(startExport).toStdString()); pMsg->addProperty("Stop "+valueType, QString::number(stopExport).toStdString()); } AvFormatContextResource pFormat(pOutFormat); VERIFY(pFormat != NULL); snprintf(pFormat->filename, sizeof(pFormat->filename), "%s", filename.c_str()); AvStreamResource pVideoStream(pFormat, pOutFormat->video_codec); if (pVideoStream == NULL) { log_error("Unable to create video stream."); return false; } /** * allow changing of: * dia_size/ */ AVCodecContext* pCodecContext = pVideoStream->codec; if (!setAvCodecOptions(pCodecContext)) { log_error("Unable to initialize CODEC options"); return false; } // set time_base, width, height, and bitrate here since // they can be passed in via the input args pCodecContext->width = resolutionX; pCodecContext->height = resolutionY; pCodecContext->bit_rate = bitrate * 1000; // the AVCodecContext wants a time_base which is // the inverse of fps. pCodecContext->time_base.num = framerate.denominator(); pCodecContext->time_base.den = framerate.numerator(); if (av_set_parameters(pFormat, NULL) < 0) { log_error("Invalid output format parameters."); return false; } if (!open_video(pFormat, pVideoStream)) { log_error("Unable to initialize video stream."); return false; } if (url_fopen(&pFormat->pb, filename.c_str(), URL_WRONLY) < 0) { log_error("Could not open the output file. Ensure the destination directory is writable."); return false; } av_write_header(pFormat); // calculate time interval if ((framerate < getFrameRate(pController)) && (mpProgress != NULL)) { mpProgress->updateProgress("The selected output frame rate may not encode all the frames in the movie. " "Frames may be dropped.", 0, WARNING); } // do not use the boost::rational<int> overloaded operator '/' since it truncates type double to int double interval = pController->getIntervalMultiplier() * framerate.denominator() / framerate.numerator(); // export the frames AVFrame* pTmpPicture = alloc_picture(PIX_FMT_RGBA32, pCodecContext->width, pCodecContext->height); if (pTmpPicture == NULL) { QString msg("Unable to allocate frame buffer of size %1 x %2"); log_error(msg.arg(pCodecContext->width).arg(pCodecContext->height).toStdString()); return false; } QImage image(pTmpPicture->data[0], pCodecContext->width, pCodecContext->height, QImage::Format_ARGB32); // For frame id based animation, each band of the data set fills one second of animation. // If the requested frame rate for export is 15 fps, then each band is replicated 15 times. The execution // loop uses a pseudo time value, video_pts, to walk through the animation. The interval between // exported frames is the inverse of the frame rate, e.g., for 15 fps the interval is 0.06667. // To fully export the last requested frame, we need to add just under an extra pseudo second to // the end time - stopExport. If we added a full extra second, we would export one video frame of the band past // the last requested frame - could cause crash if the last request frame was the last band. if (eType == FRAME_ID) { stopExport += 0.99; } bool drawClassMarkings = fullResolution && Service<ConfigurationSettings>()->getSettingDisplayClassificationMarkings(); QString classText; QFont classFont; QColor classColor; QPoint classPositionTop; QPoint classPositionBottom; const int shadowOffset = 2; if (drawClassMarkings) { const int topMargin = 1; const int bottomMargin = 4; const int leftMargin = 5; const int rightMargin = 5; classText = QString::fromStdString(pView->getClassificationText()); classColor = COLORTYPE_TO_QCOLOR(pView->getClassificationColor()); pView->getClassificationFont(classFont); QFontMetrics fontMetrics(classFont); int classWidth = fontMetrics.width(classText); int classHeight = fontMetrics.ascent(); int topX((pCodecContext->width / 2) - (classWidth / 2) + shadowOffset); int bottomX((pCodecContext->width / 2) - (classWidth / 2)); // determine the classification position switch (pView->getClassificationPosition()) { case TOP_LEFT_BOTTOM_LEFT: topX = leftMargin + shadowOffset; bottomX = leftMargin + shadowOffset; break; case TOP_LEFT_BOTTOM_RIGHT: topX = leftMargin + shadowOffset; bottomX = pCodecContext->width - rightMargin - classWidth + shadowOffset; break; case TOP_RIGHT_BOTTOM_LEFT: topX = pCodecContext->width - rightMargin - classWidth + shadowOffset; bottomX = leftMargin + shadowOffset; break; case TOP_RIGHT_BOTTOM_RIGHT: topX = pCodecContext->width - rightMargin - classWidth + shadowOffset; bottomX = pCodecContext->width - rightMargin - classWidth + shadowOffset; break; default: // nothing to do, markings centered by default break; } int screenY = 1 + classHeight; classPositionTop = QPoint(topX, 1 + classHeight); classPositionBottom = QPoint(bottomX, pCodecContext->height - 1); } // make sure controller is not running prior to export. Save current state and restore after export finished AnimationState savedAnimationState = pController->getAnimationState(); pController->setAnimationState(STOP); for (double video_pts = startExport; video_pts <= stopExport; video_pts += interval) { if (isAborted() == true) { // reset resources to close output file so it can be deleted pVideoStream = AvStreamResource(); pFormat = AvFormatContextResource(NULL); mpPicture = NULL; free(mpVideoOutbuf); mpVideoOutbuf = NULL; remove(filename.c_str()); if (mpProgress != NULL) { mpProgress->updateProgress("Export aborted", 0, ABORT); } pController->setAnimationState(savedAnimationState); mpStep->finalize(Message::Abort); return false; } // generate the next frame pController->setCurrentFrame(video_pts); if (mpProgress != NULL) { double progressValue = (video_pts - startExport) / (stopExport - startExport) * 100.0; mpProgress->updateProgress("Saving movie", static_cast<int>(progressValue), NORMAL); } if (fullResolution) { QSize totalSize(pCodecContext->width, pCodecContext->height); QSize subImageSize(pView->getWidget()->width(), pView->getWidget()->height()); // Make sure that the sub-image and the main image have the same aspect ratio to // minimize wasted tile space if (pCodecContext->width > pCodecContext->height) { subImageSize.setWidth( totalSize.width() / static_cast<float>(totalSize.height()) * subImageSize.height() + 0.5); } else { subImageSize.setHeight( totalSize.height() / static_cast<float>(totalSize.width()) * subImageSize.width() + 0.5); } // Remove pan and zoom limits so they don't interfere with the subimage grab // and restore them when done SpatialDataView* pSdv = dynamic_cast<SpatialDataView*>(pView); PanLimitType panLimit; if (pSdv != NULL) { panLimit = pSdv->getPanLimit(); pSdv->setPanLimit(NO_LIMIT); } View::SubImageIterator* pSubImage(pView->getSubImageIterator(totalSize, subImageSize)); if (pSubImage == NULL) { if (pSdv != NULL) { pSdv->setPanLimit(panLimit); } if (mpProgress != NULL) { mpProgress->updateProgress("Unable to render full scale data", 0, ERRORS); } pController->setAnimationState(savedAnimationState); mpStep->finalize(Message::Failure); return false; } QPainter outPainter(&image); QPoint origin(0, image.height() - subImageSize.height()); while (pSubImage->hasNext()) { QImage subImage; if (!pSubImage->next(subImage)) { if (pSdv != NULL) { pSdv->setPanLimit(panLimit); } if (mpProgress != NULL) { mpProgress->updateProgress("An error occurred when generating the image", 0, ERRORS); } pController->setAnimationState(savedAnimationState); mpStep->finalize(Message::Failure); delete pSubImage; return false; } // copy this subimage to the output buffer outPainter.drawImage(origin, subImage); int newX = origin.x() + subImage.width(); int newY = origin.y(); if (newX >= totalSize.width()) { newY -= subImage.height(); newX = 0; } origin = QPoint(newX, newY); } delete pSubImage; if (drawClassMarkings) { outPainter.setFont(classFont); outPainter.setPen(Qt::black); outPainter.drawText(classPositionTop, classText); outPainter.drawText(classPositionBottom, classText); outPainter.setPen(classColor); outPainter.drawText(classPositionTop - QPoint(shadowOffset, shadowOffset), classText); outPainter.drawText(classPositionBottom - QPoint(shadowOffset, shadowOffset), classText); } if (pSdv != NULL) { pSdv->setPanLimit(panLimit); } } else { pView->getCurrentImage(image); } img_convert(reinterpret_cast<AVPicture*>(mpPicture), pCodecContext->pix_fmt, reinterpret_cast<AVPicture*>(pTmpPicture), PIX_FMT_RGBA32, pCodecContext->width, pCodecContext->height); if (!write_video_frame(pFormat, pVideoStream)) { // reset resources to close output file so it can be deleted pVideoStream = AvStreamResource(); pFormat = AvFormatContextResource(NULL); mpPicture = NULL; free(mpVideoOutbuf); mpVideoOutbuf = NULL; remove(filename.c_str()); string msg = "Can't write frame."; log_error(msg.c_str()); pController->setAnimationState(savedAnimationState); return false; } } for (int frame = 0; frame < pCodecContext->delay; ++frame) { write_video_frame(pFormat, pVideoStream); } av_write_trailer(pFormat); if (mpProgress != NULL) { mpProgress->updateProgress("Finished saving movie", 100, NORMAL); } free(mpVideoOutbuf); mpVideoOutbuf = NULL; pController->setAnimationState(savedAnimationState); mpStep->finalize(Message::Success); return true; }