Writing a buffer that uses minimal color setting function calls in order to create a very quick rendering experience has been a sort of obsession of mine for quite some time now. I've written several versions which have each been a bit quicker than the last, but this version seems to have topped all of those prior.
Quick Buffer:
This buffer makes use of a method that I've had written on paper and imprinted in my mind for quite some time but was never able to translate it into code. This being the following stolen description from the file itself:
Spoiler
Quote
IMPORTANT NOTE 1:
Each buffer's contents is separated into three tables of
equal width and height:
- tText
- tTextColors
- tBackColors
Each of whom is setup that each character represents either a textual
character or a hex digit to represent a color as a single byte.
Colors are then converted at render time from their hex equivalent
into decimal format.
IMPORTANT NOTE 2:
What makes this buffer special is the way that
it handles rendering. While many similar apis simply
save a pixel as a character, a text color, and a background
color, then write said pixel out with the respective
term API calls, this buffer does something different:
Instead of changing colors all of the time, this
buffer makes rendering quicker by using a function
called 'getChunks.'
'getChunks' goes through a line in a buffer and
returns every 'chunk' of text that has the given
text and background colors. This way, the maximum
amount of text can be written before colors are
changed.
To prevent having to make 256 different iterations,
only the color pairs (set of text and background colors)
which are actually in the buffer are checked and rendered.
This is done by recording those used in 'write' and various
'clear' calls. Also, a function called 'updateColorPairs'
brute force checks the entire buffer for what color pairs
actually exist, then stores in them in the 'tColorPairs'
hash table which looks like this:
tColorPairs[sTextColor .. sBackColor] = true
(The value is true if it exists, nil or false if not.)
In essence, I think that this buffering method allows for some of the quickest rendering of common program visual output by making use of the aforementioned method. However, there is also a hangup to specializing for common output:
Quote
However, it is important to note that this maximizes
efficiency for common use, for most programs make use
of large portions of similarly colored text both in
the text color and background color.
- HOWEVER, situations in which the text and background
color pair is changing very often, this buffer may
actually be SLOWER than the classic change-every-iteration
approach!
Basically, if you have a program whose visual output is very, very colorful and unique all over the place, then rendering will probably be slower than the other rendering methods.
Uses:
- Windowing
- Multiple programs running at the same time
- Taking screen shots of the screen
- Making animations from a frame or series of frames
- Accessing information that has been written to the screen
- Stuff that I haven't even thought of
Examples of Usage:
Spoiler
Animation:
Spoiler
Here's an example API script that will give the appearance of a frame growing from the center of the screen outwards to its original dimensions and position:
[/size][/size][/size][/size]
ocal function getTableSize (tTable)
local nSize = 0
for _, __ in pairs (tTable) do
nSize = nSize + 1
end
return nSize
end
local function getShrunkBuffer (tBuffer, nWidthScale, nHeightScale)
nHeightScale = math.floor (nHeightScale)
nWidthScale = math.floor (nWidthScale)
-- Gotta set up new width and height dimensions so getChunks doesn't freak out!
local tShrunkBuffer = {
tText = {},
tTextColors = {},
tBackColors = {},
tTerm = tBuffer.tTerm
}
local nShrunkLineNumber = 1
local nLeftConstraint = tBuffer.nWidth / 2 - nWidthScale
local nRightConstraint = tBuffer.nWidth / 2 + nWidthScale
for nLineNumber = math.floor (tBuffer.nHeight / 2 - nHeightScale), math.floor (tBuffer.nHeight / 2 + nHeightScale) do
if nLineNumber >= 1 and nLineNumber <= tBuffer.nHeight then
tShrunkBuffer.tText[nShrunkLineNumber] = tBuffer.tText[nLineNumber]:sub (nLeftConstraint, nRightConstraint)
tShrunkBuffer.tTextColors[nShrunkLineNumber] = tBuffer.tTextColors[nLineNumber]:sub (nLeftConstraint, nRightConstraint)
tShrunkBuffer.tBackColors[nShrunkLineNumber] = tBuffer.tBackColors[nLineNumber]:sub (nLeftConstraint, nRightConstraint)
end
nShrunkLineNumber = nShrunkLineNumber + 1
end
tShrunkBuffer.nHeight = getTableSize (tShrunkBuffer.tText)
return tShrunkBuffer
end
function openCenter (tBuffer, nRunTime, nModifier)
local nModifier = nModifier or 1
local nRequiredIterations = math.min (tBuffer.nWidth, tBuffer.nHeight)
local nSleepTime = nRunTime / nRequiredIterations
-- Step needs to be the amount of growth per sleep time to make it under the run time!
local nWidthStep = tBuffer.nWidth / nRequiredIterations
local nHeightStep = tBuffer.nHeight / nRequiredIterations
local nWidthScale = nWidthStep
local nHeightScale = nHeightStep
for nTime = 0, nRunTime, nSleepTime * nModifier do
local tShrunkBuffer = setmetatable (getShrunkBuffer (tBuffer, nWidthScale, nHeightScale), { __index = tBuffer })
if #tShrunkBuffer.tText > 0 then
tShrunkBuffer.x = tBuffer.nWidth / 2 - tShrunkBuffer.tText[1]:len() / 2
tShrunkBuffer.y = tBuffer.nHeight / 2 - tShrunkBuffer.nHeight / 2 + (tBuffer.nHeight % 2 == 0 and 1 or 0)
tBuffer.render (tShrunkBuffer)
else
break
end
sleep (nSleepTime)
nWidthScale = nWidthScale + nWidthStep * nModifier
nHeightScale = nHeightScale + nHeightStep * nModifier
end
return tBuffer:render()
end
[size="4"][size=4][size=5][size=4]
we can create an animation that is very quick, yet still visually appealing. (This will look similar to the animations seen in oeed's OneOS as things open and close, but there is much, much more potential in the ability of you, the user, to create something cool from this template or from scratch! )
Ah, so it works on similar principles to the way my framebuffer API draws to the screen! It is an efficient method indeed. Good to see more people taking advantage of it. There are still speed gains to be had, though.
Ah, so it works on similar principles to the way my framebuffer API draws to the screen! It is an efficient method indeed. Good to see more people taking advantage of it. There are still speed gains to be had, though.
It does indeed work similarly. In fact, I tested mine against yours in a couple of tests, but I doubt the tests were well designed enough and the results conclusive enough to decide on a victor
For my own project i do need something like a buffer api to prevent flickering.
The problem is I dont understand how i'm supposed to implement QuickBuffer.
I also do not understand your example nor do i know how to start your example. Where does tBuffer come from? and is getShrunkBuffer a local function?
Before I answer your question, I'd like to point out that, given the updates to ComputerCraft, my QuickBuffer here on the forums is actually slower than what is currently possible.
To answer your question, you implement the QuickBuffer by doing the following:
1. Download the file to your CC computer.
2. Load the file using CC's os.loadAPI.
3. Create a new QuickBuffer object by doing this:
local buffer = QuickBuffer.new(51, 19 1, 1, term.current())
This example is for a buffer that takes up the whole screen.
From there, you can redirect to it by doing
local currentTerm = term.redirect(buffer:redirect())
Then, you can use the terminal API how you wish, redirecting back to the previous terminal object by doing