diff --git a/internal/comic-converter/core.go b/internal/comic-converter/core.go index 7ff6392..71f674a 100644 --- a/internal/comic-converter/core.go +++ b/internal/comic-converter/core.go @@ -2,26 +2,14 @@ package comicconverter import ( "image" + "image/color" "image/jpeg" "os" "golang.org/x/image/draw" ) -type ComicConverter struct { - Options ComicConverterOptions - img *image.Gray -} - -type ComicConverterOptions struct { - Quality int -} - -func New(opt ComicConverterOptions) *ComicConverter { - return &ComicConverter{Options: opt} -} - -func (c *ComicConverter) Load(file string) *ComicConverter { +func Load(file string) *image.Gray { f, err := os.Open(file) if err != nil { panic(err) @@ -35,32 +23,26 @@ func (c *ComicConverter) Load(file string) *ComicConverter { switch imgt := img.(type) { case *image.Gray: - c.img = imgt + return imgt default: newImg := image.NewGray(img.Bounds()) draw.Draw(newImg, newImg.Bounds(), img, image.Point{}, draw.Src) - c.img = newImg + return newImg } - - return c } -func (c *ComicConverter) isBlank(x, y int) bool { - r, g, b, _ := c.img.At(x, y).RGBA() +func isBlank(c color.Color) bool { + r, g, b, _ := c.RGBA() return r > 60000 && g > 60000 && b > 60000 } -func (c *ComicConverter) CropMarging() *ComicConverter { - if c.img == nil { - panic("load image first") - } - - imgArea := c.img.Bounds() +func CropMarging(img *image.Gray) *image.Gray { + imgArea := img.Bounds() LEFT: for x := imgArea.Min.X; x < imgArea.Max.X; x++ { for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ { - if !c.isBlank(x, y) { + if !isBlank(img.At(x, y)) { break LEFT } } @@ -70,7 +52,7 @@ LEFT: UP: for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ { for x := imgArea.Min.X; x < imgArea.Max.X; x++ { - if !c.isBlank(x, y) { + if !isBlank(img.At(x, y)) { break UP } } @@ -80,7 +62,7 @@ UP: RIGHT: for x := imgArea.Max.X - 1; x >= imgArea.Min.X; x-- { for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ { - if !c.isBlank(x, y) { + if !isBlank(img.At(x, y)) { break RIGHT } } @@ -90,40 +72,32 @@ RIGHT: BOTTOM: for y := imgArea.Max.Y - 1; y >= imgArea.Min.Y; y-- { for x := imgArea.Min.X; x < imgArea.Max.X; x++ { - if !c.isBlank(x, y) { + if !isBlank(img.At(x, y)) { break BOTTOM } } imgArea.Max.Y-- } - newImg := image.NewGray(image.Rectangle{ - Min: image.Point{0, 0}, - Max: image.Point{imgArea.Dx(), imgArea.Dy()}, - }) - - draw.Draw(newImg, newImg.Bounds(), c.img, imgArea.Min, draw.Src) - - c.img = c.img.SubImage(imgArea).(*image.Gray) - - return c + return img.SubImage(imgArea).(*image.Gray) } -func (c *ComicConverter) Resize(w, h int) *ComicConverter { - if c.img == nil { - panic("load image first") - } - - dim := c.img.Bounds() +func Resize(img *image.Gray, w, h int) *image.Gray { + dim := img.Bounds() origWidth := dim.Dx() origHeight := dim.Dy() - width, height := 1, 1 - - if origHeight > 0 && origWidth > 0 { - width, height = origWidth*h/origHeight, origHeight*w/origWidth + if origWidth == 0 || origHeight == 0 { + newImg := image.NewGray(image.Rectangle{ + image.Point{0, 0}, + image.Point{w, h}, + }) + draw.Draw(newImg, newImg.Bounds(), image.NewUniform(color.White), newImg.Bounds().Min, draw.Src) + return newImg } + width, height := origWidth*h/origHeight, origHeight*w/origWidth + if width > w { width = w } @@ -136,32 +110,24 @@ func (c *ComicConverter) Resize(w, h int) *ComicConverter { Max: image.Point{width, height}, }) - draw.BiLinear.Scale(newImg, newImg.Bounds(), c.img, c.img.Bounds(), draw.Src, nil) + draw.BiLinear.Scale(newImg, newImg.Bounds(), img, img.Bounds(), draw.Src, nil) - c.img = newImg - - return c + return newImg } -func (c *ComicConverter) Save(output string) *ComicConverter { - if c.img == nil { - panic("load image first") - } +func Save(img *image.Gray, output string, quality int) { o, err := os.Create(output) if err != nil { panic(err) } defer o.Close() - quality := 75 - if c.Options.Quality > 0 { - quality = c.Options.Quality + if quality == 0 { + quality = 75 } - err = jpeg.Encode(o, c.img, &jpeg.Options{Quality: quality}) + err = jpeg.Encode(o, img, &jpeg.Options{Quality: quality}) if err != nil { panic(err) } - - return c } diff --git a/main.go b/main.go index 85cd7b0..148933e 100644 --- a/main.go +++ b/main.go @@ -3,20 +3,56 @@ package main import ( "fmt" comicconverter "go-comic-converter/internal/comic-converter" + "io/fs" + "path/filepath" + "runtime" + "strings" + "sync" ) -func main() { - cv := comicconverter. - New(comicconverter.ComicConverterOptions{ - Quality: 75, - }) +type Todo struct { + Input string + Output string +} - for i := 1; i < 10; i++ { - cv. - Load(fmt.Sprintf("/Users/vincent/Downloads/0000%d.jpg", i)). - CropMarging(). - Resize(1860, 2480). - Save(fmt.Sprintf("/Users/vincent/Downloads/0000%d_gray.jpg", i)) +func main() { + wg := &sync.WaitGroup{} + todos := make(chan Todo, runtime.NumCPU()) + + wg.Add(runtime.NumCPU()) + for i := 0; i < runtime.NumCPU(); i++ { + go func() { + defer wg.Done() + for todo := range todos { + fmt.Printf("Processing %s\n", todo.Input) + comicconverter.Save( + comicconverter.Resize( + comicconverter.CropMarging( + comicconverter.Load(todo.Input), + ), 1860, 2480), todo.Output, 75, + ) + } + }() } + dirname := "/Users/vincent/Downloads/Bleach T01 (Tite KUBO) [eBook officiel 1920]" + filepath.WalkDir(dirname, func(path string, d fs.DirEntry, err error) error { + if d.IsDir() { + return nil + } + input := path + ext := filepath.Ext(path) + if strings.ToLower(ext) != ".jpg" { + return nil + } + output := fmt.Sprintf("%s_gray%s", input[0:len(input)-len(ext)], ext) + + todos <- Todo{input, output} + + return nil + }) + + close(todos) + + wg.Wait() }