diff --git a/Source/Ember/Renderer.cpp b/Source/Ember/Renderer.cpp index a95f0eb..455df8b 100644 --- a/Source/Ember/Renderer.cpp +++ b/Source/Ember/Renderer.cpp @@ -1137,6 +1137,25 @@ eRenderStatus Renderer::AccumulatorToFinalImage(vector& pixels, return eRenderStatus::RENDER_ERROR; } + if (RawHistogram()) + { + auto p = pixels.data() + finalOffset; + auto q = m_AccumulatorBuckets.data(); + auto bytes = sizeof(*p) * FinalRasW(); + parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j) + { + auto pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRasW();//Pull out of inner loop for optimization. + auto rowStart = j * m_SuperRasW; + memcpy(p + pixelsRowStart, q + rowStart, bytes); + } +#if defined(_WIN32) || defined(__APPLE__) + , tbb::static_partitioner() +#endif + ); + LeaveFinalAccum(); + return m_Abort ? eRenderStatus::RENDER_ABORT : eRenderStatus::RENDER_OK; + } + //Timing t(4); const size_t filterWidth = m_SpatialFilter->FinalFilterWidth(); bucketT g, linRange, vibrancy; diff --git a/Source/Ember/RendererBase.cpp b/Source/Ember/RendererBase.cpp index 3509d7b..6ef94fe 100644 --- a/Source/Ember/RendererBase.cpp +++ b/Source/Ember/RendererBase.cpp @@ -374,6 +374,40 @@ void RendererBase::ReclaimOnResize(bool reclaimOnResize) ChangeVal([&] { m_ReclaimOnResize = reclaimOnResize; }, eProcessAction::FULL_RENDER); } +/// +/// Get whether to output raw histogram values instead of gamma corrected +/// colours. +/// Default: false. +/// +/// True if raw, else false. +bool RendererBase::RawHistogram() const { return m_RawHistogram; } + +/// +/// Set whether to output raw histogram values instead of gamma corrected +/// colours. +/// +/// True if raw, else false. +void RendererBase::RawHistogram(bool rawHistogram) +{ + ChangeVal([&] { m_RawHistogram = rawHistogram; }, eProcessAction::ACCUM_ONLY); +} + +/// +/// Get whether to output raw histogram values before density filtering. +/// Default: false. +/// +/// True if raw, else false. +bool RendererBase::RawHistogramPreDensity() const { return m_RawHistogramPreDensity; } + +/// +/// Set whether to output raw histogram values before density filtering. +/// +/// True if pre density, else false. +void RendererBase::RawHistogramPreDensity(bool rawHistogramPreDensity) +{ + ChangeVal([&] { m_RawHistogramPreDensity = rawHistogramPreDensity; }, eProcessAction::ACCUM_ONLY); +} + /// /// Set the callback object. /// diff --git a/Source/Ember/RendererBase.h b/Source/Ember/RendererBase.h index 542498e..32e213c 100644 --- a/Source/Ember/RendererBase.h +++ b/Source/Ember/RendererBase.h @@ -156,6 +156,10 @@ public: void InsertPalette(bool insertPalette); bool ReclaimOnResize() const; void ReclaimOnResize(bool reclaimOnResize); + bool RawHistogram() const; + void RawHistogram(bool rawHistogram); + bool RawHistogramPreDensity() const; + void RawHistogramPreDensity(bool rawHistogramPreDensity); void Callback(RenderCallback* callback); void ThreadCount(size_t threads, const char* seedString = nullptr); size_t BytesPerChannel() const; @@ -207,6 +211,8 @@ protected: bool m_InFinalAccum = false; bool m_InsertPalette = false; bool m_ReclaimOnResize = false; + bool m_RawHistogram = false; + bool m_RawHistogramPreDensity = false; bool m_CurvesSet = false; volatile bool m_Abort = false; volatile bool m_Pause = false; diff --git a/Source/EmberAnimate/EmberAnimate.cpp b/Source/EmberAnimate/EmberAnimate.cpp index 39a190f..036b33c 100644 --- a/Source/EmberAnimate/EmberAnimate.cpp +++ b/Source/EmberAnimate/EmberAnimate.cpp @@ -332,6 +332,8 @@ bool EmberAnimate(int argc, _TCHAR* argv[], EmberOptions& opt) r->EarlyClip(opt.EarlyClip()); r->YAxisUp(opt.YAxisUp()); r->LockAccum(opt.LockAccum()); + r->RawHistogram(opt.RawHistogram()); + r->RawHistogramPreDensity(opt.RawHistogramPreDensity()); r->PixelAspectRatio(T(opt.AspectRatio())); r->Priority(eThreadPriority(Clamp(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); } @@ -471,7 +473,7 @@ bool EmberAnimate(int argc, _TCHAR* argv[], EmberOptions& opt) vector g(size); vector b(size); vector a(size); - Rgba32ToRgba32Exr(finalImagep, r.data(), g.data(), b.data(), a.data(), w, h, opt.Transparency()); + Rgba32ToRgba32Exr(finalImagep, r.data(), g.data(), b.data(), a.data(), w, h, opt.Transparency(), ! opt.RawHistogram()); const auto writeSuccess = WriteExr32(fn.c_str(), r.data(), g.data(), diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index 8f31d8d..615db39 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -433,8 +433,36 @@ template bool RendererCL::ReadFinal(v4F* pixels) { if (pixels && !m_Devices.empty()) - return m_Devices[0]->m_Wrapper.ReadImage(m_FinalImageName, FinalRasW(), FinalRasH(), 0, m_Devices[0]->m_Wrapper.Shared(), pixels); - + { + if (RawHistogram()) + { + if (RawHistogramPreDensity() ? ReadHist(0) : ReadAccum()) + { + auto p = pixels; + auto q = RawHistogramPreDensity() ? HistBuckets() : AccumulatorBuckets(); + auto bytes = sizeof(*p) * FinalRasW(); + parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j) + { + auto pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRasW();//Pull out of inner loop for optimization. + auto rowStart = j * SuperRasW(); + memcpy(p + pixelsRowStart, q + rowStart, bytes); + } + #if defined(_WIN32) || defined(__APPLE__) + , tbb::static_partitioner() + #endif + ); + return true; + } + else + { + return false; + } + } + else + { + return m_Devices[0]->m_Wrapper.ReadImage(m_FinalImageName, FinalRasW(), FinalRasH(), 0, m_Devices[0]->m_Wrapper.Shared(), pixels); + } + } return false; } diff --git a/Source/EmberCommon/EmberCommon.h b/Source/EmberCommon/EmberCommon.h index 2fbc892..0da7db2 100644 --- a/Source/EmberCommon/EmberCommon.h +++ b/Source/EmberCommon/EmberCommon.h @@ -310,10 +310,13 @@ static void Rgba32ToRgbaExr(const v4F* rgba, Rgba* ilmfRgba, size_t width, size_ /// The width of the image in pixels /// The height of the image in pixels /// True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible -static void Rgba32ToRgba32Exr(const v4F* rgba, float* r, float* g, float* b, float* a, size_t width, size_t height, bool doAlpha) +/// True to clamp output to 0..1, false to use full high dynamic range +static void Rgba32ToRgba32Exr(const v4F* rgba, float* r, float* g, float* b, float* a, size_t width, size_t height, bool doAlpha, bool doClamp) { if (rgba != nullptr && r != nullptr && g != nullptr && b != nullptr && a != nullptr) { + if (doClamp) + { for (size_t i = 0; i < (width * height); i++) { r[i] = Clamp(Sqr(rgba[i].r), 0.0f, 1.0f); @@ -321,6 +324,17 @@ static void Rgba32ToRgba32Exr(const v4F* rgba, float* r, float* g, float* b, flo b[i] = Clamp(Sqr(rgba[i].b), 0.0f, 1.0f); a[i] = doAlpha ? Clamp(rgba[i].a * 1.0f, 0.0f, 1.0f) : 1.0f; } + } + else + { + for (size_t i = 0; i < (width * height); i++) + { + r[i] = rgba[i].r; + g[i] = rgba[i].g; + b[i] = rgba[i].b; + a[i] = rgba[i].a; + } + } } } diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index 9bc0a5e..da9828d 100644 --- a/Source/EmberCommon/EmberOptions.h +++ b/Source/EmberCommon/EmberOptions.h @@ -70,6 +70,8 @@ enum class eOptionIDs : et OPT_DUMP_KERNEL, OPT_FLAM3_COMPAT, OPT_IGNORE_EXISTING, + OPT_RAW_HISTOGRAM, + OPT_RAW_HISTOGRAM_PRE_DENSITY, //Value args. OPT_NTHREADS,//Int value args. @@ -378,6 +380,8 @@ public: INITBOOLOPTION(DumpKernel, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_DUMP_KERNEL, _T("--dump_kernel"), false, SO_NONE, " --dump_kernel Print the iteration kernel string when using OpenCL (ignored for CPU) [default: false].\n")); INITBOOLOPTION(Flam3Compat, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_FLAM3_COMPAT, _T("--flam3_compat"), false, SO_NONE, " --flam3_compat The behavior of the cos, cosh, cot, coth, csc, csch, sec, sech, sin, sinh, tan and tanh variations are different in flam3/Apophysis versus Chaotica. True for flam3/Apophysis behavior, false for Chaotica behavior [default: true].\n")); INITBOOLOPTION(IgnoreExisting, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_IGNORE_EXISTING, _T("--ignore-existing"), false, SO_NONE, " --ignore-existing Skip animating a frame if the output images for all of the specified file output types already exist in the output folder [default: false].\n")); + INITBOOLOPTION(RawHistogram, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_RAW_HISTOGRAM, _T("--raw_histogram"), false, SO_NONE, " --raw_histogram Output raw histogram values (after density filter, before gamma correction). For best results, use EXR 32-bit float and set supersampling to 1x. [default: false].\n")); + INITBOOLOPTION(RawHistogramPreDensity, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_RAW_HISTOGRAM_PRE_DENSITY, _T("--raw_histogram_pre_density"), false, SO_NONE, " --raw_histogram_pre_density Output raw histogram values before density filter instead of afterwards. Requires --raw_histogram. [default: false].\n")); //Int. INITINTOPTION(Symmetry, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SYMMETRY, _T("--symmetry"), 0, SO_REQ_SEP, " --symmetry= Set symmetry of result [default: 0].\n")); INITINTOPTION(SheepGen, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_GEN, _T("--sheep_gen"), -1, SO_REQ_SEP, " --sheep_gen= Sheep generation of this flame [default: -1].\n")); @@ -541,6 +545,8 @@ public: PARSEBOOLOPTION(eOptionIDs::OPT_DUMP_KERNEL, DumpKernel); PARSEBOOLOPTION(eOptionIDs::OPT_FLAM3_COMPAT, Flam3Compat); PARSEBOOLOPTION(eOptionIDs::OPT_IGNORE_EXISTING, IgnoreExisting); + PARSEBOOLOPTION(eOptionIDs::OPT_RAW_HISTOGRAM, RawHistogram); + PARSEBOOLOPTION(eOptionIDs::OPT_RAW_HISTOGRAM_PRE_DENSITY, RawHistogramPreDensity); PARSEOPTION(eOptionIDs::OPT_SYMMETRY, Symmetry);//Int args PARSEOPTION(eOptionIDs::OPT_SHEEP_GEN, SheepGen); PARSEOPTION(eOptionIDs::OPT_SHEEP_ID, SheepId); @@ -833,6 +839,8 @@ public: Eob DumpKernel; Eob Flam3Compat; Eob IgnoreExisting; + Eob RawHistogram; + Eob RawHistogramPreDensity; Eoi Symmetry;//Value int. Eoi SheepGen; diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp index c10d065..6e51fc9 100644 --- a/Source/EmberGenome/EmberGenome.cpp +++ b/Source/EmberGenome/EmberGenome.cpp @@ -603,6 +603,8 @@ bool EmberGenome(int argc, _TCHAR* argv[], EmberOptions& opt) renderer->EarlyClip(opt.EarlyClip()); renderer->YAxisUp(opt.YAxisUp()); renderer->LockAccum(opt.LockAccum()); + renderer->RawHistogram(opt.RawHistogram()); + renderer->RawHistogramPreDensity(opt.RawHistogramPreDensity()); renderer->PixelAspectRatio(T(opt.AspectRatio())); if (opt.Repeat() == 0) diff --git a/Source/EmberRender/EmberRender.cpp b/Source/EmberRender/EmberRender.cpp index 19b9742..21f6cf4 100644 --- a/Source/EmberRender/EmberRender.cpp +++ b/Source/EmberRender/EmberRender.cpp @@ -153,6 +153,8 @@ bool EmberRender(int argc, _TCHAR* argv[], EmberOptions& opt) renderer->EarlyClip(opt.EarlyClip()); renderer->YAxisUp(opt.YAxisUp()); renderer->LockAccum(opt.LockAccum()); + renderer->RawHistogram(opt.RawHistogram()); + renderer->RawHistogramPreDensity(opt.RawHistogramPreDensity()); renderer->InsertPalette(opt.InsertPalette()); renderer->PixelAspectRatio(T(opt.AspectRatio())); renderer->Priority(eThreadPriority(Clamp(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); @@ -447,7 +449,7 @@ bool EmberRender(int argc, _TCHAR* argv[], EmberOptions& opt) vector g(size); vector b(size); vector a(size); - Rgba32ToRgba32Exr(finalImagep, r.data(), g.data(), b.data(), a.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.Transparency()); + Rgba32ToRgba32Exr(finalImagep, r.data(), g.data(), b.data(), a.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.Transparency(), ! opt.RawHistogram()); const auto writeSuccess = WriteExr32(filename.c_str(), r.data(), g.data(), diff --git a/Source/Fractorium/FractoriumRender.cpp b/Source/Fractorium/FractoriumRender.cpp index e03afdf..23421a3 100644 --- a/Source/Fractorium/FractoriumRender.cpp +++ b/Source/Fractorium/FractoriumRender.cpp @@ -183,7 +183,8 @@ bool FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, c vector g(size); vector b(size); vector a(size); - Rgba32ToRgba32Exr(data, r.data(), g.data(), b.data(), a.data(), width, height, transparency); + bool raw_histogram = false; + Rgba32ToRgba32Exr(data, r.data(), g.data(), b.data(), a.data(), width, height, transparency, ! raw_histogram); ret = WriteExr32(s.c_str(), r.data(), g.data(), b.data(), a.data(), width, height, true, comments, id, url, nick); } }