feat(image): preserve transparency for alpha images end to end

Skip WebP re-encoding for PNG/AVIF/BMP uploads and any bitmap that
actually contains alpha, tag transparency-capable canvas uploads with
the transparent-image tone, and detect alpha in merged layers so the
composed node keeps a transparent tone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-08 01:54:32 +08:00
parent bd327e8564
commit 07aa33d30e
4 changed files with 141 additions and 3 deletions
+22 -3
View File
@@ -309,6 +309,7 @@ func (s *DesignService) composeMergedLayer(ctx context.Context, projectID string
drawTextLayer(canvas, node, bounds, scale)
}
}
hasTransparency := mergedLayerHasTransparency(canvas)
var output bytes.Buffer
if err := png.Encode(&output, canvas); err != nil {
@@ -338,6 +339,10 @@ func (s *DesignService) composeMergedLayer(ctx context.Context, projectID string
if title == "" {
title = "Merged Layer"
}
tone := "natural"
if hasTransparency {
tone = "transparent-image"
}
return design.Node{
ID: newID(),
Type: design.NodeTypeImage,
@@ -347,11 +352,25 @@ func (s *DesignService) composeMergedLayer(ctx context.Context, projectID string
Width: bounds.Width,
Height: bounds.Height,
Content: content,
Tone: "natural",
Tone: tone,
Status: "success",
}, nil
}
func mergedLayerHasTransparency(img *image.NRGBA) bool {
bounds := img.Bounds()
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
offset := img.PixOffset(bounds.Min.X, y)
for x := bounds.Min.X; x < bounds.Max.X; x++ {
if img.Pix[offset+3] < 255 {
return true
}
offset += 4
}
}
return false
}
func boundsForLayerMerge(nodes []design.Node) layerMergeBounds {
left := math.Inf(1)
top := math.Inf(1)
@@ -446,8 +465,8 @@ func sampleLayerColor(src image.Image, srcBounds image.Rectangle, srcWidth int,
}
u = clampFloat(u, 0, 1)
v = clampFloat(v, 0, 1)
fx := u * float64(srcWidth-1)
fy := v * float64(srcHeight-1)
fx := clampFloat(u*float64(srcWidth)-0.5, 0, float64(srcWidth-1))
fy := clampFloat(v*float64(srcHeight)-0.5, 0, float64(srcHeight-1))
x0 := int(math.Floor(fx))
y0 := int(math.Floor(fy))
x1 := clampInt(x0+1, 0, srcWidth-1)