Task 767: .VBR File Format
Task 767: .VBR File Format
1. List of Properties Intrinsic to the .VBR File Format
The .VBR file format, as used by GIMP for generated brushes, is a plain text format with fields separated by line breaks. The properties are derived from its structure and are as follows (note that some are conditional based on the version):
- Magic string: A fixed identifier string, always "GIMP-VBR".
- Version: The format version, either "1.0" (for non-shaped brushes) or "1.5" (for shaped brushes).
- Name: The brush name, a UTF-8 encoded string with a maximum length of 255 bytes.
- Shape: The brush shape, one of "circle", "square", or "diamond" (present only in version 1.5).
- Spacing: The brush spacing, represented as a floating-point number.
- Radius: The brush radius in pixels, represented as a floating-point number.
- Spikes: The number of spikes for shaped brushes, represented as an integer (present only in version 1.5).
- Hardness: The brush hardness, represented as a floating-point number.
- Aspect ratio: The brush aspect ratio, represented as a floating-point number.
- Angle: The brush angle, represented as a floating-point number.
These properties define the intrinsic structure of the file, with the total number of lines being 8 for version 1.0 and 10 for version 1.5.
2. Two Direct Download Links for .VBR Files
The following are direct download links to sample .VBR files from publicly available GIMP brush repositories:
- https://raw.githubusercontent.com/piksels-and-lines-orchestra/gimp/master/data/brushes/gimp-obsolete-files/Diagonal-Star-11.vbr
- https://raw.githubusercontent.com/piksels-and-lines-orchestra/gimp/master/data/brushes/gimp-obsolete-files/Circle-Fuzzy-19.vbr
3. HTML/JavaScript for Drag-and-Drop .VBR File Dumper
The following is an embeddable HTML snippet with JavaScript that can be used in a blogging platform such as Ghost. It provides a drop zone where a user can drag and drop a .VBR file, parses the file, and displays all properties on the screen.
4. Python Class for .VBR File Handling
The following Python class can open, decode (read), encode (write), and print the properties of a .VBR file.
import os
class VBRFile:
def __init__(self, filename=None):
self.magic = 'GIMP-VBR'
self.version = None
self.name = None
self.shape = None
self.spacing = None
self.radius = None
self.spikes = None
self.hardness = None
self.aspect_ratio = None
self.angle = None
if filename:
self.read(filename)
def read(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = [line.strip() for line in f.readlines()]
if len(lines) < 8 or lines[0] != 'GIMP-VBR':
raise ValueError('Invalid .VBR file: Missing or incorrect magic string.')
self.magic = lines[0]
self.version = lines[1]
self.name = lines[2]
if self.version == '1.0':
if len(lines) != 8:
raise ValueError('Invalid .VBR file: Incorrect number of lines for version 1.0.')
self.spacing = float(lines[3])
self.radius = float(lines[4])
self.hardness = float(lines[5])
self.aspect_ratio = float(lines[6])
self.angle = float(lines[7])
elif self.version == '1.5':
if len(lines) != 10:
raise ValueError('Invalid .VBR file: Incorrect number of lines for version 1.5.')
self.shape = lines[3]
self.spacing = float(lines[4])
self.radius = float(lines[5])
self.spikes = int(lines[6])
self.hardness = float(lines[7])
self.aspect_ratio = float(lines[8])
self.angle = float(lines[9])
else:
raise ValueError('Unsupported .VBR version.')
def write(self, filename):
if self.version not in ['1.0', '1.5']:
raise ValueError('Version must be set to 1.0 or 1.5 before writing.')
lines = [self.magic, self.version, self.name]
if self.version == '1.5':
if self.shape is None or self.spikes is None:
raise ValueError('Shape and spikes must be set for version 1.5.')
lines.extend([self.shape, str(self.spacing), str(self.radius), str(self.spikes),
str(self.hardness), str(self.aspect_ratio), str(self.angle)])
else:
if self.shape is not None or self.spikes is not None:
raise ValueError('Shape and spikes should not be set for version 1.0.')
lines.extend([str(self.spacing), str(self.radius), str(self.hardness),
str(self.aspect_ratio), str(self.angle)])
with open(filename, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines) + '\n')
def print_properties(self):
print(f'Magic string: {self.magic}')
print(f'Version: {self.version}')
print(f'Name: {self.name}')
if self.version == '1.5':
print(f'Shape: {self.shape}')
print(f'Spacing: {self.spacing}')
print(f'Radius: {self.radius}')
if self.version == '1.5':
print(f'Spikes: {self.spikes}')
print(f'Hardness: {self.hardness}')
print(f'Aspect ratio: {self.aspect_ratio}')
print(f'Angle: {self.angle}')
5. Java Class for .VBR File Handling
The following Java class can open, decode (read), encode (write), and print the properties of a .VBR file.
import java.io.*;
import java.util.Scanner;
public class VBRFile {
private String magic = "GIMP-VBR";
private String version;
private String name;
private String shape;
private float spacing;
private float radius;
private Integer spikes;
private float hardness;
private float aspectRatio;
private float angle;
public VBRFile() {}
public VBRFile(String filename) throws IOException {
read(filename);
}
public void read(String filename) throws IOException {
try (Scanner scanner = new Scanner(new File(filename), "UTF-8")) {
String[] lines = new String[10];
int count = 0;
while (scanner.hasNextLine() && count < 10) {
lines[count++] = scanner.nextLine().trim();
}
if (count < 8 || !lines[0].equals("GIMP-VBR")) {
throw new IOException("Invalid .VBR file: Missing or incorrect magic string.");
}
this.magic = lines[0];
this.version = lines[1];
this.name = lines[2];
if (version.equals("1.0")) {
if (count != 8) {
throw new IOException("Invalid .VBR file: Incorrect number of lines for version 1.0.");
}
this.spacing = Float.parseFloat(lines[3]);
this.radius = Float.parseFloat(lines[4]);
this.hardness = Float.parseFloat(lines[5]);
this.aspectRatio = Float.parseFloat(lines[6]);
this.angle = Float.parseFloat(lines[7]);
} else if (version.equals("1.5")) {
if (count != 10) {
throw new IOException("Invalid .VBR file: Incorrect number of lines for version 1.5.");
}
this.shape = lines[3];
this.spacing = Float.parseFloat(lines[4]);
this.radius = Float.parseFloat(lines[5]);
this.spikes = Integer.parseInt(lines[6]);
this.hardness = Float.parseFloat(lines[7]);
this.aspectRatio = Float.parseFloat(lines[8]);
this.angle = Float.parseFloat(lines[9]);
} else {
throw new IOException("Unsupported .VBR version.");
}
}
}
public void write(String filename) throws IOException {
if (version == null || (!version.equals("1.0") && !version.equals("1.5"))) {
throw new IOException("Version must be set to 1.0 or 1.5 before writing.");
}
try (PrintWriter writer = new PrintWriter(new FileWriter(filename, java.nio.charset.StandardCharsets.UTF_8))) {
writer.println(magic);
writer.println(version);
writer.println(name);
if (version.equals("1.5")) {
if (shape == null || spikes == null) {
throw new IOException("Shape and spikes must be set for version 1.5.");
}
writer.println(shape);
writer.println(spacing);
writer.println(radius);
writer.println(spikes);
writer.println(hardness);
writer.println(aspectRatio);
writer.println(angle);
} else {
if (shape != null || spikes != null) {
throw new IOException("Shape and spikes should not be set for version 1.0.");
}
writer.println(spacing);
writer.println(radius);
writer.println(hardness);
writer.println(aspectRatio);
writer.println(angle);
}
}
}
public void printProperties() {
System.out.println("Magic string: " + magic);
System.out.println("Version: " + version);
System.out.println("Name: " + name);
if (version.equals("1.5")) {
System.out.println("Shape: " + shape);
}
System.out.println("Spacing: " + spacing);
System.out.println("Radius: " + radius);
if (version.equals("1.5")) {
System.out.println("Spikes: " + spikes);
}
System.out.println("Hardness: " + hardness);
System.out.println("Aspect ratio: " + aspectRatio);
System.out.println("Angle: " + angle);
}
}
6. JavaScript Class for .VBR File Handling
The following JavaScript class (intended for Node.js) can open, decode (read), encode (write), and print the properties of a .VBR file.
const fs = require('fs');
class VBRFile {
constructor(filename = null) {
this.magic = 'GIMP-VBR';
this.version = null;
this.name = null;
this.shape = null;
this.spacing = null;
this.radius = null;
this.spikes = null;
this.hardness = null;
this.aspectRatio = null;
this.angle = null;
if (filename) {
this.read(filename);
}
}
read(filename) {
const content = fs.readFileSync(filename, 'utf-8');
const lines = content.trim().split('\n');
if (lines.length < 8 || lines[0] !== 'GIMP-VBR') {
throw new Error('Invalid .VBR file: Missing or incorrect magic string.');
}
this.magic = lines[0];
this.version = lines[1];
this.name = lines[2];
if (this.version === '1.0') {
if (lines.length !== 8) {
throw new Error('Invalid .VBR file: Incorrect number of lines for version 1.0.');
}
this.spacing = parseFloat(lines[3]);
this.radius = parseFloat(lines[4]);
this.hardness = parseFloat(lines[5]);
this.aspectRatio = parseFloat(lines[6]);
this.angle = parseFloat(lines[7]);
} else if (this.version === '1.5') {
if (lines.length !== 10) {
throw new Error('Invalid .VBR file: Incorrect number of lines for version 1.5.');
}
this.shape = lines[3];
this.spacing = parseFloat(lines[4]);
this.radius = parseFloat(lines[5]);
this.spikes = parseInt(lines[6]);
this.hardness = parseFloat(lines[7]);
this.aspectRatio = parseFloat(lines[8]);
this.angle = parseFloat(lines[9]);
} else {
throw new Error('Unsupported .VBR version.');
}
}
write(filename) {
if (this.version !== '1.0' && this.version !== '1.5') {
throw new Error('Version must be set to 1.0 or 1.5 before writing.');
}
let lines = [this.magic, this.version, this.name];
if (this.version === '1.5') {
if (this.shape === null || this.spikes === null) {
throw new Error('Shape and spikes must be set for version 1.5.');
}
lines = lines.concat([this.shape, this.spacing, this.radius, this.spikes, this.hardness, this.aspectRatio, this.angle]);
} else {
if (this.shape !== null || this.spikes !== null) {
throw new Error('Shape and spikes should not be set for version 1.0.');
}
lines = lines.concat([this.spacing, this.radius, this.hardness, this.aspectRatio, this.angle]);
}
fs.writeFileSync(filename, lines.join('\n') + '\n', 'utf-8');
}
printProperties() {
console.log(`Magic string: ${this.magic}`);
console.log(`Version: ${this.version}`);
console.log(`Name: ${this.name}`);
if (this.version === '1.5') {
console.log(`Shape: ${this.shape}`);
}
console.log(`Spacing: ${this.spacing}`);
console.log(`Radius: ${this.radius}`);
if (this.version === '1.5') {
console.log(`Spikes: ${this.spikes}`);
}
console.log(`Hardness: ${this.hardness}`);
console.log(`Aspect ratio: ${this.aspectRatio}`);
console.log(`Angle: ${this.angle}`);
}
}
7. C++ Class for .VBR File Handling
The following C++ class can open, decode (read), encode (write), and print the properties of a .VBR file.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <stdexcept>
#include <sstream>
class VBRFile {
public:
std::string magic = "GIMP-VBR";
std::string version;
std::string name;
std::string shape;
float spacing;
float radius;
int spikes = -1; // -1 indicates not set
float hardness;
float aspect_ratio;
float angle;
VBRFile() {}
VBRFile(const std::string& filename) {
read(filename);
}
void read(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("Unable to open file.");
}
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line)) {
// Trim trailing whitespace, including \r if any
line.erase(std::find_if(line.rbegin(), line.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), line.end());
lines.push_back(line);
}
file.close();
if (lines.size() < 8 || lines[0] != "GIMP-VBR") {
throw std::runtime_error("Invalid .VBR file: Missing or incorrect magic string.");
}
magic = lines[0];
version = lines[1];
name = lines[2];
if (version == "1.0") {
if (lines.size() != 8) {
throw std::runtime_error("Invalid .VBR file: Incorrect number of lines for version 1.0.");
}
spacing = std::stof(lines[3]);
radius = std::stof(lines[4]);
hardness = std::stof(lines[5]);
aspect_ratio = std::stof(lines[6]);
angle = std::stof(lines[7]);
shape = "";
spikes = -1;
} else if (version == "1.5") {
if (lines.size() != 10) {
throw std::runtime_error("Invalid .VBR file: Incorrect number of lines for version 1.5.");
}
shape = lines[3];
spacing = std::stof(lines[4]);
radius = std::stof(lines[5]);
spikes = std::stoi(lines[6]);
hardness = std::stof(lines[7]);
aspect_ratio = std::stof(lines[8]);
angle = std::stof(lines[9]);
} else {
throw std::runtime_error("Unsupported .VBR version.");
}
}
void write(const std::string& filename) {
if (version != "1.0" && version != "1.5") {
throw std::runtime_error("Version must be set to 1.0 or 1.5 before writing.");
}
std::ofstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("Unable to open file for writing.");
}
file << magic << "\n";
file << version << "\n";
file << name << "\n";
if (version == "1.5") {
if (shape.empty() || spikes == -1) {
throw std::runtime_error("Shape and spikes must be set for version 1.5.");
}
file << shape << "\n";
file << spacing << "\n";
file << radius << "\n";
file << spikes << "\n";
file << hardness << "\n";
file << aspect_ratio << "\n";
file << angle << "\n";
} else {
if (!shape.empty() || spikes != -1) {
throw std::runtime_error("Shape and spikes should not be set for version 1.0.");
}
file << spacing << "\n";
file << radius << "\n";
file << hardness << "\n";
file << aspect_ratio << "\n";
file << angle << "\n";
}
file.close();
}
void print_properties() {
std::cout << "Magic string: " << magic << std::endl;
std::cout << "Version: " << version << std::endl;
std::cout << "Name: " << name << std::endl;
if (version == "1.5") {
std::cout << "Shape: " << shape << std::endl;
}
std::cout << "Spacing: " << spacing << std::endl;
std::cout << "Radius: " << radius << std::endl;
if (version == "1.5") {
std::cout << "Spikes: " << spikes << std::endl;
}
std::cout << "Hardness: " << hardness << std::endl;
std::cout << "Aspect ratio: " << aspect_ratio << std::endl;
std::cout << "Angle: " << angle << std::endl;
}
};