【问题标题】:Display an array of color in C [closed]在C中显示一组颜色[关闭]
【发布时间】:2014-03-06 19:36:04
【问题描述】:

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

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

struct Color {
    char r;
    char g;
    char b;
}

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

【问题讨论】:

  • 显示为“绘制图像”或显示为“以描述方式打印数组内容”?
  • C 编程没有用于操作图形的标准库。有许多提供图形功能的库。您必须找到符合您需求的那个。
  • 还有,你试过什么...
  • 显示为“绘制图像”我试过 SDL2.0 但我需要做双缓冲,而 SDL2.0 似乎不适合该任务...
  • 您使用的是什么操作系统?

标签: c image graphics bitmap


【解决方案1】:

图形渲染:

我习惯了win32和Borland C++环境,所以我坚持使用,但在其他环境上的差异大多仅在类名上。首先是一些方法:

  1. 控制台/文本模式

您可以使用文本图形(ASCII art 我认为是英文)。其中 pointcharacter 表示。 Intensity 由或多或少填充的字符组成。通常有一个按强度排序的字符表,如" ..:+*#",并使用它而不是颜色。要打印出一些东西,你可以使用iostream,比如cout << "text" << endl;printf from stdio 我想(我已经有十多年没有使用旧式控制台输出了)。

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

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

但是在 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;
                }
            }
        //==============================================================================

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. =====================================================================
    //==============================================================================

ma​​in.cpp

    //==============================================================================
    //=== 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. =====================================================================
    //==============================================================================
  1. GDI - 可在 Windows 上使用

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

    Form1->Canvas->Pen->Color=clYellow;
    Form1->Canvas->MoveTo(10,10);
    Form1->Canvas->LineTo(100,150);

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

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

  1. GDI 位图

这是一个特殊的对象。它是一个带有 OS 图形句柄(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

这将创建一个 VCL 位图并将其设置为100x100x32 bit 并具有直接访问权限。现在您可以访问ScanLine 属性。 bmp-&gt;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

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

    Form1->Canvas->Draw(0, 0, bmp);

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

  1. 图形库

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

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

要开始使用 OpenGL,请参阅OpenGL get Device Context

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

如前所述,我对 Borland(VCL 风格)友好,所以如果您使用不同的编译器/IDE,请更改 GDI 对象名称以对应您的环境。我认为画布是相同的,位图是HBitmap,但最好检查您的帮助/文档。至少您知道要搜索什么。

其他平台和东西

【讨论】:

  • 您好 Spektre,感谢您提供完整的答案!我习惯了 OpenGLES 2.0。但我必须实现一个软件渲染器。你认为位图最适合光栅化吗?
  • 是的,位图是最简单的方法,......并且使用适当的扫描线属性也很有效(在我的支持 phong 纹理反射的软件 3D 渲染上获得 70fps......)
  • 如果有人添加类似我对 MSVC++ gcc 的回答之类的东西会很棒......让所有基本的渲染方法在一个站点上简单明了......
  • 在 Windows 上,你也可以忘记这一点......这是一个小例子: - 我认为这将是一个“忘记它”并做的例子在 Windows 上有所不同。但它使用out 指令,所以它是直接硬件访问。它将受益于代码块顶部的注释,例如// Turbo C++, for 16-bit DOS with VGA。 (如果那是准确的。)
  • Re: 模拟 VGA:可能没有你想象的那么完全虚拟。现代 PC 视频硬件显然仍然具有对文本模式的真正硬件支持,而不是像我认为的那样缓慢地陷入 SMM:Does modern PC video hardware support VGA text mode in HW, or does the BIOS emulate it (with System Management Mode)?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-03
  • 2012-08-24
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多