Initial commit.
This commit is contained in:
		
							
								
								
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| MIT License | ||||
|  | ||||
| Copyright (c) 2025 smariot | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										36
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| # Golang FFMPEG Animation Package | ||||
|  | ||||
| This package creates an image that can be drawn on, and provides an `EmitFrame()` method to pipe the image to [FFMPEG](https://ffmpeg.org/), and a `Close()` method to finish the animation. | ||||
|  | ||||
| The process of actually drawing frames of animation is left as an exercise for the reader. | ||||
|  | ||||
| The extension of the destination file determines the encoding format and parameters: | ||||
|  | ||||
| - `.gif`: Creates a looping indexed GIF animation. | ||||
|    | ||||
|   Generating an optimal palette would require buffering the entire animation, so instead a fixed general purpose palette is used. | ||||
| - `.png`/`.apng`: Creates a looping animated PNG image. Lossless and supports transparency. | ||||
|    | ||||
|   This is the only supported format that is both completely lossless and supports transparency. | ||||
| - `.mp4`: Creates an MP4 video using the `libx264rgb` codec with the `ultrafast` preset. Should be lossless, but doesn't support transparency. | ||||
|    | ||||
|   The file will be quite large, and probably isn't something you'd want to distribute directly. | ||||
| - `.mkv`: Identical to MP4, except with a Matroska container. | ||||
| - `.webp`: Creates a looping WEBP animation. The RGB->YUV conversion is lossy, but is otherwise lossless and supports transparency. | ||||
| - `.webm`: Creates a WEBM video. The RGB->YUV conversion is lossy, but is otherwise lossless and supports transparency. | ||||
|  | ||||
| ## Installation | ||||
|  | ||||
| ```bash | ||||
| go get smariot.com/animate | ||||
| ``` | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| You can find the documentation at https://pkg.go.dev/smariot.com/animate. | ||||
|  | ||||
| ## Demo | ||||
|  | ||||
| The `cmd/demo` directory contains a simple program to demonstrate this package by generating the following image: | ||||
|  | ||||
|  | ||||
							
								
								
									
										290
									
								
								animate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								animate.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,290 @@ | ||||
| // This package enables creating simple animations by drawing onto an image, using ffmpeg which could be in the system path. | ||||
|  | ||||
| package animate | ||||
|  | ||||
| import ( | ||||
| 	"cmp" | ||||
| 	"fmt" | ||||
| 	"image" | ||||
| 	"image/color" | ||||
| 	"image/png" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Animation implements the [draw.Image] interface; you can draw to it and then invoke [EmitFrame]. | ||||
| // | ||||
| // That said, some implementations might be optimized for known image types, in which case, passing a pointer to the | ||||
| // [NRGBA] field might be more efficient. | ||||
| type Animation struct { | ||||
| 	image.NRGBA | ||||
|  | ||||
| 	// the first error where we encountered during writing | ||||
| 	err error | ||||
|  | ||||
| 	// the pipe to ffmpeg's stdin, nil if the animation is closed | ||||
| 	w io.WriteCloser | ||||
|  | ||||
| 	// the ffmpeg process, only applicable if w above is not nil | ||||
| 	c *exec.Cmd | ||||
|  | ||||
| 	// the name of the file we're writing to, | ||||
| 	// and where we'll move it to if nothing explodes | ||||
| 	tmp, out string | ||||
|  | ||||
| 	// framerate | ||||
| 	rate float64 | ||||
|  | ||||
| 	// the current frame number | ||||
| 	frame int | ||||
| } | ||||
|  | ||||
| // EmitFrame writes the image as it presently appears as a frame to the animation. | ||||
| // | ||||
| // If this is successful, then the frame counter is advanced. The frame buffer isn't cleared. | ||||
| func (a *Animation) EmitFrame() error { | ||||
| 	// if we've already encountered an error, return it | ||||
| 	if a.err != nil { | ||||
| 		return a.err | ||||
| 	} | ||||
|  | ||||
| 	// if the pipe is closed, then we can't write, obviously. | ||||
| 	if a.w == nil { | ||||
| 		return os.ErrClosed | ||||
| 	} | ||||
|  | ||||
| 	// try to write the frame to the pipe, | ||||
| 	// if if that fails, record the error and close the animation. | ||||
| 	if _, err := a.w.Write(a.Pix); err != nil { | ||||
| 		a.err = err | ||||
| 		return a.Close() | ||||
| 	} | ||||
|  | ||||
| 	a.frame++ | ||||
|  | ||||
| 	// success! | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Frame returns the current frame number, with the first frame being 0. | ||||
| func (a *Animation) Frame() int { | ||||
| 	return a.frame | ||||
| } | ||||
|  | ||||
| // Elapsed returns the timestamp for the current frame, with first frame having a timestamp of 0. | ||||
| func (a *Animation) Elapsed() time.Duration { | ||||
| 	return time.Duration(float64(a.frame) * float64(time.Second) / a.rate) | ||||
| } | ||||
|  | ||||
| // Close finishes writing the animation, and cleans up any resources. | ||||
| func (a *Animation) Close() error { | ||||
| 	// the pipe is open, shut everything down. | ||||
| 	if a.w != nil { | ||||
| 		// no longer needs to be finalized | ||||
| 		runtime.SetFinalizer(a, nil) | ||||
|  | ||||
| 		// close the pipe and wait for ffmpeg to finish, | ||||
| 		// setting a.err to the first error encountered. | ||||
| 		a.err = cmp.Or(a.err, a.w.Close(), a.c.Wait()) | ||||
| 		a.w, a.c = nil, nil | ||||
|  | ||||
| 		// if there are no errors so far, attempt to move the file to the destination. | ||||
| 		if a.err == nil { | ||||
| 			a.err = os.Rename(a.tmp, a.out) | ||||
| 		} | ||||
|  | ||||
| 		// if there was an error, delete the temporary file we wrote. | ||||
| 		if a.err != nil { | ||||
| 			os.Remove(a.tmp) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// return any error we may have encountered. | ||||
| 	return a.err | ||||
| } | ||||
|  | ||||
| func paletteToFile(palette color.Palette) (*os.File, error) { | ||||
| 	r, w, err := os.Pipe() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		defer w.Close() | ||||
|  | ||||
| 		img := image.NewPaletted(image.Rect(0, 0, 16, 16), palette) | ||||
|  | ||||
| 		for i := 0; i < 256; i++ { | ||||
| 			img.Pix[i] = uint8(min(i, len(palette)-1)) | ||||
| 		} | ||||
|  | ||||
| 		// I'm not going to bother dealing with possible write errors, | ||||
| 		// those would be because ffmpeg terminated unexpectedly, | ||||
| 		// and has nothing to do with us. | ||||
| 		png.Encode(w, img) | ||||
| 	}() | ||||
|  | ||||
| 	return r, nil | ||||
| } | ||||
|  | ||||
| func finalize(a *Animation) { | ||||
| 	err := a.Close() | ||||
| 	log.Printf("animation was finalized without being closed; err=%v", err) | ||||
| } | ||||
|  | ||||
| func new(r image.Rectangle, rate float64, palette color.Palette, out string, fmtArgs []string) (a *Animation, err error) { | ||||
| 	tmp, err := os.CreateTemp(filepath.Dir(out), "."+strings.TrimSuffix(filepath.Base(out), filepath.Ext(out))+"*.tmp") | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	defer func() { | ||||
| 		// if things go wrong, delete the file we wrote. | ||||
| 		if err != nil { | ||||
| 			os.Remove(tmp.Name()) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// we don't need the file open, we just want a safe place for ffmpeg to write to. | ||||
| 	if err := tmp.Close(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var args []string | ||||
|  | ||||
| 	args = append(args, | ||||
| 		"-hide_banner", | ||||
| 		"-loglevel", "error", | ||||
| 		"-f", "rawvideo", | ||||
| 		"-pixel_format", "rgba", | ||||
| 		"-video_size", fmt.Sprintf("%dx%d", r.Dx(), r.Dy()), | ||||
| 		"-framerate", fmt.Sprint(rate), | ||||
| 		"-i", "pipe:0", | ||||
| 	) | ||||
|  | ||||
| 	var extraFiles []*os.File | ||||
|  | ||||
| 	if palette != nil { | ||||
| 		f, err := paletteToFile(palette) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		args = append(args, | ||||
| 			"-i", fmt.Sprintf("pipe:%d", len(extraFiles)+3), | ||||
| 			"-filter_complex", "[0:v][1:v]paletteuse=dither=bayer", | ||||
| 		) | ||||
|  | ||||
| 		extraFiles = append(extraFiles, f) | ||||
| 	} | ||||
|  | ||||
| 	args = append(args, fmtArgs...) | ||||
|  | ||||
| 	args = append(args, "-y", "file:"+tmp.Name()) | ||||
|  | ||||
| 	cmd := exec.Command("ffmpeg", args...) | ||||
|  | ||||
| 	cmd.ExtraFiles = extraFiles | ||||
| 	cmd.Stderr = os.Stderr | ||||
|  | ||||
| 	w, err := cmd.StdinPipe() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	err = cmd.Start() | ||||
|  | ||||
| 	// close our copies of the file descriptors. The ffmpeg process, if it even started, has inherited them. | ||||
| 	// and if it didn't, we'd needed to close them ourselves. | ||||
| 	for _, f := range extraFiles { | ||||
| 		f.Close() | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	animation := &Animation{ | ||||
| 		NRGBA: image.NRGBA{ | ||||
| 			Pix:    make([]uint8, r.Dx()*r.Dy()*4), | ||||
| 			Stride: r.Dx() * 4, | ||||
| 			Rect:   r, | ||||
| 		}, | ||||
| 		w:     w, | ||||
| 		c:     cmd, | ||||
| 		tmp:   tmp.Name(), | ||||
| 		out:   out, | ||||
| 		rate:  rate, | ||||
| 		frame: 0, | ||||
| 	} | ||||
|  | ||||
| 	// failing to close the animation will leave temporary files on the filesystem, possibly leave the ffmpeg process hanging, | ||||
| 	// so let's try not to let that happen. | ||||
| 	runtime.SetFinalizer(animation, finalize) | ||||
|  | ||||
| 	return animation, nil | ||||
| } | ||||
|  | ||||
| // New begins a new animation at the given size and frame rate, to be written to the specified output file. | ||||
| // | ||||
| // The output filename must have one of the following extensions: | ||||
| // - .gif (lossy, uses a default fixed palette) | ||||
| // - .png / .apng (lossless) | ||||
| // - .mp4 (lossless RGB, doesn't support transparency) | ||||
| // - .mkv (lossless RGB, doesn't support transparency) | ||||
| // - .webp (lossy YUV colorspace conversion, alpha supported) | ||||
| // - .webm (lossy YUV colorspace conversion, alpha supported) | ||||
| // | ||||
| // Note that the output file won't be created immediately, the animation will be written to a temporary file and then renamed | ||||
| // by the [Close] method if no errors are encountered. | ||||
| func New(r image.Rectangle, rate float64, out string) (*Animation, error) { | ||||
| 	switch ext := filepath.Ext(out); ext { | ||||
| 	case ".gif": | ||||
| 		return new(r, rate, defaultPalette, out, []string{ | ||||
| 			"-loop", "0", | ||||
| 			"-f", "gif", | ||||
| 		}) | ||||
| 	case ".png", ".apng": | ||||
| 		return new(r, rate, nil, out, []string{ | ||||
| 			"-vcodec", "apng", | ||||
| 			"-plays", "0", | ||||
| 			"-f", "apng", | ||||
| 		}) | ||||
| 	case ".mp4": | ||||
| 		return new(r, rate, nil, out, []string{ | ||||
| 			"-vcodec", "libx264rgb", | ||||
| 			"-preset", "ultrafast", | ||||
| 			"-qp", "0", | ||||
| 			"-f", "mp4", | ||||
| 		}) | ||||
| 	case ".mkv": | ||||
| 		return new(r, rate, nil, out, []string{ | ||||
| 			"-vcodec", "libx264rgb", | ||||
| 			"-preset", "ultrafast", | ||||
| 			"-qp", "0", | ||||
| 			"-f", "matroska", | ||||
| 		}) | ||||
| 	case ".webp": | ||||
| 		return new(r, rate, nil, out, []string{ | ||||
| 			"-vcodec", "libwebp_anim", | ||||
| 			"-lossless", "1", | ||||
| 			"-loop", "0", | ||||
| 			"-f", "webp", | ||||
| 		}) | ||||
| 	case ".webm": | ||||
| 		return new(r, rate, nil, out, []string{ | ||||
| 			"-vcodec", "vp9", | ||||
| 			"-lossless", "1", | ||||
| 			"-f", "webm", | ||||
| 		}) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unsupported extension: %q", ext) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										124
									
								
								cmd/demo/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								cmd/demo/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| // Creates an animation of several renditions of "Munching Square". | ||||
|  | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"image" | ||||
| 	"image/color" | ||||
| 	"image/draw" | ||||
| 	"log" | ||||
| 	"math" | ||||
|  | ||||
| 	"smariot.com/animate" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	const ( | ||||
| 		gridSize     = 32 | ||||
| 		pixelSize    = 12 | ||||
| 		pixelRadius  = pixelSize / 2. | ||||
| 		subImageSize = gridSize * pixelSize | ||||
| 		frameRate    = 10 | ||||
| 		filename     = "demo.webp" | ||||
| 	) | ||||
|  | ||||
| 	// create a single animation large enough to contain four sub-animations. | ||||
| 	anim, err := animate.New(image.Rect(0, 0, subImageSize*2, subImageSize*2), frameRate, filename) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// create the sub images | ||||
| 	// (note that b and d are swapped because I think it looks better visually, but as for logical progression, the letter order makes more sense) | ||||
| 	a := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize)).(*image.NRGBA) | ||||
| 	b := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize).Add(image.Pt(subImageSize, subImageSize))).(*image.NRGBA) | ||||
| 	c := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize).Add(image.Pt(0, subImageSize))).(*image.NRGBA) | ||||
| 	d := anim.SubImage(image.Rect(0, 0, subImageSize, subImageSize).Add(image.Pt(subImageSize, 0))).(*image.NRGBA) | ||||
|  | ||||
| 	// I don't want to deal with the sub images all having weird rectangles that don't start at (0, 0), so copy the rectangle from sub-image a for | ||||
| 	// all of them. | ||||
| 	b.Rect, c.Rect, d.Rect = a.Rect, a.Rect, a.Rect | ||||
|  | ||||
| 	// a pixel sized circle mask, used for the PDP-1 version. | ||||
| 	// scaled by √2 so that the circles touch diagonally. | ||||
| 	// need to use floor in the inset because the result is negative, | ||||
| 	// and integers normally round toward zero. | ||||
| 	dot := image.NewAlpha(image.Rect(0, 0, pixelSize, pixelSize).Inset(int(math.Floor(pixelRadius - pixelRadius*math.Sqrt2)))) | ||||
|  | ||||
| 	for y := dot.Rect.Min.Y; y < dot.Rect.Max.Y; y++ { | ||||
| 		for x := dot.Rect.Min.X; x < dot.Rect.Max.X; x++ { | ||||
| 			dx := float64(x) + 0.5 - pixelRadius | ||||
| 			dy := float64(y) + 0.5 - pixelRadius | ||||
| 			a := uint8(max(0, min(1, pixelRadius*math.Sqrt2+0.5-math.Sqrt(dx*dx+dy*dy)))*255 + 0.5) | ||||
| 			dot.SetAlpha(x, y, color.Alpha{A: a}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	pixel := image.Rect(0, 0, pixelSize, pixelSize) | ||||
| 	black := image.NewUniform(color.Black) | ||||
| 	white := image.NewUniform(color.White) | ||||
|  | ||||
| 	for t := range gridSize { | ||||
| 		// clear the frame. | ||||
| 		draw.Draw(anim, anim.Rect, black, image.Point{}, draw.Src) | ||||
|  | ||||
| 		// the classing PDP-1 version. | ||||
| 		for x := range gridSize { | ||||
| 			y := x ^ t | ||||
| 			draw.DrawMask(a, dot.Rect.Add(image.Pt(x*pixelSize, y*pixelSize)), white, image.Point{}, dot, dot.Rect.Min, draw.Over) | ||||
| 		} | ||||
|  | ||||
| 		// an alternate version | ||||
| 		for y := range gridSize { | ||||
| 			for x := range gridSize { | ||||
| 				if (x^y+t)%gridSize <= gridSize/2 { | ||||
| 					draw.Draw(b, pixel.Add(image.Pt(x*pixelSize, y*pixelSize)), white, image.Point{}, draw.Src) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// an alternate alternate version - with color! | ||||
| 		for y := range gridSize { | ||||
| 			for x := range gridSize { | ||||
| 				r := uint8((x ^ y + t) % gridSize * 255 / gridSize) | ||||
| 				g := uint8((x ^ y + t + gridSize/3) % gridSize * 255 / gridSize) | ||||
| 				b := uint8((x ^ y + t + gridSize*2/3) % gridSize * 255 / gridSize) | ||||
|  | ||||
| 				draw.Draw(c, pixel.Add(image.Pt(x*pixelSize, y*pixelSize)), image.NewUniform(color.NRGBA{r, g, b, 255}), image.Point{}, draw.Src) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for y := range subImageSize { | ||||
| 			for x := range subImageSize { | ||||
| 				// distance, as 0 in the center and 1 at the corners. | ||||
| 				dx := float64(x) + 0.5 - subImageSize/2. | ||||
| 				dy := float64(y) + 0.5 - subImageSize/2. | ||||
| 				dist := math.Sqrt(dx*dx+dy*dy) / (math.Sqrt2 * subImageSize / 2.) | ||||
|  | ||||
| 				// distort time using a sine wave, based on distance and angle. | ||||
| 				t := (t + int((math.Sin(math.Pi*2.*dist+math.Atan2(dy, dx))*.5+.5)*gridSize)) % gridSize | ||||
|  | ||||
| 				// re-quantize space, but also remember the original pixel position for drawing the pixel. | ||||
| 				px, py := x, y | ||||
| 				x, y := x/pixelSize, y/pixelSize | ||||
|  | ||||
| 				// for extra contrast with the RGB version, let's use CMY. | ||||
| 				cyan := uint8((x ^ y + t) % gridSize * 255 / gridSize) | ||||
| 				magenta := uint8((x ^ y + t + gridSize/3) % gridSize * 255 / gridSize) | ||||
| 				yellow := uint8((x ^ y + t + gridSize*2/3) % gridSize * 255 / gridSize) | ||||
|  | ||||
| 				d.Set(px, py, color.NRGBA{255 - cyan, 255 - magenta, 255 - yellow, 255}) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// emit the frame. | ||||
| 		if err := anim.EmitFrame(); err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// and finally finish the animation by closing it. | ||||
| 	if err := anim.Close(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										264
									
								
								palette.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								palette.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,264 @@ | ||||
| package animate | ||||
|  | ||||
| import "image/color" | ||||
|  | ||||
| // Default palette to use for indexed images such as gif. | ||||
| // Subject to change. | ||||
| var defaultPalette = color.Palette{ | ||||
| 	color.RGBA{R: 0x0f, G: 0x08, B: 0x13, A: 0xff}, | ||||
| 	color.RGBA{R: 0x0f, G: 0x0d, B: 0x40, A: 0xff}, | ||||
| 	color.RGBA{R: 0x14, G: 0x12, B: 0x67, A: 0xff}, | ||||
| 	color.RGBA{R: 0x16, G: 0x16, B: 0x8c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x16, G: 0x1e, B: 0x13, A: 0xff}, | ||||
| 	color.RGBA{R: 0x17, G: 0x19, B: 0xae, A: 0xff}, | ||||
| 	color.RGBA{R: 0x1a, G: 0x19, B: 0xd0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x1a, G: 0x33, B: 0x16, A: 0xff}, | ||||
| 	color.RGBA{R: 0x1c, G: 0x28, B: 0x3e, A: 0xff}, | ||||
| 	color.RGBA{R: 0x1e, G: 0x31, B: 0x68, A: 0xff}, | ||||
| 	color.RGBA{R: 0x1f, G: 0x38, B: 0x94, A: 0xff}, | ||||
| 	color.RGBA{R: 0x1f, G: 0x3d, B: 0xc1, A: 0xff}, | ||||
| 	color.RGBA{R: 0x20, G: 0x1d, B: 0xf1, A: 0xff}, | ||||
| 	color.RGBA{R: 0x20, G: 0x47, B: 0x1a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x21, G: 0x3d, B: 0x40, A: 0xff}, | ||||
| 	color.RGBA{R: 0x25, G: 0x54, B: 0x49, A: 0xff}, | ||||
| 	color.RGBA{R: 0x25, G: 0x5b, B: 0x21, A: 0xff}, | ||||
| 	color.RGBA{R: 0x26, G: 0x47, B: 0x66, A: 0xff}, | ||||
| 	color.RGBA{R: 0x27, G: 0x42, B: 0xef, A: 0xff}, | ||||
| 	color.RGBA{R: 0x28, G: 0x55, B: 0xae, A: 0xff}, | ||||
| 	color.RGBA{R: 0x28, G: 0x5d, B: 0x75, A: 0xff}, | ||||
| 	color.RGBA{R: 0x29, G: 0x4e, B: 0x8c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x29, G: 0x6c, B: 0x1c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x29, G: 0x7f, B: 0x21, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2a, G: 0x5e, B: 0xf0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2c, G: 0x66, B: 0x9d, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2c, G: 0x6c, B: 0xc9, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2d, G: 0x0c, B: 0x29, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2d, G: 0x74, B: 0x4a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2d, G: 0x86, B: 0xd5, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2e, G: 0x68, B: 0x5c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2e, G: 0x74, B: 0xef, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2e, G: 0x90, B: 0x23, A: 0xff}, | ||||
| 	color.RGBA{R: 0x2f, G: 0x73, B: 0x86, A: 0xff}, | ||||
| 	color.RGBA{R: 0x30, G: 0x54, B: 0xd0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x30, G: 0x7c, B: 0xb0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x30, G: 0xa1, B: 0x29, A: 0xff}, | ||||
| 	color.RGBA{R: 0x31, G: 0x82, B: 0x71, A: 0xff}, | ||||
| 	color.RGBA{R: 0x32, G: 0xb2, B: 0x27, A: 0xff}, | ||||
| 	color.RGBA{R: 0x33, G: 0x12, B: 0x54, A: 0xff}, | ||||
| 	color.RGBA{R: 0x33, G: 0x89, B: 0x98, A: 0xff}, | ||||
| 	color.RGBA{R: 0x34, G: 0x89, B: 0x52, A: 0xff}, | ||||
| 	color.RGBA{R: 0x35, G: 0x97, B: 0x7e, A: 0xff}, | ||||
| 	color.RGBA{R: 0x35, G: 0x9c, B: 0xed, A: 0xff}, | ||||
| 	color.RGBA{R: 0x36, G: 0xb0, B: 0x68, A: 0xff}, | ||||
| 	color.RGBA{R: 0x37, G: 0x9d, B: 0x5b, A: 0xff}, | ||||
| 	color.RGBA{R: 0x37, G: 0xc3, B: 0x29, A: 0xff}, | ||||
| 	color.RGBA{R: 0x38, G: 0xa8, B: 0xcb, A: 0xff}, | ||||
| 	color.RGBA{R: 0x38, G: 0xbc, B: 0xd5, A: 0xff}, | ||||
| 	color.RGBA{R: 0x39, G: 0x9d, B: 0xa4, A: 0xff}, | ||||
| 	color.RGBA{R: 0x39, G: 0xd2, B: 0x2a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x39, G: 0xf0, B: 0x33, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3a, G: 0xaa, B: 0x89, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3a, G: 0xe1, B: 0x2a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3b, G: 0x17, B: 0x83, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3d, G: 0xb3, B: 0xb1, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3d, G: 0xc8, B: 0x83, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3d, G: 0xcf, B: 0x61, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3e, G: 0x19, B: 0xaf, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3e, G: 0x93, B: 0xc1, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3e, G: 0xe3, B: 0x63, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3f, G: 0x11, B: 0x13, A: 0xff}, | ||||
| 	color.RGBA{R: 0x3f, G: 0x88, B: 0xf4, A: 0xff}, | ||||
| 	color.RGBA{R: 0x40, G: 0xbe, B: 0x9a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x40, G: 0xcb, B: 0xc6, A: 0xff}, | ||||
| 	color.RGBA{R: 0x41, G: 0xbd, B: 0x5c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x42, G: 0xb0, B: 0xf0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x43, G: 0x20, B: 0xd4, A: 0xff}, | ||||
| 	color.RGBA{R: 0x43, G: 0xe7, B: 0xf2, A: 0xff}, | ||||
| 	color.RGBA{R: 0x44, G: 0x2c, B: 0x19, A: 0xff}, | ||||
| 	color.RGBA{R: 0x44, G: 0xe9, B: 0xae, A: 0xff}, | ||||
| 	color.RGBA{R: 0x44, G: 0xfc, B: 0x31, A: 0xff}, | ||||
| 	color.RGBA{R: 0x45, G: 0xd4, B: 0xab, A: 0xff}, | ||||
| 	color.RGBA{R: 0x45, G: 0xd5, B: 0xed, A: 0xff}, | ||||
| 	color.RGBA{R: 0x45, G: 0xed, B: 0x89, A: 0xff}, | ||||
| 	color.RGBA{R: 0x47, G: 0xda, B: 0x89, A: 0xff}, | ||||
| 	color.RGBA{R: 0x47, G: 0xe3, B: 0xcf, A: 0xff}, | ||||
| 	color.RGBA{R: 0x48, G: 0xc3, B: 0xf3, A: 0xff}, | ||||
| 	color.RGBA{R: 0x49, G: 0xf9, B: 0x72, A: 0xff}, | ||||
| 	color.RGBA{R: 0x4c, G: 0x33, B: 0x4a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x4c, G: 0x38, B: 0x7a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x4c, G: 0x3d, B: 0xae, A: 0xff}, | ||||
| 	color.RGBA{R: 0x4f, G: 0x13, B: 0x3d, A: 0xff}, | ||||
| 	color.RGBA{R: 0x4f, G: 0x45, B: 0x1e, A: 0xff}, | ||||
| 	color.RGBA{R: 0x50, G: 0x16, B: 0x68, A: 0xff}, | ||||
| 	color.RGBA{R: 0x50, G: 0xf8, B: 0xcd, A: 0xff}, | ||||
| 	color.RGBA{R: 0x50, G: 0xf8, B: 0xee, A: 0xff}, | ||||
| 	color.RGBA{R: 0x50, G: 0xfa, B: 0xa6, A: 0xff}, | ||||
| 	color.RGBA{R: 0x54, G: 0x22, B: 0xf1, A: 0xff}, | ||||
| 	color.RGBA{R: 0x58, G: 0x1a, B: 0x9e, A: 0xff}, | ||||
| 	color.RGBA{R: 0x58, G: 0x5c, B: 0x25, A: 0xff}, | ||||
| 	color.RGBA{R: 0x5b, G: 0x51, B: 0x51, A: 0xff}, | ||||
| 	color.RGBA{R: 0x5d, G: 0x4b, B: 0xf0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x5f, G: 0x58, B: 0x80, A: 0xff}, | ||||
| 	color.RGBA{R: 0x61, G: 0x14, B: 0x17, A: 0xff}, | ||||
| 	color.RGBA{R: 0x61, G: 0x74, B: 0x26, A: 0xff}, | ||||
| 	color.RGBA{R: 0x62, G: 0x5e, B: 0xb2, A: 0xff}, | ||||
| 	color.RGBA{R: 0x63, G: 0x1d, B: 0xca, A: 0xff}, | ||||
| 	color.RGBA{R: 0x66, G: 0x72, B: 0x98, A: 0xff}, | ||||
| 	color.RGBA{R: 0x67, G: 0x68, B: 0xeb, A: 0xff}, | ||||
| 	color.RGBA{R: 0x67, G: 0x79, B: 0xcb, A: 0xff}, | ||||
| 	color.RGBA{R: 0x68, G: 0x71, B: 0x63, A: 0xff}, | ||||
| 	color.RGBA{R: 0x68, G: 0x8c, B: 0x28, A: 0xff}, | ||||
| 	color.RGBA{R: 0x69, G: 0x19, B: 0x83, A: 0xff}, | ||||
| 	color.RGBA{R: 0x69, G: 0x34, B: 0x21, A: 0xff}, | ||||
| 	color.RGBA{R: 0x6a, G: 0x47, B: 0xcb, A: 0xff}, | ||||
| 	color.RGBA{R: 0x6b, G: 0x42, B: 0x98, A: 0xff}, | ||||
| 	color.RGBA{R: 0x6d, G: 0x17, B: 0x5f, A: 0xff}, | ||||
| 	color.RGBA{R: 0x6d, G: 0x3c, B: 0x65, A: 0xff}, | ||||
| 	color.RGBA{R: 0x6d, G: 0xa5, B: 0x2e, A: 0xff}, | ||||
| 	color.RGBA{R: 0x72, G: 0x19, B: 0x3c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x74, G: 0x88, B: 0x90, A: 0xff}, | ||||
| 	color.RGBA{R: 0x74, G: 0x8e, B: 0x64, A: 0xff}, | ||||
| 	color.RGBA{R: 0x76, G: 0x92, B: 0xea, A: 0xff}, | ||||
| 	color.RGBA{R: 0x78, G: 0x1d, B: 0xab, A: 0xff}, | ||||
| 	color.RGBA{R: 0x78, G: 0x23, B: 0xf0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7a, G: 0xbc, B: 0x2e, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7b, G: 0x61, B: 0x27, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7c, G: 0x9c, B: 0xc1, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7d, G: 0xa9, B: 0x6c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7e, G: 0xd0, B: 0x35, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7f, G: 0x46, B: 0x20, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7f, G: 0xa2, B: 0x96, A: 0xff}, | ||||
| 	color.RGBA{R: 0x7f, G: 0xe5, B: 0x38, A: 0xff}, | ||||
| 	color.RGBA{R: 0x83, G: 0x1b, B: 0x19, A: 0xff}, | ||||
| 	color.RGBA{R: 0x83, G: 0x5b, B: 0x5c, A: 0xff}, | ||||
| 	color.RGBA{R: 0x85, G: 0xb4, B: 0xc8, A: 0xff}, | ||||
| 	color.RGBA{R: 0x86, G: 0x1d, B: 0x86, A: 0xff}, | ||||
| 	color.RGBA{R: 0x87, G: 0x81, B: 0xb6, A: 0xff}, | ||||
| 	color.RGBA{R: 0x88, G: 0x1f, B: 0xcf, A: 0xff}, | ||||
| 	color.RGBA{R: 0x88, G: 0x50, B: 0xeb, A: 0xff}, | ||||
| 	color.RGBA{R: 0x89, G: 0x3f, B: 0x4f, A: 0xff}, | ||||
| 	color.RGBA{R: 0x89, G: 0x78, B: 0x2a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x89, G: 0xc3, B: 0x74, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8a, G: 0x49, B: 0x86, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8b, G: 0x64, B: 0xbc, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8c, G: 0x46, B: 0xb7, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8c, G: 0x76, B: 0xed, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8d, G: 0x68, B: 0x89, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8d, G: 0x93, B: 0x2f, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8e, G: 0x1c, B: 0x63, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8e, G: 0xf9, B: 0x37, A: 0xff}, | ||||
| 	color.RGBA{R: 0x8f, G: 0xcf, B: 0xc9, A: 0xff}, | ||||
| 	color.RGBA{R: 0x90, G: 0xaa, B: 0xee, A: 0xff}, | ||||
| 	color.RGBA{R: 0x90, G: 0xbd, B: 0xa0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x90, G: 0xda, B: 0xa6, A: 0xff}, | ||||
| 	color.RGBA{R: 0x91, G: 0x19, B: 0x40, A: 0xff}, | ||||
| 	color.RGBA{R: 0x91, G: 0xdf, B: 0x7a, A: 0xff}, | ||||
| 	color.RGBA{R: 0x93, G: 0xc6, B: 0xee, A: 0xff}, | ||||
| 	color.RGBA{R: 0x93, G: 0xe2, B: 0xed, A: 0xff}, | ||||
| 	color.RGBA{R: 0x95, G: 0x78, B: 0x65, A: 0xff}, | ||||
| 	color.RGBA{R: 0x97, G: 0xac, B: 0x31, A: 0xff}, | ||||
| 	color.RGBA{R: 0x98, G: 0x1e, B: 0xa8, A: 0xff}, | ||||
| 	color.RGBA{R: 0x9b, G: 0x25, B: 0xf0, A: 0xff}, | ||||
| 	color.RGBA{R: 0x9c, G: 0x5d, B: 0x27, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa0, G: 0x1d, B: 0x1e, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa0, G: 0xf8, B: 0x80, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa2, G: 0x42, B: 0x26, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa3, G: 0x8c, B: 0xed, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa5, G: 0x20, B: 0x81, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa5, G: 0x89, B: 0x95, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa5, G: 0x92, B: 0x6c, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa5, G: 0xf8, B: 0xb2, A: 0xff}, | ||||
| 	color.RGBA{R: 0xa6, G: 0xf9, B: 0xe7, A: 0xff}, | ||||
| 	color.RGBA{R: 0xaa, G: 0x21, B: 0xcc, A: 0xff}, | ||||
| 	color.RGBA{R: 0xaa, G: 0x49, B: 0x67, A: 0xff}, | ||||
| 	color.RGBA{R: 0xaa, G: 0x81, B: 0x2d, A: 0xff}, | ||||
| 	color.RGBA{R: 0xaa, G: 0xc2, B: 0x36, A: 0xff}, | ||||
| 	color.RGBA{R: 0xab, G: 0x4e, B: 0x9c, A: 0xff}, | ||||
| 	color.RGBA{R: 0xab, G: 0x5c, B: 0xf0, A: 0xff}, | ||||
| 	color.RGBA{R: 0xab, G: 0xab, B: 0x74, A: 0xff}, | ||||
| 	color.RGBA{R: 0xac, G: 0x1e, B: 0x54, A: 0xff}, | ||||
| 	color.RGBA{R: 0xac, G: 0xd6, B: 0x34, A: 0xff}, | ||||
| 	color.RGBA{R: 0xac, G: 0xe9, B: 0x3a, A: 0xff}, | ||||
| 	color.RGBA{R: 0xad, G: 0x62, B: 0x5f, A: 0xff}, | ||||
| 	color.RGBA{R: 0xad, G: 0x71, B: 0xc8, A: 0xff}, | ||||
| 	color.RGBA{R: 0xad, G: 0x93, B: 0xc6, A: 0xff}, | ||||
| 	color.RGBA{R: 0xaf, G: 0x4d, B: 0xc9, A: 0xff}, | ||||
| 	color.RGBA{R: 0xaf, G: 0x6e, B: 0x97, A: 0xff}, | ||||
| 	color.RGBA{R: 0xb1, G: 0xa4, B: 0xa6, A: 0xff}, | ||||
| 	color.RGBA{R: 0xb3, G: 0x98, B: 0x2f, A: 0xff}, | ||||
| 	color.RGBA{R: 0xb3, G: 0xe6, B: 0xbf, A: 0xff}, | ||||
| 	color.RGBA{R: 0xb5, G: 0x6b, B: 0x2b, A: 0xff}, | ||||
| 	color.RGBA{R: 0xb7, G: 0x22, B: 0xa6, A: 0xff}, | ||||
| 	color.RGBA{R: 0xbb, G: 0x27, B: 0xef, A: 0xff}, | ||||
| 	color.RGBA{R: 0xbb, G: 0xae, B: 0x37, A: 0xff}, | ||||
| 	color.RGBA{R: 0xbc, G: 0x21, B: 0x24, A: 0xff}, | ||||
| 	color.RGBA{R: 0xbd, G: 0xb9, B: 0xbf, A: 0xff}, | ||||
| 	color.RGBA{R: 0xbd, G: 0xc9, B: 0x7b, A: 0xff}, | ||||
| 	color.RGBA{R: 0xbf, G: 0x4b, B: 0x27, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc1, G: 0x21, B: 0x7d, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc2, G: 0xb0, B: 0xe6, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc2, G: 0xe3, B: 0x86, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc3, G: 0xce, B: 0xea, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc5, G: 0xce, B: 0xb1, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc5, G: 0xfa, B: 0x3b, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc6, G: 0x76, B: 0xee, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc7, G: 0x79, B: 0x6b, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc8, G: 0x23, B: 0x54, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc8, G: 0xe6, B: 0xe9, A: 0xff}, | ||||
| 	color.RGBA{R: 0xc9, G: 0x96, B: 0xeb, A: 0xff}, | ||||
| 	color.RGBA{R: 0xca, G: 0x4e, B: 0x87, A: 0xff}, | ||||
| 	color.RGBA{R: 0xcd, G: 0x24, B: 0xc9, A: 0xff}, | ||||
| 	color.RGBA{R: 0xce, G: 0xb2, B: 0x87, A: 0xff}, | ||||
| 	color.RGBA{R: 0xcf, G: 0x53, B: 0x5f, A: 0xff}, | ||||
| 	color.RGBA{R: 0xcf, G: 0x56, B: 0xb7, A: 0xff}, | ||||
| 	color.RGBA{R: 0xcf, G: 0x84, B: 0x30, A: 0xff}, | ||||
| 	color.RGBA{R: 0xcf, G: 0x8f, B: 0xa8, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd0, G: 0x53, B: 0xe8, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd0, G: 0x94, B: 0x75, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd1, G: 0xd1, B: 0x3c, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd2, G: 0x7a, B: 0xc5, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd3, G: 0x6f, B: 0x92, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd4, G: 0xba, B: 0x36, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd4, G: 0xe7, B: 0x3d, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd5, G: 0x68, B: 0x2c, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd6, G: 0x24, B: 0xa3, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd6, G: 0x9e, B: 0x36, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd7, G: 0x21, B: 0x22, A: 0xff}, | ||||
| 	color.RGBA{R: 0xd9, G: 0x24, B: 0xef, A: 0xff}, | ||||
| 	color.RGBA{R: 0xde, G: 0x49, B: 0x27, A: 0xff}, | ||||
| 	color.RGBA{R: 0xdf, G: 0x22, B: 0x7a, A: 0xff}, | ||||
| 	color.RGBA{R: 0xe4, G: 0x24, B: 0x4f, A: 0xff}, | ||||
| 	color.RGBA{R: 0xe6, G: 0xf5, B: 0xba, A: 0xff}, | ||||
| 	color.RGBA{R: 0xe6, G: 0xf7, B: 0x87, A: 0xff}, | ||||
| 	color.RGBA{R: 0xe9, G: 0xa5, B: 0xb5, A: 0xff}, | ||||
| 	color.RGBA{R: 0xeb, G: 0xf5, B: 0xea, A: 0xff}, | ||||
| 	color.RGBA{R: 0xec, G: 0xbc, B: 0xe9, A: 0xff}, | ||||
| 	color.RGBA{R: 0xed, G: 0xdb, B: 0x87, A: 0xff}, | ||||
| 	color.RGBA{R: 0xed, G: 0xdb, B: 0xbb, A: 0xff}, | ||||
| 	color.RGBA{R: 0xee, G: 0xbf, B: 0xb7, A: 0xff}, | ||||
| 	color.RGBA{R: 0xef, G: 0x50, B: 0x90, A: 0xff}, | ||||
| 	color.RGBA{R: 0xef, G: 0xa1, B: 0xeb, A: 0xff}, | ||||
| 	color.RGBA{R: 0xef, G: 0xc0, B: 0x81, A: 0xff}, | ||||
| 	color.RGBA{R: 0xef, G: 0xd8, B: 0xeb, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf0, G: 0x27, B: 0xc6, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf0, G: 0x5f, B: 0xed, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf0, G: 0x81, B: 0xea, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf0, G: 0x8d, B: 0xbe, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf0, G: 0xf7, B: 0x3c, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf1, G: 0x5d, B: 0x68, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf1, G: 0x77, B: 0x37, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf1, G: 0x81, B: 0x7e, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf2, G: 0x58, B: 0xc3, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf2, G: 0x90, B: 0x36, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf2, G: 0xa1, B: 0x7f, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf2, G: 0xab, B: 0x3a, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf2, G: 0xc5, B: 0x3b, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf3, G: 0x70, B: 0xa8, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf3, G: 0xde, B: 0x40, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf4, G: 0x26, B: 0x9d, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf4, G: 0x2b, B: 0xee, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf5, G: 0x26, B: 0x2a, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf5, G: 0x57, B: 0x2c, A: 0xff}, | ||||
| 	color.RGBA{R: 0xf7, G: 0x29, B: 0x69, A: 0xff}, | ||||
| 	color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0x00}, | ||||
| } | ||||
		Reference in New Issue
	
	Block a user