@@ -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,185 @@ 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+ break ;
814+ default :
815+ return false; /* unexpected token type */
816+ }
817+
818+ /* Normalise hue to the range [0, 360) */
819+ while (hue < 0 )
820+ hue += F_360 ;
821+ while (hue >= F_360 )
822+ hue -= F_360 ;
823+
824+ consumeWhitespace (vector , ctx );
825+
826+ /* whiteness */
827+ token = parserutils_vector_iterate (vector , ctx );
828+ if (token == NULL )
829+ return false;
830+
831+ if ((token -> type != CSS_TOKEN_PERCENTAGE ) &&
832+ (token -> type != CSS_TOKEN_NUMBER )) {
833+ return false;
834+ }
835+
836+ white = css__number_from_lwc_string (token -> idata , false, & consumed );
837+ if (consumed != lwc_string_length (token -> idata )) {
838+ /* failed to consume the whole string as a number */
839+ return false;
840+ }
841+
842+ /* Normalise whiteness to the range [0, 100] */
843+ if (white < INTTOFIX (0 ))
844+ white = INTTOFIX (0 );
845+ else if (white > INTTOFIX (100 ))
846+ white = INTTOFIX (100 );
847+
848+ consumeWhitespace (vector , ctx );
849+
850+ /* blackness */
851+ token = parserutils_vector_iterate (vector , ctx );
852+ if (token == NULL )
853+ return false;
854+
855+ if ((token -> type != CSS_TOKEN_PERCENTAGE ) &&
856+ (token -> type != CSS_TOKEN_NUMBER )) {
857+ return false;
858+ }
859+
860+ black = css__number_from_lwc_string (token -> idata , false, & consumed );
861+ if (consumed != lwc_string_length (token -> idata )) {
862+ /* failed to consume the whole string as a number */
863+ return false;
864+ }
865+
866+ /* Normalise blackness to the range [0, 100] */
867+ if (black < INTTOFIX (0 ))
868+ black = INTTOFIX (0 );
869+ else if (black > INTTOFIX (100 ))
870+ black = INTTOFIX (100 );
871+
872+ consumeWhitespace (vector , ctx );
873+
874+ token = parserutils_vector_iterate (vector , ctx );
875+
876+ if (tokenIsChar (token , '/' )) {
877+ consumeWhitespace (vector , ctx );
878+
879+ token = parserutils_vector_iterate (vector , ctx );
880+ if ((token == NULL ) ||
881+ (token -> type != CSS_TOKEN_NUMBER &&
882+ token -> type != CSS_TOKEN_PERCENTAGE )) {
883+ return false;
884+ }
885+
886+ alpha = css__number_from_lwc_string (token -> idata , false, & consumed );
887+ if (consumed != lwc_string_length (token -> idata )) {
888+ /* failed to consume the whole string as a number */
889+ return false;
890+ }
891+
892+ if (token -> type != CSS_TOKEN_NUMBER ) {
893+ alpha = FIXTOINT (FMUL (alpha , F_255 ));
894+ } else {
895+ alpha = FIXTOINT (FDIV (FMUL (alpha , F_255 ), F_100 ));
896+ }
897+
898+ consumeWhitespace (vector , ctx );
899+
900+ token = parserutils_vector_iterate (vector , ctx );
901+ }
902+
903+ if (!tokenIsChar (token , ')' ))
904+ return false;
905+
906+ /* have a valid HSV entry, convert to RGB */
907+ HWB_to_RGB (hue , white , black , & r , & g , & b );
908+
909+ /* apply alpha */
910+ if (alpha > 255 ) {
911+ a = 255 ;
912+ } else if (alpha < 0 ) {
913+ a = 0 ;
914+ } else {
915+ a = alpha ;
916+ }
917+
918+ * result = ((unsigned )a << 24 ) | (r << 16 ) | (g << 8 ) | b ;
919+ return true;
920+ }
921+
709922/**
710923 * Parse a colour specifier
711924 *
@@ -819,6 +1032,12 @@ css_error css__parse_colour_specifier(css_language *c,
8191032 if (!parse_hsl (vector , ctx , result )) {
8201033 goto invalid ;
8211034 }
1035+ } else if ((lwc_string_caseless_isequal (
1036+ token -> idata , c -> strings [HWB ],
1037+ & match ) == lwc_error_ok && match )) {
1038+ if (!parse_hwb (vector , ctx , result )) {
1039+ goto invalid ;
1040+ }
8221041 } else {
8231042 goto invalid ;
8241043 }
0 commit comments