mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-06-30 21:36:33 -04:00
--User changes
-Allow for saving EXR as full 31-bit float per component per pixel. -Allow for saving the output from the final render dialog as a different image format without starting the rendering process over. --Code changes: -Make StripsRender() handle memsetting the final output image so calling code no longer has to. -Make FinalRenderEmberController<T>::SaveCurrentRender() return the path that the image was actually saved to.
This commit is contained in:
@ -163,22 +163,42 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
|
||||
m_SupersampleSpin->setValue(m_Settings->FinalSupersample());
|
||||
m_StripsSpin->setValue(int(m_Settings->FinalStrips()));
|
||||
Scale(eScaleType(m_Settings->FinalScale()));
|
||||
auto menu = new QMenu(this);
|
||||
auto bumpmenu = new QMenu(this);
|
||||
auto add10 = new QAction("Add 10% quality", this); add10->setProperty("tag", QVariant(0.10));
|
||||
auto add25 = new QAction("Add 25% quality", this); add25->setProperty("tag", QVariant(0.25));
|
||||
auto add50 = new QAction("Add 50% quality", this); add50->setProperty("tag", QVariant(0.50));
|
||||
auto add100 = new QAction("Add 100% quality", this); add100->setProperty("tag", QVariant(1.0));
|
||||
auto add200 = new QAction("Add 200% quality", this); add200->setProperty("tag", QVariant(2.0));
|
||||
menu->addAction(add10);
|
||||
menu->addAction(add25);
|
||||
menu->addAction(add50);
|
||||
menu->addAction(add100);
|
||||
menu->addAction(add200);
|
||||
ui.FinalRenderBumpQualityStartButton->setMenu(menu);
|
||||
bumpmenu->addAction(add10);
|
||||
bumpmenu->addAction(add25);
|
||||
bumpmenu->addAction(add50);
|
||||
bumpmenu->addAction(add100);
|
||||
bumpmenu->addAction(add200);
|
||||
ui.FinalRenderBumpQualityStartButton->setMenu(bumpmenu);
|
||||
ui.FinalRenderBumpQualityStartButton->setProperty("tag", add25->property("tag"));
|
||||
ui.FinalRenderBumpQualityStartButton->setText(add25->text());
|
||||
ui.FinalRenderBumpQualityStartButton->setEnabled(false);
|
||||
auto saamenu = new QMenu(this);
|
||||
#ifdef _WIN32
|
||||
auto saabmp = new QAction("bmp", this);
|
||||
saamenu->addAction(saabmp);
|
||||
connect(saabmp, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked()));
|
||||
#endif
|
||||
auto saajpg = new QAction("jpg", this);
|
||||
auto saapng = new QAction("png", this);
|
||||
auto saaexr = new QAction("exr", this);
|
||||
saamenu->addAction(saajpg);
|
||||
saamenu->addAction(saapng);
|
||||
saamenu->addAction(saaexr);
|
||||
ui.FinalRenderSaveAgainAsButton->setMenu(saamenu);
|
||||
ui.FinalRenderSaveAgainAsButton->setProperty("tag", "jpg");
|
||||
ui.FinalRenderSaveAgainAsButton->setText("Save again as jpg");
|
||||
ui.FinalRenderSaveAgainAsButton->setEnabled(false);
|
||||
connect(ui.FinalRenderSaveAgainAsButton, SIGNAL(clicked()), this, SLOT(OnSaveAgainAsClicked()));
|
||||
connect(ui.FinalRenderBumpQualityStartButton, SIGNAL(clicked()), this, SLOT(OnQualityBumpClicked()));
|
||||
connect(saajpg, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked()));
|
||||
connect(saapng, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked()));
|
||||
connect(saaexr, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked()));
|
||||
connect(add10, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked()));
|
||||
connect(add25, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked()));
|
||||
connect(add50, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked()));
|
||||
@ -246,6 +266,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
|
||||
w = SetTabOrder(this, w, m_PrefixEdit);
|
||||
w = SetTabOrder(this, w, m_SuffixEdit);
|
||||
w = SetTabOrder(this, w, ui.FinalRenderTextOutput);
|
||||
w = SetTabOrder(this, w, ui.FinalRenderSaveAgainAsButton);
|
||||
w = SetTabOrder(this, w, ui.FinalRenderBumpQualityStartButton);
|
||||
w = SetTabOrder(this, w, ui.FinalRenderStartButton);
|
||||
w = SetTabOrder(this, w, ui.FinalRenderPauseButton);
|
||||
@ -457,6 +478,7 @@ void FractoriumFinalRenderDialog::OnDoAllCheckBoxStateChanged(int state)
|
||||
ui.FinalRenderDoSequenceCheckBox->setChecked(false);
|
||||
|
||||
ui.FinalRenderDoSequenceCheckBox->setEnabled(ui.FinalRenderDoAllCheckBox->isChecked());
|
||||
ui.FinalRenderSaveAgainAsButton->setEnabled(false);
|
||||
ui.FinalRenderBumpQualityStartButton->setEnabled(false);
|
||||
}
|
||||
|
||||
@ -499,6 +521,7 @@ void FractoriumFinalRenderDialog::OnApplyAllCheckBoxStateChanged(int state)
|
||||
if (state && m_Controller.get())
|
||||
m_Controller->SyncGuiToEmbers();
|
||||
|
||||
ui.FinalRenderSaveAgainAsButton->setEnabled(false);
|
||||
ui.FinalRenderBumpQualityStartButton->setEnabled(false);
|
||||
}
|
||||
|
||||
@ -776,6 +799,34 @@ void FractoriumFinalRenderDialog::OnQualityBumpClicked()
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the last rendered image output again to a different file format.
|
||||
/// Note this is only when rendering a single image with no strips.
|
||||
/// </summary>
|
||||
void FractoriumFinalRenderDialog::OnSaveAgainAsClicked()
|
||||
{
|
||||
auto act = qobject_cast<QAction*>(sender());
|
||||
auto tbtn = qobject_cast<QToolButton*>(sender());
|
||||
|
||||
if (tbtn)
|
||||
{
|
||||
if (m_Controller.get())
|
||||
{
|
||||
auto s = tbtn->property("tag").toString();
|
||||
m_Tbcw->m_Combo->blockSignals(true);
|
||||
m_Tbcw->m_Combo->setCurrentText(s);
|
||||
m_Tbcw->m_Combo->blockSignals(false);
|
||||
auto filename = m_Controller->SaveCurrentAgain();
|
||||
ui.FinalRenderTextOutput->append("\nSaved again as " + filename);
|
||||
}
|
||||
}
|
||||
else if (act)
|
||||
{
|
||||
ui.FinalRenderSaveAgainAsButton->setText("Save again as " + act->text());
|
||||
ui.FinalRenderSaveAgainAsButton->setProperty("tag", act->text());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the render process.
|
||||
/// </summary>
|
||||
@ -998,6 +1049,7 @@ bool FractoriumFinalRenderDialog::SetMemory()
|
||||
else
|
||||
ui.FinalRenderTextOutput->clear();
|
||||
|
||||
ui.FinalRenderSaveAgainAsButton->setEnabled(false);
|
||||
ui.FinalRenderBumpQualityStartButton->setEnabled(false);
|
||||
return true;
|
||||
}
|
||||
|
@ -112,6 +112,7 @@ public slots:
|
||||
void OnPrefixChanged(const QString& s);
|
||||
void OnSuffixChanged(const QString& s);
|
||||
void OnQualityBumpClicked();
|
||||
void OnSaveAgainAsClicked();
|
||||
void OnRenderClicked(bool checked);
|
||||
void OnPauseClicked(bool checked);
|
||||
void OnCancelRenderClicked(bool checked);
|
||||
|
@ -241,7 +241,7 @@
|
||||
<string><html><head/><body><p>Save each RGBA component as 16-bits when saving Png files.</p><p>This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save 16-bit Png</string>
|
||||
<string>Save 16-bit Png/32-bit Exr</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -1192,6 +1192,19 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="FinalRenderSaveAgainAsButton">
|
||||
<property name="text">
|
||||
<string>Save Again As</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::MenuButtonPopup</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextOnly</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="FinalRenderBumpQualityStartButton">
|
||||
<property name="text">
|
||||
|
@ -266,7 +266,6 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
m_Renderer->SetEmber(it);
|
||||
m_Renderer->PrepFinalAccumVector(m_FinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
|
||||
m_Stats.Clear();
|
||||
Memset(m_FinalImage);
|
||||
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
|
||||
StripsRender<T>(m_Renderer.get(), it, m_FinalImage, 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
[&](size_t strip) { currentStripForProgress = strip; },//Pre strip.
|
||||
@ -298,7 +297,6 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
m_Renderer->SetEmber(*m_Ember, isBump ? eProcessAction::KEEP_ITERATING : eProcessAction::FULL_RENDER);
|
||||
m_Renderer->PrepFinalAccumVector(m_FinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
|
||||
m_Stats.Clear();
|
||||
Memset(m_FinalImage);
|
||||
Output(ComposePath(QString::fromStdString(m_Ember->m_Name)));
|
||||
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
|
||||
StripsRender<T>(m_Renderer.get(), *m_Ember, m_FinalImage, 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
@ -744,15 +742,29 @@ EmberNs::Renderer<T, float>* FinalRenderEmberController<T>::FirstOrDefaultRender
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the output of the last rendered image using the existing image output buffer in the renderer.
|
||||
/// </summary>
|
||||
/// <returns>The full path and filename the image was saved to.</returns>
|
||||
template<typename T>
|
||||
QString FinalRenderEmberController<T>::SaveCurrentAgain()
|
||||
{
|
||||
if (m_Ember)
|
||||
return SaveCurrentRender(*m_Ember);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the output of the render.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember whose rendered output will be saved</param>
|
||||
/// <returns>The full path and filename the image was saved to.</returns>
|
||||
template<typename T>
|
||||
void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember)
|
||||
QString FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember)
|
||||
{
|
||||
auto comments = m_Renderer->ImageComments(m_Stats, 0, true);
|
||||
SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_FinalRenderDialog->Png16Bit(), m_FinalRenderDialog->Transparency());
|
||||
return SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_FinalRenderDialog->Png16Bit(), m_FinalRenderDialog->Transparency());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -763,13 +775,15 @@ void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember)
|
||||
/// <param name="pixels">The buffer containing the pixels</param>
|
||||
/// <param name="width">The width in pixels of the image</param>
|
||||
/// <param name="height">The height in pixels of the image</param>
|
||||
/// <param name="png16Bit">Whether to use 16 bits per channel per pixel when saving as Png.</param>
|
||||
/// <param name="png16Bit">Whether to use 16 bits per channel per pixel when saving as Png/32-bits per channel when saving as Exr.</param>
|
||||
/// <param name="transparency">Whether to use alpha when saving as Png or Exr.</param>
|
||||
/// <returns>The full path and filename the image was saved to.</returns>
|
||||
template<typename T>
|
||||
void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency)
|
||||
QString FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency)
|
||||
{
|
||||
QString filename = ComposePath(QString::fromStdString(ember.m_Name));
|
||||
FractoriumEmberControllerBase::SaveCurrentRender(filename, comments, pixels, width, height, png16Bit, transparency);
|
||||
return filename;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -842,6 +856,7 @@ void FinalRenderEmberController<T>::HandleFinishedProgress()
|
||||
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int((float(finishedCountCached) / float(m_ImageCount)) * 100)));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString<qulonglong>(finishedCountCached) + " / " + ToString<qulonglong>(m_ImageCount)));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderSaveAgainAsButton, "setEnabled", Qt::QueuedConnection, Q_ARG(bool, !doAll && m_Renderer.get() && m_GuiState.m_Strips == 1));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderBumpQualityStartButton, "setEnabled", Qt::QueuedConnection, Q_ARG(bool, !doAll && m_Renderer.get() && m_GuiState.m_Strips == 1));
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ public:
|
||||
virtual double OriginalAspect() { return 1; }
|
||||
virtual QString ComposePath(const QString& name) { return ""; }
|
||||
virtual bool BumpQualityRender(double d) { return false; }
|
||||
virtual QString SaveCurrentAgain() { return ""; }
|
||||
virtual void CancelRender() { }
|
||||
virtual QString CheckMemory(const tuple<size_t, size_t, size_t>& p) { return ""; }
|
||||
|
||||
@ -131,6 +132,7 @@ public:
|
||||
virtual double OriginalAspect() override { return double(m_Ember->m_OrigFinalRasW) / m_Ember->m_OrigFinalRasH; }
|
||||
virtual QString Name() const override { return QString::fromStdString(m_Ember->m_Name); }
|
||||
virtual QString ComposePath(const QString& name) override;
|
||||
virtual QString SaveCurrentAgain() override;
|
||||
virtual void CancelRender() override;
|
||||
virtual QString CheckMemory(const tuple<size_t, size_t, size_t>& p) override;
|
||||
|
||||
@ -141,8 +143,8 @@ protected:
|
||||
virtual void Pause(bool pause) override;
|
||||
virtual bool Paused() override;
|
||||
void HandleFinishedProgress();
|
||||
void SaveCurrentRender(Ember<T>& ember);
|
||||
void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
|
||||
QString SaveCurrentRender(Ember<T>& ember);
|
||||
QString SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
|
||||
void RenderComplete(Ember<T>& ember);
|
||||
void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer);
|
||||
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true);
|
||||
|
@ -118,13 +118,13 @@ void FractoriumEmberController<T>::DeleteRenderer()
|
||||
/// <param name="pixels">The buffer containing the pixels</param>
|
||||
/// <param name="width">The width in pixels of the image</param>
|
||||
/// <param name="height">The height in pixels of the image</param>
|
||||
/// <param name="png16Bit">Whether to use 16 bits per channel per pixel when saving as Png.</param>
|
||||
/// <param name="png16Bit">Whether to use 16 bits per channel per pixel when saving as Png/32-bits per channel when saving as Exr.</param>
|
||||
/// <param name="transparency">Whether to use alpha when saving as Png or Exr.</param>
|
||||
void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency)
|
||||
{
|
||||
if (filename != "")
|
||||
{
|
||||
bool b = false;
|
||||
bool ret = false;
|
||||
auto size = width * height;
|
||||
auto settings = m_Fractorium->m_Settings;
|
||||
QFileInfo fileInfo(filename);
|
||||
@ -149,9 +149,9 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, c
|
||||
Rgba32ToRgb8(data, rgb8Image.data(), width, height);
|
||||
|
||||
if (suffix.endsWith("bmp", Qt::CaseInsensitive))
|
||||
b = WriteBmp(s.c_str(), rgb8Image.data(), width, height);
|
||||
ret = WriteBmp(s.c_str(), rgb8Image.data(), width, height);
|
||||
else if (suffix.endsWith("jpg", Qt::CaseInsensitive))
|
||||
b = WriteJpeg(s.c_str(), rgb8Image.data(), width, height, 100, true, comments, id, url, nick);
|
||||
ret = WriteJpeg(s.c_str(), rgb8Image.data(), width, height, 100, true, comments, id, url, nick);
|
||||
}
|
||||
else if (suffix.endsWith("png", Qt::CaseInsensitive))
|
||||
{
|
||||
@ -159,20 +159,32 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, c
|
||||
{
|
||||
vector<byte> rgba8Image(size * 4);
|
||||
Rgba32ToRgba8(data, rgba8Image.data(), width, height, transparency);
|
||||
b = WritePng(s.c_str(), rgba8Image.data(), width, height, 1, true, comments, id, url, nick);
|
||||
ret = WritePng(s.c_str(), rgba8Image.data(), width, height, 1, true, comments, id, url, nick);
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<glm::uint16> rgba16Image(size * 4);
|
||||
Rgba32ToRgba16(data, rgba16Image.data(), width, height, transparency);
|
||||
b = WritePng(s.c_str(), (byte*)rgba16Image.data(), width, height, 2, true, comments, id, url, nick);
|
||||
ret = WritePng(s.c_str(), (byte*)rgba16Image.data(), width, height, 2, true, comments, id, url, nick);
|
||||
}
|
||||
}
|
||||
else if (suffix.endsWith("exr", Qt::CaseInsensitive))
|
||||
{
|
||||
vector<Rgba> rgba32Image(size);
|
||||
Rgba32ToRgbaExr(data, rgba32Image.data(), width, height, transparency);
|
||||
b = WriteExr(s.c_str(), rgba32Image.data(), width, height, true, comments, id, url, nick);
|
||||
if (!png16Bit)//Repurpose this for EXR 32-bit.
|
||||
{
|
||||
vector<Rgba> rgba32Image(size);
|
||||
Rgba32ToRgbaExr(data, rgba32Image.data(), width, height, transparency);
|
||||
ret = WriteExr16(s.c_str(), rgba32Image.data(), width, height, true, comments, id, url, nick);
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<float> r(size);
|
||||
vector<float> g(size);
|
||||
vector<float> b(size);
|
||||
vector<float> a(size);
|
||||
Rgba32ToRgba32Exr(data, r.data(), g.data(), b.data(), a.data(), width, height, transparency);
|
||||
ret = WriteExr32(s.c_str(), r.data(), g.data(), b.data(), a.data(), width, height, true, comments, id, url, nick);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -180,7 +192,7 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, c
|
||||
return;
|
||||
}
|
||||
|
||||
if (b)
|
||||
if (ret)
|
||||
settings->SaveFolder(fileInfo.canonicalPath());
|
||||
else
|
||||
m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder.", true);
|
||||
|
@ -524,7 +524,7 @@
|
||||
<string><html><head/><body><p>Save each RGBA component as 16-bits when saving Png files.</p><p>This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save 16-bit Png</string>
|
||||
<string>Save 16-bit Png/32-bit Exr</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
Reference in New Issue
Block a user