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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user