fractorium/Source/EmberGenome/EmberGenome.cpp
mfeemster 7715910362 --User changes
-The concept of "saving back to file in memory" has been removed. The current ember is saved back to memory whenever the render completes and the thumbnail will be updated each time.
 -Command line programs now default to using double precision.
 -The --bits argument has been removed and replaced with --sp to specify single precision. If omitted, DP is used.
 -Remove the --seed option, it was never used.
 -Remove the --sub_batch_size option, it has been part of the Xml for a long time.
 -Remove --hex_palette argument for EmberRender, it only makes sense in EmberAnimate and EmberGenome.
 -Set enable_jpg_comments and enable_png_comments to false by default. It was a very bad idea to have them be true because it reveals the flame parameters used to render the image which many artists guard closely.

--Bug fixes
 -Continuous update was broken.
 -Undo list was broken with new Library tab design.
 -Force repaint on xform checkbox change to ensure circles are immediately drawn around selected xforms.

--Code changes
 -Remove save to back to file in memory icon, document-hf-insert.png.
2016-04-11 18:15:14 -07:00

826 lines
24 KiB
C++

#include "EmberCommonPch.h"
#include "EmberGenome.h"
#include "JpegUtils.h"
/// <summary>
/// Set various default test values on the passed in ember.
/// </summary>
/// <param name="ember">The ember to test</param>
template <typename T>
void SetDefaultTestValues(Ember<T>& ember)
{
ember.m_Time = 0.0;
ember.m_Interp = eInterp::EMBER_INTERP_LINEAR;
ember.m_PaletteInterp = ePaletteInterp::INTERP_HSV;
ember.m_Background[0] = 0;
ember.m_Background[1] = 0;
ember.m_Background[2] = 0;
ember.m_Background[3] = 255;
ember.m_CenterX = 0;
ember.m_CenterY = 0;
ember.m_Rotate = 0;
ember.m_PixelsPerUnit = 64;
ember.m_FinalRasW = 128;
ember.m_FinalRasH = 128;
ember.m_Supersample = 1;
ember.m_SpatialFilterRadius = T(0.5);
ember.m_SpatialFilterType = eSpatialFilterType::GAUSSIAN_SPATIAL_FILTER;
ember.m_Zoom = 0;
ember.m_Quality = 1;
ember.m_TemporalSamples = 1;
ember.m_MaxRadDE = 0;
ember.m_MinRadDE = 0;
ember.m_CurveDE = T(0.6);
}
template <typename T>
void FormatName(Ember<T>& result, ostringstream& os, streamsize padding)
{
os << std::setw(padding) << result.m_Time;
result.m_Name = os.str();
os.str("");
}
/// <summary>
/// The core of the EmberGenome.exe program.
/// Template argument expected to be float or double.
/// </summary>
/// <param name="opt">A populated EmberOptions object which specifies all program options to be used</param>
/// <returns>True if success, else false.</returns>
template <typename T>
bool EmberGenome(EmberOptions& opt)
{
auto info = OpenCLInfo::Instance();
std::cout.imbue(std::locale(""));
if (opt.DumpArgs())
cerr << opt.GetValues(eOptionUse::OPT_USE_GENOME) << "\n";
if (opt.OpenCLInfo())
{
cerr << "\nOpenCL Info: \n";
cerr << info->DumpInfo();
return true;
}
VariationList<T>& varList(VariationList<T>::Instance());
if (opt.AllVars() || opt.RegVars() || opt.PreVars() || opt.PostVars())
{
if (opt.AllVars())
{
auto& vars = varList.AllVars();
for (auto& v : vars)
cout << v->Name() << "\n";
return true;
}
else
{
vector<Variation<T>*> vars;
if (opt.RegVars())
vars.insert(vars.end(), varList.RegVars().begin(), varList.RegVars().end());
if (opt.PreVars())
vars.insert(vars.end(), varList.PreVars().begin(), varList.PreVars().end());
if (opt.PostVars())
vars.insert(vars.end(), varList.PostVars().begin(), varList.PostVars().end());
for (auto& v : vars)
cout << v->Name() << "\n";
}
return true;
}
//Regular variables.
Timing t;
bool exactTimeMatch, randomMode, didColor, seqFlag;
size_t i, j, i0, i1, rep, val, frame, frameCount, count = 0;
size_t ftime, firstFrame, lastFrame;
size_t n, tot, totb, totw;
T avgPix, fractionBlack, fractionWhite, blend, spread, mix0, mix1;
string token, filename;
ostringstream os, os2;
vector<Ember<T>> embers, embers2, templateEmbers;
vector<eVariationId> vars, noVars;
vector<byte> finalImage;
eCrossMode crossMeth;
eMutateMode mutMeth;
Ember<T> orig, save, selp0, selp1, parent0, parent1;
Ember<T> result, result1, result2, result3, interpolated;
Ember<T>* aselp0, *aselp1, *pTemplate = nullptr;
XmlToEmber<T> parser;
EmberToXml<T> emberToXml;
EmberReport emberReport, emberReport2;
const vector<pair<size_t, size_t>> devices = Devices(opt.Devices());
auto progress = make_unique<RenderProgress<T>>();
unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, devices, false, 0, emberReport));
QTIsaac<ISAAC_SIZE, ISAAC_INT> rand(ISAAC_INT(t.Tic()), ISAAC_INT(t.Tic() * 2), ISAAC_INT(t.Tic() * 3));
vector<string> errorReport = emberReport.ErrorReport();
os.imbue(std::locale(""));
os2.imbue(std::locale(""));
if (!errorReport.empty())
cerr << emberReport.ErrorReportString();
if (!renderer.get())
{
cerr << "Renderer creation failed, exiting.\n";
return false;
}
if (!InitPaletteList<T>(opt.PalettePath()))
return false;
if (!opt.EmberCL())
{
if (opt.ThreadCount() != 0)
renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
}
else
{
cerr << "Using OpenCL to render.\n";
if (opt.Verbose())
{
for (auto& device : devices)
{
cerr << "Platform: " << info->PlatformName(device.first) << "\n";
cerr << "Device: " << info->DeviceName(device.first, device.second) << "\n";
}
}
}
//SheepTools will own the created renderer and will take care of cleaning it up.
SheepTools<T, float> tools(opt.PalettePath(), CreateRenderer<T>(opt.EmberCL() ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, devices, false, 0, emberReport2));
tools.SetSpinParams(!opt.UnsmoothEdge(),
T(opt.Stagger()),
T(opt.OffsetX()),
T(opt.OffsetY()),
opt.Nick(),
opt.Url(),
opt.Id(),
opt.Comment(),
opt.SheepGen(),
opt.SheepId());
if (opt.UseVars() != "" && opt.DontUseVars() != "")
{
cerr << "use_vars and dont_use_vars cannot both be specified. Returning without executing.\n";
return false;
}
//Specify reasonable defaults if nothing is specified.
if (opt.UseVars() == "" && opt.DontUseVars() == "")
{
noVars.push_back(eVariationId::VAR_NOISE);
noVars.push_back(eVariationId::VAR_BLUR);
noVars.push_back(eVariationId::VAR_GAUSSIAN_BLUR);
noVars.push_back(eVariationId::VAR_RADIAL_BLUR);
noVars.push_back(eVariationId::VAR_NGON);
noVars.push_back(eVariationId::VAR_SQUARE);
noVars.push_back(eVariationId::VAR_RAYS);
noVars.push_back(eVariationId::VAR_CROSS);
noVars.push_back(eVariationId::VAR_PRE_BLUR);
noVars.push_back(eVariationId::VAR_SEPARATION);
noVars.push_back(eVariationId::VAR_SPLIT);
noVars.push_back(eVariationId::VAR_SPLITS);
//Loop over the novars and set ivars to the complement.
for (i = 0; i < varList.Size(); i++)
{
for (j = 0; j < noVars.size(); j++)
{
if (noVars[j] == varList.GetVariation(i)->VariationId())
break;
}
if (j == noVars.size())
vars.push_back(varList.GetVariation(i)->VariationId());
}
}
else
{
if (opt.UseVars() != "")//Parse comma-separated list of variations to use.
{
istringstream iss(opt.UseVars());
while (std::getline(iss, token, ','))
{
if (parser.Aton(token.c_str(), val))
{
if (val < varList.Size())
vars.push_back(static_cast<eVariationId>(val));
}
}
}
else if (opt.DontUseVars() != "")
{
istringstream iss(opt.DontUseVars());
while (std::getline(iss, token, ','))
{
if (parser.Aton(token.c_str(), val))
{
if (val < varList.Size())
noVars.push_back(static_cast<eVariationId>(val));
}
}
//Loop over the novars and set ivars to the complement.
for (i = 0; i < varList.Size(); i++)
{
for (j = 0; j < noVars.size(); j++)
{
if (noVars[j] == varList.GetVariation(i)->VariationId())
break;
}
if (j == noVars.size())
vars.push_back(varList.GetVariation(i)->VariationId());
}
}
}
bool doMutate = opt.Mutate() != "";
bool doInter = opt.Inter() != "";
bool doRotate = opt.Rotate() != "";
bool doClone = opt.Clone() != "";
bool doStrip = opt.Strip() != "";
bool doCross0 = opt.Cross0() != "";
bool doCross1 = opt.Cross1() != "";
count += (doMutate ? 1 : 0);
count += (doInter ? 1 : 0);
count += (doRotate ? 1 : 0);
count += (doClone ? 1 : 0);
count += (doStrip ? 1 : 0);
count += ((doCross0 || doCross1) ? 1 : 0);
if (count > 1)
{
cerr << "Can only specify one of mutate, clone, cross, rotate, strip, or inter. Returning without executing.\n";
return false;
}
if (doCross0 != doCross1)//Must both be either true or false.
{
cerr << "Must specify both crossover arguments. Returning without executing.\n";
return false;
}
if (opt.Method() != "" && (!doCross0 && !doMutate))
{
cerr << "Cannot specify method unless doing crossover or mutate. Returning without executing.\n";
return false;
}
if (opt.TemplateFile() != "")
{
if (!ParseEmberFile(parser, opt.TemplateFile(), templateEmbers, false))//Do not use defaults here to ensure only present fields get used when applying the template.
return false;
if (templateEmbers.size() > 1)
cerr << "More than one control point in template, ignoring all but first.\n";
pTemplate = &templateEmbers[0];
}
//Methods for genetic manipulation begin here.
if (doMutate) filename = opt.Mutate();
else if (doInter) filename = opt.Inter();
else if (doRotate) filename = opt.Rotate();
else if (doClone) filename = opt.Clone();
else if (doStrip) filename = opt.Strip();
else if (doCross0) filename = opt.Cross0();
else if (opt.CloneAll() != "") filename = opt.CloneAll();
else if (opt.Animate() != "") filename = opt.Animate();
else if (opt.Sequence() != "") filename = opt.Sequence();
else if (opt.Inter() != "") filename = opt.Inter();
else if (opt.Rotate() != "") filename = opt.Rotate();
else if (opt.Strip() != "") filename = opt.Strip();
else if (opt.Clone() != "") filename = opt.Clone();
else if (opt.Mutate() != "") filename = opt.Mutate();
if (!ParseEmberFile(parser, filename, embers))
return false;
if (doCross1)
{
if (!ParseEmberFile(parser, opt.Cross1(), embers2))
return false;
}
if (opt.CloneAll() != "")
{
cout << "<clone_all version=\"Ember-" << EmberVersion() << "\">\n";
for (i = 0; i < embers.size(); i++)
{
if (pTemplate)
tools.ApplyTemplate(embers[i], *pTemplate);
tools.Offset(embers[i], T(opt.OffsetX()), T(opt.OffsetY()));
cout << emberToXml.ToString(embers[i], opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
cout << "</clone_all>\n";
return true;
}
if (opt.Animate() != "")
{
for (i = 0; i < embers.size(); i++)
{
if (i > 0 && embers[i].m_Time <= embers[i - 1].m_Time)
{
cerr << "Error: control points must be sorted by time, but " << embers[i].m_Time << " <= " << embers[i - 1].m_Time << ", index " << i << ".\n";
return false;
}
embers[i].DeleteMotionElements();
}
firstFrame = size_t(opt.FirstFrame() == UINT_MAX ? embers[0].m_Time : opt.FirstFrame());
lastFrame = size_t(opt.LastFrame() == UINT_MAX ? embers.back().m_Time : opt.LastFrame());
if (lastFrame < firstFrame)
lastFrame = firstFrame;
cout << "<animate version=\"EMBER-" << EmberVersion() << "\">\n";
for (ftime = firstFrame; ftime <= lastFrame; ftime++)
{
exactTimeMatch = false;
for (i = 0; i < embers.size(); i++)
{
if (ftime == size_t(embers[i].m_Time))
{
interpolated = embers[i];
exactTimeMatch = true;
break;
}
}
if (!exactTimeMatch)
{
Interpolater<T>::Interpolate(embers, T(ftime), T(opt.Stagger()), interpolated);
for (i = 0; i < embers.size(); i++)
{
if (ftime == size_t(embers[i].m_Time - 1))
{
exactTimeMatch = true;
break;
}
}
if (!exactTimeMatch)
interpolated.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LINEAR;
}
if (pTemplate)
tools.ApplyTemplate(interpolated, *pTemplate);
cout << emberToXml.ToString(interpolated, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
cout << "</animate>\n";
return true;
}
if (opt.Sequence() != "")
{
frame = std::max(opt.Frame(), opt.Time());
if (opt.Frames() == 0)
{
cerr << "nframes must be positive and non-zero, not " << opt.Frames() << ".\n";
return false;
}
if (opt.Enclosed())
cout << "<sequence version=\"EMBER-" << EmberVersion() << "\">\n";
spread = 1 / T(opt.Frames());
frameCount = 0;
os.str("");
os << setfill('0');
auto padding = streamsize(std::log10(((opt.Frames() * opt.Loops()) + opt.Frames()) * embers.size())) + 1;
for (i = 0; i < embers.size(); i++)
{
if (opt.Loops() > 0)
{
for (frame = 0; frame < std::round(opt.Frames() * opt.Loops()); frame++)
{
blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
//The loop above will have rotated just shy of a complete rotation.
//Rotate the next step and save in result, but do not print.
//result will be the starting point for the interp phase below.
frame = size_t(std::round(opt.Frames() * opt.Loops()));
blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount, blend);//Do not increment frameCount here.
FormatName(result, os, padding);
}
if (i < embers.size() - 1)
{
if (opt.Loops() > 0)//Store the last result as the flame to interpolate from. This applies for whole or fractional values of opt.Loops().
embers[i] = result;
for (frame = 0; frame < opt.Frames(); frame++)
{
seqFlag = (frame == 0 || frame == opt.Frames() - 1);
blend = frame / T(opt.Frames());
result.Clear();
tools.SpinInter(&embers[i], pTemplate, result, frameCount++, seqFlag, blend);
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
}
}
result = embers.back();
tools.Spin(embers.back(), pTemplate, result, frameCount, 0);
FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
if (opt.Enclosed())
cout << "</sequence>\n";
return true;
}
if (doInter || doRotate)
{
frame = std::max(opt.Frame(), opt.Time());
if (opt.Frames() == 0)
{
cerr << "nframes must be positive and non-zero, not " << opt.Frames() << ".\n";
return false;
}
blend = frame / T(opt.Frames());
spread = 1 / T(opt.Frames());
if (opt.Enclosed())
cout << "<pick version=\"EMBER-" << EmberVersion() << "\">\n";
if (doRotate)
{
if (embers.size() != 1)
{
cerr << "rotation requires one control point, not " << embers.size() << ".\n";
return false;
}
tools.Spin(embers[0], pTemplate, result1, frame - 1, blend - spread);
tools.Spin(embers[0], pTemplate, result2, frame , blend );
tools.Spin(embers[0], pTemplate, result3, frame + 1, blend + spread);
cout << emberToXml.ToString(result1, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
cout << emberToXml.ToString(result2, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
cout << emberToXml.ToString(result3, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
else
{
if (embers.size() != 2)
{
cerr << "interpolation requires two control points, not " << embers.size() << ".\n";
return false;
}
tools.SpinInter(embers.data(), pTemplate, result1, frame - 1, 0, blend - spread);
tools.SpinInter(embers.data(), pTemplate, result2, frame , 0, blend );
tools.SpinInter(embers.data(), pTemplate, result3, frame + 1, 0, blend + spread);
cout << emberToXml.ToString(result1, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
cout << emberToXml.ToString(result2, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
cout << emberToXml.ToString(result3, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
if (opt.Enclosed())
cout << "</pick>\n";
return true;
}
if (doStrip)
{
if (opt.Enclosed())
cout << "<pick version=\"EMBER-" << EmberVersion() << "\">\n";
for (i = 0; i < embers.size(); i++)
{
T oldX, oldY;
embers[i].DeleteMotionElements();
oldX = embers[i].m_CenterX;
oldY = embers[i].m_CenterY;
embers[i].m_FinalRasH = size_t(T(embers[i].m_FinalRasH) / T(opt.Frames()));
embers[i].m_CenterY = embers[i].m_CenterY - ((opt.Frames() - 1) * embers[i].m_FinalRasH) /
(2 * embers[i].m_PixelsPerUnit * pow(T(2.0), embers[i].m_Zoom));
embers[i].m_CenterY += embers[i].m_FinalRasH * opt.Frame() / (embers[i].m_PixelsPerUnit * pow(T(2.0), embers[i].m_Zoom));
tools.RotateOldCenterBy(embers[i].m_CenterX, embers[i].m_CenterY, oldX, oldY, embers[i].m_Rotate);
if (pTemplate)
tools.ApplyTemplate(embers[i], *pTemplate);
tools.Offset(embers[i], T(opt.OffsetX()), T(opt.OffsetY()));
cout << emberToXml.ToString(embers[i], opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
}
if (opt.Enclosed())
cout << "</pick>\n";
return true;
}
//Repeat.
renderer->EarlyClip(opt.EarlyClip());
renderer->YAxisUp(opt.YAxisUp());
renderer->LockAccum(opt.LockAccum());
renderer->PixelAspectRatio(T(opt.AspectRatio()));
renderer->Transparency(opt.Transparency());
if (opt.Repeat() == 0)
{
cerr << "Repeat must be positive, not " << opt.Repeat() << "\n";
return false;
}
if (opt.Enclosed())
cout << "<pick version=\"EMBER-" << EmberVersion() << "\">\n";
for (rep = 0; rep < opt.Repeat(); rep++)
{
count = 0;
os.str("");
save.Clear();
VerbosePrint("Flame = " << rep + 1 << "/" << opt.Repeat() << "..");
if (opt.Clone() != "")
{
os << "clone";//Action is 'clone' with trunc vars concat.
if (opt.CloneAction() != "")
os << " " << opt.CloneAction();
selp0 = embers[rand.Rand() % embers.size()];
save = selp0;
aselp0 = &selp0;
aselp1 = nullptr;
os << tools.TruncateVariations(save, 5);
save.m_Edits = emberToXml.CreateNewEditdoc(aselp0, aselp1, os.str(), opt.Nick(), opt.Url(), opt.Id(), opt.Comment(), opt.SheepGen(), opt.SheepId());
}
else
{
do
{
randomMode = false;
didColor = false;
os.str("");
VerbosePrint(".");
if (doMutate)
{
selp0 = embers[rand.Rand() % embers.size()];
orig = selp0;
aselp0 = &selp0;
aselp1 = nullptr;
if (opt.Method() == "")
mutMeth = eMutateMode::MUTATE_NOT_SPECIFIED;
else if (opt.Method() == "all_vars")
mutMeth = eMutateMode::MUTATE_ALL_VARIATIONS;
else if (opt.Method() == "one_xform")
mutMeth = eMutateMode::MUTATE_ONE_XFORM_COEFS;
else if (opt.Method() == "add_symmetry")
mutMeth = eMutateMode::MUTATE_ADD_SYMMETRY;
else if (opt.Method() == "post_xforms")
mutMeth = eMutateMode::MUTATE_POST_XFORMS;
else if (opt.Method() == "color_palette")
mutMeth = eMutateMode::MUTATE_COLOR_PALETTE;
else if (opt.Method() == "delete_xform")
mutMeth = eMutateMode::MUTATE_DELETE_XFORM;
else if (opt.Method() == "all_coefs")
mutMeth = eMutateMode::MUTATE_ALL_COEFS;
else
{
cerr << "method " << opt.Method() << " not defined for mutate. Defaulting to random.\n";
mutMeth = eMutateMode::MUTATE_NOT_SPECIFIED;
}
os << tools.Mutate(orig, mutMeth, vars, opt.Symmetry(), T(opt.Speed()), MAX_CL_VARS);
//Scan string returned for 'mutate color'.
if (strstr(os.str().c_str(), "mutate color"))
didColor = true;
if (orig.m_Name != "")
{
os2.str("");
os2 << "mutation " << rep << " of " << orig.m_Name;
orig.m_Name = os2.str();
}
}
else if (doCross0)
{
i0 = rand.Rand() % embers.size();
i1 = rand.Rand() % embers2.size();
selp0 = embers[i0];
selp1 = embers2[i1];
aselp0 = &selp0;
aselp1 = &selp1;
if (opt.Method() == "")
crossMeth = eCrossMode::CROSS_NOT_SPECIFIED;
else if (opt.Method() == "union")
crossMeth = eCrossMode::CROSS_UNION;
else if (opt.Method() == "interpolate")
crossMeth = eCrossMode::CROSS_INTERPOLATE;
else if (opt.Method() == "alternate")
crossMeth = eCrossMode::CROSS_ALTERNATE;
else
{
cerr << "method '" << opt.Method() << "' not defined for cross. Defaulting to random.\n";
crossMeth = eCrossMode::CROSS_NOT_SPECIFIED;
}
tools.Cross(embers[i0], embers2[i1], orig, crossMeth);
if (embers[i0].m_Name != "" || embers2[i1].m_Name != "")
{
os2.str("");
os2 << rep << " of " << embers[i0].m_Name << " x " << embers2[i1].m_Name;
orig.m_Name = os2.str();
}
}
else
{
os << "random";
randomMode = true;
tools.Random(orig, vars, opt.Symmetry(), 0, MAX_CL_VARS);
aselp0 = nullptr;
aselp1 = nullptr;
}
//Adjust bounding box half the time.
if (rand.RandBit() || randomMode)
{
T bmin[2], bmax[2];
tools.EstimateBoundingBox(orig, T(0.01), 100000, bmin, bmax);
if (rand.Frand01<T>() < T(0.3))
{
orig.m_CenterX = (bmin[0] + bmax[0]) / 2;
orig.m_CenterY = (bmin[1] + bmax[1]) / 2;
os << " recentered";
}
else
{
if (rand.RandBit())
{
mix0 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
mix1 = rand.GoldenBit<T>();
os << " reframed0";
}
else if (rand.RandBit())
{
mix0 = rand.GoldenBit<T>();
mix1 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
os << " reframed1";
}
else
{
mix0 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
mix1 = rand.GoldenBit<T>() + rand.Frand11<T>() / 5;
os << " reframed2";
}
orig.m_CenterX = mix0 * bmin[0] + (1 - mix0) * bmax[0];
orig.m_CenterY = mix1 * bmin[1] + (1 - mix1) * bmax[1];
}
orig.m_PixelsPerUnit = orig.m_FinalRasW / (bmax[0] - bmin[0]);
}
os << tools.TruncateVariations(orig, 5);
if (!didColor && rand.RandBit())
{
if (opt.Debug())
cerr << "improving colors...\n";
tools.ImproveColors(orig, 100, false, 10);
os << " improved colors";
}
orig.m_Edits = emberToXml.CreateNewEditdoc(aselp0, aselp1, os.str(), opt.Nick(), opt.Url(), opt.Id(), opt.Comment(), opt.SheepGen(), opt.SheepId());
save = orig;
SetDefaultTestValues(orig);
renderer->SetEmber(orig);
if (renderer->Run(finalImage) != eRenderStatus::RENDER_OK)
{
cerr << "Error: test image rendering failed, aborting.\n";
return false;
}
tot = totb = totw = 0;
n = orig.m_FinalRasW * orig.m_FinalRasH;
for (i = 0; i < 3 * n; i += 3)
{
tot += (finalImage[i] + finalImage[i + 1] + finalImage[i + 2]);
if (0 == finalImage[i] && 0 == finalImage[i + 1] && 0 == finalImage[i + 2]) totb++;
if (255 == finalImage[i] && 255 == finalImage[i + 1] && 255 == finalImage[i + 2]) totw++;
}
avgPix = (tot / T(3 * n));
fractionBlack = totb / T(n);
fractionWhite = totw / T(n);
if (opt.Debug())
cerr << "avgPix = " << avgPix << " fractionBlack = " << fractionBlack << " fractionWhite = " << fractionWhite << " n = " << n << "\n";
orig.Clear();
count++;
}
while ((avgPix < opt.AvgThresh() ||
fractionBlack < opt.BlackThresh() ||
fractionWhite > opt.WhiteLimit()) &&
count < opt.Tries());
if (count == opt.Tries())
cerr << "Warning: reached maximum attempts, giving up.\n";
}
if (pTemplate)
tools.ApplyTemplate(save, *pTemplate);
save.m_Time = T(rep);
if (opt.MaxXforms() != UINT_MAX)
{
save.m_Symmetry = 0;
while (save.TotalXformCount() > opt.MaxXforms())
save.DeleteTotalXform(save.TotalXformCount() - 1);
}
cout << emberToXml.ToString(save, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
VerbosePrint("\nDone. Action = " << os.str() << "\n");
cout.flush();
save.Clear();
}
if (opt.Enclosed())
cout << "</pick>\n";
return true;
}
/// <summary>
/// Main program entry point for EmberGenome.exe.
/// </summary>
/// <param name="argc">The number of command line arguments passed</param>
/// <param name="argv">The command line arguments passed</param>
/// <returns>0 if successful, else 1.</returns>
int _tmain(int argc, _TCHAR* argv[])
{
bool b = false;
EmberOptions opt;
//Required for large allocs, else GPU memory usage will be severely limited to small sizes.
//This must be done in the application and not in the EmberCL DLL.
#ifdef _WIN32
_putenv_s("GPU_MAX_ALLOC_PERCENT", "100");
#else
putenv(const_cast<char*>("GPU_MAX_ALLOC_PERCENT=100"));
#endif
if (!opt.Populate(argc, argv, eOptionUse::OPT_USE_GENOME))
{
#ifdef DO_DOUBLE
if (!opt.Sp())
b = EmberGenome<double>(opt);
else
#endif
b = EmberGenome<float>(opt);
}
return b ? 0 : 1;
}