package storage import ( "bytes" "fmt" "image" "image/png" "io" // Import image formats for decoding _ "image/gif" _ "image/jpeg" "golang.org/x/image/draw" ) // IconSizes defines the icon sizes to generate var IconSizes = []int{32, 64, 128} // ProcessIcon reads an image and generates icons at multiple sizes func (s *Storage) ProcessIcon(appID string, r io.Reader) error { // Decode the image img, _, err := image.Decode(r) if err != nil { return fmt.Errorf("failed to decode image: %w", err) } // Validate square dimensions bounds := img.Bounds() if bounds.Dx() != bounds.Dy() { return fmt.Errorf("icon must be square, got %dx%d", bounds.Dx(), bounds.Dy()) } // Minimum size check if bounds.Dx() < 128 { return fmt.Errorf("icon must be at least 128x128, got %dx%d", bounds.Dx(), bounds.Dy()) } // Generate each size for _, size := range IconSizes { resized := resizeImage(img, size, size) var buf bytes.Buffer if err := png.Encode(&buf, resized); err != nil { return fmt.Errorf("failed to encode icon-%d: %w", size, err) } if err := s.SaveIcon(appID, size, buf.Bytes()); err != nil { return fmt.Errorf("failed to save icon-%d: %w", size, err) } } return nil } // resizeImage resizes an image to the specified dimensions using high-quality interpolation func resizeImage(src image.Image, width, height int) image.Image { dst := image.NewRGBA(image.Rect(0, 0, width, height)) // Use CatmullRom for high-quality downscaling draw.CatmullRom.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil) return dst }