diff --git a/internal/epub/core.go b/internal/epub/core.go index b743997..6c3e392 100644 --- a/internal/epub/core.go +++ b/internal/epub/core.go @@ -2,6 +2,7 @@ package epub import ( "fmt" + "image/color" "path/filepath" "strings" "text/template" @@ -16,6 +17,7 @@ type ImageOptions struct { ViewHeight int Quality int Algo string + Palette color.Palette } type EpubOptions struct { diff --git a/internal/epub/image_processing.go b/internal/epub/image_processing.go index acdf2be..2968ccd 100644 --- a/internal/epub/image_processing.go +++ b/internal/epub/image_processing.go @@ -13,7 +13,7 @@ import ( "strings" "sync" - imageconverter "github.com/celogeek/go-comic-converter/internal/image-converter" + "github.com/celogeek/go-comic-converter/internal/imageconverter" "github.com/nwaples/rardecode" pdfimage "github.com/raff/pdfreader/image" @@ -81,6 +81,7 @@ func LoadImages(path string, options *ImageOptions) ([]*Image, error) { options.ViewHeight, options.Quality, options.Algo, + options.Palette, ) name := fmt.Sprintf("OEBPS/Images/%d.jpg", img.Id) if img.Id == 0 { diff --git a/internal/imageconverter/algo.go b/internal/imageconverter/algo.go new file mode 100644 index 0000000..e6e2090 --- /dev/null +++ b/internal/imageconverter/algo.go @@ -0,0 +1,29 @@ +package imageconverter + +import ( + "image/color" + "sort" +) + +var ALGO_GRAY = map[string]func(color.Color, color.Palette) color.Gray{ + "default": func(c color.Color, p color.Palette) color.Gray { + return p.Convert(c).(color.Gray) + }, + "mean": func(c color.Color, p color.Palette) color.Gray { + r, g, b, _ := c.RGBA() + y := float64(r+g+b) / 3 + return p.Convert(color.Gray16{Y: uint16(y)}).(color.Gray) + }, + "luma": func(c color.Color, p color.Palette) color.Gray { + r, g, b, _ := c.RGBA() + y := (0.2126*float64(r) + 0.7152*float64(g) + 0.0722*float64(b)) + return p.Convert(color.Gray16{Y: uint16(y)}).(color.Gray) + }, + "luster": func(c color.Color, p color.Palette) color.Gray { + r, g, b, _ := c.RGBA() + arr := []float64{float64(r), float64(g), float64(b)} + sort.Float64s(arr) + y := (arr[0] + arr[2]) / 2 + return p.Convert(color.Gray16{Y: uint16(y)}).(color.Gray) + }, +} diff --git a/internal/image-converter/core.go b/internal/imageconverter/core.go similarity index 68% rename from internal/image-converter/core.go rename to internal/imageconverter/core.go index 1a21cbf..7c27992 100644 --- a/internal/image-converter/core.go +++ b/internal/imageconverter/core.go @@ -1,4 +1,4 @@ -package comicconverter +package imageconverter import ( "bytes" @@ -6,62 +6,29 @@ import ( "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 { +func Load(reader io.ReadCloser, algo string, palette color.Palette) *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) + algoFunc, ok := ALGO_GRAY[algo] + if !ok { + panic("unknown algo") } + + grayImg := image.NewGray(img.Bounds()) + 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, algoFunc(img.At(x, y), palette)) + } + } + + return grayImg } func isBlank(c color.Color) bool { @@ -157,8 +124,8 @@ func Get(img *image.Gray, quality int) []byte { return b.Bytes() } -func Convert(reader io.ReadCloser, crop bool, w, h int, quality int, algo string) ([]byte, int, int) { - img := Load(reader, algo) +func Convert(reader io.ReadCloser, crop bool, w, h int, quality int, algo string, palette color.Palette) ([]byte, int, int) { + img := Load(reader, algo, palette) if crop { img = CropMarging(img) } diff --git a/internal/imageconverter/palette.go b/internal/imageconverter/palette.go new file mode 100644 index 0000000..c764b3b --- /dev/null +++ b/internal/imageconverter/palette.go @@ -0,0 +1,45 @@ +package imageconverter + +import "image/color" + +var PALETTE_16 = color.Palette{ + color.Gray{0x00}, + color.Gray{0x11}, + color.Gray{0x22}, + color.Gray{0x33}, + color.Gray{0x44}, + color.Gray{0x55}, + color.Gray{0x66}, + color.Gray{0x77}, + color.Gray{0x88}, + color.Gray{0x99}, + color.Gray{0xaa}, + color.Gray{0xbb}, + color.Gray{0xcc}, + color.Gray{0xdd}, + color.Gray{0xff}, +} + +var PALETTE_15 = color.Palette{ + color.Gray{0x00}, + color.Gray{0x11}, + color.Gray{0x22}, + color.Gray{0x33}, + color.Gray{0x44}, + color.Gray{0x55}, + color.Gray{0x66}, + color.Gray{0x77}, + color.Gray{0x88}, + color.Gray{0x99}, + color.Gray{0xaa}, + color.Gray{0xbb}, + color.Gray{0xcc}, + color.Gray{0xff}, +} + +var PALETTE_4 = color.Palette{ + color.Gray{0x00}, + color.Gray{0x55}, + color.Gray{0xaa}, + color.Gray{0xff}, +} diff --git a/main.go b/main.go index 2dd3251..2da2e86 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,12 @@ package main import ( "flag" "fmt" + "image/color" "os" "path/filepath" "strings" - imageconverter "github.com/celogeek/go-comic-converter/internal/image-converter" + "github.com/celogeek/go-comic-converter/internal/imageconverter" "github.com/celogeek/go-comic-converter/internal/epub" ) @@ -17,35 +18,36 @@ type Profile struct { Description string Width int Height int + Palette color.Palette } var Profiles = []Profile{ // Kindle - {"K1", "Kindle 1", 600, 670}, - {"K11", "Kindle 11", 1072, 1448}, - {"K2", "Kindle 2", 600, 670}, - {"K34", "Kindle Keyboard/Touch", 600, 800}, - {"K578", "Kindle", 600, 800}, - {"KDX", "Kindle DX/DXG", 824, 1000}, - {"KPW", "Kindle Paperwhite 1/2", 758, 1024}, - {"KV", "Kindle Paperwhite 3/4/Voyage/Oasis", 1072, 1448}, - {"KPW5", "Kindle Paperwhite 5/Signature Edition", 1236, 1648}, - {"KO", "Kindle Oasis 2/3", 1264, 1680}, - {"KS", "Kindle Scribe", 1860, 2480}, + {"K1", "Kindle 1", 600, 670, imageconverter.PALETTE_4}, + {"K11", "Kindle 11", 1072, 1448, imageconverter.PALETTE_16}, + {"K2", "Kindle 2", 600, 670, imageconverter.PALETTE_15}, + {"K34", "Kindle Keyboard/Touch", 600, 800, imageconverter.PALETTE_16}, + {"K578", "Kindle", 600, 800, imageconverter.PALETTE_16}, + {"KDX", "Kindle DX/DXG", 824, 1000, imageconverter.PALETTE_16}, + {"KPW", "Kindle Paperwhite 1/2", 758, 1024, imageconverter.PALETTE_16}, + {"KV", "Kindle Paperwhite 3/4/Voyage/Oasis", 1072, 1448, imageconverter.PALETTE_16}, + {"KPW5", "Kindle Paperwhite 5/Signature Edition", 1236, 1648, imageconverter.PALETTE_16}, + {"KO", "Kindle Oasis 2/3", 1264, 1680, imageconverter.PALETTE_16}, + {"KS", "Kindle Scribe", 1860, 2480, imageconverter.PALETTE_16}, // Kobo - {"KoMT", "Kobo Mini/Touch", 600, 800}, - {"KoG", "Kobo Glo", 768, 1024}, - {"KoGHD", "Kobo Glo HD", 1072, 1448}, - {"KoA", "Kobo Aura", 758, 1024}, - {"KoAHD", "Kobo Aura HD", 1080, 1440}, - {"KoAH2O", "Kobo Aura H2O", 1080, 1430}, - {"KoAO", "Kobo Aura ONE", 1404, 1872}, - {"KoN", "Kobo Nia", 758, 1024}, - {"KoC", "Kobo Clara HD/Kobo Clara 2E", 1072, 1448}, - {"KoL", "Kobo Libra H2O/Kobo Libra 2", 1264, 1680}, - {"KoF", "Kobo Forma", 1440, 1920}, - {"KoS", "Kobo Sage", 1440, 1920}, - {"KoE", "Kobo Elipsa", 1404, 1872}, + {"KoMT", "Kobo Mini/Touch", 600, 800, imageconverter.PALETTE_16}, + {"KoG", "Kobo Glo", 768, 1024, imageconverter.PALETTE_16}, + {"KoGHD", "Kobo Glo HD", 1072, 1448, imageconverter.PALETTE_16}, + {"KoA", "Kobo Aura", 758, 1024, imageconverter.PALETTE_16}, + {"KoAHD", "Kobo Aura HD", 1080, 1440, imageconverter.PALETTE_16}, + {"KoAH2O", "Kobo Aura H2O", 1080, 1430, imageconverter.PALETTE_16}, + {"KoAO", "Kobo Aura ONE", 1404, 1872, imageconverter.PALETTE_16}, + {"KoN", "Kobo Nia", 758, 1024, imageconverter.PALETTE_16}, + {"KoC", "Kobo Clara HD/Kobo Clara 2E", 1072, 1448, imageconverter.PALETTE_16}, + {"KoL", "Kobo Libra H2O/Kobo Libra 2", 1264, 1680, imageconverter.PALETTE_16}, + {"KoF", "Kobo Forma", 1440, 1920, imageconverter.PALETTE_16}, + {"KoS", "Kobo Sage", 1440, 1920, imageconverter.PALETTE_16}, + {"KoE", "Kobo Elipsa", 1404, 1872, imageconverter.PALETTE_16}, } var ProfilesIdx = map[string]int{} @@ -120,7 +122,7 @@ func main() { )) } availableAlgo := make([]string, 0) - for a := range imageconverter.AlgoGray { + for a := range imageconverter.ALGO_GRAY { availableAlgo = append(availableAlgo, a) } @@ -202,7 +204,7 @@ func main() { opt.Title = filepath.Base(defaultOutput[0 : len(defaultOutput)-len(ext)]) } - if _, ok := imageconverter.AlgoGray[opt.Algo]; !ok { + if _, ok := imageconverter.ALGO_GRAY[opt.Algo]; !ok { fmt.Fprintln(os.Stderr, "algo doesn't exists") flag.Usage() os.Exit(1) @@ -222,6 +224,7 @@ func main() { Quality: opt.Quality, Crop: !opt.NoCrop, Algo: opt.Algo, + Palette: profile.Palette, }, }).Write(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err)