roar/libgui/CanvasWidget.cpp
2020-01-14 21:21:49 +01:00

358 lines
9.8 KiB
C++

/*
* roar -- mIRC art editor for Windows & Linux
* Copyright (C) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <execution>
#include <QtGui/QFontDatabase>
#include <QtGui/QPainter>
#include <QtGui/QPaintEvent>
#include "CanvasWidget.hpp"
/*
* Private constants and variables
*/
#define BLEND_ALPHA_COEFFICIENT 0.8
static const Canvas::COLOUR_LIST s_colours = {
{255, 255, 255}, // Bright White
{0, 0, 0}, // Black
{0, 0, 187}, // Light Blue
{0, 187, 0}, // Green
{255, 85, 85}, // Red
{187, 0, 0}, // Light Red
{187, 0, 187}, // Pink
{187, 187, 0}, // Yellow
{255, 255, 85}, // Light Yellow
{85, 255, 85}, // Light Green
{0, 187, 187}, // Cyan
{85, 255, 255}, // Light Cyan
{85, 85, 255}, // Blue
{255, 85, 255}, // Light Pink
{85, 85, 85}, // Grey
{187, 187, 187}, // Light Grey
};
static const Canvas::COLOUR_LIST s_colours_bold = {
{255, 255, 255}, // Bright White
{85, 85, 85}, // Black
{85, 85, 255}, // Light Blue
{85, 255, 85}, // Green
{255, 85, 85}, // Red
{255, 85, 85}, // Light Red
{255, 85, 255}, // Pink
{255, 255, 85}, // Yellow
{255, 255, 85}, // Light Yellow
{85, 255, 85}, // Light Green
{85, 255, 255}, // Cyan
{85, 255, 255}, // Light Cyan
{85, 85, 255}, // Blue
{255, 85, 255}, // Light Pink
{85, 85, 85}, // Grey
{255, 255, 255}, // Light Grey
};
/*
* Public class functions
*/
CanvasWidget::CanvasWidget(
QWidget * parent,
Canvas::SIZE cellSize,
Canvas::SIZE sizeDefault
)
: QOpenGLWidget(parent), canvas(sizeDefault), cellSize(cellSize), charMap(), eventDispatcher(), parent(parent), sizeDefault(sizeDefault)
{
this->font = QFont("DejaVu Sans Mono", 8); // XXX
this->font.setStyleStrategy(QFont::PreferAntialias);
this->imageSize = QSize(this->canvas.size.w * this->cellSize.w, this->canvas.size.h * this->cellSize.h);
this->image = QImage(this->imageSize.width(), this->imageSize.height(), QImage::Format_RGB32);
this->setAutoFillBackground(false);
}
Rtl::Either<bool, Canvas::RECT>
CanvasWidget::renderCells(
bool isCursor,
const Canvas::CELL_LIST& cells
)
{
Canvas::CELL cell_, cell_canvas;
Rtl::Either<bool, Canvas::RECT> rc(true, Canvas::RECT());
CanvasWidget::RECT_EITHER rectEither{Rtl::Either<bool, Canvas::POINT>(false, Canvas::POINT()), Canvas::POINT()};
#ifdef TIMING
auto t0 = Rtl::timeBegin();
#endif /* TIMING */
for (auto& cell : cells) {
cell_ = this->cellFetch(cell, false);
if ((cell_.p.x >= this->canvas.size.w) || (cell_.p.y >= this->canvas.size.h))
continue;
else if (isCursor) {
cell_canvas = this->cellFetch(cell, true); cell_.attrs = cell_canvas.attrs;
if (COLOUR_ALPHA(cell_.bg) != 0xff) {
// cell_.fg = this->blendColours(cell_canvas.fg, cell_.bg, BLEND_ALPHA_COEFFICIENT);
// cell_.bg = this->blendColours(cell_canvas.bg, cell_.bg, BLEND_ALPHA_COEFFICIENT);
if ((cell_canvas.txt[0] == L' ') && (COLOUR_ALPHA(cell_canvas.bg) == 0xff))
cell_.txt[0] = L'\u2591', cell_.txt[1] = L'\0';
else
memcpy(cell_.txt, cell_canvas.txt, sizeof(cell_.txt));
} else if (COLOUR_ALPHA(cell_canvas.bg) == 0xff) {
cell_.fg = cell_canvas.fg, cell_.bg = cell_canvas.bg;
if (cell_canvas.txt[0] == L' ')
cell_.txt[0] = L'\u2591', cell_.txt[1] = L'\0';
else
memcpy(cell_.txt, cell_canvas.txt, sizeof(cell_.txt));
} else {
cell_.fg = cell_canvas.fg, cell_.bg = cell_canvas.bg;
memcpy(cell_.txt, cell_canvas.txt, sizeof(cell_.txt));
}
} else if ((cell_.txt[0] == L' ') && (COLOUR_ALPHA(cell_.bg) == 0xff))
cell_.bg = 0x00000000, cell_.txt[0] = L'\u2591', cell_.txt[1] = L'\0';
#if 0
if ((cell_.txt[0] != L' ') && (cell_.txt[0] != L'_'))
this->cellDrawText(cell_, rectEither);
else
this->cellDraw(cell_, rectEither);
if (cell.attrs & Canvas::CATTR_UNDERLINE)
this->cellDrawLine(cell_, rectEither);
#endif
}
if (rc)
rc.right.p0 = rectEither.p0.right, rc.right.p1 = rectEither.p1;
return rc;
}
bool
CanvasWidget::focusNextPrevChild(
bool next
)
{
(void)next;
return false;
}
bool
CanvasWidget::import(
Rtl::STATUS& pstatus,
const std::wstring& data,
Canvas::DATA_TYPE type
)
{
if (this->canvas.import(pstatus, data, type)) {
this->imageSize = QSize(this->canvas.size.w * this->cellSize.w, this->canvas.size.h * this->cellSize.h);
this->image = QImage(this->imageSize.width(), this->imageSize.height(), QImage::Format_RGB32);
this->resize(this->imageSize);
for (auto row : this->canvas.cells)
this->renderCells(false, row);
this->update();
}
return (bool)pstatus;
}
bool
CanvasWidget::import(
Rtl::STATUS& pstatus,
Canvas::CELL_MAP& cells,
Canvas::SIZE size
)
{
if (this->canvas.import(pstatus, cells, size)) {
this->imageSize = QSize(this->canvas.size.w * this->cellSize.w, this->canvas.size.h * this->cellSize.h);
this->image = QImage(this->imageSize.width(), this->imageSize.height(), QImage::Format_RGB32);
this->resize(this->imageSize);
for (auto row : this->canvas.cells)
this->renderCells(false, row);
this->update();
}
return (bool)pstatus;
}
void
CanvasWidget::keyPressEvent(
QKeyEvent * event
)
{
if (this->eventDispatcher)
this->eventDispatcher(event, nullptr, nullptr, CanvasWidget::ETYPE_KEYBOARD_PRESS);
}
void
CanvasWidget::keyReleaseEvent(
QKeyEvent * event
)
{
if (this->eventDispatcher)
this->eventDispatcher(event, nullptr, nullptr, CanvasWidget::ETYPE_KEYBOARD_RELEASE);
}
QSize
CanvasWidget::minimumSizeHint(
) const
{
return this->imageSize;
}
void
CanvasWidget::mouseMoveEvent(
QMouseEvent * event
)
{
if (this->eventDispatcher)
this->eventDispatcher(nullptr, event, nullptr, CanvasWidget::ETYPE_MOUSE_MOVE);
}
void
CanvasWidget::mousePressEvent(
QMouseEvent * event
)
{
if (this->eventDispatcher)
this->eventDispatcher(nullptr, event, nullptr, CanvasWidget::ETYPE_MOUSE_PRESS);
}
void
CanvasWidget::mouseReleaseEvent(
QMouseEvent * event
)
{
if (this->eventDispatcher)
this->eventDispatcher(nullptr, event, nullptr, CanvasWidget::ETYPE_MOUSE_RELEASE);
}
void
CanvasWidget::setEventDispatcher(
EVENT_DISPATCHER eventDispatcher
)
{
this->eventDispatcher = eventDispatcher;
}
QSize
CanvasWidget::sizeHint(
) const
{
return this->imageSize;
}
void
CanvasWidget::wheelEvent(
QWheelEvent * event
)
{
if (this->eventDispatcher)
this->eventDispatcher(nullptr, nullptr, event, CanvasWidget::ETYPE_MOUSE_WHEEL);
}
/*
* Private class methods
*/
Canvas::CELL
CanvasWidget::cellFetch(
const Canvas::CELL& cell,
bool fromCanvas
)
{
Canvas::CELL cell_;
const Canvas::COLOUR_LIST * pcolours;
if (!fromCanvas)
cell_ = Canvas::CELL(cell);
else
cell_ = this->canvas.cells[cell.p.y][cell.p.x];
if (cell_.attrs & Canvas::CATTR_BOLD)
pcolours = &s_colours_bold;
else
pcolours = &s_colours;
if ((cell_.txt[0] == L'_') && (cell_.txt[1] == L'\0'))
cell_.attrs = (Canvas::CELL_ATTRS)(cell_.attrs | Canvas::CATTR_UNDERLINE);
cell_.bg = (cell_.bg == -1) ? 0xff000000 : (Canvas::COLOUR)(((s_colours[(uint8_t)cell_.bg][0] & 0xff) << 16) | ((s_colours[(uint8_t)cell_.bg][1] & 0xff) << 8) | ((s_colours[(uint8_t)cell_.bg][2] & 0xff)));
cell_.fg = (cell_.fg == -1) ? cell_.bg : (Canvas::COLOUR)((((*pcolours)[(uint8_t)cell_.fg][0] & 0xff) << 16) | (((*pcolours)[(uint8_t)cell_.fg][1] & 0xff) << 8) | (((*pcolours)[(uint8_t)cell_.fg][2] & 0xff)));
return cell_;
}
/*
* Protected class methods
*/
#if 0
static const char *vertexShaderSource =
"attribute highp vec4 posAttr;\n"
"attribute lowp vec4 colAttr;\n"
"varying lowp vec4 col;\n"
"uniform highp mat4 matrix;\n"
"void main() {\n"
" col = colAttr;\n"
" gl_Position = matrix * posAttr;\n"
"}\n";
static const char *fragmentShaderSource =
"varying lowp vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n";
#endif
void
CanvasWidget::initializeGL(
)
{
this->initializeOpenGLFunctions();
#if 0
glClearColor(0, 0, 0, m_transparent ? 0 : 1);
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, m_core ? fragmentShaderSourceCore : fragmentShaderSource);
m_program->bindAttributeLocation("vertex", 0);
m_program->bindAttributeLocation("normal", 1);
m_program->link();
m_program->bind();
m_projMatrixLoc = m_program->uniformLocation("projMatrix");
m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
m_lightPosLoc = m_program->uniformLocation("lightPos");
// Create a vertex array object. In OpenGL ES 2.0 and OpenGL 2.x
// implementations this is optional and support may not be present
// at all. Nonetheless the below code works in all cases and makes
// sure there is a VAO when one is needed.
m_vao.create();
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
// Setup our vertex buffer object.
m_logoVbo.create();
m_logoVbo.bind();
m_logoVbo.allocate(m_logo.constData(), m_logo.count() * sizeof(GLfloat));
// Store the vertex attribute bindings for the program.
setupVertexAttribs();
// Our camera never changes in this example.
m_camera.setToIdentity();
m_camera.translate(0, 0, -1);
// Light position is fixed.
m_program->setUniformValue(m_lightPosLoc, QVector3D(0, 0, 70));
m_program->release();
#endif
}