diff --git a/kadai2/nejiyoshida/README.md b/kadai2/nejiyoshida/README.md new file mode 100644 index 0000000..db97607 --- /dev/null +++ b/kadai2/nejiyoshida/README.md @@ -0,0 +1,9 @@ +# 1.画像変換コマンドをつくろう + +## 使い方 +imgconv {-s source-extension} {-t target-extension} search-directory save-directory + +## 説明 +指定したディレクトリ配下を再帰的に探索し、指定形式の画像を変換します。 +保存先を指定する必要があり、指定の保存先が存在しない場合、新たにディレクトリが作られます。 +対応形式はjpg, png, bmp, gif の4種類です。 \ No newline at end of file diff --git a/kadai2/nejiyoshida/check/gopher2.jpg b/kadai2/nejiyoshida/check/gopher2.jpg new file mode 100644 index 0000000..7b392e7 Binary files /dev/null and b/kadai2/nejiyoshida/check/gopher2.jpg differ diff --git a/kadai2/nejiyoshida/check/gopher_favicon.png b/kadai2/nejiyoshida/check/gopher_favicon.png new file mode 100644 index 0000000..c7f3144 Binary files /dev/null and b/kadai2/nejiyoshida/check/gopher_favicon.png differ diff --git a/kadai2/nejiyoshida/dircrawler/dircrawler.go b/kadai2/nejiyoshida/dircrawler/dircrawler.go new file mode 100644 index 0000000..fd73a93 --- /dev/null +++ b/kadai2/nejiyoshida/dircrawler/dircrawler.go @@ -0,0 +1,56 @@ +// dircrawler パッケージは指定のディレクトリ以下にある指定の形式のファイルのリストを再帰的に取得する機能を提供します +package dircrawler + +import ( + "io/ioutil" + "log" + "path/filepath" +) + +// 指定ディレクトリ配下の特定形式のファイルパスリストを再帰的に取得します +func SearchSpecificFormatFiles(rootDir, format string) []string { + + paths := searchFilePaths(rootDir) + files := selectFormat(paths, format) + + return files + +} + +// 指定ディレクトリのファイルパスリストを再帰的に取得します +func searchFilePaths(rootDir string) []string { + files, err := ioutil.ReadDir(rootDir) + if err != nil { + log.Fatal(err) + } + + var paths []string + + for _, file := range files { + if file.IsDir() { + paths = append(paths, searchFilePaths(filepath.Join(rootDir, file.Name()))...) + continue + } + + paths = append(paths, filepath.Join(rootDir, file.Name())) + } + + return paths +} + +//ファイルパスリストから特定のフォーマットのもののみ抜き出します +func selectFormat(paths []string, format string) []string { + var files []string + for _, path := range paths { + if isSpecifiedFormat(path, format) { + files = append(files, path) + } + } + return files +} + +// ファイルパスが特定のフォーマットか確認します。 +func isSpecifiedFormat(path, format string) bool { + ext := filepath.Ext(path) + return ext == format +} diff --git a/kadai2/nejiyoshida/dircrawler/dircrawler_test.go b/kadai2/nejiyoshida/dircrawler/dircrawler_test.go new file mode 100644 index 0000000..1d06de9 --- /dev/null +++ b/kadai2/nejiyoshida/dircrawler/dircrawler_test.go @@ -0,0 +1,74 @@ +package dircrawler + +import ( + "fmt" + "reflect" + "testing" +) + +func ExampleisSpecifiedFormat() { + + fmt.Println(isSpecifiedFormat("hoge.jpg", ".jpg")) + //Output:true + fmt.Println(isSpecifiedFormat("huga.jpg", ".png")) + //Output:false + fmt.Println(isSpecifiedFormat("foo.png", ".png")) + //Output:true + fmt.Println(isSpecifiedFormat("bar.png", ".png")) + //Output:false +} + +func TestSelectFormat(t *testing.T) { + t.Helper() + cases := []struct { + ext string + files []string + ret []string + }{ + {ext: ".jpg", files: []string{"a.txt", "b.jpg", "c.jpg", "d.html", "e.png"}, ret: []string{"b.jpg", "c.jpg"}}, + {ext: ".exe", files: []string{"a.txt", "b.jpg", "c.jpg", "d.html", "e.png"}, ret: nil}, + {ext: ".jpg", files: []string{"./hoge/a.txt", "./hoge/huga/b.jpg", "./.git/c.jpg", "./d.html", "./e.png"}, ret: []string{"./hoge/huga/b.jpg", "./.git/c.jpg"}}, + } + for _, c := range cases { + if !reflect.DeepEqual(selectFormat(c.files, c.ext), c.ret) { + t.Errorf("test cases %v does not match\n", c) + } + } + +} + +func TestIsSpecifiedFormat(t *testing.T) { + t.Helper() + cases := []struct { + file string + ext string + ans bool + }{ + {file: "a.jpg", ext: ".jpg", ans: true}, + {file: "a.jpg", ext: ".txt", ans: false}, + {file: "./hoge/huga/a.png", ext: ".png", ans: true}, + {file: "./foo/bar/a.jpg", ext: ".png", ans: false}, + } + for _, c := range cases { + if !isSpecifiedFormat("./hoge/huga.jpg", ".jpg") { + t.Errorf("test cases %v is not %v formt\n", c.file, c.ext) + } + } +} +func TestSearchFilePaths(t *testing.T) { + t.Helper() + cases := []struct { + rootDir string + ret []string + }{ + {rootDir: "./test", ret: []string{"test\\TEST\\bar.txt", "test\\TEST\\gopher.jpg", "test\\foo.txt"}}, + {rootDir: "./test/TEST", ret: []string{"test\\TEST\\bar.txt", "test\\TEST\\gopher.jpg"}}, + } + for _, c := range cases { + + if !reflect.DeepEqual(searchFilePaths(c.rootDir), c.ret) { + t.Errorf("test case %v does not match\n", c) + } + + } +} diff --git a/kadai2/nejiyoshida/dircrawler/test/TEST/bar.txt b/kadai2/nejiyoshida/dircrawler/test/TEST/bar.txt new file mode 100644 index 0000000..e69de29 diff --git a/kadai2/nejiyoshida/dircrawler/test/TEST/gopher.jpg b/kadai2/nejiyoshida/dircrawler/test/TEST/gopher.jpg new file mode 100644 index 0000000..7b392e7 Binary files /dev/null and b/kadai2/nejiyoshida/dircrawler/test/TEST/gopher.jpg differ diff --git a/kadai2/nejiyoshida/dircrawler/test/foo.txt b/kadai2/nejiyoshida/dircrawler/test/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/kadai2/nejiyoshida/gopher.jpg b/kadai2/nejiyoshida/gopher.jpg new file mode 100644 index 0000000..7b392e7 Binary files /dev/null and b/kadai2/nejiyoshida/gopher.jpg differ diff --git a/kadai2/nejiyoshida/gopher_favicon.png b/kadai2/nejiyoshida/gopher_favicon.png new file mode 100644 index 0000000..c7f3144 Binary files /dev/null and b/kadai2/nejiyoshida/gopher_favicon.png differ diff --git a/kadai2/nejiyoshida/imgconv.go b/kadai2/nejiyoshida/imgconv.go new file mode 100644 index 0000000..b2aaaca --- /dev/null +++ b/kadai2/nejiyoshida/imgconv.go @@ -0,0 +1,28 @@ +package main + +import ( + "flag" + "fmt" + + "github.com/gopherdojo/dojo5/kadai1/nejiyoshida/dircrawler" + "github.com/gopherdojo/dojo5/kadai1/nejiyoshida/imgconverter" +) + +func main() { + + var ( + s string + t string + ) + + flag.StringVar(&s, "s", ".jpg", "format of image before conversion") + flag.StringVar(&t, "t", ".png", "format of image after conversion") + flag.Parse() + + args := flag.Args() + searchDir, saveDir := args[0], args[1] + files := dircrawler.SearchSpecificFormatFiles(searchDir, s) + c := imgconverter.New(t, saveDir, files) + c.Convert() + fmt.Println("finished") +} diff --git a/kadai2/nejiyoshida/imgconverter/imgconverter.go b/kadai2/nejiyoshida/imgconverter/imgconverter.go new file mode 100644 index 0000000..f048e96 --- /dev/null +++ b/kadai2/nejiyoshida/imgconverter/imgconverter.go @@ -0,0 +1,115 @@ +// imgconverter パッケージは指定の画像ファイルを指定の形式に変換する機能を提供します +package imgconverter + +import ( + "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" + "os" + "path/filepath" + "strings" + + "golang.org/x/image/bmp" +) + +type Client struct { + + // tgtExt:変換先のファイル形式 + tgtExt string + // savePath:保存先ディレクトリ、存在しない場合新たに作成されます + savePath string + // 変換対象の画像リスト + images []string +} + +func New(tgtExt, savePath string, images []string) *Client { + return &Client{ + + tgtExt: tgtExt, + savePath: savePath, + images: images, + } +} + +func (c *Client) Convert() { + + if _, err := os.Stat(c.savePath); os.IsNotExist(err) { + os.MkdirAll(c.savePath, 0755) + } + + for _, image := range c.images { + err := convertImage(image, c.tgtExt, c.savePath) + if err != nil { + fmt.Println(err) + } + } + + return +} + +//一枚の画像を特定のフォーマットに変換し指定のディレクトリに保存します +func convertImage(imgPath, tgtExt, savePath string) (err error) { + + if tgtExt == filepath.Ext(imgPath) { + err = fmt.Errorf("file \"%s\"'s extension is already %s, conversion skipped", imgPath, tgtExt) + } + if err == nil { + src, err := os.Open(imgPath) + if err == nil { + defer src.Close() + decoded, err := decode(src) + + if err == nil { + name := imgPath[0 : len(imgPath)-len(filepath.Ext(imgPath))] + name = filepath.Base(name) + + tgt, err := os.Create(savePath + "/" + name + tgtExt) + if err == nil { + defer tgt.Close() + err = encode(tgt, decoded) + } + } + } + } + return +} + +//画像のデコードを行います +func decode(srcImg *os.File) (decodedImg image.Image, err error) { + name := srcImg.Name() + ext := filepath.Ext(name) + switch strings.ToLower(ext) { + case ".jpg", ".jpeg": + decodedImg, err = jpeg.Decode(srcImg) + case ".png": + decodedImg, err = png.Decode(srcImg) + case ".gif": + decodedImg, err = gif.Decode(srcImg) + case ".bmp": + decodedImg, err = bmp.Decode(srcImg) + } + + return +} + +//特定の形式へのエンコードを行います +func encode(tgt *os.File, decodedImg image.Image) (err error) { + name := tgt.Name() + ext := filepath.Ext(name) + switch strings.ToLower(ext) { + case ".jpg", ".jpeg": + opt := &jpeg.Options{Quality: 100} + err = jpeg.Encode(tgt, decodedImg, opt) + case ".png": + err = png.Encode(tgt, decodedImg) + case ".gif": + opt := &gif.Options{} + err = gif.Encode(tgt, decodedImg, opt) + case ".bmp": + err = bmp.Encode(tgt, decodedImg) + } + + return +} diff --git a/kadai2/nejiyoshida/imgconverter/test/1.jpg b/kadai2/nejiyoshida/imgconverter/test/1.jpg new file mode 100644 index 0000000..7b392e7 Binary files /dev/null and b/kadai2/nejiyoshida/imgconverter/test/1.jpg differ diff --git a/kadai2/nejiyoshida/imgconverter/test/2.png b/kadai2/nejiyoshida/imgconverter/test/2.png new file mode 100644 index 0000000..c7f3144 Binary files /dev/null and b/kadai2/nejiyoshida/imgconverter/test/2.png differ