Add HSV, HSVA
This commit is contained in:
		
							parent
							
								
									1547391126
								
							
						
					
					
						commit
						a899d12756
					
				
							
								
								
									
										177
									
								
								image/color/hsv.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								image/color/hsv.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | ||||
| package color | ||||
| 
 | ||||
| import "fmt" | ||||
| import "image/color" | ||||
| 
 | ||||
| // HSV represents a color with hue, saturation, and value components. Each | ||||
| // component C is in range 0 <= C <= 1. | ||||
| type HSV struct { | ||||
| 	H float64 | ||||
| 	S float64 | ||||
| 	V float64 | ||||
| } | ||||
| 
 | ||||
| // HSVA is an HSV color with an added 8-bit alpha component. The alpha component | ||||
| // ranges from 0x0000 (fully transparent) to 0xFFFF (opaque), and has no bearing | ||||
| // on the other components. | ||||
| type HSVA struct { | ||||
| 	H float64 | ||||
| 	S float64 | ||||
| 	V float64 | ||||
| 	A uint16 | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	HSVModel  color.Model = color.ModelFunc(hsvModel) | ||||
| 	HSVAModel color.Model = color.ModelFunc(hsvaModel) | ||||
| ) | ||||
| 
 | ||||
| func (hsv HSV) RGBA () (r, g, b, a uint32) { | ||||
| 	// Adapted from: | ||||
| 	// https://www.cs.rit.edu/~ncs/color/t_convert.html | ||||
| 	 | ||||
| 	component := func (x float64) uint32 { | ||||
| 		return uint32(float64(0xFFFF) * x) | ||||
| 	} | ||||
| 
 | ||||
| 	s := clamp01(hsv.S) | ||||
| 	v := clamp01(hsv.V) | ||||
| 	if s == 0 { | ||||
| 		light := component(v) | ||||
| 		return light, light, light, 0xFFFF | ||||
| 	} | ||||
| 	 | ||||
| 	h := clamp01(hsv.H) * 360 | ||||
| 	sector := int(h / 60) | ||||
| 	// otherwise when given 1.0 for H, sector would overflow to 6 | ||||
| 	if sector > 5 { sector = 5 } | ||||
| 	offset := (h / 60) - float64(sector) | ||||
| 	 | ||||
| 	p  := component(v * (1 - s)) | ||||
| 	q  := component(v * (1 - s * offset)) | ||||
| 	t  := component(v * (1 - s * (1 - offset))) | ||||
| 	va := component(v) | ||||
| 	 | ||||
| 	switch sector { | ||||
| 	case 0:  return va, t,  p,  0xFFFF | ||||
| 	case 1:  return q,  va, p,  0xFFFF | ||||
| 	case 2:  return p,  va, t,  0xFFFF | ||||
| 	case 3:  return p,  q,  va, 0xFFFF | ||||
| 	case 4:  return t,  p,  va, 0xFFFF | ||||
| 	default: return va, p,  q,  0xFFFF | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (hsva HSVA) RGBA () (r, g, b, a uint32) { | ||||
| 	r, g, b, a = HSV { | ||||
| 		H: hsva.H, | ||||
| 		S: hsva.S, | ||||
| 		V: hsva.V, | ||||
| 	}.RGBA() | ||||
| 	a = uint32(hsva.A) | ||||
| 	// alpha premultiplication | ||||
| 	r = (r * a) / 0xFFFF | ||||
| 	g = (g * a) / 0xFFFF | ||||
| 	b = (b * a) / 0xFFFF | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Canon returns the color but with the H, S, and V fields are constrained to | ||||
| // the range 0.0-1.0 | ||||
| func (hsv HSV) Canon () HSV { | ||||
| 	hsv.H = clamp01(hsv.H) | ||||
| 	hsv.S = clamp01(hsv.S) | ||||
| 	hsv.V = clamp01(hsv.V) | ||||
| 	return hsv | ||||
| } | ||||
| 
 | ||||
| // Canon returns the color but with the H, S, and V fields are constrained to | ||||
| // the range 0.0-1.0 | ||||
| func (hsva HSVA) Canon () HSVA { | ||||
| 	hsva.H = clamp01(hsva.H) | ||||
| 	hsva.S = clamp01(hsva.S) | ||||
| 	hsva.V = clamp01(hsva.V) | ||||
| 	return hsva | ||||
| } | ||||
| 
 | ||||
| func clamp01 (x float64) float64 { | ||||
| 	if x > 1.0 { return 1.0 } | ||||
| 	if x < 0.0 { return 0.0 } | ||||
| 	return x | ||||
| } | ||||
| 
 | ||||
| func hsvModel (c color.Color) color.Color { | ||||
| 	switch c := c.(type) { | ||||
| 	case HSV:  return c | ||||
| 	case HSVA: return HSV { H: c.H, S: c.S, V: c.V } | ||||
| 	default: | ||||
| 		r, g, b, a := c.RGBA() | ||||
| 		// alpha unpremultiplication | ||||
| 		r = (r / a) * 0xFFFF | ||||
| 		g = (g / a) * 0xFFFF | ||||
| 		b = (b / a) * 0xFFFF | ||||
| 		return rgbToHSV(r, g, b) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func hsvaModel (c color.Color) color.Color { | ||||
| 	switch c := c.(type) { | ||||
| 	case HSV:  return HSVA { H: c.H, S: c.S, V: c.V, A: 0xFFFF } | ||||
| 	case HSVA: return c | ||||
| 	default: | ||||
| 		r, g, b, a := c.RGBA() | ||||
| 		hsv := rgbToHSV(r, g, b) | ||||
| 
 | ||||
| 		return HSVA { | ||||
| 			H: hsv.H, | ||||
| 			S: hsv.S, | ||||
| 			V: hsv.V, | ||||
| 			A: uint16(a), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func rgbToHSV (r, g, b uint32) HSV { | ||||
| 	// Adapted from: | ||||
| 	// https://www.cs.rit.edu/~ncs/color/t_convert.html | ||||
| 	 | ||||
| 	component := func (x uint32) float64 { | ||||
| 		return clamp01(float64(x) / 0xFFFF) | ||||
| 	} | ||||
| 	cr := component(r) | ||||
| 	cg := component(g) | ||||
| 	cb := component(b) | ||||
| 	 | ||||
| 	var maxComponent float64 | ||||
| 	if cr > maxComponent { maxComponent = cr } | ||||
| 	if cg > maxComponent { maxComponent = cg } | ||||
| 	if cb > maxComponent { maxComponent = cb } | ||||
| 	var minComponent = 1.0 | ||||
| 	if cr < minComponent { minComponent = cr } | ||||
| 	if cg < minComponent { minComponent = cg } | ||||
| 	if cb < minComponent { minComponent = cb } | ||||
| 	 | ||||
| 	hsv := HSV { | ||||
| 		V: maxComponent, | ||||
| 	} | ||||
| 	 | ||||
| 	delta := maxComponent - minComponent | ||||
| 	if delta == 0 { | ||||
| 		// hsva.S is undefined, so hue doesn't matter | ||||
| 		return hsv | ||||
| 	} | ||||
| 	hsv.S = delta / maxComponent | ||||
| 	 | ||||
| 	switch { | ||||
| 	case cr == maxComponent: hsv.H =     (cg - cb) / delta | ||||
| 	case cg == maxComponent: hsv.H = 2 + (cb - cr) / delta | ||||
| 	case cb == maxComponent: hsv.H = 4 + (cr - cg) / delta | ||||
| 	} | ||||
| 	 | ||||
| 	hsv.H *= 60 | ||||
| 	if hsv.H < 0 { hsv.H += 360 } | ||||
| 	hsv.H /= 360 | ||||
| 	 | ||||
| 	return hsv | ||||
| } | ||||
| 
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user