Back to Cli Anything

GIMP: Project-Specific Analysis & SOP

gimp/agent-harness/GIMP.md

latest12.7 KB
Original Source

GIMP: Project-Specific Analysis & SOP

Architecture Summary

GIMP (GNU Image Manipulation Program) is a GTK-based raster image editor built on the GEGL (Generic Graphics Library) processing engine and Babl color management.

┌──────────────────────────────────────────────┐
│                  GIMP GUI                    │
│  ┌──────────┐ ┌──────────┐ ┌─────────────┐  │
│  │  Canvas   │ │  Layers  │ │   Filters   │  │
│  │  (GTK)    │ │  (GTK)   │ │   (GTK)     │  │
│  └────┬──────┘ └────┬─────┘ └──────┬──────┘  │
│       │             │              │          │
│  ┌────┴─────────────┴──────────────┴───────┐ │
│  │       PDB (Procedure Database)          │ │
│  │  500+ registered procedures for all     │ │
│  │  image operations, filters, I/O         │ │
│  └─────────────────┬───────────────────────┘ │
│                    │                          │
│  ┌─────────────────┴───────────────────────┐ │
│  │       GEGL Processing Engine            │ │
│  │  DAG-based image processing pipeline    │ │
│  │  70+ built-in operations               │ │
│  └─────────────────┬───────────────────────┘ │
└────────────────────┼─────────────────────────┘
                     │
         ┌───────────┴──────────┐
         │  Babl (color mgmt)   │
         │  + GEGL operations   │
         │  + File format I/O   │
         └──────────────────────┘

CLI Strategy: Pillow + External Tools

Unlike Shotcut (which manipulates XML project files), GIMP's native .xcf format is a complex binary format. Our strategy:

  1. Pillow — Python's standard imaging library. Handles image I/O (PNG, JPEG, TIFF, BMP, GIF, WebP, etc.), pixel manipulation, basic filters, color adjustments, drawing, and compositing. This is our primary engine.
  2. GEGL CLI — If available, use gegl command for advanced operations.
  3. GIMP batch mode — If gimp is installed, use gimp -i -b for XCF operations and advanced filters via Script-Fu/Python-Fu.

Why Not XCF Directly?

XCF is a tile-based binary format with compression, layers, channels, paths, and GEGL filter graphs. Parsing it from scratch is extremely complex (5000+ lines of C in GIMP's xcf-load.c). Instead:

  • For new projects, we build layer stacks in memory using Pillow
  • For XCF import/export, we delegate to GIMP batch mode if available
  • Our "project file" is a JSON manifest tracking layers, operations, and history

The Project Format (.gimp-cli.json)

Since we can't easily manipulate XCF directly, we use a JSON project format:

json
{
  "version": "1.0",
  "name": "my_project",
  "canvas": {
    "width": 1920,
    "height": 1080,
    "color_mode": "RGB",
    "background": "#ffffff",
    "dpi": 300
  },
  "layers": [
    {
      "id": 0,
      "name": "Background",
      "type": "image",
      "source": "/path/to/image.png",
      "visible": true,
      "opacity": 1.0,
      "blend_mode": "normal",
      "offset_x": 0,
      "offset_y": 0,
      "filters": [
        {"name": "brightness", "params": {"factor": 1.2}},
        {"name": "gaussian_blur", "params": {"radius": 3}}
      ]
    },
    {
      "id": 1,
      "name": "Text Layer",
      "type": "text",
      "text": "Hello World",
      "font": "Arial",
      "font_size": 48,
      "color": "#000000",
      "visible": true,
      "opacity": 0.8,
      "blend_mode": "normal",
      "offset_x": 100,
      "offset_y": 50,
      "filters": []
    }
  ],
  "selection": null,
  "guides": [],
  "metadata": {}
}

Core Operations via Pillow

Image I/O

OperationPillow API
Open imageImage.open(path)
Save imageimage.save(path, format)
Create blankImage.new(mode, (w,h), color)
Convert modeimage.convert("RGB"/"L"/"RGBA")
Resizeimage.resize((w,h), resample)
Cropimage.crop((l, t, r, b))
Rotateimage.rotate(angle, expand=True)
Flipimage.transpose(Image.FLIP_LEFT_RIGHT)

Filters & Adjustments

OperationPillow API
BrightnessImageEnhance.Brightness(img).enhance(factor)
ContrastImageEnhance.Contrast(img).enhance(factor)
SaturationImageEnhance.Color(img).enhance(factor)
SharpnessImageEnhance.Sharpness(img).enhance(factor)
Gaussian blurimage.filter(ImageFilter.GaussianBlur(radius))
Box blurimage.filter(ImageFilter.BoxBlur(radius))
Unsharp maskimage.filter(ImageFilter.UnsharpMask(radius, percent, threshold))
Find edgesimage.filter(ImageFilter.FIND_EDGES)
Embossimage.filter(ImageFilter.EMBOSS)
Contourimage.filter(ImageFilter.CONTOUR)
Detailimage.filter(ImageFilter.DETAIL)
Smoothimage.filter(ImageFilter.SMOOTH_MORE)
GrayscaleImageOps.grayscale(image)
InvertImageOps.invert(image)
PosterizeImageOps.posterize(image, bits)
SolarizeImageOps.solarize(image, threshold)
AutocontrastImageOps.autocontrast(image)
EqualizeImageOps.equalize(image)
SepiaCustom kernel via ImageOps.colorize()

Compositing & Drawing

OperationPillow API
Paste layerImage.alpha_composite(base, overlay)
Blend modesCustom implementations (multiply, screen, overlay, etc.)
Draw rectangleImageDraw.rectangle(xy, fill, outline)
Draw ellipseImageDraw.ellipse(xy, fill, outline)
Draw textImageDraw.text(xy, text, font, fill)
Draw lineImageDraw.line(xy, fill, width)

Blend Modes

Pillow doesn't natively support Photoshop/GIMP blend modes. We implement the most common ones using NumPy-style pixel math:

ModeFormula
Normaltop (with alpha compositing)
Multiplybase * top / 255
Screen255 - (255-base)*(255-top)/255
Overlayif base < 128: 2*base*top/255 else: 255 - 2*(255-base)*(255-top)/255
Soft LightPhotoshop-style formula
Hard LightOverlay with base/top swapped
Differenceabs(base - top)
Darkenmin(base, top)
Lightenmax(base, top)
Color Dodgebase / (255 - top) * 255
Color Burn255 - (255-base) / top * 255

Command Map: GUI Action -> CLI Command

GUI ActionCLI Command
File -> Newproject new --width 1920 --height 1080 [--mode RGB]
File -> Openproject open <path>
File -> Saveproject save [path]
File -> Export Asexport render <output> [--format png] [--quality 95]
Image -> Canvas Sizecanvas resize --width W --height H
Image -> Scale Imagecanvas scale --width W --height H
Image -> Crop to Selectioncanvas crop --left L --top T --right R --bottom B
Image -> Mode -> RGBcanvas mode RGB
Layer -> New Layerlayer new [--name "Layer"] [--width W] [--height H]
Layer -> Duplicatelayer duplicate <index>
Layer -> Deletelayer remove <index>
Layer -> Flatten Imagelayer flatten
Layer -> Merge Downlayer merge-down <index>
Move layerlayer move <index> --to <position>
Set layer opacitylayer set <index> opacity <value>
Set blend modelayer set <index> mode <mode>
Toggle visibilitylayer set <index> visible <true/false>
Layer -> Add from Filelayer add-from-file <path> [--name N] [--position P]
Filters -> Blur -> Gaussianfilter add gaussian_blur --layer L --param radius=5
Colors -> Brightness-Contrastfilter add brightness --layer L --param factor=1.2
Colors -> Hue-Saturationfilter add saturation --layer L --param factor=1.3
Colors -> Invertfilter add invert --layer L
Draw text on layerdraw text --layer L --text "Hi" --x 10 --y 10 --font Arial --size 24
Draw rectangledraw rect --layer L --x1 0 --y1 0 --x2 100 --y2 100 --fill "#ff0000"
View layerslayer list
View project infoproject info
Undosession undo
Redosession redo

Filter Registry

Image Adjustments

CLI NamePillow ImplementationKey Parameters
brightnessImageEnhance.Brightnessfactor (1.0 = neutral, >1 = brighter)
contrastImageEnhance.Contrastfactor (1.0 = neutral)
saturationImageEnhance.Colorfactor (1.0 = neutral, 0 = grayscale)
sharpnessImageEnhance.Sharpnessfactor (1.0 = neutral, >1 = sharper)
autocontrastImageOps.autocontrastcutoff (0-49, percent to clip)
equalizeImageOps.equalize(no params)
invertImageOps.invert(no params)
posterizeImageOps.posterizebits (1-8)
solarizeImageOps.solarizethreshold (0-255)
grayscaleImageOps.grayscale(no params)
sepiaCustom colorizestrength (0.0-1.0)

Blur & Sharpen

CLI NamePillow ImplementationKey Parameters
gaussian_blurImageFilter.GaussianBlurradius (pixels)
box_blurImageFilter.BoxBlurradius (pixels)
unsharp_maskImageFilter.UnsharpMaskradius, percent, threshold
smoothImageFilter.SMOOTH_MORE(no params)

Stylize

CLI NamePillow ImplementationKey Parameters
find_edgesImageFilter.FIND_EDGES(no params)
embossImageFilter.EMBOSS(no params)
contourImageFilter.CONTOUR(no params)
detailImageFilter.DETAIL(no params)

Transform

CLI NamePillow ImplementationKey Parameters
rotateImage.rotateangle (degrees), expand (bool)
flip_hImage.transpose(FLIP_LEFT_RIGHT)(no params)
flip_vImage.transpose(FLIP_TOP_BOTTOM)(no params)
resizeImage.resizewidth, height, resample (nearest/bilinear/bicubic/lanczos)
cropImage.cropleft, top, right, bottom

Export Formats

FormatExtensionQuality ParamNotes
PNG.pngcompress_level (0-9)Lossless, supports alpha
JPEG.jpg/.jpegquality (1-95)Lossy, no alpha
WebP.webpquality (1-100)Both lossy/lossless
TIFF.tiffcompression (none/lzw/jpeg)Professional
BMP.bmp(none)Uncompressed
GIF.gif(none)256 colors max
ICO.ico(none)Icon format
PDF.pdf(none)Multi-page possible

Rendering Pipeline

For GIMP CLI, "rendering" means flattening the layer stack with all filters applied and exporting to a target format.

Pipeline Steps:

  1. Start with canvas (background color or transparent)
  2. For each visible layer (bottom to top): a. Load/create the layer content b. Apply all layer filters in order c. Position at layer offset d. Composite onto canvas using blend mode and opacity
  3. Export final composited image

Rendering Gap Assessment: Medium

  • Most operations (resize, crop, filters, compositing) work via Pillow directly
  • Advanced GEGL operations (high-pass filter, wavelet decompose) not available
  • No XCF round-trip without GIMP installed
  • Blend modes require custom implementation but are mathematically straightforward

Test Coverage Plan

  1. Unit tests (test_core.py): Synthetic data, no real images needed

    • Project create/open/save/info
    • Layer add/remove/reorder/properties
    • Filter application and parameter validation
    • Canvas operations (resize, scale, crop, mode conversion)
    • Session undo/redo
    • JSON project serialization/deserialization
  2. E2E tests (test_full_e2e.py): Real images

    • Full workflow: create project, add layers, apply filters, export
    • Format conversion (PNG->JPEG, etc.)
    • Blend mode compositing verification
    • Filter effect pixel-level verification
    • Multi-layer compositing
    • Text rendering
    • CLI subprocess invocation