--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 SQRT5 T(2.2360679774997896964091736687313)
#define M_PHI T(1.61803398874989484820458683436563) #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 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 WHITE 255
#define DEFAULT_SBS (1024 * 10) #define DEFAULT_SBS (1024 * 10)
//#define XC(c) ((const xmlChar*)(c)) //#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. //less error prone to just embed the palette.
if (hexPalette) if (hexPalette)
{ {
os << " <palette count=\"256\" format=\"RGB\""; os << " <palette count=\"" << ember.m_Palette.Size() << "\" format=\"RGB\"";
if (!ember.m_Palette.m_SourceColors.empty()) 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 << ">\n";
os << std::uppercase; os << std::uppercase;
auto rows = ember.m_Palette.Size() / 8;
for (i = 0; i < 32; i++) for (i = 0; i < rows; i++)
{ {
os << " "; os << " ";
@ -300,7 +301,7 @@ string EmberToXml<T>::ToString(Ember<T>& ember, const string& extraAttributes, s
} }
else 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 r = ember.m_Palette[i][0] * 255;
double g = ember.m_Palette[i][1] * 255; double g = ember.m_Palette[i][1] * 255;

View File

@ -246,7 +246,7 @@ public:
for (size_t i = 0; i < Size(); i++) 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]; T rgb[3], hsv[3];
rgb[0] = m_Entries[ii].r; rgb[0] = m_Entries[ii].r;
rgb[1] = m_Entries[ii].g; 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) void MakeAdjustedPalette(Palette<T>& palette, int rot, T hue, T sat, T bright, T cont, uint blur, uint freq)
{ {
T rgb[3], hsv[3]; T rgb[3], hsv[3];
palette.m_Entries.resize(Size());
if (freq > 1) if (freq > 1)
{ {
@ -306,13 +307,13 @@ public:
} }
auto tempPal = palette; 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(iSize + i - rot) % iSize].r;//Rotation.
rgb[0] = tempPal[std::abs(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].r;//Rotation. rgb[1] = tempPal[std::abs(iSize + i - rot) % iSize].g;
rgb[1] = tempPal[std::abs(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].g; rgb[2] = tempPal[std::abs(iSize + i - rot) % iSize].b;
rgb[2] = tempPal[std::abs(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].b;
RgbToHsv(rgb, hsv); RgbToHsv(rgb, hsv);
hsv[0] += hue * T(6.0);//Hue. hsv[0] += hue * T(6.0);//Hue.
hsv[1] = Clamp<T>(hsv[1] + sat, 0, 1);//Saturation. hsv[1] = Clamp<T>(hsv[1] + sat, 0, 1);//Saturation.
@ -335,7 +336,7 @@ public:
{ {
tempPal = palette; tempPal = palette;
for (int i = 0; i < 256; i++) for (int i = 0; i < iSize; i++)
{ {
int n = -1; int n = -1;
rgb[0] = 0; rgb[0] = 0;
@ -345,7 +346,7 @@ public:
for (int j = i - int(blur); j <= i + int(blur); j++) for (int j = i - int(blur); j <= i + int(blur); j++)
{ {
n++; n++;
int k = (256 + j) % 256; auto k = (iSize + j) % iSize;
if (k != i) 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(); size_t histIndex, intColorIndex, histSize = m_HistBuckets.size();
bucketT colorIndex, colorIndexFrac; 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. //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. //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. //Use overloaded addition and multiplication operators in vec4 to perform the accumulation.
if (PaletteMode() == ePaletteMode::PALETTE_LINEAR) 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. //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 //A color value gets retrieved from the palette and
//its RGB values are added to the existing RGB values in the histogram bucket. //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. //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) if (histIndex < histSize)
{ {
colorIndex = bucketT(p.m_ColorX) * COLORMAP_LENGTH_MINUS_1; colorIndex = bucketT(p.m_ColorX) * psm1;
intColorIndex = size_t(colorIndex); intColorIndex = size_t(colorIndex);
if (intColorIndex < 0) if (intColorIndex < 0)
@ -1550,9 +1553,9 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
intColorIndex = 0; intColorIndex = 0;
colorIndexFrac = 0; colorIndexFrac = 0;
} }
else if (intColorIndex >= COLORMAP_LENGTH_MINUS_1) else if (intColorIndex >= psm1)
{ {
intColorIndex = COLORMAP_LENGTH_MINUS_1 - 1; intColorIndex = psm2;
colorIndexFrac = 1; colorIndexFrac = 1;
} }
else else
@ -1608,7 +1611,7 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
if (histIndex < histSize) 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. 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]); 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")) if (auto palettevalsnode = GetChildNodeByNodeName(palettenode, "values"))
{ {
int i = 0;
float r = 0, g = 0, b = 0; float r = 0, g = 0, b = 0;
auto colors = CCX(palettevalsnode->children->content); auto colors = CCX(palettevalsnode->children->content);
istringstream istr(colors); 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) while (istr >> r && istr >> g && istr >> b)
{ tempv.push_back(v4F(r, g, b, 1));
currentEmber.m_Palette.m_Entries[i][0] = r;
currentEmber.m_Palette.m_Entries[i][1] = g; if (!tempv.empty())
currentEmber.m_Palette.m_Entries[i][2] = b; currentEmber.m_Palette.m_Entries = std::move(tempv);
currentEmber.m_Palette.m_Entries[i][3] = 1;
i++;
}
} }
} }
else else
@ -1393,9 +1392,10 @@ bool XmlToEmber<T>::ParseEmberElementFromChaos(xmlNode* emberNode, Ember<T>& cur
Spline<float> hspline(hvec); Spline<float> hspline(hvec);
Spline<float> sspline(svec); Spline<float> sspline(svec);
Spline<float> vspline(vvec); 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; float t = palindex * stepsize;
auto h = hspline.Interpolate(t); auto h = hspline.Interpolate(t);
@ -1768,22 +1768,15 @@ bool XmlToEmber<T>::ParseEmberElement(xmlNode* emberNode, Ember<T>& currentEmber
xmlFree(attStr); xmlFree(attStr);
} }
//Palette colors are [0..255], convert to [0..1]. while (index >= currentEmber.m_Palette.Size())
if (index >= 0 && index <= 255) currentEmber.m_Palette.m_Entries.push_back(v4F());
{
float alphaPercent = a / 255.0f;//Aplha percentage in the range of 0 to 1. float alphaPercent = a / 255.0f;//Aplha percentage in the range of 0 to 1.
//Premultiply the palette. //Premultiply the palette.
currentEmber.m_Palette.m_Entries[index].r = alphaPercent * (r / 255.0f); 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].g = alphaPercent * (g / 255.0f);
currentEmber.m_Palette.m_Entries[index].b = alphaPercent * (b / 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. 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());
}
} }
else if (!Compare(childNode->name, "colors")) 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 (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. 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.clear();//Reset and fill the string stream.
ss.str(tmpStr); ss.str(tmpStr);
ss >> tmp;//Do the conversion. 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]. 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 NTHREADS 256u\n"
"#define THREADS_PER_WARP 32u\n" "#define THREADS_PER_WARP 32u\n"
"#define NWARPS (NTHREADS / THREADS_PER_WARP)\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 DE_THRESH 100u\n"
"#define BadVal(x) (isnan(x))\n" "#define BadVal(x) (isnan(x))\n"
"#define SQR(x) ((x) * (x))\n" "#define SQR(x) ((x) * (x))\n"
@ -206,6 +204,8 @@ struct ALIGN EmberCL
m3T m_CamMat; m3T m_CamMat;
T m_CenterX, m_CenterY; T m_CenterX, m_CenterY;
T m_RotA, m_RotB, m_RotD, m_RotE; T m_RotA, m_RotB, m_RotD, m_RotE;
T m_Psm1;
T m_Psm2;
}; };
/// <summary> /// <summary>
@ -232,6 +232,8 @@ static const char* EmberCLStructString =
" real_t m_C22;\n" " real_t m_C22;\n"
" real_t m_CenterX, m_CenterY;\n" " real_t m_CenterX, m_CenterY;\n"
" real_t m_RotA, m_RotB, m_RotD, m_RotE;\n" " real_t m_RotA, m_RotB, m_RotD, m_RotE;\n"
" real_t m_Psm1;\n"
" real_t m_Psm2;\n"
"} EmberCL;\n" "} EmberCL;\n"
"\n"; "\n";

View File

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

View File

@ -471,23 +471,24 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
{ {
os << os <<
" real_t colorIndexFrac;\n" " real_t colorIndexFrac;\n"
" real_t colorIndex = secondPoint.m_ColorX * COLORMAP_LENGTH_MINUS_1;\n" " real_t colorIndex = secondPoint.m_ColorX * ember->m_Psm1;\n"
" int intColorIndex = (int)colorIndex;\n" " int intColorIndex;\n"
" float4 palColor2;\n" " float4 palColor2;\n"
"\n" "\n"
" if (intColorIndex < 0)\n" " if (colorIndex < 0)\n"
" {\n" " {\n"
" intColorIndex = 0;\n" " intColorIndex = 0;\n"
" colorIndexFrac = 0;\n" " colorIndexFrac = 0;\n"
" }\n" " }\n"
" else if (intColorIndex >= COLORMAP_LENGTH_MINUS_1)\n" " else if (colorIndex >= ember->m_Psm1)\n"
" {\n" " {\n"
" intColorIndex = COLORMAP_LENGTH_MINUS_1 - 1;\n" " intColorIndex = (int)ember->m_Psm2;\n"
" colorIndexFrac = 1.0;\n" " colorIndexFrac = 1.0;\n"
" }\n" " }\n"
" else\n" " else\n"
" {\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"
"\n" "\n"
" iPaletteCoord.x = intColorIndex;\n"//Palette operations are strictly float because OpenCL does not support dp64 textures. " 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) else if (ember.m_PaletteMode == ePaletteMode::PALETTE_STEP)
{ {
os << 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"; " 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()")) if (m_Info->CheckCL(err, "cl::ImageGL()"))
{ {
m_GLImages.push_back(namedImageGL); m_GLImages.push_back(namedImageGL);
imageIndex = int(m_GLImages.size()) - 1;
if (data)
return WriteImage2D(m_GLImages.size() - 1, true, width, height, row_pitch, data);//OpenGL images/textures require a separate write.
else
return true;
} }
else
return false;
} }
else else
{ {
@ -407,8 +405,10 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
if (m_Info->CheckCL(err, "cl::Image2D()")) if (m_Info->CheckCL(err, "cl::Image2D()"))
{ {
m_Images.push_back(namedImage); 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. 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. 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()")) if (m_Info->CheckCL(err, "cl::ImageGL()"))
{
m_GLImages[imageIndex] = namedImageGL; m_GLImages[imageIndex] = namedImageGL;
}
else else
return false; 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 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); NamedImage2D namedImage(cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err), name);
if (m_Info->CheckCL(err, "cl::Image2D()")) if (m_Info->CheckCL(err, "cl::Image2D()"))
{
m_Images[imageIndex] = namedImage; 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; 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.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. 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; 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()); ErrorStr(loc, "Write palette buffer failed", device.get());
break; break;
@ -1787,6 +1787,8 @@ void RendererCL<T, bucketT>::ConvertEmber(Ember<T>& ember, EmberCL<T>& emberCL,
emberCL.m_RotB = m_RotMat.B(); emberCL.m_RotB = m_RotMat.B();
emberCL.m_RotD = m_RotMat.D(); emberCL.m_RotD = m_RotMat.D();
emberCL.m_RotE = m_RotMat.E(); 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++) 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 PaletteCellClicked(int row, int col) { }
virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) { } virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) { }
virtual void PaletteEditorButtonClicked() { } virtual void PaletteEditorButtonClicked() { }
virtual void PaletteEditorColorChanged() { }
QImage& FinalPaletteImage() { return m_FinalPaletteImage; } QImage& FinalPaletteImage() { return m_FinalPaletteImage; }
//Info. //Info.
@ -535,6 +536,7 @@ public:
virtual void PaletteCellClicked(int row, int col) override; virtual void PaletteCellClicked(int row, int col) override;
virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) override; virtual void SetBasePaletteAndAdjust(const Palette<float>& palette) override;
virtual void PaletteEditorButtonClicked() override; virtual void PaletteEditorButtonClicked() override;
virtual void PaletteEditorColorChanged() override;
//Info. //Info.
virtual void FillSummary() override; virtual void FillSummary() override;

View File

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

View File

@ -22,12 +22,12 @@ void Fractorium::InitParamsUI()
SetFixedTableHeader(ui.IterationTableHeader->horizontalHeader()); SetFixedTableHeader(ui.IterationTableHeader->horizontalHeader());
SetFixedTableHeader(ui.AnimationTableHeader->horizontalHeader()); SetFixedTableHeader(ui.AnimationTableHeader->horizontalHeader());
//Color. //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_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, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(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, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.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, 30, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 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_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); m_HighlightSpin->DoubleClickLowVal(-1.0);
int dec = 6; int dec = 6;
m_BrightnessSpin->setDecimals(dec); m_BrightnessSpin->setDecimals(dec);

View File

@ -229,7 +229,7 @@ void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int new
template <typename T> template <typename T>
QColor FractoriumEmberController<T>::ColorIndexToQColor(double d) 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.r *= 255;
entry.g *= 255; entry.g *= 255;
entry.b *= 255; entry.b *= 255;