@@ -260,37 +260,29 @@ impl Color {
260260 let hex = hex. as_ref ( ) ;
261261 let hex = hex. strip_prefix ( '#' ) . unwrap_or ( hex) ;
262262
263- // RGB
264- if hex. len ( ) == 3 {
265- let mut data = [ 0 ; 6 ] ;
266- for ( i, ch) in hex. chars ( ) . enumerate ( ) {
267- data[ i * 2 ] = ch as u8 ;
268- data[ i * 2 + 1 ] = ch as u8 ;
263+ match * hex. as_bytes ( ) {
264+ // RGB
265+ [ r, g, b] => {
266+ let [ r, g, b, ..] = decode_hex ( [ r, r, g, g, b, b] ) ?;
267+ Ok ( Color :: rgb_u8 ( r, g, b) )
269268 }
270- return decode_rgb ( & data) ;
271- }
272-
273- // RGBA
274- if hex. len ( ) == 4 {
275- let mut data = [ 0 ; 8 ] ;
276- for ( i, ch) in hex. chars ( ) . enumerate ( ) {
277- data[ i * 2 ] = ch as u8 ;
278- data[ i * 2 + 1 ] = ch as u8 ;
269+ // RGBA
270+ [ r, g, b, a] => {
271+ let [ r, g, b, a, ..] = decode_hex ( [ r, r, g, g, b, b, a, a] ) ?;
272+ Ok ( Color :: rgba_u8 ( r, g, b, a) )
279273 }
280- return decode_rgba ( & data ) ;
281- }
282-
283- // RRGGBB
284- if hex . len ( ) == 6 {
285- return decode_rgb ( hex . as_bytes ( ) ) ;
286- }
287-
288- // RRGGBBAA
289- if hex . len ( ) == 8 {
290- return decode_rgba ( hex . as_bytes ( ) ) ;
274+ // RRGGBB
275+ [ r1 , r2 , g1 , g2 , b1 , b2 ] => {
276+ let [ r , g , b , .. ] = decode_hex ( [ r1 , r2 , g1 , g2 , b1 , b2 ] ) ? ;
277+ Ok ( Color :: rgb_u8 ( r , g , b ) )
278+ }
279+ // RRGGBBAA
280+ [ r1 , r2 , g1 , g2 , b1 , b2 , a1 , a2 ] => {
281+ let [ r , g , b , a , .. ] = decode_hex ( [ r1 , r2 , g1 , g2 , b1 , b2 , a1 , a2 ] ) ? ;
282+ Ok ( Color :: rgba_u8 ( r , g , b , a ) )
283+ }
284+ _ => Err ( HexColorError :: Length ) ,
291285 }
292-
293- Err ( HexColorError :: Length )
294286 }
295287
296288 /// New `Color` from sRGB colorspace.
@@ -1337,38 +1329,49 @@ impl encase::private::CreateFrom for Color {
13371329
13381330impl encase:: ShaderSize for Color { }
13391331
1340- #[ derive( Debug , Error ) ]
1332+ #[ derive( Debug , Error , PartialEq , Eq ) ]
13411333pub enum HexColorError {
13421334 #[ error( "Unexpected length of hex string" ) ]
13431335 Length ,
1344- #[ error( "Error parsing hex value " ) ]
1345- Hex ( # [ from ] hex :: FromHexError ) ,
1336+ #[ error( "Invalid hex char " ) ]
1337+ Char ( char ) ,
13461338}
13471339
1348- fn decode_rgb ( data : & [ u8 ] ) -> Result < Color , HexColorError > {
1349- let mut buf = [ 0 ; 3 ] ;
1350- match hex:: decode_to_slice ( data, & mut buf) {
1351- Ok ( _) => {
1352- let r = buf[ 0 ] as f32 / 255.0 ;
1353- let g = buf[ 1 ] as f32 / 255.0 ;
1354- let b = buf[ 2 ] as f32 / 255.0 ;
1355- Ok ( Color :: rgb ( r, g, b) )
1356- }
1357- Err ( err) => Err ( HexColorError :: Hex ( err) ) ,
1340+ /// Converts hex bytes to an array of RGB\[A\] components
1341+ ///
1342+ /// # Example
1343+ /// For RGB: *b"ffffff" -> [255, 255, 255, ..]
1344+ /// For RGBA: *b"E2E2E2FF" -> [226, 226, 226, 255, ..]
1345+ const fn decode_hex < const N : usize > ( mut bytes : [ u8 ; N ] ) -> Result < [ u8 ; N ] , HexColorError > {
1346+ let mut i = 0 ;
1347+ while i < bytes. len ( ) {
1348+ // Convert single hex digit to u8
1349+ let val = match hex_value ( bytes[ i] ) {
1350+ Ok ( val) => val,
1351+ Err ( byte) => return Err ( HexColorError :: Char ( byte as char ) ) ,
1352+ } ;
1353+ bytes[ i] = val;
1354+ i += 1 ;
13581355 }
1356+ // Modify the original bytes to give an `N / 2` length result
1357+ i = 0 ;
1358+ while i < bytes. len ( ) / 2 {
1359+ // Convert pairs of u8 to R/G/B/A
1360+ // e.g `ff` -> [102, 102] -> [15, 15] = 255
1361+ bytes[ i] = bytes[ i * 2 ] * 16 + bytes[ i * 2 + 1 ] ;
1362+ i += 1 ;
1363+ }
1364+ Ok ( bytes)
13591365}
13601366
1361- fn decode_rgba ( data : & [ u8 ] ) -> Result < Color , HexColorError > {
1362- let mut buf = [ 0 ; 4 ] ;
1363- match hex:: decode_to_slice ( data, & mut buf) {
1364- Ok ( _) => {
1365- let r = buf[ 0 ] as f32 / 255.0 ;
1366- let g = buf[ 1 ] as f32 / 255.0 ;
1367- let b = buf[ 2 ] as f32 / 255.0 ;
1368- let a = buf[ 3 ] as f32 / 255.0 ;
1369- Ok ( Color :: rgba ( r, g, b, a) )
1370- }
1371- Err ( err) => Err ( HexColorError :: Hex ( err) ) ,
1367+ /// Parse a single hex digit (a-f/A-F/0-9) as a `u8`
1368+ const fn hex_value ( b : u8 ) -> Result < u8 , u8 > {
1369+ match b {
1370+ b'0' ..=b'9' => Ok ( b - b'0' ) ,
1371+ b'A' ..=b'F' => Ok ( b - b'A' + 10 ) ,
1372+ b'a' ..=b'f' => Ok ( b - b'a' + 10 ) ,
1373+ // Wrong hex digit
1374+ _ => Err ( b) ,
13721375 }
13731376}
13741377
@@ -1378,29 +1381,21 @@ mod tests {
13781381
13791382 #[ test]
13801383 fn hex_color ( ) {
1381- assert_eq ! ( Color :: hex( "FFF" ) . unwrap( ) , Color :: rgb( 1.0 , 1.0 , 1.0 ) ) ;
1382- assert_eq ! ( Color :: hex( "000" ) . unwrap( ) , Color :: rgb( 0.0 , 0.0 , 0.0 ) ) ;
1383- assert ! ( Color :: hex( "---" ) . is_err( ) ) ;
1384-
1385- assert_eq ! ( Color :: hex( "FFFF" ) . unwrap( ) , Color :: rgba( 1.0 , 1.0 , 1.0 , 1.0 ) ) ;
1386- assert_eq ! ( Color :: hex( "0000" ) . unwrap( ) , Color :: rgba( 0.0 , 0.0 , 0.0 , 0.0 ) ) ;
1387- assert ! ( Color :: hex( "----" ) . is_err( ) ) ;
1388-
1389- assert_eq ! ( Color :: hex( "FFFFFF" ) . unwrap( ) , Color :: rgb( 1.0 , 1.0 , 1.0 ) ) ;
1390- assert_eq ! ( Color :: hex( "000000" ) . unwrap( ) , Color :: rgb( 0.0 , 0.0 , 0.0 ) ) ;
1391- assert ! ( Color :: hex( "------" ) . is_err( ) ) ;
1392-
1393- assert_eq ! (
1394- Color :: hex( "FFFFFFFF" ) . unwrap( ) ,
1395- Color :: rgba( 1.0 , 1.0 , 1.0 , 1.0 )
1396- ) ;
1397- assert_eq ! (
1398- Color :: hex( "00000000" ) . unwrap( ) ,
1399- Color :: rgba( 0.0 , 0.0 , 0.0 , 0.0 )
1400- ) ;
1401- assert ! ( Color :: hex( "--------" ) . is_err( ) ) ;
1402-
1403- assert ! ( Color :: hex( "1234567890" ) . is_err( ) ) ;
1384+ assert_eq ! ( Color :: hex( "FFF" ) , Ok ( Color :: WHITE ) ) ;
1385+ assert_eq ! ( Color :: hex( "FFFF" ) , Ok ( Color :: WHITE ) ) ;
1386+ assert_eq ! ( Color :: hex( "FFFFFF" ) , Ok ( Color :: WHITE ) ) ;
1387+ assert_eq ! ( Color :: hex( "FFFFFFFF" ) , Ok ( Color :: WHITE ) ) ;
1388+ assert_eq ! ( Color :: hex( "000" ) , Ok ( Color :: BLACK ) ) ;
1389+ assert_eq ! ( Color :: hex( "000F" ) , Ok ( Color :: BLACK ) ) ;
1390+ assert_eq ! ( Color :: hex( "000000" ) , Ok ( Color :: BLACK ) ) ;
1391+ assert_eq ! ( Color :: hex( "000000FF" ) , Ok ( Color :: BLACK ) ) ;
1392+ assert_eq ! ( Color :: hex( "03a9f4" ) , Ok ( Color :: rgb_u8( 3 , 169 , 244 ) ) ) ;
1393+ assert_eq ! ( Color :: hex( "yy" ) , Err ( HexColorError :: Length ) ) ;
1394+ assert_eq ! ( Color :: hex( "yyy" ) , Err ( HexColorError :: Char ( 'y' ) ) ) ;
1395+ assert_eq ! ( Color :: hex( "#f2a" ) , Ok ( Color :: rgb_u8( 255 , 34 , 170 ) ) ) ;
1396+ assert_eq ! ( Color :: hex( "#e23030" ) , Ok ( Color :: rgb_u8( 226 , 48 , 48 ) ) ) ;
1397+ assert_eq ! ( Color :: hex( "#ff" ) , Err ( HexColorError :: Length ) ) ;
1398+ assert_eq ! ( Color :: hex( "##fff" ) , Err ( HexColorError :: Char ( '#' ) ) ) ;
14041399 }
14051400
14061401 #[ test]
0 commit comments