Skip to content

Added the ability to specify the render buffer size in the constructor.#212

Closed
meetanthony wants to merge 2 commits into
magiblot:masterfrom
meetanthony:drawbuffer_custom_length
Closed

Added the ability to specify the render buffer size in the constructor.#212
meetanthony wants to merge 2 commits into
magiblot:masterfrom
meetanthony:drawbuffer_custom_length

Conversation

@meetanthony

Copy link
Copy Markdown

Added the ability to specify the render buffer size in the constructor. This can be used to render the entire control to the buffer and then call the writeBuf method once, instead of multiple writeLine calls.

Backward compatibility is preserved by using the default value (-1).

@magiblot

magiblot commented Feb 9, 2026

Copy link
Copy Markdown
Owner

Hi, Anton!

Thank you for proposing a solution. There's no doubt that there are things which simply cannot be done if a capacity is not specified when constructing a TDrawBuffer. However, I still believe that passing a parameter should not be necessary during normal usage, e.g. in a draw() method. I myself have had problems when drawing views larger than the screen dimensions, so that's why the current behaviour is to take the screen size as reference.

So, could you please explain under which circumstances you are using TDrawBuffer? Maybe I can come up with a less intrusive solution.

Cheers.

@meetanthony

Copy link
Copy Markdown
Author

Hello, magiblot.
Thanks for the quick response and the perfect port of a useful framework.
There are no problems with line-by-line rendering of controls. I think this is a difference in ways of thinking - for someone like me, it's easier to operate with the output of a whole control, instead of a line-by-line output. In addition, I saw the writeBuf method that accepts the width and height parameters and thought that it was designed to draw several lines at a time. After I ran into problems using the writeBuf method when drawing several lines of control, I looked into the source code and saw the buffer size limitations, which are apparently related to the fact that the buffer was designed mainly for line-by-line output. So I decided to suggest that you add the possibility of intentionally expanding the buffer size in order to use the writeBuf method in an intuitive way without having to keep in mind the knowledge of the method of calculating the maximum buffer size.
Since the existing buffer size calculation algorithm will be used without explicitly specifying the buffer size, I thought this would not be a problem for the existing code. And if, for some reason, the programmer encounters problems with a large buffer size, he can always return to line-by-line rendering using TDrawBuffer with the default constructor.
For my own needs, I added the following class of working with 'TDrawBuffer' and screen coordinates:

#pragma once

#include <tvision/tv.h>

enum class SpecialSymbols : char8_t {
    boxv = 179, //
    boxh = 196, //
    boxdl = 191, //
    boxul = 217, //
    boxdr = 218, //
    boxur = 192, //
    boxhd = 194, //
    boxhu = 193, //
    boxvl = 180, //
    boxvr = 195, //
    boxvL = 181, //
    boxvR = 198, //
    boxhU = 208, //
    boxhD = 210, //

    boxV = 186, //
    boxH = 205, //
    boxDR = 201, //
    boxUR = 200, //
    boxDL = 187, //
    boxUL = 188, //

    boxVl = 182, //
    boxVL = 185, //
    boxVr = 199, //
    boxHU = 202, //
    boxHD = 203, //
    boxVR = 204, //
    boxHu = 207, //
    boxHd = 209, //
};

class DrawBuffer {
public:
    const int width;
    const int height;
    TColorAttr color = {
        TColorRGB{255, 0, 0}, // toxic red
        TColorRGB{0, 0, 255} // toxic blue
    };
    TDrawBuffer b;

    DrawBuffer(const int width, const int height)
        : width(width), height(height), b(width * height) {
    }

    void setColor(const TColorAttr color) {
        this->color = color;
    }

    void draw(const char symbol, const int x, const int y, const int count = 1) {
        b.moveChar(y * width + x, symbol, color, count);
    }

    void draw(const SpecialSymbols symbol, const int x, const int y, const int count = 1) {
        char c = static_cast<char>(symbol);
        b.moveChar(y * width + x, c, color, count);
    }

    void draw(const std::string &string, const int x, const int y, int maxWidth = -1) {
        if (maxWidth == -1)
            maxWidth = string.length();
        b.moveStr(y * width + x, string, color, maxWidth);
    }

    const TDrawBuffer &getBuffer() {
        return b;
    }
};

Thank you again for your work.
Anton.

@magiblot

Copy link
Copy Markdown
Owner

Hi, @meetanthony!

I suspect that TDrawBuffer's design is highly conditioned by the runtime restrictions Turbo Vision was originally designed to run under: since memory was scarce, it was not a good idea to allocate temporary buffers that cover the whole view surface, hence it is limited to one line at a time.

It's not just you, I too have many times resorted to using a multi-line buffer for drawing. However, for this purpose, I think that it's more practical to allocate a large array of screen cells and draw on it manually. That is to say: instead of relying on TDrawBuffer, you can do the same TDrawBuffer does internally, but with a bi-dimensional buffer. In order to draw text on screen cells, you may use the TText::* functions, as (superficially) mentioned in the README. TText, TScreenCell... are API extensions: they didn't exist in the original Turbo Vision but were added in order to allow for new features.

For example (requires #define Uses_TText):

class DrawBuffer {
public:
    const int width;
    const int height;
    TColorAttr color = {
        TColorRGB{255, 0, 0}, // toxic red
        TColorRGB{0, 0, 255} // toxic blue
    };
    std::vector<TScreenCell> b;

    DrawBuffer(const int width, const int height)
        : width(width), height(height), b(width * height) {
    }

    void setColor(const TColorAttr color) {
        this->color = color;
    }

    void draw(const char symbol, const int x, const int y, const int count = 1) {
        auto cells = getRow(y).subspan(x, count);
        TText::drawChar(cells, symbol, color);
    }

    void draw(const SpecialSymbols symbol, const int x, const int y, const int count = 1) {
        char c = static_cast<char>(symbol);
        draw(c, x, y, count);
    }

    void draw(const std::string &string, const int x, const int y, int maxWidth = -1) {
        if (maxWidth == -1)
            maxWidth = strwidth(string); // Use strwidth() for multibyte character support.
        auto cells = getRow(y).subspan(x, maxWidth);
        TText::drawStr(cells, string, color);
    }

    const TScreenCell* getBuffer() {
        return b.data();
    }

private:

    TSpan<TScreenCell> getRow(const int y) {
        TSpan<TScreenCell> data(b.data(), b.size());
        // Using subspan() is safe: it ensures the returned span is within bounds.
        return data.subspan(y * width, width);
    }
};

Cheers.

@meetanthony

Copy link
Copy Markdown
Author

Thanks for the sample code provided - I checked it and everything works as intended. Well, if you don't think it's worth making these changes to the base code, then I should close the pool request. It's a pity, of course, that someone might encounter this again. It may be worth adding the code with the rendering of the entire control at a time to the examples (although I might have missed something when I examined them).
Thanks again for clarifying the situation, your sample code, and all the work you've done.

@meetanthony
meetanthony deleted the drawbuffer_custom_length branch February 12, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants