/** * Dump a numeric value to the stream in a textual form. * * \param stream Stream to write to * \param val Value to write */ void dump_css_number(FILE *stream, css_fixed val) { if (INTTOFIX(FIXTOINT(val)) == val) fprintf(stream, "%d", FIXTOINT(val)); else dump_css_fixed(stream, val); }
/* exported function documented in render/font_internal.h */ void font_plot_style_from_css(const css_computed_style *css, plot_font_style_t *fstyle) { lwc_string **families; css_fixed length = 0; css_unit unit = CSS_UNIT_PX; css_color col; fstyle->family = plot_font_generic_family( css_computed_font_family(css, &families)); css_computed_font_size(css, &length, &unit); fstyle->size = FIXTOINT(FMUL(nscss_len2pt(length, unit), INTTOFIX(FONT_SIZE_SCALE))); /* Clamp font size to configured minimum */ if (fstyle->size < (nsoption_int(font_min_size) * FONT_SIZE_SCALE) / 10) fstyle->size = (nsoption_int(font_min_size) * FONT_SIZE_SCALE) / 10; fstyle->weight = plot_font_weight(css_computed_font_weight(css)); fstyle->flags = plot_font_flags(css_computed_font_style(css), css_computed_font_variant(css)); css_computed_color(css, &col); fstyle->foreground = nscss_color_to_ns(col); fstyle->background = 0; }
/** * Dump a fixed point value to the stream in a textual form. * * \param stream Stream to write to * \param f Value to write */ void dump_css_fixed(FILE *stream, css_fixed f) { #define NSCSS_ABS(x) (uint32_t)((x) < 0 ? -(x) : (x)) uint32_t uintpart = FIXTOINT(NSCSS_ABS(f)); /* + 500 to ensure round to nearest (division will truncate) */ uint32_t fracpart = ((NSCSS_ABS(f) & 0x3ff) * 1000 + 500) / (1 << 10); #undef NSCSS_ABS fprintf(stream, "%s%d.%03d", f < 0 ? "-" : "", uintpart, fracpart); }
static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec) { int selected_face = FB_FACE_DEFAULT; switch (fstyle->family) { case PLOT_FONT_FAMILY_SERIF: if (fstyle->weight >= BOLD_WEIGHT) { selected_face = FB_FACE_SERIF_BOLD; } else { selected_face = FB_FACE_SERIF; } break; case PLOT_FONT_FAMILY_MONOSPACE: if (fstyle->weight >= BOLD_WEIGHT) { selected_face = FB_FACE_MONOSPACE_BOLD; } else { selected_face = FB_FACE_MONOSPACE; } break; case PLOT_FONT_FAMILY_CURSIVE: selected_face = FB_FACE_CURSIVE; break; case PLOT_FONT_FAMILY_FANTASY: selected_face = FB_FACE_FANTASY; break; case PLOT_FONT_FAMILY_SANS_SERIF: default: if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE)) { if (fstyle->weight >= BOLD_WEIGHT) { selected_face = FB_FACE_SANS_SERIF_ITALIC_BOLD; } else { selected_face = FB_FACE_SANS_SERIF_ITALIC; } } else { if (fstyle->weight >= BOLD_WEIGHT) { selected_face = FB_FACE_SANS_SERIF_BOLD; } else { selected_face = FB_FACE_SANS_SERIF; } } } srec->face_id = (FTC_FaceID)fb_faces[selected_face]; srec->width = srec->height = (fstyle->size * 64) / FONT_SIZE_SCALE; srec->pixel = 0; srec->x_res = srec->y_res = FIXTOINT(nscss_screen_dpi); }
/** * Parse a colour specifier * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param value Pointer to location to receive value * \param result Pointer to location to receive result (AARRGGBB) * \return CSS_OK on success, * CSS_INVALID if the input is invalid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_colour_specifier(css_language *c, const parserutils_vector *vector, int *ctx, uint16_t *value, uint32_t *result) { int orig_ctx = *ctx; const css_token *token; bool match; css_error error; consumeWhitespace(vector, ctx); /* IDENT(<colour name>) | * HASH(rgb | rrggbb) | * FUNCTION(rgb) [ [ NUMBER | PERCENTAGE ] ',' ] {3} ')' * FUNCTION(rgba) [ [ NUMBER | PERCENTAGE ] ',' ] {4} ')' * FUNCTION(hsl) ANGLE ',' PERCENTAGE ',' PERCENTAGE ')' * FUNCTION(hsla) ANGLE ',' PERCENTAGE ',' PERCENTAGE ',' NUMBER ')' * * For quirks, NUMBER | DIMENSION | IDENT, too * I.E. "123456" -> NUMBER, "1234f0" -> DIMENSION, "f00000" -> IDENT */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_HASH && token->type != CSS_TOKEN_FUNCTION)) { if (c->sheet->quirks_allowed == false || token == NULL || (token->type != CSS_TOKEN_NUMBER && token->type != CSS_TOKEN_DIMENSION)) goto invalid; } if (token->type == CSS_TOKEN_IDENT) { if ((lwc_string_caseless_isequal( token->idata, c->strings[TRANSPARENT], &match) == lwc_error_ok && match)) { *value = COLOR_TRANSPARENT; *result = 0; /* black transparent */ return CSS_OK; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[CURRENTCOLOR], &match) == lwc_error_ok && match)) { *value = COLOR_CURRENT_COLOR; *result = 0; return CSS_OK; } error = css__parse_named_colour(c, token->idata, result); if (error != CSS_OK && c->sheet->quirks_allowed) { error = css__parse_hash_colour(token->idata, result); if (error == CSS_OK) c->sheet->quirks_used = true; } if (error != CSS_OK) goto invalid; } else if (token->type == CSS_TOKEN_HASH) { error = css__parse_hash_colour(token->idata, result); if (error != CSS_OK) goto invalid; } else if (c->sheet->quirks_allowed && token->type == CSS_TOKEN_NUMBER) { error = css__parse_hash_colour(token->idata, result); if (error == CSS_OK) c->sheet->quirks_used = true; else goto invalid; } else if (c->sheet->quirks_allowed && token->type == CSS_TOKEN_DIMENSION) { error = css__parse_hash_colour(token->idata, result); if (error == CSS_OK) c->sheet->quirks_used = true; else goto invalid; } else if (token->type == CSS_TOKEN_FUNCTION) { uint8_t r = 0, g = 0, b = 0, a = 0xff; int colour_channels = 0; if ((lwc_string_caseless_isequal( token->idata, c->strings[RGB], &match) == lwc_error_ok && match)) { colour_channels = 3; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[RGBA], &match) == lwc_error_ok && match)) { colour_channels = 4; } if ((lwc_string_caseless_isequal( token->idata, c->strings[HSL], &match) == lwc_error_ok && match)) { colour_channels = 5; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[HSLA], &match) == lwc_error_ok && match)) { colour_channels = 6; } if (colour_channels == 3 || colour_channels == 4) { int i; css_token_type valid = CSS_TOKEN_NUMBER; uint8_t *components[4] = { &r, &g, &b, &a }; for (i = 0; i < colour_channels; i++) { uint8_t *component; css_fixed num; size_t consumed = 0; int32_t intval; bool int_only; component = components[i]; consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL || (token->type != CSS_TOKEN_NUMBER && token->type != CSS_TOKEN_PERCENTAGE)) goto invalid; if (i == 0) valid = token->type; else if (i < 3 && token->type != valid) goto invalid; /* The alpha channel may be a float */ if (i < 3) int_only = (valid == CSS_TOKEN_NUMBER); else int_only = false; num = css__number_from_lwc_string(token->idata, int_only, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; if (valid == CSS_TOKEN_NUMBER) { if (i == 3) { /* alpha channel */ intval = FIXTOINT( FMUL(num, F_255)); } else { /* colour channels */ intval = FIXTOINT(num); } } else { intval = FIXTOINT( FDIV(FMUL(num, F_255), F_100)); } if (intval > 255) *component = 255; else if (intval < 0) *component = 0; else *component = intval; parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL) goto invalid; if (i != (colour_channels - 1) && tokenIsChar(token, ',')) { parserutils_vector_iterate(vector, ctx); } else if (i == (colour_channels - 1) && tokenIsChar(token, ')')) { parserutils_vector_iterate(vector, ctx); } else { goto invalid; } } } else if (colour_channels == 5 || colour_channels == 6) { /* hue - saturation - lightness */ size_t consumed = 0; css_fixed hue, sat, lit; int32_t alpha = 255; /* hue is a number without a unit representing an * angle (0-360) degrees */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER)) goto invalid; hue = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ /* Normalise hue to the range [0, 360) */ while (hue < 0) hue += F_360; while (hue >= F_360) hue -= F_360; consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if (!tokenIsChar(token, ',')) goto invalid; /* saturation */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE)) goto invalid; sat = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ /* Normalise saturation to the range [0, 100] */ if (sat < INTTOFIX(0)) sat = INTTOFIX(0); else if (sat > INTTOFIX(100)) sat = INTTOFIX(100); consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if (!tokenIsChar(token, ',')) goto invalid; /* lightness */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE)) goto invalid; lit = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ /* Normalise lightness to the range [0, 100] */ if (lit < INTTOFIX(0)) lit = INTTOFIX(0); else if (lit > INTTOFIX(100)) lit = INTTOFIX(100); consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if (colour_channels == 6) { /* alpha */ if (!tokenIsChar(token, ',')) goto invalid; consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER)) goto invalid; alpha = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ alpha = FIXTOINT(FMUL(alpha, F_255)); consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); } if (!tokenIsChar(token, ')')) goto invalid; /* have a valid HSV entry, convert to RGB */ HSL_to_RGB(hue, sat, lit, &r, &g, &b); /* apply alpha */ if (alpha > 255) a = 255; else if (alpha < 0) a = 0; else a = alpha; } else { goto invalid; } *result = (a << 24) | (r << 16) | (g << 8) | b; } *value = COLOR_SET; return CSS_OK; invalid: *ctx = orig_ctx; return CSS_INVALID; }
/** * Convert Hue Saturation Lightness value to RGB. * * \param hue Hue in degrees 0..360 * \param sat Saturation value in percent 0..100 * \param lit Lightness value in percent 0..100 * \param r red component * \param g green component * \param b blue component */ static void HSL_to_RGB(css_fixed hue, css_fixed sat, css_fixed lit, uint8_t *r, uint8_t *g, uint8_t *b) { css_fixed min_rgb, max_rgb, chroma; css_fixed relative_hue, scaled_hue, mid1, mid2; int sextant; #define ORGB(R, G, B) \ *r = FIXTOINT(FDIV(FMUL((R), F_255), F_100)); \ *g = FIXTOINT(FDIV(FMUL((G), F_255), F_100)); \ *b = FIXTOINT(FDIV(FMUL((B), F_255), F_100)) /* If saturation is zero there is no hue and r = g = b = lit */ if (sat == INTTOFIX(0)) { ORGB(lit, lit, lit); return; } /* Compute max(r,g,b) */ if (lit <= INTTOFIX(50)) { max_rgb = FDIV(FMUL(lit, FADD(sat, F_100)), F_100); } else { max_rgb = FDIV(FSUB(FMUL(FADD(lit, sat), F_100), FMUL(lit, sat)), F_100); } /* Compute min(r,g,b) */ min_rgb = FSUB(FMUL(lit, INTTOFIX(2)), max_rgb); /* We know that the value of at least one of the components is * max(r,g,b) and that the value of at least one of the other * components is min(r,g,b). * * We can determine which components have these values by * considering which the sextant of the hexcone the hue lies * in: * * Sextant: max(r,g,b): min(r,g,b): * * 0 r b * 1 g b * 2 g r * 3 b r * 4 b g * 5 r g * * Thus, we need only compute the value of the third component */ /* Chroma is the difference between min and max */ chroma = FSUB(max_rgb, min_rgb); /* Compute which sextant the hue lies in (truncates result) */ hue = FDIV(FMUL(hue, INTTOFIX(6)), F_360); sextant = FIXTOINT(hue); /* Compute offset of hue from start of sextant */ relative_hue = FSUB(hue, INTTOFIX(sextant)); /* Scale offset by chroma */ scaled_hue = FMUL(relative_hue, chroma); /* Compute potential values of the third colour component */ mid1 = FADD(min_rgb, scaled_hue); mid2 = FSUB(max_rgb, scaled_hue); /* Populate result */ switch (sextant) { case 0: ORGB(max_rgb, mid1, min_rgb); break; case 1: ORGB(mid2, max_rgb, min_rgb); break; case 2: ORGB(min_rgb, max_rgb, mid1); break; case 3: ORGB(min_rgb, mid2, max_rgb); break; case 4: ORGB(mid1, min_rgb, max_rgb); break; case 5: ORGB(max_rgb, min_rgb, mid2); break; } #undef ORGB }
static nserror html_object_callback(hlcache_handle *object, const hlcache_event *event, void *pw) { struct content_html_object *o = pw; html_content *c = (html_content *) o->parent; int x, y; struct box *box; assert(c->base.status != CONTENT_STATUS_ERROR); box = o->box; if (box == NULL && event->type != CONTENT_MSG_ERROR) { return NSERROR_OK; } switch (event->type) { case CONTENT_MSG_LOADING: if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL) content_open(object, c->bw, &c->base, box->object_params); break; case CONTENT_MSG_READY: if (content_can_reformat(object)) { /* TODO: avoid knowledge of box internals here */ content_reformat(object, false, box->max_width != UNKNOWN_MAX_WIDTH ? box->width : 0, box->max_width != UNKNOWN_MAX_WIDTH ? box->height : 0); /* Adjust parent content for new object size */ html_object_done(box, object, o->background); if (c->base.status == CONTENT_STATUS_READY || c->base.status == CONTENT_STATUS_DONE) content__reformat(&c->base, false, c->base.available_width, c->base.height); } break; case CONTENT_MSG_DONE: c->base.active--; LOG("%d fetches active", c->base.active); html_object_done(box, object, o->background); if (c->base.status != CONTENT_STATUS_LOADING && box->flags & REPLACE_DIM) { union content_msg_data data; if (!box_visible(box)) break; box_coords(box, &x, &y); data.redraw.x = x + box->padding[LEFT]; data.redraw.y = y + box->padding[TOP]; data.redraw.width = box->width; data.redraw.height = box->height; data.redraw.full_redraw = true; content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } break; case CONTENT_MSG_ERROR: hlcache_handle_release(object); o->content = NULL; if (box != NULL) { c->base.active--; LOG("%d fetches active", c->base.active); content_add_error(&c->base, "?", 0); html_object_failed(box, c, o->background); } break; case CONTENT_MSG_REDRAW: if (c->base.status != CONTENT_STATUS_LOADING) { union content_msg_data data = event->data; if (!box_visible(box)) break; box_coords(box, &x, &y); if (object == box->background) { /* Redraw request is for background */ css_fixed hpos = 0, vpos = 0; css_unit hunit = CSS_UNIT_PX; css_unit vunit = CSS_UNIT_PX; int width = box->padding[LEFT] + box->width + box->padding[RIGHT]; int height = box->padding[TOP] + box->height + box->padding[BOTTOM]; int t, h, l, w; /* Need to know background-position */ css_computed_background_position(box->style, &hpos, &hunit, &vpos, &vunit); w = content_get_width(box->background); if (hunit == CSS_UNIT_PCT) { l = (width - w) * hpos / INTTOFIX(100); } else { l = FIXTOINT(nscss_len2px(hpos, hunit, box->style)); } h = content_get_height(box->background); if (vunit == CSS_UNIT_PCT) { t = (height - h) * vpos / INTTOFIX(100); } else { t = FIXTOINT(nscss_len2px(vpos, vunit, box->style)); } /* Redraw area depends on background-repeat */ switch (css_computed_background_repeat( box->style)) { case CSS_BACKGROUND_REPEAT_REPEAT: data.redraw.x = 0; data.redraw.y = 0; data.redraw.width = box->width; data.redraw.height = box->height; break; case CSS_BACKGROUND_REPEAT_REPEAT_X: data.redraw.x = 0; data.redraw.y += t; data.redraw.width = box->width; break; case CSS_BACKGROUND_REPEAT_REPEAT_Y: data.redraw.x += l; data.redraw.y = 0; data.redraw.height = box->height; break; case CSS_BACKGROUND_REPEAT_NO_REPEAT: data.redraw.x += l; data.redraw.y += t; break; default: break; } data.redraw.object_width = box->width; data.redraw.object_height = box->height; /* Add offset to box */ data.redraw.x += x; data.redraw.y += y; data.redraw.object_x += x; data.redraw.object_y += y; content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); break; } else { /* Non-background case */ if (hlcache_handle_get_content(object) == event->data.redraw.object) { int w = content_get_width(object); int h = content_get_height(object); if (w != 0) { data.redraw.x = data.redraw.x * box->width / w; data.redraw.width = data.redraw.width * box->width / w; } if (h != 0) { data.redraw.y = data.redraw.y * box->height / h; data.redraw.height = data.redraw.height * box->height / h; } data.redraw.object_width = box->width; data.redraw.object_height = box->height; } data.redraw.x += x + box->padding[LEFT]; data.redraw.y += y + box->padding[TOP]; data.redraw.object_x += x + box->padding[LEFT]; data.redraw.object_y += y + box->padding[TOP]; } content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } break; case CONTENT_MSG_REFRESH: if (content_get_type(object) == CONTENT_HTML) { /* only for HTML objects */ guit->misc->schedule(event->data.delay * 1000, html_object_refresh, o); } break; case CONTENT_MSG_LINK: /* Don't care about favicons that aren't on top level content */ break; case CONTENT_MSG_GETCTX: *(event->data.jscontext) = NULL; break; case CONTENT_MSG_SCROLL: if (box->scroll_x != NULL) scrollbar_set(box->scroll_x, event->data.scroll.x0, false); if (box->scroll_y != NULL) scrollbar_set(box->scroll_y, event->data.scroll.y0, false); break; case CONTENT_MSG_DRAGSAVE: { union content_msg_data msg_data; if (event->data.dragsave.content == NULL) msg_data.dragsave.content = object; else msg_data.dragsave.content = event->data.dragsave.content; content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data); } break; case CONTENT_MSG_SAVELINK: case CONTENT_MSG_POINTER: case CONTENT_MSG_SELECTMENU: case CONTENT_MSG_GADGETCLICK: /* These messages are for browser window layer. * we're not interested, so pass them on. */ content_broadcast(&c->base, event->type, event->data); break; case CONTENT_MSG_CARET: { union html_focus_owner focus_owner; focus_owner.content = box; switch (event->data.caret.type) { case CONTENT_CARET_REMOVE: case CONTENT_CARET_HIDE: html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner, true, 0, 0, 0, NULL); break; case CONTENT_CARET_SET_POS: html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner, false, event->data.caret.pos.x, event->data.caret.pos.y, event->data.caret.pos.height, event->data.caret.pos.clip); break; } } break; case CONTENT_MSG_DRAG: { html_drag_type drag_type = HTML_DRAG_NONE; union html_drag_owner drag_owner; drag_owner.content = box; switch (event->data.drag.type) { case CONTENT_DRAG_NONE: drag_type = HTML_DRAG_NONE; drag_owner.no_owner = true; break; case CONTENT_DRAG_SCROLL: drag_type = HTML_DRAG_CONTENT_SCROLL; break; case CONTENT_DRAG_SELECTION: drag_type = HTML_DRAG_CONTENT_SELECTION; break; } html_set_drag_type(c, drag_type, drag_owner, event->data.drag.rect); } break; case CONTENT_MSG_SELECTION: { html_selection_type sel_type; union html_selection_owner sel_owner; if (event->data.selection.selection) { sel_type = HTML_SELECTION_CONTENT; sel_owner.content = box; } else { sel_type = HTML_SELECTION_NONE; sel_owner.none = true; } html_set_selection(c, sel_type, sel_owner, event->data.selection.read_only); } break; default: break; } if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 && (event->type == CONTENT_MSG_LOADING || event->type == CONTENT_MSG_DONE || event->type == CONTENT_MSG_ERROR)) { /* all objects have arrived */ content__reformat(&c->base, false, c->base.available_width, c->base.height); content_set_done(&c->base); } /* If 1) the configuration option to reflow pages while objects are * fetched is set * 2) an object is newly fetched & converted, * 3) the box's dimensions need to change due to being replaced * 4) the object's parent HTML is ready for reformat, * 5) the time since the previous reformat is more than the * configured minimum time between reformats * then reformat the page to display newly fetched objects */ else if (nsoption_bool(incremental_reflow) && event->type == CONTENT_MSG_DONE && box != NULL && !(box->flags & REPLACE_DIM) && (c->base.status == CONTENT_STATUS_READY || c->base.status == CONTENT_STATUS_DONE) && (wallclock() > c->base.reformat_time)) { content__reformat(&c->base, false, c->base.available_width, c->base.height); } return NSERROR_OK; }