Commit d82b35b8 authored by Matan Ziv-Av's avatar Matan Ziv-Av Committed by Tomaz Canabrava
Browse files

Some Sixel fixes

- Define MAX_IMAGE_DIM and do not try to create larger image.
- Define MAX_SIXEL_COLORS.
- Optimize repeat draw operation
parent f7c8dd9b
......@@ -1237,7 +1237,8 @@ void Vt102Emulation::processToken(int token, int p, int q)
case token_csi_pr('s', 2004) : saveMode (MODE_BracketedPaste); break; //XTERM
case token_csi_pr('r', 2004) : restoreMode (MODE_BracketedPaste); break; //XTERM
case token_csi_pr('S', 1) : if(argv[2]==1||argv[2]==4)sendString("\033[?1;0;256S"); break;
case token_csi_pr('S', 1) : sixelQuery (1 ); break;
case token_csi_pr('S', 2) : sixelQuery (2 ); break;
// Set Cursor Style (DECSCUSR), VT520, with the extra xterm sequences
// the first one is a special case, 'ESC[ q', which mimics 'ESC[1 q'
// Using 0 to reset to default is matching VTE, but not any official standard.
......@@ -2118,18 +2119,38 @@ void Vt102Emulation::reportDecodingError()
outputError.append(hexdump2(tokenBuffer, tokenBufferPos));
}
void Vt102Emulation::sixelQuery(int q)
{
char tmp[30];
if (q == 1) {
if (argv[2] == 1 || argv[2] == 4) {
snprintf(tmp, sizeof(tmp), "\033[?1;0;%dS", MAX_SIXEL_COLORS);
sendString(tmp);
}
}
if (q == 2) {
if (argv[2] == 1 || argv[2] == 4) {
snprintf(tmp, sizeof(tmp), "\033[?2;0;%d;%dS", MAX_IMAGE_DIM, MAX_IMAGE_DIM);
sendString(tmp);
}
}
}
void Vt102Emulation::SixelModeEnable(int width, int height)
{
if (width <= 0 || height <= 0) {
return;
}
if (width > MAX_IMAGE_DIM)
width = MAX_IMAGE_DIM;
if (height > MAX_IMAGE_DIM)
height = MAX_IMAGE_DIM;
m_actualSize = QSize(width, height);
// We assume square pixels because I'm lazy and didn't implement the stuff in vt102emulation.
int charactersWidth = width / 6 + 1;
int charactersHeight = height / 6 + 1;
m_currentImage = QImage(charactersWidth * 6 + 1, charactersHeight * 6 + 1, QImage::Format_Indexed8);
m_currentImage = QImage(width, charactersHeight * 6 + 1, QImage::Format_Indexed8);
m_currentColor = 2;
m_currentX = 0;
m_verticalPosition = 0;
......@@ -2172,7 +2193,7 @@ void Vt102Emulation::SixelModeDisable()
void Vt102Emulation::SixelColorChangeRGB(const int index, int red, int green, int blue)
{
if (index < 0) {
if (index < 0 || index >= MAX_SIXEL_COLORS) {
return;
}
......@@ -2187,7 +2208,7 @@ void Vt102Emulation::SixelColorChangeRGB(const int index, int red, int green, in
void Vt102Emulation::SixelColorChangeHSL(const int index, int hue, int saturation, int value)
{
if (index < 0) {
if (index < 0 || index >= MAX_SIXEL_COLORS) {
return;
}
......@@ -2205,7 +2226,7 @@ void Vt102Emulation::SixelColorChangeHSL(const int index, int hue, int saturatio
m_currentColor = index;
}
void Vt102Emulation::SixelCharacterAdd(uint8_t character)
void Vt102Emulation::SixelCharacterAdd(uint8_t character, int repeat)
{
if (!m_SixelStarted) {
return;
......@@ -2224,54 +2245,91 @@ void Vt102Emulation::SixelCharacterAdd(uint8_t character)
character -= '?';
const int top = m_verticalPosition * 6;
const int bottom = (m_verticalPosition + 1) * 6;
if (bottom > MAX_IMAGE_DIM) // Ignore lines below MAX_IMAGE_DIM
return;
repeat = std::clamp(repeat, 1, MAX_IMAGE_DIM - m_currentX); // Won't repeat beyond MAX_IMAGE_DIM
if (bottom >= m_currentImage.height() - 1 || m_currentX >= m_currentImage.width() - 1) {
if (bottom >= m_currentImage.height() - 1 || m_currentX + repeat >= m_currentImage.width()) {
// If we copy out of bounds it gets filled with 0
int newWidth = (qMax(m_currentX + 256, m_currentImage.width() + 256) / 6 + 1) * 6;
int extraWidth = 255 + repeat; // Increase size by at least 256, to avoid increasing for every pixel
int newWidth = qMax(m_currentX + extraWidth, m_currentImage.width() + extraWidth);
int newHeight = (qMax(bottom + 256, m_currentImage.height() + 256) / 6 + 1) * 6;
m_currentImage = m_currentImage.copy(0, 0, newWidth, newHeight);
newWidth = qMin(newWidth, MAX_IMAGE_DIM);
newHeight = qMin(newHeight, MAX_IMAGE_DIM);
if (newWidth != m_currentImage.width() || newHeight != m_currentImage.height())
m_currentImage = m_currentImage.copy(0, 0, newWidth, newHeight);
if (m_currentImage.isNull()) {
m_SixelStarted = false;
return;
}
}
if (m_currentX > m_actualSize.width()) {
m_actualSize.setWidth(m_currentX);
}
if (bottom > m_actualSize.height()) {
m_actualSize.setHeight(bottom);
}
const ptrdiff_t bpl = m_currentImage.bytesPerLine();
uchar *data = m_currentImage.bits() + top * bpl + m_currentX;
if (m_preserveBackground) {
// Konsole is built without _any_ optimizations by default, so a little
// manual unrolling to avoid calling shift for every loop iteration.
if (character & (1 << 0)) {
data[0 * bpl] = m_currentColor;
}
if (character & (1 << 1)) {
data[1 * bpl] = m_currentColor;
}
if (character & (1 << 2)) {
data[2 * bpl] = m_currentColor;
}
if (character & (1 << 3)) {
data[3 * bpl] = m_currentColor;
}
if (character & (1 << 4)) {
data[4 * bpl] = m_currentColor;
}
if (character & (1 << 5)) {
data[5 * bpl] = m_currentColor;
if (repeat == 1) {
if (m_preserveBackground) {
// Konsole is built without _any_ optimizations by default, so a little
// manual unrolling to avoid calling shift for every loop iteration.
if (character & (1 << 0)) {
data[0 * bpl] = m_currentColor;
}
if (character & (1 << 1)) {
data[1 * bpl] = m_currentColor;
}
if (character & (1 << 2)) {
data[2 * bpl] = m_currentColor;
}
if (character & (1 << 3)) {
data[3 * bpl] = m_currentColor;
}
if (character & (1 << 4)) {
data[4 * bpl] = m_currentColor;
}
if (character & (1 << 5)) {
data[5 * bpl] = m_currentColor;
}
} else {
for (int i = 0; i < 6; i++, data += bpl) {
*data = (character & (1 << i)) * m_currentColor;
}
}
m_currentX++;
} else {
for (int i = 0; i < 6; i++, data += bpl) {
*data = (character & (1 << i)) * m_currentColor;
if (m_preserveBackground) {
// Konsole is built without _any_ optimizations by default, so a little
// manual unrolling to avoid calling shift for every loop iteration.
if (character & (1 << 0)) {
memset(&data[0 * bpl], m_currentColor, repeat);
}
if (character & (1 << 1)) {
memset(&data[1 * bpl], m_currentColor, repeat);
}
if (character & (1 << 2)) {
memset(&data[2 * bpl], m_currentColor, repeat);
}
if (character & (1 << 3)) {
memset(&data[3 * bpl], m_currentColor, repeat);
}
if (character & (1 << 4)) {
memset(&data[4 * bpl], m_currentColor, repeat);
}
if (character & (1 << 5)) {
memset(&data[5 * bpl], m_currentColor, repeat);
}
} else {
for (int i = 0; i < 6; i++, data += bpl) {
memset(data, (character & (1 << i)) * m_currentColor, repeat);
}
}
m_currentX += repeat;
}
if (m_currentX > m_actualSize.width()) {
m_actualSize.setWidth(m_currentX);
}
if (bottom > m_actualSize.height()) {
m_actualSize.setHeight(bottom);
}
m_currentX++;
}
bool Vt102Emulation::processSixel(uint cc)
......@@ -2354,10 +2412,7 @@ bool Vt102Emulation::processSixel(uint cc)
return true;
}
// We could optimize this, but meh
for (int i = 0; i < argv[0]; i++) {
SixelCharacterAdd(cc);
}
SixelCharacterAdd(cc, argv[0]);
resetTokenizer();
return true;
}
......@@ -2384,8 +2439,9 @@ bool Vt102Emulation::processSixel(uint cc)
default:
return false;
}
} else if (argc == 1 && argv[0] >= 0) { // could check max, but people should be able to do what they want idc
m_currentColor = index;
} else if (argc == 1 && index >= 0) { // Negative index is an error. Too large index is ignored
if (index < MAX_SIXEL_COLORS)
m_currentColor = index;
} else {
return false;
}
......
......@@ -194,12 +194,15 @@ private:
bool _reportFocusEvents;
// Sixel:
#define MAX_SIXEL_COLORS 256
#define MAX_IMAGE_DIM 16384
void sixelQuery(int query);
bool processSixel(uint cc);
void SixelModeEnable(int width, int height /*, bool preserveBackground*/);
void SixelModeDisable();
void SixelColorChangeRGB(const int index, int red, int green, int blue);
void SixelColorChangeHSL(const int index, int hue, int saturation, int value);
void SixelCharacterAdd(uint8_t character);
void SixelCharacterAdd(uint8_t character, int repeat = 1);
bool m_SixelStarted = false;
QImage m_currentImage;
int m_currentX = 0;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment