Класс
Mark
всего лишь позволяет легко создать объект класса
Marks
с единственной точкой, помеченной единственным символом. Стоило ли тратить силы, чтобы определять такой класс? Или он является следствием “ложного стремления к усложнениям и недоразумениям”? Однозначного и логичного ответа на этот вопрос нет. Мы много думали над этим и в конце концов решили, что для пользователей этот класс был бы полезен, а определить его было совсем нетрудно.
Почему в качестве метки используется символ? Можно было бы нарисовать любую маленькую фигуру, но символы нагляднее и проще. Они часто позволяют отделить одно множество точек от другого. К тому же такие символы, как
x
,
o
,
+
и
*
, обладают центральной симметрией.
13.17. Класс Image
Файлы в типичном персональном компьютере хранят тысячи изображений. Кроме того, миллионы изображений доступны в сети веб. Естественно, мы хотели бы отображать содержимое этих файлов на экране с помощью относительно простых программ. Например, ниже продемонстрирован рисунок (
rita_path.gif
), иллюстрирующий путь урагана “Рита”, пришедшего из Мексиканского залива.
Мы можем выбрать часть этого изображения и добавить фотографию урагана, сделанную из космоса (
rita.jpg
).
Image rita(Point(0,0),"rita.jpg");
Image path(Point(0,0),"rita_path.gif");
path.set_mask(Point(50,250),600,400); // выбираем желательную область
win.attach(path);
win.attach(rita);
Операция
set_mask()
выбирает часть рисунка, которую следует изобразить на экране. В данном случае мы выбрали изображение размером 600×400 пикселей из файла
rita_path.gif
(загруженный как объект
path
) и показали его в области, левый верхний угол которой имеет координаты (50,250). Выбор части рисунка — довольно распространенный прием, поэтому мы предусмотрели для него отдельную операцию.
Фигуры изображаются одна поверх другой, подобно листам бумаги, в порядке их добавления на экран. По этой причине объект
path
оказался на самом “дне”, просто потому, что он был связан с окном до объекта
rita
. Изображения могут кодироваться во множестве форматов. Здесь мы используем только два из них: JPEG и GIF.
struct Suffix {
enum Encoding { none, jpg, gif };
};
В нашей библиотеке графического интерфейса изображение в памяти представляется как объект класса
Image
.
struct Image:Shape {
Image(Point xy, string file_name,
Suffix::Encoding e = Suffix::none);
~Image() { delete p; }
void draw_lines() const;
void set_mask(Point xy, int ww, int hh)
{ w=ww; h=hh; cx=xy.x; cy=xy.y; }
private:
int w,h; // определяем "маскировочное окно" внутри изображения
// по отношению к позиции (cx,cy)
int cx,cy;
Fl_Image* p;
Text fn;
};
Конструктор класса
Image
пытается открыть файл с указанным именем, затем создать рисунок, используя кодировку, указанную в дополнительном аргументе или (как правило) в расширении файла. Если изображение невозможно вывести на экран (например, потому, что файл не найден), класс
Image
выводит на экран объект
Bad_image
. Определение класса
Bad_image
выглядит так:
struct Bad_image:Fl_Image {
Bad_image(int h, int w):Fl_Image(h,w,0) { }
void draw(int x,int y, int, int, int, int) { draw_empty(x,y);
}
};
Работа с изображениями в графической библиотеке довольно сложна, но основная сложность класса
Image
кроется в файле, который обрабатывает его конструктор.
// более сложный конструктор, потому что ошибки,
// связанные с графическими файлами, трудно найти
Image::Image(Point xy, string s, Suffix::Encoding e)
:w(0), h(0), fn(xy,"")
{
add(xy);
if (!can_open(s)) { // можно ли открыть файл s?
fn.set_label("Невозможно открыть \""+s+" ");
p = new Bad_image(30,20); // ошибка графики
return;
}
if (e == Suffix::none) e = get_encoding(s);
switch(e) { // проверка кодировки
case Suffix::jpg:
p = new Fl_JPEG_Image(s.c_str());
break;
case Suffix::gif:
p = new Fl_GIF_Image(s.c_str());
break;
default: // неприемлемая кодировка
fn.set_label("Неприемлемый тип файла \""+s+" ");
p = new Bad_image(30,20); // ошибка графики
}
}