[ Назад ] [ Содержание ] [ Далее ]

Работа с цветами

    При работе с цветовой палитрой самой большой проблемой являтся то, что на разных типах аппаратного обеспечения работа цветной графикой осуществляется различными путями. Различия заключаются в количестве разрядов, отводимых для описания цветов одной точки, что ведет к различию количества цветовых оттенков, одновременно отображамых на экране. Но это не самая большая проблема.

     Наиболее существенны различия в способах формирования цветовой палитры. В графических адаптерах, у которых количество разрядов на одну точку невелико, применяют динамическую палитру, загружаемую в адаптер. В этом случае содержимое ячеек видеопамяти является индексом в этой самой динамической палитре. Это позволяет значительно расширить диапазон используемых цветов, но с ограничем их одновременного показа на экране. В этом режиме работа с полноцветными изображениями весьма затруднительна - программе всегда необходимо вести пересчет информации о цветах, чтобы минимизировать потери при переполнении палитры. Приложение может оперировать полноцветной информацией в памяти, а вывод на экран производить в индексированном виде, но тогда будут присутствовать большие потери процессорного времени на преобразование из полноцветного формата в индексированный.

     Если количество разрядов на одну точку значительное, применяют статическую палитру, в которой разряды разбиваются на три группы, образуя три числа, представляющих значения яркости красного, зелёного и синего каналов. Это наиболее простой и удобный формат отображения информации. Благодаря техническому прогрессу, этот режим является основным на практически всех современных компьютерах.

     Xlib разработана таким образом, чтобы максимально сгладить различия между этими принципиально разными подходами формирования цветовой гаммы путем использования во всех режимах индексного способа обращения к цветам. В итоге большей части программ неважно, в каком режиме работает графический адаптер. Однако, для приложений, работающих с полноцветной графикой такой подход не является оптимальным и для них оставлен механизм более быстрой работы с цветами для режимов графического адаптера со статической RGB палитрой. Для этого в библиотеке Xlib есть специальные функции для получения режима работы графического адаптера, чтобы программа могла определить возможность оптимизации своей работы.

     Совокупность характеристик графического адаптера, которая доступна программе, называется визуальная характеристика адаптера или просто визуальный тип (visual type).

    Режим формирования палитры называется визуальным классом. В библиотеке Xlib определено несколько визуальных классов:

    Вне зависимости от режима работы палитры, все функции работы с графикой в качестве указателя значения цвета используют тип unsigned long, который вне зависимости от визуального класса используется как индекс палитры. Однако для классов DirectColor и TrueColor этот индекс формируется особенным образом, позволяющим легко преобразовывать значения RGB в индекс палитры.

    Цветовые палитры не привязываются к экрану. У каждого окна может быть своя палитра. Но при этом, если графический адаптер работает в режиме с динамической палитрой, в графический адаптер загружается палитра только из активного окна. Цветовая информация в других окнах при этом может искажаться, что выглядит довольно неприглядно. Если графический адаптер работает со статической палитрой, то такого эффекта не будет. Одним из способов минимизировать этот эффект - использовать одну и ту же палитру для как можно большего количества окон.

     Также одновременно на экране можно использовать разные визуальные типы для разных окон. Поэтому программа перед началом работы может запросить список поддерживаемых экраном визуальных типов и выбрать наиболее предпочтительный.

    Для этого в библиотеке Xlib есть функции для получения визуальных характеристик: XGetVisualInfo и XMatchVisualInfo.

    Программы, которым для работы не требуется какой-то особенный визуальный тип, могут просто использовать тот, который предоставляется по-умолчанию через макроопределение DefaultVisual. Приложения, работающий с полноцветными изображениями могут запросить визуальный класс TrueColor и, в случае если его нет, либо просто отказаться работать, либо попытаться работать с другим визуальным классом, но для этого придется разработать отдельную ветку подпрограмм вывода для работы в универсальном режиме с индексами или подстраиваться каким-либо образом под другие визуальные классы.

    Поскольку в то время, когда происходило проектирование системы X-Window, преобладали графические адаптеры с динамической загружаемой палитрой, в библиотеке Xlib представлен богатый набор функций для работы с палитрами. В терминах билиотеки Xlib цветовая палитра называется цветовой картой (colormap).

    При работе с палитрами также надо учитывать, что она хранится на X-сервере и на любое обращение к ней требуется много времени, поэтому более предпочтительно использовать групповые операции работы с элементами палитры.

     Поскольку палитра хранится на X-сервере, то программе доступен только её идентификатор. Тип идентификатора палитры называется Colormap. Приложение может использовать палитру по-умолчанию, которую можно получить при помощи макроопредения DefaultColormap, а может создать для окна свою собственную, новую палитру. Второе скорее всего будет необходимо в том случае, если графический адаптер работает в режиме с динамической палитрой и приложению нужна чистая палитра, чтобы разместить в ней только свои цвета. Создание палитры также может потребоваться в том случае, если дальнейшая работа будет идти с другим визуальным типом, нежели предлагаемым по-умолчанию.

     Перечень некоторых функций библиотеки Xlib для работы с палитрами:

Пример ex004.c:
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <math.h>

#define pi 3.1415
#define childNum 16   /* Количество дочерних окон в хороводе */

/* Эта функция по точке в центре, радиусу и углу */
/* вычисляет точку на периметре круга            */
void calcXY(int cx, int cy, int rd, int ang, int *wx, int *wy)
 {
  *wx=cx+(int) (cos((float)ang*pi/180.0)*(float)rd);
  *wy=cy-(int) (sin((float)ang*pi/180.0)*(float)rd);
 };

/* Подпрограмма вывода списка визуальных типов */
void listVisuals(XVisualInfo *firstVisual, int nitems)
 {
   int i,j;
   
   if (firstVisual==NULL)
     {
      printf("Визуальные типы не найдены.\n");
     }
    else
     {
      printf("Найдено визуальных типов: %d.\n",nitems);
      for(i=0; i<nitems; i++)
       {
        printf("%d. Глубина цветности: %d, Класс: ",i,firstVisual[i].depth);
        switch (firstVisual[i].c_class)
	 {
	  case StaticGray: printf("StaticGray"); break;
	  case StaticColor: printf("StaticColor"); break;
	  case TrueColor: printf("TrueColor"); break;
	  case GrayScale: printf("GrayScale"); break;
	  case PseudoColor: printf("PseudoColor"); break;
	  case DirectColor: printf("DirectColor"); break;
          default: printf("Неизвестен"); break;
	 };
        printf(".\n");
       };      
     };
 };

int main(int argc, char **argv)
 {
  Display *ourDisplay;
  int ourScreen;
  Colormap myColormap;
  int myDepth;
  Window rootWindow;
  Window  myWindow;                    /* Рабочее окно верхнего уровня */
  Window childWindows[childNum];       /* Список дочерних окон         */
  unsigned long childColors[childNum]; /* Набор фоновых цветов         */
  XColor theColor;
  XSetWindowAttributes myAttr;
  Visual *myVisual;
  XVisualInfo *listOfVisuals, templateVisual;
  XSizeHints mySizeHints;
  XClassHint myClassHint;
  int numberOfVisuals;
  int i,j,wx,wy;
  char *colorNames[]={"black","dark blue","dark red","dark magenta",
                      "dark green","dark cyan","brown","dim gray",
		      "gray","blue","red","magenta","green","cyan",
		      "yellow","white"};
  
  ourDisplay=XOpenDisplay(NULL);
  if (ourDisplay==NULL)
    {
      printf("Не удалось установить соединение с графическим терминалом.\n");
      return 1;
    };

  /* Получим предварительные сведения */
  ourScreen=DefaultScreen(ourDisplay);           /* Экран по-умолчанию        */
  rootWindow=RootWindow(ourDisplay, ourScreen);  /* Корневое окно             */
  myDepth=DefaultDepth(ourDisplay, ourScreen);   /* Глубина цветности экрана  */
  myVisual=DefaultVisual(ourDisplay, ourScreen); /* Визуальные характеристики */
  myColormap=DefaultColormap(ourDisplay, ourScreen); /* Цветовая палитра */

  /* Выведем список всех визуальных типов */
  listOfVisuals=XGetVisualInfo(ourDisplay, VisualNoMask, &templateVisual,
   &numberOfVisuals);
  listVisuals(listOfVisuals,numberOfVisuals);
  if (listOfVisuals!=NULL)
    XFree(listOfVisuals);
  
  /* Выбираем фон для окна верхнего уровня */
  myAttr.background_pixel=WhitePixel(ourDisplay, ourScreen);

  /* Создаем окно верхнего уровня */
  myWindow=XCreateWindow(ourDisplay,rootWindow,100, 100, 320, 300,
    0, myDepth, InputOutput, myVisual, CWBackPixel, &myAttr);
    
  /* Устанавливаем заголовок окна */
  XStoreName(ourDisplay,myWindow,"Color example window");

  /* Устанавливаем заголовок иконки */
  XSetIconName(ourDisplay,myWindow,"example");

  /* Устанавливаем ограничения на размеры окна */
  mySizeHints.flags=PMinSize | PMaxSize;
  mySizeHints.min_width=320; mySizeHints.min_height=300;
  mySizeHints.max_width=320; mySizeHints.max_height=300;
  XSetWMNormalHints(ourDisplay, myWindow, &mySizeHints);

  /* Делаем окна видимыми */
  XMapWindow(ourDisplay, myWindow);

  /* Зададим цвет бордюра для дочерних окон */
  myAttr.border_pixel=BlackPixel(ourDisplay, ourScreen);

  /* Создадим второму окну набор дочерних окон, расположенных по кругу */
  for (i=0; i<childNum; i++)
   {
    /* Вычисляем координаты дочернего окна */
    calcXY(160,150,110,i*(320/childNum),&wx,&wy);

    /* Выделяем цвет для окна */
    if (XAllocNamedColor(ourDisplay,myColormap,colorNames[i&15],
                             &theColor,&theColor)!=0)
      {
       childColors[i]=theColor.pixel;
      }
     else
      {
       /* Аварийно выходим из программы */
       printf("Не удалось получить цвета для дочерних окон.\n");
       if (i>0)
        XFreeColors(ourDisplay,myColormap,childColors,i,0);
       XDestroyWindow(ourDisplay, myWindow);
       XCloseDisplay(ourDisplay);
       return 2;
      };

    myAttr.background_pixel=childColors[i];
    childWindows[i]=XCreateWindow(ourDisplay,myWindow,wx-40,wy-30,80, 60,
                 2, myDepth, InputOutput, myVisual,
		 CWBackPixel | CWBorderPixel, &myAttr);
   };

  /* Включаем дочерние окна */
  XMapSubwindows(ourDisplay,myWindow);

  XFlush(ourDisplay);
  sleep(1);
  
  /* Устраиваем хоровод из дочерних окон на 3 круга */
  for (j=1080; j>0; )
   {
    for (i=0; i<childNum; i++)
     {
      calcXY(160,150,110,i*(320/childNum)+j,&wx,&wy);
      XMoveWindow(ourDisplay,childWindows[i],wx-40,wy-30);
     };

    XFlush(ourDisplay);
    usleep(20);
    j-=2;
   };

  XFlush(ourDisplay);
  sleep(1);

  /* Развернем окна в обратном порядке */
  XRestackWindows(ourDisplay,childWindows,childNum);  

  XFlush(ourDisplay);
  sleep(2);

  /* Хоровод движется в другую сторону... */
  for (j=0; j<1080; )
   {
    for (i=0; i<childNum; i++)
     {
      calcXY(160,150,110,i*(320/childNum)+j,&wx,&wy);
      XMoveWindow(ourDisplay,childWindows[i],wx-40,wy-30);
     };
    XFlush(ourDisplay);
    usleep(20);
    j+=2;
   };

  /* Освобождаем выделенные цвета */
  XFreeColors(ourDisplay, myColormap,childColors,childNum,0);

  /* Уничтожаем окно */
  XDestroyWindow(ourDisplay, myWindow);

  /* Закрываем соединение с сервером */
  XCloseDisplay(ourDisplay);

  return 0;
 };

[ Назад ] [ Содержание ] [ Далее ]