rework color parsing & dynamic palettes
This commit is contained in:
		
							parent
							
								
									8f18af72f7
								
							
						
					
					
						commit
						82f09db519
					
				| 
						 | 
					@ -8,27 +8,31 @@ import (
 | 
				
			||||||
	"golang.org/x/image/draw"
 | 
						"golang.org/x/image/draw"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// pattern provides infinite algorithmically generated patterns and implements
 | 
					// Pattern provides infinite algorithmically generated Patterns and implements
 | 
				
			||||||
// the image.Image interface.
 | 
					// the image.Image interface.
 | 
				
			||||||
type pattern struct{}
 | 
					type Pattern struct {
 | 
				
			||||||
 | 
						Size image.Point
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Image interface
 | 
					// Image interface
 | 
				
			||||||
func (c *pattern) ColorModel() color.Model { return color.NRGBAModel }
 | 
					func (c *Pattern) ColorModel() color.Model { return color.NRGBAModel }
 | 
				
			||||||
func (c *pattern) At(x, y int) color.Color { return color.Transparent }
 | 
					func (c *Pattern) At(x, y int) color.Color { return color.Transparent }
 | 
				
			||||||
func (c *pattern) Bounds() image.Rectangle {
 | 
					func (c *Pattern) Bounds() image.Rectangle {
 | 
				
			||||||
	return image.Rectangle{image.Point{-1e9, -1e9}, image.Point{1e9, 1e9}}
 | 
						return image.Rectangle{image.Point{-1e9, -1e9}, image.Point{1e9, 1e9}}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *pattern) ToFixedImage(bounds image.Rectangle) *image.NRGBA {
 | 
					func (p *Pattern) ToFixedImage(bounds image.Rectangle) *image.NRGBA {
 | 
				
			||||||
	img := image.NewNRGBA(bounds)
 | 
						img := image.NewNRGBA(bounds)
 | 
				
			||||||
 | 
						// TODO: allow setting a mask?
 | 
				
			||||||
 | 
						// override size for pattern?
 | 
				
			||||||
 | 
						p.Size = bounds.Size() // override size for this Pattern
 | 
				
			||||||
	draw.Draw(img, bounds, p, image.Point{}, draw.Src)
 | 
						draw.Draw(img, bounds, p, image.Point{}, draw.Src)
 | 
				
			||||||
	return img
 | 
						return img
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type StripePattern struct {
 | 
					type StripePattern struct {
 | 
				
			||||||
	pattern
 | 
						Pattern
 | 
				
			||||||
	Palette color.Palette
 | 
						Palette color.Palette
 | 
				
			||||||
	Size    int
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *StripePattern) At(x, y int) color.Color {
 | 
					func (c *StripePattern) At(x, y int) color.Color {
 | 
				
			||||||
| 
						 | 
					@ -37,7 +41,7 @@ func (c *StripePattern) At(x, y int) color.Color {
 | 
				
			||||||
		return color.Transparent
 | 
							return color.Transparent
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pxPerStripe := c.Size / n
 | 
						pxPerStripe := c.Size.Y / n
 | 
				
			||||||
	if pxPerStripe <= 0 {
 | 
						if pxPerStripe <= 0 {
 | 
				
			||||||
		pxPerStripe = 1
 | 
							pxPerStripe = 1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -46,8 +50,8 @@ func (c *StripePattern) At(x, y int) color.Color {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewPrideImage(p color.Palette, bounds image.Rectangle) image.Image {
 | 
					func NewPrideImage(p color.Palette, bounds image.Rectangle) image.Image {
 | 
				
			||||||
	pattern := StripePattern{Palette: p, Size: bounds.Dy()}
 | 
						Pattern := StripePattern{Palette: p}
 | 
				
			||||||
	return pattern.ToFixedImage(bounds)
 | 
						return Pattern.ToFixedImage(bounds)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var PrideFlags = map[string]color.Palette{
 | 
					var PrideFlags = map[string]color.Palette{
 | 
				
			||||||
| 
						 | 
					@ -74,27 +78,67 @@ var PrideFlags = map[string]color.Palette{
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SineColorPattern struct {
 | 
					type DynamicPattern struct {
 | 
				
			||||||
	pattern
 | 
						Pattern
 | 
				
			||||||
	Luma  uint8
 | 
					
 | 
				
			||||||
	Freq  float64
 | 
						Color func(x, y int, p *DynamicPattern) (float64, float64, float64) // should return rgb in [0,1] range
 | 
				
			||||||
	Phase float64
 | 
					
 | 
				
			||||||
 | 
						Luma  float64 // color parameter
 | 
				
			||||||
 | 
						Freq  float64 // frequency parameter
 | 
				
			||||||
 | 
						Phase float64 // phase parameter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Brightness float64 // additive base brightness
 | 
				
			||||||
 | 
						ColFactor  float64 // Pattern intensity
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *SineColorPattern) At(x, y int) color.Color {
 | 
					func (c *DynamicPattern) At(x, y int) color.Color {
 | 
				
			||||||
	TAU := 6.28318
 | 
					 | 
				
			||||||
	offset := 0.5
 | 
					 | 
				
			||||||
	scale := 0.5
 | 
					 | 
				
			||||||
	v := math.Atan2(float64(x), float64(y))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	col := color.NRGBA{}
 | 
						col := color.NRGBA{}
 | 
				
			||||||
	col.R = uint8((offset+scale*math.Sin(v/c.Freq*TAU))*128 + 128)
 | 
					
 | 
				
			||||||
	col.G = uint8((offset+scale*math.Sin(v/c.Freq*TAU+TAU/3))*128 + 128)
 | 
						if c.ColFactor == 0.0 && c.Brightness == 0.0 {
 | 
				
			||||||
	col.B = uint8((offset+scale*math.Sin(v/c.Freq*TAU+TAU/1.5))*128 + 128)
 | 
							c.ColFactor = 1.0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Color != nil {
 | 
				
			||||||
 | 
							r, g, b := c.Color(x, y, c)
 | 
				
			||||||
 | 
							col.R = uint8(r * c.ColFactor * 255)
 | 
				
			||||||
 | 
							col.G = uint8(g * c.ColFactor * 255)
 | 
				
			||||||
 | 
							col.B = uint8(b * c.ColFactor * 255)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Freq == 0.0 {
 | 
				
			||||||
 | 
							c.Freq = 1.0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						col.R = col.R + uint8(255*c.Brightness)
 | 
				
			||||||
 | 
						col.G = col.G + uint8(255*c.Brightness)
 | 
				
			||||||
 | 
						col.B = col.B + uint8(255*c.Brightness)
 | 
				
			||||||
	col.A = 0xff
 | 
						col.A = 0xff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// col.R = c.Luma
 | 
					 | 
				
			||||||
	// col.G = uint8(math.Sin(float64(x)/c.Freq+c.Phase) * 128 + 128)
 | 
					 | 
				
			||||||
	// col.B = uint8(math.Cos(float64(y)/c.Freq+c.Phase) * 128 + 128)
 | 
					 | 
				
			||||||
	return col
 | 
						return col
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var DynPatterns = map[string]image.Image{
 | 
				
			||||||
 | 
						"rbow": &DynamicPattern{
 | 
				
			||||||
 | 
							Color: func(x, y int, p *DynamicPattern) (float64, float64, float64) {
 | 
				
			||||||
 | 
								xx := float64(x) / 26.0 // TODO: dynamic sizing
 | 
				
			||||||
 | 
								yy := float64(y) / 13.0
 | 
				
			||||||
 | 
								r := 0.5 + 0.5*math.Cos(xx+p.Phase)
 | 
				
			||||||
 | 
								g := 0.5 + 0.5*math.Sin(yy+p.Phase)
 | 
				
			||||||
 | 
								b := 0.5 + 0.5*math.Sin(xx+p.Phase)*math.Cos(yy+p.Phase)
 | 
				
			||||||
 | 
								return r, g, b
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"pastel": &DynamicPattern{
 | 
				
			||||||
 | 
							Brightness: 0.5, // TODO: expose these params?
 | 
				
			||||||
 | 
							ColFactor:  0.5,
 | 
				
			||||||
 | 
							Freq:       5.0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Color: func(x, y int, p *DynamicPattern) (float64, float64, float64) {
 | 
				
			||||||
 | 
								offset := 0.0
 | 
				
			||||||
 | 
								v := math.Atan2(float64(x)-offset, float64(y)-offset) + p.Phase
 | 
				
			||||||
 | 
								r := 0.5 + 0.5*math.Sin(v*p.Freq)
 | 
				
			||||||
 | 
								g := 0.5 + 0.5*math.Cos(v*p.Freq)
 | 
				
			||||||
 | 
								b := 0.5 + 0.5*math.Sin(v*p.Freq+6.28318/p.Luma)
 | 
				
			||||||
 | 
								return r, g, b
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										23
									
								
								rpc/repl.go
								
								
								
								
							
							
						
						
									
										23
									
								
								rpc/repl.go
								
								
								
								
							| 
						 | 
					@ -128,16 +128,27 @@ func RunREPL(f Fluter) {
 | 
				
			||||||
// alternatively treat it as palette name. If both fail,
 | 
					// alternatively treat it as palette name. If both fail,
 | 
				
			||||||
// give image.Transparent
 | 
					// give image.Transparent
 | 
				
			||||||
func parseColorOrPalette(input string) image.Image {
 | 
					func parseColorOrPalette(input string) image.Image {
 | 
				
			||||||
	if col, err := hex.DecodeString(input); err == nil {
 | 
						if input == "w" {
 | 
				
			||||||
 | 
							return image.NewUniform(color.White)
 | 
				
			||||||
 | 
						} else if input == "b" {
 | 
				
			||||||
 | 
							return image.NewUniform(color.Black)
 | 
				
			||||||
 | 
						} else if input == "t" {
 | 
				
			||||||
 | 
							return image.Transparent
 | 
				
			||||||
 | 
						} else if col, err := hex.DecodeString(input); err == nil && len(col) >= 3 {
 | 
				
			||||||
		var alpha byte = 0xff
 | 
							var alpha byte = 0xff
 | 
				
			||||||
		if len(col) == 4 {
 | 
							if len(col) == 4 {
 | 
				
			||||||
			alpha = col[3]
 | 
								alpha = col[3]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return image.NewUniform(color.NRGBA{col[0], col[1], col[2], alpha})
 | 
							return image.NewUniform(color.NRGBA{col[0], col[1], col[2], alpha})
 | 
				
			||||||
	} else if pal := render.PrideFlags[input]; len(pal) != 0 {
 | 
					 | 
				
			||||||
		return &render.StripePattern{Palette: pal, Size: 13}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return &render.SineColorPattern{Luma: 0xf0, Freq: 1}
 | 
					 | 
				
			||||||
		return image.Transparent
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pal := render.PrideFlags[input]; len(pal) != 0 {
 | 
				
			||||||
 | 
							return &render.StripePattern{Palette: pal}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if p, ok := render.DynPatterns[input]; ok {
 | 
				
			||||||
 | 
							return p
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return image.Transparent
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue