|
4 | 4 |
|
5 | 5 | use std::cmp;
|
6 | 6 | use std::fmt;
|
| 7 | +use std::f32::consts::PI; |
7 | 8 |
|
8 | 9 | use super::{Token, Parser, ToCss};
|
9 | 10 | use tokenizer::NumericValue;
|
@@ -398,75 +399,151 @@ fn clamp_f32(val: f32) -> u8 {
|
398 | 399 |
|
399 | 400 | #[inline]
|
400 | 401 | fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
|
401 |
| - let (is_rgb, has_alpha) = match_ignore_ascii_case! { name, |
402 |
| - "rgba" => (true, true), |
403 |
| - "rgb" => (true, false), |
404 |
| - "hsl" => (false, false), |
405 |
| - "hsla" => (false, true), |
| 402 | + let is_rgb = match_ignore_ascii_case! { name, |
| 403 | + "rgb" | "rgba" => true, |
| 404 | + "hsl" | "hsla" => false, |
406 | 405 | _ => return Err(())
|
407 | 406 | };
|
408 | 407 |
|
| 408 | + let (red, green, blue, uses_commas) = if is_rgb { |
| 409 | + parse_rgb_components_rgb(arguments)? |
| 410 | + } else { |
| 411 | + parse_rgb_components_hsl(arguments)? |
| 412 | + }; |
| 413 | + |
| 414 | + let alpha = if !arguments.is_exhausted() { |
| 415 | + if uses_commas { |
| 416 | + try!(arguments.expect_comma()); |
| 417 | + } else { |
| 418 | + match try!(arguments.next()) { |
| 419 | + Token::Delim('/') => {}, |
| 420 | + _ => return Err(()) |
| 421 | + }; |
| 422 | + }; |
| 423 | + let token = try!(arguments.next()); |
| 424 | + match token { |
| 425 | + Token::Number(NumericValue { value: v, .. }) => { |
| 426 | + clamp_f32(v) |
| 427 | + } |
| 428 | + Token::Percentage(ref v) => { |
| 429 | + clamp_f32(v.unit_value) |
| 430 | + } |
| 431 | + _ => { |
| 432 | + return Err(()) |
| 433 | + } |
| 434 | + } |
| 435 | + } else { |
| 436 | + 255 |
| 437 | + }; |
| 438 | + |
| 439 | + try!(arguments.expect_exhausted()); |
| 440 | + rgba(red, green, blue, alpha) |
| 441 | +} |
| 442 | + |
| 443 | + |
| 444 | +#[inline] |
| 445 | +fn parse_rgb_components_rgb(arguments: &mut Parser) -> Result<(u8, u8, u8, bool), ()> { |
409 | 446 | let red: u8;
|
410 | 447 | let green: u8;
|
411 | 448 | let blue: u8;
|
412 |
| - if is_rgb { |
413 |
| - // Either integers or percentages, but all the same type. |
414 |
| - // https://drafts.csswg.org/css-color/#rgb-functions |
415 |
| - match try!(arguments.next()) { |
416 |
| - Token::Number(NumericValue { int_value: Some(v), .. }) => { |
417 |
| - red = clamp_i32(v); |
| 449 | + let mut uses_commas = false; |
| 450 | + |
| 451 | + // Either integers or percentages, but all the same type. |
| 452 | + // https://drafts.csswg.org/css-color/#rgb-functions |
| 453 | + match try!(arguments.next()) { |
| 454 | + Token::Number(NumericValue { value: v, .. }) => { |
| 455 | + red = clamp_i32(v as i32); |
| 456 | + green = clamp_i32(match try!(arguments.next()) { |
| 457 | + Token::Number(NumericValue { value: v, .. }) => v, |
| 458 | + Token::Comma => { |
| 459 | + uses_commas = true; |
| 460 | + try!(arguments.expect_number()) |
| 461 | + } |
| 462 | + _ => return Err(()) |
| 463 | + } as i32); |
| 464 | + if uses_commas { |
418 | 465 | try!(arguments.expect_comma());
|
419 |
| - green = clamp_i32(try!(arguments.expect_integer())); |
420 |
| - try!(arguments.expect_comma()); |
421 |
| - blue = clamp_i32(try!(arguments.expect_integer())); |
422 | 466 | }
|
423 |
| - Token::Percentage(ref v) => { |
424 |
| - red = clamp_f32(v.unit_value); |
425 |
| - try!(arguments.expect_comma()); |
426 |
| - green = clamp_f32(try!(arguments.expect_percentage())); |
| 467 | + blue = clamp_i32(try!(arguments.expect_number()) as i32); |
| 468 | + } |
| 469 | + Token::Percentage(ref v) => { |
| 470 | + red = clamp_f32(v.unit_value); |
| 471 | + green = clamp_f32(match try!(arguments.next()) { |
| 472 | + Token::Percentage(ref v) => v.unit_value, |
| 473 | + Token::Comma => { |
| 474 | + uses_commas = true; |
| 475 | + try!(arguments.expect_percentage()) |
| 476 | + } |
| 477 | + _ => return Err(()) |
| 478 | + }); |
| 479 | + if uses_commas { |
427 | 480 | try!(arguments.expect_comma());
|
428 |
| - blue = clamp_f32(try!(arguments.expect_percentage())); |
429 | 481 | }
|
430 |
| - _ => return Err(()) |
431 |
| - }; |
432 |
| - } else { |
433 |
| - let hue_degrees = try!(arguments.expect_number()); |
434 |
| - // Subtract an integer before rounding, to avoid some rounding errors: |
435 |
| - let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor(); |
436 |
| - let hue = hue_normalized_degrees / 360.; |
437 |
| - // Saturation and lightness are clamped to 0% ... 100% |
438 |
| - // https://drafts.csswg.org/css-color/#the-hsl-notation |
439 |
| - try!(arguments.expect_comma()); |
440 |
| - let saturation = try!(arguments.expect_percentage()).max(0.).min(1.); |
441 |
| - try!(arguments.expect_comma()); |
442 |
| - let lightness = try!(arguments.expect_percentage()).max(0.).min(1.); |
443 |
| - |
444 |
| - // https://drafts.csswg.org/css-color/#hsl-color |
445 |
| - // except with h pre-multiplied by 3, to avoid some rounding errors. |
446 |
| - fn hue_to_rgb(m1: f32, m2: f32, mut h3: f32) -> f32 { |
447 |
| - if h3 < 0. { h3 += 3. } |
448 |
| - if h3 > 3. { h3 -= 3. } |
449 |
| - |
450 |
| - if h3 * 2. < 1. { m1 + (m2 - m1) * h3 * 2. } |
451 |
| - else if h3 * 2. < 3. { m2 } |
452 |
| - else if h3 < 2. { m1 + (m2 - m1) * (2. - h3) * 2. } |
453 |
| - else { m1 } |
| 482 | + blue = clamp_f32(try!(arguments.expect_percentage())); |
454 | 483 | }
|
455 |
| - let m2 = if lightness <= 0.5 { lightness * (saturation + 1.) } |
456 |
| - else { lightness + saturation - lightness * saturation }; |
457 |
| - let m1 = lightness * 2. - m2; |
458 |
| - let hue_times_3 = hue * 3.; |
459 |
| - red = clamp_f32(hue_to_rgb(m1, m2, hue_times_3 + 1.)); |
460 |
| - green = clamp_f32(hue_to_rgb(m1, m2, hue_times_3)); |
461 |
| - blue = clamp_f32(hue_to_rgb(m1, m2, hue_times_3 - 1.)); |
462 |
| - } |
| 484 | + _ => return Err(()) |
| 485 | + }; |
| 486 | + return Ok((red, green, blue, uses_commas)); |
| 487 | +} |
463 | 488 |
|
464 |
| - let alpha = if has_alpha { |
465 |
| - try!(arguments.expect_comma()); |
466 |
| - clamp_f32(try!(arguments.expect_number())) |
467 |
| - } else { |
468 |
| - 255 |
| 489 | +#[inline] |
| 490 | +fn parse_rgb_components_hsl(arguments: &mut Parser) -> Result<(u8, u8, u8, bool), ()> { |
| 491 | + let mut uses_commas = false; |
| 492 | + // Hue given as an angle |
| 493 | + // https://drafts.csswg.org/css-values/#angles |
| 494 | + let hue_degrees = match try!(arguments.next()) { |
| 495 | + Token::Number(NumericValue { value: v, .. }) => v, |
| 496 | + Token::Dimension(NumericValue { value: v, .. }, unit) => { |
| 497 | + match_ignore_ascii_case! { &*unit, |
| 498 | + "deg" => v, |
| 499 | + "grad" => v * 360. / 400., |
| 500 | + "rad" => v * 360. / (2. * PI), |
| 501 | + "turn" => v * 360., |
| 502 | + _ => return Err(()) |
| 503 | + } |
| 504 | + } |
| 505 | + _ => return Err(()) |
469 | 506 | };
|
470 |
| - try!(arguments.expect_exhausted()); |
471 |
| - rgba(red, green, blue, alpha) |
| 507 | + // Subtract an integer before rounding, to avoid some rounding errors: |
| 508 | + let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor(); |
| 509 | + let hue = hue_normalized_degrees / 360.; |
| 510 | + |
| 511 | + // Saturation and lightness are clamped to 0% ... 100% |
| 512 | + // https://drafts.csswg.org/css-color/#the-hsl-notation |
| 513 | + let saturation = match try!(arguments.next()) { |
| 514 | + Token::Percentage(ref v) => v.unit_value, |
| 515 | + Token::Comma => { |
| 516 | + uses_commas = true; |
| 517 | + try!(arguments.expect_percentage()) |
| 518 | + } |
| 519 | + _ => return Err(()) |
| 520 | + }; |
| 521 | + let saturation = saturation.max(0.).min(1.); |
| 522 | + |
| 523 | + if uses_commas { |
| 524 | + try!(arguments.expect_comma()); |
| 525 | + } |
| 526 | + |
| 527 | + let lightness = try!(arguments.expect_percentage()); |
| 528 | + let lightness = lightness.max(0.).min(1.); |
| 529 | + |
| 530 | + // https://drafts.csswg.org/css-color/#hsl-color |
| 531 | + // except with h pre-multiplied by 3, to avoid some rounding errors. |
| 532 | + fn hue_to_rgb(m1: f32, m2: f32, mut h3: f32) -> f32 { |
| 533 | + if h3 < 0. { h3 += 3. } |
| 534 | + if h3 > 3. { h3 -= 3. } |
| 535 | + |
| 536 | + if h3 * 2. < 1. { m1 + (m2 - m1) * h3 * 2. } |
| 537 | + else if h3 * 2. < 3. { m2 } |
| 538 | + else if h3 < 2. { m1 + (m2 - m1) * (2. - h3) * 2. } |
| 539 | + else { m1 } |
| 540 | + } |
| 541 | + let m2 = if lightness <= 0.5 { lightness * (saturation + 1.) } |
| 542 | + else { lightness + saturation - lightness * saturation }; |
| 543 | + let m1 = lightness * 2. - m2; |
| 544 | + let hue_times_3 = hue * 3.; |
| 545 | + let red = clamp_f32(hue_to_rgb(m1, m2, hue_times_3 + 1.)); |
| 546 | + let green = clamp_f32(hue_to_rgb(m1, m2, hue_times_3)); |
| 547 | + let blue = clamp_f32(hue_to_rgb(m1, m2, hue_times_3 - 1.)); |
| 548 | + return Ok((red, green, blue, uses_commas)); |
472 | 549 | }
|
0 commit comments