From 0e2806f80a0d826c367c64c9dfb2918203295269 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Thu, 27 Mar 2025 22:09:43 +0300 Subject: [PATCH] add jpeg generation, refactor (#2) --- barnsley-fern.go | 108 +++-------------------------------------------- color.go | 47 +++++++++++++++++++++ image.go | 68 +++++++++++++++++++++++++++++ main.go | 38 +++++++++++++++++ settings.go | 63 +++++++++++++++++++++++++++ 5 files changed, 222 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..0932fbd --- /dev/null +++ b/image.go @@ -0,0 +1,68 @@ +package main + +import ( + "errors" + "fmt" + "image" + "image/color" + "image/jpeg" + "image/png" + "io" +) + +var ( + errEncodeImage = errors.New("не удалось создать изображение") + errUnknownFormat = errors.New("неизвестный формат изображения") +) + +func createBarnsleyFernImage(s *settings, w io.Writer) error { + img := newImageWithBackGround(s.X, s.Y, s.BG) + + drawBarnsleyFern(img, newColorFunc(s.CM), s.Dots) + + switch s.OM { + case outputjpeg: + opts := &jpeg.Options{ + Quality: 100, + } + if err := jpeg.Encode(w, img, opts); err != nil { + return errors.Join(errEncodeImage, err) + } + + case outputpng: + if err := png.Encode(w, img); err != nil { + return errors.Join(errEncodeImage, err) + } + + default: + return fmt.Errorf("%w: %d", s.OM) + } + + 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..a2f887a --- /dev/null +++ b/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + exitCode := run() + os.Exit(exitCode) +} + +func run() int { + s := parseSettings() + + file, err := os.Create(s.Fname) + if err != nil { + fmt.Println("ошибка открытия файла", err) + return 1 + } + + defer func() { + }() + + 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 new file mode 100644 index 0000000..6b5646f --- /dev/null +++ b/settings.go @@ -0,0 +1,63 @@ +package main + +import ( + "flag" + "fmt" + "image/color" +) + +type colormode int + +const ( + cdefault colormode = iota + crandom + ctimed + crainbow +) + +type outputmode int + +const ( + outputpng outputmode = iota + outputjpeg +) + +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 + OM outputmode // what to output + Fname string // filename for output +} + +func parseSettings() *settings { + var ( + 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, "c", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow") + flag.IntVar(&output, "o", 0, fmt.Sprintf("режим вывода: %d - png, %d - jpeg", outputpng, outputjpeg)) + flag.StringVar(&fname, "out", "barnsley-fern.png", "полный путь файла для записи изображения") + + flag.Parse() + + s := &settings{ + X: x, + Y: y, + Dots: dots, + BG: color.White, + CM: colormode(cmode), + OM: outputmode(output), + Fname: fname, + } + + return s +}