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 }