fix: luminance-masked screen blend + cache bypass param

- Screen blend now uses brightness mask from the design so only
  non-dark pixels show through. Prevents visible dark rectangle
  when design has its own dark background (e.g. DefectFi tee).
- Add ?fresh=1 query param to /mockup endpoint for cache bypass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-24 15:46:26 -08:00
parent 067e14ed0c
commit eda9a0df23
1 changed files with 16 additions and 13 deletions

View File

@ -86,7 +86,7 @@ async def get_design_image(slug: str):
@router.get("/{slug}/mockup")
async def get_design_mockup(slug: str, type: str = "shirt"):
async def get_design_mockup(slug: str, type: str = "shirt", fresh: bool = False):
"""Serve the design composited onto a product mockup template.
For Printful-provider designs: fetches photorealistic mockup from
@ -95,9 +95,10 @@ async def get_design_mockup(slug: str, type: str = "shirt"):
Query params:
type: Product type "shirt", "sticker", or "print" (default: shirt)
fresh: If true, bypass cache and regenerate mockup
"""
cache_key = (slug, type)
if cache_key in _mockup_cache:
if not fresh and cache_key in _mockup_cache:
return StreamingResponse(
io.BytesIO(_mockup_cache[cache_key]),
media_type="image/png",
@ -225,10 +226,11 @@ async def _pillow_mockup(slug: str, type: str) -> StreamingResponse:
blend_mode = template_config.get("blend", "paste")
if blend_mode == "screen":
# Screen blend: makes light designs appear printed on dark fabric.
# Formula: 1 - (1-base)(1-overlay). Preserves fabric texture.
# Screen blend for light designs on dark fabric.
# We use a brightness-based mask so only non-dark pixels from
# the design show through, preventing a visible dark rectangle
# when the design has its own dark background.
design_rgb = design_resized.convert("RGB")
alpha = design_resized.split()[3] if design_resized.mode == "RGBA" else None
# Extract the region under the design
region = canvas.crop((dx, dy, dx + dw, dy + dh))
@ -236,14 +238,15 @@ async def _pillow_mockup(slug: str, type: str) -> StreamingResponse:
# Screen blend the design onto the fabric region
blended = ImageChops.screen(region, design_rgb)
# If design has alpha, composite using it as mask
if alpha:
# Convert alpha to 3-channel for masking
region_arr = region.copy()
blended_with_alpha = Image.composite(blended, region_arr, alpha)
canvas.paste(blended_with_alpha, (dx, dy))
else:
canvas.paste(blended, (dx, dy))
# Create a luminance mask from the design — only bright pixels blend in.
# This prevents the design's dark background from creating a visible box.
lum = design_rgb.convert("L")
# Boost contrast so only clearly visible parts of the design show
lum = lum.point(lambda p: min(255, int(p * 1.5)))
# Composite: use luminance as mask (bright pixels = show blended, dark = keep original)
result = Image.composite(blended, region, lum)
canvas.paste(result, (dx, dy))
else:
# Direct paste — for stickers/prints where design goes on a light surface
if design_resized.mode == "RGBA":