<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=Drawing_In_a_Linear_Framebuffer</id>
	<title>Drawing In a Linear Framebuffer - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=Drawing_In_a_Linear_Framebuffer"/>
	<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=Drawing_In_a_Linear_Framebuffer&amp;action=history"/>
	<updated>2026-04-06T05:46:24Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>http://wiki.foofun.cn//index.php?title=Drawing_In_a_Linear_Framebuffer&amp;diff=697&amp;oldid=prev</id>
		<title>Zhang3：创建页面，内容为“现在，你已经知道如何使用硬件VGA支持轻松地将文本写入屏幕，你可能想知道如何能够显示漂亮的图像、窗口、菜单、图标、漂亮的光标和按钮等。 本页介绍如何在线性帧缓冲区中显示图形，线性帧缓冲区是在内存中映射的一个简单数组，代表屏幕。  ==图形模式== {{Main|Getting VBE Mode Info}} {{Main|GOP}} VGA和VBE模式可以使用BIOS中断0x10选择（在实模式下）。…”</title>
		<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=Drawing_In_a_Linear_Framebuffer&amp;diff=697&amp;oldid=prev"/>
		<updated>2022-03-01T07:53:50Z</updated>

		<summary type="html">&lt;p&gt;创建页面，内容为“现在，你已经知道如何使用硬件VGA支持轻松地将文本写入屏幕，你可能想知道如何能够显示漂亮的图像、窗口、菜单、图标、漂亮的光标和按钮等。 本页介绍如何在线性帧缓冲区中显示图形，线性帧缓冲区是在内存中映射的一个简单数组，代表屏幕。  ==图形模式== {{Main|Getting VBE Mode Info}} {{Main|GOP}} VGA和VBE模式可以使用BIOS中断0x10选择（在实模式下）。…”&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;现在，你已经知道如何使用硬件VGA支持轻松地将文本写入屏幕，你可能想知道如何能够显示漂亮的图像、窗口、菜单、图标、漂亮的光标和按钮等。 本页介绍如何在线性帧缓冲区中显示图形，线性帧缓冲区是在内存中映射的一个简单数组，代表屏幕。&lt;br /&gt;
&lt;br /&gt;
==图形模式==&lt;br /&gt;
{{Main|Getting VBE Mode Info}}&lt;br /&gt;
{{Main|GOP}}&lt;br /&gt;
VGA和VBE模式可以使用BIOS中断0x10选择（在实模式下）。 [http://www.ctyme.com/intr/int-10.htm INT 0x10], [[VESA Video Modes]] 和 [http://www.vesa.org/ VESA] 是 VGA 和 VBE/VESA的参考资料。&lt;br /&gt;
&lt;br /&gt;
VGA仅限于16色640x480，而VBE（BIOS系统）和[[GOP]]（[[UEFI]]机器，不包括一些非常早期的机器）可以达到显示器和视频卡支持的最大分辨率。&lt;br /&gt;
&lt;br /&gt;
==切换==&lt;br /&gt;
&lt;br /&gt;
设置视频模式最干净的方法是通过视频BIOS。 它可以通过常规的&amp;lt;tt&amp;gt;Int 0x10&amp;lt;/tt&amp;gt;接口执行，也可以通过VBE3提供的（可选）[[Protected mode|包含模式]]接口执行。 正如你所猜测的，&amp;lt;tt&amp;gt;Int 0x10&amp;lt;/tt&amp;gt; 需要一个16位的环境，所以你只能在[[Real Mode|实模式]]或[[Virtual 8086 Mode|虚拟8086模式]]中使用它&lt;br /&gt;
&lt;br /&gt;
实际上，可用选项按难度排序是：&lt;br /&gt;
*在进入保护模式之前，在早期阶段（在引导加载程序中）设置所需的模式。&lt;br /&gt;
**在[[UEFI]]系统上开发，并在启动时获得[[GOP]]帧缓冲区。&lt;br /&gt;
**让你的[[Bootloader]]帮你切换。&lt;br /&gt;
*切换回[[Real Mode|真实模式]]或[[Unreal Mode|非实模式]]以设置正确的视频模式（Napalm 于[http://www.rohitab.com/discuss/topic/35103-switch-between-real-mode-and-protected-mode/ rohitab.com]有一个简洁的小功能供参考。）&lt;br /&gt;
*编写一个[[VGA Hardware| VGA驱动程序]]，可以在几乎所有硬件上执行低分辨率模式&lt;br /&gt;
*如果存在，使用VBE3中的PMID&lt;br /&gt;
*设置一个[[Virtual 8086 Mode|V8086]]的[[Virtual_Monitor|Monitor]]来执行模式切换代码&lt;br /&gt;
*运行软件代码翻译工具，从bios rmode代码中生成pmode代码。([http://www.osdev.org/phpBB2/viewtopic.php?t=10321 SANiK正在进行]）&lt;br /&gt;
*为特定的[[:Category:Video|图形卡]]编写驱动程序&lt;br /&gt;
&lt;br /&gt;
==定位视频内存==&lt;br /&gt;
&lt;br /&gt;
对于标准VGA视频模式，对于EGA/VGA视频模式，视频内存将位于地址&amp;lt;tt&amp;gt;0xA0000&amp;lt;/tt&amp;gt;，对于CGA和文本模式，视频内存将位于地址&amp;lt;tt&amp;gt;0xB8000&amp;lt;/tt&amp;gt;。 要找出哪一个，请查看下表：&lt;br /&gt;
&lt;br /&gt;
{| {{wikitable}}&lt;br /&gt;
|-&lt;br /&gt;
| 00&lt;br /&gt;
| text 40*25 16 color (mono)&lt;br /&gt;
|-&lt;br /&gt;
| 01&lt;br /&gt;
| text 40*25 16 color&lt;br /&gt;
|-&lt;br /&gt;
| 02&lt;br /&gt;
| text 80*25 16 color (mono)&lt;br /&gt;
|-&lt;br /&gt;
| 03&lt;br /&gt;
| text 80*25 16 color&lt;br /&gt;
|-&lt;br /&gt;
| 04&lt;br /&gt;
| CGA 320*200 4 color&lt;br /&gt;
|-&lt;br /&gt;
| 05&lt;br /&gt;
| CGA 320*200 4 color (m)&lt;br /&gt;
|-&lt;br /&gt;
| 06&lt;br /&gt;
| CGA 640*200 2 color&lt;br /&gt;
|-&lt;br /&gt;
| 07&lt;br /&gt;
| MDA monochrome text 80*25&lt;br /&gt;
|-&lt;br /&gt;
| 08&lt;br /&gt;
| PCjr&lt;br /&gt;
|-&lt;br /&gt;
| 09&lt;br /&gt;
| PCjr&lt;br /&gt;
|-&lt;br /&gt;
| 0A&lt;br /&gt;
| PCjr&lt;br /&gt;
|-&lt;br /&gt;
| 0B&lt;br /&gt;
| ''reserved''&lt;br /&gt;
|-&lt;br /&gt;
| 0C&lt;br /&gt;
| ''reserved''&lt;br /&gt;
|-&lt;br /&gt;
| 0D&lt;br /&gt;
| EGA 320*200 16 color&lt;br /&gt;
|-&lt;br /&gt;
| 0E&lt;br /&gt;
| EGA 640*200 16 color&lt;br /&gt;
|-&lt;br /&gt;
| 0F&lt;br /&gt;
| EGA 640*350 mono&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| EGA 640*350 16 color&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| VGA 640*480 mono&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| VGA 640*480 16 color&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| VGA 320*200 256 color&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
对于VESA模式，帧缓冲区地址存储在[[Getting VBE Mode Info|mode info block]]中。 这是线性帧缓冲区的'''物理'''地址（它不是16位远指针，而是32位线性指针）： 如果你使用分页，你必须把它映射到某个地方才能使用它。 帧缓冲区的长度（字节）是pitch*height。&lt;br /&gt;
&lt;br /&gt;
对于GOP，帧缓冲区地址位于EFI_GRAPHICS_PROTOCOL结构中，GOP-&amp;gt;Mode-&amp;gt;FrameBufferBase。 GOP'''不'''支持电传字符模式，只支持面向图形像素的模式。&lt;br /&gt;
&lt;br /&gt;
==绘制像素（Plotting Pixels）==&lt;br /&gt;
&lt;br /&gt;
===定位===&lt;br /&gt;
如果你想在屏幕中间绘制一个红色像素。 你首先要知道的是屏幕中间的位置。 在320x200x8（mode 13）中，这将是偏移100x320+160=32160。 一般来说，你的屏幕可以通过以下方式描述：&lt;br /&gt;
{| {{wikitable}}&lt;br /&gt;
| 宽度 width&lt;br /&gt;
|水平线上有多少像素&lt;br /&gt;
|-&lt;br /&gt;
|高度 height&lt;br /&gt;
|有多少像素的水平线&lt;br /&gt;
|-&lt;br /&gt;
| 间距 pitch&lt;br /&gt;
|为了降低一个像素，你应该跳过多少''字节''的VRAM&lt;br /&gt;
|-&lt;br /&gt;
|深度 depth&lt;br /&gt;
|你有多少种颜色&lt;br /&gt;
|-&lt;br /&gt;
|“像素宽度pixelwidth”&lt;br /&gt;
|你应该跳过多少字节的VRAM才能向右移动一个像素。&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
“间距”和“宽度”乍一看可能显得多余，但事实并非如此。 当你使用更高的分辨率时，比如每行8K字节，而你的屏幕实际上是1500像素宽（每像素32位），这种情况并不罕见。 好消息是，它允许平滑的水平滚动（主要用于2D游戏：P）&lt;br /&gt;
&lt;br /&gt;
间距和像素宽度通常由VESA模式信息发布。一旦你知道了它们，你就可以计算出你绘制像素的位置：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;unsigned char *pixel = vram + y*pitch + x*pixelwidth;&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===颜色===&lt;br /&gt;
要知道的第二件事是你应该为“红色”（或其它颜色）写什么值。 这同样取决于你的屏幕设置。 在EGA模式下，你有一个固定的调色板，具有暗红色（颜色4）和浅红色（颜色12）。 然而，EGA要求你在不同的像素平面上绘制每一位，因此，如果你 '''实在'''希望支持这种模式，请参阅EGA编程教程。&lt;br /&gt;
在传统的320x200x8 VGA模式下，你的4色和12色与EGA中的颜色相同，因此你可以使用如下方式&lt;br /&gt;
&lt;br /&gt;
 *pixel = 4;&lt;br /&gt;
&lt;br /&gt;
然而，在VGA中，调色板是可重新编程的（正如你可以在FreeVGA文档中了解到的），因此几乎任何介于0..255之间的值都可能为“红色”。&lt;br /&gt;
&lt;br /&gt;
所以VGA的完整“putpixel”功能如下&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* 320x200 VGA的示例 */&lt;br /&gt;
void putpixel(int pos_x, int pos_y, unsigned char VGA_COLOR)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char* location = (unsigned char*)0xA0000 + 320 * pos_y + pos_x;&lt;br /&gt;
    *location = VGA_COLOR;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
最后，在VESA和GOP模式下，通常使用truecolor或hicolor，在这两种模式下，必须为每个像素提供独立的红色、绿色和蓝色值。 modeinfo将（再次）指导你如何以像素位组织RGB组件。 例如，对于15位模式，你将使用&amp;lt;tt&amp;gt;0xRRRRRGGGBBBBB&amp;lt;/tt&amp;gt;，这意味着#ff0000红色在那里&amp;lt;tt&amp;gt;0x7800&amp;lt;/tt&amp;gt;，而#808080灰色在那里&amp;lt;tt&amp;gt;0x4210&amp;lt;/tt&amp;gt; （拿起铅笔，画出比特，自己看）&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void putpixel(unsigned char* screen, int x,int y, int color) {&lt;br /&gt;
    unsigned where = x*pixelwidth + y*pitch;&lt;br /&gt;
    screen[where] = color &amp;amp; 255;              // BLUE&lt;br /&gt;
    screen[where + 1] = (color &amp;gt;&amp;gt; 8) &amp;amp; 255;   // GREEN&lt;br /&gt;
    screen[where + 2] = (color &amp;gt;&amp;gt; 16) &amp;amp; 255;  // RED&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===优化===&lt;br /&gt;
&lt;br /&gt;
从这里可以很容易地编写其他绘图函数，从调用putpixel... 不要这样做。绘制一个填充矩形意味着你可以访问连续的像素，然后按“pitch-rect_width”前进以填充下一行。 如果你执行&amp;quot;for(y=100;y&amp;lt;200;y++) for(x=100;x&amp;lt;200;x++) putpixel (screen,x,y,RED);&amp;quot;循环时，你将重新计算大约10000次“where”，并进行过多的函数调用。 即使编译器将y*pitch优化为加法和移位，而不是乘法，在可能的情况下浪费CPU时间也是愚蠢的&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static void fillrect(unsigned char *vram, unsigned char r, unsigned char g, unsigned   char b, unsigned char w, unsigned char h) {&lt;br /&gt;
    unsigned char *where = vram;&lt;br /&gt;
    int i, j;&lt;br /&gt;
&lt;br /&gt;
    for (i = 0; i &amp;lt; w; i++) {&lt;br /&gt;
        for (j = 0; j &amp;lt; h; j++) {&lt;br /&gt;
            //putpixel(vram, 64 + j, 64 + i, (r &amp;lt;&amp;lt; 16) + (g &amp;lt;&amp;lt; 8) + b);&lt;br /&gt;
            where[j*pixelwidth] = r;&lt;br /&gt;
            where[j*pixelwidth + 1] = g;&lt;br /&gt;
            where[j*pixelwidth + 2] = b;&lt;br /&gt;
        }&lt;br /&gt;
        where+=pitch;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
这应该足以让你开始编写（或在谷歌上搜索）一个像样的视频库。&lt;br /&gt;
&lt;br /&gt;
==绘制文本==&lt;br /&gt;
{{Main|VGA Fonts}}&lt;br /&gt;
&lt;br /&gt;
一旦进入图形模式，你就不再有BIOS或硬件来为你绘制字体。 基本思想是为每个字符提供字体数据，并使用它来绘制（或留白）像素。 存储这些字体的方法有很多，这取决于它们是否有多种颜色、alpha通道等。 然而，你基本上拥有的是：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// 保存你所需要的每一个char&lt;br /&gt;
font_char* font_data[CHARS];&lt;br /&gt;
 &lt;br /&gt;
// 在给定font_data的情况下渲染其中一个character&lt;br /&gt;
void draw_char(screen, where, font_char*);&lt;br /&gt;
&lt;br /&gt;
void draw_string(screen, where, char* input) {&lt;br /&gt;
    while(*input) {&lt;br /&gt;
        draw_char(screen,where,font_data[input]);&lt;br /&gt;
        where += char_width;&lt;br /&gt;
        input++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void draw_char(screen, where, font_char*) {&lt;br /&gt;
    for (l = 0; l &amp;lt; 8; l++) {&lt;br /&gt;
        for (i = 8; i &amp;gt; 0; i--) {&lt;br /&gt;
            j++;&lt;br /&gt;
            if ((font_char[l] &amp;amp; (1 &amp;lt;&amp;lt; i))) {&lt;br /&gt;
                c = c1;&lt;br /&gt;
                put_pixel(j, h, c);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        h++;&lt;br /&gt;
        j = x;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 绘制图标==&lt;br /&gt;
{{Main|Loading Icons}}&lt;br /&gt;
&lt;br /&gt;
对于GUI，你可能需要显示图标。就如下这么简单：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void draw_icon(x, y, w, h, pixels) {&lt;br /&gt;
    for (l = j = 0; l &amp;lt; h; l++) {&lt;br /&gt;
        for (i = 0; i &amp;lt; w; i++, j++) {&lt;br /&gt;
            put_pixel(x + i, y + l, pixels[j]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
困难在于，图标不是以宽度、高度和像素阵列的形式存储在磁盘上。 首先，你必须[[Loading Icons|解码图像文件]]才能获得这些信息。&lt;br /&gt;
&lt;br /&gt;
==另见==&lt;br /&gt;
*[[How_do_I_set_a_graphics_mode|Extra Notes]]&lt;br /&gt;
*[[Double Buffering]]&lt;br /&gt;
*[[3D Renderer Basics]]&lt;br /&gt;
*[[PC Screen Font]]&lt;br /&gt;
*[[Scalable Screen Font]]&lt;br /&gt;
*[[Loading Icons]]&lt;br /&gt;
&lt;br /&gt;
==外部链接==&lt;br /&gt;
*[http://bos.asmhackers.net/forum/viewtopic.php?id=65 Covers basically the same, and is ASM-oriented.]&lt;br /&gt;
&lt;br /&gt;
[[Category:Video]]&lt;br /&gt;
[[Category:Graphical UI]]&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Zhang3</name></author>
	</entry>
</feed>