在 C 中显示颜色数组

nee*_*eeh 0 c graphics image bitmap

我的程序既写入又读取这样的颜色数组:

struct Image {
    size_t width;
    size_t height;
    struct Color *data;
}

struct Color {
    char r;
    char g;
    char b;
}
Run Code Online (Sandbox Code Playgroud)

如何在 C 语言的屏幕上显示这样的数组?

Spe*_*tre 6

图形渲染:

我习惯了win32和Borland C++环境,所以就坚持了下来,但是在其他环境上的区别大多只在类名上。首先是一些方法:

  1. 控制台/文本模式

您可以使用文本图形(我认为是英文的ASCII 艺术)。其中字符表示。强度由或多或少填充的字符决定。通常有一个按强度排序的字符表," ..:+*#"并使用它而不是颜色。要打印某些内容,您可以使用iostream, likecout << "text" << endl;printffromstdio我认为(我已经十多年没有使用旧式控制台输出了)。

文本模式视频 RAM ( VRAM ) 开始,0B000:0000如果您有权限,您可以像这样直接访问:

    char far *scr = (char far*)0x0B0000000;
    scr[0] = 'A'; // Print A to left upper corner
Run Code Online (Sandbox Code Playgroud)

但是在 Windows 上,您可以忘记直接访问

  1. VGA图形模式

(仅限 DOS,而不是 Windows;这是对 VGA 硬件的直接访问)。这是一个小例子:

 // Turbo C++ for 16-bit real mode DOS
        //==============================================================================
        char far* scr;              // VGA screen
        const _sx= 320;             // Physical screen size
        const _sy= 200;
        //==============================================================================
        void gfxinit();
        void cls();
        void pnt(int x,int y,char c);
        //==============================================================================
        void gfxinit()
            {
            asm {   mov ax,19               // This switches  VGA to 320*200*256 color mode (fits inside a single 64 KB segment so no funny stuff is needed)
                int 16
                }
            for (int i=0;i<256;i++) asm { // This overwrites 256 color palette with some BW gradients
                mov dx,0x3C8
                mov ax,i
                out dx,al              // Overwrite color al = i 
                inc dx
                shr al,2               // al=al>>2
                out dx,al              // r,g,b or b,g,r not sure now 
                out dx,al              // All values are 6-bit long, therefore the shr al,2 
                out dx,al
                }
            scr=(char far*)0xA0000000;     // VRAM start address
            }
        //==============================================================================
        void cls()   // This clears the screen with zeros
            {
            asm {   push    es
                mov ax,0xA000
                mov es,ax
                mov di,0x0000
                sub ax,ax
                mov cx,32000
                rep stosw
                pop es
                }
            }
        //==============================================================================
        void pnt(int x,int y,char c) // This draws a single point of color c
            {
            unsigned int adr;
            if (x<_sx)
             if (x>=0)
              if (y<_sy)
               if (y>=0)
                {
                y=y*_sx;
                adr=x+y;
                scr[adr]=c;
                }
            }
        //==============================================================================
Run Code Online (Sandbox Code Playgroud)

VESA访问类似,但您必须处理段交叉和寻呼。这是一个小的 Turbo C++ 示例:

VESA.h

// Turbo C++, still 16-bit DOS, 
// but using VESA calls to set modes instead of VGA registers
    //==============================================================================
    //=== Globals: =================================================================
    //==============================================================================
    char far* scr=(char far*)0xA0000000;    // VGA/VESA memory pointer
    int VESA_page,VESA_pages;       // Actual page and total pages
    int VESA_xs,VESA_ys,VESA_bpp;       // Video mode properties
    int VESA_page_xy[64]={-1,-1};       // Starting x,y for each page
    const int VESAmodes[]=          // Usable video modes table
        {
         320, 200, 8,0x150,
         640, 480, 8,0x101,
         800, 600, 8,0x103,
        1024, 768, 8,0x105,
        1280,1024, 8,0x107,
    
         320, 200,16,0x10E,
         640, 480,16,0x111,
         800, 600,16,0x114,
        1024, 768,16,0x117,
    
         320, 200,32,0x10F,
         640, 480,32,0x112,
         800, 600,32,0x115,
    
        0,0,0,0
        };
    //==============================================================================
    //=== Headers: =================================================================
    //==============================================================================
    int  VESAmode(int xs,int ys,int bpp);   // Set video mode
    void VESApage(int page);        // Set page
    void VESAexit();            // Return to VGA text mode
    void VESAcls();             // Clear with 0
    void VESApnt(int x,int y,unsigned int c); // Render 8/16 bpp point
    void VESApnt32(int x,int y,int r,int g ,int b); // render 32bpp point
    //==============================================================================
    //=== Graphic: =================================================================
    //==============================================================================
    int VESAmode(int xs,int ys,int bpp)
        {
        int i,mode,x,y;
        unsigned int adr0,adr,dx,dy;
        // find video mode
        for (i=0;VESAmodes[i];i+=4)
         if (VESAmodes[i+0]==xs)
          if (VESAmodes[i+1]==ys)
           if (VESAmodes[i+2]==bpp)
            break;
        if (!VESAmodes[i]) return 0;
        mode=VESAmodes[i+3];
        VESA_xs=xs;
        VESA_ys=ys;
        VESA_bpp=bpp;

        // Compute start x,y for each page>0
        dx=bpp>>3;
        dy=xs*dx;
        VESA_pages=1;
        for (adr=i=x=y=0;y<VESA_ys;y++)
            {
            adr0=adr;
            adr+=dy;
            if (adr0>adr)
                {
                while (adr>0) { adr-=dx; x--; }
                while (x<0) { x+=VESA_xs; y--; }
                VESA_page_xy[i]=x; i++;
                VESA_page_xy[i]=y+1; i++;
                VESA_pages++;
                }
            }
        VESA_page_xy[i]=-1; i++;
        VESA_page_xy[i]=-1; i++;
    
    // Set video mode
        asm {
            mov bx,mode
            mov ax,0x4F02
            int 16
            }
        VESApage(0);
    /*
        // Set palette to grayscale
        if (VESAbpp==8)
         for (int i=0;i<256;i++) asm {
            mov dx,0x3C8
            mov ax,i
            out dx,al
            inc dx
            shr al,2
            out dx,al
            out dx,al
            out dx,al
            }
    */
        return 1;
        }
    //==============================================================================
    void VESApage(int page)
        {
        int p=page;
        asm {
            mov dx,p
            mov bx,0
            mov ax,0x4f05
            int 16
            }
        VESA_page=page;
        }
    //==============================================================================
    void VESAexit()
        {
        asm     {
            // Wait for key press
            mov ax,0
            int 0x16
            // VGA 80x25 text mode
            mov ax,3
            int 16
            }
        }
    //==============================================================================
    void VESAcls()
        {
        int i;
        for (i=0;i<VESA_pages;i++)
            {
            VESApage(i);
            asm     {
                push es
                mov ax,0xA000
                mov es,ax
                mov di,0x0000
                mov ax,0
                mov cx,32000
                rep stosw
                pop es
                }
            }
        }
    //==============================================================================
    void VESApnt(int x,int y,unsigned int c)
        {
        unsigned int adr;
        int p;
        // inside screen?
        if ((x>=0)&&(x<VESA_xs))
         if ((y>=0)&&(y<VESA_ys))
            {
            // Low 16 bit of address
            adr=y;
            adr*=VESA_xs;
            adr+=x;
            adr*=(VESA_bpp>>3);
            // Page
            for (p=0;VESA_page_xy[p+p+0]>=0;p++)
                {
                if (VESA_page_xy[p+p+1]>y) break;
                if (VESA_page_xy[p+p+1]<y) continue;
                if (VESA_page_xy[p+p+0]>x) break;
                }
            if (p!=VESA_page) VESApage(p);
            // Render
            scr[adr]=c;
            if (VESA_bpp==16)
                {
                adr++; if (adr==0) VESApage(p+1);
                scr[adr]=(c>>8);
                }
            }
        }
    //==============================================================================
    void VESApnt32(int x,int y,int r,int g ,int b)
        {
        unsigned int adr;
        int p;
        // inside screen?
        if ((x>=0)&&(x<VESA_xs))
         if ((y>=0)&&(y<VESA_ys))
            {
            // Low 16 bit of address
            adr=y;
            adr*=VESA_xs;
            adr+=x;
            adr*=(VESA_bpp>>3);
            // Page
            for (p=0;VESA_page_xy[p+p+0]>=0;p++)
                {
                if (VESA_page_xy[p+p+1]>y) break;
                if (VESA_page_xy[p+p+1]<y) continue;
                if (VESA_page_xy[p+p+0]>x) break;
                }
            if (p!=VESA_page) VESApage(p);
            // Render
            scr[adr]=b; adr++; if (adr==0) VESApage(p+1);
            scr[adr]=g; adr++; if (adr==0) VESApage(p+1);
            scr[adr]=r;
            }
        }
    //==============================================================================
    //=== End. =====================================================================
    //==============================================================================
Run Code Online (Sandbox Code Playgroud)

主程序

    //==============================================================================
    //=== Includes: ================================================================
    //==============================================================================
    #include "vesa.h"
    //==============================================================================
    //=== Main: ====================================================================
    //==============================================================================
    void main()
        {
        if (!VESAmode(800,600,32)) return;
        VESAcls();
        int x,y;
        unsigned int c;
        for (y=0;y<VESA_ys;y++)
         for (x=0;x<VESA_xs;x++)
            {
            if (VESA_bpp== 8)
                {
                c=x+y;
                VESApnt(x,y,c);
                }
            if (VESA_bpp==16)
                {
                c=(x&31)+((y&63)<<5);
                VESApnt(x,y,c);
                }
            if (VESA_bpp==32) VESApnt32(x,y,x,x+y,y);
            }
    
        VESAexit();
        }
    //==============================================================================
    //=== End. =====================================================================
    //==============================================================================
Run Code Online (Sandbox Code Playgroud)
  1. GDI - 可在 Windows 上使用

Canvas是 Windows 上可视组件的图形子组件。在 Borland 中是TCanvas名为Canvas. 所有窗户也有它,PaintBoxes, Bitmaps, ...。它是Windows 和您的应用程序之间的GDI接口。它具有诸如Pen, Brush, and Font线条、填充或文本纸、文本墨水等子组件。

    Form1->Canvas->Pen->Color=clYellow;
    Form1->Canvas->MoveTo(10,10);
    Form1->Canvas->LineTo(100,150);
Run Code Online (Sandbox Code Playgroud)

Form1我的VCL窗口在哪里。这段代码画了一条黄线。

GDI有很多功能,比如Arc, Ellipse, Pixels[][],.... 有关详细信息,请参阅 IDE 的内置帮助。

  1. GDI位图

这是一个特殊的对象。它是一个带有操作系统图形句柄(DC设备上下文)的位图。这允许位图类似于窗口并且可以访问GDI

    Graphics::TBitmap *bmp=new Graphics::TBitmap;
    bmp->Width=100;
    bmp->Height=100;
    bmp->HandleType=bmDIB;    // Allows use of ScanLine
    bmp->PixelFormat=pf32bit; // 32-bit - the same as int so we can use int* for pixels pointer
Run Code Online (Sandbox Code Playgroud)

这将创建一个VCL位图并将其设置为100x100x32 bit直接访问。现在您可以访问该ScanLine属性。也bmp->Canvas存在,所以你也可以做所有GDI 的东西。

    int *p=bmp->ScanLine[10]; // p = pointer to y=10 line of bitmap
    p[20]=0;                    // Draw dot on x=20,y=10   color=0x00000000 which is black
    int c = p[15];              // Read pixel x=15,y=10 from bitmap to c
Run Code Online (Sandbox Code Playgroud)

小心留x,y在位图中,否则会抛出异常。颜色编码取决于pixelformat,通常是0x00RRGGBB0x00BBGGRR。我认为这种方法是您的最佳选择。此外,您可以将任何GDI对象绘制到任何其他GDI对象:

    Form1->Canvas->Draw(0, 0, bmp);
Run Code Online (Sandbox Code Playgroud)

这会将您的位图绘制到窗口,因此您可以实际看到它。

  1. 图形库

有很多,但最常用的是OpenGLDirectX。我更喜欢 OpenGL,因为它实现起来更简单(至少对于初学者来说),而且 OpenGL 是跨平台的,而 DirectX 仅适用于 Windows。此外,当我开始编码时,没有任何 DirecX。当我开始使用 OpenGL 时,所有供应商都将其包含在驱动程序中。现在唯一仍然保持最新状态的供应商是NvidiaATI (AMD)。它们之间几乎总是存在一些驱动程序问题,但总的来说,Nvidia 更适合 OpenGL(在 DirectX 实现中存在错误),而ATI(仅限 AMD 版本)更适合 DirectX(它在 OpenGL 实现中存在错误)。但是对于基本操作,您很好(问题会出现在更高级的功能上)。

Intel、SiS 等供应商已停止在较新的 OpenGL 版本上实施。至少,我不知道有比 OpenGL 3.3 更好的驱动程序。

要开始使用 OpenGL,请参阅OpenGL 获取设备上下文

我强烈建议先从 GDI + Bitmap 开始。你可以用它们做很多事情。我仍在使用它进行非复杂渲染。

如前所述,我对 Borland(VCL风格)友好,因此如果您使用不同的编译器/IDE,则更改GDI对象名称以对应您的环境。我认为 Canvas 和 bitmap 是一样的HBitmap,但最好检查您的帮助/文档。至少你知道要搜索什么。

其他平台和东西

  • 回复:模拟 VGA:也许不像您想象的那么完全虚拟。现代 PC 视频硬件显然仍然具有对文本模式的真正硬件支持,而不是像我想象的那样缓慢地陷入 SMM:[现代 PC 视频硬件是否支持硬件中的 VGA 文本模式,或者 BIOS 是否模拟它(通过系统管理)模式)?](/sf/ask/4306527361/) (2认同)