@@ -15,7 +15,15 @@ public static bool TryParse(ReadOnlySpan<char> value, out float red, out float g
1515
1616 if ( value [ 0 ] == '#' )
1717 {
18- return TryParseHex ( value , out red , out green , out blue , out alpha ) ;
18+ try
19+ {
20+ ( red , green , blue , alpha ) = FromArgb ( value ) ;
21+ return true ;
22+ }
23+ catch
24+ {
25+ return false ;
26+ }
1927 }
2028
2129 if ( value . StartsWith ( "rgba" . AsSpan ( ) , StringComparison . OrdinalIgnoreCase ) )
@@ -163,187 +171,6 @@ public static bool TryParse(ReadOnlySpan<char> value, out float red, out float g
163171 return false ;
164172 }
165173
166- /// <summary>
167- /// Attempts to parse a hex color string (with or without #).
168- /// Supports formats: #RGB, #ARGB, #RRGGBB, #AARRGGBB
169- /// Uses ARGB format for 4-digit and 8-digit hex values.
170- /// </summary>
171- /// <param name="colorAsHex">The hex color string</param>
172- /// <param name="red">The red component (0.0-1.0)</param>
173- /// <param name="green">The green component (0.0-1.0)</param>
174- /// <param name="blue">The blue component (0.0-1.0)</param>
175- /// <param name="alpha">The alpha component (0.0-1.0)</param>
176- /// <returns>True if parsing succeeded, false otherwise</returns>
177- public static bool TryParseHex ( ReadOnlySpan < char > colorAsHex , out float red , out float green , out float blue , out float alpha )
178- {
179- red = green = blue = 0 ;
180- alpha = 1 ;
181-
182- if ( colorAsHex . IsEmpty )
183- return false ;
184-
185- //Skip # if present
186- if ( colorAsHex [ 0 ] == '#' )
187- colorAsHex = colorAsHex . Slice ( 1 ) ;
188-
189- try
190- {
191- return TryParseArgbHex ( colorAsHex , out red , out green , out blue , out alpha ) ;
192- }
193- catch
194- {
195- return false ;
196- }
197- }
198-
199- /// <summary>
200- /// Attempts to parse a hex color string in RGBA format (with or without #).
201- /// Supports formats: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
202- /// Uses RGBA format for 4-digit and 8-digit hex values.
203- /// </summary>
204- /// <param name="colorAsHex">The hex color string</param>
205- /// <param name="red">The red component (0.0-1.0)</param>
206- /// <param name="green">The green component (0.0-1.0)</param>
207- /// <param name="blue">The blue component (0.0-1.0)</param>
208- /// <param name="alpha">The alpha component (0.0-1.0)</param>
209- /// <returns>True if parsing succeeded, false otherwise</returns>
210- public static bool TryParseRgbaHexFormat ( ReadOnlySpan < char > colorAsHex , out float red , out float green , out float blue , out float alpha )
211- {
212- red = green = blue = 0 ;
213- alpha = 1 ;
214-
215- if ( colorAsHex . IsEmpty )
216- return false ;
217-
218- //Skip # if present
219- if ( colorAsHex [ 0 ] == '#' )
220- colorAsHex = colorAsHex . Slice ( 1 ) ;
221-
222- try
223- {
224- return TryParseRgbaHex ( colorAsHex , out red , out green , out blue , out alpha ) ;
225- }
226- catch
227- {
228- return false ;
229- }
230- }
231-
232- /// <summary>
233- /// Parses hex color in ARGB format (#ARGB, #AARRGGBB)
234- /// </summary>
235- private static bool TryParseArgbHex ( ReadOnlySpan < char > colorAsHex , out float red , out float green , out float blue , out float alpha )
236- {
237- int r = 0 , g = 0 , b = 0 , a = 255 ;
238-
239- if ( colorAsHex . Length == 6 )
240- {
241- //#RRGGBB
242- r = ParseInt ( colorAsHex . Slice ( 0 , 2 ) ) ;
243- g = ParseInt ( colorAsHex . Slice ( 2 , 2 ) ) ;
244- b = ParseInt ( colorAsHex . Slice ( 4 , 2 ) ) ;
245- }
246- else if ( colorAsHex . Length == 3 )
247- {
248- //#RGB
249- Span < char > temp = stackalloc char [ 2 ] ;
250- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 0 ] ;
251- r = ParseInt ( temp ) ;
252-
253- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 1 ] ;
254- g = ParseInt ( temp ) ;
255-
256- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 2 ] ;
257- b = ParseInt ( temp ) ;
258- }
259- else if ( colorAsHex . Length == 4 )
260- {
261- //#ARGB
262- Span < char > temp = stackalloc char [ 2 ] ;
263- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 0 ] ;
264- a = ParseInt ( temp ) ;
265-
266- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 1 ] ;
267- r = ParseInt ( temp ) ;
268-
269- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 2 ] ;
270- g = ParseInt ( temp ) ;
271-
272- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 3 ] ;
273- b = ParseInt ( temp ) ;
274- }
275- else if ( colorAsHex . Length == 8 )
276- {
277- //#AARRGGBB
278- a = ParseInt ( colorAsHex . Slice ( 0 , 2 ) ) ;
279- r = ParseInt ( colorAsHex . Slice ( 2 , 2 ) ) ;
280- g = ParseInt ( colorAsHex . Slice ( 4 , 2 ) ) ;
281- b = ParseInt ( colorAsHex . Slice ( 6 , 2 ) ) ;
282- }
283- else
284- {
285- red = green = blue = alpha = 0 ;
286- return false ;
287- }
288-
289- red = ( r / 255f ) . Clamp ( 0 , 1 ) ;
290- green = ( g / 255f ) . Clamp ( 0 , 1 ) ;
291- blue = ( b / 255f ) . Clamp ( 0 , 1 ) ;
292- alpha = ( a / 255f ) . Clamp ( 0 , 1 ) ;
293- return true ;
294- }
295-
296- /// <summary>
297- /// Parses hex color in RGBA format (#RGBA, #RRGGBBAA)
298- /// For 3-digit and 6-digit (no alpha), delegates to ARGB parsing since they're identical
299- /// </summary>
300- private static bool TryParseRgbaHex ( ReadOnlySpan < char > colorAsHex , out float red , out float green , out float blue , out float alpha )
301- {
302- if ( colorAsHex . Length == 3 || colorAsHex . Length == 6 )
303- {
304- // For 3-digit and 6-digit hex, RGBA and ARGB are the same since there's no alpha
305- return TryParseArgbHex ( colorAsHex , out red , out green , out blue , out alpha ) ;
306- }
307-
308- int r = 0 , g = 0 , b = 0 , a = 255 ;
309-
310- if ( colorAsHex . Length == 4 )
311- {
312- //#RGBA
313- Span < char > temp = stackalloc char [ 2 ] ;
314- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 0 ] ;
315- r = ParseInt ( temp ) ;
316-
317- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 1 ] ;
318- g = ParseInt ( temp ) ;
319-
320- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 2 ] ;
321- b = ParseInt ( temp ) ;
322-
323- temp [ 0 ] = temp [ 1 ] = colorAsHex [ 3 ] ;
324- a = ParseInt ( temp ) ;
325- }
326- else if ( colorAsHex . Length == 8 )
327- {
328- //#RRGGBBAA
329- r = ParseInt ( colorAsHex . Slice ( 0 , 2 ) ) ;
330- g = ParseInt ( colorAsHex . Slice ( 2 , 2 ) ) ;
331- b = ParseInt ( colorAsHex . Slice ( 4 , 2 ) ) ;
332- a = ParseInt ( colorAsHex . Slice ( 6 , 2 ) ) ;
333- }
334- else
335- {
336- red = green = blue = alpha = 0 ;
337- return false ;
338- }
339-
340- red = ( r / 255f ) . Clamp ( 0 , 1 ) ;
341- green = ( g / 255f ) . Clamp ( 0 , 1 ) ;
342- blue = ( b / 255f ) . Clamp ( 0 , 1 ) ;
343- alpha = ( a / 255f ) . Clamp ( 0 , 1 ) ;
344- return true ;
345- }
346-
347174 /// <summary>
348175 /// Converts HSL values to RGB.
349176 /// </summary>
@@ -362,7 +189,7 @@ public static (float red, float green, float blue) ConvertHslToRgb(float hue, fl
362189 {
363190 return ( luminosity , luminosity , luminosity ) ;
364191 }
365-
192+
366193 float temp2 = luminosity <= 0.5f ? luminosity * ( 1.0f + saturation ) : luminosity + saturation - luminosity * saturation ;
367194 float temp1 = 2.0f * luminosity - temp2 ;
368195
@@ -387,6 +214,115 @@ public static (float red, float green, float blue) ConvertHslToRgb(float hue, fl
387214 return ( clr [ 0 ] , clr [ 1 ] , clr [ 2 ] ) ;
388215 }
389216
217+ public static ( float red , float green , float blue , float alpha ) FromRgba ( ReadOnlySpan < char > colorAsHex )
218+ {
219+ int red = 0 ;
220+ int green = 0 ;
221+ int blue = 0 ;
222+ int alpha = 255 ;
223+
224+ if ( ! colorAsHex . IsEmpty )
225+ {
226+ //Skip # if present
227+ if ( colorAsHex [ 0 ] == '#' )
228+ colorAsHex = colorAsHex . Slice ( 1 ) ;
229+
230+ if ( colorAsHex . Length == 6 || colorAsHex . Length == 3 )
231+ {
232+ //#RRGGBB or #RGB - since there is no A, use FromArgb
233+
234+ return FromArgb ( colorAsHex ) ;
235+ }
236+ else if ( colorAsHex . Length == 4 )
237+ {
238+ //#RGBA
239+ Span < char > temp = stackalloc char [ 2 ] ;
240+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 0 ] ;
241+ red = ParseInt ( temp ) ;
242+
243+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 1 ] ;
244+ green = ParseInt ( temp ) ;
245+
246+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 2 ] ;
247+ blue = ParseInt ( temp ) ;
248+
249+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 3 ] ;
250+ alpha = ParseInt ( temp ) ;
251+ }
252+ else if ( colorAsHex . Length == 8 )
253+ {
254+ //#RRGGBBAA
255+ red = ParseInt ( colorAsHex . Slice ( 0 , 2 ) ) ;
256+ green = ParseInt ( colorAsHex . Slice ( 2 , 2 ) ) ;
257+ blue = ParseInt ( colorAsHex . Slice ( 4 , 2 ) ) ;
258+ alpha = ParseInt ( colorAsHex . Slice ( 6 , 2 ) ) ;
259+ }
260+ }
261+
262+ return ( red / 255f , green / 255f , blue / 255f , alpha / 255f ) ;
263+ }
264+
265+ public static ( float red , float green , float blue , float alpha ) FromArgb ( ReadOnlySpan < char > colorAsHex )
266+ {
267+ int red = 0 ;
268+ int green = 0 ;
269+ int blue = 0 ;
270+ int alpha = 255 ;
271+
272+ if ( ! colorAsHex . IsEmpty )
273+ {
274+ //Skip # if present
275+ if ( colorAsHex [ 0 ] == '#' )
276+ colorAsHex = colorAsHex . Slice ( 1 ) ;
277+
278+ if ( colorAsHex . Length == 6 )
279+ {
280+ //#RRGGBB
281+ red = ParseInt ( colorAsHex . Slice ( 0 , 2 ) ) ;
282+ green = ParseInt ( colorAsHex . Slice ( 2 , 2 ) ) ;
283+ blue = ParseInt ( colorAsHex . Slice ( 4 , 2 ) ) ;
284+ }
285+ else if ( colorAsHex . Length == 3 )
286+ {
287+ //#RGB
288+ Span < char > temp = stackalloc char [ 2 ] ;
289+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 0 ] ;
290+ red = ParseInt ( temp ) ;
291+
292+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 1 ] ;
293+ green = ParseInt ( temp ) ;
294+
295+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 2 ] ;
296+ blue = ParseInt ( temp ) ;
297+ }
298+ else if ( colorAsHex . Length == 4 )
299+ {
300+ //#ARGB
301+ Span < char > temp = stackalloc char [ 2 ] ;
302+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 0 ] ;
303+ alpha = ParseInt ( temp ) ;
304+
305+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 1 ] ;
306+ red = ParseInt ( temp ) ;
307+
308+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 2 ] ;
309+ green = ParseInt ( temp ) ;
310+
311+ temp [ 0 ] = temp [ 1 ] = colorAsHex [ 3 ] ;
312+ blue = ParseInt ( temp ) ;
313+ }
314+ else if ( colorAsHex . Length == 8 )
315+ {
316+ //#AARRGGBB
317+ alpha = ParseInt ( colorAsHex . Slice ( 0 , 2 ) ) ;
318+ red = ParseInt ( colorAsHex . Slice ( 2 , 2 ) ) ;
319+ green = ParseInt ( colorAsHex . Slice ( 4 , 2 ) ) ;
320+ blue = ParseInt ( colorAsHex . Slice ( 6 , 2 ) ) ;
321+ }
322+ }
323+
324+ return ( red / 255f , green / 255f , blue / 255f , alpha / 255f ) ;
325+ }
390326 /// <summary>
391327 /// Converts HSV values to RGB.
392328 /// </summary>
0 commit comments