Some checks failed
CI / Test (Go 1.24.x, ubuntu-latest) (push) Successful in 1m53s
CI / Code Quality (push) Failing after 26s
CI / Security Scan (push) Failing after 11s
CI / Test Coverage (push) Successful in 1m13s
CI / Benchmarks (push) Failing after 10m22s
CI / Build CLI (push) Failing after 8s
Benchmarks / Run Benchmarks (push) Failing after 10m13s
Release / Test (push) Successful in 55s
Release / Build (amd64, darwin, ) (push) Failing after 12s
Release / Build (amd64, linux, ) (push) Failing after 6s
Release / Build (amd64, windows, .exe) (push) Failing after 12s
Release / Build (arm64, darwin, ) (push) Failing after 12s
Release / Build (arm64, linux, ) (push) Failing after 12s
Release / Release (push) Has been skipped
CI / Test (Go 1.24.x, macos-latest) (push) Has been cancelled
CI / Test (Go 1.24.x, windows-latest) (push) Has been cancelled
Move hosting from GitHub to private Gitea instance.
111 lines
2.7 KiB
Go
111 lines
2.7 KiB
Go
package jdenticon
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gitea.dockr.co/kev/go-jdenticon/internal/engine"
|
|
"gitea.dockr.co/kev/go-jdenticon/internal/renderer"
|
|
)
|
|
|
|
// Icon represents a generated identicon that can be rendered as SVG or PNG.
|
|
// It wraps the internal engine.Icon to provide a clean public API.
|
|
type Icon struct {
|
|
engineIcon *engine.Icon
|
|
}
|
|
|
|
// newIcon creates a new public Icon from an internal engine.Icon.
|
|
func newIcon(engineIcon *engine.Icon) *Icon {
|
|
return &Icon{
|
|
engineIcon: engineIcon,
|
|
}
|
|
}
|
|
|
|
// ToSVG renders the icon as an SVG string.
|
|
//
|
|
// Returns the SVG markup as a string, or an error if rendering fails.
|
|
func (i *Icon) ToSVG() (string, error) {
|
|
if i.engineIcon == nil {
|
|
return "", ErrInvalidIcon("icon data is nil")
|
|
}
|
|
|
|
size := int(i.engineIcon.Size)
|
|
svgRenderer := renderer.NewSVGRenderer(size)
|
|
|
|
if err := i.renderToRenderer(svgRenderer); err != nil {
|
|
return "", NewErrRenderFailed("SVG", err)
|
|
}
|
|
|
|
return svgRenderer.ToSVG(), nil
|
|
}
|
|
|
|
// ToPNG renders the icon as PNG bytes.
|
|
//
|
|
// Returns the PNG data as a byte slice, or an error if rendering fails.
|
|
func (i *Icon) ToPNG() ([]byte, error) {
|
|
if i.engineIcon == nil {
|
|
return nil, ErrInvalidIcon("icon data is nil")
|
|
}
|
|
|
|
size := int(i.engineIcon.Size)
|
|
pngRenderer := renderer.NewPNGRenderer(size)
|
|
|
|
if err := i.renderToRenderer(pngRenderer); err != nil {
|
|
return nil, NewErrRenderFailed("PNG", err)
|
|
}
|
|
|
|
png, err := pngRenderer.ToPNG()
|
|
if err != nil {
|
|
return nil, NewErrRenderFailed("PNG", err)
|
|
}
|
|
|
|
return png, nil
|
|
}
|
|
|
|
// renderToRenderer renders the icon to any renderer that implements the Renderer interface.
|
|
func (i *Icon) renderToRenderer(r renderer.Renderer) error {
|
|
// Set background if specified
|
|
if i.engineIcon.Config.BackColor != nil {
|
|
color := i.engineIcon.Config.BackColor
|
|
colorStr := color.String()
|
|
opacity := float64(color.A) / 255.0
|
|
r.SetBackground(colorStr, opacity)
|
|
}
|
|
|
|
// Render each shape group
|
|
for _, shapeGroup := range i.engineIcon.Shapes {
|
|
colorStr := shapeGroup.Color.String()
|
|
|
|
r.BeginShape(colorStr)
|
|
|
|
for _, shape := range shapeGroup.Shapes {
|
|
if err := i.renderShape(r, shape); err != nil {
|
|
return fmt.Errorf("shape rendering failed: %w", err)
|
|
}
|
|
}
|
|
|
|
r.EndShape()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// renderShape renders a single shape to the renderer.
|
|
func (i *Icon) renderShape(r renderer.Renderer, shape engine.Shape) error {
|
|
switch shape.Type {
|
|
case "polygon":
|
|
if len(shape.Points) < 3 {
|
|
return fmt.Errorf("polygon must have at least 3 points, got %d", len(shape.Points))
|
|
}
|
|
r.AddPolygon(shape.Points)
|
|
|
|
case "circle":
|
|
topLeft := engine.Point{X: shape.CircleX, Y: shape.CircleY}
|
|
r.AddCircle(topLeft, shape.CircleSize, shape.Invert)
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported shape type: %s", shape.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|