содержание   вперед   назад

18. Резидентная программа обработки картинок

Описание работы программы

В этой главе я покажу программу обработки картинок, сделанную почти резидентной с использованием сложной графики. Как обычно, вместо обсуждения общих принципов я даю код полностью готовой программы и объясню как она написана. Для начала я предлагаю вам скачать, скомпилировать и запустить эту программу, которую я назвал vkSM.jar, что означает Slide Maker от Виктора Кона. Прежде всего скачиваем zip-архив программы кликая ЗДЕСЬ. Вынимаем (копируем) все файлы из архива в какую-нибудь папку на компьютере. Затем надо отредактировать файл _compile.bat и указать в нем точный путь к папке вашего компилятора java. После этого запускаем bat-файл на исполнение и получаем все нужные классы. При компиляции выдается сообщение, что код содержит deprecated методы, то есть устаревшие и не рекомендуемые к использованию, но пока все работает, а перестанет работать и появятся ошибки, тогда можно будет и исправить. Затем можно запустить саму программу на исполнение, кликая на иконке файла _run.bat. И вот тут начинаются чудеса.

Программа окно не показывает, но ее иконка появляется в статусной строке. На самом деле у программы окно есть, но оно очень маленькое и спрятано за статусной строкой. Так надо, потому что одной из функций программы является получение скриншота, то есть захват экрана компьютера в картинку. Далее из этой картинки можно вырезать фрагмент и спасти его в файл. А содержание экрана при этом рисуется с помощью других произвольных программ операционной системы. Есть еще и вторая функция: из разных фрагментов составить полную картинку заданного размера, вот она то и представляет собой слайд. То есть сначала нарезаем из разных источников фрагменты, а затем складываем эти фрагменты в том порядке как нам надо. Это самый прямой, простой и быстрый способ представить новую информацию. Крошечная программа фактически заменяет Power Point и ее аналоги для приготовления любых презентаций. Сами фрагменты можно приготовить в разных программах каких много.

Активируется программа простым кликом на ее иконке в статусной строке. После этого она реагирует на нажатие трех клавиш. Клавиша [Esc] позволяет закрыть программу совсем, то есть убрать из памяти. Клавиша [F1] запускает операцию скриншота. Клавиша [F2] запускает операцию создания слайда. Рассмотрим первую из них, то есть как делать скриншот. После нажатия клавиши [F1] программа захватывает весь экран и показывает его в виде картинки. Картинка показывается в окне с титульной строкой "Captured screen". Так как весь экран в окно не влезает, то даются линейки прокрутки. В нижней части под окном указываются размеры экрана в квадратных скобках и текущая позиция курсора мыши. Чтобы вырезать фрагмент картинки делаем так: ставим курсор мыши в нижний левый угол области фрагмента и нажимаем левую клавишу, затем не отпуская клавишу передвигаем курсор в правый верхний угол области. Область при этом отмечается черной рамкой. Когда область аккуратно выбрана, отпускаем клавишу. Выбранная область появляется в новом окне. Кликаем на ней и открывается окно менеджера файлов, в котором можно выбрать нужную папку и ввести имя файла. Нажимаем [OK] и вырезанная картинка спасается в выбранный файл.

В имени файла надо ставить расширения jpg или png, другие типы не поддерживаются. Проще всего скопировать в строку ввода уже имеющийся файл и отредактировать имя, изменив его на новое. Однако, способ вырезания мышью не всегда удобен, так как мышь дрожит и вырезать картинку с заранее заданными размерами очень сложно. Поэтому есть второй способ. Когда изображение всего экрана показывается в окне с названием "Captured screen", то смотрим в статусную строку, показывающую текущие координаты курсора. Выбираем нужные координаты начала фрагмента и затем нажимаем клавишу [C]. Появляется окно ввода, в него надо ввести 4 числа, координаты x,y левого нижнего угла фрагмента, ширину и высоту фрагмента и нажать кнопку [OK] или клавишу [Enter]. Выбранный фрагмент будет показан в отдельном окне. Далее действуем обычным образом как описано выше.

Хотя программ, которые делают скриншоты очень много, но мне нравятся оба способа данной программы. Во-первых, если размеры не важны, легко вырезаем нужный фрамент, а если важны - получаем их с абсолютной точностью. И все очень быстро работает, активировать программу по иконке при любом состоянии экрана очень просто. Можно сразу открыть много окон документов и получать фрагменты из любого документа таких программ как word, pdf, gstools и так далее. Спасать лучше всего в png файлы, так как они не теряют точность. Набрав нужные фрагменты можно перейти к следующему этапу создания слайда.

Второй этап делается чуть сложнее. Прежде всего надо создать файл с названием vkSM.txt в любом текстовом редакторе и записать в него план создания слайда. Файл должен находиться в той же папке, что и программа. Этот план записывается следующим образом: В первой строке надо задать адрес рабочей папки, где находятся все фрагменты слайда. Вот образец адреса: d:/tmp/2/ . То есть буква диска, двоеточие и затем структура вложенных папок, только не обратная наклонная черта, как принято в Виндовс, а знак деления и текст заканчивается знаком деления. Во второй строке надо набрать три числа и через вертикальную черту имя файла будущего слайда без расширения, вот так: w h n|имя . При этом w и h -- ширина и высота будущей картинки слайда в пикселях, n -- число фрагментов. Исходно поле слайда имеет белый цвет. Однако поместив картинку на все поле слайда фон можно сделать любым. Далее надо записать ровно n строк (можно и больше, но остальные не прочитаются) с информацией по каждому фрагменту. Информация выглядит так: семь чисел через пробел, вертикальная черта и полное имя файла в указанной папке. Условно это можно записать так: x y a Sx Sy sx sy|имя.тип. Объяснения я начну с конца. Имя файла может быть любым (какое надо), тип - либо jpg либо png, другие типы картинок не поддерживаются.

Параметры sx и sy -- это коэффициенты масштабирования исходной картинки перед наложением на поле слайда. Если масштабирование не нужно, то надо писать 1 1. Параметры Sx Sy -- это коэффициенты сдвигового преобразования картинки. Как это работает проще посмотреть, чем описать. Если преобразование не нужно, то надо писать 0 0. Параметр а -- это угол поворота картинки в градусах вокруг ее центра. Параметры x и y задают координаты фрагмента на поле слайда, отсчитываемые от его левого нижнего угла, но имеют двойной смысл. Если а = 0, то это координаты левого нижнего угла фрагмента. Если а > 0 или а < 0, то это координаты центра картинки фрагмента. Так сделано просто из соображений удобства. Если вращения нет, то координаты 0 0 означают, что фрагмент будет в левом нижнем углу слайда и легко вычислить как его надо сместить. Если вращение есть, то оно происходит вокруг центра и лучше задавать координаты центра. Операция создания слайда запускается после активации программы и нажатия клавиши [F2]. Программа считывает текст из файла vkSM.txt, затем считывает все файлы фрагментов и сразу делает рисунок полного слайда со всеми фрагментами, установленными в нужных местах. Клик на картинке или нажатие любой клавиши приводит к тому, что картинка исчезает, а в рабочей папке появляются два файла с расширениями jpg и png и с тем же самым (указанным во второй строке) именем.

Если компоновка слайда не понравилась, то можно отредактировать параметры в файле и повторить попытку. Важно помнить, что последующие фрагменты накладываются на предыдущие, так что изменяя порядок фрагментов можно получить различные картинки. В отличие от программы Power Point или Word данная операция сразу дает твердую копию слайда в виде двух файлов. А дальше выбираем один из них исходя их соображений размера файла или качества картинки. Ясно, что png файлы всегда имеют лучшее качество, но инода они имеют очень большой размер. И если это важно, а качество сносное, то можно выбрать и jpg файл. Чтобы эффективно работать с программой надо сделать jar-файл и тогда окно терминала не будет портить экран. Как это делается было написано в самом начале, точнее в главе 8. Надо сказать, что эта программа не совсем резидентная, как например Task manager. Если она не активна, то нажатие клавиш ничего не даст. Ее надо каждый раз активировать. Но это очень простая процедура. Пока программа сидит в памяти она моментально запускается и сразу работает. Выключить (убрать из памяти) ее можно по клавише [Esc] после активации, то есть клика на ее иконке в статусной строке.

Разбор кода программы

На этом этапе вы уже запустили программу и посмотрели как она работает. Сейчас приступим к разбору самого java кода. Начальным файлом является файл vkSM.java. На этот раз он побольше. Сам класс vkSM имеет только один главный метод main(String[] args){new iniPro();}, в котором записан запуск конструктора другого класса iniPro. Повторяю еще раз, что так не обязательно, это просто мой стиль. Сам класс iniPro написан тут же. Он реализует интерфейс Runnable. Это значит, что он должен запускаться в новой нитке и иметь метод run(). Код написан так, что конструктор этого класса как раз и запускает новую нитку (процесс), а чтобы показать, что в ней запускается именно этот класс, в аргументе конструктора Thread записано служебное слово this. Таким образом, при запуске конструктора этого класса образуется новая нить с этим же классом, а по команде tr.start(); запускается метод run() этого класса, который записан сразу за конструктором. В этом методе определяется титульная строка окна программы. Хотя само окно и не видно, но текст титульной строки появляется при наведении курсора мыши на иконку программы в статусной строке. Так что текст имеет смысл. Затем запускается конструктор класса, который описывает окно программы, а именно, MainFrame, и в него передается титульная строка окна.

Главная работа делается в классе MainFrame, который записан тут же. В этом классе уже записано больше кода. Он расширяет стандартный класс JFrame и реализует интерфейс KeyListener. Он импортирует некоторые методы стандартных классов и использует много разных переменных и объектов классов, которые тоже играют роль переменных. Далее, как и в предыдущей главе, я буду ссылаться на номера строк файла. Итак, начнем разбирать конструктор этого класса. Сначала все знакомо по предыдущим программам. В строках 28 и 29 задается внешний вид окна программы в варианте, разработанном авторами языка Java. Здесь иконка программы из файла не задается, то есть используется стандартная.

В строке 30 задается титульная строка окна и к окну присоединяется детектор сигналов с клавиатуры, буквально "слушатель клавиатуры". Эта операция означает, что объявленный нами интерфейс будет реализован и все события клавиатуры будут обрабатываться должным образом. Строка 31 присоединяет дополнительно детектор сигналов от иконок окна. Но тут мы делаем по другому. Вместо того, чтобы объявить интерфейс окна, записать волшебное слово this и расписать интерфейс, мы записываем его сразу в аргументе команды. А именно, в аргументе мы записываем конструктор класса WindowAdapter и сразу записываем код этого конструктора, то есть сам интерфейс. В нем мы используем только один метод windowClosing(WindowEvent e), в котором задаем команду dispose() чтобы принудительно убрать окно и очистить в памяти все ресурсы, связанные с ним. Иначе память засоряется и ее может не хватить. Этот код обеспечивает пользователю возможность закрыть окно кликом крестика в правом верхнем углу окна. В нашем случае это уже не используется, так как окна не видно, но пусть лучше стоит на всякий случай.

В строках 32-34 определяются размеры экрана монитора, задаются размеры и положение окна и окно активируется (делается видимым). Вот и весь конструктор. В строках 37-40 записываются стандартные методы интерфейса KeyListener. Мы используем только событие keyPressed() и в нем как раз запускаем методы snapop() и slidop() для кодов клавиш 112 (F1) и 113 (F2). А по коду 27 (Esc) закрываем программу. Прямо тут же записан первый метод, реализующий функцию snapshot. Захват экрана записан в строках 43-44. Для этого используется класс Robot и его метод createScreenCapture(sr), где sr -- это прямоугольник с заданными координатами и размерами. Мы берем весь экран. Эта операция требует обработать исключение, все такие сложности обычно подсказывает сам компилятор, так что достаточно знать только общие принципы обработки исключений. Теперь захваченную картинку в памяти надо показать. Это делается в строке 45 точно так же, как и раньше. Если выход из показа был реализован нажатием клавиши [C], ее код 67, то надо выставить окно ввода. Это делается в строках 46-48 стандартным образом, уже описанным ранее в этой книге.

Замечу только, что в данной программе класс inpForm чуть более развит, чем было описано раньше в главе 15. Окна ввода также реагируют на нажатие клавиши [Enter] и реакция такая же, как и при клике на кнопке [OK]. В строке 50 реализован альтернативный метод записи координат и размера области по начальной и конечной координате мыши при опущенной левой клавише. Наконец, в строке 51 выбирается фрагмент картинки. Тут ничего делать не надо, есть стандартный метод класса BufferedImage. Далее надо показать вырезанный фрагмент, код точно такой же, как и в начале. А затем включается класс JFileChooser уже описанный ранее в главе 11, с его помощью выбирается адрес файла для записи фрагмента картинки. Запись реализуется методом write класса javax.imageio.ImageIO. Его надо выписывать в явном виде и полностью так как иначе могут быть конфликты. Класс с таким же именем есть и в других пакетах. Вот и вся программа. Как видим кода очень мало, практически вся работа делается стандартными методами стандартных классов, включая и описанные в этой книге.

Вторая программа slidop() использует новый класс универсальной графики, который мы еще не использовали. В строке 64 объявляется новый объект класса File по имени нашего файла с планом создания слайда. И затем стандартной функцией fileToText содержание файла переписывается в строку. Эту функцию мы напишем ниже, хотя в принципе она у меня находится в статическом классе, реализующем все операции с файлами. Далее весь код имеет смысл только в том случае, если операция прошла удачно и в строке txt действительно есть информация, то есть она не пустая. Сразу полный текст в строке расщепляется на массив строк по признаку конца строки с юникодом 10. По первой строке создается объект класса File. Но нам нужна именно папка. В Java папки и файлы являются объектами одно и того же класса, но можно проверить, является ли действительно объект папкой. Именно это тут же и делается. Если нет, то выдается сообщение об ошибке номер 13 и выход из программы.

В строке 66 кода работаем со второй строкой плана. Ее надо разделить по знаку вертикальной черты на два куска. И из первого куска прочесть три числа. Методы статического класса MyPro описаны в главе 10. И в переменных n1, n2, n запоминаем данные о слайде, а в строке ls -- имя двух файлов слайда, которые будут созданы. В строке 67 открываем картинку для слайда с нужными размерами и привязываем к ней новый объект класса Graphics2D. Я привык все объявления делать в самом начале класса, хотя это можно делать в любом месте по ходу или даже в конце. У каждого свой стиль. Реально я записываю объявления не сразу, а потом. И даже иногда по указанию компилятора об ошибке. Но при чтении удобно все иметь в одном месте и лучше всего в начале. Обратите внимание, что в данном случае связь картинки с графикой делается чуть иначе. Как авторы языка сделали, так и надо писать, комментарии излишни. В строке 68 устанавливается свойство как перерисовывать графику при переходе от одного окна к другому. Ничего не могу сказать, в одном из учебников мне посоветовали писать так, я и пишу, хотя есть и другие варианты. Как получать информацию я опишу в следующей главе. В строке 69 устанавливается белый цвет и вся область заполняется белым. И тут могут быть варианты, об этом тоже в других главах.

Наступило время ставить фрагменты на поле слайда. Это делается в цикле по номерам строк. Каждая строка разделяется на два куска. Из первого куска берется 7 чисел типа double. Но метод ttrarr класса MyPro раньше не описывался. Он написан специально для этой программы. Далее в строке 71 создается объект класса File по полному имени, представляющему собой сумму названия рабочей папки и названия файла. Программа может работать с любой папкой на компьютере находясь в каком-нибудь стандартном месте. Сразу проверяется наличие файла. Если его нет, выдается сообщение об ошибке номер 13 и конец работы. Можно было бы продолжить, это кому как нравится, но я считаю, что исправить план не трудно и снова повторить быстро. После этого надо прочитать картинку из файла. Это мы уже делали. А код в строках 73-76 новый. Здесь объявляются локальные переменные, действующие только в пределах данной функции. Затем вычисляется расстояние от левого нижнего края картинки до ее центра. Стандартно координаты картинки отсчитываются в верхней линии, а я люблю отсчитывать от нижней линии, поэтому вертикальную координату надо пересчитывать. Затем угол пересчитывается в радианы и меняется знак и вычисляются координаты смещения в зависимости от значения угла (ноль или нет).

А в строке 75 определяется объект класса AffineTransform. Этот класс имеет несколько методов геометрического преобразования графических объектов в классе Graphics2D. В этом классе вообще работа идет иначе чем в классе Graphics. Фактически рисуют только две команды draw и fill. А все условия рисования или заполнения областей, включая сам контур описываются предварительно в других классах. Одним из них и является класс AffineTransform. В нем мы сразу задаем условия трансляции, сдвига и масштабирования объекта при рисовании. А затем в команде drawImage мы указываем на объект картинки и на объект преобразований. И виртуальная машина Java дальше все делает за нас. Ну и весь процесс надо поставить в обработку исключения. И если что-то не сработало, то сообщаем об ошибке номер 15. Вот и все самое интересное. А остальное нам уже знакомо. Осталось просто записать полученный слайд в два файла. В конце стоит сообщение об ошибке номер 3, если вдруг не получилось прочитать файл плана. Номера ошибок произвольные, просто данный код является частью кода программы редактора vkNotepad.

Двигаемся дальше. Следуюшая программа fileToText. Она возвращает строку txt, которая первоначально обнуляется. Помните проверку на нулевую строку в предыдущей программе. Если что-то не сработает, то программа выдаст нулевую строку. Далее определяется размер файла и заказывается байтовый массив ровно с таким размером. А дальше объявляется буферизованный поток чтения данных из файла. У этого объекта есть метод прочитать из файла байтовый массив от начального индекса до конечного индекса. Мы читаем весь байтовый массив, то есть весь файл. И закрываем поток. Далее в цикле делаются три вещи. Первая: байты преобразуются в юникоды. Вторая: по ходу ликвидируются символы с юникодом 13, если они есть. Дело в том, что признак конца строки в Виндовс записывается двумя кодами 13 и 10. Это аппендикс из древних времен пишущей машинки. Тогда надо было сдвинуть каретку в начало и потом перевести на новую строку. Что и делают два символа. Но у нас нет пишущей машинки и достаточно одного знака, как делается в системах Юникс, Линукс и Java. Третье: к отрицательным байтам прибавляется число 1104. Это локализация на русский. Если в файле набиты русские символы в Виндовс кодировке, то они при этом автоматически становятся русскими символами в юникодах. Таким образом мы преобразуем текст файла, записанный в аски кодах сразу в юникоды, не используя стандартные и сложные методы локализации, а напрямую. Хотя это и не универсальный метод, но он отлично работает и код простой.

Дальше идет программа сообщений об ошибках. Тут все так просто, что и нечего объяснять. Все сообщения делаются одинаковым образом, только тексты меняются. Вот и все. Теперь о других классах. Класс butImage написан универсально и не меняется. Класс inpForm был чуть модифицирован добавлением интерфейса KeyListener. О нем я уже писал. Но тут тонкость в том, что все такие интерфейсы надо обязательно добавлять в конкретным объектам, иначе они не будут работать. Так вот тут он добавляется не к главному окну, а к каждой строке ввода. Это важно. Про класс MyPro я тоже писал неоднократно. Этот класс аккумулирует общие статические методы. Но данная программа небольшая и она не использует многие из методов, какие тут обычно бывают. И я эти методы для данной программы исключил просто чтобы уменьшить код. Это можно было и не делать, ничего бы не изменилось, просто в программе был бы лишний код. Так часто делают при использовании фреймворков, то есть библиотек стандартных процедур. Так как в данном случае библиотека моя собственная, то я могу что-то ставить, а что-то не ставить.

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

содержание   вперед   назад


.