Thank you all for the support and feedback!
I updated the post to the new version, 1.0.4.
It has a bunch of new features and improvements, most of which were due to Fraktalist's thorough feedback and ideas. Thanks for that!
I removed the background images and the input text display.
The original preview was replaced by a new one, which now displays all gradients in a pack (at the cost of a slightly higher processing time).
I also added the batch convert feature, although I didn't include a custom naming option yet (mostly because the UI is already pretty crammed) but instead a lot of different naming options.
The default extension is now .kfp, although that shouldn't have been an issue anyways. Originally, I wanted to overwrite only the colours part in a file, but due to a little error that did not work out in the end. I fixed that, and now you can overwrite existing .kfr and .kfp files' colours without changing other data. However, that does
not work with batch convert.
The filename for a single conversion will now be the gradient title by default and the UI was also improved.
-probably complicated: give ability to preview in context. choose a good location and then map the gradient to it, maybe add a way to adjust divide iteration. add this display below the gradient viewer, update in realtime when switching through
Yeah, this is complicated. I already tried creating a primitive Mandelbrot explorer using the same IDE, and it turned out to be rather slow, even at low canvas size and iteration count.
A faster idea would be to pre-render a part of the set and save the iteration count for each pixel. Then it would be possible to insert the colours at runtime at a moderate speed. If anyone has a suggestion for an adequate location, feel free to share it with me. I will see what I can do.
If you would like me to make more file types available for conversion, please message me with at least one file and a visualisation of it. I will try to get them in for the next update.
It would be great to integrate this within KF! The IDE I used to create the program is unfortunately for Delphi / Free Pascal.
I will try to translate the relevant parts of the code though and explain how it works:
The .ugr and .gradient files don't have any difference except for the extension.
Here is a part of the gradient pack 'Teuns FGP' by Teuns:
Coffee_in_a_red_mug {
gradient:
title="Coffee_in_a_red_mug" smooth=no
index=0 color=3819102
index=2 color=2897741
index=3 color=1515586
index=5 color=395066
...
index=393 color=1906811
index=394 color=2104443
index=396 color=2439020
index=397 color=3226207
index=399 color=3161691
}
I extracted the relevant data (title, indices, colours) by going through the input string and watching for certain characters and character combinations (e.g. ' " ' marks the beginning of the title).
The colour format is essentially a Hex BGR value converted to decimal.
That means, in order to get the RGB values of '3819102' you would first have to convert the value to hexadecimal, which would look like this: '3A465E'. The first two digits are the blue value, the middle two are the green value and the last two are the red value.
Converting back to decimal, the extracted colour would look like this (R,G,B): 94, 70, 58.
Using code, it could be expressed like this (no specific language):
int[] colours; //Has the input colour values
int[] red;
int[] green; //Store the extracted colour values
int[] blue;
string hexString;
for (int i = 0; i < colours.length; i++) //Loop through all the input colours
{
hexString = DecToHex(colours[i]); //Convert the input to hexadecimal
red[i] = HexToDec(hexString[4] + hexString[5]);
green[i] = HexToDec(hexString[2] + hexString[3]); //Extract the colour values and re-order to RGB
blue[i] = HexToDec(hexString[0] + hexString[1]);
}
Once we have extracted all the colours, we can create the gradient with them. This is done by linear interpolation.
Basically, we want to evenly scatter the colours across our output, and then 'fill in the gaps'.
Here is the code:
public void SequenceDraw(color[] colours, int amount) { //'colours' contains all ordered colours (RGB), amount is the amount of colours the output is supposed to have
int R;
int G; //The colour values in RGB that will be calculated
int B;
int XPosition; //Stores the horizontal location
bool[] filled; //assuming that booleans are initialised 'false', filled stores, if a pixel (or similar, depending on the context) already has a colour assigned to it
float segment = Amount/(colours.Length-1); //segment is the distance between two colours. E.g. if you have 26 colours and want to have an output of length 75, each segment will be 3 units
//the '-1' is because we don't want to add another segment after the last colours
for (int i = 0; i < (colours.length - 1); i++){ //Now we actually begin drawing. We loop through all the colours, but ignore the last one ('-1') because the last one should not have another segment attached
for (int j = 0; j <= ceil(Segment); j++) { //We loop through each unit/pixel within the length of a segment. We round up so that we don't miss out on any pixels/units
R = round((( (colours[i+1].Red - colours[i].Red) / Segment)*j) + colours[i].Red); //These three lines actually calculate the value at that specific position
G = round((( (colours[i+1].Green- colours[i].Green) / Segment)*j) + colours[i].Green); //The formula can be derived and understood when you think a little bit about it,
B = round((( (colours[i+1].Blue - colours[i].Blue) / Segment)*j) + colours[i].Blue); //but I also created a Desmos graph to visualise it: https://www.desmos.com/calculator/n7ak6xbooi
XPosition = ceil((segment) * i) + j; //Calculate the position of the colour in the output
if (filled[XPosition]) { //Now we interpolate linearly, in case ther already is a colour where we want to draw:
R = (R + Output[XPosition].Red) / 2;
G = (G + Output[XPosition].Green) / 2; //Basically, if there already is a colour present,
B = (B + Output[XPosition].Blue) / 2; //we take the intermediate value of it and out new colour, and store it as our new colour
} else {
filled[XPosition] = true;
};
Output[XPosition] = RGBToColor(R, G, B); //And finally, we convert our single values to the output colour at that position
}
}
}
public void IndexDraw(color[] colours, int[] indices) { //'colours' contains all ordered colours (RGB), indices contains the corresponding indices
int R;
int G; //The colour values in RGB that will be calculated
int B;
float segment; //This time, the segment won't have a rigid value for each colour, so we will have to calculate it in between each one
int XPosition; //Stores the horizontal location
bool[] filled; //assuming that booleans are initialised 'false', filled stores, if a pixel (or similar, depending on the context) already has a colour assigned to it
int MaxIndex = indices[indices.length - 1]; //Stores the last index value as the highest index value
for (int i = 0; i < (colours.length - 1); i++){ //Now we actually begin drawing. We loop through all the colours, but ignore the last one ('-1') because the last one should not have another segment attached
Segment = ((indices[i+1] - indices[i]) / MaxIndex) * 1024; //Before calculating the colours, we have to calculate the segment length. We do that by determining what fraction of the whole output the segment will cover, and multiply that by the output length, in this case 1024
for (int j = 0; j <= floor(Segment); j++) { //We loop through each unit/pixel within the length of a segment. We round down because otherwise ugly colour glitches would appear
R = round((( (colours[i+1].Red - colours[i].Red) / Segment)*j) + colours[i].Red); //These three lines actually calculate the value at that specific position
G = round((( (colours[i+1].Green- colours[i].Green) / Segment)*j) + colours[i].Green); //The formula can be derived and understood when you think a little bit about it,
B = round((( (colours[i+1].Blue - colours[i].Blue) / Segment)*j) + colours[i].Blue); //but I also created a Desmos graph to visualise it: https://www.desmos.com/calculator/n7ak6xbooi
XPosition = ceil(( (indices[i]/MaxIndex) * 1024) + j); //Calculate the position of the colour in the output
if (filled[XPosition]) { //Now we interpolate linearly, in case ther already is a colour where we want to draw:
R = (R + Output[XPosition].Red) / 2;
G = (G + Output[XPosition].Green) / 2; //Basically, if there already is a colour present,
B = (B + Output[XPosition].Blue) / 2; //we take the intermediate value of it and out new colour, and store it as our new colour
} else {
filled[XPosition] = true;
};
Output[XPosition] = RGBToColor(R, G, B); //And finally, we convert our single values to the output colour at that position
}
}
}
I hope this helps and that the code has no syntax errors.
You can use it all you want, no credit necessary.
Lifting the colour limit would be awesome. This would allow for much less monotonous Mandelbrot zoom videos.