I made a program to convert KFB maps from Kalles Fraktaler to the Mandel Machine MMIT format that can be loaded in Mandel Machine. My motivation was to use Mandel Machine to save renders as PNG (Kalles Fraktaler only saves as JPEG) and to use the coloring methods available in Mandel Machine. KFB files from fractal types other than the Mandelbrot set (burning ship and others) also work but of course you can't continue zooming in Mandel Machine.
Download it here:
https://mega.nz/#!M40BBQLA!vAeotI2-0_kaWBMoDqyGs_wDMTIAGe1tn0yqBt_CIqUInformation:
It's a commandline program.
The MMIT and KFB file formats only have iteration data in common which has consequences for the other relevant data.
If you want to load the gradient in Mandel Machine, save both a KFB and KFR file. Mandel Machine can read the KFR file and load the gradient from there.
Because KFB doesn't have location information, I designed the program to write some arbitrary location information to the MMIT file. Otherwise Mandel Machine doesn't understand the file. If you want the correct location information (for example to continue zooming) you will also need to load the KFR file or just copy and paste it from Kalles Fraktaler, but make sure to deselect location information when you load the MMIT.
The MMIT file format was designed to allow for efficient storage of iteration data. My program generates very inefficient MMIT files because I'm too lazy to figure out how to do zlib compression and use a smaller datatype for the iteration counts when possible.
This program makes use of source code from Claude's
kf-extras to read KFB files and extract iteration counts from them. Claude also helped me to find the relevant source code in Mandel Machine. Thanks for the help.
If you find a location that causes problems please let me know. I will try to improve the program if I can. (I'm not an experienced programmer.)
Source code (written in C) that depends on kfb.h and kfb.c from kf-extras:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include "kfb.h"
void asserts(){
assert(sizeof(char)==1);
assert(sizeof(short)==2);
assert(sizeof(int)==4);
assert(sizeof(float)==4);
assert(sizeof(double)==8);
assert(sizeof(long)==4);
assert(sizeof(long long)==8);
return;
}
//data_size counts how large the mmit data is before zlib formatting
int data_size = 0;
const char one = 1;
union DoubleBytes {//to interpret double as char array and inverse byte order
double d;
char b[8];
};
union IntBytes {//to interpret int as char array
int i;
char b[4];
};
//functions to write data in big endian notation
int le_to_be_int(int input){
int num = input;
int b0,b1,b2,b3;
int res;
b0 = (num & 0x000000ff) << 24u;
b1 = (num & 0x0000ff00) << 8u;
b2 = (num & 0x00ff0000) >> 8u;
b3 = (num & 0xff000000) >> 24u;
res = b0 | b1 | b2 | b3;
return res;
}
void write_be_int(int input, FILE *outfile){
int res = le_to_be_int(input);
fwrite(&res, 4, 1, outfile);
data_size += 4;
}
void write_be_char(char input, FILE *outfile){
fwrite(&input, 1, 1, outfile);
data_size += 1;
}
void write_be_double(double input, FILE *outfile){
union DoubleBytes doublebytes;
doublebytes.d = input;
for(int i=0; i<4; i++){//reverse byte order in the double with char array interpretation
char temp = doublebytes.b[i];
doublebytes.b[i] = doublebytes.b[7-i];
doublebytes.b[7-i] = temp;
}
fwrite(&doublebytes.d, 8, 1, outfile);
data_size += 8;
}
int main(int argc, char **argv) {
//Check and load KFB file-----------------------------------------------------------------------------------
asserts();
if (argc < 2)
{
printf("This program converts a given KFB file to MMIT. Program version: 1.0
");
printf("Usage: "); printf(argv[0]); printf(" path_to_KFB [path_to_MMIT]
");
printf("Default for path_to_MMIT: MMIT-convert.mmit
");
return 1;
}
printf("Loading KFB file...
");
kfb *in = kfb_load(argv[1]);
if (! in) { printf("Loading KFB file failed.
"); return 1; }
int width = kfb_width(in);
int height = kfb_height(in);
//Writing all relevant mmit data to tempoutfile without zlib format--------------------------------------------
FILE *tempoutfile;
tempoutfile=fopen("tempoutfile", "wb");
//Get min and max iteration values present in KFB
double max_iters = 0;
double min_iters = 800100100;
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
double ij = kfb_get(in, j, i);
if(ij<min_iters) min_iters = ij;
if(ij>max_iters) max_iters = ij;
}
}
printf("Lowest iteration count: %lf
", min_iters);
printf("Highest iteration count: %lf
", max_iters);
//Parameters and settings
write_be_int(one, tempoutfile); //Program version
write_be_int(width, tempoutfile); //Canvas width
write_be_int(height, tempoutfile); //Canvas height
char supersampling = 0; write_be_char(supersampling, tempoutfile); //Supersampling (Always 0 for no supersampling)
double magnification = 512; write_be_double(magnification, tempoutfile); //Fake magnification default to arbitrary value;
double rotation = 256; write_be_double(rotation, tempoutfile); //Fake rotation default to arbitrary value;
int coord_digits = 11; write_be_int(coord_digits, tempoutfile); //Fake number of coordinate digits
int coords[44] = {-1, -1, -1, -1, -1768346623, 1239184185, -1450836726, -1610156628, -1160793342, 1122682978, 1750969101, 1468815386, -86691575, -319241320, -141699069, 731720154, -1302511102, 2092147506, 1358717447, -121412270, -362838523, 1906712988, 218032387, 147395776, 1528659973, -1874890155, 1817790991, 431114295, 1763611407, -389200653, 1363516937, 1455562147, 707986444, -847133922, 1130970381, 1568324293, 1567881485, -580086891, 1843248142, 1016614712, 1950251523, -75377623, -1674907892, 1914116203};
for(int i=0; i<44; i++){ //Fake array of coordinate digits
fwrite(&coords[i], 4, 1, tempoutfile);
data_size += 4;
}
int iteration_limit = 799999999; write_be_int(iteration_limit, tempoutfile); //default one below MM's 800M limit, seems safe
char bytesPerSample = 4; write_be_char(bytesPerSample, tempoutfile); //always 4 (the max) for simplicity
write_be_double(min_iters, tempoutfile); //minimum number of iterations in the data
double granularity = (799999999 - min_iters)/4294967290; write_be_double(granularity, tempoutfile); //(equal to iterRange / 4294967290, where iterRange is maxIt - minIt)
printf("Writing iteration data...
");
//Iteration data
double granRec = 0; if(granularity != 0) granRec = 1/granularity;
long long prevEncoded = 0;
char buffer[width*4];
union IntBytes intbytes;
for(int row=0; row<height; row++){
for(int col=0; col<width; col++){
double it = kfb_get(in, col, row);
long long encoded;
if (it == max_iters) encoded = 3; //Point inside minibrot
else encoded = round((it-min_iters)*granRec) + 5; //encodeSample
int diff = encoded - prevEncoded;
//writeSample:
intbytes.i = diff;
buffer[col+3*width] = intbytes.b[3];
buffer[col+2*width] = intbytes.b[2];
buffer[col+width] = intbytes.b[1];
buffer[col] = intbytes.b[0];
//
prevEncoded = encoded;
}
fwrite(&buffer, width*4, 1, tempoutfile);
data_size += width*4;
}
fclose(tempoutfile);
kfb_free(in);
printf ("Total data size: %d
", data_size);
//read uncompressed MMIT data into array a---------------------------------------------------------------------
char* a = malloc(data_size);
if(a==NULL){ printf ("Allocation of memory failed.
"); return 1;}
FILE *readbackfile;
readbackfile=fopen("tempoutfile", "rb");
fread(a, data_size, 1, readbackfile);
printf("Writing zlib format...
");
//Convert to zlib format and write to file---------------------------------------------------------------------
FILE *outfile;
if(argc > 2) outfile=fopen(argv[2], "wb");
else outfile=fopen("MMIT-convert.mmit", "wb");
int bytes_written = 0;
char header_more[] = {0, 86, 129, 169, 126}; // hardcoded zlib header for a block size of 33110;
while(bytes_written<data_size)
{
if(data_size - bytes_written > 33110)
{ //not the last block header
fwrite(&header_more, 5, 1, outfile);
for(int j=0; j<33110; j++)
{
fwrite(&a[bytes_written+j], 1, 1, outfile);
}
bytes_written += 33110;
}
else
{ //the last block header
short to_go = data_size - bytes_written;
short complement = 65535 - to_go;
fwrite(&one, 1, 1, outfile);
fwrite(&to_go, 2, 1, outfile);
fwrite(&complement, 2, 1, outfile);
for(int j=0; j<to_go; j++)
{
fwrite(&a[bytes_written+j], 1, 1, outfile);
}
bytes_written += to_go;
}
}
//-----------------------------------------------------------------------------------
fclose(readbackfile);
if(remove("tempoutfile") != 0) printf("Deleting the temporary file failed.");
printf ("Done");
return 0;
}
The Mandel Machine MMIT compressed iteration data file format:
Every datatype needs to be written in big endian notation.
The following streamed through a Deflater (raw zlib format):
<int 4 bytes> Program Version
<int 4 bytes> Canvas Width
<int 4 bytes> Canvas Height
<byte> Supersampling
<double> Magnification
<double> Rotation
<int 4 bytes> Number of coordinate digits represented by long ints
<long long array> array 2 times as long as the above int
<int 4 bytes> Iteration limit
<byte> Bytes per sample
<double> minimum iteration count
<double> granularity (equal to iterRange / (2^(bytes_per_sample*8) - 6), where iterRange is maxIt - minIt)
<int array> row by row encoded samples (one int value per pixel)