The drawing module is the core orchestrator of the dataset generation pipeline. For each image it callsDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ollm/opencomic-ai-training/llms.txt
Use this file to discover all available pages before exploring further.
options.setCurrentImageRand() and options.randomize() to produce a deterministic randomised config, then communicates with a running Krita instance via the krita module to create a new document, add the required group and paint layers, execute every configured drawing type (lineart, colorize-mask, background, texture, paint, dots, lines, halftone), export the clean render, and finally hands off to the output and sharp degradation modules to produce and save each lossy variant. The whole cycle is repeated for every entry in degradations[] and for every degraded-image-per-clean-image count requested by the config.
drawing.generateImage()
Generates one complete clean-plus-degraded image pair (or set of pairs) for the given 1-based image index.
The 1-based index of the clean image to generate. Passed directly to
options.setCurrentImageRand() to seed the per-image RNG and used to build
output file names.Callback invoked after each degraded variant is successfully saved to disk.
Receives the current clean image index and the running total count of degraded
images written so far (across all images in the run). Used by the CLI progress
bar in
index.mts.Execution steps
The function executes the following sequence every time it is called: 1. Initialise per-image stateif-condition values from the previous image are wiped, and the brush state is reset. A deep clone of the loaded Options is taken so that randomisation does not mutate the master config.
2. Randomise the config for this image
options.randomize() is called separately on drawings (depth 1, so only the list entries are resolved, not their full sub-trees), base, and postProcessing. Dimensions are snapped to a multiple grid if the config specifies one, then scaled by base.scale.
3. Create the Krita document
base.background).
4. Add group and paint layers
Depending on imageOptions.drawings.type:
'singlelayered'— one group layer'general'and one set of layers for area'all'.'3layered'— one group layer'general'and three sets of layers for areas'up','middle', and'down'.
- A
paintlayernamedopencomic:lineart:<area>inside the group. - A colorize mask renamed to
opencomic:colorize-mask:<area>. - Optionally a
opencomic:lineart-texture:<area>layer if'lineart-texture'is in the drawing list. - Optionally a
opencomic:lineart-random:<area>layer if'lineart-random'is in the drawing list.
krita.send().
5. Draw each layer
For every area, processLayer() iterates imageOptions.drawings.list and dispatches each drawing type:
| Drawing type | Module called |
|---|---|
'lineart' | lineart.draw(imageOptions, drawing, area) |
'lineart-texture' | lineart.draw(imageOptions, drawing, area, 'lineart-texture') |
'lineart-random' | lineart.draw(imageOptions, drawing, area, 'lineart-random') |
'colorize-mask' | colorizeMask.colorize(imageOptions, drawing, area) |
'paint' | paint.draw(imageOptions, drawing, area, draws) |
'dots' | dots.draw(imageOptions, drawing, area, groupLayer, draws) |
'circles' | dots.circles(imageOptions, drawing, area, groupLayer, draws) |
'circles-with-dot' | dots.circlesWithDot(imageOptions, drawing, area, groupLayer, draws) |
'parallel-lines' | lines.parallel(imageOptions, drawing, area, groupLayer, draws) |
'grid' | lines.grid(imageOptions, drawing, area, groupLayer, draws) |
'texture' | texture.add(imageOptions, drawing) |
'gradient' | gradient.add(imageOptions, drawing) |
The types
'circles', 'circles-with-dot', 'parallel-lines', 'grid',
and 'gradient' are recognised by the runtime dispatch switch in
drawing.mts but are not part of the TypeScript Drawing union type in
types.mts. They can be used in YAML config files and will be dispatched
correctly at runtime.prob evaluates to false for the current image it is skipped entirely. If a drawing has a skipIf list and any of its entries appear in the list of already-completed drawing types, it is also skipped.
6. Process degradations
processDegradations() is called with the completed layers map. For every degradation in imageOptions.degradations and every repeat in 1..degradedImagesPerCleanImage:
a. inKrita operations — any halftone filter layers are configured via halftone.config() and applied to the Krita document. krita.refreshProjection() forces a full re-render.
b. Clean export — output.clean() exports the current document state as a PNG buffer (the “clean” half of the pair).
c. Degraded export — halftone layers are re-applied in their visible state and output.degraded() exports the degraded document state.
d. inNode operations — the clean and degraded buffers are each passed through the enabled sharp operations in declaration order:
inNode.type | Sharp operation |
|---|---|
'resize' | sharp.resize() |
'resize-blur' | sharp.resizeBlur() |
'blur' | sharp.blur() |
'rotate' | sharp.rotate() on both clean and degraded |
'jpeg' | sharp.jpeg() on degraded only |
'webp' | sharp.webp() on degraded only |
'avif' | sharp.avif() on degraded only |
'jxl' | sharp.jxl() on degraded only |
both: true are applied to the clean buffer as well as the degraded one.
e. Save — if checkSizes passes (or is disabled), the clean buffer, degraded buffer, and a JSON snapshot of all options/configs are written to the three output directories configured in degradation.output.
7. Close / retry logic
generateImage() itself does not close the Krita document between images — the document is reused and resized at the start of each call. Error handling lives in index.mts: if generateImage() throws, the caller decrements image and retries, ensuring no index is permanently skipped due to a transient Krita error.
krita module
Thekrita module (src/krita.mts) is the low-level bridge between Node.js and a running Krita instance. It spawns Krita as a child process, connects via a WebSocket to the kra-remote plugin running inside Krita, and exposes promise-based wrappers for every plugin command.
krita.init()
Launches Krita, waits for the process to signal readiness, connects the WebSocket, and sends an initial edit_layer command to verify the connection.
Absolute path to the Krita executable. When an empty string is passed, Krita
is assumed to already be running and only the WebSocket connection step is
performed.
krita.send()
Sends a command string to the kra-remote WebSocket plugin and returns a promise that resolves with the plugin’s response payload.
A colon-delimited command string. The part before the first colon is the
command key; everything after is the JSON payload. For example:
'add_layer:{"name":"opencomic:lineart:all","type":"paintlayer"}'.layer_image, filter_properties, layers, resources, etc.) are parsed automatically; all others return the raw string payload.
krita.getAllResources()
Loads all available brush presets, gradients, patterns, and palettes from Krita and stores them in the module-level resourcesData object.
krita.init() before any drawing code that references krita.presets or krita.gradients. The brush presets are parsed through parseResources(), which assigns each preset its PresetCategory and letter prefix.
krita.filterProperties()
Retrieves the current property values for a named Krita filter.
An object with at least a
filter key naming the filter to inspect (e.g.
{ filter: 'halftone' }). Returns an object with filter and properties
fields.krita.checkRestartKrita()
Increments an internal counter and, when it exceeds restartEvery, kills the current Krita process and calls krita.init() again with a fresh instance.
The Krita executable path passed back to
krita.init() on restart.Restart Krita after this many calls. Pass
0 to disable automatic restarts.krita.setReject()
Sets the promise rejection handler that is called when the Krita child process closes unexpectedly during an active image generation.
The rejection callback for the currently active image’s promise. Called with
the process exit code if Krita closes while a generation is in flight.
krita.presets
A Record<string, any> keyed by preset name containing each brush preset’s metadata (name, filename, category, and letter prefix). Populated after getAllResources().
krita.gradients
The raw gradients object returned by Krita’s resource API. Populated after getAllResources().
BRUSHES_MAX_SIZE
A Record<string, number> mapping brush preset names to their recommended maximum brush size in pixels. The krita.editView() function clamps brushSize to this value before sending it to Krita, preventing brushes from being used at sizes that produce undesirable or broken results.
BRUSHES_MIN_SIZE
A Record<string, number> mapping brush preset names to their recommended minimum brush size in pixels. Brushes with hard pixel edges at small sizes are listed here; editView() clamps upward to this minimum.
Brushes listed in
BRUSHES_MIN_SIZE are no longer excluded from random
selection; they are only size-clamped upward when brushSize would otherwise
fall below the listed minimum.