MineOS/Documentation/doubleBuffering.md
2017-12-10 19:13:34 +03:00

388 lines
26 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

| Содержание |
| ----- |
| [О библиотеке](#О-библиотеке) |
| [Установка](#Установка) |
| [Методы библиотеки](#Методы-библиотеки) |
| [buffer.getResolution](#buffergetresolution-int-width-int-height) |
| [buffer.setResolution](#buffersetresolution-width-height-) |
| [buffer.bindScreen](#bufferbindscreen-address-) |
| [buffer.bindGPU](#bufferbindgpu-address-) |
| [Методы отрисовки](#Методы-отрисовки) |
| [buffer.draw](#bufferdraw-force-) |
| [buffer.setDrawLimit](#buffersetdrawlimit-x1-y1-x2-y2-) |
| [buffer.getDrawLimit](#buffergetdrawlimit--int-x1-int-y1-int-x2-int-y2) |
| [buffer.copy](#buffercopy-x-y-width-height--table-pixeldata) |
| [buffer.paste](#bufferpaste-x-y-pixeldata-) |
| [buffer.set](#bufferpaste-x-y-pixeldata-) |
| [buffer.get](#bufferpaste-x-y-pixeldata-) |
| [buffer.square](#buffersquare-x-y-width-height-background-foreground-symbol-transparency-) |
| [buffer.clear](#bufferclear-color-transparency-) |
| [buffer.text](#buffertext-x-y-color-text-transparency-) |
| [buffer.formattedText](#bufferformattedtext-x-y-text-) |
| [buffer.image](#bufferimage-x-y-picture-) |
| [Методы полупиксельной отрисовки:](#Методы-полупиксельной-отрисовки) |
| [buffer.semiPixelSet](#buffersemipixelset-x-y-color-) |
| [buffer.semiPixelSquare](#buffersemipixelsquare-x-y-width-height-color-) |
| [buffer.semiPixelLine](#buffersemipixelline-x1-y1-x2-y2-color-) |
| [buffer.semiPixelCircle](#buffersemipixelcircle-xcenter-ycenter-radius-color-) |
| [buffer.semiPixelBezierCurve](#buffersemipixelbeziercurve-points-color-precision-) |
| [Вспомогательные методы:](#Вспомогательные-методы) |
| [buffer.flush](#bufferflush-width-height-) |
| [buffer.getIndexByCoordinates](#buffergetindexbycoordinates-x-y--int-index) |
| [buffer.getCoordinatesByIndex](#buffergetcoordinatesbyindex-index--int-x-int-y) |
| [buffer.rawSet](#bufferrawset-index-background-foreground-symbol-) |
| [buffer.rawGet](#bufferrawget-index--int-background-int-foreground-char-symbol) |
| [Практический пример #1](#Практический-пример-1) |
О библиотеке
======
DoubleBuffering - низкоуровневая библиотека для эффективного использования ресурсов GPU и отрисовки содержимого экрана с предельно возможной скоростью. К примеру, с ее помощью реализован наш игровой движок с динамическим освещением сцен, а также небольшая игра на алгоритме рейкастинга, выдающие более чем достойные значения FPS:
![Imgur](http://i.imgur.com/YgL9fCo.png?1)
![Imgur](http://i.imgur.com/yHEwiNo.png?1)
Сама суть библиотеки очень проста: в оперативной памяти хранится два массива, содержащих информацию о пикселях на экране. Первый хранит то, что отображено в данный момент, а второй - то, что пользователь желает отрисовать. После осуществления всех операций отрисовки пользователь вызывает метод buffer.**draw**(), затем система автоматически определяет изменившиеся пиксели, группирует их в промежуточный буфер, чтобы число GPU-операций было минимальным, а затем выводит изменения на экран.
По сравнению с стандартной отрисовкой время отображения сокращается в сотни и тысячи раз. На рисунке ниже наглядно показана эффективность библиотеки:
![meow](http://i60.fastpic.ru/big/2015/1026/8a/4c72bfcbe8fbee5993bfd7a058a5f88a.png)
Цена таких космических скоростей - повышенный расход оперативной памяти. Чтобы предельно уменьшить его, мы используем одномерную структуру экранных массивов вместо трехмерной:
![Imgur](http://i.imgur.com/2Pkne53.png)
Для получения данных о пикселях используются специальные методы, преобразующие экранные координаты в индексы экранного буфера и наоборот, подробнее об этом написано ниже в разделе "**Вспомогательные методы**".
Кроме того, библиотека не обращается ни к одной lua-таблице напрямую, заменяя их на переменные-аналоги и избегая при этом расчетов данных хеш-таблиц: к примеру, каждый метод GPU экранирован, и вместо gpu.setBackground используется GPUSetBackground(). При грамотной реализации такой подход колоссально увеличивает производительность, не нагружая при этом Lua GC.
Установка
======
| Библиотека | Функционал |
| ------ | ------ |
| *[advancedLua](https://github.com/IgorTimofeev/OpenComputers/blob/master/lib/advancedLua.lua)* | Дополнение стандартных библиотек Lua множеством функций: быстрой сериализацией таблиц, переносом строк, методами обработки бинарных данных и т.д. |
| *[color](https://github.com/IgorTimofeev/OpenComputers/blob/master/lib/color.lua)* | Экструзия цветовых каналов, альфа-блендинг, поддержка различных палитр и конвертации цвета в 8-битный формат |
| *[image](https://github.com/IgorTimofeev/OpenComputers/blob/master/lib/image.lua)* | Реализация стандарта изображений для OpenComputers и базовые методы их обработки: транспонирование, обрезка, поворот, отражение и т.д. |
| *[OCIF](https://github.com/IgorTimofeev/OpenComputers/blob/master/lib/FormatModules/OCIF.lua)* | Модуль формата изображения OCIF (OpenComputers Image Format) для библиотеки image, написанный с учетом особенностей мода и реализующий эффективное сжатие пиксельных данных |
| *[doubleBuffering](https://github.com/IgorTimofeev/OpenComputers/blob/master/lib/doubleBuffering.lua)* | Данная библиотека |
Вы можете использовать имеющиеся выше ссылки для установки зависимостей вручную или запустить автоматический [установщик](https://pastebin.com/vTM8nbSZ), загружающий все необходимые файлы за вас:
pastebin run vTM8nbSZ
Основные методы
======
buffer.**getResolution**(): *int* width, *int* height
-----------------------------------------------------------
Получить разрешение экранного буфера. Для удобства также имеются методы buffer.**getWidth**() и buffer.**getHeight**().
buffer.**setResolution**( width, height )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *int* | width | Ширина буфера |
| *int* | height | Высота буфера |
Установить разрешение экранного буфера и GPU равным указанному. Содержимое буфера при этом будет заполнено черными пикселями с символом пробела.
buffer.**bindScreen**( address )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *string* | address | Адрес компонента экрана |
Связать используемую буфером видеокарту с указанным адресом компонента экрана. Содержимое буфера при этом будет очищено черными пикселями с символом пробела.
buffer.**bindGPU**( address )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *string* | address | Адрес компонента видеокарты |
Изменить используемую буфером видеокарту на указанную. Содержимое буфера при этом будет очищено черными пикселями с символом пробела.
buffer.**getGPUProxy**( ): *table* GPUProxy
-----------------------------------------------------------
Получить указатель на proxy используемого буфером компонента видеокарты.
Методы отрисовки
======
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 | Символ, которым будет заполнен прямоугольник |
| [*float* | transparency] | Опциональная прозрачность прямоугольника |
Заполнить прямоугольную область указанными данными. При указании прозрачности в диапазоне [0.0; 1.0] прямоугольник будет накладываться поверх существующей информации, словно прозрачное стеклышко.
buffer.**clear**( [color, transparency] )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| [*int* | background] | Опциональный цвет фона |
| [*float* | transparency] | Опциональная прозрачность фона |
Работает как buffer.**square**(...), однако применяется сразу ко всем пикселям буфера. Если аргументов не передается, то буфер заполняется стандартным черным цветом и символом пробела. Удобно для быстрой очистки содержимого буфера.
buffer.**text**( x, y, color, text, transparency )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *int* | x | Координата текста по оси x |
| *int* | y | Координата текста по оси y |
| *int* | foreground | Цвет текста |
| *string* | text | Текст |
| [*float* | transparency] | Опциональная прозрачность текста |
Нарисовать текст указанного цвета поверх имеющихся пикселей. Цвет фона при этом остается прежним. Можно также указать опциональную прозрачность текста текста в диапазоне [0.0; 1.0].
buffer.**formattedText**( x, y, text )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *int* | x | Координата текста по оси x |
| *int* | y | Координата текста по оси y |
| *string* | text | Текст |
Аналогичен методу buffer.**text**(), однако поддерживает цветовое форматирование. По умолчанию цвет текста имеет значение 0xFFFFFF, для его изменения используйте синтаксическую вставку вида **\#RRGGBB**. К примеру, "Hello world, **\#FF00FF**this is formatted text!".
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 | Точность отрисовки кривой Безье. Чем меньше - тем точнее |
Растеризация [кривой Безье](https://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D0%B2%D0%B0%D1%8F_%D0%91%D0%B5%D0%B7%D1%8C%D0%B5) с указанным цветом.
Вспомогательные методы
======
Ниже перечислены методы, используемые самой библиотекой или приложениями, требующими максимального быстродействия и рассчитывающими пиксельные данные буфера вручную. В большинстве случаев они не пригождаются, однако для ознакомления указаны.
buffer.**flush**( [width, height] )
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *int* | width | Ширина буфера |
| *int* | height | Высота буфера |
Метод, устанавливающий разрешение экранного буфера равным указанному и заполняющий его черными пикселями с символом пробела. В отличие от buffer.**setResolution** не изменяет текущего разрешения GPU. Если опциональные аргументы не указаны, то размер буфера становится эквивалентным текущему разрешению GPU.
buffer.**getIndex*( x, y ): int index
-----------------------------------------------------------
| Тип | Аргумент | Описание |
| ------ | ------ | ------ |
| *int* | x | Координата пикселя экрана по оси x |
| *int* | y | Координата пикселя экрана по оси y |
Метод, преобразующий экранные координаты в индекс экраннного буфера. К примеру, пиксель 2x1 имеет индекс буфера 4, а пиксель 3x1 имеет индекс буфера 7.
buffer.**getCoordinates**( 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 | Индекс экранного буфера |
Метод, возвращающий соответствующие значения цветов и символа пикселя с указанным индексом.
Практический пример
======
```lua
-- Подключаем библиотеки
local buffer = require("doubleBuffering")
local image = require("image")
-----------------------------------------------------------------------------------------------
-- Загружаем и рисуем изображение
buffer.image(1, 1, image.load("/MineOS/Pictures/Raspberry.pic"))
-- Заполняем буфер черным цветом с прозрачностью 0.6, чтобы изображение было чуть темнее
buffer.clear(0x0, 0.6)
-- Рисуем 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()
```
Результат:
![Imgur](http://i.imgur.com/wvu0jeh.png?1)