Skip to content

Commit 2dc4d06

Browse files
committed
feat: draw grids in an image
1 parent 3253961 commit 2dc4d06

File tree

2 files changed

+184
-16
lines changed

2 files changed

+184
-16
lines changed

cmd/draw.go

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,27 @@ import (
1414
var (
1515
colorB string
1616
BorderThickness int
17+
18+
gridSize int
19+
gridColor string
20+
gridThickness int
21+
gridMask bool
1722
)
1823

1924
var drawCmd = &cobra.Command{
20-
Use: "draw [PATH] [OPTIONAL OUTPUT]",
21-
Short: "draw a border with a color and thickness (currently)",
22-
Long: `The draw command allows you to draw a plethora of effects. Currently only drawing a border is supported with more to come`,
25+
Use: "draw [DRAW-COMMAND] [INPUT] [OPTIONAL OUTPUT]",
26+
Short: "Draw on images",
27+
Long: `Draw a [border,grid] in your images`,
28+
Run: func(cmd *cobra.Command, args []string) {
29+
logger.Print("Please specify an draw command to apply")
30+
err := cmd.Usage()
31+
utils.HandleError(err)
32+
},
33+
}
34+
35+
var GridCmd = &cobra.Command{
36+
Use: "grid [PATH] [OPTIONAL OUTPUT]",
37+
Short: "draw a grid with a specific size,color and thickness on the image",
2338
PreRunE: func(cmd *cobra.Command, args []string) error {
2439
err := validateInput(shared, args)
2540
if err != nil {
@@ -28,19 +43,45 @@ var drawCmd = &cobra.Command{
2843
return nil
2944
},
3045
Run: func(cmd *cobra.Command, args []string) {
31-
if len(args) > 0 {
32-
logger.Print("Processing single image...")
46+
imageOps, err := imageio.DetermineImageOperations(shared, args)
47+
utils.HandleError(err)
48+
logger.Print("Processing images...")
49+
50+
processor := &image.GridProcessor{}
51+
processor.SetGridOptions(
52+
image.WithGridSize(gridSize),
53+
image.WithGridColor(gridColor),
54+
image.WithGridThickness(gridThickness),
55+
image.WithMaskonly(gridMask),
56+
)
57+
processedImages, err := image.ProcessImgs(processor, imageOps, theme)
58+
utils.HandleError(err, "Error")
59+
60+
if err != nil {
61+
logger.Error(err, "The following images had errors while processing")
3362
}
34-
if isInputBatch(shared) {
35-
logger.Print("Processing batch of images...")
63+
openImageInViewer(shared, args, processedImages[0])
64+
},
65+
}
66+
67+
var BorderCmd = &cobra.Command{
68+
Use: "border [PATH] [OPTIONAL OUTPUT]",
69+
Short: "draw a border with a specified color and thickness on the image",
70+
PreRunE: func(cmd *cobra.Command, args []string) error {
71+
err := validateInput(shared, args)
72+
if err != nil {
73+
return err
3674
}
75+
return nil
76+
},
77+
Run: func(cmd *cobra.Command, args []string) {
3778
hex, err := cmd.Flags().GetString("color")
3879
utils.HandleError(err, "Error")
3980

4081
clr, err := image.HexToRGBA(hex)
4182
utils.HandleError(err, "Error")
4283

43-
processor := &image.DrawProcessor{
84+
processor := &image.BorderProcessor{
4485
Color: clr,
4586
BorderThickness: BorderThickness,
4687
}
@@ -53,10 +94,6 @@ var drawCmd = &cobra.Command{
5394
processedImages, err := image.ProcessImgs(processor, imageOps, theme)
5495
utils.HandleError(err, "Error")
5596

56-
// if len(processedImages) == 0 {
57-
// utils.HandleError(err, "No images were processed")
58-
// }
59-
6097
if err != nil {
6198
logger.Error(err, "The following images had errors while processing")
6299
}
@@ -67,7 +104,16 @@ var drawCmd = &cobra.Command{
67104

68105
func init() {
69106
rootCmd.AddCommand(drawCmd)
70-
drawCmd.Flags().StringVarP(&colorB, "color", "c", "#5D3FD3", "--color #5D3FD3")
71-
drawCmd.Flags().IntVarP(&BorderThickness, "borderThickness", "b", 5, "-b 5")
107+
108+
drawCmd.AddCommand(BorderCmd)
109+
drawCmd.AddCommand(GridCmd)
110+
111+
BorderCmd.Flags().StringVarP(&colorB, "color", "c", "#5D3FD3", "--color #5D3FD3")
112+
BorderCmd.Flags().IntVarP(&BorderThickness, "borderThickness", "b", 5, "-b 5")
113+
GridCmd.Flags().IntVarP(&gridSize, "size", "s", 80, "--size 80")
114+
GridCmd.Flags().IntVarP(&gridThickness, "thickness", "t", 1, "--thickness 1")
115+
GridCmd.Flags().StringVarP(&gridColor, "color", "c", "#5D3FD3", "--color #5D3FD3")
116+
GridCmd.Flags().BoolVarP(&gridMask, "mask", "m", false, "--mask true to use apply the grid only to transparent pixels (background)")
117+
72118
addGlobalFlags(drawCmd)
73119
}

internal/image/draw.go

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import (
44
"image"
55
"image/color"
66
"image/draw"
7+
8+
"github.com/Achno/gowall/utils"
79
)
810

9-
type DrawProcessor struct {
11+
type BorderProcessor struct {
1012
Color color.RGBA
1113
BorderThickness int
1214
}
1315

14-
func (b *DrawProcessor) Process(img image.Image, theme string) (image.Image, error) {
16+
func (b *BorderProcessor) Process(img image.Image, theme string) (image.Image, error) {
1517

1618
newImg := drawBorder(img, b.BorderThickness, b.Color)
1719

@@ -46,3 +48,123 @@ func drawBorder(img image.Image, borderThickness int, borderColor color.Color) i
4648

4749
return newImg
4850
}
51+
52+
type GridProcessor struct {
53+
options GridOptions
54+
}
55+
56+
type GridOptions struct {
57+
GridSize int
58+
GridColor color.RGBA
59+
GridThickness int
60+
MaskOnly bool
61+
}
62+
63+
type GridOption func(*GridOptions)
64+
65+
func WithGridSize(gridsize int) GridOption {
66+
return func(g *GridOptions) {
67+
g.GridSize = gridsize
68+
}
69+
}
70+
func WithGridColor(gridColor string) GridOption {
71+
return func(g *GridOptions) {
72+
c, err := HexToRGBA(gridColor)
73+
utils.HandleError(err)
74+
g.GridColor = c
75+
}
76+
}
77+
func WithGridThickness(gridThickness int) GridOption {
78+
return func(g *GridOptions) {
79+
g.GridThickness = gridThickness
80+
}
81+
}
82+
func WithMaskonly(maskOnly bool) GridOption {
83+
return func(g *GridOptions) {
84+
g.MaskOnly = maskOnly
85+
}
86+
}
87+
88+
// Available options : WithGridSize,WithGridColor,WithGridThickness,WithMaskonly
89+
func (g *GridProcessor) SetGridOptions(options ...GridOption) {
90+
opts := GridOptions{
91+
GridSize: 80,
92+
GridColor: color.RGBA{R: 93, G: 63, B: 211, A: 255},
93+
GridThickness: 1,
94+
MaskOnly: false,
95+
}
96+
97+
for _, option := range options {
98+
option(&opts)
99+
}
100+
101+
g.options = opts
102+
}
103+
104+
func (g *GridProcessor) Process(img image.Image, theme string) (image.Image, error) {
105+
106+
newImg, err := applyGridToImage(img, &g.options)
107+
if err != nil {
108+
return nil, err
109+
}
110+
return newImg, nil
111+
112+
}
113+
114+
func applyGridToImage(img image.Image, options *GridOptions) (image.Image, error) {
115+
bounds := img.Bounds()
116+
newImg := image.NewRGBA(bounds)
117+
118+
draw.Draw(newImg, bounds, img, bounds.Min, draw.Src)
119+
120+
// optionally use the input image as a mask, and apply the grid only to the transparent areas.
121+
if options.MaskOnly {
122+
gridImg := image.NewRGBA(bounds)
123+
drawGridOnImage(gridImg, options)
124+
125+
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
126+
for x := bounds.Min.X; x < bounds.Max.X; x++ {
127+
_, _, _, srcAlpha := img.At(x, y).RGBA()
128+
129+
// (low alpha)
130+
if srcAlpha < 0x8000 {
131+
gridColor := gridImg.At(x, y)
132+
newImg.Set(x, y, gridColor)
133+
}
134+
}
135+
}
136+
} else {
137+
drawGridOnImage(newImg, options)
138+
}
139+
140+
return newImg, nil
141+
}
142+
143+
// drawGrid draws a grid on the image with the given thickness,color and grid size
144+
func drawGridOnImage(img *image.RGBA, c *GridOptions) {
145+
bounds := img.Bounds()
146+
147+
for x := bounds.Min.X; x < bounds.Max.X; x += c.GridSize {
148+
for thickness := 0; thickness < c.GridThickness; thickness++ {
149+
if x+thickness >= bounds.Max.X {
150+
break
151+
}
152+
153+
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
154+
img.Set(x+thickness, y, c.GridColor)
155+
}
156+
}
157+
}
158+
159+
for y := bounds.Min.Y; y < bounds.Max.Y; y += c.GridSize {
160+
for thickness := 0; thickness < c.GridThickness; thickness++ {
161+
if y+thickness >= bounds.Max.Y {
162+
break
163+
}
164+
165+
for x := bounds.Min.X; x < bounds.Max.X; x++ {
166+
img.Set(x, y+thickness, c.GridColor)
167+
}
168+
}
169+
}
170+
}

0 commit comments

Comments
 (0)