Графика для JVM / Хабр
Допустим, я хочу создавать качественные десктопные приложения. Я также хочу сделать это на JVM. Не надейтесь — мы еще не достигли цели. Но у меня есть план.
Почему именно JVM?
Это производительность на достаточно высоком уровне, но не заставляет вас слишком много задумываться о каждом выделение памяти. Это кроссплатформенно. В нем есть отличные языки — Kotlin, Scala и, конечно же, Clojure. C # тоже подойдет, но в нем нет Clojure.
Разве вы уже не можете создавать десктопные приложения на JVM?
Вы можете. Но традиционно AWT, Swing и JavaFX сопровождались множеством недостатков в качестве и производительности. Они были настолько существенными, что только одной компании удалось создать прилично выглядящее приложение на Swing. Это возможно, но требует огромных усилий.
Разве не все пользовательские интерфейсы Java прокляты?
Нет, не совсем. У AWT, Swing и JavaFX есть свои проблемы, но это исключительно их проблемы. Нет фундаментальной причины, по которой невозможно создать высококачественный пользовательский интерфейс на JVM. Просто это еще не было сделано.
Почему это еще не было сделано?
Почему не Electron?
Первая причина — производительность. JS — отличный язык для создания пользовательского интерфейса, но он намного медленнее, чем JVM. Wasm может быть быстрым, но подразумевает C ++ или Rust.
Второй — это модель DOM. Это ужасающая коллекция уловок, которые делают простые вещи сложными, а сложные — невозможными. Я много раз думал: «Если бы я рисовал этот элемент управления/макет напрямую, я бы закончил несколько часов назад».
Это означает, что существует очень низкий предел возможностей веб-приложений с точки зрения производительности и качества. Я считаю, что мы можем и должны добиться большего.
Однако Electron научил нас двум хорошим вещам:
- Люди жаждут нативных приложений. Никто не хочет работать из браузера.
- Людей не волнует, что приложения не выглядят нативными для платформы, если они хорошо выглядят.
Десктоп по-прежнему актуален?
Я верю, что это так!
Недавно я смотрел интервью между разработчиком Android и разработчиком iOS. Один спрашивал:
«Кто-нибудь по-прежнему пишет десктопные приложения?»
На что другой ответил:
«Понятия не имею… Может быть?»
Оба они записывали его на десктопе, в десктопном приложении, во время разговора через другое десктопное приложение. Вероятно, для пост-обработки использовалось несколько других десктопных приложений. Ни один из них не был написан волшебными эльфами и не оставлен нам могущественной древней цивилизацией. Десктоп может быть менее модным, но только потому, что здесь труднее продавать бесполезную хрень.
И я был на обеих сторонах. Однажды я жил без десктопа несколько недель. К этому привыкаешь, но это, конечно, не идеал. Сбор и обработка любой информации очень болезненны: сложно выделить текст, сложно выполнить поиск на странице, сложно иметь несколько вкладок, сложно перемещать данные между приложениями. Например, вы добавляете событие в календарь. Вам нужно найти адрес события в почте, где есть ссылка, открывающая браузер. К тому времени, когда вы нашли то, что вам нужно, и вернули в календарь, оно уже выгружается из памяти, и весь контекст теряется. Возможность одновременного открытия нескольких окон — это суперспособность десктопа.
Телефоны отлично подходят для небольших быстрых одноцелевых задач. У них есть свое место, но жизнь намного сложнее, чем телефон. Многим из нас все еще нужен этот велосипед для ума.
Хорошо, что же ты предлагаешь?
Путь к качественному пользовательскому интерфейсу на JVM — долгий.
Нам понадобятся:- графическая библиотека,
- библиотека интеграции окна / ОС,
- набор инструментов пользовательского интерфейса.
Сегодня я рад анонсировать первую часть этого эпического квеста: графическую библиотеку. Она называется Skija и представляет собой просто набор привязок к очень мощной, хорошо известной библиотеке Skia. Та, которую поддерживает Chrome, Android, Flutter и Xamarin.
Как и любая другая библиотека JVM, она кроссплатформенная. Она работает в Windows, Linux и macOS. Это так же просто, как добавить файл JAR. Намного проще, чем массировать флаги компилятора C ++ в течение нескольких дней, прежде чем вы сможете что-либо скомпилировать. Skija позаботится об управлении памятью за вас. И привязки создаются вручную, поэтому они всегда имеют смысл и доставляют удовольствие (по крайней мере, насколько позволяет Skia API).
Что с этим делать? В основном рисовать. Линии. Треугольники. Прямоугольники. Но также: кривые, контуры, формы, буквы, тени, градиенты.
Думайте об этом как о Canvas API. Но вроде бы действительно продвинутой версии. Она понимает цветовые пространства, современную типографику, макет текста, ускорение графического процессора и тому подобное.
О, и это быстро. Действительно быстро. Если этого достаточно для Chrome, вероятно, этого будет достаточно и для вашего приложения.
Что я могу с этим сделать?
Много вещей! Пользовательские библиотеки виджетов пользовательского интерфейса и целые наборы инструментов, графики, диаграммы, визуализации, игры. Например, мы поигрались с реализацией java.awt.Graphics2D и запуском Swing поверх него — похоже, все работает нормально.
Зачем выпускать отдельную графическую библиотеку? Чем это полезно?
Я не большой поклонник объединять все в одном месте. Вы никогда не сможете угадать все детали правильно — кто-то всегда будет недоволен конкретным решением.
Независимые взаимозаменяемые библиотеки более гибкие. Философия Unix.
Что с остальной кусочками паззла?
Обе вещи находятся в разработке в JetBrains.
- Для интеграции управления окнами и ОС есть Skiko. Она говорит, что это Skia для Kotlin, но она также реализует создание окон, события, пэкэджинг и все остальное. Она даже интегрируется с AWT и Swing.
- А для инструментария пользовательского интерфейса есть Compose Desktop. Это форк Android Compose, декларативного UI-фреймворка, работающего в десктопной среде.
Но прелесть в том, что это даже не обязательно эти двое!
Не нравится AWT? Принесите свою собственную библиотеку окон.
Kotlin не подходит Вам? Используйте любой другой язык JVM.
Compose плохо справляется с нагрузкой? Молитесь, чтобы кто-нибудь написал альтернативу или написал что-то свое (извините, но хорошего решения пока нет. Это еще только начало).
И, конечно, если вы хотите развернуть свою собственную библиотеку окон или набор инструментов для виджетов — пожалуйста, сделайте это! Мы надеемся, что это произойдет.
В заключение
Skija — это часть более широкой картины. Прогресс пользовательского интерфейса Java был заблокирован некачественной Graphics2D. Но все меняется. Что из этого выйдет? Время покажет.
Пожалуйста, попробуйте Skija и поделитесь с нами своим мнением. Или, может быть, начните ей пользоваться — мы были бы счастливы, если бы вы это сделали! Вот ссылка:github.com/JetBrains/skija
14 ноября 2020 · Обсуждение на HackerNews
Облачные серверы от Маклауд быстрые и безопасные.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!
Java 2D . Графические интерфейсы пользователя Java
Как уже было сказано, библиотека AWT была дополнена библиотекой Java 2D API, расширяющей возможности работы с двухмерной графикой и изображениями.
Java 2D API предоставляет единую модель рендеринга для разных типов устройств.
На уровне приложения процесс рендеринга одинаковый, является ли целевое устройство рендеринга экраном или принтером.
Когда компонент должен отображаться, автоматически вызывается его метод paint или update с соответствующим графическим контекстом Graphics.
Java 2D API предоставляет класс Graphics2D, который расширяет класс Graphics, чтобы обеспечить доступ к расширенной графике и функциям рендеринга Java 2D API.
Чтобы использовать функции Java 2D API в приложении, нужно привести объект Graphics, переданный в метод рендеринга компонента, к объекту Graphics2D.
И объект Graphics2D предоставит следующие функции:
Это отображение набора примитивов, реализующих интерфейс Shape.
При этом примитив может быть заполнен цветом и его контуры могут быть нарисованы с указанием определенной толщины и шаблоном штрихов.
Graphics2D позволяет создать композицию примитивов, переместить, масштабировать, повернуть и обрезать форму.
Также Graphics2D позволяет конвертировать текстовую строку в глифы, которые затем могут быть заполнены цветом.
Примитивы – это точки, линии, прямоугольники, эллипсы, дуги, кривые.
Также можно нарисовать произвольную форму с помощью пути рисования GeneralPath.
Контур примитива можно определить с помощью объекта Stroke.
А заполнить примитив цветом можно с помощью объекта Paint.
Java 2D API предоставляет различные возможности для отображения текста, включая установку атрибутов шрифта и выполнения компоновки текста.
Если вы просто хотите нарисовать статическую текстовую строку, проще всего это сделать с помощью метода drawString класса Graphics, указав шрифт методом setFont класса Graphics.
Если вы хотите контролировать качество отображения текста и его компоновку, вы можете использовать Java 2D API.
Java 2D API позволяет контролировать качество отображения с помощью подсказок класса RenderingHints, например, указав сглаживание текста.
Класс TextLayout предоставляет различные возможности для стилизации текста.
Перед тем, как текст может быть отображен, он должен быть правильно сформирован и расположен с использованием соответствующих символов и лигатур.
Этот процесс называется компоновкой текста.
Процесс компоновки текста включает в себя формирование текста с использованием соответствующих глифов и лигатур, упорядочивание текста, измерение и позиционирование текста.
Что касается изображений, Java 2D API позволяет загрузить внешний файл изображения формата GIF, PNG, JPEG во внутреннее представление изображения, используемое Java 2D.
Непосредственно создать 2D Java изображение и отобразить его.
Отрисовать содержимого 2D-изображения Java на поверхности.
Сохранить содержимое 2D Java изображения во внешний файл изображения GIF, PNG или JPEG.
Для работы с изображениями используются два класса – это класс Image – суперкласс, представляющий графические изображения в виде прямоугольных массивов пикселей.
И класс BufferedImage, который расширяет класс Image, чтобы приложение могло работать непосредственно с данными изображения, например, извлечение или настройка цвета пикселя.
И приложения могут напрямую создавать экземпляры этого класса.
Класс BufferedImage управляет изображением в памяти и предоставляет методы для хранения, интерпретации и получения данных пикселей.
Так как BufferedImage является подклассом Image, его контент может быть визуализирован с помощью методов Graphics и Graphics2D, которые принимают параметр Image.
Для загрузки изображения из внешнего источника используется Image I/O API, которое поддерживает форматы изображения GIF, PNG, JPEG, BMP.
Соответственно Image I/O API используется и для сохранения объекта BufferedImage во внешний формат изображения.
Для получения изображения из внешнего источника также можно использовать класс Toolkit, как мы видели уже раньше.
Для отрисовки полученного изображения нужен объект Graphics или Graphics2D.
Если мы не используем метод paint или update компонента, получить объект Graphics2D можно вызвав метод createGraphics, но только изображения, которое вы создали сами.
Поэтому, получив изображение из внешнего источника, мы создаем пустое изображение того же размера, получаем из него объект Graphics2D, и используем его для отрисовки нашего изображения.
Используя возможность получения графического контекста из изображения, можно, например, создать свое изображение, рисуя в нем с помощью объекта Graphics или Graphics2D.
Работа с изображениями
AWT предоставляет некоторые возможности для управления изображениями с помощью пакета java.awt.image.
Как мы уже говорили, получить изображение из внешнего источника можно с помощью объекта Toolkit.
Затем с помощью BufferedImage мы можем получить объект графического контекста и нарисовать изображение.
Далее вы можете масштабировать это изображение.
Если вы хотите что-то нарисовать на своем изображении, вы можете получить графический объект и делать все что захотите.
Java была разработана для загрузки изображения во время работы программы.
Таким образом, вы можете вызвать методы getWidth и getHeight до того, как Java узнает размер изображения.
В этом случае размер изображения будет установлен в -1.
Это основная проблема, когда вам нужно знать размер изображения.
Решение этой проблемы заключается в том, чтобы ваша программа дождалась загрузки изображения.
Сделать это можно с помощью класса MediaTracker, предназначенного для отслеживания состояния изображений.
Мы добавляем изображение для отслеживания и вызываем метод waitForID, который начинает загрузку изображения, отслеживаемого этим медиа-трекером, с указанным идентификатором.
Этот метод ожидает завершения загрузки изображения с указанным идентификатором.
Отслеживать загрузку изображения также можно с помощью интерфейса ImageObserver, который реализуется классом Component.
ImageObserver – это интерфейс, используемый для приема уведомлений о том, как генерируется изображение.
ImageObserver определяет только один метод: ImageUpdate ().
Использование наблюдателя изображения позволяет выполнять (параллельно с загрузкой изображения) другие действия, такие как показ индикатора хода работы или дополнительного экрана, который информирует о ходе загрузки изображения.
Многие из разработчиков Java находили интерфейс ImageObserver слишком сложным для понимания и управления загрузкой множественных изображений.
Поэтому был создан класс MediaTracker, который позволяет параллельно проверять состояние произвольного числа изображений.
Для многих реальных задач обработки изображений необходимо получить массив с пикселями изображения.
Здесь показан код, который позволяет сделать это.
В этом коде мы предполагаем, что изображение полностью загружено.
Сначала мы получаем размер изображения.
Затем создаем массив для пикселей изображения.
Далее создаем объект PixelGrabber, который с помощью метода grabPixels позволяет извлечь пиксели в массив, указанный при создании объекта PixelGrabber.
После получения массива пикселей изображения, его можно обработать и создать новое изображение.
Для создания изображения из массива пикселей используется вспомогательный класс MemoryImageSource и объект Toolkit.
И наконец полученное изображение можно нарисовать.
На слайде показано, как создаются данные изображения за сценой.
Производитель изображения – это объект, который реализует интерфейс ImageProducer, и создает необработанные данные для объекта изображения Image.
Производитель изображения предоставляет эти данные потребителю изображения – объекту, который реализует интерфейс ImageConsumer.
Если вам не нужно манипулировать или создавать пользовательские изображения, вам обычно не нужно знать о производителе изображения и потребителе изображения.
AWT автоматически использует производителя и потребителя изображения за сценой.
При создании экземпляра класса Image требуется наличие производителя изображений.
Код глубоко внутри AWT создает производителя изображений.
Как альтернатива программист может предоставить создателя изображения, при создании объекта изображения Image.
При рисовании экземпляра класса Image методом draw, глубоко внутри AWT создается потребитель изображения.
Здесь показано, как при создании пользовательского компонента, можно с помощью метода createImage класса Component создать пользовательское изображение, реализуя собственные ImageProducer и ImageConsumer, и нарисовать это изображение в методе paint компонента.
Java позволяет «фильтровать» изображения с помощью класса ImageFilter или его подклассов.
AWT поддерживает обработку изображений, позволяя вставлять фильтры изображений между производителем изображения и потребителем изображения.
Фильтр изображения представляет собой объект ImageFilter, который находится между производителем и потребителем изображения, изменяя данные изображения до того, как потребитель получит его.
ImageFilter реализует интерфейс ImageConsumer, поскольку фильтр изображения перехватывает сообщения, которые производитель отправляет потребителю.
Для использования фильтра, создается объект фильтра.
Далее нужно создать объект класса ImageProducer.
Этот объект создается при помощи конструктора класса FilteredImageSource.
В качестве первого параметра мы передаем конструктору ссылку на источник данных исходного изображения, полученный методом getSource, а через второй – ссылку на свой фильтр.
Затем мы можем создать новое изображение методом createImage.
Чтобы дождаться процесса завершения формирования изображения, используйте класс MediaTracker.
После этого изображение готово и доступно для рисования методом drawImage.
Все фильтры изображений должны быть подклассами класса ImageFilter.
Если ваш фильтр изображения изменяет цвета или прозрачность изображения, вы можете создать подкласс класса RGBImageFilter, который расширяет класс ImageFilter.
Здесь показан простой фильтр изображения, который отфильтровывает отдельные цветовые компоненты (красный, зеленый и синий) изображения.
Класс ColorFilter расширяет класс RGBImageFilter и содержит три логические переменные, которые определяют, какие цвета должны быть отфильтрованы из изображения.
Эти переменные задаются параметрами, переданными в конструктор.
Переменная canFilterIndexColorModel, унаследованная от RGBImageFilter, установлена в значение true, чтобы указать, что записи цветовой карты могут быть отфильтрованы, если входящее изображение использует модель индексированного цвета.
В Java цвета пикселей управляются через цветовые модели.
Цветовые модели Java обеспечивают важную абстракцию, которая позволяет Java работать с изображениями разных форматов единым образом.
Цветовая модель представляет собой объект Java, который предоставляет методы для перевода значений пикселей в соответствующие красные, зеленые и синие цветовые компоненты изображения.
Это может показаться тривиальной задачей, зная, что компоненты цвета пикселей аккуратно упакованы в 32-битное значение.
Тем не менее, существуют различные типы цветовых моделей, отражающие различные методы определения цветов пикселей.
Двумя типами цветовых моделей, поддерживаемых Java, являются прямая цветовая модель и модель индексированных цветов.
Прямая цветовая модель работает со значениями пикселей, которые представляют цвет RGB и альфа-информацию отдельно, которые упакованы для каждого пикселя в одно значение.
Модель индексированных цветов поддерживается 8-битными изображениями, содержащими не более 256 цветов.
Эта модель работает с картой цветов изображения, в которой хранятся и индексируются цвета, используемые в изображении.
Эта позволяет уменьшить размер файла изображения, при этом сохраняя качество изображения.
Вернемся к нашему примеру.
Помимо конструктора, класс ColorFilter реализует только один метод filterRGB, который является абстрактным методом, определенным в классе RGBImageFilter.
Метод filterRGB принимает три параметра: положение x и y пикселя внутри изображения и 32-битное (целочисленное) значение цвета.
Единственный параметр, которым вы занимаетесь, является значение цвета, rgb.
Стандартная цветовая модель RGB помещает красные, зеленые и синие компоненты в нижние 24 бита 32-битного значения цвета.
И каждый из них можно извлечь, сдвигая параметр rgb.
Эти отдельные компоненты хранятся в локальных переменных r, g и b.
Обратите внимание, что здесь каждый компонент цвета смещается только в том случае, если он не фильтруется.
Для фильтрованных цветов, компонент цвета установлен в 0.
Новые цветовые компоненты затем переносятся обратно в 32-битное значение цвета и возвращаются из метода filterRGB.
Обратите внимание, что альфа-компонент значения цвета не изменяется.
Для этого используется маска 0xff000000, потому что альфа-компонент находится в верхнем байте значения цвета.
Помимо обработки изображений, с помощью вставки фильтров изображений между производителем изображения и потребителем изображения,
Java поддерживает фильтрацию изображений с помощью интерфейса BufferedImageOp.
Метод filter интерфейса BufferedImageOp принимает объект BufferedImage как вход (исходное изображение) и выполняет обработку данных изображения, создавая другой объект BufferedImage (конечное изображение).
Напомним, что класс BufferedImage расширяет класс Image, обеспечивая доступ к буферу данных изображения.
Java 2D API предоставляет набор реализаций интерфейса BufferedImageOp.
AffineTransformOp – преобразует изображение геометрически.
ColorConvertOp – выполняет по-пиксельное преобразование цвета в исходном изображении.
ConvolveOp – выполняет свертку, математическую операцию, которая может использоваться для размытия, изменения резкости или другой обработки изображения.
LookupOp – изменяет отдельные составляющие цвета.
RescaleOp – изменяет интенсивность изображения.
Здесь показан пример применения фильтра RescaleOp, изменяющего интенсивность цвета.
В этом примере сначала создается исходный объект BufferedImage на основе изображения, затем создается пустой объект BufferedImage.
Который заполняется отфильтрованными данными исходного изображения, с помощью метода filter созданного объекта RescaleOp.
Данный текст является ознакомительным фрагментом.
Документация JDK 19 — Главная
- Главная
- Ява
- Java SE
- 19
Обзор
- Прочтите меня
- Примечания к выпуску
- Что нового
- Руководство по миграции
- Загрузить JDK
- Руководство по установке
- Формат строки версии
Инструменты
- Технические характеристики инструментов JDK
- Руководство пользователя JShell
- Руководство по JavaDoc
- Руководство пользователя средства упаковки
Язык и библиотеки
- Обновления языка
- Основные библиотеки
- HTTP-клиент JDK
- Учебники по Java
- Модульный JDK
- Руководство программиста API бортового регистратора
- Руководство по интернационализации
Технические характеристики
- Документация API
- Язык и ВМ
- Имена стандартных алгоритмов безопасности Java
- банок
- Собственный интерфейс Java (JNI)
- Инструментальный интерфейс JVM (JVM TI)
- Сериализация
- Проводной протокол отладки Java (JDWP)
- Спецификация комментариев к документации для стандартного доклета
- Прочие характеристики
Безопасность
- Руководство по безопасному кодированию
- Руководство по безопасности
Виртуальная машина HotSpot
- Руководство по виртуальной машине Java
- Настройка сборки мусора
Управление и устранение неполадок
- Руководство по устранению неполадок
- Руководство по мониторингу и управлению
- Руководство по JMX
Client Technologies
- Руководство по специальным возможностям Java
java-графика · Темы GitHub · GitHub
Здесь 32 публичных репозитория соответствует этой теме.
..ТоталКросс / тоталкросс
Звезда 197рафиульгиты / SFC-игра
Звезда 17Берей / java-флэппи-птичья игра
Звезда 4ЛазоКодер / Площадка для Java-графики
Звезда 4h4AR7B3A7 / JavaSnake
Звезда 3жаюн3233 / Аквапоника
Звезда 2h4AR7B3A7 / JavaГрафика
Звезда 2Ван2403986 / jSoftOpenGL
Звезда 1simonguozirui / UCCFYCompsci
Звезда 1Адитья Шарма484 / Java-Мотиватор
Звезда 1авирайс / AIRodentRevenge
Звезда 1ph04 / java-трассировщик лучей
Звезда 1дирижабль / эскиз
Звезда 1Колинбут / 3d-карта
Звезда 0плохое имя419 / Java-шары
Звезда 0Эджебечи / CannonGame
Звезда 1юсуфактог / wordle-клон
Звезда 0эванв555 / Астероиды
Звезда 0дирижабль / холст
Звезда 0Алекстер93 / Гонки черепах и зайцев-
Звезда 0Улучшить эту страницу
Добавьте описание, изображение и ссылки на java-графика страницу темы, чтобы разработчикам было легче узнать о ней.