--User changes

-Clear all color curves when clicking Reset while holding down Ctrl.

--Code changes
 -No longer assume palettes are 256 elements. Can now read and write longer palettes.
 -Ensure OpenCL images always get written when created.
This commit is contained in:
Person 2019-04-23 19:50:42 -07:00
parent 5209ead086
commit 77515aae73
14 changed files with 115 additions and 101 deletions

View File

@ -53,7 +53,6 @@ namespace EmberNs
#define SQRT5 T(2.2360679774997896964091736687313)
#define M_PHI T(1.61803398874989484820458683436563)
#define COLORMAP_LENGTH 256//These will need to change if 2D palette support is ever added, or variable sized palettes.
#define COLORMAP_LENGTH_MINUS_1 255
#define WHITE 255
#define DEFAULT_SBS (1024 * 10)
//#define XC(c) ((const xmlChar*)(c))

View File

@ -259,7 +259,7 @@ string EmberToXml<T>::ToString(Ember<T>& ember, const string& extraAttributes, s
//less error prone to just embed the palette.
if (hexPalette)
{
os << " <palette count=\"256\" format=\"RGB\"";
os << " <palette count=\"" << ember.m_Palette.Size() << "\" format=\"RGB\"";
if (!ember.m_Palette.m_SourceColors.empty())
{
@ -279,8 +279,9 @@ string EmberToXml<T>::ToString(Ember<T>& ember, const string& extraAttributes, s
os << ">\n";
os << std::uppercase;
auto rows = ember.m_Palette.Size() / 8;
for (i = 0; i < 32; i++)
for (i = 0; i < rows; i++)
{
os << " ";
@ -300,7 +301,7 @@ string EmberToXml<T>::ToString(Ember<T>& ember, const string& extraAttributes, s
}
else
{
for (i = 0; i < 256; i++)
for (i = 0; i < ember.m_Palette.Size(); i++)
{
double r = ember.m_Palette[i][0] * 255;
double g = ember.m_Palette[i][1] * 255;

View File

@ -246,7 +246,7 @@ public:
for (size_t i = 0; i < Size(); i++)
{
size_t ii = (i * 256) / COLORMAP_LENGTH;
size_t ii = (i * Size()) / Size();
T rgb[3], hsv[3];
rgb[0] = m_Entries[ii].r;
rgb[1] = m_Entries[ii].g;
@ -279,6 +279,7 @@ public:
void MakeAdjustedPalette(Palette<T>& palette, int rot, T hue, T sat, T bright, T cont, uint blur, uint freq)
{
T rgb[3], hsv[3];
palette.m_Entries.resize(Size());
if (freq > 1)
{
@ -306,13 +307,13 @@ public:
}
auto tempPal = palette;
intmax_t iSize = intmax_t(Size());
for (size_t i = 0; i < Size(); i++)
for (intmax_t i = 0; i < iSize; i++)
{
int ii = int(i);
rgb[0] = tempPal[std::abs(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].r;//Rotation.
rgb[1] = tempPal[std::abs(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].g;
rgb[2] = tempPal[std::abs(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].b;
rgb[0] = tempPal[std::abs(iSize + i - rot) % iSize].r;//Rotation.
rgb[1] = tempPal[std::abs(iSize + i - rot) % iSize].g;
rgb[2] = tempPal[std::abs(iSize + i - rot) % iSize].b;
RgbToHsv(rgb, hsv);
hsv[0] += hue * T(6.0);//Hue.
hsv[1] = Clamp<T>(hsv[1] + sat, 0, 1);//Saturation.
@ -335,7 +336,7 @@ public:
{
tempPal = palette;
for (int i = 0; i < 256; i++)
for (int i = 0; i < iSize; i++)
{
int n = -1;
rgb[0] = 0;
@ -345,7 +346,7 @@ public:
for (int j = i - int(blur); j <= i + int(blur); j++)
{
n++;
int k = (256 + j) % 256;
auto k = (iSize + j) % iSize;
if (k != i)
{

View File

@ -1497,6 +1497,7 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
{
size_t histIndex, intColorIndex, histSize = m_HistBuckets.size();
bucketT colorIndex, colorIndexFrac;
auto psm1 = m_Ember.m_Palette.Size() - 1;
//Linear is a linear scale for when the color index is not a whole number, which is most of the time.
//It uses a portion of the value of the index, and the remainder of the next index.
@ -1506,6 +1507,8 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
//Use overloaded addition and multiplication operators in vec4 to perform the accumulation.
if (PaletteMode() == ePaletteMode::PALETTE_LINEAR)
{
auto psm2 = psm1 - 1;
//It's critical to understand what's going on here as it's one of the most important parts of the algorithm.
//A color value gets retrieved from the palette and
//its RGB values are added to the existing RGB values in the histogram bucket.
@ -1542,7 +1545,7 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
//This will result in a few points at the very edges getting discarded, but prevents a crash and doesn't seem to make a speed difference.
if (histIndex < histSize)
{
colorIndex = bucketT(p.m_ColorX) * COLORMAP_LENGTH_MINUS_1;
colorIndex = bucketT(p.m_ColorX) * psm1;
intColorIndex = size_t(colorIndex);
if (intColorIndex < 0)
@ -1550,9 +1553,9 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
intColorIndex = 0;
colorIndexFrac = 0;
}
else if (intColorIndex >= COLORMAP_LENGTH_MINUS_1)
else if (intColorIndex >= psm1)
{
intColorIndex = COLORMAP_LENGTH_MINUS_1 - 1;
intColorIndex = psm2;
colorIndexFrac = 1;
}
else
@ -1608,7 +1611,7 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
if (histIndex < histSize)
{
intColorIndex = Clamp<size_t>(size_t(p.m_ColorX * COLORMAP_LENGTH_MINUS_1), 0, COLORMAP_LENGTH_MINUS_1);
intColorIndex = Clamp<size_t>(size_t(p.m_ColorX * psm1), 0, psm1);
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[histIndex]);//Vectorizer can't tell these point to different locations.
const bucketT* __restrict pal = glm::value_ptr(palette->m_Entries[intColorIndex]);

View File

@ -1310,19 +1310,18 @@ bool XmlToEmber<T>::ParseEmberElementFromChaos(xmlNode* emberNode, Ember<T>& cur
{
if (auto palettevalsnode = GetChildNodeByNodeName(palettenode, "values"))
{
int i = 0;
float r = 0, g = 0, b = 0;
auto colors = CCX(palettevalsnode->children->content);
istringstream istr(colors);
currentEmber.m_Palette.m_Entries.clear();
std::vector<v4F> tempv;
tempv.reserve(256);
while (istr >> r && istr >> g && istr >> b && i < COLORMAP_LENGTH)
{
currentEmber.m_Palette.m_Entries[i][0] = r;
currentEmber.m_Palette.m_Entries[i][1] = g;
currentEmber.m_Palette.m_Entries[i][2] = b;
currentEmber.m_Palette.m_Entries[i][3] = 1;
i++;
}
while (istr >> r && istr >> g && istr >> b)
tempv.push_back(v4F(r, g, b, 1));
if (!tempv.empty())
currentEmber.m_Palette.m_Entries = std::move(tempv);
}
}
else
@ -1393,9 +1392,10 @@ bool XmlToEmber<T>::ParseEmberElementFromChaos(xmlNode* emberNode, Ember<T>& cur
Spline<float> hspline(hvec);
Spline<float> sspline(svec);
Spline<float> vspline(vvec);
auto stepsize = (1.0f / (COLORMAP_LENGTH - 1));
currentEmber.m_Palette.m_Entries.resize(COLORMAP_LENGTH);
auto stepsize = (1.0f / (currentEmber.m_Palette.Size() - 1));
for (auto palindex = 0; palindex < COLORMAP_LENGTH; palindex++)
for (auto palindex = 0; palindex < currentEmber.m_Palette.Size(); palindex++)
{
float t = palindex * stepsize;
auto h = hspline.Interpolate(t);
@ -1768,22 +1768,15 @@ bool XmlToEmber<T>::ParseEmberElement(xmlNode* emberNode, Ember<T>& currentEmber
xmlFree(attStr);
}
//Palette colors are [0..255], convert to [0..1].
if (index >= 0 && index <= 255)
{
float alphaPercent = a / 255.0f;//Aplha percentage in the range of 0 to 1.
//Premultiply the palette.
currentEmber.m_Palette.m_Entries[index].r = alphaPercent * (r / 255.0f);
currentEmber.m_Palette.m_Entries[index].g = alphaPercent * (g / 255.0f);
currentEmber.m_Palette.m_Entries[index].b = alphaPercent * (b / 255.0f);
currentEmber.m_Palette.m_Entries[index].a = a / 255.0f;//Will be one for RGB, and other than one if RGBA with A != 255.
}
else
{
stringstream ss;
ss << "ParseEmberElement() : Color element with bad/missing index attribute " << index;
AddToReport(ss.str());
}
while (index >= currentEmber.m_Palette.Size())
currentEmber.m_Palette.m_Entries.push_back(v4F());
float alphaPercent = a / 255.0f;//Aplha percentage in the range of 0 to 1.
//Premultiply the palette.
currentEmber.m_Palette.m_Entries[index].r = alphaPercent * (r / 255.0f);//Palette colors are [0..255], convert to [0..1].
currentEmber.m_Palette.m_Entries[index].g = alphaPercent * (g / 255.0f);
currentEmber.m_Palette.m_Entries[index].b = alphaPercent * (b / 255.0f);
currentEmber.m_Palette.m_Entries[index].a = a / 255.0f;//Will be one for RGB, and other than one if RGBA with A != 255.
}
else if (!Compare(childNode->name, "colors"))
{
@ -2487,12 +2480,16 @@ bool XmlToEmber<T>::ParseHexColors(const char* colstr, Ember<T>& ember, size_t n
for (size_t strIndex = 0; strIndex < length;)
{
for (glm::length_t i = 0; i < 3 && colorCount < ember.m_Palette.Size(); i++)
for (glm::length_t i = 0; i < 3; i++)
{
const char tmpStr[3] = { s[strIndex++], s[strIndex++], 0 };//Read out and convert the string two characters at a time.
ss.clear();//Reset and fill the string stream.
ss.str(tmpStr);
ss >> tmp;//Do the conversion.
while (colorCount >= ember.m_Palette.Size())
ember.m_Palette.m_Entries.push_back(v4F());
ember.m_Palette.m_Entries[colorCount][i] = float(tmp) / 255.0f;//Hex palette is [0..255], convert to [0..1].
}

View File

@ -70,8 +70,6 @@ static string ConstantDefinesString(bool doublePrecision)
"#define NTHREADS 256u\n"
"#define THREADS_PER_WARP 32u\n"
"#define NWARPS (NTHREADS / THREADS_PER_WARP)\n"
"#define COLORMAP_LENGTH 256u\n"
"#define COLORMAP_LENGTH_MINUS_1 255\n"
"#define DE_THRESH 100u\n"
"#define BadVal(x) (isnan(x))\n"
"#define SQR(x) ((x) * (x))\n"
@ -206,6 +204,8 @@ struct ALIGN EmberCL
m3T m_CamMat;
T m_CenterX, m_CenterY;
T m_RotA, m_RotB, m_RotD, m_RotE;
T m_Psm1;
T m_Psm2;
};
/// <summary>
@ -232,6 +232,8 @@ static const char* EmberCLStructString =
" real_t m_C22;\n"
" real_t m_CenterX, m_CenterY;\n"
" real_t m_RotA, m_RotB, m_RotD, m_RotE;\n"
" real_t m_Psm1;\n"
" real_t m_Psm2;\n"
"} EmberCL;\n"
"\n";

View File

@ -412,21 +412,21 @@ FunctionMapper::FunctionMapper()
" return 0.0;\n"
" }\n"
"\n"
" if (w <= 4) // w in (0, 4]\n"
" if (w <= 4)\n"
" {\n"
" real_t y = x * x;\n"
" r = EvalRational(P1, Q1, y, 7);\n"
" factor = w * (w + x1) * ((w - x11 / 256) - x12);\n"
" value = factor * r;\n"
" }\n"
" else if (w <= 8) // w in (4, 8]\n"
" else if (w <= 8)\n"
" {\n"
" real_t y = x * x;\n"
" r = EvalRational(P2, Q2, y, 8);\n"
" factor = w * (w + x2) * ((w - x21 / 256) - x22);\n"
" value = factor * r;\n"
" }\n"
" else // w in (8, \infty)\n"
" else\n"
" {\n"
" real_t y = 8 / w;\n"
" real_t y2 = y * y;\n"
@ -440,7 +440,7 @@ FunctionMapper::FunctionMapper()
"\n"
" if (x < 0)\n"
" {\n"
" value *= -1; // odd function\n"
" value *= -1;\n"
" }\n"
"\n"
" return value;\n"

View File

@ -471,23 +471,24 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
{
os <<
" real_t colorIndexFrac;\n"
" real_t colorIndex = secondPoint.m_ColorX * COLORMAP_LENGTH_MINUS_1;\n"
" int intColorIndex = (int)colorIndex;\n"
" real_t colorIndex = secondPoint.m_ColorX * ember->m_Psm1;\n"
" int intColorIndex;\n"
" float4 palColor2;\n"
"\n"
" if (intColorIndex < 0)\n"
" if (colorIndex < 0)\n"
" {\n"
" intColorIndex = 0;\n"
" colorIndexFrac = 0;\n"
" }\n"
" else if (intColorIndex >= COLORMAP_LENGTH_MINUS_1)\n"
" else if (colorIndex >= ember->m_Psm1)\n"
" {\n"
" intColorIndex = COLORMAP_LENGTH_MINUS_1 - 1;\n"
" intColorIndex = (int)ember->m_Psm2;\n"
" colorIndexFrac = 1.0;\n"
" }\n"
" else\n"
" {\n"
" colorIndexFrac = colorIndex - (real_t)intColorIndex;\n"//Interpolate between intColorIndex and intColorIndex + 1.
" intColorIndex = (int)colorIndex;\n"
" colorIndexFrac = colorIndex - intColorIndex;\n"//Interpolate between intColorIndex and intColorIndex + 1.
" }\n"
"\n"
" iPaletteCoord.x = intColorIndex;\n"//Palette operations are strictly float because OpenCL does not support dp64 textures.
@ -499,7 +500,7 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
else if (ember.m_PaletteMode == ePaletteMode::PALETTE_STEP)
{
os <<
" iPaletteCoord.x = (int)(secondPoint.m_ColorX * COLORMAP_LENGTH_MINUS_1);\n"
" iPaletteCoord.x = (int)(secondPoint.m_ColorX * ember->m_Psm1);\n"
" palColor1 = read_imagef(palette, paletteSampler, iPaletteCoord);\n";
}

View File

@ -393,12 +393,10 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
if (m_Info->CheckCL(err, "cl::ImageGL()"))
{
m_GLImages.push_back(namedImageGL);
if (data)
return WriteImage2D(m_GLImages.size() - 1, true, width, height, row_pitch, data);//OpenGL images/textures require a separate write.
else
return true;
imageIndex = int(m_GLImages.size()) - 1;
}
else
return false;
}
else
{
@ -407,8 +405,10 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
if (m_Info->CheckCL(err, "cl::Image2D()"))
{
m_Images.push_back(namedImage);
return true;
imageIndex = int(m_Images.size()) - 1;
}
else
return false;
}
}
else//It did exist, so create new if sizes are different. Write if data is not NULL.
@ -422,18 +422,10 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
NamedImage2DGL namedImageGL(cl::ImageGL(m_Context, flags, GL_TEXTURE_2D, 0, texName, &err), name);//Sizes are different, so create new.
if (m_Info->CheckCL(err, "cl::ImageGL()"))
{
m_GLImages[imageIndex] = namedImageGL;
}
else
return false;
}
//Write data to new image since OpenGL images/textures require a separate write, must match new size.
if (data)
return WriteImage2D(imageIndex, true, width, height, row_pitch, data);
else
return true;
}
else
{
@ -443,17 +435,17 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
NamedImage2D namedImage(cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err), name);
if (m_Info->CheckCL(err, "cl::Image2D()"))
{
m_Images[imageIndex] = namedImage;
return true;
}
else
return false;
}
else if (data)
return WriteImage2D(imageIndex, false, width, height, row_pitch, data);
else//Strange case: images were same dimensions but no data was passed in, so do nothing.
return true;
}
}
if (data)
return WriteImage2D(imageIndex, shared, width, height, row_pitch, data);
else//Strange case: images were same dimensions but no data was passed in, so do nothing.
return true;
}
return false;

View File

@ -81,7 +81,7 @@ bool RendererCL<T, bucketT>::Init(const vector<pair<size_t, size_t>>& devices, b
{
if (b && !(b = cld->m_Wrapper.AddProgram(m_IterOpenCLKernelCreator.ZeroizeEntryPoint(), zeroizeProgram, m_IterOpenCLKernelCreator.ZeroizeEntryPoint(), m_DoublePrecision))) { ErrorStr(loc, "Failed to init zeroize program: "s + cld->ErrorReportString(), cld.get()); }
if (b && !(b = cld->m_Wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, 256, 1, 0, nullptr))) { ErrorStr(loc, "Failed to init palette buffer: "s + cld->ErrorReportString(), cld.get()); }
if (b && !(b = cld->m_Wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, m_Ember.m_Palette.Size(), 1, 0, nullptr))) { ErrorStr(loc, "Failed to init palette buffer: "s + cld->ErrorReportString(), cld.get()); }
if (b && !(b = cld->m_Wrapper.AddAndWriteBuffer(m_GlobalSharedBufferName, m_GlobalShared.second.data(), m_GlobalShared.second.size() * sizeof(m_GlobalShared.second[0])))) { ErrorStr(loc, "Failed to init global shared buffer: "s + cld->ErrorReportString(), cld.get()); }//Empty at start, will be filled in later if needed.
@ -853,7 +853,7 @@ EmberStats RendererCL<T, bucketT>::Iterate(size_t iterCount, size_t temporalSamp
break;
}
if (b && !(b = wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, m_Dmap.m_Entries.size(), 1, 0, m_Dmap.m_Entries.data())))
if (b && !(b = wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, m_Dmap.Size(), 1, 0, m_Dmap.m_Entries.data())))
{
ErrorStr(loc, "Write palette buffer failed", device.get());
break;
@ -1787,6 +1787,8 @@ void RendererCL<T, bucketT>::ConvertEmber(Ember<T>& ember, EmberCL<T>& emberCL,
emberCL.m_RotB = m_RotMat.B();
emberCL.m_RotD = m_RotMat.D();
emberCL.m_RotE = m_RotMat.E();
emberCL.m_Psm1 = T(m_Dmap.Size() - 1);
emberCL.m_Psm2 = T(m_Dmap.Size() - 2);
for (size_t i = 0; i < ember.TotalXformCount() && i < xformsCL.size(); i++)
{

View File

@ -248,6 +248,7 @@ public:
virtual void PaletteCellClicked(int row, int col) { }
virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) { }
virtual void PaletteEditorButtonClicked() { }
virtual void PaletteEditorColorChanged() { }
QImage& FinalPaletteImage() { return m_FinalPaletteImage; }
//Info.
@ -535,6 +536,7 @@ public:
virtual void PaletteCellClicked(int row, int col) override;
virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) override;
virtual void PaletteEditorButtonClicked() override;
virtual void PaletteEditorColorChanged() override;
//Info.
virtual void FillSummary() override;

View File

@ -406,7 +406,7 @@ void FractoriumEmberController<T>::PaletteEditorButtonClicked()
if (auto xform = m_Ember.GetTotalXform(index.first, forceFinal))
xform->m_ColorX = index.second;
edPal = ed->GetPalette(int(256));
edPal = ed->GetPalette(int(prevPal.Size()));
SetBasePaletteAndAdjust(edPal);//This will take care of updating the color index controls.
if (edPal.m_Filename.get() && !edPal.m_Filename->empty())
@ -458,10 +458,16 @@ void Fractorium::OnPaletteEditorButtonClicked(bool checked)
/// <summary>
/// Slot called every time a color is changed in the palette editor.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::PaletteEditorColorChanged()
{
SetBasePaletteAndAdjust(m_Fractorium->m_PaletteEditor->GetPalette(int(m_TempPalette.Size())));
}
void Fractorium::OnPaletteEditorColorChanged()
{
m_PaletteChanged = true;
m_Controller->SetBasePaletteAndAdjust(m_PaletteEditor->GetPalette(int(256)));
m_Controller->PaletteEditorColorChanged();
}
/// <summary>
@ -569,7 +575,7 @@ void Fractorium::SetPaletteFileComboIndex(const string& filename)
/// <summary>
/// Reset the color curve values for the selected curve in the current ember to their default state and also update the curves control.
/// Called when ResetCurvesButton is clicked.
/// Note if they click Reset Curves when the "All" radio button is selected, then it clears all curves.
/// Note if they click Reset Curves when the ctrl is pressed, then it clears all curves.
/// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip.
/// </summary>
/// <param name="i">The index of the curve to be cleared, 0 to clear all.</param>
@ -578,26 +584,34 @@ void FractoriumEmberController<T>::ClearColorCurves(int i)
{
Update([&]
{
if (i)
m_Ember.m_Curves.Init(i);
else
m_Ember.m_Curves.Init(0);
m_Ember.m_Curves.Init(i);
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY);
FillCurvesControl();
}
void Fractorium::OnResetCurvesButtonClicked(bool checked)
{
if (ui.CurvesAllRadio->isChecked())
m_Controller->ClearColorCurves(0);
else if (ui.CurvesRedRadio->isChecked())
m_Controller->ClearColorCurves(1);
else if (ui.CurvesGreenRadio->isChecked())
m_Controller->ClearColorCurves(2);
else if (ui.CurvesBlueRadio->isChecked())
m_Controller->ClearColorCurves(3);
if (!QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
{
if (ui.CurvesAllRadio->isChecked())
m_Controller->ClearColorCurves(0);
else if (ui.CurvesRedRadio->isChecked())
m_Controller->ClearColorCurves(1);
else if (ui.CurvesGreenRadio->isChecked())
m_Controller->ClearColorCurves(2);
else if (ui.CurvesBlueRadio->isChecked())
m_Controller->ClearColorCurves(3);
else
m_Controller->ClearColorCurves(0);
}
else
{
m_Controller->ClearColorCurves(0);
m_Controller->ClearColorCurves(1);
m_Controller->ClearColorCurves(2);
m_Controller->ClearColorCurves(3);
}
}
/// <summary>

View File

@ -22,12 +22,12 @@ void Fractorium::InitParamsUI()
SetFixedTableHeader(ui.IterationTableHeader->horizontalHeader());
SetFixedTableHeader(ui.AnimationTableHeader->horizontalHeader());
//Color.
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.05, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaSpin, spinHeight, 1, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 30, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.01, dmax, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaSpin, spinHeight, 1, dmax, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, dmax, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_VibrancySpin, spinHeight, 0, dmax, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_HighlightSpin, spinHeight, -1.0, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, 1.0, 1.0, -1.0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_K2Spin, spinHeight, 0, 10.0, 0.0001, SIGNAL(valueChanged(double)), SLOT(OnK2Changed(double)), true, 0, 0.0001, 0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_K2Spin, spinHeight, 0, 99.0, 0.0001, SIGNAL(valueChanged(double)), SLOT(OnK2Changed(double)), true, 0, 0.0001, 0);
m_HighlightSpin->DoubleClickLowVal(-1.0);
int dec = 6;
m_BrightnessSpin->setDecimals(dec);

View File

@ -229,7 +229,7 @@ void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int new
template <typename T>
QColor FractoriumEmberController<T>::ColorIndexToQColor(double d)
{
v4F entry = m_Ember.m_Palette[Clamp<size_t>(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())];
v4F entry = m_Ember.m_Palette[Clamp<size_t>(d * (m_Ember.m_Palette.Size() - 1), 0, m_Ember.m_Palette.Size())];
entry.r *= 255;
entry.g *= 255;
entry.b *= 255;