void KnobGuiColor::onDimensionsMadeVisible(bool visible) { KnobColorPtr knob = _knob.lock(); if (!knob) { return; } int nDims = knob->getNDimensions(); QColor colors[4]; colors[0].setRgbF(0.851643, 0.196936, 0.196936); colors[1].setRgbF(0, 0.654707, 0); colors[2].setRgbF(0.345293, 0.345293, 1); colors[3].setRgbF(0.398979, 0.398979, 0.398979); for (int i = 0; i < nDims; ++i) { SpinBox* sb = 0; getSpinBox(DimIdx(i), &sb); assert(sb); if (!visible) { sb->setAdditionalDecorationTypeEnabled(LineEdit::eAdditionalDecorationColoredUnderlinedText, false); } else { sb->setAdditionalDecorationTypeEnabled(LineEdit::eAdditionalDecorationColoredUnderlinedText, true, colors[i]); } } }
void KnobGuiColor::onDialogCurrentColorChanged(const QColor & color) { KnobColorPtr knob = _knob.lock(); bool isSimple = _useSimplifiedUI; int nDims = knob->getDimension(); if (nDims == 1) { knob->setValue(color.redF(), ViewSpec::all(), 0); } else if (nDims == 3) { knob->setValues(isSimple ? color.redF() : Color::from_func_srgb( color.redF() ), isSimple ? color.greenF() : Color::from_func_srgb( color.greenF() ), isSimple ? color.blueF() : Color::from_func_srgb( color.blueF() ), ViewSpec::all(), eValueChangedReasonNatronInternalEdited); } else if (nDims == 4) { knob->setValues(isSimple ? color.redF() : Color::from_func_srgb( color.redF() ), isSimple ? color.greenF() : Color::from_func_srgb( color.greenF() ), isSimple ? color.blueF() : Color::from_func_srgb( color.blueF() ), color.alphaF(), ViewSpec::all(), eValueChangedReasonNatronInternalEdited); } if ( getGui() ) { getGui()->setDraftRenderEnabled(true); } }
KnobGuiColor::KnobGuiColor(const KnobGuiPtr& knobUI, ViewIdx view) : KnobGuiValue(knobUI, view) , _knob( toKnobColor(knobUI->getKnob()) ) , _colorLabel(0) , _colorDialogButton(0) , _useSimplifiedUI(false) , _uiColorspaceLut(0) , _internalColorspaceLut(0) { KnobColorPtr knob = _knob.lock(); if (!knob) { return; } _useSimplifiedUI = knob && knob->isSimplified(); if (!_useSimplifiedUI) { DimIdx singleDim; bool singleDimEnabled = knobUI->isSingleDimensionalEnabled(&singleDim); if (knobUI->getLayoutType() == KnobGui::eKnobLayoutTypeViewerUI && !singleDimEnabled) { _useSimplifiedUI = true; } } const std::string& uiName = knob->getUIColorspaceName(); const std::string& internalName = knob->getInternalColorspaceName(); _uiColorspaceLut = Color::LutManager::findLut(uiName); _internalColorspaceLut = Color::LutManager::findLut(internalName); }
void KnobGuiColor::onDialogCurrentColorChanged(const QColor & color) { KnobColorPtr knob = _knob.lock(); bool isSimple = _useSimplifiedUI; int nDims = knob->getNDimensions(); std::vector<double> values(nDims); values[0] = isSimple ? color.redF() : Color::from_func_srgb( color.redF() ); if (nDims >= 3) { values[1] = isSimple ? color.greenF() : Color::from_func_srgb( color.greenF() ); values[2] = isSimple ? color.blueF() : Color::from_func_srgb( color.blueF() ); if (nDims == 4) { values[3] = color.alphaF(); } } KnobGuiPtr knobUI = getKnobGui(); knob->setValueAcrossDimensions(values, DimIdx(0), getView(), eValueChangedReasonUserEdited); if ( knobUI->getGui() ) { knobUI->getGui()->setDraftRenderEnabled(true); } }
void KnobGuiColor::onDialogCurrentColorChanged(const QColor & color) { KnobColorPtr knob = _knob.lock(); if (!knob) { return; } int nDims = knob->getNDimensions(); std::vector<double> values(nDims); values[0] = color.redF(); convertFromUIToInternalColorspace(&values[0]); if (nDims > 1) { values[1] = color.greenF(); convertFromUIToInternalColorspace(&values[1]); } if (nDims > 2) { values[2] = color.blueF(); convertFromUIToInternalColorspace(&values[2]); } if (nDims > 3) { values[3] = color.alphaF(); } KnobGuiPtr knobUI = getKnobGui(); knob->setValueAcrossDimensions(values, DimIdx(0), getView(), eValueChangedReasonUserEdited); if ( knobUI->getGui() ) { knobUI->getGui()->setDraftRenderEnabled(true); } }
void KnobGuiColor::connectKnobSignalSlots() { KnobColorPtr knob = _knob.lock(); QObject::connect( this, SIGNAL(dimensionSwitchToggled(bool)), knob.get(), SLOT(onDimensionSwitchToggled(bool)) ); QObject::connect( knob.get(), SIGNAL(mustActivateAllDimensions()), this, SLOT(onMustShowAllDimension()) ); QObject::connect( knob.get(), SIGNAL(pickingEnabled(bool)), this, SLOT(setPickingEnabled(bool)) ); }
void KnobGuiColor::connectKnobSignalSlots() { KnobColorPtr knob = _knob.lock(); if (!knob) { return; } QObject::connect( knob.get(), SIGNAL(pickingEnabled(ViewSetSpec,bool)), this, SLOT(onInternalKnobPickingEnabled(ViewSetSpec,bool)) ); }
void KnobGuiColor::setPickingEnabledInternal(bool enabled) { KnobColorPtr knob = _knob.lock(); if ( knob->getHolder()->getApp() ) { if (enabled) { getKnobGui()->getGui()->registerNewColorPicker( knob, getView() ); } else { getKnobGui()->getGui()->removeColorPicker( knob, getView() ); } } }
void KnobGuiColor::onColorLabelPickingEnabled(bool enabled) { KnobColorPtr knob = _knob.lock(); KnobGuiPtr knobUI = getKnobGui(); if ( knob->getHolder()->getApp() ) { if (enabled) { knobUI->getGui()->registerNewColorPicker( _knob.lock(), getView() ); } else { knobUI->getGui()->removeColorPicker( _knob.lock(), getView()); } } }
void KnobGuiColor::getIncrements(std::vector<double>* increments) const { KnobColorPtr knob = _knob.lock(); if (!knob) { return; } int nDims = knob->getNDimensions(); increments->resize(nDims); for (int i = 0; i < nDims; ++i) { (*increments)[i] = 0.001; } }
void KnobGuiColor::onDimensionsFolded() { KnobColorPtr knob = _knob.lock(); int nDims = knob->getDimension(); for (int i = 0; i < nDims; ++i) { SpinBox* sb = 0; getSpinBox(i, &sb); assert(sb); sb->setUseLineColor(false, Qt::red); } Q_EMIT dimensionSwitchToggled(false); }
void KnobGuiColor::getDecimals(std::vector<int>* decimals) const { KnobColorPtr knob = _knob.lock(); if (!knob) { return; } int nDims = knob->getNDimensions(); decimals->resize(nDims); for (int i = 0; i < nDims; ++i) { (*decimals)[i] = 6; } }
void KnobGuiColor::onDimensionsExpanded() { QColor colors[4]; colors[0].setRgbF(0.851643, 0.196936, 0.196936); colors[1].setRgbF(0, 0.654707, 0); colors[2].setRgbF(0.345293, 0.345293, 1); colors[3].setRgbF(0.398979, 0.398979, 0.398979); KnobColorPtr knob = _knob.lock(); int nDims = knob->getDimension(); for (int i = 0; i < nDims; ++i) { SpinBox* sb = 0; Label* label = 0; getSpinBox(i, &sb, &label); assert(sb); sb->setUseLineColor(true, colors[i]); } Q_EMIT dimensionSwitchToggled(true); }
NATRON_NAMESPACE_ENTER // those parameters should be ignored (they are always secret in Natron) #define kOCIOParamInputSpace "ocioInputSpace" #define kOCIOParamOutputSpace "ocioOutputSpace" // those parameters should not have their options in the help file if generating markdown, // because the options are dinamlically constructed at run-time from the OCIO config. #define kOCIOParamInputSpaceChoice "ocioInputSpaceIndex" #define kOCIOParamOutputSpaceChoice "ocioOutputSpaceIndex" #define kOCIODisplayPluginIdentifier "fr.inria.openfx.OCIODisplay" #define kOCIODisplayParamDisplay "display" #define kOCIODisplayParamDisplayChoice "displayIndex" #define kOCIODisplayParamView "view" #define kOCIODisplayParamViewChoice "viewIndex" // not yet implemented (see OCIOCDLTransform.cpp) //#define kOCIOCDLTransformPluginIdentifier "fr.inria.openfx.OCIOCDLTransform" //#define kOCIOCDLTransformParamCCCID "cccId" //#define kOCIOCDLTransformParamCCCIDChoice "cccIdIndex" #define kOCIOFileTransformPluginIdentifier "fr.inria.openfx.OCIOFileTransform" #define kOCIOFileTransformParamCCCID "cccId" #define kOCIOFileTransformParamCCCIDChoice "cccIdIndex" // genHTML: true for live HTML output for the internal web-server, false for markdown output QString Node::makeDocumentation(bool genHTML) const { QString ret; QString markdown; QTextStream ts(&ret); QTextStream ms(&markdown); QString pluginID, pluginLabel, pluginDescription, pluginIcon; int majorVersion = getMajorVersion(); int minorVersion = getMinorVersion(); std::vector<std::string> pluginGroup; bool pluginDescriptionIsMarkdown = false; QVector<QStringList> inputs; QVector<QStringList> items; { PluginPtr plugin = getPlugin(); assert(plugin); pluginID = QString::fromUtf8(plugin->getPluginID().c_str()); pluginLabel = QString::fromUtf8( Plugin::makeLabelWithoutSuffix( plugin->getPluginLabel() ).c_str()); pluginDescription = QString::fromUtf8( plugin->getPropertyUnsafe<std::string>(kNatronPluginPropDescription).c_str() ); pluginIcon = QString::fromUtf8(plugin->getPropertyUnsafe<std::string>(kNatronPluginPropIconFilePath).c_str()); pluginGroup = plugin->getPropertyNUnsafe<std::string>(kNatronPluginPropGrouping); pluginDescriptionIsMarkdown = plugin->getPropertyUnsafe<bool>(kNatronPluginPropDescriptionIsMarkdown); for (int i = 0; i < _imp->effect->getNInputs(); ++i) { QStringList input; input << convertFromPlainTextToMarkdown( QString::fromStdString( getInputLabel(i) ), genHTML, true ); input << convertFromPlainTextToMarkdown( QString::fromStdString( getInputHint(i) ), genHTML, true ); input << ( isInputOptional(i) ? tr("Yes") : tr("No") ); inputs.push_back(input); // Don't show more than doc for 4 inputs otherwise it will just clutter the page if (i == 3) { break; } } } // check for plugin icon QString pluginIconUrl; if ( !pluginIcon.isEmpty() ) { QFile iconFile(pluginIcon); if ( iconFile.exists() ) { if (genHTML) { pluginIconUrl.append( QString::fromUtf8("/LOCAL_FILE/") ); pluginIconUrl.append(pluginIcon); pluginIconUrl.replace( QString::fromUtf8("\\"), QString::fromUtf8("/") ); } else { pluginIconUrl.append(pluginID); pluginIconUrl.append(QString::fromUtf8(".png")); } } } // check for extra markdown file QString extraMarkdown; QString pluginMD = pluginIcon; pluginMD.replace( QString::fromUtf8(".png"), QString::fromUtf8(".md") ); QFile pluginMarkdownFile(pluginMD); if ( pluginMarkdownFile.exists() ) { if ( pluginMarkdownFile.open(QIODevice::ReadOnly | QIODevice::Text) ) { extraMarkdown = QString::fromUtf8( pluginMarkdownFile.readAll() ); pluginMarkdownFile.close(); } } // generate knobs info KnobsVec knobs = getEffectInstance()->getKnobs_mt_safe(); for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { #pragma message WARN("TODO: restore getDefaultIsSecret from RB-2.2") //if ( (*it)->getDefaultIsSecret() ) { if ( (*it)->getIsSecret() ) { continue; } if ((*it)->getKnobDeclarationType() != KnobI::eKnobDeclarationTypePlugin) { continue; } // do not escape characters in the scriptName, since it will be put between backquotes QString knobScriptName = /*NATRON_NAMESPACE::convertFromPlainTextToMarkdown(*/ QString::fromUtf8( (*it)->getName().c_str() )/*, genHTML, true)*/; QString knobLabel = NATRON_NAMESPACE::convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getLabel().c_str() ), genHTML, true); QString knobHint = NATRON_NAMESPACE::convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getHintToolTip().c_str() ), genHTML, true); // totally ignore the documentation for these parameters (which are always secret in Natron) if ( knobScriptName.startsWith( QString::fromUtf8("NatronOfxParam") ) || knobScriptName == QString::fromUtf8("exportAsPyPlug") || knobScriptName == QString::fromUtf8(kOCIOParamInputSpace) || knobScriptName == QString::fromUtf8(kOCIOParamOutputSpace) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamDisplay) ) ) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamView) ) ) || //( ( pluginID == QString::fromUtf8(kOCIOCDLTransformPluginIdentifier) ) && // ( knobScriptName == QString::fromUtf8(kOCIOCDLTransformParamCCCID) ) ) || ( ( pluginID == QString::fromUtf8(kOCIOFileTransformPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIOFileTransformParamCCCID) ) ) || false) { continue; } QString defValuesStr, knobType; std::vector<std::pair<QString, QString> > dimsDefaultValueStr; KnobIntPtr isInt = toKnobInt(*it); KnobChoicePtr isChoice = toKnobChoice(*it); KnobBoolPtr isBool = toKnobBool(*it); KnobDoublePtr isDbl = toKnobDouble(*it); KnobStringPtr isString = toKnobString(*it); bool isLabel = isString && isString->isLabel(); KnobSeparatorPtr isSep = toKnobSeparator(*it); KnobButtonPtr isBtn = toKnobButton(*it); KnobParametricPtr isParametric = toKnobParametric(*it); KnobGroupPtr isGroup = toKnobGroup(*it); KnobPagePtr isPage = toKnobPage(*it); KnobColorPtr isColor = toKnobColor(*it); if (isInt) { knobType = tr("Integer"); } else if (isChoice) { knobType = tr("Choice"); } else if (isBool) { knobType = tr("Boolean"); } else if (isDbl) { knobType = tr("Double"); } else if (isString) { if (isLabel) { knobType = tr("Label"); } else { knobType = tr("String"); } } else if (isSep) { knobType = tr("Separator"); } else if (isBtn) { knobType = tr("Button"); } else if (isParametric) { knobType = tr("Parametric"); } else if (isGroup) { knobType = tr("Group"); } else if (isPage) { knobType = tr("Page"); } else if (isColor) { knobType = tr("Color"); } else { knobType = tr("N/A"); } if (!isGroup && !isPage) { for (int i = 0; i < (*it)->getNDimensions(); ++i) { QString valueStr; if (!isBtn && !isSep && !isParametric) { // If this is a ChoiceParam and we are not generating live HTML doc, // only add the list of entries and their halp if this node should not be // ignored (eg. OCIO colorspace knobs). if ( isChoice && (genHTML || !( knobScriptName == QString::fromUtf8(kOCIOParamInputSpaceChoice) || knobScriptName == QString::fromUtf8(kOCIOParamOutputSpaceChoice) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamDisplayChoice) ) ) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamViewChoice) ) ) || //( ( pluginID == QString::fromUtf8(kOCIOCDLTransformPluginIdentifier) ) && // ( knobScriptName == QString::fromUtf8(kOCIOCDLTransformParamCCCIDChoice) ) ) || ( ( pluginID == QString::fromUtf8(kOCIOFileTransformPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIOFileTransformParamCCCIDChoice) ) ) || ( ( pluginID == QString::fromUtf8("net.fxarena.openfx.Text") ) && ( knobScriptName == QString::fromUtf8("name") ) ) || // font family from Text plugin ( ( pluginID == QString::fromUtf8("net.fxarena.openfx.Polaroid") ) && ( knobScriptName == QString::fromUtf8("font") ) ) || // font family from Polaroid plugin ( ( pluginID == QString::fromUtf8(PLUGINID_NATRON_PRECOMP) ) && ( knobScriptName == QString::fromUtf8("writeNode") ) ) || ( ( pluginID == QString::fromUtf8(PLUGINID_NATRON_ONEVIEW) ) && ( knobScriptName == QString::fromUtf8("view") ) ) ) ) ) { // see also KnobChoice::getHintToolTipFull() int index = isChoice->getDefaultValue(DimIdx(i)); std::vector<ChoiceOption> entries = isChoice->getEntries(); if ( (index >= 0) && ( index < (int)entries.size() ) ) { valueStr = QString::fromUtf8( entries[index].id.c_str() ); } bool first = true; for (size_t i = 0; i < entries.size(); i++) { QString entryHelp = QString::fromUtf8( entries[i].tooltip.c_str() ); QString entry; if (entries[i].id != entries[i].label) { entry = QString::fromUtf8( "%1 (%2)" ).arg(QString::fromUtf8( entries[i].label.c_str() )).arg(QString::fromUtf8( entries[i].id.c_str() )); } else { entry = QString::fromUtf8( entries[i].label.c_str() ); } if (!entry.isEmpty()) { if (first) { // empty line before the option descriptions if (genHTML) { if ( !knobHint.isEmpty() ) { knobHint.append( QString::fromUtf8("<br />") ); } knobHint.append( tr("Possible values:") + QString::fromUtf8("<br />") ); } else { // we do a hack for multiline elements, because the markdown->rst conversion by pandoc doesn't use the line block syntax. // what we do here is put a supplementary dot at the beginning of each line, which is then converted to a pipe '|' in the // genStaticDocs.sh script by a simple sed command after converting to RsT if ( !knobHint.isEmpty() ) { if (!knobHint.startsWith( QString::fromUtf8(". ") )) { knobHint.prepend( QString::fromUtf8(". ") ); } knobHint.append( QString::fromUtf8("\\\n") ); } knobHint.append( QString::fromUtf8(". ") + tr("Possible values:") + QString::fromUtf8("\\\n") ); } first = false; } if (genHTML) { knobHint.append( QString::fromUtf8("<br />") ); } else { knobHint.append( QString::fromUtf8("\\\n") ); // we do a hack for multiline elements, because the markdown->rst conversion by pandoc doesn't use the line block syntax. // what we do here is put a supplementary dot at the beginning of each line, which is then converted to a pipe '|' in the // genStaticDocs.sh script by a simple sed command after converting to RsT knobHint.append( QString::fromUtf8(". ") ); } if (entryHelp.isEmpty()) { knobHint.append( QString::fromUtf8("**%1**").arg( convertFromPlainTextToMarkdown(entry, genHTML, true) ) ); } else { knobHint.append( QString::fromUtf8("**%1**: %2").arg( convertFromPlainTextToMarkdown(entry, genHTML, true) ).arg( convertFromPlainTextToMarkdown(entryHelp, genHTML, true) ) ); } } } } else if (isInt) { valueStr = QString::number( isInt->getDefaultValue( DimIdx(i) ) ); } else if (isDbl) { valueStr = QString::number( isDbl->getDefaultValue( DimIdx(i) ) ); } else if (isBool) { valueStr = isBool->getDefaultValue( DimIdx(i) ) ? tr("On") : tr("Off"); } else if (isString) { valueStr = QString::fromUtf8( isString->getDefaultValue( DimIdx(i) ).c_str() ); } else if (isColor) { valueStr = QString::number( isColor->getDefaultValue( DimIdx(i) ) ); } } dimsDefaultValueStr.push_back( std::make_pair(convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getDimensionName( DimIdx(i) ).c_str() ), genHTML, true ), convertFromPlainTextToMarkdown(valueStr, genHTML, true)) ); } for (std::size_t i = 0; i < dimsDefaultValueStr.size(); ++i) { if ( !dimsDefaultValueStr[i].second.isEmpty() ) { if (dimsDefaultValueStr.size() > 1) { defValuesStr.append(dimsDefaultValueStr[i].first); defValuesStr.append( QString::fromUtf8(": ") ); } defValuesStr.append(dimsDefaultValueStr[i].second); if (i < dimsDefaultValueStr.size() - 1) { defValuesStr.append( QString::fromUtf8(" ") ); } } } if ( defValuesStr.isEmpty() ) { defValuesStr = tr("N/A"); } } if (!isPage && !isSep && !isGroup && !isLabel) { QStringList row; row << knobLabel << knobScriptName << knobType << defValuesStr << knobHint; items.append(row); } } // for (KnobsVec::const_iterator it = knobs.begin(); it!=knobs.end(); ++it) { // generate plugin info ms << tr("%1 node").arg(pluginLabel) << "\n==========\n\n"; // a hack to avoid repeating the documentation for the various merge plugins if ( pluginID.startsWith( QString::fromUtf8("net.sf.openfx.Merge") ) ) { std::string id = pluginID.toStdString(); std::string op; if (id == PLUGINID_OFX_MERGE) { // do nothing } else if (id == "net.sf.openfx.MergeDifference") { op = "difference (a.k.a. absminus)"; } else if (id == "net.sf.openfx.MergeIn") { op = "in"; } else if (id == "net.sf.openfx.MergeMatte") { op = "matte"; } else if (id == "net.sf.openfx.MergeMax") { op = "max"; } else if (id == "net.sf.openfx.MergeMin") { op = "min"; } else if (id == "net.sf.openfx.MergeMultiply") { op = "multiply"; } else if (id == "net.sf.openfx.MergeOut") { op = "out"; } else if (id == "net.sf.openfx.MergePlus") { op = "plus"; } else if (id == "net.sf.openfx.MergeScreen") { op = "screen"; } if ( !op.empty() ) { // we should use the custom link "[Merge node](|http::/plugins/" PLUGINID_OFX_MERGE ".html||rst::net.sf.openfx.MergePlugin|)" // but pandoc borks it ms << tr("The *%1* node is a convenience node identical to the %2, except that the operator is set to *%3* by default.") .arg(pluginLabel) .arg(genHTML ? QString::fromUtf8("<a href=\"" PLUGINID_OFX_MERGE ".html\">Merge node</a>") : QString::fromUtf8(":ref:`" PLUGINID_OFX_MERGE "`") //QString::fromUtf8("[Merge node](http::/plugins/" PLUGINID_OFX_MERGE ".html)") ) .arg( QString::fromUtf8( op.c_str() ) ); goto OUTPUT; } } if (!pluginIconUrl.isEmpty()) { // add a nonbreaking space so that pandoc doesn't use the alt-text as a caption // http://pandoc.org/MANUAL.html#images ms << "![pluginIcon](" << pluginIconUrl << ")"; if (!genHTML) { // specify image width for pandoc-generated printed doc // (for hoedown-generated HTML, this handled by the CSS using the alt=pluginIcon attribute) // see http://pandoc.org/MANUAL.html#images // note that only % units are understood both by pandox and sphinx ms << "{ width=10% }"; } ms << " \n\n"; // required so that there is no legend when converted to rst by pandoc } ms << tr("*This documentation is for version %2.%3 of %1.*").arg(pluginLabel).arg(majorVersion).arg(minorVersion) << "\n\n"; ms << "\n" << tr("Description") << "\n--------------------------------------------------------------------------------\n\n"; if (!pluginDescriptionIsMarkdown) { if (genHTML) { pluginDescription = NATRON_NAMESPACE::convertFromPlainText(pluginDescription, NATRON_NAMESPACE::WhiteSpaceNormal); // replace URLs with links QRegExp re( QString::fromUtf8("((http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?)") ); pluginDescription.replace( re, QString::fromUtf8("<a href=\"\\1\">\\1</a>") ); } else { pluginDescription = convertFromPlainTextToMarkdown(pluginDescription, genHTML, false); } } ms << pluginDescription << "\n"; // create markdown table ms << "\n" << tr("Inputs") << "\n--------------------------------------------------------------------------------\n\n"; ms << tr("Input") << " | " << tr("Description") << " | " << tr("Optional") << "\n"; ms << "--- | --- | ---\n"; if (inputs.size() > 0) { Q_FOREACH(const QStringList &input, inputs) { QString inputName = input.at(0); QString inputDesc = input.at(1); QString inputOpt = input.at(2); ms << inputName << " | " << inputDesc << " | " << inputOpt << "\n"; }
void KnobGuiColor::showColorDialog() { QColorDialog dialog( _colorLabel->parentWidget() ); dialog.setOption(QColorDialog::DontUseNativeDialog); KnobColorPtr knob = _knob.lock(); const int nDims = knob->getDimension(); double curR = knob->getValue(0); _lastColor[0] = curR; double curG = curR; double curB = curR; double curA = 1.; if (nDims > 1) { curG = knob->getValue(1); _lastColor[1] = curG; curB = knob->getValue(2); _lastColor[2] = curB; } if (nDims > 3) { dialog.setOption(QColorDialog::ShowAlphaChannel); curA = knob->getValue(3); _lastColor[3] = curA; } bool isSimple = _useSimplifiedUI; QColor curColor; curColor.setRgbF( Image::clamp<qreal>(isSimple ? curR : Color::to_func_srgb(curR), 0., 1.), Image::clamp<qreal>(isSimple ? curG : Color::to_func_srgb(curG), 0., 1.), Image::clamp<qreal>(isSimple ? curB : Color::to_func_srgb(curB), 0., 1.), Image::clamp<qreal>(curA, 0., 1.) ); dialog.setCurrentColor(curColor); QObject::connect( &dialog, SIGNAL(currentColorChanged(QColor)), this, SLOT(onDialogCurrentColorChanged(QColor)) ); if ( !dialog.exec() ) { if (nDims == 3) { knob->setValues(_lastColor[0], _lastColor[1], _lastColor[2], ViewSpec::all(), eValueChangedReasonNatronGuiEdited); } else if (nDims == 4) { knob->setValues(_lastColor[0], _lastColor[1], _lastColor[2], _lastColor[3], ViewSpec::all(), eValueChangedReasonNatronGuiEdited); } else if (nDims == 1) { knob->setValue(_lastColor[0], ViewSpec::all(), 0, eValueChangedReasonNatronGuiEdited); } } else { QColor userColor = dialog.currentColor(); std::vector<double> color(4); color[0] = isSimple ? userColor.redF() : Color::from_func_srgb( userColor.redF() ); color[1] = isSimple ? userColor.greenF() : Color::from_func_srgb( userColor.greenF() ); color[2] = isSimple ? userColor.blueF() : Color::from_func_srgb( userColor.blueF() ); color[3] = userColor.alphaF(); for (int i = 0; i < 3; ++i) { SpinBox* sb = 0; getSpinBox(i, &sb); assert(sb); sb->setValue(color[i]); } // Refresh the last value so that the undo command retrieves the value that was prior to opening the dialog if (nDims == 3) { knob->setValues(_lastColor[0], _lastColor[1], _lastColor[2], ViewSpec::all(), eValueChangedReasonUserEdited); } else if (nDims == 4) { knob->setValues(_lastColor[0], _lastColor[1], _lastColor[2], _lastColor[3], ViewSpec::all(), eValueChangedReasonUserEdited); } else if (nDims == 1) { knob->setValue(_lastColor[0], ViewSpec::all(), 0, eValueChangedReasonUserEdited); } onSpinBoxValueChanged(); } if ( getGui() ) { getGui()->setDraftRenderEnabled(false); } //knob->evaluateValueChange(0, knob->getCurrentTime(), ViewIdx(0), eValueChangedReasonNatronGuiEdited); } // showColorDialog
ActionRetCodeEnum RotoShapeRenderNode::render(const RenderActionArgs& args) { #if !defined(ROTO_SHAPE_RENDER_CPU_USES_CAIRO) && !defined(HAVE_OSMESA) getNode()->setPersistentMessage(eMessageTypeError, kNatronPersistentErrorGenericRenderMessage, tr("Roto requires either OSMesa (CONFIG += enable-osmesa) or Cairo (CONFIG += enable-cairo) in order to render on CPU").toStdString()); return eActionStatusFailed; #endif #if !defined(ROTO_SHAPE_RENDER_CPU_USES_CAIRO) if (args.backendType == eRenderBackendTypeCPU) { getNode()->setPersistentMessage(eMessageTypeError, kNatronPersistentErrorGenericRenderMessage, tr("An OpenGL context is required to draw with the Roto node. This might be because you are trying to render an image too big for OpenGL.").toStdString()); return eActionStatusFailed; } #endif RenderScale combinedScale = EffectInstance::getCombinedScale(args.mipMapLevel, args.proxyScale); // Get the Roto item attached to this node. It will be a render-local clone of the original item. RotoDrawableItemPtr rotoItem = getAttachedRotoItem(); assert(rotoItem); if (!rotoItem) { return eActionStatusFailed; } // To be thread-safe we can only operate on a render clone. assert(rotoItem->isRenderClone()); // Is it a smear or regular solid render ? assert(_imp->renderType.lock()); RotoShapeRenderTypeEnum type = (RotoShapeRenderTypeEnum)_imp->renderType.lock()->getValue(); // We only support rendering Bezier or strokes RotoStrokeItemPtr isStroke = toRotoStrokeItem(rotoItem); BezierPtr isBezier = toBezier(rotoItem); // Get the real stroke (the one the user interacts with) RotoStrokeItemPtr nonRenderStroke = toRotoStrokeItem(getOriginalAttachedItem()); if (type == eRotoShapeRenderTypeSmear && !isStroke) { return eActionStatusFailed; } // Check that the item is really activated... it should have been caught in isIdentity otherwise. assert(rotoItem->isActivated(args.time, args.view) && (!isBezier || ((isBezier->isCurveFinished(args.view) || isBezier->isOpenBezier()) && ( isBezier->getControlPointsCount(args.view) > 1 )))); const OSGLContextPtr& glContext = args.glContext; // There must be an OpenGL context bound when using OpenGL. if ((args.backendType == eRenderBackendTypeOpenGL || args.backendType == eRenderBackendTypeOSMesa) && !glContext) { getNode()->setPersistentMessage(eMessageTypeError, kNatronPersistentErrorGenericRenderMessage, tr("An OpenGL context is required to draw with the Roto node").toStdString()); return eActionStatusFailed; } // This is the image plane where we render, we are not multiplane so we only render out one plane assert(args.outputPlanes.size() == 1); const std::pair<ImagePlaneDesc,ImagePtr>& outputPlane = args.outputPlanes.front(); // True if this render was trigger because the user is painting (with a pen or mouse) bool isDuringPainting = isStroke && isStroke->isCurrentlyDrawing(); // These variables are useful to pick the stroke drawing algorithm where it was at the previous draw step. double distNextIn = 0.; Point lastCenterIn = { INT_MIN, INT_MIN }; int strokeStartPointIndex = 0; int strokeMultiIndex = 0; // For strokes and open-bezier evaluate them to get the points and their pressure // We also compute the bounding box of the item taking into account the motion blur if (isStroke) { strokeStartPointIndex = isStroke->getRenderCloneCurrentStrokeStartPointIndex(); strokeMultiIndex = isStroke->getRenderCloneCurrentStrokeIndex(); isStroke->getStrokeState(&lastCenterIn, &distNextIn); } // Ensure that the indices of the draw step are valid. #ifdef DEBUG if (isDuringPainting && isStroke->getRenderCloneCurrentStrokeEndPointIndex() >= strokeStartPointIndex) { if (strokeStartPointIndex == 0) { assert((isStroke->getRenderCloneCurrentStrokeEndPointIndex() + 1) == isStroke->getNumControlPoints(0)); } else { // +2 because we also add the last point of the previous draw step in the call to cloneIndexRange(), otherwise it would make holes in the drawing assert((isStroke->getRenderCloneCurrentStrokeEndPointIndex() + 2 - strokeStartPointIndex) == isStroke->getNumControlPoints(0)); } } #endif // Now we are good to start rendering // This is the state of the stroke aglorithm in output of this draw step double distToNextOut = 0.; Point lastCenterOut; // Retrieve the OpenGL context local data that were allocated in attachOpenGLContext RotoShapeRenderNodeOpenGLDataPtr glData; if (args.glContextData) { glData = boost::dynamic_pointer_cast<RotoShapeRenderNodeOpenGLData>(args.glContextData); assert(glData); } // Firs time we draw this clear the background since we are not going to render the full image with OpenGL. if (strokeStartPointIndex == 0 && strokeMultiIndex == 0) { outputPlane.second->fillBoundsZero(); } bool clipToFormat = _imp->clipToFormatKnob.lock()->getValue(); switch (type) { case eRotoShapeRenderTypeSolid: { // Account for motion-blur RangeD range; int divisions; rotoItem->getMotionBlurSettings(args.time, args.view, &range, &divisions); if (isDuringPainting) { // Do not use motion-blur when drawing. range.min = range.max = args.time; divisions = 1; } #ifdef ROTO_SHAPE_RENDER_CPU_USES_CAIRO // When cairo is enabled, render with it for a CPU render if (args.backendType == eRenderBackendTypeCPU) { RotoShapeRenderCairo::renderMaskInternal_cairo(rotoItem, args.roi, outputPlane.first, args.time, args.view, range, divisions, combinedScale, isDuringPainting, distNextIn, lastCenterIn, outputPlane.second, &distToNextOut, &lastCenterOut); if (isDuringPainting && isStroke) { nonRenderStroke->updateStrokeData(lastCenterOut, distToNextOut, isStroke->getRenderCloneCurrentStrokeEndPointIndex()); } } else #endif // Otherwise render with OpenGL or OSMesa if (args.backendType == eRenderBackendTypeOpenGL || args.backendType == eRenderBackendTypeOSMesa) { // Figure out the shape color ColorRgbaD shapeColor; { const double t = args.time; KnobColorPtr colorKnob = rotoItem->getColorKnob(); if (colorKnob) { shapeColor.r = colorKnob->getValueAtTime(TimeValue(t), DimIdx(0), args.view); shapeColor.g = colorKnob->getValueAtTime(TimeValue(t), DimIdx(1), args.view); shapeColor.b = colorKnob->getValueAtTime(TimeValue(t), DimIdx(2), args.view); shapeColor.a = colorKnob->getValueAtTime(TimeValue(t), DimIdx(3), args.view); } } // Figure out the opacity double opacity = rotoItem->getOpacityKnob() ? rotoItem->getOpacityKnob()->getValueAtTime(args.time, DimIdx(0), args.view) : 1.; // For a stroke or an opened Bezier, use the generic stroke algorithm if ( isStroke || ( isBezier && (isBezier->isOpenBezier() || !isBezier->isFillEnabled()) ) ) { const bool doBuildUp = !isStroke ? false : isStroke->getBuildupKnob()->getValueAtTime(args.time, DimIdx(0), args.view); RotoShapeRenderGL::renderStroke_gl(glContext, glData, args.roi, outputPlane.second, isDuringPainting, distNextIn, lastCenterIn, rotoItem, doBuildUp, opacity, args.time, args.view, range, divisions, combinedScale, &distToNextOut, &lastCenterOut); // Update the stroke algorithm in output if (isDuringPainting && isStroke) { nonRenderStroke->updateStrokeData(lastCenterOut, distToNextOut, isStroke->getRenderCloneCurrentStrokeEndPointIndex()); } } else { // Render a Bezier //qDebug() << QThread::currentThread() << this << isBezier.get()<< "RoD while render:"; //isBezier->getBoundingBox(args.time, args.view).debug(); RotoShapeRenderGL::renderBezier_gl(glContext, glData, args.roi, isBezier, outputPlane.second, clipToFormat, opacity, args.time, args.view, range, divisions, combinedScale, GL_TEXTURE_2D); } } // useOpenGL } break; case eRotoShapeRenderTypeSmear: { OSGLContextAttacherPtr contextAttacher; if (args.backendType == eRenderBackendTypeOSMesa && !glContext->isGPUContext()) { // When rendering smear with OSMesa we need to write to the full image bounds and not only the RoI, so re-attach the default framebuffer // with the image bounds Image::CPUData imageData; outputPlane.second->getCPUData(&imageData); contextAttacher = OSGLContextAttacher::create(glContext, imageData.bounds.width(), imageData.bounds.height(), imageData.bounds.width(), imageData.ptrs[0]); } // Ensure that initially everything in the background is the source image if (strokeStartPointIndex == 0 && strokeMultiIndex == 0) { GetImageOutArgs outArgs; GetImageInArgs inArgs(&args.mipMapLevel, &args.proxyScale, &args.roi, &args.backendType); inArgs.inputNb = 0; if (!getImagePlane(inArgs, &outArgs)) { getNode()->setPersistentMessage(eMessageTypeError, kNatronPersistentErrorGenericRenderMessage, tr("Failed to fetch source image").toStdString()); return eActionStatusFailed; } ImagePtr bgImage = outArgs.image; if (args.backendType == eRenderBackendTypeCPU || glContext->isGPUContext()) { // Copy the BG image to the output image Image::CopyPixelsArgs cpyArgs; cpyArgs.roi = outputPlane.second->getBounds(); outputPlane.second->copyPixels(*bgImage, cpyArgs); } else { // With OSMesa we cannot re-use the existing output plane as source because mesa clears the framebuffer out upon the first draw // after a binding. // The only option is to draw in a tmp texture that will live for the whole stroke painting life Image::InitStorageArgs initArgs; initArgs.bounds = bgImage->getBounds(); initArgs.bitdepth = outputPlane.second->getBitDepth(); initArgs.storage = eStorageModeGLTex; initArgs.glContext = glContext; initArgs.textureTarget = GL_TEXTURE_2D; _imp->osmesaSmearTmpTexture = Image::create(initArgs); if (!_imp->osmesaSmearTmpTexture) { return eActionStatusFailed; } // Make sure the texture is ready before rendering the smear GL_CPU::Flush(); GL_CPU::Finish(); } } else { if (args.backendType == eRenderBackendTypeOSMesa && !glContext->isGPUContext() && strokeStartPointIndex == 0) { // Ensure the tmp texture has correct size assert(_imp->osmesaSmearTmpTexture); ActionRetCodeEnum stat = _imp->osmesaSmearTmpTexture->ensureBounds(outputPlane.second->getBounds(), args.mipMapLevel, std::vector<RectI>(), shared_from_this()); if (isFailureRetCode(stat)) { return stat; } } } bool renderedDot; #ifdef ROTO_SHAPE_RENDER_CPU_USES_CAIRO // Render with cairo if we need to render on CPU if (args.backendType == eRenderBackendTypeCPU) { renderedDot = RotoShapeRenderCairo::renderSmear_cairo(args.time, args.view, combinedScale, isStroke, args.roi, outputPlane.second, distNextIn, lastCenterIn, &distToNextOut, &lastCenterOut); } else #endif if (args.backendType == eRenderBackendTypeOpenGL || args.backendType == eRenderBackendTypeOSMesa) { // Render with OpenGL ImagePtr dstImage = glContext->isGPUContext() ? outputPlane.second : _imp->osmesaSmearTmpTexture; assert(dstImage); renderedDot = RotoShapeRenderGL::renderSmear_gl(glContext, glData, args.roi, dstImage, distNextIn, lastCenterIn, isStroke, 1., args.time, args.view, combinedScale, &distToNextOut, &lastCenterOut); } // Update the stroke algorithm in output if (isDuringPainting) { Q_UNUSED(renderedDot); nonRenderStroke->updateStrokeData(lastCenterOut, distToNextOut, isStroke->getRenderCloneCurrentStrokeEndPointIndex()); } } break; } // type return eActionStatusOK; } // RotoShapeRenderNode::render
void KnobGuiColor::showColorDialog() { QColorDialog dialog( _colorLabel->parentWidget() ); dialog.setOption(QColorDialog::DontUseNativeDialog); KnobColorPtr knob = _knob.lock(); if (!knob) { return; } const int nDims = knob->getNDimensions(); ViewIdx view = getView(); double curR = knob->getValue(DimIdx(0), view, false /*clampToMinmax*/); _lastColor.resize(nDims); _lastColor[0] = curR; double curG = curR; double curB = curR; double curA = 1.; if (nDims > 1) { curG = knob->getValue(DimIdx(1), view, false /*clampToMinmax*/); _lastColor[1] = curG; curB = knob->getValue(DimIdx(2), view, false /*clampToMinmax*/); _lastColor[2] = curB; } if (nDims > 3) { dialog.setOption(QColorDialog::ShowAlphaChannel); curA = knob->getValue(DimIdx(3), view, false /*clampToMinmax*/); _lastColor[3] = curA; } convertFromInternalToUIColorspace(&curR, &curG, &curB); QColor curColor; curColor.setRgbF( Image::clamp<qreal>(curR, 0., 1.), Image::clamp<qreal>(curG, 0., 1.), Image::clamp<qreal>(curB, 0., 1.), Image::clamp<qreal>(curA, 0., 1.) ); dialog.setCurrentColor(curColor); QObject::connect( &dialog, SIGNAL(currentColorChanged(QColor)), this, SLOT(onDialogCurrentColorChanged(QColor)) ); if ( !dialog.exec() ) { knob->setValueAcrossDimensions(_lastColor, DimIdx(0), view, eValueChangedReasonUserEdited); } else { QColor userColor = dialog.currentColor(); std::vector<double> color(nDims); color[0] = userColor.redF(); convertFromUIToInternalColorspace(&color[0]); if (nDims > 1) { color[1] = userColor.greenF(); convertFromUIToInternalColorspace(&color[1]); } if (nDims > 2) { color[2] = userColor.blueF(); convertFromUIToInternalColorspace(&color[2]); } if (nDims > 3) { color[3] = userColor.alphaF(); } for (int i = 0; i < 3; ++i) { SpinBox* sb = 0; getSpinBox(DimIdx(i), &sb); if (sb) { sb->setValue(color[i]); } } std::vector<double> oldColor(nDims); for (int i = 0; i < nDims; ++i) { oldColor[i] = _lastColor[i]; } KnobGuiPtr knobUI = getKnobGui(); knobUI->pushUndoCommand( new KnobUndoCommand<double>(knob, oldColor, color, getView()) ); } KnobGuiPtr knobUI = getKnobGui(); if ( knobUI->getGui() ) { knobUI->getGui()->setDraftRenderEnabled(false); } } // showColorDialog