1 #Region "Microsoft.VisualBasic::ca2b7ce5a34fb486948900d2d2c75a71, Microsoft.VisualBasic.Core\Extensions\Image\Colors\GDIColors.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module GDIColors
35     
36     '         Properties: AllDotNetPrefixColors, ChartColors
37     
38     '         Function: __getDotNetColors, (+2 Overloads) Alpha, ARGBExpression, AsDefaultColor, Average
39     '                   Dark, Equals, EuclideanDistance, HTMLColors, IsColorExpression
40     '                   IsNullOrEmpty, IsTransparent, Light, Middle, RGBExpression
41     '                   ToColor, TranslateColor
42     
43     
44     ' /********************************************************************************/
45
46 #End Region
47
48 Imports System.Drawing
49 Imports System.Reflection
50 Imports System.Runtime.CompilerServices
51 Imports System.Text.RegularExpressions
52 Imports Microsoft.VisualBasic.CommandLine.Reflection
53 Imports Microsoft.VisualBasic.Language
54 Imports Microsoft.VisualBasic.Language.Default
55
56 Namespace Imaging
57
58     ''' <summary>
59     ''' Extensions function for the gdi+ color type.
60     ''' </summary>
61     Public Module GDIColors
62
63         <MethodImpl(MethodImplOptions.AggressiveInlining)>
64         <Extension>
65         Public Function HTMLColors(colors As IEnumerable(Of Color)) As IEnumerable(Of String)
66             Return colors.Select(AddressOf ToHtmlColor)
67         End Function
68
69         ''' <summary>
70         ''' ``<paramref name="x"/> -> <paramref name="y"/>``:返回两个颜色之间的中间的颜色,这个函数是某些插值操作所需要的
71         ''' </summary>
72         ''' <param name="x"></param>
73         ''' <param name="y"></param>
74         ''' <returns></returns>
75         <Extension> Public Function Middle(x As Color, y As Color) As Color
76             Dim r% = (y.R - x.R) / 2 + x.R
77             Dim g% = (y.G - x.G) / 2 + x.G
78             Dim b% = (y.B - x.B) / 2 + x.B
79             Dim a% = (y.A - x.A) / 2 + x.A
80             Dim c As Color = Color.FromArgb(a, r, g, b)
81             Return c
82         End Function
83
84         ''' <summary>
85         ''' 调整所输入的这一组颜色的alpha值
86         ''' </summary>
87         ''' <param name="colors"></param>
88         ''' <param name="alphaValue%"></param>
89         ''' <returns></returns>
90         <Extension>
91         Public Function Alpha(colors As IEnumerable(Of Color), alphaValue%) As Color()
92             Dim out As New List(Of Color)
93             For Each c As Color In colors
94                 With c
95                     out += Color.FromArgb(alphaValue, .R, .G, .B)
96                 End With
97             Next
98             Return out
99         End Function
100
101         ''' <summary>
102         ''' alpha=[0, 255]
103         ''' </summary>
104         ''' <param name="c"></param>
105         ''' <param name="alphaValue%"></param>
106         ''' <returns></returns>
107         <MethodImpl(MethodImplOptions.AggressiveInlining)>
108         <Extension>
109         Public Function Alpha(c As Color, alphaValue%) As Color
110             Return Color.FromArgb(alphaValue, c.R, c.G, c.B)
111         End Function
112
113         <Extension>
114         Public Function Average(colors As IEnumerable(Of Color)) As Color
115             Dim data As Color() = colors.ToArray
116             Dim A% = data.Select(Function(c) CDbl(c.A)).Average
117             Dim R% = data.Select(Function(c) CDbl(c.R)).Average
118             Dim G% = data.Select(Function(c) CDbl(c.G)).Average
119             Dim B% = data.Select(Function(c) CDbl(c.B)).Average
120
121             Return Color.FromArgb(A, R, G, B)
122         End Function
123
124         ''' <summary>
125         ''' Creates a new light color object for the control from the specified color and
126         ''' lightens it by the specified percentage.
127         ''' </summary>
128         ''' <param name="base">The <see cref="System.Drawing.Color"/> to be lightened.</param>
129         ''' <param name="percent!">The percentage to lighten the specified <see cref="System.Drawing.Color"/>.</param>
130         ''' <returns>A <see cref="System.Drawing.Color"/> that represents the light color on the control.</returns>
131         ''' 
132         <MethodImpl(MethodImplOptions.AggressiveInlining)>
133         <Extension>
134         Public Function Light(base As Color, percent!) As Color
135             Return ControlPaint.Light(base, percent)
136         End Function
137
138         ''' <summary>
139         ''' Creates a new dark color object for the control from the specified color and
140         ''' darkens it by the specified percentage.
141         ''' </summary>
142         ''' <param name="base">The <see cref="System.Drawing.Color"/> to be darkened.</param>
143         ''' <param name="percent!">The percentage to darken the specified <see cref="System.Drawing.Color"/>.</param>
144         ''' <returns>A <see cref="System.Drawing.Color"/> that represent the dark color on the control.</returns>
145         ''' 
146         <MethodImpl(MethodImplOptions.AggressiveInlining)>
147         <Extension>
148         Public Function Dark(base As Color, percent!) As Color
149             Return ControlPaint.Dark(base, percent)
150         End Function
151
152         ''' <summary>
153         ''' ``rgb(r,g,b)``
154         ''' </summary>
155         ''' <param name="c"></param>
156         ''' <returns></returns>
157         <Extension>
158         Public Function RGBExpression(c As Color) As String
159             With c
160                 Return $"rgb({ .R},{ .G},{ .B})"
161             End With
162         End Function
163
164         ''' <summary>
165         ''' ``rgb(a,r,g,b)``
166         ''' </summary>
167         ''' <param name="c"></param>
168         ''' <returns></returns>
169         <Extension>
170         Public Function ARGBExpression(c As Color) As String
171             With c
172                 Return $"rgb({ .A},{ .R},{ .G},{ .B})"
173             End With
174         End Function
175
176 #If NET_40 = 0 Then
177
178         ''' <summary>
179         ''' Reads all of the color property from <see cref="Color"/> and then creates the color dictionary based on the property name.
180         ''' </summary>
181         ''' <returns></returns>
182         Private Function __getDotNetColors() As Dictionary(Of String, Color)
183             Dim props As IEnumerable(Of PropertyInfo) =
184                 GetType(Color).GetProperties(BindingFlags.Public Or BindingFlags.Static)
185             Dim getValues = From p As PropertyInfo  Gets all of the known name color from the Color object its shared property.
186                             In props
187                             Where p.PropertyType = GetType(Color)
188                             Let ColorValue As Color = DirectCast(p.GetValue(Nothing), Color)
189                             Select name = p.Name,
190                                 ColorValue
191             Dim hash As Dictionary(Of String, Color) =
192                 getValues.ToDictionary(Function(x) x.name.ToLower,
193                                        Function(x) x.ColorValue)
194             Return hash
195         End Function
196
197         ''' <summary>
198         ''' Key都是小写的
199         ''' </summary>
200         ReadOnly __allDotNETPrefixColors As Dictionary(Of String, Color) =
201             __getDotNetColors()
202
203         ''' <summary>
204         ''' Gets all of the known name color from the Color object its shared property.
205         ''' </summary>
206         ''' <returns></returns>
207         Public ReadOnly Property AllDotNetPrefixColors As Color()
208             <MethodImpl(MethodImplOptions.AggressiveInlining)>
209             Get
210                 Return __allDotNETPrefixColors.Values.Shuffles
211             End Get
212         End Property
213
214         ''' <summary>
215         ''' 经过人工筛选的颜色,不会出现过白或者过黑,过度相似的情况
216         ''' </summary>
217         ''' <returns></returns>
218         Public ReadOnly Property ChartColors As Color() = {
219             Color.AliceBlue, Color.Aquamarine, Color.BlueViolet, Color.BurlyWood,
220             Color.CadetBlue, Color.Chartreuse, Color.Chocolate, Color.Coral,
221             Color.CornflowerBlue, Color.Crimson, Color.Cyan, Color.DarkBlue,
222             Color.DarkCyan, Color.DarkGoldenrod, Color.DarkGray, Color.DarkMagenta,
223             Color.DarkOliveGreen, Color.DarkOrchid, Color.DarkSeaGreen, Color.DarkSlateBlue,
224             Color.DarkSlateGray, Color.DeepPink, Color.DeepSkyBlue, Color.DodgerBlue,
225             Color.GreenYellow, Color.ForestGreen, Color.Firebrick, Color.Gold, Color.Indigo,
226             Color.LightSeaGreen, Color.LightSkyBlue, Color.LimeGreen, Color.MediumSeaGreen,
227             Color.MediumTurquoise, Color.MidnightBlue, Color.Orchid, Color.OrangeRed, Color.Red,
228             Color.RoyalBlue, Color.SeaGreen, Color.SpringGreen, Color.SteelBlue, Color.Teal,
229             Color.YellowGreen
230         }
231 #End If
232         ''' <summary>
233         ''' Regex expression for parsing the rgb(a,r,g,b) expression of the color.(解析颜色表达式里面的RGB的正则表达式)
234         ''' </summary>
235         Public Const rgbExprValues$ = "\d+\s*,\s*\d+\s*,\s*\d+\s*(,\s*\d+)?"
236         Public Const rgbExpr$ = "rgb\(" & rgbExprValues & "\)"
237
238         ''' <summary>
239         ''' <see cref="Color"/>.Name, rgb(a,r,g,b)
240         ''' </summary>
241         ''' <param name="str">颜色表达式或者名称</param>
242         ''' <returns></returns>
243         <ExportAPI("Get.Color")>
244         <Extension> Public Function ToColor(str As StringOptional onFailure As Color = NothingOptional throwEx As Boolean = TrueAs Color
245 #If NET_40 = 0 Then
246             If String.IsNullOrEmpty(str) Then
247                 Return Color.Black
248             ElseIf str.TextEquals("transparent"Then
249                 Return Color.Transparent
250             End If
251
252             Dim s As String = Regex.Match(str, rgbExprValues).Value
253
254             If String.IsNullOrEmpty(s) Then ' Color from name/known color
255                 Dim key As String = str.ToLower
256
257                 If __allDotNETPrefixColors.ContainsKey(key) Then
258                     Return __allDotNETPrefixColors(key)
259                 Else
260                     Return Color.FromName(str)
261                 End If
262             Else
263                 Dim tokens As String() = s.Split(","c)
264
265                 If tokens.Length = 3 Then  ' rgb
266                     Dim R As Integer = CInt(Val(tokens(0)))
267                     Dim G As Integer = CInt(Val(tokens(1)))
268                     Dim B As Integer = CInt(Val(tokens(2)))
269
270                     Return Color.FromArgb(R, G, B)
271                 ElseIf tokens.Length = 4 Then ' argb
272                     Dim A As Integer = CInt(Val(tokens(0)))
273                     Dim R As Integer = CInt(Val(tokens(1)))
274                     Dim G As Integer = CInt(Val(tokens(2)))
275                     Dim B As Integer = CInt(Val(tokens(3)))
276
277                     Return Color.FromArgb(A, R, G, B)
278                 Else
279                     If Not onFailure.IsEmpty Then
280                         Return onFailure
281                     Else
282                         If throwEx Then
283                             Throw New Exception("Unable parsing any color information from expression: " & str)
284                         Else
285                             Return Nothing
286                         End If
287                     End If
288                 End If
289             End If
290 #Else
291             Throw New NotSupportedException
292 #End If
293         End Function
294
295         ''' <summary>
296         ''' 这个函数会尝试用不同的模式来解析颜色表达式
297         ''' </summary>
298         ''' <param name="exp$"></param>
299         ''' <returns></returns>
300         <Extension> Public Function TranslateColor(exp$, Optional throwEx As Boolean = TrueAs Color
301             If exp.StringEmpty Then
302                 Return Color.Black
303             End If
304             If exp.First = "#"Then
305                 ' 2017-2-2
306                 ' 经过测试与3mf文件之中的材质颜色定义一致,没有问题
307                 Return HexColor.ConvertToRbg(exp)
308             End If
309             If Regex.Match(exp, "\d+").Value = exp Then
310                 Return ColorTranslator.FromOle(CInt(exp))
311             End If
312
313             Return exp.ToColor(throwEx:=throwEx)
314         End Function
315
316         <Extension>
317         Public Function IsColorExpression(expression$) As Boolean
318             If expression.MatchPattern(rgbExpr, RegexICSng) Then
319                 Return True
320             ElseIf __allDotNETPrefixColors.ContainsKey(expression.ToLower) Then
321                 Return True
322             ElseIf expression.MatchPattern("\d+"Then
323                 Return True
324             ElseIf expression.MatchPattern("#[a-z0-9]+", RegexICSng) Then
325                 Return True
326             Else
327                 Return False
328             End If
329         End Function
330
331         ''' <summary>
332         ''' Determine that the target color value is a empty variable.(判断目标颜色值是否为空值)
333         ''' </summary>
334         ''' <returns></returns>
335         ''' <remarks></remarks>
336         ''' 
337         <MethodImpl(MethodImplOptions.AggressiveInlining)>
338         <Extension> Public Function IsNullOrEmpty(Color As Color) As Boolean
339             Return Color = Nothing OrElse Color.IsEmpty
340         End Function
341
342         <MethodImpl(MethodImplOptions.AggressiveInlining)>
343         <Extension>
344         Public Function AsDefaultColor(color As Color) As DefaultValue(Of Color)
345             Return color.AsDefault(Function(c) DirectCast(c, Color).IsNullOrEmpty)
346         End Function
347
348         ' 透明色
349         
350         '    A,R,G,B
351         ' 1. 0,0,0,0
352         ' 2. 0,255,255,255
353         ' 3. 255,0,0,0
354
355         <MethodImpl(MethodImplOptions.AggressiveInlining)>
356         <Extension>
357         Public Function IsTransparent(c As Color) As Boolean
358             Return c.A = 0 OrElse (c.R = 0 AndAlso c.G = 0 AndAlso c.B = 0)
359         End Function
360
361         ''' <summary>
362         ''' 分别比较A,R,G,B这些属性值来判断这样个颜色对象值是否相等
363         ''' </summary>
364         ''' <param name="a"></param>
365         ''' <param name="b"></param>
366         ''' <returns></returns>
367         <Extension> Public Function Equals(a As Color, b As Color) As Boolean
368             If a.IsTransparent AndAlso b.IsTransparent Then
369                 Return True
370             End If
371
372             If a.A = b.A Then
373                 If a.A = 0 Then
374                     ' 只要是alpha值为零,肯定是透明色
375                     ' 在这里判定为相同的颜色
376                     Return True
377                 End If
378             Else
379                 Return False '  alpha值不相等,则颜色值肯定不相等
380             End If
381
382             If a.B <> b.B Then
383                 Return False
384             End If
385             If a.G <> b.G Then
386                 Return False
387             End If
388             If a.R <> b.R Then
389                 Return False
390             End If
391
392             Return True
393         End Function
394
395         <Extension>
396         Public Function EuclideanDistance(a As Color, b As Color) As Double
397             Return Math.EuclideanDistance({a.R, a.G, a.B}, {b.R, b.G, b.B})
398         End Function
399     End Module
400 End Namespace