mirror of
https://github.com/gen2brain/cbconvert
synced 2025-10-14 02:28:51 +02:00
add encodeImage and encodeImageMagick
This commit is contained in:
251
cbconvert.go
251
cbconvert.go
@@ -23,7 +23,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
_ "image/gif"
|
"image/gif"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
"io"
|
"io"
|
||||||
@@ -46,7 +46,7 @@ import (
|
|||||||
"github.com/gographics/imagick/imagick"
|
"github.com/gographics/imagick/imagick"
|
||||||
_ "github.com/hotei/bmp"
|
_ "github.com/hotei/bmp"
|
||||||
"github.com/skarademir/naturalsort"
|
"github.com/skarademir/naturalsort"
|
||||||
_ "golang.org/x/image/tiff"
|
"golang.org/x/image/tiff"
|
||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
@@ -130,116 +130,60 @@ func convertImage(img image.Image, index int, pathName string) {
|
|||||||
filename = filepath.Join(workdir, fmt.Sprintf("%03d.%s", index, ext))
|
filename = filepath.Join(workdir, fmt.Sprintf("%03d.%s", index, ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
var i image.Image
|
if opts.ToPNG {
|
||||||
|
// convert image to PNG
|
||||||
if opts.Width > 0 || opts.Height > 0 {
|
if opts.Grayscale {
|
||||||
i = imaging.Resize(img, opts.Width, opts.Height, filters[opts.Filter])
|
encodeImageMagick(img, filename)
|
||||||
|
} else {
|
||||||
|
encodeImage(img, filename)
|
||||||
|
}
|
||||||
|
} else if opts.ToBMP {
|
||||||
|
// convert image to 4-Bit BMP (16 colors)
|
||||||
|
encodeImageMagick(img, filename)
|
||||||
|
} else if opts.ToGIF {
|
||||||
|
// convert image to GIF
|
||||||
|
encodeImageMagick(img, filename)
|
||||||
} else {
|
} else {
|
||||||
i = img
|
// convert image to JPEG (default)
|
||||||
|
if opts.Grayscale {
|
||||||
|
encodeImageMagick(img, filename)
|
||||||
|
} else {
|
||||||
|
encodeImage(img, filename)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Grayscale {
|
<-throttle
|
||||||
i = imaging.Grayscale(img)
|
}
|
||||||
|
|
||||||
|
// Transforms image (resize|rotate|flip)
|
||||||
|
func transformImage(img image.Image) image.Image {
|
||||||
|
var i image.Image = img
|
||||||
|
|
||||||
|
if opts.Width > 0 || opts.Height > 0 {
|
||||||
|
i = imaging.Resize(i, opts.Width, opts.Height, filters[opts.Filter])
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Rotate > 0 {
|
if opts.Rotate > 0 {
|
||||||
switch opts.Rotate {
|
switch opts.Rotate {
|
||||||
case 90:
|
case 90:
|
||||||
i = imaging.Rotate90(img)
|
i = imaging.Rotate90(i)
|
||||||
case 180:
|
case 180:
|
||||||
i = imaging.Rotate180(img)
|
i = imaging.Rotate180(i)
|
||||||
case 270:
|
case 270:
|
||||||
i = imaging.Rotate270(img)
|
i = imaging.Rotate270(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Flip != "none" {
|
if opts.Flip != "none" {
|
||||||
switch opts.Flip {
|
switch opts.Flip {
|
||||||
case "horizontal":
|
case "horizontal":
|
||||||
i = imaging.FlipH(img)
|
i = imaging.FlipH(i)
|
||||||
case "vertical":
|
case "vertical":
|
||||||
i = imaging.FlipV(img)
|
i = imaging.FlipV(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ToPNG {
|
return i
|
||||||
// convert image to PNG
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error Create: %v\n", err.Error())
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
png.Encode(f, i)
|
|
||||||
} else if opts.ToBMP {
|
|
||||||
// convert image to 4-Bit BMP (16 colors)
|
|
||||||
imagick.Initialize()
|
|
||||||
|
|
||||||
mw := imagick.NewMagickWand()
|
|
||||||
defer mw.Destroy()
|
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
jpeg.Encode(b, i, &jpeg.Options{jpeg.DefaultQuality})
|
|
||||||
|
|
||||||
err := mw.ReadImageBlob(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error ReadImageBlob: %v\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
w := imagick.NewPixelWand()
|
|
||||||
w.SetColor("black")
|
|
||||||
defer w.Destroy()
|
|
||||||
|
|
||||||
var cs imagick.ColorspaceType = imagick.COLORSPACE_SRGB
|
|
||||||
if opts.Grayscale {
|
|
||||||
cs = imagick.COLORSPACE_GRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
mw.SetImageFormat("BMP3")
|
|
||||||
mw.SetImageBackgroundColor(w)
|
|
||||||
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE)
|
|
||||||
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE)
|
|
||||||
mw.SetImageMatte(false)
|
|
||||||
mw.SetImageCompression(imagick.COMPRESSION_NO)
|
|
||||||
mw.QuantizeImage(16, cs, 8, true, true)
|
|
||||||
mw.WriteImage(filename)
|
|
||||||
} else if opts.ToGIF {
|
|
||||||
// convert image to GIF
|
|
||||||
imagick.Initialize()
|
|
||||||
|
|
||||||
mw := imagick.NewMagickWand()
|
|
||||||
defer mw.Destroy()
|
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
jpeg.Encode(b, i, &jpeg.Options{jpeg.DefaultQuality})
|
|
||||||
|
|
||||||
err := mw.ReadImageBlob(b.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error ReadImageBlob: %v\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var cs imagick.ColorspaceType = imagick.COLORSPACE_SRGB
|
|
||||||
if opts.Grayscale {
|
|
||||||
cs = imagick.COLORSPACE_GRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
mw.SetImageFormat("GIF")
|
|
||||||
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE)
|
|
||||||
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE)
|
|
||||||
mw.SetImageMatte(false)
|
|
||||||
mw.SetImageCompression(imagick.COMPRESSION_LZW)
|
|
||||||
mw.QuantizeImage(256, cs, 8, true, true)
|
|
||||||
mw.WriteImage(filename)
|
|
||||||
} else {
|
|
||||||
// convert image to JPEG (default)
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error Create: %v\n", err.Error())
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
jpeg.Encode(f, i, &jpeg.Options{opts.Quality})
|
|
||||||
}
|
|
||||||
|
|
||||||
<-throttle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts PDF/EPUB/XPS document to CBZ
|
// Converts PDF/EPUB/XPS document to CBZ
|
||||||
@@ -269,7 +213,11 @@ func convertDocument(file string) {
|
|||||||
|
|
||||||
img, err := doc.Image(n)
|
img, err := doc.Image(n)
|
||||||
|
|
||||||
if err == nil && img != nil {
|
if err == nil {
|
||||||
|
img = transformImage(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
if img != nil {
|
||||||
throttle <- 1
|
throttle <- 1
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
@@ -338,15 +286,17 @@ func convertArchive(file string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.RGB && !isGrayScale(img) {
|
i := transformImage(img)
|
||||||
copyFile(bytes.NewReader(buf), filepath.Join(workdir, filepath.Base(pathname)))
|
|
||||||
|
if !opts.RGB && !isGrayScale(i) {
|
||||||
|
encodeImage(i, filepath.Join(workdir, filepath.Base(pathname)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if img != nil {
|
if i != nil {
|
||||||
throttle <- 1
|
throttle <- 1
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go convertImage(img, 0, pathname)
|
go convertImage(i, 0, pathname)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if opts.NonImage {
|
if opts.NonImage {
|
||||||
@@ -388,8 +338,10 @@ func convertDirectory(path string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i = transformImage(i)
|
||||||
|
|
||||||
if !opts.RGB && !isGrayScale(i) {
|
if !opts.RGB && !isGrayScale(i) {
|
||||||
copyFile(f, filepath.Join(workdir, filepath.Base(img)))
|
encodeImage(i, filepath.Join(workdir, filepath.Base(img)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +400,95 @@ func saveArchive(file string) {
|
|||||||
z.Close()
|
z.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decodes image from reader
|
||||||
|
func decodeImage(reader io.Reader, filename string) (i image.Image, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Recovered in decodeImage %s: %v\n", filename, r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
i, _, err = image.Decode(reader)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode image to file
|
||||||
|
func encodeImage(i image.Image, filename string) (err error) {
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filepath.Ext(filename) {
|
||||||
|
case ".png":
|
||||||
|
err = png.Encode(f, i)
|
||||||
|
case ".tif":
|
||||||
|
case ".tiff":
|
||||||
|
err = tiff.Encode(f, i, nil)
|
||||||
|
case ".gif":
|
||||||
|
err = gif.Encode(f, i, nil)
|
||||||
|
default:
|
||||||
|
err = jpeg.Encode(f, i, &jpeg.Options{opts.Quality})
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode image to file (ImageMagick)
|
||||||
|
func encodeImageMagick(i image.Image, filename string) (err error) {
|
||||||
|
imagick.Initialize()
|
||||||
|
|
||||||
|
mw := imagick.NewMagickWand()
|
||||||
|
defer mw.Destroy()
|
||||||
|
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
jpeg.Encode(b, i, &jpeg.Options{opts.Quality})
|
||||||
|
|
||||||
|
err = mw.ReadImageBlob(b.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error ReadImageBlob: %v\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Grayscale {
|
||||||
|
c := mw.GetImageColors()
|
||||||
|
mw.QuantizeImage(c, imagick.COLORSPACE_GRAY, 8, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch filepath.Ext(filename) {
|
||||||
|
case ".png":
|
||||||
|
mw.SetImageFormat("PNG")
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
case ".gif":
|
||||||
|
mw.SetImageFormat("GIF")
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
case ".bmp":
|
||||||
|
w := imagick.NewPixelWand()
|
||||||
|
w.SetColor("black")
|
||||||
|
defer w.Destroy()
|
||||||
|
|
||||||
|
cs := mw.GetImageColorspace()
|
||||||
|
if opts.Grayscale {
|
||||||
|
cs = imagick.COLORSPACE_GRAY
|
||||||
|
}
|
||||||
|
|
||||||
|
mw.SetImageFormat("BMP3")
|
||||||
|
mw.SetImageBackgroundColor(w)
|
||||||
|
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE)
|
||||||
|
mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE)
|
||||||
|
mw.SetImageMatte(false)
|
||||||
|
mw.SetImageCompression(imagick.COMPRESSION_NO)
|
||||||
|
mw.QuantizeImage(16, cs, 8, true, true)
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
default:
|
||||||
|
mw.SetImageFormat("JPEG")
|
||||||
|
mw.WriteImage(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Lists contents of archive
|
// Lists contents of archive
|
||||||
func listArchive(file string) []string {
|
func listArchive(file string) []string {
|
||||||
var contents []string
|
var contents []string
|
||||||
@@ -718,18 +759,6 @@ func isGrayScale(img image.Image) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes image from reader
|
|
||||||
func decodeImage(reader io.Reader, filename string) (i image.Image, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Recovered in decodeImage %s: %v\n", filename, r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
i, _, err = image.Decode(reader)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copies reader to file
|
// Copies reader to file
|
||||||
func copyFile(reader io.Reader, filename string) error {
|
func copyFile(reader io.Reader, filename string) error {
|
||||||
os.MkdirAll(filepath.Dir(filename), 0755)
|
os.MkdirAll(filepath.Dir(filename), 0755)
|
||||||
@@ -872,7 +901,7 @@ func parseFlags() {
|
|||||||
convert.Flag("png", "Encode images to PNG instead of JPEG").BoolVar(&opts.ToPNG)
|
convert.Flag("png", "Encode images to PNG instead of JPEG").BoolVar(&opts.ToPNG)
|
||||||
convert.Flag("bmp", "Encode images to 4-Bit BMP (16 colors) instead of JPEG").BoolVar(&opts.ToBMP)
|
convert.Flag("bmp", "Encode images to 4-Bit BMP (16 colors) instead of JPEG").BoolVar(&opts.ToBMP)
|
||||||
convert.Flag("gif", "Encode images to GIF instead of JPEG").BoolVar(&opts.ToGIF)
|
convert.Flag("gif", "Encode images to GIF instead of JPEG").BoolVar(&opts.ToGIF)
|
||||||
convert.Flag("rgb", "Convert images that have RGB colorspace (use --no-rgb if you only want to process grayscale images)").Default("true").BoolVar(&opts.RGB)
|
convert.Flag("rgb", "Convert images that have RGB colorspace (use --no-rgb if you only want to convert grayscale images)").Default("true").BoolVar(&opts.RGB)
|
||||||
convert.Flag("nonimage", "Leave non image files in archive (use --no-nonimage to remove non image files from archive)").Default("true").BoolVar(&opts.NonImage)
|
convert.Flag("nonimage", "Leave non image files in archive (use --no-nonimage to remove non image files from archive)").Default("true").BoolVar(&opts.NonImage)
|
||||||
convert.Flag("grayscale", "Convert images to grayscale (monochromatic)").BoolVar(&opts.Grayscale)
|
convert.Flag("grayscale", "Convert images to grayscale (monochromatic)").BoolVar(&opts.Grayscale)
|
||||||
convert.Flag("rotate", "Rotate images, valid values are 0, 90, 180, 270").Default(strconv.Itoa(0)).IntVar(&opts.Rotate)
|
convert.Flag("rotate", "Rotate images, valid values are 0, 90, 180, 270").Default(strconv.Itoa(0)).IntVar(&opts.Rotate)
|
||||||
|
Reference in New Issue
Block a user