2022-12-30 18:30:38 +01:00

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()
}