added icon downloader
This commit is contained in:
parent
8b5693b5c6
commit
5a2e37ae06
10 changed files with 829 additions and 68 deletions
139
pipeline/03_icon_download/image.go
Normal file
139
pipeline/03_icon_download/image.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
// detectImageType checks magic bytes to determine the actual image format.
|
||||
// Returns empty string if not a recognized image format.
|
||||
func detectImageType(data []byte) string {
|
||||
if len(data) < 4 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// PNG: 89 50 4E 47
|
||||
if data[0] == 0x89 && data[1] == 'P' && data[2] == 'N' && data[3] == 'G' {
|
||||
return "image/png"
|
||||
}
|
||||
|
||||
// GIF: GIF87a or GIF89a
|
||||
if data[0] == 'G' && data[1] == 'I' && data[2] == 'F' {
|
||||
return "image/gif"
|
||||
}
|
||||
|
||||
// JPEG: FF D8 FF
|
||||
if data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF {
|
||||
return "image/jpeg"
|
||||
}
|
||||
|
||||
// ICO: 00 00 01 00
|
||||
if data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01 && data[3] == 0x00 {
|
||||
return "image/x-icon"
|
||||
}
|
||||
|
||||
// BMP: BM
|
||||
if data[0] == 'B' && data[1] == 'M' {
|
||||
return "image/bmp"
|
||||
}
|
||||
|
||||
// WebP: RIFF....WEBP
|
||||
if len(data) >= 12 && string(data[0:4]) == "RIFF" && string(data[8:12]) == "WEBP" {
|
||||
return "image/webp"
|
||||
}
|
||||
|
||||
// SVG: look for <?xml or <svg in first 256 bytes
|
||||
if len(data) > 5 {
|
||||
header := string(data[:min(256, len(data))])
|
||||
if bytes.Contains([]byte(header), []byte("<svg")) || bytes.Contains([]byte(header), []byte("<?xml")) {
|
||||
return "image/svg+xml"
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// getImageDimensions reads image dimensions from the data.
|
||||
// Returns (0, 0) for SVG or if dimensions can't be determined.
|
||||
func getImageDimensions(data []byte, contentType string) (int, int) {
|
||||
switch contentType {
|
||||
case "image/svg+xml":
|
||||
return 0, 0
|
||||
case "image/x-icon":
|
||||
return getICODimensions(data)
|
||||
default:
|
||||
// Use Go's image.DecodeConfig for standard formats
|
||||
cfg, _, err := image.DecodeConfig(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
return cfg.Width, cfg.Height
|
||||
}
|
||||
}
|
||||
|
||||
// getICODimensions reads the ICO directory to find the largest image ≤64x64.
|
||||
// ICO format: 6-byte header + 16-byte directory entries.
|
||||
func getICODimensions(data []byte) (int, int) {
|
||||
if len(data) < 6 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
numImages := int(binary.LittleEndian.Uint16(data[4:6]))
|
||||
if numImages == 0 || len(data) < 6+numImages*16 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
bestW, bestH := 0, 0
|
||||
for i := 0; i < numImages; i++ {
|
||||
offset := 6 + i*16
|
||||
w := int(data[offset])
|
||||
h := int(data[offset+1])
|
||||
// ICO uses 0 to mean 256
|
||||
if w == 0 {
|
||||
w = 256
|
||||
}
|
||||
if h == 0 {
|
||||
h = 256
|
||||
}
|
||||
|
||||
// Pick the largest that's ≤64x64
|
||||
if w <= 64 && h <= 64 && w*h > bestW*bestH {
|
||||
bestW = w
|
||||
bestH = h
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing ≤64, just report the largest
|
||||
if bestW == 0 {
|
||||
for i := 0; i < numImages; i++ {
|
||||
offset := 6 + i*16
|
||||
w := int(data[offset])
|
||||
h := int(data[offset+1])
|
||||
if w == 0 {
|
||||
w = 256
|
||||
}
|
||||
if h == 0 {
|
||||
h = 256
|
||||
}
|
||||
if w*h > bestW*bestH {
|
||||
bestW = w
|
||||
bestH = h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestW, bestH
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue