Added tool to crop & resize images.
This commit is contained in:
parent
d6502c9080
commit
1e407e21c0
72
cmd/imadapt/args.go
Normal file
72
cmd/imadapt/args.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func atois(s ...string) ([]int, error) {
|
||||||
|
ints := make([]int, len(s))
|
||||||
|
for i, s := range s {
|
||||||
|
value, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't convert '%s' to a number; error: %v", s, err)
|
||||||
|
}
|
||||||
|
ints[i] = value
|
||||||
|
}
|
||||||
|
return ints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanDir(dir string) ([]string, error) {
|
||||||
|
var files []string
|
||||||
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if path == dir || info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
files = append(files, path)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type point struct {
|
||||||
|
x, y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePoint(s string) (point, error) {
|
||||||
|
components := strings.Split(s, ",")
|
||||||
|
if len(components) != 2 {
|
||||||
|
return point{}, errors.New("expected two components separated by a comma")
|
||||||
|
}
|
||||||
|
ints, err := atois(components...)
|
||||||
|
if err != nil {
|
||||||
|
return point{}, err
|
||||||
|
}
|
||||||
|
return point{x: ints[0], y: ints[1]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type rect struct {
|
||||||
|
min, max point
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRect(s string) (rect, error) {
|
||||||
|
components := strings.Split(s, ",")
|
||||||
|
if len(components) != 4 {
|
||||||
|
return rect{}, errors.New("expected four components separated by a comma")
|
||||||
|
}
|
||||||
|
ints, err := atois(components...)
|
||||||
|
if err != nil {
|
||||||
|
return rect{}, err
|
||||||
|
}
|
||||||
|
return rect{min: point{x: ints[0], y: ints[1]}, max: point{x: ints[2], y: ints[3]}}, nil
|
||||||
|
}
|
25
cmd/imadapt/crop.go
Normal file
25
cmd/imadapt/crop.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
|
||||||
|
"github.com/nfnt/resize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func crop(path string, crop rect, size *point) error {
|
||||||
|
src, err := decodeImage(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
srcRect := image.Rect(crop.min.x, crop.min.y, crop.max.x, crop.max.y)
|
||||||
|
dstRect := image.Rect(0, 0, crop.max.x-crop.min.x, crop.max.y-crop.min.y)
|
||||||
|
dst := image.NewRGBA(dstRect)
|
||||||
|
draw.Draw(dst, dstRect, src, srcRect.Min, draw.Src)
|
||||||
|
if size == nil {
|
||||||
|
return encodePNG(path, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
resized := resize.Resize(uint(size.x), uint(size.y), dst, resize.Bilinear)
|
||||||
|
return encodePNG(path, resized)
|
||||||
|
}
|
29
cmd/imadapt/gray.go
Normal file
29
cmd/imadapt/gray.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
func convertToGray(path string) error {
|
||||||
|
src, err := decodeImage(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bounds := src.Bounds()
|
||||||
|
dst := image.NewRGBA(bounds)
|
||||||
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||||
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||||
|
c := src.At(x, y)
|
||||||
|
rgba := color.NRGBAModel.Convert(c).(color.NRGBA)
|
||||||
|
if rgba.A > 0 {
|
||||||
|
gray := color.GrayModel.Convert(c).(color.Gray)
|
||||||
|
rgba.R, rgba.G, rgba.B = gray.Y, gray.Y, gray.Y
|
||||||
|
dst.Set(x, y, rgba)
|
||||||
|
} else {
|
||||||
|
dst.Set(x, y, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encodePNG(path, dst)
|
||||||
|
}
|
@ -4,11 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"image/png"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
@ -33,76 +29,54 @@ func run() error {
|
|||||||
flags := flag.NewFlagSet("gray", flag.ContinueOnError)
|
flags := flag.NewFlagSet("gray", flag.ContinueOnError)
|
||||||
flags.Parse(args[1:])
|
flags.Parse(args[1:])
|
||||||
for _, path := range flags.Args() {
|
for _, path := range flags.Args() {
|
||||||
err := gray(path)
|
err := convertToGray(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't convert to grayscale of '%s'; error: %v", path, err)
|
return fmt.Errorf("couldn't convert to grayscale of '%s'; error: %v", path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "crop":
|
||||||
|
flags := flag.NewFlagSet("crop", flag.ContinueOnError)
|
||||||
|
var rect string
|
||||||
|
var resize string
|
||||||
|
var dir string
|
||||||
|
flags.StringVar(&rect, "crop", "", "sets the crop rectangle \"x1,y1,y1,y2\"")
|
||||||
|
flags.StringVar(&resize, "resize", "", "sets the target size, \"width,height\", if not specified the crop rectangle size is used")
|
||||||
|
flags.StringVar(&dir, "dir", "", "crops all images in the specified directory (recursively)")
|
||||||
|
flags.Parse(args[1:])
|
||||||
|
|
||||||
|
cropRect, err := parseRect(rect)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("crop rectangle is invalid: error: %v", err)
|
||||||
|
}
|
||||||
|
var size *point
|
||||||
|
if len(resize) != 0 {
|
||||||
|
p, err := parsePoint(resize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("resize parameter is invalid: error: %v", err)
|
||||||
|
}
|
||||||
|
size = &p
|
||||||
|
}
|
||||||
|
|
||||||
|
var files []string
|
||||||
|
if len(dir) == 0 {
|
||||||
|
files = flags.Args()
|
||||||
|
} else {
|
||||||
|
files, err = scanDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't find files to crop; error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range files {
|
||||||
|
err := crop(path, cropRect, size)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't crop '%s'; error: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAlpha(path string, alpha int) error {
|
|
||||||
src, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer src.Close()
|
|
||||||
im, _, err := image.Decode(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bounds := im.Bounds()
|
|
||||||
dst := image.NewNRGBA(bounds)
|
|
||||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
||||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|
||||||
c := color.NRGBAModel.Convert(im.At(x, y)).(color.NRGBA)
|
|
||||||
if c.A > 0 {
|
|
||||||
c.A = uint8(alpha)
|
|
||||||
}
|
|
||||||
dst.Set(x, y, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encodePNG(path, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
func gray(path string) error {
|
|
||||||
src, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer src.Close()
|
|
||||||
im, _, err := image.Decode(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bounds := im.Bounds()
|
|
||||||
dst := image.NewRGBA(bounds)
|
|
||||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
|
||||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
|
||||||
c := im.At(x, y)
|
|
||||||
rgba := color.NRGBAModel.Convert(c).(color.NRGBA)
|
|
||||||
if rgba.A > 0 {
|
|
||||||
gray := color.GrayModel.Convert(c).(color.Gray)
|
|
||||||
rgba.R, rgba.G, rgba.B = gray.Y, gray.Y, gray.Y
|
|
||||||
dst.Set(x, y, rgba)
|
|
||||||
} else {
|
|
||||||
dst.Set(x, y, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encodePNG(path, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodePNG(path string, im image.Image) error {
|
|
||||||
dst, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer dst.Close()
|
|
||||||
return png.Encode(dst, im)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := run(); err != nil {
|
if err := run(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
29
cmd/imadapt/io.go
Normal file
29
cmd/imadapt/io.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encodePNG(path string, im image.Image) error {
|
||||||
|
dst, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
return png.Encode(dst, im)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeImage(path string) (image.Image, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
im, _, err := image.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return im, nil
|
||||||
|
}
|
25
cmd/imadapt/setalpha.go
Normal file
25
cmd/imadapt/setalpha.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setAlpha(path string, alpha int) error {
|
||||||
|
src, err := decodeImage(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bounds := src.Bounds()
|
||||||
|
dst := image.NewNRGBA(bounds)
|
||||||
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||||
|
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||||
|
c := color.NRGBAModel.Convert(src.At(x, y)).(color.NRGBA)
|
||||||
|
if c.A > 0 {
|
||||||
|
c.A = uint8(alpha)
|
||||||
|
}
|
||||||
|
dst.Set(x, y, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encodePNG(path, dst)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user