String Drawing System

How guideXOS renders text on screen

Overview

guideXOS uses a custom bitmap font rendering system that draws text directly to the framebuffer. Unlike hardware-accelerated text rendering in modern operating systems, guideXOS implements pure software rendering with pixel-by-pixel drawing operations.

?? Key Features:
  • Variable-width bitmap fonts
  • Alpha transparency support
  • Automatic line wrapping
  • Newline character support
  • Text measurement before rendering

Font System Architecture

IFont Class

The IFont class in Kernel\Misc\IFont.cs handles all text rendering:

internal class IFont {
    private readonly Image image;        // PNG font texture
    private readonly string charset;     // Character mapping
    public int FontSize;                 // Size of each character (e.g., 16px)
    
    public IFont(Image _img, string _charset, int size) {
        image = _img;
        charset = _charset;
        FontSize = size;
    }
}

Font Structure

Component Description
Font Image PNG file containing all characters in a grid layout
Charset String mapping characters to positions (e.g., "ABC...xyz")
Font Size Width/height of each character cell in pixels
NumRow Number of characters per row in the font image

Font Loading Example

// Load a 128px font from PNG
IFont font = new IFont(
    new PNG(File.ReadAllBytes("Images/Yahei128.png")),
    @"!""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
    128  // Font size
);

// Used globally via WindowManager
WindowManager.font = font;

The Drawing Process

1. DrawString() Method

The main entry point for rendering text:

public void DrawString(int X, int Y, string Str, 
                       int LineLimit = -1, int HeightLimit = -1) {
    int w = 0, h = 0;  // Current cursor position
    
    for (int i = 0; i < Str.Length; i++) {
        // Skip leading spaces on new lines
        if (h != 0 && w == 0 && Str[i] == ' ') 
            continue;
        
        // Draw character and advance cursor
        w += DrawChar(Framebuffer.Graphics, X + w, Y + h, Str[i]);
        
        // Handle line wrapping
        if (w + FontSize > LineLimit && LineLimit != -1 || Str[i] == '\n') {
            w = 0;              // Reset to start of line
            h += FontSize;      // Move down one line
            
            if (HeightLimit != -1 && h >= HeightLimit)
                return;  // Stop if height exceeded
        }
    }
}

2. DrawChar() - Character Rendering

Renders a single character pixel-by-pixel:

public int DrawChar(Graphics g, int X, int Y, char Chr) {
    // Step 1: Find character in charset
    int index = charset.IndexOf(Chr);
    if (index == -1) {
        if (Chr == ' ') return FontSize / 2;  // Space is half width
        return 0;  // Unknown char = skip
    }
    
    // Step 2: Calculate position in font texture
    int baseX = 0, baseY = 0;
    for (int i = 0; i <= index; i++) {
        if ((i % NumRow) == 0 && i != 0) {
            baseX = 0;
            baseY += FontSize;
        }
        if (i != index)
            baseX += FontSize;
    }
    
    // Step 3: Draw character pixel-by-pixel
    for (int w = 0; w < FontSize; w++) {
        int counter = 0;
        for (int h = 0; h < FontSize; h++) {
            uint color = image.GetPixel(baseX + w, baseY + h);
            
            if (X != -1 && Y != -1)
                g.DrawPoint(X + w, Y + h, color, true);  // Alpha blend
            
            if ((color & 0xFF000000) == 0) counter++;
        }
        
        // Step 4: Variable-width - stop at empty column
        if (w > (FontSize / 3) && counter == FontSize) 
            return w;  // Return actual width used
    }
    
    return FontSize;  // Full width if no empty column
}

Rendering Pipeline

DrawString("Hello")
    ?
For each character 'H', 'e', 'l', 'l', 'o':
    ?
    1. Find character index in charset
    2. Calculate texture coordinates (baseX, baseY)
    3. Extract character bitmap from font image
    4. Draw pixel-by-pixel to framebuffer
    5. Apply alpha transparency
    6. Detect actual character width
    7. Advance cursor position
    ?
Return total width used

Usage Examples

Simple Text

// Draw text at position (100, 50)
WindowManager.font.DrawString(100, 50, "Hello World");

Text with Line Wrapping

// Wrap text at 300px width, limit to 2 lines
WindowManager.font.DrawString(
    100,                        // X position
    50,                         // Y position
    "This is a long text that will wrap",
    300,                        // Line width limit
    WindowManager.font.FontSize * 2  // Height limit (2 lines)
);

Centered Text

// Measure text width first
string text = "Centered Text";
int textWidth = WindowManager.font.MeasureString(text);

// Calculate center position
int x = (screenWidth / 2) - (textWidth / 2);
int y = (screenHeight / 2) - (WindowManager.font.FontSize / 2);

// Draw centered
WindowManager.font.DrawString(x, y, text);

Measuring Text (No Drawing)

// Calculate width without rendering
int width = WindowManager.font.MeasureString("Sample Text");

// Useful for:
// - Layout calculations
// - Button sizing
// - Text centering
// - Clipping detection

Multi-line Text with Newlines

// Newlines are automatically handled
WindowManager.font.DrawString(100, 50, 
    "Line 1\nLine 2\nLine 3");

Performance Characteristics

Rendering Speed

Operation Complexity Notes
DrawChar() O(FontSize²) Pixel-by-pixel loop (e.g., 16×16 = 256 pixels)
DrawString() O(n × FontSize²) n = string length
MeasureString() O(n × FontSize²) Same as drawing (no optimization)
Character lookup O(charset length) Linear search in charset string

Optimization Strategies

  • Variable-width fonts - Characters only use pixels they need
  • Early termination - Stop drawing when empty column detected
  • No memory allocation - All drawing uses stack variables
  • Direct framebuffer access - Bypasses abstraction layers
?? Performance Note:

Text rendering is CPU-intensive. A 16px font character requires ~256 pixel operations. Drawing a full screen of text (1920×1080) can take several milliseconds. Use StringPool to avoid repeated string allocations in render loops.


StringPool Integration

Why StringPool Matters

Since text rendering happens every frame in the GUI, creating new strings causes severe memory leaks. The StringPool system caches commonly used strings to eliminate allocations.

Problem Without StringPool

// ? BAD: Allocates new string every frame
void OnDraw() {
    int cpuUsage = 42;
    string text = cpuUsage.ToString() + "%";  // New allocation
    WindowManager.font.DrawString(100, 50, text);
    // Memory leak: 'text' is never freed
}

// Result: ~8 MB/minute leaked in Task Manager alone!

Solution With StringPool

// ? GOOD: Reuses cached string
void OnDraw() {
    int cpuUsage = 42;
    string text = StringPool.GetPercentage(cpuUsage);  // Cached "42%"
    WindowManager.font.DrawString(100, 50, text);
    // No memory leak: string is reused, never disposed
}

// Result: Zero allocations after warm-up

StringPool API

Method Usage Cache Size
GetPercentage(int) Returns "0%" to "100%" 101 entries
GetNumber(int) Returns "0" to "10,000" 10,001 entries
GetMemorySize(ulong) Returns "42 KB", "128 MB", "2 GB", etc. 2,000 entries (LRU)
GetTransferRate(int) Returns "42 KB/s", "5 MB/s", etc. Uses GetNumber internally
FormatUptime(ulong) Returns "12:34:56" format Composed from GetNumber

Best Practices

  • ? Use StringPool for any strings in render loops
  • ? Cache strings that update infrequently (update every second, not every frame)
  • ? Reuse pooled strings - never call Dispose() on them
  • ? Avoid ToString() in OnDraw() methods
  • ? Avoid string concatenation in high-frequency code
  • ? Avoid string.Format() - use StringPool methods instead
? Impact:

Using StringPool in TaskManager, Monitor, and PerformanceWidget reduced memory leak from 8.3 MB/minute to near zero. Free/Alloc ratio improved from 72% to 95%+.


Technical Details

Font File Format

guideXOS fonts are stored as PNG images with characters laid out in a grid:

  • Each character occupies a FontSize × FontSize cell
  • Characters are arranged left-to-right, top-to-bottom
  • Order matches the charset string parameter
  • Transparent pixels (alpha = 0) are not drawn
  • Colored pixels are alpha-blended with background

Available Fonts

Font File Size Usage
Yahei128.png 128px Lock screen, large text
Default system font 16px WindowManager.font (all GUI)

Limitations

  • ? No font scaling (must load multiple sizes)
  • ? No anti-aliasing (except alpha from PNG)
  • ? No kerning adjustments
  • ? No Unicode support (ASCII/extended ASCII only)
  • ? No right-to-left or vertical text
  • ? No hardware acceleration

Related Topics