Отрисовка SVG в J2ME без использования JSR 226 API

Отрисовка SVG без использования JSR 226 API.

Довольно часто при написании проектов на Java2ME возникает необходимость в рисовании несложной векторной графики - это может быть логотип или график, а может быть и полноценный оконный интерфейс.  При этом у программиста  всего три доступных пути: использовать JSR 226 и резко ограничить количество моделей телефонов на которых приложение будет запускаться, рисовать графику вручную прописывая координаты в процедурах рисования и убив на это огромное количество времени или написать простенький векторный редактор никак не конкурирующий с большими системами. Существует и четвертый путь - написать конвертер из любого доступного векторного формата, при этом удобно использовать свободный кроссплатформенный формат - например тот же SVG. Изнутри SVG представляет подмножество XML форматов и является полностью текстовым. Формат достаточно сложный, поэтому на написание конвертера у меня ушло почти две недели. Результатом является консольная утилита InkToDraw.exe преобразовывающая исходный SVG файл в максимально простой формат и набор процедур написанных на  Java2ME для отрисовки графики на мобильном устройстве. Конвертер писался под Windows в среде Code::Block и дополнительно компилировался под Linux ASP 9.2 в среде Anjuta, соответственно работать он будет и под Windows и под Linux, такая вот кроссплатформа. Все необходимое для работы лежит вархиве.

Формат используемый на мобильном устройстве привязан к особенностям вывода в MIDP 2.0, для хранения координат используется один байт, что дает максимальный размер рисунка 256 на 256 точек, перейти на 16 бит не проблема - но при этом размер исходного блока данных увеличится в два раза, что было признано нецелесообразным.  В общем виде формат состоит из байта типа и набора координат для этого типа. Так как возможности отрисовки привязаны к Java 2 ME  спецификация формата содержит Линию, Прямоугольник, Эллипс/Дугу и Треугольник. Линия имеет два подвида: линия от точки к точке и полилиния когда результирующая ломаная описывается координатами точек. Кривые Безье апроксимируются 10 отрезками. Спецификация формата:

Вид Код Описание
100 100,3,X,Y,X1,Y1,X2,Y2,X3,Y3 (Ломаная линия, второй параметр – количество сегментов, далее идут координаты начала и координаты вершин)
101 101,X1,Y1,X2,Y2,X3,Y3,X4,Y4 (Кривая Безье, 4 пары координат полностью описывают кривую, первая и последняя пара - координаты начала и конца кривой)
102 102,3,X1,Y1,X2,Y2,X3,Y3,X4,Y4,X12,Y12,X13,Y13,X14,Y14,X22,Y22,X23,Y23,X24,Y24 (Кривая  состоящая из нескольких кривых Безье. Второй параметр - количество кривых,далее идут координаты начала и координаты вершин)
103 103,X1,Y1,W,H,A1,A2 (Контур эллипса или дуги,X1,Y1 - координаты левого верхнего угла прямоугольника в который вписан эллипс,W,H -ширина и высота эллипса/дуги,A1,A2 стартовый и конечный угол для дуги деленный пополам, чтобы уложиться в байт)
104 104,X1,Y1,W,H,A1,A2 (Залитый эллипс или сектор,X1,Y1 - координаты левого верхнего угла прямоугольника в который вписан эллипс,W,H -ширина и высота эллипса/дуги,A1,A2 стартовый и конечный угол для дуги деленный пополам, чтобы уложиться в байт)
105 105,X1,Y1,W,H,A1,A2 (Залитый эллипс или сектор,X1,Y1 - координаты левого верхнего угла прямоугольника в который вписан эллипс,W,H -ширина и высота эллипса/дуги,A1,A2 стартовый и конечный угол для дуги деленный пополам, чтобы уложиться в байт)
106 106,X1,Y1,W,H (Контур прямоугольника,X1,Y1 - координаты левого верхнего угла,W,H -ширина и высота)
107 107,X1,Y1,W,H (Залитый прямоугольник с обводкой,X1,Y1 - координаты левого верхнего угла,W,H -ширина и высота)
108 108,X1,Y1,W,H (Залитый прямоугольник,X1,Y1 - координаты левого верхнего угла,W,H -ширина и высота)
109 109,X1,Y1,W,H,RW,RH (Контур скругленного прямоугольника,X1,Y1 - координаты левого верхнего угла,W,H -ширина и высота,RW,RH - горизонтальный и вертикальный диаметр дуги скругления)
110 110,X1,Y1,W,H,RW,RH (Залитый скругленный прямоугольник с контуром,X1,Y1 - координаты левого верхнего угла,W,H -ширина и высота,RW,RH - горизонтальный и вертикальный диаметр дуги скругления)
111 111,X1,Y1,W,H,RW,RH (Залитый скругленный прямоугольник без контура,X1,Y1 - координаты левого верхнего угла,W,H -ширина и высота,RW,RH - горизонтальный и вертикальный диаметр дуги скругления)
112 112,X1,Y1,X2,Y2,X3,Y3 (Контур треугольника,X1,Y1,X2,Y2,X3,Y3 - координаты трех вершин)
113 113,X1,Y1,X2,Y2,X3,Y3 (Залитый треугольник с контуром,X1,Y1,X2,Y2,X3,Y3 - координаты трех вершин)
114 114,X1,Y1,X2,Y2,X3,Y3 (Залитый треугольник без контура,X1,Y1,X2,Y2,X3,Y3 - координаты трех вершин)
115 115,R,G,B (Цвет заливки, используется для всех залитых объектов,R,G,B - компоненты  цвета: красный, зеленый, синий)
116 116,R,G,B (Цвет рисования линий и контуров,R,G,B - компоненты  цвета: красный, зеленый, синий)
117 117,X1,Y1,X2,Y2 (Прямая, координаты задают начальную и конечную точку)
118 118 (После данного маркера все линии контура и кривые рисуются сплошной линией)
119 119 (После данного маркера все линии контура и кривые рисуются пунктирной линией)

255 255 (Маркер конца блока данных, после него рисование блока данных прекращается)

Для конвертирования под Windows используется утилита InkToDraw.exe, под Linux соответственно inktodraw. Формат командной строки у них идентичен. В общем виде коммандная строка состоит из трех аргументов - исходного файла в формате SVG, файла в который будет записан результат и опции. Из доступных опций только одна -show, вывод расширенной информации о конвертировании.

    Windows:InkToDraw.exe file.svg file.dat -options Linux: inktodraw file.svg file.dat -options

Если задан только один файл то вывод будет идти в InkDrawing.dat, а если утилита запущена без параметров будет осуществлена попытка открыть файл ScreenTest.svg и результат будет сохранен в InkDrawing.dat. К примеру, для конвертирования, мною была нарисована вот такая вот тыква.

Для того чтобы конвертировать ее в понятный обработчику формат достаточно положить файл ScreenTest.svg в одну папку с конвертером и отдать команду:

    Windows:InkToDraw.exe ScreenTest.svg Linux: inktodraw ScreenTest.svg

В результате в этой же папке появится файл InkDrawing.dat размером 453 байта, исходный файл при этом занимает  почти 18 килобайт, первые преимущества конвертера налицо ;). Теперь наша задача воспроизвести полученную картинку на мобильном устройстве. Для нетерпеливых в папке Jar есть два готовых примера: DrawTest.jar и DrawTest2.jar, друг от друга они отличаются только движком рендеринга.  Для более терпеливых читателей приготовлены проекты NetBean в папке NetBean и исходный текст без ресурсов в папке Source. Для тех кто будет собирать пакет руками достаточно создать новый проект, вставить в него исходный текст из файлов DrawTest.java или DrawTest2.java и забросить результат конвертации в папку res проекта. После сборки и запуска на экране телефона отобразится все та же тыква, после нажатия на клавишу пять апплет завершит свою работу, а любая другая клавиша отобразит ту же тыкву через другую процедуру вывода. Вариантов всего три: вывод без масштаба от левого верхнего угла, вывод с произвольным смещением и вывод со смещением и масштабом. 

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

InkDrawD Рисуем  от левого верхнего угла экрана, без масштабирования
InkDrawN Рисуем  со сдвигом, без масштаба
InkDrawS Рисуем  со сдвигом и масштабом

Параметрами для вывода является массив с данными m[], графический контекст g, смещение по x и y - dx,dy и масштабный коэффициент заданный в виде отношений двух пар целых чисел sx/mx и sy/my. К примеру для уменьшения картинки в два раза нужно задать sx=1,mx=2,sy=1,my=2 (1/2 и 1/2). Для рисования кривых Безье используется отдельная процедура drawBesier(), для преобразования байта в целое - функция i(). Процедуры восстанавливают значение текущего цвета и штриховки, так что в программе об этом беспокоиться не стоит, вывод происходит поверх уже существующего изображения. Теперь перейдем к вопросу почему вархиве приведено два практически идентичных примера. Отличаются они только способом хранения данных. В первом варианте массив данных хранится в виде байтов, но медленнее обрабатывается из за конвертации в целое каждого значения, во втором данные хранятся в виде целого (short) и занимают в два раза больше памяти, но читаются без преобразований. Варианты выбираем исходя из того что нам надо: экономить скорость вывода или память? 

При использовании данных процедур в своих проектах необходимо помнить о том что InkDrawD(), InkDrawN(), InkDrawS() независимы друг от друга, поэтому если вам нужен только вывод в фиксированном разрешении достаточно включить в проект только InkDrawD, при этом за собой она потянет процедуру рисования кривых Безье drawBesier(), а если используется хранение данных в байтовом массиве то и функцию  i(). В остальном никаких ограничений не накладывается. Если известно что рисунок будет состоять только из линий то из обработчика можно будет выкинуть все, кроме линий и цвета, что ускорит вывод и уменьшит размер кода. В общем простор для модификации достаточен. Код использует только одну возможность MIDP 2.0 - залитые треугольники, если их не использовать работать будет на любом устройстве с J2ME.

Теперь перейдем к особенностям рисования графики для конвертирования. Используем для этого Inkspace, размер нового листа указываем не больше 255 на 255 точек. Рисовать желательно с привязкой к пиксельной сетке, за границы рисунка не выходить, особенно при рисовании кривых Безье. Для рисования доступны все инструменты за исключением заливки, которую можно имитировать рисуя залитые фигуры - овал/треугольник/прямоугольник. При использовании текста не забывайте его конвертировать в кривые. Все нарисованные линии рисуются толщиной в один пиксель (толщина не учитывается). Если нарисовать замкнутую кривую с заливкой то после конвертации она будет в виде контура. Кривые содержащие больше 255 линий - отбрасываются. Недоступны градиенты и растровые эффекты, реализовать их на мобильном устройстве с нормальной скоростью весьма проблематично. Конвертер не поддерживает глобальные преобразование - поэтому не стоит ему подсовывать уменьшенную картинку из клипарта - все равно качественнее будет нарисовать или обвести готовый рисунок. 

Область применения данного конвертера достаточно широка - от текстово-графических игр до создания масштабируемых интерфейсов. Основное преимущество - возможность нарисовать картинку в произвольном разрешении. При необходимости движок вывода легко портируется на любую старую платформу и может быть использован на том же ZX-SPECTRUM или Commodore 64. Было бы желание.

Ограничения и неточности в текущей версии:
 - Залитые сектора с контуром рисуются без линий ограничивающих сектор;
 - Эллипсы и сектора нельзя поворачивать;

Что хотелось бы доработать в следующей версии (TO DO LIST ;):
 - Триангуляцию залитых кривых для вывода их треугольниками;
 - Рисование линиями разной толщины;
 - Рисование залитых кривых с помощью овалов или секторов;
 - Вывод текста векторным шрифтом;

Все необходимые упомянутые в статье файлы находятсяздесь.

Автор: Shadowsshot. Почта: shadwork(a)ukr.net.




Наши соцсети

Подписаться Facebook Подписаться Вконтакте Подписаться Twitter Подписаться Google Подписаться Telegram

Популярное

Ссылки

Новости [1] [2] [3]... Android/ iOS/ J2ME[1] [2] [3]) Android / Архив

Рейтинг@Mail.ru Яндекс.Метрика
MobiLab.ru © 2005-2018
При использовании материалов сайта ссылка на www.mobilab.ru обязательна