Различия между версиями 4 и 6 (по 2 версиям)
Версия 4 от 2016-11-10 12:02:40
Размер: 9901
Редактор: FrBrGeorge
Комментарий:
Версия 6 от 2016-11-10 15:01:30
Размер: 16798
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 25: Строка 25:
=== Байты и договоренности === === Просто строки ===
Строка 84: Строка 84:

'''TODO''' [[RW:X_Pixmap|X Pixmap]]
Ещё одна особенность XPM — «значимые» имена цветов (задаются в форме «s '''имя'''»); в примере значимые имена даны всем цветам, кроме красного и чёрного. Соответствующая библиотека (`libxpm`) позволяет программам работать с этими значащими именами вместо непосредственно символов, так что нет необходимости пользоваться ровно теми символами, которые заданы в палитре. Например, при разработке графического интерфейса можно выделить цвет фона, цвет печати, цвета обычной, затенённой и высветленной областей границы и т. п. «Кнопочка» будет нарисована независимо от того, какая в действительности задана палитра. Но и без библиотеки такие имена полезны — они указывают на назначение цвета. При описании цвета можно ещё задавать, как отображать точки этого цвета в монохромном и чёрно-белом пространстве (когда-то давно это было насущной необходимостью, т. к. далеко не все графические устройства были цветными).

Одно из имён цвета — «`None`» — особенное. Это не значащее имя, а именно задание цвета (значащее имя для цвета None в примере — `Transparent`). При обработке изображения точки цвета `None` считаются прозрачными, выводятся и обрабатываются соответственно.

Дальнейшее обсуждение формата выходит за рамки интересного, можно поизучать [[https://www.opennet.ru/docs/formats/xpm.pdf|соответствующую документацию]].

=== Программа на Си ===

Рассмотрим [[attachment:xpmex.c|простую учебную программу]] на языке Си. В программе
 * XPM-файл с картинкой просто включается в исходный текст (он же тоже написан на Си!),
 * затем создаётся доступная на запись копия (заметим, что исходный код имеет тип `const char **`),
 * в этой копии модифицируется содержимое растровой матрицы,
 * результат записывается в новый XPM-файл (формат XPM при этом соблюдается вручную, это несложно :) )
{{{#!highlight c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "face.xpm"

int main(int argc, char *argv[]) {
  int Width, Height, PSize, BPP;
  int size;
  int beg, end, y, x, len;
  char **newface;
  FILE *fout;

  /* Извлекаем размеры картинки и палитры */
  /* ВНИМАНИЕ! Для простоты считаем, что BPP=1 */
  sscanf(face[0], "%d %d %d %d", &Width, &Height, &PSize, &BPP);

  /* Область картинки */
  beg = PSize+1;
  end = PSize+Height+1;

  /* Копия картинки, доступная для модификации */
  newface = calloc(end, sizeof(char *));
  for(y=size=0; y<end; y++) {
    len = strlen(face[y]);
    strcpy(newface[y]=malloc(len+1), face[y]);
  }

  /* Удаляем все '*' (красные точки) */
  for(y=beg; y<end; y++)
    for(x=0; x<Width; x++)
      if(newface[y][x] == '*')
 newface[y][x] = ' ';

  /* Посмотрим на безротую картинку */
  for(y=beg; y<end; y++)
    puts(newface[y]);

  /* Перевернём рот. ВНИМАНИЕ! Мы заранее знали, в каких он строках */
  for(y=0; y<4; y++)
    for(x=0; x<Width; x++)
      if(face[end-8+y][x] == '*')
 newface[end-7-y][x] = '*';

  /* Создадим XPM-файл. ВНИМАНИЕ! Индентификатор картинки задан заранее */
  fout = fopen("sadface.xpm","w");
  fputs("/* XPM */",fout);
  fputs("static char * sadface[] = {",fout);
  for(y=0; y<end-1; y++)
    fprintf(fout,"\"%s\",\n",newface[y]);
  fprintf(fout,"\"%s\"};\n",newface[y]);
  fclose(fout);

  return 0;
}
}}}

Обратите внимание на то, что программа несложная, однако затрагивает сразу несколько тем:
 * работа со строками (включая преобразование числовых форматов)
 * работа с памятью и указателями
 * работа с файлами
 * работа с растровой графикой :)

Вот [[attachment:sadface.xpm|такой файл]] получается в результате:
{{{#!highlight c
/* XPM */static char * sadface[] = {"32 32 5 1",
" c none s mask",
"# c #55aad4 s HiShadow",
"* c #ff0000",
"+ c #4488aa s Foreground",
". c #33667f s LoShadow",
" ",
" ......... ",
" .... .... ",
" .. .. ",
" ... ... ",
" .. .. ",
" .. .# ",
" . ### ### # ",
" .. ## ## ## ## ## ",
" .. # . # . ## ",
" . .. .. .. .. # ",
" . ... ... # ",
" .. ##",
" . #",
" . #",
" . #",
" . . #",
" . #",
" . #",
" . #",
" .. ##",
" . # ",
" . *********** # ",
" .. ** ** ## ",
" .. *** *** ## ",
" . ** ** # ",
" .# ## ",
" ## ## ",
" ### ### ",
" ## ## ",
" #### #### ",
" ######### "};
}}}


=== Командный интерфейс (намёк) ===
Если есть интерес изучать
'''TODO''' convert: ограничение цветов

XPM: растровое изображение своими руками

Возьмём какую-нибудь тему из школьной информатики. Например, изучение растровой графики.

О чём по этой теме стоит разговаривать?

  • О матричном представлении изображения, в том числе — о том, что одной только матрицы недостаточно: размеры, способ кодирования цвета, другая дополнительная информация
  • О кодировании цветов и цветовых пространствах (как минимум — RGB, CMYK и HLS/HLV)
  • Об упаковке изображения и об упаковке «с потерями»
  • О проблемах геометрических преобразований

Ставить точки руками или обрабатывать области магией?

Среди этих тем некоторые — явно выше того, что можно в школе объяснить до конца (та же упаковка с потерями, например). Некоторые темы можно осветить только поверхностно. Можно запустить любой приличный редактор растровой графики и показать диалог выбора цвета в различных цветовых пространствах — какой при изменении параметров получается в результате цвет. Однако объяснять, почему цветовые пространства именно такие, придётся всё равно «на пальцах» (колбочки, палочки, складывание цветов, вычитание, а на освещённости/светлоте начинается уже математика).

colorpick.jpg

Диалог выбора цвета в редакторе GIMP. Довольно поучительно начать изменять один параметр из пространства HSV и пронаблюдать, как движется «прицел» и вращается маркер цвета.

Геометрические преобразования стоит исследовать уже после близкого знакомства с предыдущими темами.

А вот теме «матричное представление изображения», как это ни странно, не хватает программной наглядности. С одной стороны, имеется растровый редактор, в котором всё вполне наглядно, своими руками делается. А с другой стороны, со стороны программиста — разнообразные прекрасные библиотеки для работы с разнообразными прекрасными растровыми форматами.

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

Просто строки

Чего не хватает — это наглядного представления изображения в виде двумерного массива байтов и сопутствующей информации. Например, в «проекте рисования графика» мы использовал для этой цели… двумерный массив байтов! Более точно — массив строк, каждый символ которых соответствует отдельной точке «экрана». Обновление такого «экрана» — просто вывод всех строк на текстовый терминал.

Довольно интересный компромисс в этом плане — формат X Pixmap.

Вот, например, маленькая картинка в формате xpm: rbomb.png А вот тот же самый файл непосредственно:

   1 static char * rbomb_xpm[] = {
   2 /* width height ncolors chars_per_pixel */
   3 "32 32 5 1",
   4 /* colors */
   5 "       c None          s Transparent",
   6 "o      c gray50        s Cord",
   7 "O      c black",
   8 "$      c red",
   9 "%      c #D0D0A0       s Gold",
  10 /* pixels */
  11 "                                ",
  12 "                                ",
  13 "                                ",
  14 "                                ",
  15 "                                ",
  16 "                                ",
  17 "                                ",
  18 "                                ",
  19 "           oOoOo                ",
  20 "          O     Oo              ",
  21 "         o        O             ",
  22 "         O        o             ",
  23 "       OOOOO       O            ",
  24 "       OOOOO       o            ",
  25 "      OOOOOOO       O           ",
  26 "    oOOOOOOOOOo     o  $       $",
  27 "   oOOOOOOOOOOOo    O      $  $ ",
  28 "  oOOOOOOOOOOOOOo    o  $       ",
  29 "  OOOOOOOO%ooOOOO    O   $  $   ",
  30 " oOOOOOOOOO%ooOOOo    o   $ $   ",
  31 " OOOOOOOOOOOOOOOOO     OoO $ $ $",
  32 " OOOOOOOOOOO%ooOOO        $$    ",
  33 " OOOOOOOOOOO%ooOOO      $   $   ",
  34 " OOOOOOOOOOO%ooOOO        $  $  ",
  35 " OOOOOOOOOOOOOOOOO     $        ",
  36 " oOOOOOOOOOOOOOOOo    $   $    $",
  37 "  OOOOOOOOOOOOOOO               ",
  38 "  oOOOOOOOOOOOOOo         $     ",
  39 "   oOOOOOOOOOOOo                ",
  40 "    oOOOOOOOOOo                 ",
  41 "      oOOOOOo                   ",
  42 "                                "};

То есть сам формат — строго текстовый. Зная структуру XPM-файла, его несложно создать, и ещё проще — редактировать в произвольном текстовом редакторе.

Нетрудно заметить, что XPM-файл — это просто массив строк, записанный на языке программирования Си. Первая строка массива содержит (в строковом виде) метаинформацию: ширину и высоту картинки в точках растра, количество цветов в палитре и «количество символов на цвет». Вот этот последний параметр особенно забавен: дело в том, что последние строки XPM-файла представляют собственно изображение, причём одна точка растра соответствует фиксированному числу символов (удобнее всего, когда одному). При этом сами символы задаются произвольно в палитре, начинающейся со второй строки массива. В частности, в примере чёрному цвету соответствует символ 'O', а красному — '$'.

Цвета в палитре можно задавать в пространстве RBG (24 бита на точку), как это сделано для золотистого цвета, а можно по именам. Имена цветов X.Org (графической подсистеме, используемой в большинстве дистрибутивов Linux) заданы примерно в том же формате в файле /usr/share/X11/rgb.txt. Среди них встречаются весьма поэтично названные, например LavenderBlush или PeachPuff.

Ещё одна особенность XPM — «значимые» имена цветов (задаются в форме «s имя»); в примере значимые имена даны всем цветам, кроме красного и чёрного. Соответствующая библиотека (libxpm) позволяет программам работать с этими значащими именами вместо непосредственно символов, так что нет необходимости пользоваться ровно теми символами, которые заданы в палитре. Например, при разработке графического интерфейса можно выделить цвет фона, цвет печати, цвета обычной, затенённой и высветленной областей границы и т. п. «Кнопочка» будет нарисована независимо от того, какая в действительности задана палитра. Но и без библиотеки такие имена полезны — они указывают на назначение цвета. При описании цвета можно ещё задавать, как отображать точки этого цвета в монохромном и чёрно-белом пространстве (когда-то давно это было насущной необходимостью, т. к. далеко не все графические устройства были цветными).

Одно из имён цвета — «None» — особенное. Это не значащее имя, а именно задание цвета (значащее имя для цвета None в примере — Transparent). При обработке изображения точки цвета None считаются прозрачными, выводятся и обрабатываются соответственно.

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

Программа на Си

Рассмотрим простую учебную программу на языке Си. В программе

  • XPM-файл с картинкой просто включается в исходный текст (он же тоже написан на Си!),
  • затем создаётся доступная на запись копия (заметим, что исходный код имеет тип const char **),

  • в этой копии модифицируется содержимое растровой матрицы,
  • результат записывается в новый XPM-файл (формат XPM при этом соблюдается вручную, это несложно :) )

   1 #include <stdio.h>
   2 #include <string.h>
   3 #include <stdlib.h>
   4 #include "face.xpm"
   5 
   6 int main(int argc, char *argv[]) {
   7   int Width, Height, PSize, BPP;
   8   int size;
   9   int beg, end, y, x, len;
  10   char **newface;
  11   FILE *fout;
  12 
  13   /* Извлекаем размеры картинки и палитры */
  14   /* ВНИМАНИЕ! Для простоты считаем, что BPP=1 */
  15   sscanf(face[0], "%d %d %d %d", &Width, &Height, &PSize, &BPP);
  16 
  17   /* Область картинки */
  18   beg = PSize+1;
  19   end = PSize+Height+1;
  20 
  21   /* Копия картинки, доступная для модификации */
  22   newface = calloc(end, sizeof(char *));
  23   for(y=size=0; y<end; y++) {
  24     len = strlen(face[y]);
  25     strcpy(newface[y]=malloc(len+1), face[y]);
  26   }
  27 
  28   /* Удаляем все '*' (красные точки) */
  29   for(y=beg; y<end; y++) 
  30     for(x=0; x<Width; x++)
  31       if(newface[y][x] == '*')
  32         newface[y][x] = ' ';
  33 
  34   /* Посмотрим на безротую картинку */
  35   for(y=beg; y<end; y++)
  36     puts(newface[y]);
  37 
  38   /* Перевернём рот. ВНИМАНИЕ! Мы заранее знали, в каких он строках */
  39   for(y=0; y<4; y++)
  40     for(x=0; x<Width; x++)
  41       if(face[end-8+y][x] == '*')
  42         newface[end-7-y][x] = '*';
  43 
  44   /* Создадим XPM-файл. ВНИМАНИЕ! Индентификатор картинки задан заранее */
  45   fout = fopen("sadface.xpm","w");
  46   fputs("/* XPM */",fout);
  47   fputs("static char * sadface[] = {",fout);
  48   for(y=0; y<end-1; y++)
  49     fprintf(fout,"\"%s\",\n",newface[y]);
  50   fprintf(fout,"\"%s\"};\n",newface[y]);
  51   fclose(fout);
  52 
  53   return 0;
  54 }

Обратите внимание на то, что программа несложная, однако затрагивает сразу несколько тем:

  • работа со строками (включая преобразование числовых форматов)
  • работа с памятью и указателями
  • работа с файлами
  • работа с растровой графикой :)

Вот такой файл получается в результате:

   1 /* XPM */static char * sadface[] = {"32 32 5 1",
   2 "       c none s mask",
   3 "#      c #55aad4 s HiShadow",
   4 "*      c #ff0000",
   5 "+      c #4488aa s Foreground",
   6 ".      c #33667f s LoShadow",
   7 "                                ",
   8 "            .........           ",
   9 "         ....       ....        ",
  10 "        ..             ..       ",
  11 "      ...               ...     ",
  12 "     ..                   ..    ",
  13 "    ..                     .#   ",
  14 "    .   ###          ###    #   ",
  15 "   ..  ## ##        ## ##   ##  ",
  16 "  ..   #   .        #   .    ## ",
  17 "  .    .. ..        .. ..     # ",
  18 "  .     ...          ...      # ",
  19 " ..                           ##",
  20 " .                             #",
  21 " .                             #",
  22 " .                             #",
  23 " .              .              #",
  24 " .                             #",
  25 " .                             #",
  26 " .                             #",
  27 " ..                           ##",
  28 "  .                           # ",
  29 "  .        ***********        # ",
  30 "  ..      **         **      ## ",
  31 "   ..   ***           ***   ##  ",
  32 "    .  **               **  #   ",
  33 "    .#                     ##   ",
  34 "     ##                   ##    ",
  35 "      ###               ###     ",
  36 "        ##             ##       ",
  37 "         ####       ####        ",
  38 "            #########           "};

Командный интерфейс (намёк)

Если есть интерес изучать TODO convert: ограничение цветов

Что в итоге

При всё своей «игрушечности» формат XPM:

  • На самом деле никакой не игрушечный
  • Долгое время был чуть ли не де-факто стандартом на маленькие изображения в графической системе X.org
    • Правда, броузеры его показывать отказываются :(

  • Позволяет сразу зацепить несколько тем:
    • Растр как матрица точек + дополнительная информация

    • Представление цветов в пространстве RGB и именование
    • Палитрованое изображение
    • Абстракции «прозрачность», «цвет фона», «цвет рисования»
    • Работа с точками растра при преобразовании
  • Неплохо подходит для изучения в курсе программирования на Си :)

FrBrGeorge/LearnXPM (последним исправлял пользователь FrBrGeorge 2019-03-15 14:55:56)