25 KiB
О библиотеке
DoubleBuffering - низкоуровневая библиотека для эффективного использования ресурсов GPU и отрисовки содержимого экрана с предельной скоростью. К примеру, с ее помощью реализован наш игровой движок с динамическим освещением сцен, а также небольшая игра на алгоритме рейкастинга, выдающие более чем достойные значения FPS:
Сама суть библиотеки очень проста: в оперативной памяти хранится два массива, содержащих информацию о пикселях на экране. Первый хранит то, что отображено в данный момент, а второй - то, что пользователь желает отрисовать. После осуществления всех операций отрисовки пользователь вызывает метод buffer.draw(), затем система автоматически определяет изменившиеся пиксели, группирует их в промежуточный буфер, чтобы число GPU-операций было минимальным, а затем выводит изменения на экран.
По сравнению с стандартной отрисовкой время отображения сокращается в сотни и тысячи раз. На рисунке ниже наглядно показана эффективность библиотеки:
Цена таких космических скоростей - повышенный расход оперативной памяти. Чтобы предельно уменьшить его, мы используем одномерную структуру экранных массивов вместо трехмерной:
Для получения данных о пикселях используются специальные методы, преобразующие экранные координаты в индексы экранного буфера и наоборот, подробнее об этом написано ниже в разделе "Вспомогательные методы".
Установка
| Библиотека | Функционал |
|---|---|
| doubleBuffering | Данная библиотека |
| color | Низкоуровневая библиотека для работы с цветом, предоставляющая методы получения цветовых каналов, различные палитры и конвертацию цвета в 8-битный формат |
| image | Библиотека, реализующая стандарт изображений для OpenComputers и методы их обработки: транспонирование, обрезку, поворот, отражение и т.д. |
| OCIF | Модуль формата изображения OCIF (OpenComputers Image Format) для библиотеки image, написанный с учетом особенностей мода и реализующий эффективное сжатие пиксельных данных |
Вы можете использовать имеющиеся выше ссылки для установки зависимостей вручную или запустить автоматический установщик, загружающий все необходимые файлы за вас:
pastebin run vTM8nbSZ
Свойства библиотеки
| Тип свойства | Свойство | Описание |
|---|---|---|
| int | buffer.width | Текущее разрешение буфера по ширине |
| int | buffer.height | Текущее разрешение буфера по высоте |
| table | buffer.GPUProxy | Указатель на proxy компонента видеокарты, с которой работает буфер |
| table | buffer.currentFrame | Указатель на таблицу с пиксельными данными, содержащая то, что в данный момент отображено на экране. Она имеет структуру { 0xFFFFFF, 0x000000, "Q", 0xFFFFFF, 0x000000, "W", ... }, где первый элемент - цвет фона, второй - цвет текста, третий - символ. И так до конца размера буфера |
| table | buffer.newFrame | Указатель на таблицу с пиксельными данными, содержащая то, что пользователь отрисовывает в него в данный момент. Структура аналогична предыдущей |
Основные методы
buffer.setResolution( width, height )
| Тип | Аргумент | Описание |
|---|---|---|
| int | width | Ширина буфера |
| int | height | Высота буфера |
Установить разрешение экранного буфера и GPU равным указанному. Содержимое буфера при этом будет заполнено черными пикселями с символом пробела.
buffer.bindScreen( address )
| Тип | Аргумент | Описание |
|---|---|---|
| string | address | Адрес компонента экрана |
Связать используемую буфером видеокарту с указанным компонентом экрана. Содержимое буфера при этом будет заполнено черными пикселями с символом пробела.
buffer.bindGPU( address )
| Тип | Аргумент | Описание |
|---|---|---|
| string | address | Адрес компонента видеокарты |
Изменить используемую буфером видеокарту на указанную. Содержимое буфера при этом будет заполнено черными пикселями с символом пробела.
Методы отрисовки
buffer.draw( [force] )
| Тип | Аргумент | Описание |
|---|---|---|
| [boolean | force] | Принудительная отрисовка |
Отрисовать содержимое буфера на экран. Если имеется опциональный аргумент force, то содержимое буфера будет отрисовано полностью и вне зависимости от изменившихся пикселей.
buffer.setDrawLimit( x1, y1, x2, y2 )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x1 | Координата первой точки лимита отрисовки по оси x |
| int | y1 | Координата первой точки лимита отрисовки по оси y |
| int | x2 | Координата второй точки лимита отрисовки по оси x |
| int | y2 | Координата второй точки лимита отрисовки по оси y |
Установить лимит отрисовки буфера до указанного. При этом любые операции, выходящие за границы лимита, будут игнорироваться. По умолчанию буфер всегда имеет лимит отрисовки в диапазонах x ∈ [1; buffer.width] и y ∈ [1; buffer.height]
buffer.getDrawLimit( ): int x1, int y1, int x2, int y2
Получить текущий лимит отрисовки.
buffer.copy( x, y, width, height ): table pixelData
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата копируемой области по оси x |
| int | y | Координата копируемой области по оси y |
| int | width | Ширина копируемой области |
| int | height | Высота копируемой области |
Скопировать содержимое указанной области из буфера и выдать в виде таблицы. Впоследствии можно использовать с buffer.paste(...).
buffer.paste( x, y, pixelData )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата вставки по оси x |
| int | y | Координата вставки по оси y |
| table | pixelData | Таблица со скопированной ранее областью буфера |
Вставить скопированное содержимое буфера по указанным координатам.
buffer.set( x, y, background, foreground, symbol )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата по оси x |
| int | y | Координата по оси y |
| int | background | Цвет фона |
| int | foreground | Цвет символа |
| char | symbol | Символ |
Установить значение конкретного пикселя на экране. Полезно для мелкого и точного редактирования.
buffer.get( x, y ): int background, int foreground, char symbol
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата по оси x |
| int | y | Координата по оси y |
Получить значение конкретного пикселя на экране.
buffer.square( x, y, width, height, background, foreground, symbol, transparency )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата прямоугольника по оси x |
| int | y | Координата прямоугольника по оси y |
| int | width | Ширина прямоугольника |
| int | height | Высота прямоугольника |
| int | background | Цвет фона прямоугольника |
| int | foreground | Цвет символов прямоугольника |
| char | symbol | Символ, которым будет заполнен прямоугольник |
| [int | transparency] | Опциональная прозрачность прямоугольника |
Заполнить прямоугольную область указанными данными. При указании прозрачности в диапазоне [0; 100] прямоугольник будет накладываться поверх существующей информации, словно прозрачное стеклышко.
buffer.clear( [color, transparency] )
| Тип | Аргумент | Описание |
|---|---|---|
| [int | background] | Опциональный цвет фона |
| [int | transparency] | Опциональная прозрачность фона |
Работает как buffer.square(...), однако применяется сразу ко всем пикселям буфера. Если аргументов не передается, то буфер заполняется стандартным черным цветом и символом пробела. Удобно для быстрой очистки содержимого буфера.
buffer.text( x, y, color, text, transparency )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата текста по оси x |
| int | y | Координата текста по оси y |
| int | foreground | Цвет текста |
| string | text | Текст |
| [int | transparency] | Опциональная прозрачность текста |
Нарисовать текст указанного цвета поверх имеющихся пикселей. Цвет фона при этом остается прежним. Можно также указать опциональную прозрачность текста текста в диапазоне [0; 100].
buffer.image( x, y, picture )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата изображения по оси x |
| int | y | Координата изображения по оси y |
| table | picture | Загруженное изображение |
Нарисовать загруженное через image.load(string path) изображение. Альфа-канал изображения также поддерживается.
Методы полупиксельной отрисовки
Все методы полупиксельной отрисовки позволяют избежать эффекта удвоения высоты пикселя консольной графики, используя специальные символы наподобие "▄". При этом передаваемые координаты по оси Y должны принадлежать промежутку [0; buffer.height * 2].
buffer.semiPixelSet( x, y, color )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x1 | Координата пикселя по оси x |
| int | y1 | Координата пикселя по оси y |
| int | color | Цвет пикселя |
Установка пиксельного значения в указанной точке.
buffer.semiPixelSquare( x, y, width, height, color )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата прямоугольника по оси x |
| int | y | Координата прямоугольника по оси y |
| int | width | Ширина прямоугольника |
| int | height | Высота прямоугольника |
| int | color | Цвет прямоугольника |
Отрисовка прямоугольника с указанными параметрами.
buffer.semiPixelLine( x1, y1, x2, y2, color )
| Тип | Аргумент | Описание |
|---|---|---|
| int | x1 | Координата первой точки линии по оси x |
| int | y1 | Координата первой точки линии по оси y |
| int | x2 | Координата второй точки линии по оси x |
| int | y2 | Координата второй точки линии по оси y |
| int | color | Цвет линии |
Растеризация отрезка указанного цвета
buffer.semiPixelCircle( xCenter, yCenter, radius, color )
| Тип | Аргумент | Описание |
|---|---|---|
| int | xCenter | Координата центральной точки окружности по оси x |
| int | yCenter | Координата центральной точки окружности по оси y |
| int | radius | Радиус окружности |
| int | color | Цвет окружности |
buffer.semiPixelBezierCurve( points, color, precision )
| Тип | Аргумент | Описание |
|---|---|---|
| table | points | Таблица вида {{x = 32, y = 2}, {x = 2, y = 2}, {x = 2, y = 98}}, содержащая опорные точки для отрисовки кривой Безье |
| int | color | Цвет кривой Безье |
| float | precision | Точность отрисовки кривой Безье. Чем меньше - тем точнее |
Растеризация кривой Безье с указанным цветом.
Вспомогательные методы
Ниже перечислены методы, используемые самой библиотекой или приложениями, требующими максимального быстродействия и рассчитывающими пиксельные данные буфера вручную. В большинстве случаев они не пригождаются, однако для ознакомления указаны.
buffer.flush( [width, height] )
| Тип | Аргумент | Описание |
|---|---|---|
| int | width | Ширина буфера |
| int | height | Высота буфера |
Метод, устанавливающий разрешение экранного буфера равным указанному и заполняющий его черными пикселями с символом пробела. В отличие от buffer.setResolution не изменяет текущего разрешения GPU. Если опциональные аргументы не указаны, то размер буфера становится эквивалентным текущему разрешению GPU.
buffer.getIndexByCoordinates( x, y ): int index
| Тип | Аргумент | Описание |
|---|---|---|
| int | x | Координата пикселя экрана по оси x |
| int | y | Координата пикселя экрана по оси y |
Метод, преобразующий экранные координаты в индекс экраннного буфера. К примеру, пиксель 2x1 имеет индекс буфера 4, а пиксель 3x1 имеет индекс буфера 7.
buffer.getCoordinatesByIndex( index ): int x, int y
| Тип | Аргумент | Описание |
|---|---|---|
| int | index | Индекс экранного буфера |
Метод, преобразующий индекс буфера в соответствующие ему координаты на экране.
buffer.rawSet( index, background, foreground, symbol )
| Тип | Аргумент | Описание |
|---|---|---|
| int | index | Индекс экранного буфера |
| int | background | Цвет фона |
| int | foreground | Цвет символа |
| char | symbol | Символ |
Метод, устанавливающий соответствующие значения цветов и символа пикселю с указанным индексом.
buffer.rawGet( index ): int background, int foreground, char symbol
| Тип | Аргумент | Описание |
|---|---|---|
| int | index | Индекс экранного буфера |
Метод, возвращающий соответствующие значения цветов и символа пикселя с указанным индексом.
Практический пример #1
-- Подключаем библиотеку
local buffer = require("doubleBuffering")
-- Загружаем и рисуем изображение с лукошком с малиной
buffer.image(1, 1, image.load("/MineOS/Pictures/Raspberry.pic"))
-- Заполняем буфер черным цветом с прозрачностью 60%, чтобы малина на фоне была чуть темнее
buffer.clear(0x0, 60)
-- Рисуем 10 квадратиков, заполненных случайным цветом
local x, y, xStep, yStep = 2, 2, 4, 2
for i = 1, 10 do
buffer.square(x, y, 6, 3, math.random(0x0, 0xFFFFFF), 0x0, " ")
x, y = x + xStep, y + yStep
end
-- Рисуем желтую окружность
buffer.semiPixelCircle(22, 22, 10, 0xFFDB40)
-- Рисуем белую линию
buffer.semiPixelLine(2, 36, 35, 3, 0xFFFFFF)
-- Рисуем зеленую кривую Безье с точностью 0.01
buffer.semiPixelBezierCurve(
{
{ x = 2, y = 63},
{ x = 63, y = 63},
{ x = 63, y = 2}
},
0x44FF44,
0.01
)
-- Выводим содержимое буфера на экран
buffer.draw()
Результат:




