@@ -375,6 +375,40 @@ static void HSL_to_RGB(
375375 * b = FIXTOINT (FDIV (bf , F_100 ));
376376}
377377
378+ /**
379+ * Convert Hue Saturation Lightness value to RGB.
380+ *
381+ * \param hue Hue in degrees 0..360
382+ * \param white Whiteness value in percent 0..100
383+ * \param black Blackness value in percent 0..100
384+ * \param r red component
385+ * \param g green component
386+ * \param b blue component
387+ */
388+ static void HWB_to_RGB (
389+ css_fixed hue , css_fixed white , css_fixed black ,
390+ uint8_t * r , uint8_t * g , uint8_t * b )
391+ {
392+ if (FADD (white , black ) >= F_100 ) {
393+ css_fixed grey = (FDIV (FMUL (white , F_255 ), FADD (white , black )));
394+ uint8_t grey_int = FIXTOINT (grey );
395+
396+ * r = grey_int ;
397+ * g = grey_int ;
398+ * b = grey_int ;
399+ } else {
400+ css_fixed rf , gf , bf ; // 0..25500
401+ css_fixed val = FSUB (F_100 , FADD (white , black )); // 0..100
402+
403+ HSL_to_RGB_fixed (hue , INTTOFIX (100 ), INTTOFIX (50 ),
404+ & rf , & gf , & bf );
405+
406+ * r = FIXTOINT (FDIV (FADD (FMUL (FDIV (rf , F_100 ), val ), FMUL (white , F_255 )), F_100 ));
407+ * g = FIXTOINT (FDIV (FADD (FMUL (FDIV (gf , F_100 ), val ), FMUL (white , F_255 )), F_100 ));
408+ * b = FIXTOINT (FDIV (FADD (FMUL (FDIV (bf , F_100 ), val ), FMUL (white , F_255 )), F_100 ));
409+ }
410+ }
411+
378412/**
379413 * Parse a RGB(A) colour specifier
380414 *
@@ -706,6 +740,186 @@ static bool parse_hsl(
706740 return true;
707741}
708742
743+ /**
744+ * Parse a HWB colour specifier (hue, whiteness, blackness)
745+ *
746+ * It's up to the caller to reset the ctx if this fails.
747+ *
748+ * \param vector Vector of tokens to process
749+ * \param ctx Pointer to vector iteration context
750+ * \param result Pointer to location to receive result (AARRGGBB)
751+ * \return true on success, false on error.
752+ */
753+ static bool parse_hwb (
754+ const parserutils_vector * vector ,
755+ int32_t * ctx ,
756+ uint32_t * result )
757+ {
758+ const css_token * token ;
759+ size_t consumed = 0 ;
760+ css_fixed hue , white , black ;
761+ int32_t alpha = 255 ;
762+ css_error error ;
763+ uint8_t r = 0 , g = 0 , b = 0 , a = 0xff ;
764+
765+ /* hue is a number without a unit representing an
766+ * angle (0-360) degrees, or it can be an angle dimension.
767+ */
768+ consumeWhitespace (vector , ctx );
769+
770+ token = parserutils_vector_iterate (vector , ctx );
771+ if ((token == NULL ) ||
772+ (token -> type != CSS_TOKEN_NUMBER &&
773+ token -> type != CSS_TOKEN_DIMENSION )) {
774+ return false;
775+ }
776+
777+ hue = css__number_from_lwc_string (token -> idata , false, & consumed );
778+
779+ switch (token -> type ) {
780+ case CSS_TOKEN_NUMBER :
781+ if (consumed != lwc_string_length (token -> idata )) {
782+ return false; /* failed to consume the whole string as a number */
783+ }
784+ break ;
785+ case CSS_TOKEN_DIMENSION : {
786+ size_t len = lwc_string_length (token -> idata );
787+ const char * data = lwc_string_data (token -> idata );
788+ uint32_t unit = UNIT_DEG ;
789+
790+ error = css__parse_unit_keyword (
791+ data + consumed ,
792+ len - consumed ,
793+ & unit );
794+ if (error != CSS_OK ) {
795+ return false;
796+ }
797+
798+ switch (unit ) {
799+ case UNIT_DEG :
800+ break ;
801+ case UNIT_RAD :
802+ hue = FDIV (FMUL (hue , F_180 ), F_PI );
803+ break ;
804+ case UNIT_GRAD :
805+ hue = FMUL (hue , FLTTOFIX (0.9 ));
806+ break ;
807+ case UNIT_TURN :
808+ hue = FMUL (hue , F_360 );
809+ break ;
810+ default :
811+ return false;
812+ }
813+ }
814+ break ;
815+ default :
816+ return false; /* unexpected token type */
817+ }
818+
819+ /* Normalise hue to the range [0, 360) */
820+ while (hue < 0 )
821+ hue += F_360 ;
822+ while (hue >= F_360 )
823+ hue -= F_360 ;
824+
825+ consumeWhitespace (vector , ctx );
826+
827+ /* whiteness */
828+ token = parserutils_vector_iterate (vector , ctx );
829+ if (token == NULL )
830+ return false;
831+
832+ if ((token -> type != CSS_TOKEN_PERCENTAGE ) &&
833+ (token -> type != CSS_TOKEN_NUMBER )) {
834+ return false;
835+ }
836+
837+ white = css__number_from_lwc_string (token -> idata , false, & consumed );
838+ if (consumed != lwc_string_length (token -> idata )) {
839+ /* failed to consume the whole string as a number */
840+ return false;
841+ }
842+
843+ /* Normalise whiteness to the range [0, 100] */
844+ if (white < INTTOFIX (0 ))
845+ white = INTTOFIX (0 );
846+ else if (white > INTTOFIX (100 ))
847+ white = INTTOFIX (100 );
848+
849+ consumeWhitespace (vector , ctx );
850+
851+ /* blackness */
852+ token = parserutils_vector_iterate (vector , ctx );
853+ if (token == NULL )
854+ return false;
855+
856+ if ((token -> type != CSS_TOKEN_PERCENTAGE ) &&
857+ (token -> type != CSS_TOKEN_NUMBER )) {
858+ return false;
859+ }
860+
861+ black = css__number_from_lwc_string (token -> idata , false, & consumed );
862+ if (consumed != lwc_string_length (token -> idata )) {
863+ /* failed to consume the whole string as a number */
864+ return false;
865+ }
866+
867+ /* Normalise blackness to the range [0, 100] */
868+ if (black < INTTOFIX (0 ))
869+ black = INTTOFIX (0 );
870+ else if (black > INTTOFIX (100 ))
871+ black = INTTOFIX (100 );
872+
873+ consumeWhitespace (vector , ctx );
874+
875+ token = parserutils_vector_iterate (vector , ctx );
876+
877+ if (tokenIsChar (token , '/' )) {
878+ consumeWhitespace (vector , ctx );
879+
880+ token = parserutils_vector_iterate (vector , ctx );
881+ if ((token == NULL ) ||
882+ (token -> type != CSS_TOKEN_NUMBER &&
883+ token -> type != CSS_TOKEN_PERCENTAGE )) {
884+ return false;
885+ }
886+
887+ alpha = css__number_from_lwc_string (token -> idata , false, & consumed );
888+ if (consumed != lwc_string_length (token -> idata )) {
889+ /* failed to consume the whole string as a number */
890+ return false;
891+ }
892+
893+ if (token -> type != CSS_TOKEN_NUMBER ) {
894+ alpha = FIXTOINT (FMUL (alpha , F_255 ));
895+ } else {
896+ alpha = FIXTOINT (FDIV (FMUL (alpha , F_255 ), F_100 ));
897+ }
898+
899+ consumeWhitespace (vector , ctx );
900+
901+ token = parserutils_vector_iterate (vector , ctx );
902+ }
903+
904+ if (!tokenIsChar (token , ')' ))
905+ return false;
906+
907+ /* have a valid HSV entry, convert to RGB */
908+ HWB_to_RGB (hue , white , black , & r , & g , & b );
909+
910+ /* apply alpha */
911+ if (alpha > 255 ) {
912+ a = 255 ;
913+ } else if (alpha < 0 ) {
914+ a = 0 ;
915+ } else {
916+ a = alpha ;
917+ }
918+
919+ * result = ((unsigned )a << 24 ) | (r << 16 ) | (g << 8 ) | b ;
920+ return true;
921+ }
922+
709923/**
710924 * Parse a colour specifier
711925 *
@@ -819,6 +1033,12 @@ css_error css__parse_colour_specifier(css_language *c,
8191033 if (!parse_hsl (vector , ctx , result )) {
8201034 goto invalid ;
8211035 }
1036+ } else if ((lwc_string_caseless_isequal (
1037+ token -> idata , c -> strings [HWB ],
1038+ & match ) == lwc_error_ok && match )) {
1039+ if (!parse_hwb (vector , ctx , result )) {
1040+ goto invalid ;
1041+ }
8221042 } else {
8231043 goto invalid ;
8241044 }
0 commit comments