mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-01-22 05:30:06 -05:00
07592c9d78
Add Curves.h, and CurvesGraphicsView.h/cpp to support bezier color curves. Add Curves member to Ember. Add curves capability to EmberCL. Remove some unused variables in the kernel created in RendererCL::CreateFinalAccumKernelString(). Use glm namespace for vec classes if GLM_VERSION >= 96, else use glm::detail. As a result of using glm namespace, all instances of min and max had to be qualified with std:: Split ComputeCamera into that and ComputeQuality(). Reduce the amount of ComputeCamera() and MakeDmap() calls on each incremental iter that doesn't use temporal samples. Fix clamping bug with DE filter widths. Provide functions to return the kernels from RendererCL to assist with diagnostics and debugging. Prevent extra newline in EmberRender when only rendering a single image. Add the ability to delete an ember at a given index in EmberFile. Allow deleting/focusing ember in library tab with delete and enter keys. Reorder some code in Fractorium.h to match the tabs order. Add and call ClearFinalImages() to clear buffers in controller to fix bug where previous CPU render would be shown for a split second when switching from OpenCL back to CPU. Refactor ember library pointer syncing to a function SyncPointers(). Add the ability to save ember Xmls to an unique automatically generated name after the first time the user has specified a name.
373 lines
12 KiB
C++
373 lines
12 KiB
C++
#include "FractoriumPch.h"
|
|
#include "Fractorium.h"
|
|
|
|
/// <summary>
|
|
/// Initialize the library tree UI.
|
|
/// </summary>
|
|
void Fractorium::InitLibraryUI()
|
|
{
|
|
connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
|
|
connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
|
|
connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
|
|
ui.LibraryTree->installEventFilter(this);//Needed for keypress events other than enter.
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get the index of the currently selected ember in the library tree.
|
|
/// </summary>
|
|
/// <returns>A pair containing the index of the item clicked and a pointer to the item</param>
|
|
pair<size_t, QTreeWidgetItem*> Fractorium::GetCurrentEmberIndex()
|
|
{
|
|
size_t index = 0;
|
|
QTreeWidgetItem* item = nullptr;
|
|
QTreeWidget* tree = ui.LibraryTree;
|
|
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
{
|
|
for (int i = 0; i < top->childCount(); i++)//Iterate through all of the children, which will represent the open embers.
|
|
{
|
|
item = top->child(index);
|
|
|
|
if (item && !item->isSelected())
|
|
index++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pair<size_t, QTreeWidgetItem*>(index, item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slot function to be called via QMetaObject::invokeMethod() to update preview images in the preview thread.
|
|
/// </summary>
|
|
/// <param name="item">The item double clicked on</param>
|
|
/// <param name="v">The vector holding the RGBA bitmap</param>
|
|
/// <param name="w">The width of the bitmap</param>
|
|
/// <param name="h">The height of the bitmap</param>
|
|
void Fractorium::SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector<byte>& v, uint w, uint h)
|
|
{
|
|
item->SetImage(v, w, h);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set all libary tree entries to the name of the corresponding ember they represent.
|
|
/// </summary>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::SyncNames()
|
|
{
|
|
EmberTreeWidgetItem<T>* item;
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
|
|
tree->blockSignals(true);
|
|
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
{
|
|
for (int i = 0; i < top->childCount(); i++)//Iterate through all of the children, which will represent the open embers.
|
|
{
|
|
if ((item = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i))) && i < m_EmberFile.Size())//Cast the child widget to the EmberTreeWidgetItem type.
|
|
item->setText(0, QString::fromStdString(m_EmberFile.m_Embers[i].m_Name));
|
|
}
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set all libary tree entries to point to the underlying ember they represent.
|
|
/// </summary>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::SyncPointers()
|
|
{
|
|
EmberTreeWidgetItem<T>* item;
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
|
|
tree->blockSignals(true);
|
|
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
{
|
|
size_t childCount = top->childCount();
|
|
|
|
for (int i = 0; i < childCount; i++)//Iterate through all of the children, which will represent the open embers.
|
|
{
|
|
if ((item = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i))) && i < m_EmberFile.Size())//Cast the child widget to the EmberTreeWidgetItem type.
|
|
item->SetEmberPointer(&m_EmberFile.m_Embers[i]);
|
|
}
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill the library tree with the names of the embers in the
|
|
/// currently opened file.
|
|
/// Start preview render thread.
|
|
/// </summary>
|
|
/// <param name="selectIndex">After the tree is filled, select this index. Pass -1 to omit selecting an index.</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
|
|
{
|
|
uint i, j, size = 64;
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
vector<byte> v(size * size * 4);
|
|
|
|
StopPreviewRender();
|
|
tree->clear();
|
|
QCoreApplication::flush();
|
|
|
|
tree->blockSignals(true);
|
|
|
|
QTreeWidgetItem* fileItem = new QTreeWidgetItem(tree);
|
|
QFileInfo info(m_EmberFile.m_Filename);
|
|
|
|
fileItem->setText(0, info.fileName());
|
|
fileItem->setToolTip(0, m_EmberFile.m_Filename);
|
|
fileItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
|
|
|
for (j = 0; j < m_EmberFile.Size(); j++)
|
|
{
|
|
Ember<T>* ember = &m_EmberFile.m_Embers[j];
|
|
EmberTreeWidgetItem<T>* emberItem = new EmberTreeWidgetItem<T>(ember, fileItem);
|
|
|
|
emberItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
|
|
|
if (ember->m_Name.empty())
|
|
emberItem->setText(0, ToString(j));
|
|
else
|
|
emberItem->setText(0, ember->m_Name.c_str());
|
|
|
|
emberItem->setToolTip(0, emberItem->text(0));
|
|
emberItem->SetImage(v, size, size);
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
|
|
if (selectIndex != -1)
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(selectIndex)))
|
|
emberItem->setSelected(true);
|
|
|
|
QCoreApplication::flush();
|
|
RenderPreviews(0, m_EmberFile.Size());
|
|
tree->expandAll();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the library tree with the newly added embers (most likely from pasting) and
|
|
/// only render previews for the new ones, without clearing the entire tree.
|
|
/// </summary>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::UpdateLibraryTree()
|
|
{
|
|
uint i, size = 64;
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
vector<byte> v(size * size * 4);
|
|
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
{
|
|
int childCount = top->childCount();
|
|
|
|
tree->blockSignals(true);
|
|
|
|
for (i = childCount; i < m_EmberFile.Size(); i++)
|
|
{
|
|
Ember<T>* ember = &m_EmberFile.m_Embers[i];
|
|
EmberTreeWidgetItem<T>* emberItem = new EmberTreeWidgetItem<T>(ember, top);
|
|
|
|
emberItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
|
|
|
if (ember->m_Name.empty())
|
|
emberItem->setText(0, ToString(i));
|
|
else
|
|
emberItem->setText(0, ember->m_Name.c_str());
|
|
|
|
emberItem->setToolTip(0, emberItem->text(0));
|
|
emberItem->SetImage(v, size, size);
|
|
}
|
|
|
|
//When adding elements to the vector, they may have been reshuffled which will have invalidated
|
|
//the pointers contained in the EmberTreeWidgetItems. So reassign all pointers here.
|
|
SyncPointers();
|
|
tree->blockSignals(false);
|
|
RenderPreviews(childCount, m_EmberFile.Size());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy the text of the item which was changed to the name of the current ember.
|
|
/// Ensure all names are unique in the opened file.
|
|
/// This seems to be called spuriously, so we do a check inside to make sure
|
|
/// the text was actually changed.
|
|
/// We also have to wrap the dynamic_cast call in a try/catch block because this can
|
|
/// be called on a widget that has already been deleted.
|
|
/// </summary>
|
|
/// <param name="item">The libary tree item changed</param>
|
|
/// <param name="col">The column clicked, ignored.</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::EmberTreeItemChanged(QTreeWidgetItem* item, int col)
|
|
{
|
|
try
|
|
{
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(item);
|
|
|
|
if (emberItem)
|
|
{
|
|
string oldName = emberItem->GetEmber()->m_Name;//First preserve the previous name.
|
|
|
|
tree->blockSignals(true);
|
|
emberItem->UpdateEmberName();//Copy edit text to the ember's name variable.
|
|
m_EmberFile.MakeNamesUnique();//Ensure all names remain unique.
|
|
SyncNames();//Copy all ember names to the tree items since some might have changed to be made unique.
|
|
string newName = emberItem->GetEmber()->m_Name;//Get the new, final, unique name.
|
|
|
|
if (m_Ember.m_Name == oldName && oldName != newName)//If the ember edited was the current one, and the name was indeed changed, update the name of the current one.
|
|
{
|
|
m_Ember.m_Name = newName;
|
|
m_LastSaveCurrent = "";//Reset will force the dialog to show on the next save current since the user probably wants a different name.
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
}
|
|
else if (QTreeWidgetItem* parentItem = dynamic_cast<QTreeWidgetItem*>(item))
|
|
{
|
|
QString text = parentItem->text(0);
|
|
|
|
if (text != "")
|
|
{
|
|
m_EmberFile.m_Filename = text;
|
|
m_LastSaveAll = "";//Reset will force the dialog to show on the next save all since the user probably wants a different name.
|
|
}
|
|
}
|
|
}
|
|
catch(std::exception& e)
|
|
{
|
|
qDebug() << "FractoriumEmberController<T>::EmberTreeItemChanged() : Exception thrown: " << e.what();
|
|
}
|
|
}
|
|
|
|
void Fractorium::OnEmberTreeItemChanged(QTreeWidgetItem* item, int col) { m_Controller->EmberTreeItemChanged(item, col); }
|
|
|
|
/// <summary>
|
|
/// Set the current ember to the selected item.
|
|
/// Clears the undo state.
|
|
/// Resets the rendering process.
|
|
/// Called when the user double clicks on a library tree item.
|
|
/// </summary>
|
|
/// <param name="item">The item double clicked on</param>
|
|
/// <param name="col">The column clicked, ignored.</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col)
|
|
{
|
|
if (EmberTreeWidgetItem<T>* emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(item))
|
|
{
|
|
ClearUndo();
|
|
SetEmber(*emberItem->GetEmber());
|
|
}
|
|
}
|
|
|
|
void Fractorium::OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { m_Controller->EmberTreeItemDoubleClicked(item, col); }
|
|
|
|
/// <summary>
|
|
/// Delete the currently selected item in the tree.
|
|
/// Note this is not necessarilly the current ember, it's just the item
|
|
/// in the tree that is selected.
|
|
/// </summary>
|
|
/// <param name="p">A pair containing the index of the item clicked and a pointer to the item</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::Delete(const pair<size_t, QTreeWidgetItem*>& p)
|
|
{
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
|
|
tree->blockSignals(true);
|
|
|
|
if (m_EmberFile.Delete(p.first))
|
|
{
|
|
delete p.second;
|
|
SyncPointers();
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
|
|
//If there is now only one item left and it wasn't selected, select it.
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
{
|
|
if (top->childCount() == 1)
|
|
if (auto item = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(0)))
|
|
if (item->GetEmber()->m_Name != m_Ember.m_Name)
|
|
EmberTreeItemDoubleClicked(top->child(0), 0);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the user presses and releases the delete key while the library tree has the focus,
|
|
/// and an item is selected.
|
|
/// </summary>
|
|
/// <param name="p">A pair containing the index of the item clicked and a pointer to the item</param>
|
|
void Fractorium::OnDelete(const pair<size_t, QTreeWidgetItem*>& p)
|
|
{
|
|
m_Controller->Delete(p);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop the preview renderer if it's already running.
|
|
/// Clear all of the existing preview images, then start the preview rendering thread.
|
|
/// Optionally only render previews for a subset of all open embers.
|
|
/// </summary>
|
|
/// <param name="start">The 0-based index to start rendering previews for</param>
|
|
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::RenderPreviews(uint start, uint end)
|
|
{
|
|
StopPreviewRender();
|
|
|
|
if (start == UINT_MAX && end == UINT_MAX)
|
|
{
|
|
QTreeWidget* tree = m_Fractorium->ui.LibraryTree;
|
|
|
|
tree->blockSignals(true);
|
|
|
|
if (QTreeWidgetItem* top = tree->topLevelItem(0))
|
|
{
|
|
int childCount = top->childCount();
|
|
vector<byte> emptyPreview(PREVIEW_SIZE * PREVIEW_SIZE * 3);
|
|
|
|
for (int i = 0; i < childCount; i++)
|
|
if (EmberTreeWidgetItem<T>* treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))
|
|
treeItem->SetImage(emptyPreview, PREVIEW_SIZE, PREVIEW_SIZE);
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, 0, m_EmberFile.Size());
|
|
}
|
|
else
|
|
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, start, end);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop the preview rendering thread.
|
|
/// </summary>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::StopPreviewRender()
|
|
{
|
|
m_PreviewRun = false;
|
|
|
|
while (m_PreviewRunning)
|
|
QApplication::processEvents();
|
|
|
|
m_PreviewResult.cancel();
|
|
|
|
while (m_PreviewResult.isRunning())
|
|
QApplication::processEvents();
|
|
|
|
QCoreApplication::sendPostedEvents(m_Fractorium->ui.LibraryTree);
|
|
QCoreApplication::flush();
|
|
}
|
|
|
|
template class FractoriumEmberController<float>;
|
|
|
|
#ifdef DO_DOUBLE
|
|
template class FractoriumEmberController<double>;
|
|
#endif
|