...

Source file src/gonum.org/v1/plot/palette/moreland/convert.go

Documentation: gonum.org/v1/plot/palette/moreland

     1  // Copyright ©2016 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package moreland
     6  
     7  import (
     8  	"image/color"
     9  	"math"
    10  )
    11  
    12  // rgb represents a physically linear RGB color.
    13  type rgb struct {
    14  	R, G, B float64
    15  }
    16  
    17  // cieXYZ returns a CIE XYZ color representation of the receiver.
    18  func (c rgb) cieXYZ() cieXYZ {
    19  	return cieXYZ{
    20  		X: 0.4124*c.R + 0.3576*c.G + 0.1805*c.B,
    21  		Y: 0.2126*c.R + 0.7152*c.G + 0.0722*c.B,
    22  		Z: 0.0193*c.R + 0.1192*c.G + 0.9505*c.B,
    23  	}
    24  }
    25  
    26  // sRGBA returns an sRGB color representation of the receiver using the
    27  // provided alpha which must be in [0, 1].
    28  func (c rgb) sRGBA(alpha float64) sRGBA {
    29  	// f converts from a linear RGB component to an sRGB component.
    30  	f := func(v float64) float64 {
    31  		if v > 0.0031308 {
    32  			return 1.055*math.Pow(v, 1/2.4) - 0.055
    33  		}
    34  		return 12.92 * v
    35  	}
    36  
    37  	return sRGBA{
    38  		R: f(c.R),
    39  		G: f(c.G),
    40  		B: f(c.B),
    41  		A: alpha,
    42  	}
    43  }
    44  
    45  // cieXYZ represents a color in CIE XYZ space.
    46  // Y must be in the range [0,1]. X and Z must be greater than 0.
    47  type cieXYZ struct {
    48  	X, Y, Z float64
    49  }
    50  
    51  // rgb returns a linear RGB representation of the receiver.
    52  func (c cieXYZ) rgb() rgb {
    53  	return rgb{
    54  		R: c.X*3.2406 + c.Y*-1.5372 + c.Z*-0.4986,
    55  		G: c.X*-0.9689 + c.Y*1.8758 + c.Z*0.0415,
    56  		B: c.X*0.0557 + c.Y*-0.204 + c.Z*1.057,
    57  	}
    58  }
    59  
    60  // cieLAB returns a CIELAB color representation of the receiver.
    61  func (c cieXYZ) cieLAB() cieLAB {
    62  	// f is an intermediate step in converting from CIE XYZ to CIE LAB.
    63  	f := func(v float64) float64 {
    64  		if v > 0.008856 {
    65  			return math.Pow(v, 1.0/3.0)
    66  		}
    67  		return 7.787*v + 16.0/116.0
    68  	}
    69  
    70  	tempX := f(c.X / 0.9505)
    71  	tempY := f(c.Y)
    72  	tempZ := f(c.Z / 1.089)
    73  	return cieLAB{
    74  		L: (116.0 * tempY) - 16.0,
    75  		A: 500.0 * (tempX - tempY),
    76  		B: 200 * (tempY - tempZ),
    77  	}
    78  }
    79  
    80  // sRGBA represents a color within the sRGB color space, with an alpha channel
    81  // but not premultiplied. All values must be in the range [0,1].
    82  type sRGBA struct {
    83  	R, G, B, A float64
    84  }
    85  
    86  // rgb returns a linear RGB representation of the receiver.
    87  func (c sRGBA) rgb() rgb {
    88  	// f converts from an sRGB component to a linear RGB component.
    89  	f := func(v float64) float64 {
    90  		if v > 0.04045 {
    91  			return math.Pow((v+0.055)/1.055, 2.4)
    92  		}
    93  		return v / 12.92
    94  	}
    95  
    96  	return rgb{
    97  		R: f(c.R),
    98  		G: f(c.G),
    99  		B: f(c.B),
   100  	}
   101  }
   102  
   103  // RGBA implements the color.Color interface.
   104  func (c sRGBA) RGBA() (r, g, b, a uint32) {
   105  	return uint32(c.R * c.A * 0xffff), uint32(c.G * c.A * 0xffff), uint32(c.B * c.A * 0xffff), uint32(c.A * 0xffff)
   106  }
   107  
   108  // cieLAB returns a CIE LAB representation of the receiver.
   109  func (c sRGBA) cieLAB() cieLAB {
   110  	return c.rgb().cieXYZ().cieLAB()
   111  }
   112  
   113  // colorTosRGBA converts a color to an sRGBA.
   114  func colorTosRGBA(c color.Color) sRGBA {
   115  	r, g, b, a := c.RGBA()
   116  	if a == 0 {
   117  		return sRGBA{}
   118  	}
   119  	return sRGBA{
   120  		R: float64(r) / float64(a),
   121  		G: float64(g) / float64(a),
   122  		B: float64(b) / float64(a),
   123  		A: float64(a) / 0xffff,
   124  	}
   125  }
   126  
   127  // clamp forces all channels in c to be within the range [0, 1].
   128  func (c *sRGBA) clamp() {
   129  	if c.R > 1 {
   130  		c.R = 1
   131  	}
   132  	if c.G > 1 {
   133  		c.G = 1
   134  	}
   135  	if c.B > 1 {
   136  		c.B = 1
   137  	}
   138  	if c.A > 1 {
   139  		c.A = 1
   140  	}
   141  	if c.R < 0 {
   142  		c.R = 0
   143  	}
   144  	if c.G < 0 {
   145  		c.G = 0
   146  	}
   147  	if c.B < 0 {
   148  		c.B = 0
   149  	}
   150  	if c.A < 0 {
   151  		c.A = 0
   152  	}
   153  }
   154  
   155  // cieLAB represents a color in CIE LAB space.
   156  // L must be in the range [0, 100].
   157  type cieLAB struct {
   158  	L, A, B float64
   159  }
   160  
   161  // sRGBA return a linear RGB color representation of the receiver using the
   162  // provided alpha which must be in [0, 1].
   163  func (c cieLAB) sRGBA(alpha float64) sRGBA {
   164  	return c.cieXYZ().rgb().sRGBA(alpha)
   165  }
   166  
   167  // cieXYZ returns a CIE XYZ color representation of the receiver.
   168  func (c cieLAB) cieXYZ() cieXYZ {
   169  	// f is an intermediate step in converting from CIE LAB to CIE XYZ.
   170  	f := func(v float64) float64 {
   171  		const (
   172  			xlim = 0.008856
   173  			a    = 7.787
   174  			b    = 16. / 116.
   175  			ylim = a*xlim + b
   176  		)
   177  		if v > ylim {
   178  			return v * v * v
   179  		}
   180  		return (v - b) / a
   181  	}
   182  
   183  	// Reference white-point D65
   184  	const xn, yn, zn = 0.95047, 1.0, 1.08883
   185  	return cieXYZ{
   186  		X: xn * f((c.A/500)+(c.L+16)/116),
   187  		Y: yn * f((c.L+16)/116),
   188  		Z: zn * f((c.L+16)/116-(c.B/200)),
   189  	}
   190  }
   191  
   192  // MSH returns an MSH color representation of the receiver.
   193  func (c cieLAB) MSH() msh {
   194  	m := math.Pow(c.L*c.L+c.A*c.A+c.B*c.B, 0.5)
   195  	return msh{
   196  		M: m,
   197  		S: math.Acos(c.L / m),
   198  		H: math.Atan2(c.B, c.A),
   199  	}
   200  }
   201  
   202  // MSH represents a color in Magnitude-Saturation-Hue color space.
   203  type msh struct {
   204  	M, S, H float64
   205  }
   206  
   207  // colorToMSH converts a color to MSH space.
   208  // TODO: If msh ever becomes exported, change this to implment color.Model
   209  func colorToMSH(c color.Color) msh {
   210  	return colorTosRGBA(c).cieLAB().MSH()
   211  }
   212  
   213  // cieLAB returns a CIELAB representation of the receiver.
   214  func (c msh) cieLAB() cieLAB {
   215  	return cieLAB{
   216  		L: c.M * math.Cos(c.S),
   217  		A: c.M * math.Sin(c.S) * math.Cos(c.H),
   218  		B: c.M * math.Sin(c.S) * math.Sin(c.H),
   219  	}
   220  }
   221  
   222  // RGBA implements the color.Color interface.
   223  func (c msh) RGBA() (r, g, b, a uint32) {
   224  	return c.cieLAB().sRGBA(1.0).RGBA()
   225  }
   226  
   227  // hueTwist returns the hue twist between color c and converge magnitude
   228  // convergeM.
   229  func hueTwist(c msh, convergeM float64) float64 {
   230  	signH := c.H / math.Abs(c.H)
   231  	return signH * c.S * math.Sqrt(convergeM*convergeM-c.M*c.M) / (c.M * math.Sin(c.S))
   232  }
   233  

View as plain text