An engagement based washing machine that spins you round and round!
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Apply circular mask to prevent avatar shrinking on rotation

Brooke fd703788 2e1c35f4

+52 -5
+52 -5
internal/image/processor.go
··· 45 45 return data, nil 46 46 } 47 47 48 - // RotateImage rotates an image by the specified degrees 48 + // RotateImage rotates an image by the specified degrees while maintaining a square canvas 49 + // with a circular mask in the center 49 50 func (p *Processor) RotateImage(data []byte, degrees float64) ([]byte, string, error) { 50 51 // Decode the image 51 52 img, format, err := image.Decode(bytes.NewReader(data)) ··· 53 54 return nil, "", fmt.Errorf("failed to decode image: %w", err) 54 55 } 55 56 56 - // Rotate the image 57 + bounds := img.Bounds() 58 + width := bounds.Dx() 59 + height := bounds.Dy() 60 + 61 + // Ensure we're working with a square - use the smaller dimension 62 + size := width 63 + if height < size { 64 + size = height 65 + } 66 + 67 + // Crop to square if needed 68 + if width != height { 69 + img = imaging.CropCenter(img, size, size) 70 + } 71 + 72 + // Calculate the circle radius (we want a circle that fits in the square) 73 + // Use a slightly smaller radius to avoid edge artifacts 74 + radius := float64(size) / 2.0 75 + 76 + // Rotate the image around its center 77 + // Use a larger canvas to prevent cropping during rotation 57 78 rotated := imaging.Rotate(img, degrees, color.Transparent) 58 79 80 + // Crop back to original size centered 81 + rotated = imaging.CropCenter(rotated, size, size) 82 + 83 + // Create a new image with circular mask 84 + masked := image.NewRGBA(image.Rect(0, 0, size, size)) 85 + 86 + // Apply circular mask 87 + centerX := float64(size) / 2.0 88 + centerY := float64(size) / 2.0 89 + 90 + for y := 0; y < size; y++ { 91 + for x := 0; x < size; x++ { 92 + // Calculate distance from center 93 + dx := float64(x) - centerX 94 + dy := float64(y) - centerY 95 + distance := dx*dx + dy*dy 96 + 97 + // If within circle, use the rotated pixel, otherwise transparent 98 + if distance <= radius*radius { 99 + masked.Set(x, y, rotated.At(x, y)) 100 + } else { 101 + masked.Set(x, y, color.Transparent) 102 + } 103 + } 104 + } 105 + 59 106 // Encode the image back to bytes 60 107 var buf bytes.Buffer 61 108 switch format { 62 109 case "jpeg", "jpg": 63 - if err := jpeg.Encode(&buf, rotated, &jpeg.Options{Quality: 95}); err != nil { 110 + if err := jpeg.Encode(&buf, masked, &jpeg.Options{Quality: 95}); err != nil { 64 111 return nil, "", fmt.Errorf("failed to encode jpeg: %w", err) 65 112 } 66 113 case "png": 67 - if err := png.Encode(&buf, rotated); err != nil { 114 + if err := png.Encode(&buf, masked); err != nil { 68 115 return nil, "", fmt.Errorf("failed to encode png: %w", err) 69 116 } 70 117 default: 71 118 // Default to JPEG if format is unknown 72 - if err := jpeg.Encode(&buf, rotated, &jpeg.Options{Quality: 95}); err != nil { 119 + if err := jpeg.Encode(&buf, masked, &jpeg.Options{Quality: 95}); err != nil { 73 120 return nil, "", fmt.Errorf("failed to encode jpeg: %w", err) 74 121 } 75 122 format = "jpeg"