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