mirror of
https://github.com/celogeek/go-comic-converter.git
synced 2025-05-25 08:12:36 +02:00
168 lines
3.5 KiB
Go
168 lines
3.5 KiB
Go
package comicconverter
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"image/color"
|
|
"image/jpeg"
|
|
"io"
|
|
"sort"
|
|
|
|
"golang.org/x/image/draw"
|
|
)
|
|
|
|
var AlgoGray = map[string]func(color.Color) color.Gray{
|
|
"default": func(c color.Color) color.Gray {
|
|
return color.GrayModel.Convert(c).(color.Gray)
|
|
},
|
|
"mean": func(c color.Color) color.Gray {
|
|
r, g, b, _ := c.RGBA()
|
|
y := float64(r+g+b) / 3 * (255.0 / 65535)
|
|
return color.Gray{uint8(y)}
|
|
},
|
|
"luma": func(c color.Color) color.Gray {
|
|
r, g, b, _ := c.RGBA()
|
|
y := (0.2126*float64(r) + 0.7152*float64(g) + 0.0722*float64(b)) * (255.0 / 65535)
|
|
return color.Gray{uint8(y)}
|
|
},
|
|
"luster": func(c color.Color) color.Gray {
|
|
r, g, b, _ := c.RGBA()
|
|
arr := []float64{float64(r), float64(g), float64(b)}
|
|
sort.Float64s(arr)
|
|
y := (arr[0] + arr[2]) / 2 * (255.0 / 65535)
|
|
return color.Gray{uint8(y)}
|
|
},
|
|
}
|
|
|
|
func toGray(img image.Image, algo string) *image.Gray {
|
|
grayImg := image.NewGray(img.Bounds())
|
|
algoConv, ok := AlgoGray[algo]
|
|
if !ok {
|
|
panic("wrong gray algo")
|
|
}
|
|
|
|
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
|
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
|
grayImg.SetGray(x, y, algoConv(img.At(x, y)))
|
|
}
|
|
}
|
|
return grayImg
|
|
}
|
|
|
|
func Load(reader io.ReadCloser, algo string) *image.Gray {
|
|
defer reader.Close()
|
|
img, _, err := image.Decode(reader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
switch imgt := img.(type) {
|
|
case *image.Gray:
|
|
return imgt
|
|
default:
|
|
return toGray(img, algo)
|
|
}
|
|
}
|
|
|
|
func isBlank(c color.Color) bool {
|
|
r, g, b, _ := c.RGBA()
|
|
return r > 60000 && g > 60000 && b > 60000
|
|
}
|
|
|
|
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 !isBlank(img.At(x, y)) {
|
|
break LEFT
|
|
}
|
|
}
|
|
imgArea.Min.X++
|
|
}
|
|
|
|
UP:
|
|
for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ {
|
|
for x := imgArea.Min.X; x < imgArea.Max.X; x++ {
|
|
if !isBlank(img.At(x, y)) {
|
|
break UP
|
|
}
|
|
}
|
|
imgArea.Min.Y++
|
|
}
|
|
|
|
RIGHT:
|
|
for x := imgArea.Max.X - 1; x >= imgArea.Min.X; x-- {
|
|
for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ {
|
|
if !isBlank(img.At(x, y)) {
|
|
break RIGHT
|
|
}
|
|
}
|
|
imgArea.Max.X--
|
|
}
|
|
|
|
BOTTOM:
|
|
for y := imgArea.Max.Y - 1; y >= imgArea.Min.Y; y-- {
|
|
for x := imgArea.Min.X; x < imgArea.Max.X; x++ {
|
|
if !isBlank(img.At(x, y)) {
|
|
break BOTTOM
|
|
}
|
|
}
|
|
imgArea.Max.Y--
|
|
}
|
|
|
|
return img.SubImage(imgArea).(*image.Gray)
|
|
}
|
|
|
|
func Resize(img *image.Gray, w, h int) *image.Gray {
|
|
dim := img.Bounds()
|
|
origWidth := dim.Dx()
|
|
origHeight := dim.Dy()
|
|
|
|
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
|
|
}
|
|
if height > h {
|
|
height = h
|
|
}
|
|
|
|
newImg := image.NewGray(image.Rectangle{
|
|
Min: image.Point{0, 0},
|
|
Max: image.Point{width, height},
|
|
})
|
|
|
|
draw.BiLinear.Scale(newImg, newImg.Bounds(), img, img.Bounds(), draw.Src, nil)
|
|
|
|
return newImg
|
|
}
|
|
|
|
func Get(img *image.Gray, quality int) []byte {
|
|
b := bytes.NewBuffer([]byte{})
|
|
err := jpeg.Encode(b, img, &jpeg.Options{Quality: quality})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b.Bytes()
|
|
}
|
|
|
|
func Convert(reader io.ReadCloser, crop bool, w, h int, quality int, algo string) ([]byte, int, int) {
|
|
img := Load(reader, algo)
|
|
if crop {
|
|
img = CropMarging(img)
|
|
}
|
|
img = Resize(img, w, h)
|
|
return Get(img, quality), img.Bounds().Dx(), img.Bounds().Dy()
|
|
}
|