Display an array of color in C [closed]

2018-12-31 06:22发布

My program both writes an reads arrays of colors like this one :

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

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

How can I display such an array on the screen in C ?

1条回答
姐姐魅力值爆表
2楼-- · 2018-12-31 06:51

Graphics rendering:

I am used to win32 and Borland C++ environments so I stick to it but the differences on other environments are mostly only in Class names. First some approaches:

  1. console/text modes

    you can use text graphics (ASCII art I think in English). Where point is represented by character. Intensity is made by more or less filled chars. Usually have a table of characters sorted by intensity like " ..:+*#" and use that instead of colors. For printing out something can use iostream like cout << "text" << endl; or printf from stdio I think (not using old-style console output for more than a decade).

    Text modes videoram(VRAM) starts at 0B000:0000 if you have the priviledges for it you can do direct access like this:

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

    but on Windows you can forget about direct access

  2. VGA gfx mode

    on Windows you can forget about this also... Here small example:

        //==============================================================================
        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 swith VGA to 320*200*256 color mode (fits inside single 64KB 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 6bit long therefore the shr al,2 
                out dx,al
                }
            scr=(char far*)0xA0000000;     // VRAM start address
            }
        //==============================================================================
        void cls()   // this clear screen with zero
            {
            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 draw 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 access is similar but you have to deal with segment crossing and paging. Here small Turbo C++ example:

    VESA.h

    //==============================================================================
    //=== Globals: =================================================================
    //==============================================================================
    char far* scr=(char far*)0xA0000000;    // VGA/VESA memory pointer
    int VESA_page,VESA_pages;       // actaul 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 vide 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     {
            // waut 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. =====================================================================
    //==============================================================================
    

    main.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. =====================================================================
    //==============================================================================
    
  3. GDI

    Canvas is graphic subcomponent of visual components on Windows. In borland is the class TCanvas named Canvas. All windows has it also PaintBoxes,Bitmaps,.... It is the GDI interface between Windows and your App. It has subcomponents like Pen,Brush,Font for lines,fills or text paper,texts ink.

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

    where Form1 is my VCL window this code draws a yellow line.

    GDI has many functions like Arc,Ellipse,Pixels[][],... see build in help of your IDE for more info.

  4. GDI Bitmap

    this is special object it is a bitmap with OS graphic handle (DC device context). This allows bitmap to be something like window and have access to GDI

    Graphics::TBitmap *bmp=new Graphics::TBitmap;
    bmp->Width=100;
    bmp->Height=100;
    bmp->HandleType=bmDIB;    // allows use of ScanLine
    bmp->PixelFormat=pf32bit; // 32bit the same as int so we can use int* for pixels pointer
    

    this creates a VCL bitmap and sets it to 100x100x32bit with direct access. Now you can access ScanLine property. Also bmp->Canvas is present so you can do all GDI stuff too.

    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
    

    Be careful to stay with x,y inside bitmap or exception will be thrown. Color coding depends on pixelformat usually is 0x00RRGGBB or 0x00BBGGRR. I think this approach is the best option for you also you can draw any GDI object to any other GDI object

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

    this draws your bitmap to window so you can see it actually.

  5. Graphics library

    There are many but the most used are OpenGL an DirectX. I prefer OpenGL because it is more simple to implement (at least for starters) and also OpenGL is cross-platform an DirectX is windows only. Also when I start coding there was no DirecX. When I started using OpenGL all vendors has it in drivers included. Now the only vendors which are still up to date are nVidia and ATI(AMD). There is almost always some driver issue between them but in general nVidia is better for OpenGL (has bugs in DirectX implementation) and ATI(AMD versions only) is better for DirectX (has bugs in OpenGL implementation). But for basic operations you are fine (problems gets on more advanced functions)

    Vendors like Intel,SiS,... has stopped their implementations on newer OpenGL versions at least I do not know of any driver better then OpenGL 3.3 for them

    To get started with OpenGL see OpenGL get Device Context

I strongly recommend to start with GDI+Bitmap first. you can do a lot with them I am still using it for non complex rendering.

As mentioned before I am borland (VCL style) friendly so if you use different compiler/IDE then change the GDI object names to correspond your environment. I think Canvas is the same and bitmap is HBitmap but better check your help/docs at least you know what to search for.

Hope it helps a little.

[Edit1] other platforms

查看更多
登录 后发表回答