Matte semi-transparent pixels onto garment bgColor so Printful underbase has no grey halo (ZLY-1512)
Printful DTG binary alpha (ZLY-1512, often called “bitmapping”) fixes a print defect on garments fulfilled by Printful’s direct-to-garment (DTG) process.
Printful keeps our PNG transparency and lays down a white underbase only where there is ink. That underbase opacity follows pixel coverage — so pixels with partial alpha get a partial underbase and print as a white/grey halo or blurry fade along soft edges.
The fix is not a plain 1-bit threshold (which makes jagged edges). Instead we matte only semi-transparent pixels onto the product’s known garment color (bgColor), then force alpha to fully transparent or fully opaque. Soft edges stay smooth because antialiasing is baked into the RGB color.
This applies to apparel / DTG (transparency + underbase). It does not help posters on white paper — there is no fabric underbase problem there.
| Approach | Why we rejected / limited it |
|---|---|
| Flatten entire image onto garment color | Prints a full opaque rectangle — silhouette lost |
| Plain 1-bit alpha threshold | Jagged edges; throws away antialiasing |
| Same treatment for posters | Not needed — white paper, no DTG underbase |
Core helper: binarizeAlpha in packages/merch/img-gen/src/img-processing/binarize-alpha.ts.
For each pixel (alpha 0–255), with defaults lowerEpsilon = 3, upperEpsilon = 252:
| Alpha | Result |
|---|---|
α ≤ 3 | Fully transparent (α = 0) — fabric shows, no ink |
3 < α < 252 | Blend out = α·C + (1-α)·bg, then α = 255 |
α ≥ 252 | Fully opaque (α = 255) — RGB unchanged |
If bgColor is missing/empty, or the image has no alpha channel, the function no-ops (returns the buffer unchanged). Guessing a background would bake a wrong fringe into the print.
Binarization is wired at the source of soft alpha and again on the print file:
| Stage | Hook | logLabel |
|---|---|---|
| After the design mask (generation) | applyTemplateMask(..., bgColor?) → applyMaskToImage | after-alpha-mask |
| After logo/text overlay | compositeOverlay(..., bgColor?) | after-overlay |
| Final print file (before DPI / upload) | upscalePrintArt → binarizeAlpha | print-file |
Call sites that pass catalog bgColor (or flattenBgColor on generation):
process-ai-image / generate-ai-art)render route, render-garment, seller-store, admin render-preview)upscale-print-art, admin upscale, dispatch)Not wired (no catalog garment color / not a print path): benchmark, simulation, design-request.
Pre-generation template flatten (ensureOpaqueImage / flattenBgColor on the AI template) already existed and is unchanged — that is a different step (make the template opaque before the model runs).
bgColor RequirementApparel catalog products need a correct background / garment color in admin (catalog product bgColor). That value is the fabric color used for matting.
rgb(), named).| Package / file | Role |
|---|---|
@zooly/merch-img-gen → binarize-alpha.ts | Core matte + binary alpha |
remove-background-corners.ts | Calls binarize after mask |
composite-overlay.ts | Calls binarize after overlay |
packages/merch/srv/src/upscale-print-art.ts | Final print-file pass |
packages/merch-fulfillment/srv/src/dispatch.ts | Passes catalogProduct.bgColor into upscale before Printful |
bgColor should log / no-op rather than invent a color.