DDirectorWikiDirector & Lingo Encyclopedia

Ink Modes

All Director sprite ink modes with numeric IDs, pixel semantics, hit-testing implications, and imaging Lingo ink usage.

What ink is

Ink is per-sprite operation state: it changes how the sprite's source pixels combine with the pixels behind it at composite time. Ink is not baked into the cast member; the same member can render with different inks in different sprites simultaneously. (The native renderer keeps source pixels, coverage, key state, ink id, and blend as separate inputs until the final compositing callback; see Native rendering.)

lingo
sprite(5).ink = 8        -- Matte
sprite(5).ink = 36       -- Background Transparent
image.copyPixels(src, dstRect, srcRect, [#ink: 8, #blendLevel: 128])

The ink table

IDNamePixel behavior
0CopySource drawn opaquely (32-bit alpha respected when useAlpha). Fastest. Default.
1TransparentLight/white pixels become transparent (palette-era rule: background-color pixels of 1-bit art).
2ReverseSource inverts overlapping destination pixels; white source pixels are transparent. XOR-family.
3GhostLike Reverse but only where source overlaps dark destination; elsewhere transparent.
4Not CopyInvert source first, then Copy.
5Not TransparentInvert source, then Transparent.
6Not ReverseInvert source, then Reverse.
7Not GhostInvert source, then Ghost.
8MatteRemoves the white region connected to the bitmap's bounding edge (the "matte"); interior artwork opaque. Changes the mouse hit area to visible pixels.
9MaskUses the NEXT cast member slot as a mask: black opaque, white transparent, grays partial.
32BlendUniform opacity from the sprite blend percentage.
33Add Pindst + src per channel, clamped at 255.
34Adddst + src per channel, wrapping (mod 256).
35Subtract Pindst - src per channel, clamped at 0.
36Background TransparentPixels matching the sprite's backColor key become transparent; rest opaque.
37Lightestmax(dst, src) per channel.
38Subtractdst - src per channel, wrapping.
39Darkestmin(dst, src) per channel.
40LightenBackground acts as brightness filter; foreColor tints ("color filter" family).
41DarkenBackground darkens through the source; foreColor added like colored light ("color filter" family).

IDs 10-31 are unused gaps from the QuickDraw transfer-mode heritage. The imaging Lingo copyPixels #ink parameter accepts the same IDs/symbols.

Semantics that content depends on

Matte (8)

  • The matte is computed from the source bitmap: white pixels connected to the border are removed; enclosed white areas stay opaque. It is not "all white is transparent".
  • Matte state is coverage data derived from the member, cached per member, and applied at composite time. Native evidence: 4-bit and 8-bit sources have dedicated coverage-converter paths where stored bytes act as coverage, not visible paint (details).
  • Matte changes hit testing: mouse events trigger on the visible area only.
  • createMatte() (imaging Lingo) builds the same coverage for an image object.

Background Transparent (36)

  • The transparent key is the sprite's backColor (default white). It is a keying rule, not a recoloring rule: surviving pixels keep their source colors.
  • For indexed (paletted) sources, the key comparison happens against the resolved key color; palette index 0 acts as the default key while the source retains index provenance (native finding).
  • Anti-aliased edges against white produce halos; that is authentic behavior, not an emulator bug.

Blend (32) and the blend property

  • blend (0-100) maps to an internal 0-256 alpha scale (native formula: src*alpha + dst*(256-alpha) fixed-point).
  • Blend combines with other inks: a Matte sprite at blend = 50 composites its matte-covered pixels at half opacity. The native arithmetic-ink callbacks ignore blend (Add/Subtract/Darkest/Lightest write RGB results directly); sprite blend does not attenuate arithmetic inks.

Arithmetic family (33/34/35/37/38/39)

  • Pin variants saturate; non-pin variants wrap modulo 256, producing visible wraparound artifacts that some content exploits deliberately.
  • Operate on RGB channels independently. For 8-bit grayscale sources with index provenance, the native path feeds storage bytes as luminance operands, not palette-visible RGB.

Mask (9)

  • The mask is the cast member in the next slot after the sprite's member. Black = opaque, white = transparent, gray = partial.
  • For low-depth masks Director interprets grayscale-style coverage regardless of the display palette.
  • Partial mask coverage uses its own blend formula distinct from uniform sprite blend (native: shifted products with coverage xor 0xFF as the destination complement).
  • Imaging Lingo #maskImage / createMask() provide the same per-blit.

Reverse/Ghost/Not family (2-7)

QuickDraw XOR heritage; on direct-color displays Director approximates the classic 1-bit behavior channel-wise. Rarely load-bearing in games except for selection rectangles and "invert" highlights.

Lighten/Darken (40/41)

Color-filter inks: source RGB is scaled/offset by backColor/foreColor acting as filter parameters. Native path applies a fixed-point scale and offset and writes the result with the source alpha byte (no alpha-over compositing of the filtered result).

Ink and hit testing

InkMouse hit area (documented)
MatteVisible (matte-covered) pixels only
All othersBounding box

The documented rule is coarse; per-title behavior around 32-bit alpha and alphaThreshold adds nuance (alphaThreshold gates hits on 32-bit members with useAlpha). Emulators should not make Background Transparent or Blend sprites click-through just because pixels ended up transparent; that diverges from native behavior. Open questions and native probe status: Input and hit testing.

Performance notes

Copy is the fast path (opaque blit). Matte and Mask cost matte/mask coverage generation and per-pixel tests; classic advice was to avoid them on large or animated sprites, so shipped content uses Background Transparent heavily (cheaper: single key compare). Emulators can implement everything as shaders, but must preserve the coverage/key semantics above rather than converting sources to premultiplied RGBA at load time; that early flattening is the root cause of a whole family of emulator rendering bugs (wrong halos, vanished white UI, broken arithmetic inks).