From a95b833004d20a17bee745e80fb18ee7d2ead959 Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Tue, 25 Mar 2025 22:18:06 +0300 Subject: [PATCH 1/6] add Makefile --- Makefile | 8 ++++++++ go.mod | 2 ++ go.sum | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 Makefile create mode 100644 go.sum diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..76a2d60 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.PHONY: build, clean + +build: + go build -v + +clean: + rm -f barnsley-fern-go barnsley-fern.png + diff --git a/go.mod b/go.mod index 69ed724..cc9bd77 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module code.uint32.ru/dmitry/barnsley-fern-go go 1.24.1 + +require github.com/AlexEidt/Vidio v1.5.1 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..022a789 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/AlexEidt/Vidio v1.5.1 h1:tovwvtgQagUz1vifiL9OeWkg1fP/XUzFazFKh7tFtaE= +github.com/AlexEidt/Vidio v1.5.1/go.mod h1:djhIMnWMqPrC3X6nB6ymGX6uWWlgw+VayYGKE1bNwmI= -- 2.49.1 From c3b20367134d54ad3b8d3f5da748ee226207eea9 Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Tue, 25 Mar 2025 23:55:13 +0300 Subject: [PATCH 2/6] WIP --- barnsley-fern.go | 108 +++-------------------------------------------- color.go | 47 +++++++++++++++++++++ image.go | 49 +++++++++++++++++++++ main.go | 29 +++++++++++++ settings.go | 56 ++++++++++++++++++++++++ 5 files changed, 187 insertions(+), 102 deletions(-) create mode 100644 color.go create mode 100644 image.go create mode 100644 main.go create mode 100644 settings.go diff --git a/barnsley-fern.go b/barnsley-fern.go index ffb0ba9..5d7351e 100644 --- a/barnsley-fern.go +++ b/barnsley-fern.go @@ -1,78 +1,12 @@ package main import ( - "flag" - "fmt" "image" - "image/color" - "image/png" "math/rand" - "os" "time" ) -func createImage(h, v int) *image.RGBA { - rect := image.Rectangle{ - image.Point{0, 0}, - image.Point{h, v}, - } - return image.NewRGBA(rect) -} - -func fillBackground(img *image.RGBA, c color.Color) { - rect := img.Bounds() - for x := range rect.Max.X { - for y := range rect.Max.Y { - img.Set(x, y, c) - } - } -} - -func newColorFunc(random bool, timed bool, rainbow bool) func(float64, float64) color.Color { - return func(x, y float64) color.Color { - var ( - r, g, b, a uint8 - ) - - switch { - case random: - r = uint8(rand.Intn(256)) - g = uint8(rand.Intn(256)) - b = uint8(rand.Intn(256)) - a = 255 - - case timed: - n := time.Now().Nanosecond() % 256 // Nanosecond returns range [0, 999999999] - - r = uint8((n + 256) % 256) - g = uint8((n + 128) % 256) - b = uint8(n % 256) - - a = 255 - - case rainbow: - r = uint8(y * 256) - g = uint8(x * 256) - b = uint8(((y - x) - min(x, y)) * 256) - - a = 255 - - default: - r, g, b, a = 0, 153, 0, 255 - } - - return color.RGBA{r, g, b, a} - } -} - -func min(a, b float64) float64 { - if a > b { - a = b - } - return a -} - -func drawBarnsleyFern(img *image.RGBA, colorfunc func(float64, float64) color.Color, dots int) { +func drawBarnsleyFern(img *image.RGBA, colorfunc colorFunc, dots int) { var ( x, y, tmpx, tmpy, r, maxy, maxx, scale, yoffset, xoffset float64 ) @@ -107,45 +41,15 @@ func drawBarnsleyFern(img *image.RGBA, colorfunc func(float64, float64) color.Co // рисуем точку x, y = tmpx, tmpy - color := colorfunc(x, y) + color := colorfunc(x, y, time.Now()) img.Set(int(xoffset+x*scale), int(maxy-yoffset-y*scale), color) } } -func main() { - var ( - h = flag.Int("h", 1920, "размер картинки по горизонтали") - v = flag.Int("v", 1080, "размер картинки по вертикали") - dots = flag.Int("d", 100000, "сколько точек рисовать") - rand = flag.Bool("rand", false, "использовать случайные цвета") - timed = flag.Bool("timed", false, "привязка к наносекундам времени исполнения") - rainbow = flag.Bool("rainbow", false, "привязка к координатам") - filename = flag.String("out", "barnsley-fern.png", "полный путь файла для записи изображения") - ) - - flag.Parse() - - img := createImage(*h, *v) - - fillBackground(img, color.White) - - colorfunc := newColorFunc(*rand, *timed, *rainbow) - - drawBarnsleyFern(img, colorfunc, *dots) - - f, err := os.Create(*filename) - if err != nil { - fmt.Printf("не удалось создать файл %s: %v", *filename, err) - return +func min(a, b float64) float64 { + if a > b { + a = b } - - defer f.Close() - - if err = png.Encode(f, img); err != nil { - fmt.Printf("не удалось сохранить изображение: %v", err) - return - } - - fmt.Println("done...") + return a } diff --git a/color.go b/color.go new file mode 100644 index 0000000..56bd929 --- /dev/null +++ b/color.go @@ -0,0 +1,47 @@ +package main + +import ( + "image/color" + "math/rand" + "time" +) + +type colorFunc func(float64, float64, time.Time) color.Color + +func newColorFunc(c colormode) colorFunc { + start := time.Now() + return func(x, y float64, t time.Time) color.Color { + var ( + r, g, b, a uint8 + ) + + switch c { + case crandom: + r = uint8(rand.Intn(256)) + g = uint8(rand.Intn(256)) + b = uint8(rand.Intn(256)) + a = 255 + + case ctimed: + n := time.Since(start).Nanoseconds() + + r = uint8(n % 256) + g = uint8((n + 128) % 256) + //b = uint8((n + 1) % 256) + + a = 255 + + case crainbow: + r = uint8(y * 256) + g = uint8(x * 256) + b = uint8(((y - x) - min(x, y)) * 256) + + a = 255 + + default: + r, g, b, a = 0, 153, 0, 255 + } + + return color.RGBA{r, g, b, a} + } +} diff --git a/image.go b/image.go new file mode 100644 index 0000000..bf20338 --- /dev/null +++ b/image.go @@ -0,0 +1,49 @@ +package main + +import ( + "errors" + "image" + "image/color" + "image/png" + "io" +) + +var erreEcnodePng = errors.New("не удалось создать png изображение") + +func createBarnsleyFernPng(s *settings, w io.Writer) error { + img := newImageWithBackGround(s.X, s.Y, s.BG) + + drawBarnsleyFern(img, newColorFunc(s.CM), s.Dots) + + if err := png.Encode(w, img); err != nil { + return errors.Join(erreEcnodePng, err) + } + + return nil +} + +func newImageWithBackGround(x, y int, background color.Color) *image.RGBA { + img := createImage(x, y) + + fillBackground(img, background) + + return img +} + +func createImage(x, y int) *image.RGBA { + rect := image.Rectangle{ + image.Point{0, 0}, + image.Point{x, y}, + } + + return image.NewRGBA(rect) +} + +func fillBackground(img *image.RGBA, c color.Color) { + rect := img.Bounds() + for x := range rect.Max.X { + for y := range rect.Max.Y { + img.Set(x, y, c) + } + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..1b767ca --- /dev/null +++ b/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + filename, settings := parseSettings() + + file, err := os.Create(filename) + if err != nil { + fmt.Println("ошибка открытия файла", err) + return + } + + defer func() { + if err := file.Close(); err != nil { + fmt.Println("ошибка закрытия файла", err) + } + }() + + if err := createBarnsleyFernPng(settings, file); err != nil { + fmt.Println("ошибка создания изображения:", err) + return + } + + fmt.Println("done...") +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..ebc650a --- /dev/null +++ b/settings.go @@ -0,0 +1,56 @@ +package main + +import ( + "flag" + "image/color" +) + +type colormode int + +const ( + cdefault colormode = iota + crandom + ctimed + crainbow +) + +type outputmode int + +const ( + outputpng outputmode = iota + outputvideo +) + +type settings struct { + X, Y int // image size + Dots int // dots to draw for single image + BG color.Color // background color + CM colormode // settings for colorFunc +} + +func parseSettings() (string, *settings) { + var ( + x, y int + dots int + cmode int + fname string + ) + + flag.IntVar(&x, "x", 1920, "размер картинки по горизонтали") + flag.IntVar(&y, "y", 1080, "размер картинки по вертикали") + flag.IntVar(&dots, "d", 100000, "сколько точек рисовать") + flag.IntVar(&cmode, "color", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow") + flag.StringVar(&fname, "out", "barnsley-fern.png", "полный путь файла для записи изображения") + + flag.Parse() + + s := &settings{ + X: x, + Y: y, + Dots: dots, + BG: color.White, + CM: colormode(cmode), + } + + return fname, s +} -- 2.49.1 From d72540d0c3865ce03ae51c8849a059c67da88565 Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Wed, 26 Mar 2025 00:51:46 +0300 Subject: [PATCH 3/6] add video encoding. broken for now --- go.mod | 2 +- image.go | 4 ++-- main.go | 21 +++++++++++++++++---- settings.go | 36 +++++++++++++++++++++--------------- video.go | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 video.go diff --git a/go.mod b/go.mod index cc9bd77..01b0c92 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module code.uint32.ru/dmitry/barnsley-fern-go go 1.24.1 -require github.com/AlexEidt/Vidio v1.5.1 // indirect +require github.com/AlexEidt/Vidio v1.5.1 diff --git a/image.go b/image.go index bf20338..fbc86f7 100644 --- a/image.go +++ b/image.go @@ -8,7 +8,7 @@ import ( "io" ) -var erreEcnodePng = errors.New("не удалось создать png изображение") +var errEncodePng = errors.New("не удалось создать png изображение") func createBarnsleyFernPng(s *settings, w io.Writer) error { img := newImageWithBackGround(s.X, s.Y, s.BG) @@ -16,7 +16,7 @@ func createBarnsleyFernPng(s *settings, w io.Writer) error { drawBarnsleyFern(img, newColorFunc(s.CM), s.Dots) if err := png.Encode(w, img); err != nil { - return errors.Join(erreEcnodePng, err) + return errors.Join(errEncodePng, err) } return nil diff --git a/main.go b/main.go index 1b767ca..10de3e8 100644 --- a/main.go +++ b/main.go @@ -2,13 +2,26 @@ package main import ( "fmt" + "io" "os" ) func main() { - filename, settings := parseSettings() + s := parseSettings() - file, err := os.Create(filename) + var processor func(*settings, io.Writer) error + + switch s.OM { + case outputpng: + processor = createBarnsleyFernPng + case outputvideo: + processor = createBarnsLeyFernVideo + default: + fmt.Println("неизвестный тип вывода:", s.OM) + return + } + + file, err := os.Create(s.Fname) if err != nil { fmt.Println("ошибка открытия файла", err) return @@ -20,8 +33,8 @@ func main() { } }() - if err := createBarnsleyFernPng(settings, file); err != nil { - fmt.Println("ошибка создания изображения:", err) + if err := processor(s, file); err != nil { + fmt.Println("ошибка создания объекта:", err) return } diff --git a/settings.go b/settings.go index ebc650a..8b67c6f 100644 --- a/settings.go +++ b/settings.go @@ -22,35 +22,41 @@ const ( ) type settings struct { - X, Y int // image size - Dots int // dots to draw for single image - BG color.Color // background color - CM colormode // settings for colorFunc + X, Y int // image size + Dots int // dots to draw for single image + BG color.Color // background color + CM colormode // settings for colorFunc + OM outputmode // what to output + Fname string // filename for output } -func parseSettings() (string, *settings) { +func parseSettings() *settings { var ( - x, y int - dots int - cmode int - fname string + x, y int + dots int + cmode int + output int + fname string ) flag.IntVar(&x, "x", 1920, "размер картинки по горизонтали") flag.IntVar(&y, "y", 1080, "размер картинки по вертикали") flag.IntVar(&dots, "d", 100000, "сколько точек рисовать") flag.IntVar(&cmode, "color", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow") + flag.IntVar(&output, "o", 0, "режим вывода: 0 - изображение png, 1 - видео") flag.StringVar(&fname, "out", "barnsley-fern.png", "полный путь файла для записи изображения") flag.Parse() s := &settings{ - X: x, - Y: y, - Dots: dots, - BG: color.White, - CM: colormode(cmode), + X: x, + Y: y, + Dots: dots, + BG: color.White, + CM: colormode(cmode), + OM: outputmode(output), + Fname: fname, } - return fname, s + return s } diff --git a/video.go b/video.go new file mode 100644 index 0000000..d6f3d9b --- /dev/null +++ b/video.go @@ -0,0 +1,38 @@ +package main + +import ( + "bytes" + "errors" + "io" + + vid "github.com/AlexEidt/Vidio" +) + +var erreEncodeVideo = errors.New("не удалось создать видео") + +func createBarnsLeyFernVideo(s *settings, w io.Writer) error { + opts := &vid.Options{ + FPS: 1, + Quality: 0, + Delay: 1000, + } + + vw, err := vid.NewVideoWriter(s.Fname, s.X, s.Y, opts) + if err != nil { + return errors.Join(erreEncodeVideo, err) + } + + frames := 100 + for range frames { + imgbuf := new(bytes.Buffer) + if err := createBarnsleyFernPng(s, imgbuf); err != nil { + return errors.Join(erreEncodeVideo, err) + } + + if err := vw.Write(imgbuf.Bytes()); err != nil { + return errors.Join(erreEncodeVideo, err) + } + } + + return nil +} -- 2.49.1 From b5df6e8c392413053d56117534807eac7e426c5e Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Wed, 26 Mar 2025 22:11:52 +0300 Subject: [PATCH 4/6] add jpeg encoder --- image.go | 16 ++++++++++++++-- settings.go | 6 ++++-- video.go | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/image.go b/image.go index fbc86f7..7ed023f 100644 --- a/image.go +++ b/image.go @@ -4,6 +4,7 @@ import ( "errors" "image" "image/color" + "image/jpeg" "image/png" "io" ) @@ -15,8 +16,19 @@ func createBarnsleyFernPng(s *settings, w io.Writer) error { drawBarnsleyFern(img, newColorFunc(s.CM), s.Dots) - if err := png.Encode(w, img); err != nil { - return errors.Join(errEncodePng, err) + switch s.OM { + case outputjpeg: + opts := &jpeg.Options{ + Quality: 100, + } + if err := jpeg.Encode(w, img, opts); err != nil { + return errors.Join(errEncodePng, err) + } + + case outputpng: + if err := png.Encode(w, img); err != nil { + return errors.Join(errEncodePng, err) + } } return nil diff --git a/settings.go b/settings.go index 8b67c6f..a0b9b9e 100644 --- a/settings.go +++ b/settings.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "image/color" ) @@ -18,6 +19,7 @@ type outputmode int const ( outputpng outputmode = iota + outputjpeg outputvideo ) @@ -42,8 +44,8 @@ func parseSettings() *settings { flag.IntVar(&x, "x", 1920, "размер картинки по горизонтали") flag.IntVar(&y, "y", 1080, "размер картинки по вертикали") flag.IntVar(&dots, "d", 100000, "сколько точек рисовать") - flag.IntVar(&cmode, "color", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow") - flag.IntVar(&output, "o", 0, "режим вывода: 0 - изображение png, 1 - видео") + flag.IntVar(&cmode, "c", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow") + flag.IntVar(&output, "o", 0, fmt.Sprintf("режим вывода: %d - png, %d - jpeg, %d - видео", outputpng, outputjpeg, outputvideo)) flag.StringVar(&fname, "out", "barnsley-fern.png", "полный путь файла для записи изображения") flag.Parse() diff --git a/video.go b/video.go index d6f3d9b..2967974 100644 --- a/video.go +++ b/video.go @@ -11,6 +11,8 @@ import ( var erreEncodeVideo = errors.New("не удалось создать видео") func createBarnsLeyFernVideo(s *settings, w io.Writer) error { + s.OM = outputjpeg + opts := &vid.Options{ FPS: 1, Quality: 0, -- 2.49.1 From 71b95dbfae0a6a44d11c2bc0138edc57e3cae3d6 Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Thu, 27 Mar 2025 22:04:21 +0300 Subject: [PATCH 5/6] png and jpeg gen, refactoring --- image.go | 15 +++++++++++---- main.go | 36 ++++++++++++++++-------------------- settings.go | 3 +-- video.go | 40 ---------------------------------------- 4 files changed, 28 insertions(+), 66 deletions(-) delete mode 100644 video.go diff --git a/image.go b/image.go index 7ed023f..0932fbd 100644 --- a/image.go +++ b/image.go @@ -2,6 +2,7 @@ package main import ( "errors" + "fmt" "image" "image/color" "image/jpeg" @@ -9,9 +10,12 @@ import ( "io" ) -var errEncodePng = errors.New("не удалось создать png изображение") +var ( + errEncodeImage = errors.New("не удалось создать изображение") + errUnknownFormat = errors.New("неизвестный формат изображения") +) -func createBarnsleyFernPng(s *settings, w io.Writer) error { +func createBarnsleyFernImage(s *settings, w io.Writer) error { img := newImageWithBackGround(s.X, s.Y, s.BG) drawBarnsleyFern(img, newColorFunc(s.CM), s.Dots) @@ -22,13 +26,16 @@ func createBarnsleyFernPng(s *settings, w io.Writer) error { Quality: 100, } if err := jpeg.Encode(w, img, opts); err != nil { - return errors.Join(errEncodePng, err) + return errors.Join(errEncodeImage, err) } case outputpng: if err := png.Encode(w, img); err != nil { - return errors.Join(errEncodePng, err) + return errors.Join(errEncodeImage, err) } + + default: + return fmt.Errorf("%w: %d", s.OM) } return nil diff --git a/main.go b/main.go index 10de3e8..a2f887a 100644 --- a/main.go +++ b/main.go @@ -2,41 +2,37 @@ package main import ( "fmt" - "io" "os" ) func main() { + exitCode := run() + os.Exit(exitCode) +} + +func run() int { s := parseSettings() - var processor func(*settings, io.Writer) error - - switch s.OM { - case outputpng: - processor = createBarnsleyFernPng - case outputvideo: - processor = createBarnsLeyFernVideo - default: - fmt.Println("неизвестный тип вывода:", s.OM) - return - } - file, err := os.Create(s.Fname) if err != nil { fmt.Println("ошибка открытия файла", err) - return + return 1 } defer func() { - if err := file.Close(); err != nil { - fmt.Println("ошибка закрытия файла", err) - } }() - if err := processor(s, file); err != nil { - fmt.Println("ошибка создания объекта:", err) - return + if err := createBarnsleyFernImage(s, file); err != nil { + fmt.Println("ошибка создания изображения:", err) + return 2 + } + + if err := file.Close(); err != nil { + fmt.Println("ошибка закрытия файла:", err) + return 3 } fmt.Println("done...") + + return 0 } diff --git a/settings.go b/settings.go index a0b9b9e..6b5646f 100644 --- a/settings.go +++ b/settings.go @@ -20,7 +20,6 @@ type outputmode int const ( outputpng outputmode = iota outputjpeg - outputvideo ) type settings struct { @@ -45,7 +44,7 @@ func parseSettings() *settings { flag.IntVar(&y, "y", 1080, "размер картинки по вертикали") flag.IntVar(&dots, "d", 100000, "сколько точек рисовать") flag.IntVar(&cmode, "c", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow") - flag.IntVar(&output, "o", 0, fmt.Sprintf("режим вывода: %d - png, %d - jpeg, %d - видео", outputpng, outputjpeg, outputvideo)) + flag.IntVar(&output, "o", 0, fmt.Sprintf("режим вывода: %d - png, %d - jpeg", outputpng, outputjpeg)) flag.StringVar(&fname, "out", "barnsley-fern.png", "полный путь файла для записи изображения") flag.Parse() diff --git a/video.go b/video.go deleted file mode 100644 index 2967974..0000000 --- a/video.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "io" - - vid "github.com/AlexEidt/Vidio" -) - -var erreEncodeVideo = errors.New("не удалось создать видео") - -func createBarnsLeyFernVideo(s *settings, w io.Writer) error { - s.OM = outputjpeg - - opts := &vid.Options{ - FPS: 1, - Quality: 0, - Delay: 1000, - } - - vw, err := vid.NewVideoWriter(s.Fname, s.X, s.Y, opts) - if err != nil { - return errors.Join(erreEncodeVideo, err) - } - - frames := 100 - for range frames { - imgbuf := new(bytes.Buffer) - if err := createBarnsleyFernPng(s, imgbuf); err != nil { - return errors.Join(erreEncodeVideo, err) - } - - if err := vw.Write(imgbuf.Bytes()); err != nil { - return errors.Join(erreEncodeVideo, err) - } - } - - return nil -} -- 2.49.1 From 4dc6e6a0413608530994d3100bb27c942a760fab Mon Sep 17 00:00:00 2001 From: Dmitry Fedotov Date: Thu, 27 Mar 2025 22:06:48 +0300 Subject: [PATCH 6/6] fix: go.mod --- go.mod | 2 -- go.sum | 2 -- 2 files changed, 4 deletions(-) diff --git a/go.mod b/go.mod index 01b0c92..69ed724 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module code.uint32.ru/dmitry/barnsley-fern-go go 1.24.1 - -require github.com/AlexEidt/Vidio v1.5.1 diff --git a/go.sum b/go.sum index 022a789..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +0,0 @@ -github.com/AlexEidt/Vidio v1.5.1 h1:tovwvtgQagUz1vifiL9OeWkg1fP/XUzFazFKh7tFtaE= -github.com/AlexEidt/Vidio v1.5.1/go.mod h1:djhIMnWMqPrC3X6nB6ymGX6uWWlgw+VayYGKE1bNwmI= -- 2.49.1