GIMP brush format is native to the GIMP image editor for storing a brush or a texture. It can be used in all Paint Tools, for example Pencil and Paintbrush. It works by repeating the brush bitmap as you move the tool. The Spacing parameter sets the distance between the brush marks as a percentage of brush width. Its default value can be set in the brush file.
You can also use GIMP to create new brushes in this format. Custom brushes can be loaded into GIMP for use in the paint tools by copying them into one of the Brush Folders - select Edit > Preferences in the menu bar, expand the Folders section and choose Brushes to see the recognized Brush Folders or to add new ones.
This page hosts a formal specification of GIMP brush file version 2 using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
"io"
)
/**
* GIMP brush format is native to the GIMP image editor for storing a brush or a texture.
* It can be used in all [Paint Tools](https://docs.gimp.org/2.10/en/gimp-tools-paint.html),
* for example Pencil and Paintbrush. It works by repeating the brush bitmap as you move
* the tool. The Spacing parameter sets the distance between the brush marks as a percentage
* of brush width. Its default value can be set in the brush file.
*
* You can also use GIMP to create new brushes in this format. Custom brushes can be loaded
* into GIMP for use in the paint tools by copying them into one of the Brush Folders -
* select **Edit** > **Preferences** in the menu bar, expand the **Folders** section
* and choose **Brushes** to see the recognized Brush Folders or to add new ones.
* @see <a href="https://github.com/GNOME/gimp/blob/441631322b/devel-docs/gbr.txt">Source</a>
*/
type GimpBrush_ColorDepth int
const (
GimpBrush_ColorDepth__Grayscale GimpBrush_ColorDepth = 1
GimpBrush_ColorDepth__Rgba GimpBrush_ColorDepth = 4
)
var values_GimpBrush_ColorDepth = map[GimpBrush_ColorDepth]struct{}{1: {}, 4: {}}
func (v GimpBrush_ColorDepth) isDefined() bool {
_, ok := values_GimpBrush_ColorDepth[v]
return ok
}
type GimpBrush struct {
LenHeader uint32
Header *GimpBrush_Header
_io *kaitai.Stream
_root *GimpBrush
_parent kaitai.Struct
_raw_Header []byte
_f_body bool
body []byte
_f_lenBody bool
lenBody int
}
func NewGimpBrush() *GimpBrush {
return &GimpBrush{
}
}
func (this GimpBrush) IO_() *kaitai.Stream {
return this._io
}
func (this *GimpBrush) Read(io *kaitai.Stream, parent kaitai.Struct, root *GimpBrush) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1, err := this._io.ReadU4be()
if err != nil {
return err
}
this.LenHeader = uint32(tmp1)
tmp2, err := this._io.ReadBytes(int(this.LenHeader - 4))
if err != nil {
return err
}
tmp2 = tmp2
this._raw_Header = tmp2
_io__raw_Header := kaitai.NewStream(bytes.NewReader(this._raw_Header))
tmp3 := NewGimpBrush_Header()
err = tmp3.Read(_io__raw_Header, this, this._root)
if err != nil {
return err
}
this.Header = tmp3
return err
}
func (this *GimpBrush) Body() (v []byte, err error) {
if (this._f_body) {
return this.body, nil
}
this._f_body = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(this.LenHeader), io.SeekStart)
if err != nil {
return nil, err
}
tmp4, err := this.LenBody()
if err != nil {
return nil, err
}
tmp5, err := this._io.ReadBytes(int(tmp4))
if err != nil {
return nil, err
}
tmp5 = tmp5
this.body = tmp5
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.body, nil
}
func (this *GimpBrush) LenBody() (v int, err error) {
if (this._f_lenBody) {
return this.lenBody, nil
}
this._f_lenBody = true
this.lenBody = int((this.Header.Width * this.Header.Height) * this.Header.BytesPerPixel)
return this.lenBody, nil
}
type GimpBrush_Bitmap struct {
Rows []*GimpBrush_Row
_io *kaitai.Stream
_root *GimpBrush
_parent kaitai.Struct
}
func NewGimpBrush_Bitmap() *GimpBrush_Bitmap {
return &GimpBrush_Bitmap{
}
}
func (this GimpBrush_Bitmap) IO_() *kaitai.Stream {
return this._io
}
func (this *GimpBrush_Bitmap) Read(io *kaitai.Stream, parent kaitai.Struct, root *GimpBrush) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0; i < int(this._root.Header.Height); i++ {
_ = i
tmp6 := NewGimpBrush_Row()
err = tmp6.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Rows = append(this.Rows, tmp6)
}
return err
}
type GimpBrush_Header struct {
Version uint32
Width uint32
Height uint32
BytesPerPixel GimpBrush_ColorDepth
Magic []byte
Spacing uint32
BrushName string
_io *kaitai.Stream
_root *GimpBrush
_parent *GimpBrush
}
func NewGimpBrush_Header() *GimpBrush_Header {
return &GimpBrush_Header{
}
}
func (this GimpBrush_Header) IO_() *kaitai.Stream {
return this._io
}
func (this *GimpBrush_Header) Read(io *kaitai.Stream, parent *GimpBrush, root *GimpBrush) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp7, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Version = uint32(tmp7)
if !(this.Version == 2) {
return kaitai.NewValidationNotEqualError(2, this.Version, this._io, "/types/header/seq/0")
}
tmp8, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Width = uint32(tmp8)
if !(this.Width >= 1) {
return kaitai.NewValidationLessThanError(1, this.Width, this._io, "/types/header/seq/1")
}
if !(this.Width <= 10000) {
return kaitai.NewValidationGreaterThanError(10000, this.Width, this._io, "/types/header/seq/1")
}
tmp9, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Height = uint32(tmp9)
if !(this.Height >= 1) {
return kaitai.NewValidationLessThanError(1, this.Height, this._io, "/types/header/seq/2")
}
if !(this.Height <= 10000) {
return kaitai.NewValidationGreaterThanError(10000, this.Height, this._io, "/types/header/seq/2")
}
tmp10, err := this._io.ReadU4be()
if err != nil {
return err
}
this.BytesPerPixel = GimpBrush_ColorDepth(tmp10)
tmp11, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp11 = tmp11
this.Magic = tmp11
if !(bytes.Equal(this.Magic, []uint8{71, 73, 77, 80})) {
return kaitai.NewValidationNotEqualError([]uint8{71, 73, 77, 80}, this.Magic, this._io, "/types/header/seq/4")
}
tmp12, err := this._io.ReadU4be()
if err != nil {
return err
}
this.Spacing = uint32(tmp12)
tmp13, err := this._io.ReadBytesFull()
if err != nil {
return err
}
tmp13 = kaitai.BytesTerminate(tmp13, 0, false)
this.BrushName = string(tmp13)
return err
}
/**
* @see <a href="https://github.com/GNOME/gimp/blob/441631322b/app/core/gimpbrush-load.c#L170">Source</a>
* @see <a href="https://github.com/GNOME/gimp/blob/441631322b/app/core/gimpbrush-header.h#L24">Source</a>
*/
/**
* @see <a href="https://github.com/GNOME/gimp/blob/441631322b/app/core/gimpbrush-load.c#L177">Source</a>
* @see <a href="https://github.com/GNOME/gimp/blob/441631322b/app/core/gimpbrush-header.h#L24">Source</a>
*/
/**
* Default spacing to be used for brush. Percentage of brush width.
*/
type GimpBrush_Row struct {
Pixels []kaitai.Struct
_io *kaitai.Stream
_root *GimpBrush
_parent *GimpBrush_Bitmap
}
func NewGimpBrush_Row() *GimpBrush_Row {
return &GimpBrush_Row{
}
}
func (this GimpBrush_Row) IO_() *kaitai.Stream {
return this._io
}
func (this *GimpBrush_Row) Read(io *kaitai.Stream, parent *GimpBrush_Bitmap, root *GimpBrush) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0; i < int(this._root.Header.Width); i++ {
_ = i
switch (this._root.Header.BytesPerPixel) {
case GimpBrush_ColorDepth__Grayscale:
tmp14 := NewGimpBrush_Row_PixelGray()
err = tmp14.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Pixels = append(this.Pixels, tmp14)
case GimpBrush_ColorDepth__Rgba:
tmp15 := NewGimpBrush_Row_PixelRgba()
err = tmp15.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Pixels = append(this.Pixels, tmp15)
}
}
return err
}
type GimpBrush_Row_PixelGray struct {
Gray uint8
_io *kaitai.Stream
_root *GimpBrush
_parent *GimpBrush_Row
_f_alpha bool
alpha uint8
_f_blue bool
blue int8
_f_green bool
green int8
_f_red bool
red int8
}
func NewGimpBrush_Row_PixelGray() *GimpBrush_Row_PixelGray {
return &GimpBrush_Row_PixelGray{
}
}
func (this GimpBrush_Row_PixelGray) IO_() *kaitai.Stream {
return this._io
}
func (this *GimpBrush_Row_PixelGray) Read(io *kaitai.Stream, parent *GimpBrush_Row, root *GimpBrush) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp16, err := this._io.ReadU1()
if err != nil {
return err
}
this.Gray = tmp16
return err
}
func (this *GimpBrush_Row_PixelGray) Alpha() (v uint8, err error) {
if (this._f_alpha) {
return this.alpha, nil
}
this._f_alpha = true
this.alpha = uint8(this.Gray)
return this.alpha, nil
}
func (this *GimpBrush_Row_PixelGray) Blue() (v int8, err error) {
if (this._f_blue) {
return this.blue, nil
}
this._f_blue = true
this.blue = int8(0)
return this.blue, nil
}
func (this *GimpBrush_Row_PixelGray) Green() (v int8, err error) {
if (this._f_green) {
return this.green, nil
}
this._f_green = true
this.green = int8(0)
return this.green, nil
}
func (this *GimpBrush_Row_PixelGray) Red() (v int8, err error) {
if (this._f_red) {
return this.red, nil
}
this._f_red = true
this.red = int8(0)
return this.red, nil
}
type GimpBrush_Row_PixelRgba struct {
Red uint8
Green uint8
Blue uint8
Alpha uint8
_io *kaitai.Stream
_root *GimpBrush
_parent *GimpBrush_Row
}
func NewGimpBrush_Row_PixelRgba() *GimpBrush_Row_PixelRgba {
return &GimpBrush_Row_PixelRgba{
}
}
func (this GimpBrush_Row_PixelRgba) IO_() *kaitai.Stream {
return this._io
}
func (this *GimpBrush_Row_PixelRgba) Read(io *kaitai.Stream, parent *GimpBrush_Row, root *GimpBrush) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp17, err := this._io.ReadU1()
if err != nil {
return err
}
this.Red = tmp17
tmp18, err := this._io.ReadU1()
if err != nil {
return err
}
this.Green = tmp18
tmp19, err := this._io.ReadU1()
if err != nil {
return err
}
this.Blue = tmp19
tmp20, err := this._io.ReadU1()
if err != nil {
return err
}
this.Alpha = tmp20
return err
}