From 6db5ad1b73c3a59390d4e060a3752a09ec75ec44 Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sun, 17 Oct 2004 21:05:12 +0000 Subject: [project @ 2004-10-17 21:05:12 by bursa] New target-independent implementation of html_redraw(), using plotters. svn path=/import/netsurf/; revision=1313 --- render/html_redraw.c | 770 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 770 insertions(+) create mode 100644 render/html_redraw.c (limited to 'render/html_redraw.c') diff --git a/render/html_redraw.c b/render/html_redraw.c new file mode 100644 index 000000000..8c7a4e274 --- /dev/null +++ b/render/html_redraw.c @@ -0,0 +1,770 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 James Bursa + */ + +/** \file + * Redraw of a CONTENT_HTML (implementation). + */ + +#include +#include +#include +#include "netsurf/utils/config.h" +#include "netsurf/content/content.h" +#include "netsurf/css/css.h" +#include "netsurf/desktop/plotters.h" +#include "netsurf/render/box.h" +#include "netsurf/render/font.h" +#include "netsurf/render/form.h" +#include "netsurf/render/layout.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/messages.h" +#include "netsurf/utils/utils.h" + + +static bool html_redraw_box(struct box *box, + int x, int y, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, colour current_background_color); +static bool html_redraw_borders(struct box *box, int x, int y, + int padding_width, int padding_height, float scale); +static colour html_redraw_darker(colour c); +static colour html_redraw_lighter(colour c); +static colour html_redraw_aa(colour c0, colour c1); +static bool html_redraw_checkbox(int x, int y, int width, int height, + bool selected); +static bool html_redraw_radio(int x, int y, int width, int height, + bool selected); +static bool html_redraw_file(int x, int y, int width, int height, + struct box *box, float scale, colour background_colour); +static bool html_redraw_background(int x, int y, + struct box *box, float scale, colour background_colour); +static bool html_redraw_scrollbars(struct box *box, float scale, + int x, int y, int padding_width, int padding_height); + +bool html_redraw_debug = false; + + +/** + * Draw a CONTENT_HTML using the current set of plotters (plot). + * + * \param c content of type CONTENT_HTML + * \param x coordinate for top-left of redraw + * \param y coordinate for top-left of redraw + * \param width available width (not used for HTML redraw) + * \param height available height (not used for HTML redraw) + * \param clip_x0 clip rectangle + * \param clip_y0 clip rectangle + * \param clip_x1 clip rectangle + * \param clip_y1 clip rectangle + * \param scale scale for redraw + * \param background_colour the background colour + * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. + */ + +bool html_redraw(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour) +{ + struct box *box; + + box = c->data.html.layout; + assert(box); + + /* clear to background colour */ + if (c->data.html.background_colour != TRANSPARENT) + background_colour = c->data.html.background_colour; + plot.clg(background_colour); + + return html_redraw_box(box, x, y, + clip_x0, clip_y0, clip_x1, clip_y1, + scale, background_colour); +} + + +/** + * Recursively draw a box. + * + * \param box box to draw + * \param x coordinate of parent box + * \param y coordinate of parent box + * \param clip_x0 clip rectangle + * \param clip_y0 clip rectangle + * \param clip_x1 clip rectangle + * \param clip_y1 clip rectangle + * \param scale scale for redraw + * \param current_background_color background colour under this box + * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. + */ + +bool html_redraw_box(struct box *box, + int x, int y, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, colour current_background_color) +{ + struct box *c; + int width, height; + int padding_left, padding_top, padding_width, padding_height; + int x0, y0, x1, y1; + int colour; + int x_scrolled, y_scrolled; + + x += box->x * scale; + y += box->y * scale; + width = box->width * scale; + height = box->height * scale; + padding_left = box->padding[LEFT] * scale; + padding_top = box->padding[TOP] * scale; + padding_width = (box->padding[LEFT] + box->width + + box->padding[RIGHT]) * scale; + padding_height = (box->padding[TOP] + box->height + + box->padding[BOTTOM]) * scale; + x_scrolled = x - box->scroll_x * scale; + y_scrolled = y - box->scroll_y * scale; + + /* calculate clip rectangle for this box */ + if (box->style && box->style->overflow != CSS_OVERFLOW_VISIBLE) { + x0 = x; + y0 = y; + x1 = x + padding_width; + y1 = y + padding_height; + } else { + x0 = x + box->descendant_x0 * scale; + y0 = y + box->descendant_y0 * scale; + x1 = x + box->descendant_x1 * scale + 1; + y1 = y + box->descendant_y1 * scale + 1; + } + + /* if visibility is hidden render children only */ + if (box->style && box->style->visibility == CSS_VISIBILITY_HIDDEN) { + for (c = box->children; c; c = c->next) + if (!html_redraw_box(c, x_scrolled, y_scrolled, + x0, y0, x1, y1, + scale, current_background_color)) + return false; + return true; + } + + /* dotted debug outlines */ + if (html_redraw_debug) { + if (!plot.rectangle(x, y, padding_width, padding_height, + 0x0000ff, true)) + return false; + if (!plot.rectangle(x + padding_left, y + padding_top, + width, height, 0xff0000, true)) + return false; + if (!plot.rectangle(x - (box->border[LEFT] + + box->margin[LEFT]) * scale, + y - (box->border[TOP] + + box->margin[TOP]) * scale, + padding_width + (box->border[LEFT] + + box->margin[LEFT] + box->border[RIGHT] + + box->margin[RIGHT]) * scale, + padding_height + (box->border[TOP] + + box->margin[TOP] + box->border[BOTTOM] + + box->margin[BOTTOM]) * scale, + 0x00ffff, true)) + return false; + } + + /* borders */ + if (box->style) + if (!html_redraw_borders(box, x, y, + padding_width, padding_height, + scale)) + return false; + + /* return if the box is completely outside the clip rectangle */ + if (clip_y1 < y0 || y1 < clip_y0 || clip_x1 < x0 || x1 < clip_x0) + return true; + + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + /* find intersection of clip rectangle and box */ + if (x0 < clip_x0) x0 = clip_x0; + if (y0 < clip_y0) y0 = clip_y0; + if (clip_x1 < x1) x1 = clip_x1; + if (clip_y1 < y1) y1 = clip_y1; + /* clip to it */ + if (!plot.clip(x0, y0, x1, y1)) + return false; + } else { + /* clip box unchanged */ + x0 = clip_x0; + y0 = clip_y0; + x1 = clip_x1; + y1 = clip_y1; + } + + /* background colour and image */ + if (box->style && box->type != BOX_BR && (box->type != BOX_INLINE || + box->style != box->parent->parent->style)) { + /* find intersection of clip box and padding box */ + int px0 = x < x0 ? x0 : x; + int py0 = y < y0 ? y0 : y; + int px1 = x + padding_width < x1 ? x + padding_width : x1; + int py1 = y + padding_height < y1 ? y + padding_height : y1; + + /* background colour */ + if (box->style->background_color != TRANSPARENT && + px0 < px1 && py0 < py1 && + /* don't redraw background if there is an + * image covering it */ + ((box->style->background_repeat != + CSS_BACKGROUND_REPEAT_REPEAT) || + (box->background->bitmap == NULL) || + /*(ro_gui_current_redraw_gui == NULL) || + (!ro_gui_current_redraw_gui-> + option.background_images) ||*/ + (!bitmap_test_opaque(box->background-> + bitmap)))) { + if (!plot.fill(px0, py0, px1, py1, + box->style->background_color)) + return false; + /* set current background color for font painting */ + current_background_color = box->style->background_color; + } + + if (box->background) { + /* clip to padding box for everything but the main window */ + if (box->parent) { + if (!plot.clip(px0, py0, px1, py1)) + return false; + } else { + if (!plot.clip(clip_x0, clip_y0, + clip_x1, clip_y1)) + return false; + } + + /* plot background image */ + if (!html_redraw_background(x, y, box, scale, + current_background_color)) + return false; + + /* restore previous graphics window */ + if (!plot.clip(x0, y0, x1, y1)) + return false; + } + + /* scrollbars */ + if (box->style->overflow == CSS_OVERFLOW_SCROLL) { + if (!html_redraw_scrollbars(box, scale, x, y, + padding_width, padding_height)) + return false; + } + + } + + if (box->object) { + if (!content_redraw(box->object, + x + padding_left, y + padding_top, + width, height, x0, y0, x1, y1, scale, + current_background_color)) + return false; + + } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { + if (!html_redraw_checkbox(x + padding_left, y + padding_top, + width, height, + box->gadget->selected)) + return false; + + } else if (box->gadget && box->gadget->type == GADGET_RADIO) { + if (!html_redraw_radio(x + padding_left, y + padding_top, + width, height, + box->gadget->selected)) + return false; + + } else if (box->gadget && box->gadget->type == GADGET_FILE) { + if (!html_redraw_file(x + padding_left, y + padding_top, + width, height, box, scale, + current_background_color)) + return false; + + } else if (box->text && box->font) { + /* antialias colour for under/overline */ + colour = html_redraw_aa(current_background_color, + /*print_text_black ? 0 :*/ box->style->color); + + if (box->style->text_decoration & + CSS_TEXT_DECORATION_UNDERLINE) { + if (!plot.line(x, + y + (int) (box->height * 0.9 * scale), + x + box->width * scale, + y + (int) (box->height * 0.9 * scale), + 0, colour, false, false)) + return false; + } + + if (box->style->text_decoration & + CSS_TEXT_DECORATION_OVERLINE) { + if (!plot.line(x, + y + (int) (box->height * 0.1 * scale), + x + box->width * scale, + y + (int) (box->height * 0.1 * scale), + 0, colour, false, false)) + return false; + } + + if (box->style->text_decoration & + CSS_TEXT_DECORATION_LINE_THROUGH) { + if (!plot.line(x, + y + (int) (box->height * 0.5 * scale), + x + box->width * scale, + y + (int) (box->height * 0.5 * scale), + 0, colour, false, false)) + return false; + } + + if (!plot.text(x, y + (int) (box->height * 0.75 * scale), + box->font, box->text, box->length, + current_background_color, + /*print_text_black ? 0 :*/ box->style->color)) + return false; + + } else { + for (c = box->children; c != 0; c = c->next) + if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) + if (!html_redraw_box(c, x_scrolled, y_scrolled, + x0, y0, x1, y1, scale, + current_background_color)) + return false; + + for (c = box->float_children; c != 0; c = c->next_float) + if (!html_redraw_box(c, x_scrolled, y_scrolled, + x0, y0, x1, y1, scale, + current_background_color)) + return false; + } + + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) + if (!plot.clip(clip_x0, clip_y0, clip_x1, clip_y1)) + return false; + + return true; +} + + +/** + * Draw borders for a box. + * + * \param box box to draw + * \param x coordinate of left padding edge + * \param y coordinate of top padding edge + * \param padding_width width of padding box + * \param padding_height height of padding box + * \param scale scale for redraw + * \return true if successful, false otherwise + */ + +bool html_redraw_borders(struct box *box, int x, int y, + int padding_width, int padding_height, float scale) +{ + unsigned int i; + int top = box->border[TOP] * scale; + int right = box->border[RIGHT] * scale; + int bottom = box->border[BOTTOM] * scale; + int left = box->border[LEFT] * scale; + int p[20] = { + x, y, + x - left, y - top, + x + padding_width + right, y - top, + x + padding_width, y, + x + padding_width, y + padding_height, + x + padding_width + right, y + padding_height + bottom, + x - left, y + padding_height + bottom, + x, y + padding_height, + x, y, + x - left, y - top + }; + int z[8]; + colour c; + bool dotted; + unsigned int light; + + assert(box->style); + + for (i = 0; i != 4; i++) { + if (box->border[i] == 0) + continue; + + c = box->style->border[i].color; + dotted = false; + light = i; + + switch (box->style->border[i].style) { + case CSS_BORDER_STYLE_DOTTED: + dotted = true; + case CSS_BORDER_STYLE_DASHED: + if (!plot.line((p[i * 4 + 0] + p[i * 4 + 2]) / 2, + (p[i * 4 + 1] + p[i * 4 + 3]) / 2, + (p[i * 4 + 4] + p[i * 4 + 6]) / 2, + (p[i * 4 + 5] + p[i * 4 + 7]) / 2, + box->border[i] * scale, + c, dotted, !dotted)) + return false; + continue; + + case CSS_BORDER_STYLE_SOLID: + break; + + case CSS_BORDER_STYLE_DOUBLE: + z[0] = p[i * 4 + 0]; + z[1] = p[i * 4 + 1]; + z[2] = (p[i * 4 + 0] * 2 + p[i * 4 + 2]) / 3; + z[3] = (p[i * 4 + 1] * 2 + p[i * 4 + 3]) / 3; + z[4] = (p[i * 4 + 6] * 2 + p[i * 4 + 4]) / 3; + z[5] = (p[i * 4 + 7] * 2 + p[i * 4 + 5]) / 3; + z[6] = p[i * 4 + 6]; + z[7] = p[i * 4 + 7]; + if (!plot.polygon(z, 4, c)) + return false; + z[0] = p[i * 4 + 2]; + z[1] = p[i * 4 + 3]; + z[2] = (p[i * 4 + 2] * 2 + p[i * 4 + 0]) / 3; + z[3] = (p[i * 4 + 3] * 2 + p[i * 4 + 1]) / 3; + z[4] = (p[i * 4 + 4] * 2 + p[i * 4 + 6]) / 3; + z[5] = (p[i * 4 + 5] * 2 + p[i * 4 + 7]) / 3; + z[6] = p[i * 4 + 4]; + z[7] = p[i * 4 + 5]; + if (!plot.polygon(z, 4, c)) + return false; + continue; + + case CSS_BORDER_STYLE_GROOVE: + light = 3 - light; + case CSS_BORDER_STYLE_RIDGE: + z[0] = p[i * 4 + 0]; + z[1] = p[i * 4 + 1]; + z[2] = (p[i * 4 + 0] + p[i * 4 + 2]) / 2; + z[3] = (p[i * 4 + 1] + p[i * 4 + 3]) / 2; + z[4] = (p[i * 4 + 6] + p[i * 4 + 4]) / 2; + z[5] = (p[i * 4 + 7] + p[i * 4 + 5]) / 2; + z[6] = p[i * 4 + 6]; + z[7] = p[i * 4 + 7]; + if (!plot.polygon(z, 4, + light == 0 || light == 1 ? + html_redraw_darker(c) : + html_redraw_lighter(c))) + return false; + z[0] = p[i * 4 + 2]; + z[1] = p[i * 4 + 3]; + z[6] = p[i * 4 + 4]; + z[7] = p[i * 4 + 5]; + if (!plot.polygon(z, 4, + light == 0 || light == 1 ? + html_redraw_lighter(c) : + html_redraw_darker(c))) + return false;; + continue; + + case CSS_BORDER_STYLE_INSET: + if (i == 0 || i == 3) + c = html_redraw_darker(c); + else + c = html_redraw_lighter(c); + break; + case CSS_BORDER_STYLE_OUTSET: + if (i == 0 || i == 3) + c = html_redraw_lighter(c); + else + c = html_redraw_darker(c); + break; + + default: + break; + } + + if (!plot.polygon(p + i * 4, 4, c)) + return false; + } + + return true; +} + + +/** + * Make a colour darker. + * + * \param c colour + * \return a darker shade of c + */ + +colour html_redraw_darker(colour c) +{ + int b = c >> 16, g = (c >> 8) & 0xff, r = c & 0xff; + b = b * 3 / 4; + g = g * 3 / 4; + r = r * 3 / 4; + return (b << 16) | (g << 8) | r; +} + + +/** + * Make a colour lighter. + * + * \param c colour + * \return a lighter shade of c + */ + +colour html_redraw_lighter(colour c) +{ + int b = 0xff - (c >> 16), g = 0xff - ((c >> 8) & 0xff), + r = 0xff - (c & 0xff); + b = b * 3 / 4; + g = g * 3 / 4; + r = r * 3 / 4; + return ((0xff - b) << 16) | ((0xff - g) << 8) | (0xff - r); +} + + +/** + * Mix two colours to produce a colour suitable for anti-aliasing. + * + * \param c0 first colour + * \param c1 second colour + * \return a colour half way between c0 and c1 + */ + +colour html_redraw_aa(colour c0, colour c1) +{ + return ((((c0 >> 16) + (c1 >> 16)) / 2) << 16) | + (((((c0 >> 8) & 0xff) + ((c1 >> 8) & 0xff)) / 2) << 8) | + ((((c0 & 0xff) + (c1 & 0xff)) / 2) << 0); +} + + +/** + * Plot a checkbox. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of checkbox + * \param height dimensions of checkbox + * \param selected the checkbox is selected + * \return true if successful, false otherwise + */ + +bool html_redraw_checkbox(int x, int y, int width, int height, + bool selected) +{ + int z = width * 0.15; + if (z == 0) + z = 1; + if (!plot.fill(x, y, x + width, y + height, 0x000000)) + return false; + if (!plot.fill(x + z, y + z, x + width - z, y + height - z, 0xffffff)) + return false; + if (selected) + if (!plot.fill(x + z + z, y + z + z, + x + width - z - z, y + height - z - z, + 0x0000ff)) + return false; + + return true; +} + + +/** + * Plot a radio icon. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of radio icon + * \param height dimensions of radio icon + * \param selected the radio icon is selected + * \return true if successful, false otherwise + */ + +bool html_redraw_radio(int x, int y, int width, int height, + bool selected) +{ + if (!plot.disc(x + width * 0.5, y + height * 0.5, + width * 0.5 - 1, 0x000000)) + return false; + if (!plot.disc(x + width * 0.5, y + height * 0.5, + width * 0.4 - 1, 0xffffff)) + return false; + if (selected) + if (!plot.disc(x + width * 0.5, y + height * 0.5, + width * 0.3 - 1, 0x0000ff)) + return false; + + return true; +} + + +/** + * Plot a file upload input. + * + * \param x left coordinate + * \param y top coordinate + * \param width dimensions of input + * \param height dimensions of input + * \param box box of input + * \param scale scale for redraw + * \param background_colour current background colour + * \return true if successful, false otherwise + */ + +bool html_redraw_file(int x, int y, int width, int height, + struct box *box, float scale, colour background_colour) +{ + int text_width; + const char *text; + size_t length; + + if (box->gadget->value) + text = box->gadget->value; + else + text = messages_get("Form_Drop"); + length = strlen(text); + + text_width = nsfont_width(box->font, text, length) * scale; + if (width < text_width + 8) + x = x + width - text_width - 4; + else + x = x + 4; + + return plot.text(x, y + height * 0.75, box->font, text, length, + background_colour, + /*print_text_black ? 0 :*/ box->style->color); +} + + +/** + * Plot background images. + * + * \param x coordinate of box + * \param y coordinate of box + * \param box box to draw background image of + * \param scale scale for redraw + * \param background_colour current background colour + * \return true if successful, false otherwise + */ + +bool html_redraw_background(int x, int y, + struct box *box, float scale, colour background_colour) +{ + int image_width, image_height; + bool repeat_x = false; + bool repeat_y = false; + + assert(box->background); + + /* only bitmaps handled currently */ + if (!box->background->bitmap) + return true; + + /* exit if background images aren't wanted */ + /*if (ro_gui_current_redraw_gui) + if (!ro_gui_current_redraw_gui->option.background_images) + return true; + else if (!option_background_images) + return true;*/ + + /* get the image dimensions for our positioning and scaling */ + image_width = box->background->width * scale; + image_height = box->background->height * scale; + + /* handle background-repeat */ + switch (box->style->background_repeat) { + case CSS_BACKGROUND_REPEAT_REPEAT: + repeat_x = repeat_y = true; + break; + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; + break; + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; + break; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + break; + default: + break; + } + + /* handle background-position */ + switch (box->style->background_position.horz.pos) { + case CSS_BACKGROUND_POSITION_PERCENT: + x += (box->padding[LEFT] + box->width + + box->padding[RIGHT] - image_width) * + box->style->background_position.horz. + value.percent / 100; + break; + case CSS_BACKGROUND_POSITION_LENGTH: + x += (int) (css_len2px(&box->style->background_position. + horz.value.length, box->style) * scale); + break; + default: + break; + } + + switch (box->style->background_position.vert.pos) { + case CSS_BACKGROUND_POSITION_PERCENT: + y += (box->padding[TOP] + box->height + + box->padding[BOTTOM] - image_height) * + box->style->background_position.vert. + value.percent / 100; + break; + case CSS_BACKGROUND_POSITION_LENGTH: + y -= (int) (css_len2px(&box->style->background_position. + vert.value.length, box->style) * scale); + break; + default: + break; + } + + /* and plot the image */ + return plot.bitmap_tile(x, y, image_width, image_height, + box->background->bitmap, + background_colour, + repeat_x, repeat_y); +} + + +/** + * Plot scrollbars for a scrolling box. + * + * \param box scrolling box + * \param scale scale for redraw + * \param x coordinate of box + * \param y coordinate of box + * \param padding_width width of padding box + * \param padding_height height of padding box + * \return true if successful, false otherwise + */ + +bool html_redraw_scrollbars(struct box *box, float scale, + int x, int y, int padding_width, int padding_height) +{ + int w = SCROLLBAR_WIDTH * scale; + + /* vertical scrollbar */ + if (box->descendant_y0 < box->descendant_y1) { + int bar_top = (float) padding_height * (float) box->scroll_y / + (float) (box->descendant_y1 - + box->descendant_y0); + int bar_height = (float) padding_height * (float) box->height / + (float) (box->descendant_y1 - + box->descendant_y0); + if (!plot.fill(x + padding_width - w, y, + x + padding_width, y + padding_height, + 0x777777)) + return false; + if (!plot.fill(x + padding_width - w + 4, y + bar_top, + x + padding_width - 4, y + bar_top + bar_height, + 0xbbbbbb)) + return false; + } + + return true; +} -- cgit v1.2.3