Task 228: .FLAME File Format
Task 228: .FLAME File Format
.FLAME File Format Specifications
The .FLAME file format is an XML-based format used to store parameters for generating fractal flames, originally developed for tools like flam3 and Apophysis. It defines the mathematical transformations, color palettes, and rendering settings for creating fractal images. The format is text-based and human-readable, with no binary header or magic number (it starts with <flame>
). It is not tied to specific file system properties beyond being a plain text file with the .flame extension (sometimes .flam3 in flam3 tools). The structure includes a root <flame>
element, multiple <xform>
elements for transformations, a <palette>
element for colors, and optional elements like <motion>
and <edit>
.
- List of all the properties of this file format intrinsic to its file system:
- name: The name of the flame.
- version: The software version that created the file.
- size: The image width and height (two integers).
- center: The center coordinates (two floats, x y).
- scale: The scaling factor (float).
- angle: The rotation angle in radians (float).
- rotate: The rotation angle in degrees (float).
- oversample: The oversampling factor (integer; deprecated in some versions, replaced by supersample).
- supersample: The supersampling factor (integer).
- filter: The filter setting (integer).
- quality: The quality setting (integer).
- background: The background color (three floats, r g b).
- brightness: The brightness value (float).
- gamma: The gamma correction value (float).
- estimator_radius: The estimator radius for rendering (float).
- estimator_minimum: The estimator minimum (float).
- estimator_curve: The estimator curve (float).
- temporal_samples: The number of temporal samples for animations (integer).
- motion_exponent: The motion exponent (float).
- gamma_threshold: The gamma threshold (float).
- highlight_power: The highlight power (float).
- palette_mode: The palette blending mode (string, e.g., "step" or "linear").
- interpolation_type: The interpolation type for animations (string, e.g., "linear", "log", "old", "older").
- interpolation_space: The interpolation space (string).
- color_speed: The color speed for animations (float).
- animate: The animation flag (integer).
- xforms: A list of transformations, each with:
- weight: The probability weight (float).
- color: The color index (float, 0 to 1).
- opacity: The opacity (float, 0 to 1).
- chaos: Chaos factors for interaction with other xforms (space-separated floats).
- variations: A list of variation types (e.g., "linear", "spherical", "bubble") with their weights (floats).
- coefs: The affine transformation coefficients (six floats: a b c d e f).
- post: Post-transformation coefficients (six floats, optional).
- motion: Motion parameters for animation (optional element with attributes like frequency, function).
- palette: The color palette, with:
- count: Number of colors (integer, typically 256).
- format: The format (string, e.g., "RGB").
- data: The palette data (string of RGB or hex values, or floating-point for blends).
- symmetry: Symmetry type (integer or string, for bilateral or rotational symmetry).
- edit: Edit history (optional, with depth control).
These properties are extracted from the XML structure and are intrinsic to the format's definition, not the underlying file system (e.g., no specific byte alignment or endianness, as it's text-based XML).
- Two direct download links for files of format .FLAME:
- http://dl.dropbox.com/u/2426275/xyrus.clan-boa.com/download/misc/reformed_apophysis_flame_format.zip (This zip contains a sample .flame file.)
- https://sourceforge.net/projects/apophysis/files/apophysis/Apophysis 2.09/apo209-100607.exe/download (This installer contains example .flame files in the installation directory after extraction.)
- Ghost blog embedded HTML JavaScript for drag and drop .FLAME file to dump properties:
- Python class for opening, decoding, reading, writing, and printing .FLAME properties:
import xml.etree.ElementTree as ET
import os
class FlameFile:
def __init__(self, filepath=None):
self.filepath = filepath
self.tree = None
self.root = None
if filepath and os.path.exists(filepath):
self.read(filepath)
def read(self, filepath):
self.tree = ET.parse(filepath)
self.root = self.tree.getroot()
self.filepath = filepath
def print_properties(self):
if not self.root:
print("No file loaded")
return
print("Flame Properties:")
for key, value in self.root.attrib.items():
print(f"{key}: {value}")
print("\nXforms:")
for i, xform in enumerate(self.root.findall('xform')):
print(f"Xform {i+1}:")
for key, value in xform.attrib.items():
print(f" {key}: {value}")
print("\nPalette:")
palette = self.root.find('palette')
if palette is not None:
for key, value in palette.attrib.items():
print(f" {key}: {value}")
print(f" data: {palette.text.strip() if palette.text else 'None'}")
def write(self, filepath=None):
if not self.tree:
print("No data to write")
return
if not filepath:
filepath = self.filepath
self.tree.write(filepath, encoding='utf-8', xml_declaration=True)
def add_xform(self, attributes):
if not self.root:
self.root = ET.Element('flame')
self.tree = ET.ElementTree(self.root)
xform = ET.SubElement(self.root, 'xform')
for key, value in attributes.items():
xform.set(key, str(value))
# Example usage:
# f = FlameFile('example.flame')
# f.print_properties()
# f.add_xform({'weight': 0.5, 'color': 0})
# f.write('modified.flame')
- Java class for opening, decoding, reading, writing, and printing .FLAME properties:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;
import java.io.File;
public class FlameFile {
private String filepath;
private Document doc;
public FlameFile(String filepath) {
this.filepath = filepath;
if (filepath != null && new File(filepath).exists()) {
read(filepath);
}
}
public void read(String filepath) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(filepath);
this.filepath = filepath;
} catch (Exception e) {
e.printStackPrint();
}
}
public void printProperties() {
if (doc == null) {
System.out.println("No file loaded");
return;
}
Element root = doc.getDocumentElement();
System.out.println("Flame Properties:");
NamedNodeMap attrs = root.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
System.out.println(attrs.item(i).getNodeName() + ": " + attrs.item(i).getNodeValue());
}
System.out.println("\nXforms:");
NodeList xforms = root.getElementsByTagName("xform");
for (int i = 0; i < xforms.getLength(); i++) {
System.out.println("Xform " + (i + 1) + ":");
NamedNodeMap xAttrs = xforms.item(i).getAttributes();
for (int j = 0; j < xAttrs.getLength(); j++) {
System.out.println(" " + xAttrs.item(j).getNodeName() + ": " + xAttrs.item(j).getNodeValue());
}
}
System.out.println("\nPalette:");
NodeList palettes = root.getElementsByTagName("palette");
if (palettes.getLength() > 0) {
NamedNodeMap pAttrs = palettes.item(0).getAttributes();
for (int j = 0; j < pAttrs.getLength(); j++) {
System.out.println(" " + pAttrs.item(j).getNodeName() + ": " + pAttrs.item(j).getNodeValue());
}
System.out.println(" data: " + palettes.item(0).getTextContent().trim());
}
}
public void write(String filepath) {
if (doc == null) {
System.out.println("No data to write");
return;
}
try {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filepath != null ? filepath : this.filepath));
transformer.transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addXform(String weight, String color) {
if (doc == null) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.newDocument();
Element root = doc.createElement("flame");
doc.appendChild(root);
} catch (Exception e) {
e.printStackTrace();
}
}
Element root = doc.getDocumentElement();
Element xform = doc.createElement("xform");
xform.setAttribute("weight", weight);
xform.setAttribute("color", color);
root.appendChild(xform);
}
// Example usage:
// public static void main(String[] args) {
// FlameFile f = new FlameFile("example.flame");
// f.printProperties();
// f.addXform("0.5", "0");
// f.write("modified.flame");
// }
}
- JavaScript class for opening, decoding, reading, writing, and printing .FLAME properties (note: JS file I/O is limited in browser; this assumes Node.js with 'fs' module):
const fs = require('fs');
const { DOMParser, XMLSerializer } = require('xmldom');
class FlameFile {
constructor(filepath = null) {
this.filepath = filepath;
this.doc = null;
if (filepath && fs.existsSync(filepath)) {
this.read(filepath);
}
}
read(filepath) {
const xml = fs.readFileSync(filepath, 'utf8');
const parser = new DOMParser();
this.doc = parser.parseFromString(xml, 'text/xml');
this.filepath = filepath;
}
printProperties() {
if (!this.doc) {
console.log('No file loaded');
return;
}
const root = this.doc.getElementsByTagName('flame')[0];
console.log('Flame Properties:');
for (let i = 0; i < root.attributes.length; i++) {
const attr = root.attributes[i];
console.log(`${attr.name}: ${attr.value}`);
}
console.log('\nXforms:');
const xforms = this.doc.getElementsByTagName('xform');
for (let i = 0; i < xforms.length; i++) {
console.log(`Xform ${i + 1}:`);
for (let j = 0; j < xforms[i].attributes.length; j++) {
const attr = xforms[i].attributes[j];
console.log(` ${attr.name}: ${attr.value}`);
}
}
console.log('\nPalette:');
const palette = this.doc.getElementsByTagName('palette')[0];
if (palette) {
for (let j = 0; j < palette.attributes.length; j++) {
const attr = palette.attributes[j];
console.log(` ${attr.name}: ${attr.value}`);
}
console.log(` data: ${palette.textContent.trim()}`);
}
}
write(filepath = null) {
if (!this.doc) {
console.log('No data to write');
return;
}
const serializer = new XMLSerializer();
const xmlStr = serializer.serializeToString(this.doc);
fs.writeFileSync(filepath || this.filepath, xmlStr, 'utf8');
}
addXform(attributes) {
if (!this.doc) {
this.doc = new DOMParser().parseFromString('<flame></flame>', 'text/xml');
}
const root = this.doc.getElementsByTagName('flame')[0];
const xform = this.doc.createElement('xform');
for (const [key, value] of Object.entries(attributes)) {
xform.setAttribute(key, value);
}
root.appendChild(xform);
}
}
// Example usage:
// const f = new FlameFile('example.flame');
// f.printProperties();
// f.addXform({ weight: '0.5', color: '0' });
// f.write('modified.flame');
- C class (using C++ for class support, as C does not have classes; assumes libxml2 for XML parsing, which must be linked):
#include <iostream>
#include <fstream>
#include <string>
#include <libxml/parser.h>
#include <libxml/tree.h>
class FlameFile {
private:
std::string filepath;
xmlDocPtr doc;
public:
FlameFile(const std::string& fp = "") : filepath(fp), doc(nullptr) {
if (!fp.empty()) {
read(fp);
}
}
~FlameFile() {
if (doc) xmlFreeDoc(doc);
}
void read(const std::string& fp) {
doc = xmlReadFile(fp.c_str(), NULL, 0);
filepath = fp;
}
void printProperties() {
if (!doc) {
std::cout << "No file loaded" << std::endl;
return;
}
xmlNode *root = xmlDocGetRootElement(doc);
if (root) {
std::cout << "Flame Properties:" << std::endl;
for (xmlAttr *attr = root->properties; attr; attr = attr->next) {
std::cout << attr->name << ": " << attr->children->content << std::endl;
}
std::cout << "\nXforms:" << std::endl;
int i = 1;
for (xmlNode *node = root->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE && strcmp((char*)node->name, "xform") == 0) {
std::cout << "Xform " << i++ << ":" << std::endl;
for (xmlAttr *attr = node->properties; attr; attr = attr->next) {
std::cout << " " << attr->name << ": " << attr->children->content << std::endl;
}
}
}
std::cout << "\nPalette:" << std::endl;
for (xmlNode *node = root->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE && strcmp((char*)node->name, "palette") == 0) {
for (xmlAttr *attr = node->properties; attr; attr = attr->next) {
std::cout << " " << attr->name << ": " << attr->children->content << std::endl;
}
if (node->content) {
std::cout << " data: " << node->content << std::endl;
}
}
}
}
}
void write(const std::string& fp = "") {
if (!doc) {
std::cout << "No data to write" << std::endl;
return;
}
xmlSaveFormatFileEnc(fp.empty() ? filepath.c_str() : fp.c_str(), doc, "UTF-8", 1);
}
void addXform(const std::string& weight, const std::string& color) {
if (!doc) {
doc = xmlNewDoc(BAD_CAST "1.0");
xmlNodePtr root = xmlNewNode(NULL, BAD_CAST "flame");
xmlDocSetRootElement(doc, root);
}
xmlNodePtr root = xmlDocGetRootElement(doc);
xmlNodePtr xform = xmlNewChild(root, NULL, BAD_CAST "xform", NULL);
xmlNewProp(xform, BAD_CAST "weight", BAD_CAST weight.c_str());
xmlNewProp(xform, BAD_CAST "color", BAD_CAST color.c_str());
}
};
// Example usage:
// int main() {
// FlameFile f("example.flame");
// f.printProperties();
// f.addXform("0.5", "0");
// f.write("modified.flame");
// return 0;
// }