diff options
author | Michael Drake <tlsa@netsurf-browser.org> | 2021-03-04 21:22:06 +0000 |
---|---|---|
committer | Michael Drake <tlsa@netsurf-browser.org> | 2021-05-19 14:40:54 +0100 |
commit | 6cd205329373efe5a1629518c2875724cc79dce3 (patch) | |
tree | e9598f4e0ae907eacf43c391fb4b54a79059627f /src/select/unit.c | |
parent | 0cf10a040aea028c7dc81cf353da7a7af5331076 (diff) | |
download | libcss-6cd205329373efe5a1629518c2875724cc79dce3.tar.gz libcss-6cd205329373efe5a1629518c2875724cc79dce3.tar.bz2 |
Units: Add support for length unit conversion to libcss.
Currently only used for unit conversion.
Diffstat (limited to 'src/select/unit.c')
-rw-r--r-- | src/select/unit.c | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/src/select/unit.c b/src/select/unit.c new file mode 100644 index 0000000..f9ecf83 --- /dev/null +++ b/src/select/unit.c @@ -0,0 +1,498 @@ +/* + * This file is part of LibCSS + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * + * Copyright 2021 Michael Drake <tlsa@netsurf-browser.org> + */ + +#include <libcss/stylesheet.h> + +#include "utils/utils.h" + +#include "propget.h" +#include "unit.h" + +/** + * Map viewport-relative length units to either vh or vw. + * + * Non-viewport-relative units are unchanged. + * + * \param[in] style Reference style. + * \param[in] viewport_height Viewport height in px. + * \param[in] viewport_width Viewport width in px. + * \param[in] unit Unit to map. + * \return the mapped unit. + */ +static inline css_unit css_unit__map_viewport_units( + const css_computed_style *style, + const css_fixed viewport_height, + const css_fixed viewport_width, + const css_unit unit) +{ + switch (unit) { + case CSS_UNIT_VI: + return (style != NULL && get_writing_mode(style) != + CSS_WRITING_MODE_HORIZONTAL_TB) ? + CSS_UNIT_VH : CSS_UNIT_VW; + + case CSS_UNIT_VB: + return (style != NULL && get_writing_mode(style) != + CSS_WRITING_MODE_HORIZONTAL_TB) ? + CSS_UNIT_VW : CSS_UNIT_VH; + + case CSS_UNIT_VMIN: + return (viewport_height < viewport_width) ? + CSS_UNIT_VH : CSS_UNIT_VW; + + case CSS_UNIT_VMAX: + return (viewport_height > viewport_width) ? + CSS_UNIT_VH : CSS_UNIT_VW; + + default: + return unit; + } +} + +/** + * Convert an absolute length to points (pt). + * + * \param[in] style Style to get font-size from, or NULL. + * \param[in] viewport_height Client viewport height. + * \param[in] viewport_width Client viewport width. + * \param[in] length Length to convert. + * \param[in] unit Current unit of length. + * \return length in points (pt). + */ +static inline css_fixed css_unit__absolute_len2pt( + const css_computed_style *style, + const css_fixed viewport_height, + const css_fixed viewport_width, + const css_fixed length, + const css_unit unit) +{ + /* Length must not be relative */ + assert(unit != CSS_UNIT_EM && + unit != CSS_UNIT_EX && + unit != CSS_UNIT_CH && + unit != CSS_UNIT_REM); + + switch (css_unit__map_viewport_units(style, + viewport_height, + viewport_width, + unit)) { + case CSS_UNIT_PX: + return FDIV(FMUL(length, F_72), F_96); + + case CSS_UNIT_IN: + return FMUL(length, F_72); + + case CSS_UNIT_CM: + return FMUL(length, FDIV(F_72, FLTTOFIX(2.54))); + + case CSS_UNIT_MM: + return FMUL(length, FDIV(F_72, FLTTOFIX(25.4))); + + case CSS_UNIT_Q: + return FMUL(length, FDIV(F_72, FLTTOFIX(101.6))); + + case CSS_UNIT_PT: + return length; + + case CSS_UNIT_PC: + return FMUL(length, INTTOFIX(12)); + + case CSS_UNIT_VH: + return FDIV(FMUL(FDIV(FMUL(length, viewport_height), F_100), + F_72), F_96); + + case CSS_UNIT_VW: + return FDIV(FMUL(FDIV(FMUL(length, viewport_width), F_100), + F_72), F_96); + + default: + return 0; + } +} + +/* Exported function, documented in unit.h. */ +css_fixed css_unit_font_size_len2pt( + const css_unit_len_ctx *ctx, + const css_fixed length, + const css_unit unit) +{ + return css_unit__absolute_len2pt( + ctx->ref_style, + ctx->viewport_height, + ctx->viewport_width, + length, + unit); +} + +/** + * Get font size from a style in CSS pixels. + * + * The style should have font size in absolute units. + * + * \param[in] style Style to get font-size from, or NULL. + * \param[in] font_size_default Client font size for NULL style. + * \param[in] font_size_minimum Client minimum font size clamp. + * \param[in] viewport_height Client viewport height. + * \param[in] viewport_width Client viewport width. + * \return font-size in CSS pixels. + */ +static inline css_fixed css_unit__font_size_px( + const css_computed_style *style, + const css_fixed font_size_default, + const css_fixed font_size_minimum, + const css_fixed viewport_height, + const css_fixed viewport_width) +{ + css_fixed font_length = 0; + css_unit font_unit = CSS_UNIT_PT; + + if (style == NULL) { + return font_size_default; + } + + get_font_size(style, &font_length, &font_unit); + + if (font_unit != CSS_UNIT_PX) { + font_length = css_unit__absolute_len2pt(style, + viewport_height, + viewport_width, + font_length, + font_unit); + + /* Convert from pt to CSS pixels.*/ + font_length = FDIV(FMUL(font_length, F_96), F_72); + } + + /* Clamp to configured minimum */ + if (font_length < font_size_minimum) { + font_length = font_size_minimum; + } + + return font_length; +} + +/** + * Get the number of CSS pixels for a given unit. + * + * \param[in] measure Client callback for font measuring. + * \param[in] ref_style Reference style. (Element or parent, or NULL). + * \param[in] root_style Root element style or NULL. + * \param[in] font_size_default Client default font size in CSS pixels. + * \param[in] font_size_minimum Client minimum font size in CSS pixels. + * \param[in] viewport_height Viewport height in CSS pixels. + * \param[in] viewport_width Viewport width in CSS pixels. + * \param[in] unit The unit to convert from. + * \param[in] pw Client private word for measure callback. + * \return Number of CSS pixels equivalent to the given unit. + */ +static inline css_fixed css_unit__px_per_unit( + const css_unit_len_measure measure, + const css_computed_style *ref_style, + const css_computed_style *root_style, + const css_fixed font_size_default, + const css_fixed font_size_minimum, + const css_fixed viewport_height, + const css_fixed viewport_width, + const css_unit unit, + void *pw) +{ + switch (css_unit__map_viewport_units( + ref_style, + viewport_height, + viewport_width, + unit)) { + case CSS_UNIT_EM: + return css_unit__font_size_px( + ref_style, + font_size_default, + font_size_minimum, + viewport_height, + viewport_width); + + case CSS_UNIT_EX: + if (measure != NULL) { + return measure(pw, ref_style, CSS_UNIT_EX); + } + return FMUL(css_unit__font_size_px( + ref_style, + font_size_default, + font_size_minimum, + viewport_height, + viewport_width), FLTTOFIX(0.6)); + + case CSS_UNIT_CH: + if (measure != NULL) { + return measure(pw, ref_style, CSS_UNIT_CH); + } + return FMUL(css_unit__font_size_px( + ref_style, + font_size_default, + font_size_minimum, + viewport_height, + viewport_width), FLTTOFIX(0.4)); + + case CSS_UNIT_PX: + return F_1; + + case CSS_UNIT_IN: + return F_96; + + case CSS_UNIT_CM: + return FDIV(F_96, FLTTOFIX(2.54)); + + case CSS_UNIT_MM: + return FDIV(F_96, FLTTOFIX(25.4)); + + case CSS_UNIT_Q: + return FDIV(F_96, FLTTOFIX(101.6)); + + case CSS_UNIT_PT: + return FDIV(F_96, F_72); + + case CSS_UNIT_PC: + return FDIV(F_96, INTTOFIX(6)); + + case CSS_UNIT_REM: + return css_unit__font_size_px( + root_style, + font_size_default, + font_size_minimum, + viewport_height, + viewport_width); + + case CSS_UNIT_VH: + return FDIV(viewport_width, F_100); + + case CSS_UNIT_VW: + return FDIV(viewport_height, F_100); + + default: + return 0; + } +} + +/* Exported function, documented in unit.h. */ +css_fixed css_unit_len2px_mq( + const css_media *media, + const css_fixed length, + const css_unit unit) +{ + /* In the media query context there is no reference or root element + * style, so these are hardcoded to NULL. */ + css_fixed px_per_unit = css_unit__px_per_unit( + NULL, + NULL, + NULL, + media->client_font_size, + INTTOFIX(0), + media->height, + media->width, + unit, + NULL); + + /* Ensure we round px_per_unit to the nearest whole number of pixels: + * the use of FIXTOINT() below will truncate. */ + px_per_unit += F_0_5; + + /* Calculate total number of pixels */ + return FMUL(length, TRUNCATEFIX(px_per_unit)); +} + +/* Exported function, documented in unit.h. */ +css_fixed css_unit_len2css_px( + const css_unit_len_ctx *ctx, + const css_fixed length, + const css_unit unit) +{ + css_fixed px_per_unit = css_unit__px_per_unit( + ctx->measure, + ctx->ref_style, + ctx->root_style, + ctx->font_size_default, + ctx->font_size_minimum, + ctx->viewport_height, + ctx->viewport_width, + unit, + ctx->pw); + + /* Ensure we round px_per_unit to the nearest whole number of pixels: + * the use of FIXTOINT() below will truncate. */ + px_per_unit += F_0_5; + + /* Calculate total number of pixels */ + return FMUL(length, TRUNCATEFIX(px_per_unit)); +} + +/* Exported function, documented in unit.h. */ +css_fixed css_unit_len2device_px( + const css_unit_len_ctx *ctx, + const css_fixed length, + const css_unit unit) +{ + css_fixed px_per_unit = css_unit__px_per_unit( + ctx->measure, + ctx->ref_style, + ctx->root_style, + ctx->font_size_default, + ctx->font_size_minimum, + ctx->viewport_height, + ctx->viewport_width, + unit, + ctx->pw); + + px_per_unit = css_unit_css2device_px(px_per_unit, ctx->device_dpi); + + /* Ensure we round px_per_unit to the nearest whole number of pixels: + * the use of FIXTOINT() below will truncate. */ + px_per_unit += F_0_5; + + /* Calculate total number of pixels */ + return FMUL(length, TRUNCATEFIX(px_per_unit)); +} + +/** + * Get font size from a computed style. + * + * The computed style will have font-size with an absolute unit. + * If no computed style is given, the client default font-size will be returned. + * + * \param[in] ctx Length unit conversion context. + * \param[in] style The style to get the font-size for, or NULL. + * \return The font size in absolute units. + */ +static inline css_hint_length css_unit__get_font_size( + const css_unit_len_ctx *ctx, + const css_computed_style *style) +{ + css_hint_length size; + + if (style == NULL) { + size.value = ctx->font_size_default; + size.unit = CSS_UNIT_PX; + } else { + enum css_font_size_e status = get_font_size( + style, &size.value, &size.unit); + + UNUSED(status); + + assert(status == CSS_FONT_SIZE_DIMENSION); + assert(size.unit != CSS_UNIT_EM); + assert(size.unit != CSS_UNIT_EX); + assert(size.unit != CSS_UNIT_PCT); + } + + return size; +} + +/* Exported function, documented in unit.h. */ +css_error css_unit_compute_absolute_font_size( + const css_unit_len_ctx *ctx, + css_hint *size) +{ + css_hint_length ref_len; + + assert(size->status != CSS_FONT_SIZE_INHERIT); + + switch (size->status) { + case CSS_FONT_SIZE_XX_SMALL: /* Fall-through. */ + case CSS_FONT_SIZE_X_SMALL: /* Fall-through. */ + case CSS_FONT_SIZE_SMALL: /* Fall-through. */ + case CSS_FONT_SIZE_MEDIUM: /* Fall-through. */ + case CSS_FONT_SIZE_LARGE: /* Fall-through. */ + case CSS_FONT_SIZE_X_LARGE: /* Fall-through. */ + case CSS_FONT_SIZE_XX_LARGE: + { + static const css_fixed factors[CSS_FONT_SIZE_XX_LARGE] = { + [CSS_FONT_SIZE_XX_SMALL - 1] = FLTTOFIX(0.5625), + [CSS_FONT_SIZE_X_SMALL - 1] = FLTTOFIX(0.6250), + [CSS_FONT_SIZE_SMALL - 1] = FLTTOFIX(0.8125), + [CSS_FONT_SIZE_MEDIUM - 1] = FLTTOFIX(1.0000), + [CSS_FONT_SIZE_LARGE - 1] = FLTTOFIX(1.1250), + [CSS_FONT_SIZE_X_LARGE - 1] = FLTTOFIX(1.5000), + [CSS_FONT_SIZE_XX_LARGE - 1] = FLTTOFIX(2.0000), + }; + assert(CSS_FONT_SIZE_INHERIT == 0); + assert(CSS_FONT_SIZE_XX_SMALL == 1); + + size->data.length.value = FMUL(factors[size->status - 1], + ctx->font_size_default); + size->data.length.unit = CSS_UNIT_PX; + size->status = CSS_FONT_SIZE_DIMENSION; + break; + } + case CSS_FONT_SIZE_LARGER: + ref_len = css_unit__get_font_size(ctx, ctx->ref_style); + size->data.length.value = FMUL(ref_len.value, FLTTOFIX(1.2)); + size->data.length.unit = ref_len.unit; + size->status = CSS_FONT_SIZE_DIMENSION; + break; + + case CSS_FONT_SIZE_SMALLER: + ref_len = css_unit__get_font_size(ctx, ctx->ref_style); + size->data.length.value = FDIV(ref_len.value, FLTTOFIX(1.2)); + size->data.length.unit = ref_len.unit; + size->status = CSS_FONT_SIZE_DIMENSION; + break; + + case CSS_FONT_SIZE_DIMENSION: + /* Convert any relative units to absolute. */ + switch (size->data.length.unit) { + case CSS_UNIT_PCT: + ref_len = css_unit__get_font_size(ctx, ctx->ref_style); + size->data.length.value = FDIV( + FMUL(size->data.length.value, + ref_len.value), INTTOFIX(100)); + size->data.length.unit = ref_len.unit; + break; + + case CSS_UNIT_EM: /* Fall-through */ + case CSS_UNIT_EX: /* Fall-through */ + case CSS_UNIT_CH: + /* Parent relative units. */ + ref_len = css_unit__get_font_size(ctx, ctx->ref_style); + + size->data.length.unit = ref_len.unit; + size->data.length.value = FMUL(size->data.length.value, + ref_len.value); + + switch (size->data.length.unit) { + case CSS_UNIT_EX: + size->data.length.value = FMUL( + size->data.length.value, + FLTTOFIX(0.6)); + break; + + case CSS_UNIT_CH: + size->data.length.value = FMUL( + size->data.length.value, + FLTTOFIX(0.4)); + break; + + default: + break; + } + break; + + case CSS_UNIT_REM: + /* Root element relative units. */ + ref_len = css_unit__get_font_size(ctx, ctx->root_style); + + size->data.length.unit = ref_len.unit; + size->data.length.value = FMUL(size->data.length.value, + ref_len.value); + break; + + default: + break; + } + default: + break; + } + + return CSS_OK; +} |