@@ -260,37 +260,29 @@ impl Color {
260
260
let hex = hex. as_ref ( ) ;
261
261
let hex = hex. strip_prefix ( '#' ) . unwrap_or ( hex) ;
262
262
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) )
269
268
}
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) )
279
273
}
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 ) ,
291
285
}
292
-
293
- Err ( HexColorError :: Length )
294
286
}
295
287
296
288
/// New `Color` from sRGB colorspace.
@@ -1337,38 +1329,49 @@ impl encase::private::CreateFrom for Color {
1337
1329
1338
1330
impl encase:: ShaderSize for Color { }
1339
1331
1340
- #[ derive( Debug , Error ) ]
1332
+ #[ derive( Debug , Error , PartialEq , Eq ) ]
1341
1333
pub enum HexColorError {
1342
1334
#[ error( "Unexpected length of hex string" ) ]
1343
1335
Length ,
1344
- #[ error( "Error parsing hex value " ) ]
1345
- Hex ( # [ from ] hex :: FromHexError ) ,
1336
+ #[ error( "Invalid hex char " ) ]
1337
+ Char ( char ) ,
1346
1338
}
1347
1339
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 ;
1358
1355
}
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)
1359
1365
}
1360
1366
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) ,
1372
1375
}
1373
1376
}
1374
1377
@@ -1378,29 +1381,21 @@ mod tests {
1378
1381
1379
1382
#[ test]
1380
1383
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 ( '#' ) ) ) ;
1404
1399
}
1405
1400
1406
1401
#[ test]
0 commit comments