From ebc975a162af5abf13664094d2e2958475c44b6e Mon Sep 17 00:00:00 2001 From: igor Date: Thu, 2 Nov 2017 04:14:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=B0=D0=BB=D1=8C=D0=BD=D0=B5=D0=B9?= =?UTF-8?q?=D1=88=D0=B8=D0=B9=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Applications.cfg | 2 +- Documentation/doubleBuffering.md | 57 ++++++++++++-------- lib/doubleBuffering.lua | 91 ++++++++++++++------------------ 3 files changed, 77 insertions(+), 73 deletions(-) diff --git a/Applications.cfg b/Applications.cfg index 2bcee556..29b356d1 100644 --- a/Applications.cfg +++ b/Applications.cfg @@ -351,7 +351,7 @@ url="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/doubleBuffering.lua", type="Library", preloadFile=true, - version=1.35, + version=1.36, }, { path="/lib/compressor.lua", diff --git a/Documentation/doubleBuffering.md b/Documentation/doubleBuffering.md index ba3c7b41..fefd81a7 100644 --- a/Documentation/doubleBuffering.md +++ b/Documentation/doubleBuffering.md @@ -2,8 +2,8 @@ | ----- | | [О библиотеке](#О-библиотеке) | | [Установка](#Установка) | -| [Свойства библиотеки](#Параметры-библиотеки) | | [Методы библиотеки](#Методы-библиотеки) | +| [    buffer.getResolution](#buffergetresolution-int-width-int-height) | | [    buffer.setResolution](#buffersetresolution-width-height-) | | [    buffer.bindScreen](#bufferbindscreen-address-) | | [    buffer.bindGPU](#bufferbindgpu-address-) | @@ -18,6 +18,8 @@ | [    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-) | @@ -35,7 +37,7 @@ О библиотеке ====== -DoubleBuffering - низкоуровневая библиотека для эффективного использования ресурсов GPU и отрисовки содержимого экрана с предельной скоростью. К примеру, с ее помощью реализован наш игровой движок с динамическим освещением сцен, а также небольшая игра на алгоритме рейкастинга, выдающие более чем достойные значения FPS: +DoubleBuffering - низкоуровневая библиотека для эффективного использования ресурсов GPU и отрисовки содержимого экрана с предельно возможной скоростью. К примеру, с ее помощью реализован наш игровой движок с динамическим освещением сцен, а также небольшая игра на алгоритме рейкастинга, выдающие более чем достойные значения FPS: ![Imgur](http://i.imgur.com/YgL9fCo.png?1) @@ -53,6 +55,7 @@ DoubleBuffering - низкоуровневая библиотека для эф Для получения данных о пикселях используются специальные методы, преобразующие экранные координаты в индексы экранного буфера и наоборот, подробнее об этом написано ниже в разделе "**Вспомогательные методы**". +Кроме того, библиотека не обращается ни к одной lua-таблице напрямую, заменяя их на переменные-аналоги и избегая при этом расчетов данных хеш-таблиц: к примеру, каждый метод GPU экранирован, и вместо gpu.setBackground используется GPUSetBackground(). При грамотной реализации такой подход колоссально увеличивает производительность, не нагружая при этом Lua GC. Установка ====== @@ -68,20 +71,13 @@ DoubleBuffering - низкоуровневая библиотека для эф 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.**getResolution**(): *int* width, *int* height +----------------------------------------------------------- +Получить разрешение экранного буфера. Для удобства также имеются методы buffer.**getWidth**() и buffer.**getHeight**(). + buffer.**setResolution**( width, height ) ----------------------------------------------------------- | Тип | Аргумент | Описание | @@ -97,7 +93,7 @@ buffer.**bindScreen**( address ) | ------ | ------ | ------ | | *string* | address | Адрес компонента экрана | -Связать используемую буфером видеокарту с указанным компонентом экрана. Содержимое буфера при этом будет заполнено черными пикселями с символом пробела. +Связать используемую буфером видеокарту с указанным адресом компонента экрана. Содержимое буфера при этом будет очищено черными пикселями с символом пробела. buffer.**bindGPU**( address ) ----------------------------------------------------------- @@ -105,7 +101,11 @@ buffer.**bindGPU**( address ) | ------ | ------ | ------ | | *string* | address | Адрес компонента видеокарты | -Изменить используемую буфером видеокарту на указанную. Содержимое буфера при этом будет заполнено черными пикселями с символом пробела. +Изменить используемую буфером видеокарту на указанную. Содержимое буфера при этом будет очищено черными пикселями с символом пробела. + +buffer.**getGPUProxy**( ): *table* GPUProxy +----------------------------------------------------------- +Получить указатель на proxy используемого буфером компонента видеокарты. Методы отрисовки ====== @@ -211,6 +211,16 @@ buffer.**text**( x, y, color, text, transparency ) Нарисовать текст указанного цвета поверх имеющихся пикселей. Цвет фона при этом остается прежним. Можно также указать опциональную прозрачность текста текста в диапазоне [0; 100]. +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 ) ----------------------------------------------------------- | Тип | Аргумент | Описание | @@ -294,7 +304,7 @@ buffer.**flush**( [width, height] ) Метод, устанавливающий разрешение экранного буфера равным указанному и заполняющий его черными пикселями с символом пробела. В отличие от buffer.**setResolution** не изменяет текущего разрешения GPU. Если опциональные аргументы не указаны, то размер буфера становится эквивалентным текущему разрешению GPU. -buffer.**getIndexByCoordinates**( x, y ): int index +buffer.**getIndex*( x, y ): int index ----------------------------------------------------------- | Тип | Аргумент | Описание | | ------ | ------ | ------ | @@ -303,7 +313,7 @@ buffer.**getIndexByCoordinates**( x, y ): int index Метод, преобразующий экранные координаты в индекс экраннного буфера. К примеру, пиксель 2x1 имеет индекс буфера 4, а пиксель 3x1 имеет индекс буфера 7. -buffer.**getCoordinatesByIndex**( index ): int x, int y +buffer.**getCoordinates**( index ): int x, int y ----------------------------------------------------------- | Тип | Аргумент | Описание | | ------ | ------ | ------ | @@ -330,17 +340,20 @@ buffer.**rawGet**( index ): int background, int foreground, char symbol Метод, возвращающий соответствующие значения цветов и символа пикселя с указанным индексом. -Практический пример #1 +Практический пример ====== ```lua --- Подключаем библиотеку +-- Подключаем библиотеки local buffer = require("doubleBuffering") +local image = require("image") --- Загружаем и рисуем изображение с лукошком с малиной +----------------------------------------------------------------------------------------------- + +-- Загружаем и рисуем изображение buffer.image(1, 1, image.load("/MineOS/Pictures/Raspberry.pic")) --- Заполняем буфер черным цветом с прозрачностью 60%, чтобы малина на фоне была чуть темнее -buffer.clear(0x0, 60) +-- Заполняем буфер черным цветом с прозрачностью 60%, чтобы изображение было чуть темнее +buffer.clear(0x0, 0.6) -- Рисуем 10 квадратиков, заполненных случайным цветом local x, y, xStep, yStep = 2, 2, 4, 2 diff --git a/lib/doubleBuffering.lua b/lib/doubleBuffering.lua index fd0f7b0d..c2a34b5a 100755 --- a/lib/doubleBuffering.lua +++ b/lib/doubleBuffering.lua @@ -14,7 +14,7 @@ local drawLimitX1, drawLimitX2, drawLimitY1, drawLimitY2 local GPUProxy, GPUProxyGetResolution, GPUProxySetResolution, GPUProxyBind, GPUProxyGetBackground, GPUProxyGetForeground, GPUProxySetBackground, GPUProxySetForeground, GPUProxyGet, GPUProxySet local mathCeil, mathFloor, mathModf, mathAbs = math.ceil, math.floor, math.modf, math.abs -local tableInsert = table.insert +local tableInsert, tableConcat = table.insert, table.concat local colorBlend = color.blend local unicodeLen, unicodeSub = unicode.len, unicode.sub @@ -554,64 +554,67 @@ local function info(...) GPUProxySetBackground(0x0) GPUProxySetForeground(0xFFFFFF) GPUProxyFill(1, bufferHeight, bufferWidth, 1, " ") - GPUProxySet(2, bufferHeight, table.concat(text, ", ")) + GPUProxySet(2, bufferHeight, tableConcat(text, ", ")) GPUProxySetBackground(b) GPUProxySetForeground(f) end -local function calculateDifference(index, indexPlus1, indexPlus2) - local somethingIsChanged = - currentFrame[index] ~= newFrame[index] or - currentFrame[indexPlus1] ~= newFrame[indexPlus1] or - currentFrame[indexPlus2] ~= newFrame[indexPlus2] - - currentFrame[index] = newFrame[index] - currentFrame[indexPlus1] = newFrame[indexPlus1] - currentFrame[indexPlus2] = newFrame[indexPlus2] - - return somethingIsChanged -end - local function draw(force) -- local oldClock = os.clock() - local changes, index, indexStepOnEveryLine, indexPlus1, indexPlus2, sameCharArray, x, xCharCheck, indexCharCheck, indexCharCheckPlus1, indexCharCheckPlus2, currentForeground = {}, getIndex(drawLimitX1, drawLimitY1), (bufferWidth - drawLimitX2 + drawLimitX1 - 1) * 3 + local changes, index, indexStepOnEveryLine, indexPlus1, indexPlus2, equalChars, x, charX, charIndex, charIndexPlus1, charIndexPlus2, currentForeground = {}, getIndex(drawLimitX1, drawLimitY1), (bufferWidth - drawLimitX2 + drawLimitX1 - 1) * 3 for y = drawLimitY1, drawLimitY2 do x = drawLimitX1 - while x <= drawLimitX2 do indexPlus1, indexPlus2 = index + 1, index + 2 - if calculateDifference(index, indexPlus1, indexPlus2) or force then - sameCharArray = { currentFrame[indexPlus2] } - xCharCheck, indexCharCheck = x + 1, index + 3 - - while xCharCheck <= drawLimitX2 do - indexCharCheckPlus1, indexCharCheckPlus2 = indexCharCheck + 1, indexCharCheck + 2 + -- Determine if some pixel data was changed (or if argument was passed) + if + currentFrame[index] ~= newFrame[index] or + currentFrame[indexPlus1] ~= newFrame[indexPlus1] or + currentFrame[indexPlus2] ~= newFrame[indexPlus2] or + force + then + -- Make pixel at both frames equal + currentFrame[index] = newFrame[index] + currentFrame[indexPlus1] = newFrame[indexPlus1] + currentFrame[indexPlus2] = newFrame[indexPlus2] + + -- Look for pixels with equal chars from right of current pixel + equalChars = {currentFrame[indexPlus2]} + charX, charIndex = x + 1, index + 3 + while charX <= drawLimitX2 do + charIndexPlus1, charIndexPlus2 = charIndex + 1, charIndex + 2 + -- Pixels becomes equal only if they have same background and (whitespace char or same foreground) if - currentFrame[index] == newFrame[indexCharCheck] and + currentFrame[index] == newFrame[charIndex] and ( - newFrame[indexCharCheckPlus2] == " " or - currentFrame[indexPlus1] == newFrame[indexCharCheckPlus1] + newFrame[charIndexPlus2] == " " or + currentFrame[indexPlus1] == newFrame[charIndexPlus1] ) then - calculateDifference(indexCharCheck, indexCharCheckPlus1, indexCharCheckPlus2) - tableInsert(sameCharArray, currentFrame[indexCharCheckPlus2]) + -- Make pixel at both frames equal + currentFrame[charIndex] = newFrame[charIndex] + currentFrame[charIndexPlus1] = newFrame[charIndexPlus1] + currentFrame[charIndexPlus2] = newFrame[charIndexPlus2] + + tableInsert(equalChars, currentFrame[charIndexPlus2]) else break end - indexCharCheck, xCharCheck = indexCharCheck + 3, xCharCheck + 1 + charIndex, charX = charIndex + 3, charX + 1 end + -- Group pixels that need to be drawn by background and foreground changes[currentFrame[index]] = changes[currentFrame[index]] or {} changes[currentFrame[index]][currentFrame[indexPlus1]] = changes[currentFrame[index]][currentFrame[indexPlus1]] or {} - + tableInsert(changes[currentFrame[index]][currentFrame[indexPlus1]], x) tableInsert(changes[currentFrame[index]][currentFrame[indexPlus1]], y) - tableInsert(changes[currentFrame[index]][currentFrame[indexPlus1]], table.concat(sameCharArray)) + tableInsert(changes[currentFrame[index]][currentFrame[indexPlus1]], tableConcat(equalChars)) - x, index = x + #sameCharArray - 1, index + #sameCharArray * 3 - 3 + x, index = x + #equalChars - 1, index + (#equalChars - 1) * 3 end x, index = x + 1, index + 3 @@ -620,17 +623,18 @@ local function draw(force) index = index + indexStepOnEveryLine end - for background in pairs(changes) do + -- Draw grouped pixels on screen + for background, foregrounds in pairs(changes) do GPUProxySetBackground(background) - for foreground in pairs(changes[background]) do + for foreground, pixels in pairs(foregrounds) do if currentForeground ~= foreground then GPUProxySetForeground(foreground) currentForeground = foreground end - for i = 1, #changes[background][foreground], 3 do - GPUProxySet(changes[background][foreground][i], changes[background][foreground][i + 1], changes[background][foreground][i + 2]) + for i = 1, #pixels, 3 do + GPUProxySet(pixels[i], pixels[i + 1], pixels[i + 2]) end end end @@ -687,17 +691,4 @@ return { scrollBar = scrollBar, horizontalScrollBar = horizontalScrollBar, customImage = customImage, -} - - - - - - - - - - - - - +} \ No newline at end of file