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