--Bug fixes

-Fix crash on palette editor when opening it with certain palette files in a particular order.
 -An xform with only a post variation in it might have showed up wrong.
 -The xforms combo box was obscuring the name of the xforms by not being wide enough.
 -Make variation state preservation be a little bit more correct in OpenCL.

--Code changes
 -Make all iterators on the CPU use a temporary point.
This commit is contained in:
Person 2020-02-17 18:45:02 -08:00
parent 8cd2340484
commit 8c66fa2c24
18 changed files with 341 additions and 119 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ProductVersion="1.0.0.18" ?>
<?define ProductVersion="1.0.0.19" ?>
<?define ProductName="Fractorium $(var.ProductVersion) ($(var.GpuType))" ?>
<?define UpgradeCode="{4714cd15-bfba-44f6-8059-9e1466ebfa6e}"?>
<?define Manufacturer="Fractorium"?>
@ -13,7 +13,7 @@
<!--
Change this for every release.
-->
<?define ProductCode="{B0ECFACF-9166-4AE2-A1BF-FF0C8D87FFDF}"?>
<?define ProductCode="{8A8580F6-017D-455D-899E-C0FF91ECEB68}"?>
<Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)">
<Package

Binary file not shown.

View File

@ -49,8 +49,8 @@
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 18
PRODUCTVERSION 1, 0, 0, 18
FILEVERSION 1, 0, 0, 19
PRODUCTVERSION 1, 0, 0, 19
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -67,12 +67,12 @@
BEGIN
VALUE "CompanyName", "Open Source"
VALUE "FileDescription", "Renders fractal flames as animations with motion blur"
VALUE "FileVersion", "1, 0, 0, 18"
VALUE "FileVersion", "1, 0, 0, 19"
VALUE "InternalName", "EmberAnimate.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2019, GPL v3"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2020, GPL v3"
VALUE "OriginalFilename", "EmberAnimate.exe"
VALUE "ProductName", "Ember Animate"
VALUE "ProductVersion", "1, 0, 0, 18"
VALUE "ProductVersion", "1, 0, 0, 19"
END
END
BLOCK "VarFileInfo"

Binary file not shown.

View File

@ -49,8 +49,8 @@
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 18
PRODUCTVERSION 1, 0, 0, 18
FILEVERSION 1, 0, 0, 19
PRODUCTVERSION 1, 0, 0, 19
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -66,13 +66,13 @@
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Open Source"
VALUE "FileDescription", "Manipulates fractal flames parameter files"
VALUE "FileVersion", "1, 0, 0, 18"
VALUE "FileDescription", "Manipulates fractal flame parameter files"
VALUE "FileVersion", "1, 0, 0, 19"
VALUE "InternalName", "EmberGenome.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2019, GPL v3"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2020, GPL v3"
VALUE "OriginalFilename", "EmberGenome.exe"
VALUE "ProductName", "Ember Genome"
VALUE "ProductVersion", "1, 0, 0, 18"
VALUE "ProductVersion", "1, 0, 0, 19"
END
END
BLOCK "VarFileInfo"

View File

@ -49,8 +49,8 @@
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 18
PRODUCTVERSION 1, 0, 0, 18
FILEVERSION 1, 0, 0, 19
PRODUCTVERSION 1, 0, 0, 19
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -67,12 +67,12 @@
BEGIN
VALUE "CompanyName", "Open Source"
VALUE "FileDescription", "Renders fractal flames as single images"
VALUE "FileVersion", "1, 0, 0, 18"
VALUE "FileVersion", "1, 0, 0, 19"
VALUE "InternalName", "EmberRender.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2019, GPL v3"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2020, GPL v3"
VALUE "OriginalFilename", "EmberRender.exe"
VALUE "ProductName", "Ember Render"
VALUE "ProductVersion", "1, 0, 0, 18"
VALUE "ProductVersion", "1, 0, 0, 19"
END
END
BLOCK "VarFileInfo"

Binary file not shown.

View File

@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c)
namespace EmberNs
{
#define EMBER_VERSION "1.0.0.18"
#define EMBER_VERSION "1.0.0.19"
//#define FLAM3_COMPAT 1//Uncomment this if you want full compatibility with flam3 regarding some of the trig-based variations in Variations01.h
#define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.

View File

@ -300,7 +300,7 @@ public:
virtual size_t Iterate(Ember<T>& ember, const IterParams<T> params, const CarToRas<T>& ctr, Point<T>* samples, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
size_t i, badVals = 0;
Point<T> tempPoint, p1;
Point<T> tempPoint, p1, p2;
auto xforms = ember.NonConstXforms();
if (ember.ProjBits())//No xaos, 3D.
@ -311,8 +311,10 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
@ -320,10 +322,11 @@ public:
for (i = 1; i < params.m_Count; i++)//Real loop.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
ember.Proj(samples[i], rand, ctr);
}
}
@ -333,8 +336,10 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
samples[0] = p1;
@ -358,18 +363,21 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
for (i = 1; i < params.m_Count; i++)//Real loop.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
}
}
else//No xaos, no 3D, no final.
@ -378,8 +386,10 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
samples[0] = p1;
@ -468,7 +478,7 @@ public:
size_t i, xformIndex;
size_t lastXformUsed = 0;
size_t badVals = 0;
Point<T> tempPoint, p1;
Point<T> tempPoint, p1, p2;
auto xforms = ember.NonConstXforms();
if (ember.ProjBits())//Xaos, 3D.
@ -481,9 +491,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -494,10 +505,11 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
ember.Proj(samples[i], rand, ctr);
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -510,9 +522,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -523,10 +536,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
samples[i] = p1;
samples[i] = p1 = p2;
ember.Proj(samples[i], rand, ctr);
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -542,9 +555,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -554,10 +568,11 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
}
@ -569,9 +584,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}

View File

@ -603,24 +603,24 @@ public:
outPoint->m_Opacity = m_Opacity;
iterHelper.m_Color.x = outPoint->m_ColorX = m_ColorSpeedCache + (m_OneMinusColorCache * inPoint->m_ColorX);
//Compute the pre affine portion of the transform.
//These x, y values are what get passed to the variations below.
//Note that they are not changed after this, except in the case of pre_ variations.
if (m_HasPre)
{
iterHelper.m_TransX = (m_Affine.A() * inPoint->m_X) + (m_Affine.B() * inPoint->m_Y) + m_Affine.C();
iterHelper.m_TransY = (m_Affine.D() * inPoint->m_X) + (m_Affine.E() * inPoint->m_Y) + m_Affine.F();
}
else
{
iterHelper.m_TransX = inPoint->m_X;
iterHelper.m_TransY = inPoint->m_Y;
}
iterHelper.m_TransZ = inPoint->m_Z;
if (m_HasPreOrRegularVars)
{
//Compute the pre affine portion of the transform.
//These x, y values are what get passed to the variations below.
//Note that they are not changed after this, except in the case of pre_ variations.
if (m_HasPre)
{
iterHelper.m_TransX = (m_Affine.A() * inPoint->m_X) + (m_Affine.B() * inPoint->m_Y) + m_Affine.C();
iterHelper.m_TransY = (m_Affine.D() * inPoint->m_X) + (m_Affine.E() * inPoint->m_Y) + m_Affine.F();
}
else
{
iterHelper.m_TransX = inPoint->m_X;
iterHelper.m_TransY = inPoint->m_Y;
}
iterHelper.m_TransZ = inPoint->m_Z;
//Apply pre_ variations, these don't affect outPoint, only iterHelper.m_TransX, Y, Z.
for (i = 0; i < PreVariationCount(); i++)
{

View File

@ -25,10 +25,87 @@ template <typename T> const string& IterOpenCLKernelCreator<T>::ZeroizeEntryPoin
template <typename T> const string& IterOpenCLKernelCreator<T>::SumHistKernel() const { return m_SumHistKernel; }
template <typename T> const string& IterOpenCLKernelCreator<T>::SumHistEntryPoint() const { return m_SumHistEntryPoint; }
template <typename T> const string& IterOpenCLKernelCreator<T>::IterEntryPoint() const { return m_IterEntryPoint; }
/// <summary>
/// Create the iteration kernel string using the Cuburn method.
/// Template argument expected to be float or double.
/// Pre Reg Post Formula
/// x trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// outpoint = trans
///
/// x x trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
//
/// x x x
/// trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// x x
/// trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// outpoint = trans
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// x
/// trans = affine(inpoint)
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
///
/// x x
/// trans = affine(inpoint)
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// x
/// trans = affine(inpoint)
/// outpoint = 0
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// none trans = affine(inpoint)
/// outpoint = 0
///
/// </summary>
/// <param name="ember">The ember to create the kernel string for</param>
/// <param name="params">The parametric variation #define string</param>
@ -59,6 +136,7 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
bool needPrecalcAngles = false;
bool needPrecalcAtanXY = false;
bool needPrecalcAtanYX = false;
bool hasPreReg = (xform->PreVariationCount() + xform->VariationCount()) > 0;
v = varIndex = varCount = 0;
xformFuncs <<
"void Xform" << i << "(__constant XformCL* xform, __constant real_t* parVars, __global real_t* globalShared, Point* inPoint, Point* outPoint, uint2* mwc, VariationState* varState)\n" <<
@ -96,32 +174,24 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
xformFuncs << "\treal_t tempColor = outPoint->m_ColorX = fma(xform->m_OneMinusColorCache, inPoint->m_ColorX, xform->m_ColorSpeedCache);\n\n";
if (xform->PreVariationCount() + xform->VariationCount() == 0)
if (optAffine && xform->m_Affine.IsID())
{
xformFuncs <<
" outPoint->m_X = 0;\n"
" outPoint->m_Y = 0;\n"
" outPoint->m_Z = 0;\n";
" transX = inPoint->m_X;\n" <<
" transY = inPoint->m_Y;\n";
}
else
{
if (optAffine && xform->m_Affine.IsID())
{
xformFuncs <<
" transX = inPoint->m_X;\n" <<
" transY = inPoint->m_Y;\n";
}
else
{
xformFuncs <<
" transX = fma(xform->m_A, inPoint->m_X, fma(xform->m_B, inPoint->m_Y, xform->m_C));\n" <<
" transY = fma(xform->m_D, inPoint->m_X, fma(xform->m_E, inPoint->m_Y, xform->m_F));\n";
}
xformFuncs <<
" transZ = inPoint->m_Z;\n";
varCount = xform->PreVariationCount();
" transX = fma(xform->m_A, inPoint->m_X, fma(xform->m_B, inPoint->m_Y, xform->m_C));\n" <<
" transY = fma(xform->m_D, inPoint->m_X, fma(xform->m_E, inPoint->m_Y, xform->m_F));\n";
}
xformFuncs << " transZ = inPoint->m_Z;\n";
varCount = xform->PreVariationCount();
if (hasPreReg)
{
if (varCount > 0)
{
xformFuncs << "\n\t//Apply each of the " << varCount << " pre variations in this xform.\n";
@ -186,6 +256,13 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
" outPoint->m_Z = transZ;\n";
}
}
else
{
xformFuncs <<
" outPoint->m_X = 0;\n"
" outPoint->m_Y = 0;\n"
" outPoint->m_Z = 0;\n";
}
if (xform->PostVariationCount() > 0)
{
@ -450,13 +527,6 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
"\n"
//Write to another thread's location.
" swap[sw] = secondPoint;\n";
if (hasVarState)
{
os <<
" varStates[blockStartIndex + sw] = varState;\n";
}
os <<
"\n"
//Populate randomized xform index buffer with new random values.
@ -466,13 +536,6 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
" barrier(CLK_LOCAL_MEM_FENCE);\n"
//Another thread will have written to this thread's location, so read the new value and use it for accumulation below.
" firstPoint = swap[threadIndex];\n";
if (hasVarState)
{
os <<
" varState = varStates[blockStartThreadIndex];\n"
;
}
}
else
{
@ -488,14 +551,40 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
os <<
"\n"
" if (fuse)\n"
" {\n"
" {\n";
if (hasVarState && ember.XformCount() > 1)
{
os <<
" varStates[blockStartIndex + sw] = varState;\n\n";
}
os <<
" if (i >= fuseCount - 1)\n"
" {\n"
" i = 0;\n"
" fuse = false;\n"
" itersToDo = iterCount;\n"
" barrier(CLK_LOCAL_MEM_FENCE);\n"//Sort of seems necessary, sort of doesn't. Makes no speed difference.
" itersToDo = iterCount;\n";
if (ember.XformCount() > 1)
os <<
" barrier(CLK_LOCAL_MEM_FENCE);\n"//Sort of seems necessary, sort of doesn't. Makes no speed difference.
;
os <<
" }\n"
;
if (hasVarState && ember.XformCount() > 1)
{
os <<
"\n"
" barrier(CLK_GLOBAL_MEM_FENCE);\n"//Sort of seems necessary, sort of doesn't. Makes no speed difference.
" varState = varStates[blockStartThreadIndex];"
;
}
os <<
"\n"
" continue;\n"
" }\n"
@ -516,6 +605,13 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
"\n";
}
if (hasVarState && ember.XformCount() > 1)
{
os <<
" varStates[blockStartIndex + sw] = varState;\n"
;
}
os << CreateProjectionString(ember);
if (doAccum)
@ -591,8 +687,16 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
os <<
" }\n"//histIndex < histSize.
" }\n"//CarToRasInBounds.
"\n"
"\n";
os <<
" barrier(CLK_GLOBAL_MEM_FENCE);\n";//Barrier every time, whether or not the point was in bounds, else artifacts will occur when doing strips.
if (hasVarState && ember.XformCount() > 1)
{
os <<
" varState = varStates[blockStartThreadIndex];\n"
;
}
}
os <<
@ -610,7 +714,7 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
" seeds[pointsIndex] = mwc;\n"
" points[blockStartThreadIndex] = firstPoint;\n";
if (hasVarState)
if (hasVarState && ember.XformCount() == 1)
{
os <<
" varStates[blockStartThreadIndex] = varState;\n";

View File

@ -58,7 +58,7 @@
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Fractorium 1.0.0.18&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Fractorium 1.0.0.19&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>

View File

@ -139,6 +139,7 @@ public slots:
void OnActionNewEmptyFlameInCurrentFile(bool checked);
void OnActionNewRandomFlameInCurrentFile(bool checked);
void OnActionCopyFlameInCurrentFile(bool checked);
void OnActionCreateReferenceFile(bool checked);
void OnActionOpen(bool checked);
void OnActionSaveCurrentAsXml(bool checked);
void OnActionSaveEntireFileAsXml(bool checked);

View File

@ -96,6 +96,7 @@ public:
virtual void NewEmptyFlameInCurrentFile() { }
virtual void NewRandomFlameInCurrentFile() { }
virtual void CopyFlameInCurrentFile() { }
virtual void CreateReferenceFile() { }
virtual void OpenAndPrepFiles(const QStringList& filenames, bool append) { }
virtual void SaveCurrentAsXml() { }
virtual void SaveEntireFileAsXml() { }
@ -384,6 +385,7 @@ public:
virtual void NewEmptyFlameInCurrentFile() override;
virtual void NewRandomFlameInCurrentFile() override;
virtual void CopyFlameInCurrentFile() override;
virtual void CreateReferenceFile() override;
virtual void OpenAndPrepFiles(const QStringList& filenames, bool append) override;
virtual void SaveCurrentAsXml() override;
virtual void SaveEntireFileAsXml() override;

View File

@ -11,6 +11,7 @@ void Fractorium::InitMenusUI()
connect(ui.ActionNewEmptyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewEmptyFlameInCurrentFile(bool)), Qt::QueuedConnection);
connect(ui.ActionNewRandomFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewRandomFlameInCurrentFile(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyFlameInCurrentFile(bool)), Qt::QueuedConnection);
connect(ui.ActionCreateReferenceFile, SIGNAL(triggered(bool)), this, SLOT(OnActionCreateReferenceFile(bool)), Qt::QueuedConnection);
connect(ui.ActionOpen, SIGNAL(triggered(bool)), this, SLOT(OnActionOpen(bool)), Qt::QueuedConnection);
connect(ui.ActionSaveCurrentAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentAsXml(bool)), Qt::QueuedConnection);
connect(ui.ActionSaveEntireFileAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveEntireFileAsXml(bool)), Qt::QueuedConnection);
@ -172,6 +173,104 @@ void FractoriumEmberController<T>::CopyFlameInCurrentFile()
void Fractorium::OnActionCopyFlameInCurrentFile(bool checked) { m_Controller->CopyFlameInCurrentFile(); }
/// <summary>
/// Create a reference file containing one ember for every possible regular variation, plus one for post_smartcrop
/// since it only exists in post form.
/// This will replace whatever file the user has open.
/// Clears the undo state.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::CreateReferenceFile()
{
bool nv = false;
size_t i;
StopAllPreviewRenderers();
auto temppal = m_Ember.m_Palette;
m_EmberFile.Clear();
m_EmberFile.m_Filename = QString("Reference_") + EMBER_VERSION;
auto varList = VariationList<T>::Instance();
auto& regVars = varList->RegVars();
auto count = regVars.size();
auto addsquaresfunc = [&](size_t i, const Variation<T>* var)
{
Ember<T> ember;
Xform<T> xf0, xf1, xf2, xf3, xf4, xffinal;
//
xf0.AddVariation(varList->GetVariationCopy(eVariationId::VAR_SQUARE, T(0.5)));
//
xf1.m_Affine.C(T(0.5));
xf1.m_Affine.F(T(0.5));
xf1.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf2.m_Affine.C(T(-0.5));
xf2.m_Affine.F(T(0.5));
xf2.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf3.m_Affine.C(T(-0.5));
xf3.m_Affine.F(T(-0.5));
xf3.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf4.m_Affine.C(T(0.5));
xf4.m_Affine.F(T(-0.5));
xf4.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xffinal.AddVariation(var->Copy());
//
ember.AddXform(xf0);
ember.AddXform(xf1);
ember.AddXform(xf2);
ember.AddXform(xf3);
ember.AddXform(xf4);
ember.SetFinalXform(xffinal);
ember.EqualizeWeights();
ember.m_Index = i;
ember.m_MaxRadDE = 0;
ember.m_Name = var->Name() + " Squares";
ember.m_Palette = temppal;
m_EmberFile.m_Embers.push_back(ember);
};
auto addsbarsfunc = [&](size_t i, const Variation<T>* var)
{
Ember<T> ember;
Xform<T> xf0, xf1, xf2, xffinal;
//
xf0.AddVariation(varList->GetVariationCopy(eVariationId::VAR_PRE_BLUR, T(100)));
xf0.AddVariation(varList->GetVariationCopy(eVariationId::VAR_CYLINDER, T(0.1)));
//
xf1.m_Affine.C(T(0.4));
xf1.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf2.m_Affine.C(T(-0.4));
xf2.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xffinal.AddVariation(var->Copy());
ember.AddXform(xf0);
ember.AddXform(xf1);
ember.AddXform(xf2);
ember.SetFinalXform(xffinal);
ember.EqualizeWeights();
ember.m_Index = i;
ember.m_MaxRadDE = 0;
ember.m_Name = var->Name() + " Bars";
ember.m_Palette = temppal;
m_EmberFile.m_Embers.push_back(ember);
};
for (i = 0; i < count; i++)
{
addsquaresfunc(i, regVars[i]);
addsbarsfunc(i, regVars[i]);
}
addsquaresfunc(i, varList->GetVariation(eVariationId::VAR_POST_SMARTCROP));//post_smartcrop is the only variation that exists only in post form, so it must be done manually here.
addsbarsfunc(i, varList->GetVariation(eVariationId::VAR_POST_SMARTCROP));
m_LastSaveAll = "";
FillLibraryTree();
}
void Fractorium::OnActionCreateReferenceFile(bool checked) { m_Controller->CreateReferenceFile(); }
/// <summary>
/// Open a list of ember Xml files, apply various values from the GUI widgets.
/// Either append these newly read embers to the existing open embers,

View File

@ -36,7 +36,7 @@ void Fractorium::InitXformsUI()
ui.CurrentXformCombo->view()->setMinimumWidth(100);
ui.CurrentXformCombo->view()->setMaximumWidth(500);
//ui.CurrentXformCombo->view()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
ui.CurrentXformCombo->view()->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow);
ui.CurrentXformCombo->view()->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents);
#ifndef _WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
ui.AddXformButton->setIconSize(QSize(16, 16));
@ -518,9 +518,9 @@ void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)
if (auto item = m_Fractorium->ui.XformWeightNameTable->item(0, 1))
{
m_Fractorium->ui.XformWeightNameTable->blockSignals(true);
item->setText(QString::fromStdString(xform->m_Name));
m_Fractorium->ui.XformWeightNameTable->blockSignals(false);
m_Fractorium->m_XformNameEdit->blockSignals(true);
m_Fractorium->m_XformNameEdit->setText(QString::fromStdString(xform->m_Name));
m_Fractorium->m_XformNameEdit->blockSignals(false);
}
FillVariationTreeWithXform(xform);
@ -656,7 +656,7 @@ void FractoriumEmberController<T>::UpdateXformName(int index)
auto view = m_Fractorium->ui.CurrentXformCombo->view();
auto fontMetrics1 = view->fontMetrics();
auto textWidth = m_Fractorium->ui.CurrentXformCombo->width();
auto ww = fontMetrics1.width("WW");
auto ww = fontMetrics1.width("WW") * 3;
for (int i = 0; i < m_Fractorium->ui.CurrentXformCombo->count(); ++i)
textWidth = std::max(fontMetrics1.width(m_Fractorium->ui.CurrentXformCombo->itemText(i)) + ww, textWidth);

View File

@ -433,7 +433,7 @@ void PaletteEditor::OnCopyPaletteFileButtonClicked()
/// </summary>
void PaletteEditor::OnAppendPaletteButtonClicked()
{
auto& pal = m_GradientColorView->GetPalette(256);
auto& pal = GetPalette(256);
m_PaletteList->AddPaletteToFile(m_CurrentPaletteFilePath, pal);
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
m_PaletteIndex = ui->PaletteListTable->rowCount() - 1;
@ -446,7 +446,7 @@ void PaletteEditor::OnAppendPaletteButtonClicked()
/// </summary>
void PaletteEditor::OnOverwritePaletteButtonClicked()
{
auto& pal = m_GradientColorView->GetPalette(256);
auto& pal = GetPalette(256);
m_PaletteList->Replace(m_CurrentPaletteFilePath, pal, m_PaletteIndex);
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
emit PaletteFileChanged();
@ -632,10 +632,11 @@ void PaletteEditor::EnablePaletteFileControls()
void PaletteEditor::EnablePaletteControls()
{
bool b = IsCurrentPaletteAndFileEditable();//Both the file and the current palette must be editable.
auto& pal = GetPalette(256);
ui->DeletePaletteButton->setEnabled(b);
ui->CopyPaletteFileButton->setEnabled(b);
ui->AppendPaletteButton->setEnabled(b);
ui->OverwritePaletteButton->setEnabled(b && (GetFilename(m_CurrentPaletteFilePath) == GetFilename(*m_GradientColorView->GetPalette(256).m_Filename.get())));//Only allow overwrite if the palette is from the file it's overwriting a palette in.
ui->OverwritePaletteButton->setEnabled(b && pal.m_Filename.get() && (GetFilename(m_CurrentPaletteFilePath) == GetFilename(*pal.m_Filename.get())));//Only allow overwrite if the palette is from the file it's overwriting a palette in.
ui->AddColorButton->setEnabled(b);
ui->DistributeColorsButton->setEnabled(b);
ui->AutoDistributeCheckBox->setEnabled(b);
@ -654,5 +655,5 @@ void PaletteEditor::EnablePaletteControls()
/// <returns>True if both the currently selected palette is editable and if all palettes in the currently selected file are editable.</returns>
bool PaletteEditor::IsCurrentPaletteAndFileEditable()
{
return m_PaletteList->IsModifiable(m_CurrentPaletteFilePath) && !m_GradientColorView->GetPalette(256).m_SourceColors.empty();
return m_PaletteList->IsModifiable(m_CurrentPaletteFilePath) && !GetPalette(256).m_SourceColors.empty();
}

View File

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -10,7 +9,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("apoconv")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]