在控制台(framebuffer)中绘制字符的最快方法?

时间:2011-07-12 11:33:00

标签: c console framebuffer

在基于 framebuffer 的控制台中渲染字符的最快方法是什么?我正在使用 XNU 分发中的iso_font.h字体。

现在我正在使用此代码渲染角色,但它看起来效率不高:

px = px* ISO_CHAR_WIDTH;
py = py* (ISO_CHAR_HEIGHT-1);

for (int i = 0; i < 15; i += 1) 
{
    int sym = iso_font[c*16+i];

    int x = px;
    int y = py + i;

    for (int ii =0; ii < 8; ii++) 
    {
        x+=1;
        if ((sym & (1 << ii)))
        {
            fb_set_px(x,y,fg);
        }
        else 
        {
            fb_set_px(x,y,bg);
        }

    }   
}

我也想知道这段代码是否可以简化:

void fb_set_px(x,y,hex){
    void*ptr = ((_base + (_bpr*y) + (_bpe*x)));
    unsigned int *p = (unsigned int *) ptr;
    *p=hex;
}

直到有太多行并且我需要重绘整个控制台(滚动)这一点是不错的,此时存在明显的延迟。

2 个答案:

答案 0 :(得分:4)

我想到的一些事情(这让我回到了旧的DOS时代!): -

1)使用增量寻址来写入像素:

  p = calculate address of x,y
    for line = 0 to 15
      for column = 0 to 7
        write to p
        increment p
      end
      p += stride - 8 (stride = distance in memory between vertically adjacent pixels)
    end

2)消除内循环中的if:

 draw pixel (fg + (bg - fg) & (((sym >> column) & 1) - 1)

3)使用操作系统的任何帮助。例如,这可能是硬件加速。

4)滚动时,不要重绘所有字符,只需记住将保留的屏幕部分。例如从第1行到第0行记忆行数 - 1.然后清除曝光区域。

|-------|                             |-------|                      |-------|
|.......|                             |@@@@@@@|                      |@@@@@@@|
|@@@@@@@| scroll up a line => memmove |#######| then clear => memset |#######|
|#######|                             |#######|                      |       |
|-------|                             |-------|                      |-------|

答案 1 :(得分:2)

通常,大多数硬件帧缓冲区(例如VGA帧缓冲区)都具有硬件滚动功能。不仅如此,一些帧缓冲区(不幸的是不是VGA字符控制台)将“环绕”,这意味着当你写入帧缓冲区的最后一个字节(或位),然后再写入第一个帧缓冲区的字节(或位),这些字节将作为屏幕上的下一行出现在硬件中。所以你可以做几件事:

  1. 完成后,使用帧缓冲区的硬件滚动功能滚动到下一行。这意味着您将首先清除帧缓冲区的下一行,在该行上写入您需要的字符,然后将帧缓冲区起始行指针递增1。然后,这将“出现”,好像帧缓冲区已向下滚动一行。当你到达帧缓冲区内存的末尾时,保持相同的进程,但是你要清除的下一行将是帧缓冲区内存的第一行。然后你将继续递增帧缓冲区的起始地址(这仍然指向帧缓冲区内存的末尾附近),直到它也指向帧缓冲区的内存中的最后一行,此时然后你将回到内存的第一行。
  2. 如果你的帧缓冲区仍然是多页的,但是没有环绕功能(比如VGA字符控制台),那么保持一个指向帧缓冲区开头的指针,当你到达在内存中的最后一个“页面”,使用memcpy()将内存中的最后一页复制到内存中帧缓冲区的第一页...完成该步骤后,请保持相同的页面滚动例程在那里清除下一行,然后递增帧缓冲区的起始行指针,以给出向上滚动一行的错觉。页面副本将比到目前为止使用的所有其他硬件页面滚动慢一点,但memcpy()非常有效,特别是如果您使用更大的内存类型一次复制多个字节,例如long而不是char
  3. 接下来,就你的访问函数fb_set_px而言,我会1)在头文件中使它inline,这样你就可以避免实际函数调用的开销,这需要使用堆栈来设置一个激活帧,2)你可以使用你的帧缓冲区只是一个内存数组的事实来实际映射指向一些struct类型的指针数组,表示你的frame_buffer的每字符数据布局(即,某些帧缓冲区有一个字符用于字符,另一个字节用于某些类型的属性,如颜色等应用于该字符)。例如,您可以执行以下操作:

    typedef struct frame_buffer_t
    {
        unsigned char character;
        unsigned char attributes;
        //add or omit any fields that describe your frame-buffer data-layout
    } __attribute__((packed)) frame_buffer_t;
    
    //define as global variable
    //make volatile to avoid any compiler re-ordering
    unsigned volatile frame_buffer_t* framebuffer[MAX_ROWS_IN_FRAMEBUFFER];
    
    int main()
    {
        unsigned volatile frame_buffer_t* temp = global_frame_buffer_start_ptr;
    
        for (int i=0; i < MAX_ROWS_IN_FRAMEBUFFER; i++)
        {
            framebuffer[i] = temp;
            temp += NUM_COLUMNS;
        }
    
        //... more code
    }
    

    现在,您可以简单地将帧缓冲区中的x,y位置作为

    framebuffer[Y_POS][X_POS].character = SOME_VALUE;