Programowanie w systemie UNIX/c grafika

Grafika w Linuksie
Grafika w Linuksie

Grafika na ekranie

edytuj
  • Grafika nie jest znormalizowana w C.
    • Trzeba napisać kod.
    • można korzystać z bibliotek lub programów graficznych ( gnuplot)
  • rysowanie w terminalu:[1]
    • Terminale wyświetlają znaki i nie obsługują rysowania grafiki pikselowej.
    • Można korzystać z grafiki ASCII / UTF-8

Tworzenie plików graficznych

edytuj

Bezpośrednie tworzenie plików graficznych

edytuj

z użyciem basha

edytuj
./a.out >h.pgm

Z poziomu c

edytuj
 
Przykład użycia tej techniki, sekwencyjny dostęp do danych (kod źródłowy)
 
Przykład użycia tej techniki, swobodny dostęp do danych (kod źródłowy)

Najprostszym przykładem rastrowego pliku graficznego jest plik PPM. Poniższy program pokazuje jak utworzyć plik w katalogu roboczym programu. Do zapisu:[2]

  • nagłówka pliku używana jest funkcja fprintf, która zapisuje do plików binarnych lub tekstowych
  • tablicy do pliku używana jest funkcja fwrite, która zapisuje do plików binarnych,
 #include <stdio.h>
 int main() {
        const int dimx = 800; 
        const int dimy = 800;
        int i, j;
        FILE * fp = fopen("first.ppm", "wb"); /* b - tryb binarny */
        fprintf(fp, "P6\n%d %d\n255\n", dimx, dimy);
        for(j=0; j<dimy; ++j){
          for(i=0; i<dimx; ++i){         
                        static unsigned char color[3];
                        color[0]=i % 255; /* red */
                        color[1]=j % 255; /* green */
                        color[2]=(i*j) % 255; /* blue */
                        fwrite(color,1,3,fp);
                }
        }
        fclose(fp);
        return 0;
 }

W powyższym przykładzie dostęp do danych jest sekwencyjny. Jeśli chcemy mieć swobodny dostęp do danych to:

  • korzystać z funkcji: fsetpos, fgetpos oraz fseek,
  • utworzyć tablicę (dla dużych plików dynamiczną), zapisać do niej wszystkie dane a następnie zapisać całą tablicę do pliku. Ten sposób jest prostszy i szybszy. Należy zwrócić uwagę, że do obliczania rozmiaru całej tablicy nie możemy użyć funkcji sizeof.


/*

How do I get the length of a dynamically allocated array in C? You can't.  You have to pass the length as a parameter to your function.
https://stackoverflow.com/questions/5126353/get-the-length-of-dynamically-allocated-array-in-c


--------------


gcc p.c -Wall -lm

./a.out

ls -l *.ppm


*/

#include <stdio.h>
#include <string.h> // strncat
#include <stdlib.h> // malloc
#include <math.h> // log10

// color = RGB triplet = (thus three bytes per pixel) in the order red, green, then blue
// color = 3 bytes
// color component ( channel) = 1 byte =  number from 0 to 255 = unsigned char 


// size of virtual 2D array of pixels
// each piexel has a RGB color
int iWidth = 1000;
int iHeight ; // 


// size of the dynamic 1D array 
unsigned char * data;
size_t ColorSize = 3; // RGB = number of color components = channels
size_t ArrayLength; // number of 1D array's elements = ENumber = iWidth*iHeight*ColorSize
size_t ElementSize; // size of array's element in bytes
size_t ArraySize; // size of array in bytes = ElementSize*ArrayLength 


size_t HeaderSize ; // size of the P6 file  header in bytes
size_t FileSize; // = HeaderSize +ArraySize [bytes]



// --------------- save dynamic "A" array of uinsigned char to the binary ppm file ( P6 ) --------------------------------
int SaveArray2PPM (unsigned char A[], size_t ESize, size_t ALength,  int k)
{

  FILE *fp;
  const unsigned int MaxColorComponentValue = 255;	/* color component is coded from 0 to 255 ;  it is 8 bit color file */
  
  char name [100]; /* name of file */
  snprintf(name, sizeof name, "%d", k); /*  */
  char *filename =strncat(name,".ppm", 4);
  
  

  /* save image to the pgm file  */
  fp = fopen (filename, "wb");	/*create new file,give it a name and open it in binary mode  */
  if (fp == NULL) 
  	{	printf("File open error"); 
  		return 1;}
  	else {
  		fprintf (fp, "P6\n%u %d\n%d\n", iWidth, iHeight, MaxColorComponentValue);	/*write header to the file */
  		fwrite (A, ESize, ALength, fp);	// write dynamic A array to the binary file in one step  = fwrite (A , ESize, ALength, fp);
 
  		printf ("File %s saved. \n", filename);
  		fclose (fp);
  		return 0;}
  
}



int setup(){

	iHeight = iWidth; // quadratic 2D array
	// 1D array
	ArrayLength = iWidth*iHeight*ColorSize;
	ElementSize = sizeof(unsigned char);
	ArraySize = ElementSize*ArrayLength ; 
	HeaderSize = 11 + (size_t) (log10(iHeight) +log10(iWidth)); 
	
	FileSize = HeaderSize + ArraySize; 
	
	/* create dynamic 1D array for RGB colors  */
  	data = malloc (ArraySize);
  	if (data == NULL ){
    		printf ( "Could not allocate memory for the array\n");
    		return 1;}

	
	return 0;
}


void info(){

	printf("ppm (P6) header size = %zu bytes\n", HeaderSize);
	printf("Array Size  = %zu bytes\n", ArraySize);
	printf("PPM file size  = %zu bytes\n", FileSize);

	
}



int end(){


  printf (" allways free memory (deallocate )  to avoid memory leaks \n"); // https://en.wikipedia.org/wiki/C_dynamic_memory_allocation
  free (data);
  info();
  return 0;

}



// ================================== main ============================================

int main (){

	setup();
	SaveArray2PPM(data, ElementSize, ArrayLength, 1);
	end();

	return 0;
}


Bardzo łatwo również utworzyć plik SVG[3]

/*  

c console program based on :
cpp code by Claudio Rocchini

http://commons.wikimedia.org/wiki/File:Poincare_halfplane_eptagonal_hb.svg


http://validator.w3.org/ 
The uploaded document "circle.svg" was successfully checked as SVG 1.1. 
This means that the resource in question identified itself as "SVG 1.1" 
and that we successfully performed a formal validation using an SGML, HTML5 and/or XML 
Parser(s) (depending on the markup language used). 

*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>



const double PI = 3.1415926535897932384626433832795;

const int  iXmax = 1000,
           iYmax = 1000,
           radius=100,
           cx=200,
           cy=200;
const char *black="#FFFFFF", /* hexadecimal number as a string for svg color*/
           *white="#000000";
           
 FILE * fp;

void draw_circle(FILE * FileP,int radius,int cx,int cy)
{
    fprintf(FileP,"<circle cx=\"%f\" cy=\"%f\" r=\"%f\" style=\"stroke:%s; stroke-width:2; fill:%s\"/>\n",
    cx,cy,radius,white,black);
}

void beginSVG(

int main(){
    FILE * fp;
    char *filename="circle.svg";
    fp = fopen(filename,"w");
	char *comment = "<!-- sample comment in SVG file  \n can be multi-line -->";

	fprintf(fp,
		    "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
		    "%s \n "
           "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n"
           "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
           "<svg width=\"20cm\" height=\"20cm\" viewBox=\"0 0 %f %f \"\n"
           " xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n",
           comment,iXmax,iYmax);


	draw_circle(fp,radius,cx,cy);
	

	
    
    
    fprintf(fp,"</svg>\n");
	fclose(fp);
	printf(" file %s saved \n",filename ); 
	getchar();
	return 0;
}

Biblioteki

edytuj

make -C lib/gks GRDIR=/usr/local/gr make[1]: Entering directory '/home/a/gr/lib/gks' make -C ../.. Makedefs make[2]: Entering directory '/home/a/gr'

Building GR Framework
---------------------
          C: yes [cc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0]
        C++: yes [c++ (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0]
     Python:  no [not found]
      LaTeX: yes [version 3.141592653-2.6-1.40.22]
     dvipng:  no [not found]
        Qt4:  no [Qt4 API not found]
        Qt5: yes [version 5.15.3]
        Qt6:  no [Qt6 API not found]
  wxWidgets:  no [wx-config not found]
       GTK+:  no [gtk+-2.0 not found]
        X11: yes [/usr/X11]
        Xft:  no [disabled]
Ghostscript:  no [GS API not found]
       GLFW: yes [version 3.3.6]
     OpenGL: yes
        0MQ:  no [0MQ 3.x API not found]
     ffmpeg: yes [version 4.4.2-0ubuntu0.22.04.1]
      Cairo: yes [version 1.16.0]
    libtiff: yes [version 4.3.0]
        agg:  no [agg not found]

dislin

edytuj
cp -p -R /usr/local/dislin/examples $HOME
cd $HOME/examples
clink -c exa_c


Dodatkowe programy

  • dlink


/******************************************************************/
   /**                      G F 7 7 L I N K                         **/
   /**                                                              **/
   /** GF77LINK links Fortran 77 programs with the DISLIN library.  **/
   /**                                                              **/
   /** Command:    gf77link    [option]  [-r8]    main              **/
   /**                                                              **/
   /** option      is an optional  parameter  that can  have one of **/
   /**             the following values:                            **/
   /**        -c   for compiling programs before linking            **/
   /**        -r   for running programs after linking               **/
   /**        -a   for compiling, linking and running programs.     **/
   /**                                                              **/
   /** -r8         is an optional parameter for using the double    **/
   /**             precision library of DISLIN.                     **/
   /**                                                              **/
   /** main        is the name of the main program or may be in the **/
   /**             form   'main obj lib'   where obj is  a field of **/
   /**             object files and lib a  field of library  files. **/
   /**             Several files must be separated by blanks.       **/
   /**             A file  'main'  will be created after linking.   **/
   /**                                                              **/
   /** Example:    gf77link  -a  test mylib.a                       **/
   /** Version:    GFortran, Linux, Fortran 77                      **/
   /******************************************************************/


 
               #include <stdio.h>
               #include "dislin.h"
               main()
               {
                 disini ();
                 messag ("This is a test", 100, 100);
                 disfin ();
               }


gcc exa_c.c -L$DISLIN -ldislin -lm


Dodatki

  • sudo apt-get install xfonts-75dpi
  • sudo apt-get install xfonts-100dpi

liblot

edytuj

Biblioteka libplot[4] służy do tworzenia grafiki 2D, zarówno wektorowej jak i rastrowej. Przykłady użycia w C: [5]

  • libplot-dev
sudo apt install libplot-dev


Zobacz również:

pgplot

edytuj
  • ./makemake ~/Dokumenty/c/pgplot/pgplot5.2/pgplot linux f77_gcc
  • make cpg
  • ld -shared -o libcpgplot.so --whole-archive libcpgplot.a
  • PGPLOT_DIR="/usr/local/pgplot/"; export PGPLOT_DIR
  • C PGPLOT wrapper library = cpgplot



Przykład [6]

#include "cpgplot.h"
#include "math.h"

int main()
{
   int i;
   float xs[9], ys[9];
   float xr[101], yr[101];

   /* Compute numbers to be plotted. */

   for (i=0; i<101; i++) {
     xr[i] = 0.1*i;
     yr[i] = xr[i]*xr[i]*exp(-xr[i]);
   }
   for (i=0; i<9; i++) {
     xs[i] = i+1;
     ys[i] = xs[i]*xs[i]*exp(-xs[i]);
   }

   /* Open graphics device. */

   if (cpgopen("?") < 1)
     return 1;

   /* Define coordinate range of graph (0 < x < 10, 0 < y < 0.65),
      and draw axes. */

   cpgenv(0., 10., 0., 0.65, 0, 0);
   
   /* Label the axes (note use of \\u and \\d for raising exponent). */
   
   cpglab("x", "y", "PGPLOT Graph: y = x\\u2\\dexp(-x)");

   /*  Plot the line graph. */
    
   cpgline(101, xr, yr);

   /* Plot symbols at selected points. */

   cpgpt(9, xs, ys, 18);

   /* Close the graphics device */

   cpgclos();
   return 0;
}

Zewnętrzne programy

edytuj

Gnuplot

edytuj

Gnuplot

#!/bin/bash

# http://mathr.co.uk/blog/
creal=0.258385930
cimag=0.00148483651
rot=34
den=$(echo "2^${rot}-1" | bc)
for i in $(seq 0 $((rot-1)))
do
  num=$(echo "2^${i}" | bc)
  ./julia-exray "${creal}" "${cimag}" "${num}/${den}"
  echo
  echo
done > "${rot}.txt"
gnuplot <<EOF
set terminal png size 1024,1024
set output "${rot}.png"
plot [-0.2:1] [-0.2:1] "${rot}.txt" using 1:2:(column(-2)%8) with lines lc variable title "quadratic Julia set external rays at 2^k/(2^${rot}-1) for c = ${creal} + ${cimag} i"
EOF

Plik z danymi

edytuj
Przygotowanie pliku z danymi
edytuj

Ten program tworzy w swoim katalogu roboczym plik tekstowy data.txt zawierający dane w formacie, który akceptuje gnuplot. Zawiera nagłówek poprzedzony znakiem "#", który przy rysowaniu jest ignorowany, oraz 2 kolumny liczb rozdzielone spacjami.

#include <stdio.h>
#include <stdlib.h>
int main(void) {
  int i;
  double x,y;
  char *output_filename="data.txt";
  FILE *output_file;
  output_file = fopen(output_filename, "w");  
  if (output_file  == NULL) {
    fprintf(stderr, "Nie moge otworzyc %s\n", output_filename);
    getchar();
    return 1;

  } else {
    /* nagłówek */
    fprintf(output_file,"%s %s      %s \n","#","x","y");
    y = 0.005;
    x = 0.0;
    /* 2 kolumny liczb rozdzielone spacjami */
    fprintf(output_file,"% 6.2f % 6.2f \n",x,y);
    for(i=0;i<5;++i) {
      x += y;
      /* 2 kolumny liczb rozdzielone spacjami */
      fprintf(output_file,"% 6.2f % 6.2f \n",x,y);
    }; /* for(i */
  }; /* if ((output_file ... else */
  fclose(output_file);

  fprintf(stderr,"file saved");
  getchar();
  
  
  return 0;
}

Zawartość pliku wygląda następująco:

# x      y 
 0.00   0.01 
 0.01   0.01 
 0.01   0.01 
 0.01   0.01 
 0.02   0.01 
 0.03   0.01
Rysowanie danych z pliku
edytuj
 
Przykładowy wykres zawierający dane z pliku oraz wykresy dwóch funkcji

Uruchom gnuplot:

gnuplot

i w linii komend wprowadź polecenie:

plot "data.txt" 

W cudzysłowie jest nazwa pliku, może być poprzedzona ścieżką.

Plotutils

edytuj

Pakiet Plotutils [7][8][9] korzysta z biblioteki libplot.

Obrazy utworzone z użyciem plotutils )( kod na stronie):


Instalacja

  • sudo apt install plotutils

Przygotowanie plików z danymi

edytuj
int SaveFiles(double A[Length][2], int p)
{

  int p;
  int i;
  char name [30]; /* name of the file */
  char *filename;
  FILE * fp;

 // save ray from A array to the text file
 sprintf(name,"Ray%d", p); /* create name from number */
 filename =strcat(name,".dat");
 fp= fopen(filename,"wb"); /*create new file,give it a name and open it in binary mode  */
 fprintf(fp ,"%s %s      %s \n","#","x","y");
 for (i=0; i<Length ; ++i) fprintf(fp ,"% 1.12f % 1.12f \n",A[i][0],A[i][1]);
 fclose(fp);
 printf("file %s  saved \n", filename);
  
 return 0;
}


bezpieczniej :

char name [100]; /* name of file */
  snprintf(name, sizeof name, "%d", k); /*  */
  char *filename =strncat(name,".ppm", 4);

Tworzenie grafiki

edytuj

Wykonanie jednego rysunku z jednego pliku:

graph -T svg < inter.dat > inter.svg

Wykonanie jednego rysunku (do pliku all.png) z kilku plików z rozszerzeniem dat:

 graph -T png *.dat> all.png

lub poprzez podanie nazw:

graph -T png Ray0.dat Ray1.dat Ray2.dat Ray3.dat > all.png

Standardowo każdy zbiór danych jest rysowany w innym stylu (ang. line mode), np. pierwszy w stylu nr 1, drugi w stylu nr 2 itd. Jeśli chcemy to wyłączyć to użyjemy opcji B (--toggle-auto-bump) wyłączającej autoinkrementację:

graph -T png -B -m 1 *.dat> all.png

Operacje matrycowe

edytuj

GPU shaders ( shader to program który działa na GPU)

  • pixel shader (2D graphic)
  • vertex (3D graphic)
  • geometry ( 3D graphic)
  • tessellation ( 3D graphic )
  • compute ( GPGPU ) Moduły cieniujące obliczeniowe (ang. compute shaders) są to programy działające na GPU poza normalnym potokiem renderowania


GPGPU [13]

  • OpenCL : stagnacja, słabe wsparcie dostawców
  • CUDA : doskonałe narzędzia, powszechna wiedza, ale tylko sprzęt Nvidia
  • Compute shaders: okropne narzędzia, skąpa wiedza, ale działa na wszystkich obecnych kartach graficznych
    • Różnorodność API: Vulkan, DX12, Metal, WebGPU obiecujące, ale jeszcze nie gotowe



OpenGl

edytuj

OpenGL

Cg czyli C dla grafiki


Główny program dla CPU jest napisany w C. Ten program otwiera i uruchamia program w Cg (dla GPU).[14][15]


Potrzebne oprogramowanie:

  • edytor tekstu
  • Cg Toolkit
    • kompilator Cg (cgc.exe)
    • Cg/CgFX biblioteki dla graficznego API (OpenGL lub Direct X)
  • kompilator C, np. gcc

Etapy:

  • Cg / GPU
    • utworzenie kodu Cg w postaci osobnego pliku , np. Fragment.cg
    • prekompilacja kodu Cg do assemblera specyficznego dla danego GPU i sterownika
  • C / CPU
    • utworzenie programu C w postaci osobnego pliku, np. main.c
      • dołączenie bibliotek Cg [15]
      • wczytanie podprogramu napisanego w Cg [16]
    • skompilowanie programu
    • uruchomienie programu

Biblioteki Cg dołączamy poprzez umieszczenie w programie napisanym w C następującego kodu:

#include <cg\cg.h> // Cg Header
#include <cg\cggl.h> // Cg OpenGL Specific Header

Podręcznik Gtk+

Przykładowy kod w języku C wyświetlający puste okienko:

#include <gtk/gtk.h>

int main(int argc, char* argv[])
{
  GtkWidget* window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "GTK+");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  gtk_widget_show(window);

  g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

  gtk_main();

  return 0;
}

Qt

#include <QtGui>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel label("Hello, world!");
    label.show();
    return app.exec();
}

Opis kompilacji

Glut. Przykład: [21]

#include<GL/glut.h> 

void main(int argc, char**argv) { 
    glutInit(&argc, argv); 
    glutInitWindowPosition(100,100); 
    glutInitWindowSize(500,500); 
    glutCreateWindow(“Hello World”); 
    glutMainLoop(); 
}

Odnośniki

edytuj
  1. unix.stackexchange question: how-to-draw-a-single-pixel-in-a-terminal
  2. PBMPAK is a collection of C routines for creating and reading Portable Bit Map files (PBM).
  3. Tworzenie pliku SVG - Claudio Rocchini
  4. libplot, a 2-D Vector Graphics Library
  5. Programowanie w c z użyciem biblioteki libplot
  6. PGPLOT Examples
  7. Pakiet Plotutils
  8. Fossies Dox: plotutils-2.6.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)
  9. Popularity contest statistics for plotutils
  10. Using GNU plotutils for your assignments by Luke Hutchison
  11. Plot coordinate pairs - rosettacode
  12. GSL example program
  13. Compute shader 101 by Raph Levien
  14. Cg Bumpmapping by Razvan Surdulescu at GameDev
  15. 15,0 15,1 Lesson: 47 from NeHe Productions
  16. Cg & HLSL Shading Language FAQ by Fusion Industries
  17. cgui - a library for making graphical user interfaces (GUI) by Christer Sandberg
  18. 2D Graphics Using C in Linux by Anthoniraj Amalan
  19. Graphics.h in Linux for c/c++ 02/29/2012 ~ Mandeep Simak
  20. IUP is a multi-platform toolkit for building graphical user interfaces. It offers a simple API in three basic languages: C, Lua and LED.
  21. Graphics Programming in Linux