bowser/src/render/pipeline.py

302 lines
10 KiB
Python
Raw Normal View History

"""Render pipeline - coordinates layout and painting."""
import skia
from typing import Optional, Callable
from ..parser.html import Element
from ..layout.document import DocumentLayout
from ..layout.embed import ImageLayout
from .fonts import get_font
from .paint import DisplayList, DrawImage
class RenderPipeline:
"""Coordinates layout calculation and rendering to a Skia canvas."""
def __init__(self):
# Layout cache
self._layout: Optional[DocumentLayout] = None
self._layout_width = 0
self._layout_doc_id = None
self._layout_base_url = None
# Paint cache
self._text_paint: Optional[skia.Paint] = None
self._display_list: Optional[DisplayList] = None
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
# Base URL for resolving relative paths
self.base_url: Optional[str] = None
# Debug mode
self.debug_mode = False
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
# Async image loading
self.async_images = True # Enable async image loading by default
self._on_needs_redraw: Optional[Callable[[], None]] = None
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
def set_redraw_callback(self, callback: Callable[[], None]):
"""Set a callback to be called when async images finish loading."""
self._on_needs_redraw = callback
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
# Also set on ImageLayout class for global notification
def on_image_loaded():
# Invalidate layout cache so positions are recalculated with actual image sizes
self.invalidate()
if self._on_needs_redraw:
self._on_needs_redraw()
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
ImageLayout._on_any_image_loaded = on_image_loaded
def layout(self, document: Element, width: int) -> DocumentLayout:
"""
Calculate layout for the document.
Returns the DocumentLayout with all positioned elements.
"""
doc_id = id(document)
# Check cache (also invalidate if base_url changed)
if (self._layout_doc_id == doc_id and
self._layout_width == width and
self._layout_base_url == self.base_url and
self._layout is not None):
return self._layout
# Build new layout with base_url for resolving image paths
self._layout = DocumentLayout(
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
document,
base_url=self.base_url,
async_images=self.async_images
)
self._layout.layout(width)
self._layout_doc_id = doc_id
self._layout_width = width
self._layout_base_url = self.base_url
return self._layout
def render(self, canvas: skia.Canvas, document: Element,
width: int, height: int, scroll_y: float = 0):
"""
Render the document to the canvas.
Args:
canvas: Skia canvas to draw on
document: DOM document root
width: Viewport width
height: Viewport height
scroll_y: Vertical scroll offset
"""
# Get or update layout
layout = self.layout(document, width)
if not layout.lines and not layout.images:
return
# Apply scroll transform
canvas.save()
canvas.translate(0, -scroll_y)
# Get paint
if self._text_paint is None:
self._text_paint = skia.Paint()
self._text_paint.setAntiAlias(True)
self._text_paint.setColor(skia.ColorBLACK)
# Render visible content
visible_top = scroll_y - 50
visible_bottom = scroll_y + height + 50
# Render visible lines
for line in layout.lines:
baseline_y = line.y + line.font_size
if baseline_y < visible_top or line.y > visible_bottom:
continue
font = get_font(line.font_size, getattr(line, "font_family", ""), text=line.text)
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
# Use line color if specified (for links), otherwise black
paint = skia.Paint()
paint.setAntiAlias(True)
if line.color:
paint.setColor(self._parse_color(line.color))
else:
paint.setColor(skia.ColorBLACK)
canvas.drawString(line.text, line.x, baseline_y, font, paint)
# Draw underline for links
if line.href:
underline_paint = skia.Paint()
underline_paint.setColor(paint.getColor())
underline_paint.setStyle(skia.Paint.kStroke_Style)
underline_paint.setStrokeWidth(1)
underline_y = baseline_y + 2
canvas.drawLine(line.x, underline_y, line.x + line.width, underline_y, underline_paint)
# Render visible images (both loaded and placeholder)
for layout_image in layout.images:
image_bottom = layout_image.y + layout_image.height
if image_bottom < visible_top or layout_image.y > visible_bottom:
continue
image_layout = layout_image.image_layout
# Use image_layout dimensions directly for accurate sizing after async load
img_width = image_layout.width if image_layout.width > 0 else layout_image.width
img_height = image_layout.height if image_layout.height > 0 else layout_image.height
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
# Always create DrawImage command - it handles None images as placeholders
draw_cmd = DrawImage(
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
layout_image.x,
layout_image.y,
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
img_width,
img_height,
image_layout.image, # May be None, DrawImage handles this
image_layout.alt_text
)
draw_cmd.execute(canvas)
# Draw debug overlays if enabled
if self.debug_mode:
self._render_debug_overlays(canvas, layout)
canvas.restore()
def _render_debug_overlays(self, canvas: skia.Canvas, layout: DocumentLayout):
"""Render debug bounding boxes for layout blocks."""
# Color scheme for different block types
colors = {
"block": (255, 0, 0, 60), # Red
"inline": (0, 0, 255, 60), # Blue
"list-item": (0, 255, 0, 60), # Green
"text": (255, 255, 0, 60), # Yellow
}
border_colors = {
"block": (255, 0, 0, 180),
"inline": (0, 0, 255, 180),
"list-item": (0, 255, 0, 180),
"text": (255, 255, 0, 180),
}
for block in layout.blocks:
block_type = block.block_type
# Calculate block bounds from lines
if not block.lines:
continue
x = block.x - 5
y = block.y - block.lines[0].font_size if block.lines else block.y
w = block.width + 10
h = block.height + 5
# Fill
fill_paint = skia.Paint()
c = colors.get(block_type, colors["block"])
fill_paint.setColor(skia.Color(*c))
fill_paint.setStyle(skia.Paint.kFill_Style)
rect = skia.Rect.MakeLTRB(x, y, x + w, y + h)
canvas.drawRect(rect, fill_paint)
# Border
border_paint = skia.Paint()
bc = border_colors.get(block_type, border_colors["block"])
border_paint.setColor(skia.Color(*bc))
border_paint.setStyle(skia.Paint.kStroke_Style)
border_paint.setStrokeWidth(1)
canvas.drawRect(rect, border_paint)
def get_text_layout(self) -> list:
"""
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
Get the text layout for text selection and link hit testing.
Returns list of line info dicts with char_positions and href.
"""
if self._layout is None:
return []
result = []
for line in self._layout.lines:
result.append({
"text": line.text,
"x": line.x,
"y": line.y,
"width": line.width,
"height": line.height,
"font_size": line.font_size,
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
"char_positions": line.char_positions,
"href": getattr(line, "href", None)
})
return result
def get_document_height(self) -> float:
"""Get the total document height for scrolling."""
if self._layout is None:
return 0
return self._layout.height
def invalidate(self):
"""Invalidate the layout cache, forcing recalculation."""
self._layout = None
self._layout_doc_id = None
self._display_list = None
This commit introduces several enhancements to the browser rendering engine. Key changes include the ability to handle link clicks, improved link detection, and enhanced color parsing for proper rendering of styled links. The following modifications are included: - **Link Detection and Navigation**: Added functionality to detect if a mouse click falls within a link area. If a link is clicked, the browser now navigates to the corresponding URL while logging the action. This also includes handling relative URLs based on the current page context. - **Line Layout Enhancements**: The `LayoutLine` class now includes optional attributes for color and href, allowing links to maintain their designated colors in the rendered output. - **Color Parsing**: Implemented a new `_parse_color` method in the `RenderPipeline` class to convert various color formats (hex and named colors) to Skia-compatible values. This ensures that default link colors are correctly applied and that extremely light colors are rendered as black for visibility. - **Rendering Links**: During the rendering process, links in the text layout are now rendered with their specified colors, and an underline is drawn under links to indicate interactivity. - **Document Layout Updates**: The document parsing system has been updated to extract link information correctly while preserving text hierarchy. - **Tests**: A comprehensive suite of tests has been added, including tests for link parsing, layout characteristics, styling application, and default color handling for links.
2026-01-13 12:06:20 +00:00
def _parse_color(self, color_str: str) -> int:
"""Parse a CSS color string to a Skia color value.
Supports:
- Hex colors: #rgb, #rrggbb
- Named colors (limited set)
Note: Very light colors (like white) that would be invisible on
our white background are converted to black.
"""
if not color_str:
return skia.ColorBLACK
color_str = color_str.strip().lower()
# Named colors
named_colors = {
"black": skia.ColorBLACK,
"white": skia.ColorBLACK, # White is invisible on white bg, use black
"red": skia.ColorRED,
"green": skia.ColorGREEN,
"blue": skia.ColorBLUE,
"yellow": skia.ColorYELLOW,
"cyan": skia.ColorCYAN,
"magenta": skia.ColorMAGENTA,
"gray": skia.ColorGRAY,
"grey": skia.ColorGRAY,
}
if color_str in named_colors:
return named_colors[color_str]
# Hex colors
if color_str.startswith("#"):
hex_str = color_str[1:]
try:
if len(hex_str) == 3:
# #rgb -> #rrggbb
r = int(hex_str[0] * 2, 16)
g = int(hex_str[1] * 2, 16)
b = int(hex_str[2] * 2, 16)
elif len(hex_str) == 6:
r = int(hex_str[0:2], 16)
g = int(hex_str[2:4], 16)
b = int(hex_str[4:6], 16)
else:
return skia.ColorBLACK
# Check if color is too light (would be invisible on white)
# Use relative luminance approximation
if r > 240 and g > 240 and b > 240:
return skia.ColorBLACK
return skia.Color(r, g, b, 255)
except ValueError:
pass
# Fallback to black
return skia.ColorBLACK