/* * Qt UI * * Copyright (C) 2014 Samsung Electronics Co., Ltd. All rights reserved. * * Contact: * Jinhyung Jo * GiWoong Kim * SeokYeon Hwang * Stanislav Vorobiov * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * Contributors: * - S-Core Co., Ltd * */ #include #include #include "displayglwidget.h" #include "input/multitouchtracker.h" extern "C" { extern uint32_t brightness_level; extern bool display_off; extern uint8_t brightness_tbl[]; }; // number of coordinates that two triangles to make a rectangle #define COORD_COUNT (6) // coordinate is the point of two float #define COORDS_SIZE (2 * 4 * COORD_COUNT) // vertex coords + texture coords #define FULL_COORDS_SIZE (2 * COORDS_SIZE) // cast an uint to void pointer #define TO_VOIDP(x) ((const void *)((uintptr_t)(x))) static const char *vs_tex_source_gl2 = "#version 120\n\n" "attribute vec4 vertCoord;\n" "uniform mat4 proj;\n" "attribute vec2 texCoord;\n" "varying vec2 v_texCoord;\n" "void main()\n" "{\n" " v_texCoord = texCoord;\n" " gl_Position = proj * vertCoord;\n" "}\n"; static const char *vs_tex_source_gl3 = "#version 140\n\n" "in vec4 vertCoord;\n" "uniform mat4 proj;\n" "in vec2 texCoord;\n" "out vec2 v_texCoord;\n" "void main()\n" "{\n" " v_texCoord = texCoord;\n" " gl_Position = proj * vertCoord;\n" "}\n"; static const char *fs_dpy_source_gl2 = "#version 120\n\n" "uniform sampler2D tex;\n" "uniform float brightness;\n" "varying vec2 v_texCoord;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, v_texCoord) * brightness;\n" "}\n"; static const char *fs_dpy_source_gl3 = "#version 140\n\n" "uniform sampler2D tex;\n" "uniform float brightness;\n" "in vec2 v_texCoord;\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = texture(tex, v_texCoord) * brightness;\n" "}\n"; static const char *fs_scale_source_gl2 = "#version 120\n\ \n\ uniform sampler2D tex;\n\ uniform float brightness;\n\ uniform vec2 texSize;\n\ varying vec2 v_texCoord;\n\ vec4 cubic(float x)\n\ {\n\ float x2 = x * x;\n\ float x3 = x2 * x;\n\ vec4 w;\n\ w.x = -x3 + 3*x2 - 3*x + 1;\n\ w.y = 3*x3 - 6*x2 + 4;\n\ w.z = -3*x3 + 3*x2 + 3*x + 1;\n\ w.w = x3;\n\ return w / 6.f;\n\ }\n\ void main()\n\ {\n\ vec2 texscale = vec2(1.0 / texSize.x, 1.0 / texSize.y);\n\ vec2 texcoord = vec2(v_texCoord.x * texSize.x, v_texCoord.y * texSize.y);\n\ float fx = fract(texcoord.x);\n\ float fy = fract(texcoord.y);\n\ texcoord.x -= fx;\n\ texcoord.y -= fy;\n\ \n\ vec4 xcubic = cubic(fx);\n\ vec4 ycubic = cubic(fy);\n\ \n\ vec4 c = vec4(texcoord.x - 0.5, texcoord.x + 1.5, texcoord.y -\n\ 0.5, texcoord.y + 1.5);\n\ vec4 s = vec4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x +\n\ ycubic.y, ycubic.z + ycubic.w);\n\ vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s;\n\ \n\ vec4 sample0 = texture2D(tex, vec2(offset.x, offset.z) *\n\ texscale);\n\ vec4 sample1 = texture2D(tex, vec2(offset.y, offset.z) *\n\ texscale);\n\ vec4 sample2 = texture2D(tex, vec2(offset.x, offset.w) *\n\ texscale);\n\ vec4 sample3 = texture2D(tex, vec2(offset.y, offset.w) *\n\ texscale);\n\ \n\ float sx = s.x / (s.x + s.y);\n\ float sy = s.z / (s.z + s.w);\n\ \n\ gl_FragColor = mix(\n\ mix(sample3, sample2, sx),\n\ mix(sample1, sample0, sx), sy) * brightness;\n\ }"; static const char *fs_scale_source_gl3 = "#version 140\n\ \n\ uniform sampler2D tex;\n\ uniform float brightness;\n\ uniform vec2 texSize;\n\ in vec2 v_texCoord;\n\ out vec4 FragColor;\n\ vec4 cubic(float x)\n\ {\n\ float x2 = x * x;\n\ float x3 = x2 * x;\n\ vec4 w;\n\ w.x = -x3 + 3*x2 - 3*x + 1;\n\ w.y = 3*x3 - 6*x2 + 4;\n\ w.z = -3*x3 + 3*x2 + 3*x + 1;\n\ w.w = x3;\n\ return w / 6.f;\n\ }\n\ void main()\n\ {\n\ vec2 texscale = vec2(1.0 / texSize.x, 1.0 / texSize.y);\n\ vec2 texcoord = vec2(v_texCoord.x * texSize.x, v_texCoord.y * texSize.y);\n\ float fx = fract(texcoord.x);\n\ float fy = fract(texcoord.y);\n\ texcoord.x -= fx;\n\ texcoord.y -= fy;\n\ \n\ vec4 xcubic = cubic(fx);\n\ vec4 ycubic = cubic(fy);\n\ \n\ vec4 c = vec4(texcoord.x - 0.5, texcoord.x + 1.5, texcoord.y -\n\ 0.5, texcoord.y + 1.5);\n\ vec4 s = vec4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x +\n\ ycubic.y, ycubic.z + ycubic.w);\n\ vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) /\n\ s;\n\ \n\ vec4 sample0 = texture(tex, vec2(offset.x, offset.z) *\n\ texscale);\n\ vec4 sample1 = texture(tex, vec2(offset.y, offset.z) *\n\ texscale);\n\ vec4 sample2 = texture(tex, vec2(offset.x, offset.w) *\n\ texscale);\n\ vec4 sample3 = texture(tex, vec2(offset.y, offset.w) *\n\ texscale);\n\ \n\ float sx = s.x / (s.x + s.y);\n\ float sy = s.z / (s.z + s.w);\n\ \n\ FragColor = mix(\n\ mix(sample3, sample2, sx),\n\ mix(sample1, sample0, sx), sy) * brightness;\n\ }"; DisplayGLWidget::DisplayGLWidget(QWidget *parent, DisplayType *displayForm, QSize resolution, qreal scaleFactor) : QOpenGLWidget(parent), DisplayBase(displayForm, resolution, scaleFactor, this), mGuestResolution(resolution) { this->maskQImage = displayForm->getMask().toImage(); this->maskTexture = 0; this->needScale = false; this->mtTexture = 0; this->texProgram = NULL; this->scaleProgram = NULL; this->next = NULL; this->current = NULL; /* to be enable to drop events */ setAcceptDrops(true); } void DisplayGLWidget::changedTexture(struct dpy_item *item) { // wait while "next" is being updated since it is fast enough. textureListMutex.lock(); if (next) { qemu_mutex_lock(&next->mutex); next->available = true; qemu_mutex_unlock(&next->mutex); } next = item; textureListMutex.unlock(); this->update(); } float DisplayGLWidget::getBrightness() { if (display_off) { return 0.0f; } return ((float)(255 - brightness_tbl[brightness_level]) / 255.0f); } // TODO: if possible, simply clean up rendeing & init code void DisplayGLWidget::drawQuad(GLuint textureID, bool isMask) { QOpenGLShaderProgram *program = needScale ? scaleProgram : texProgram; mFuncs->glBindTexture(GL_TEXTURE_2D, textureID); program->bind(); program->enableAttributeArray("vertCoord"); program->enableAttributeArray("texCoord"); program->setUniformValue("proj", mOrtho); if (needScale) { GLfloat texSize[2]; texSize[0] = (GLfloat)mGuestResolution.width(); texSize[1] = (GLfloat)mGuestResolution.height(); program->setUniformValueArray("texSize", texSize, 1, 2); } program->setUniformValue("brightness", isMask ? 1.0f : getBrightness()); mVBO->bind(); mVBO->write(0, mVertCoords, COORDS_SIZE); mVBO->write(COORDS_SIZE, mTexCoords, COORDS_SIZE); program->setAttributeArray("vertCoord", GL_FLOAT, NULL, 2, 0); program->setAttributeArray("texCoord", GL_FLOAT, TO_VOIDP(COORDS_SIZE), 2, 0); mVBO->release(); mFuncs->glDrawArrays(GL_TRIANGLES, 0, COORD_COUNT); program->disableAttributeArray("texCoord"); program->disableAttributeArray("vertCoord"); program->release(); } void DisplayGLWidget::drawMultiTouchPoints() { MultiTouchTracker *mtTracker = getTouchScreenHelper()->getMtTracker(); QList pointList = mtTracker->getTouchPointList(); if (pointList.isEmpty()) { return; } mVBO->bind(); if (mVBO->size() < pointList.count() * (int)FULL_COORDS_SIZE) { mVBO->allocate(pointList.count() * FULL_COORDS_SIZE); } for (int i = 0; i < pointList.count(); i++) { TouchPoint *p = pointList.at(i); GLfloat vertCoords[12]; GLfloat x = p->getHostPos().x() - 32 / 2; GLfloat y = height() - p->getHostPos().y() - 32 / 2; vertCoords[6] = vertCoords[0] = x; vertCoords[7] = vertCoords[1] = y; vertCoords[2] = x + 32; vertCoords[3] = y; vertCoords[8] = vertCoords[4] = x + 32; vertCoords[9] = vertCoords[5] = y + 32; vertCoords[10] = x; vertCoords[11] = y + 32; mVBO->write(i * COORDS_SIZE, vertCoords, COORDS_SIZE); mVBO->write(pointList.count() * COORDS_SIZE + i * COORDS_SIZE, mTexCoords, COORDS_SIZE); } mFuncs->glBlendFunc(GL_SRC_COLOR, GL_ONE); mFuncs->glBindTexture(GL_TEXTURE_2D, mtTexture); texProgram->bind(); texProgram->enableAttributeArray("vertCoord"); texProgram->enableAttributeArray("texCoord"); QMatrix4x4 mat; mat.ortho(0.0f, (float)width(), 0.0f, (float)height(), -1.0f, 1.0f); texProgram->setUniformValue("proj", mat); texProgram->setUniformValue("brightness", 0.7f); texProgram->setAttributeArray("vertCoord", GL_FLOAT, NULL, 2, 0); texProgram->setAttributeArray("texCoord", GL_FLOAT, TO_VOIDP(pointList.count() * COORDS_SIZE), 2, 0); mVBO->release(); mFuncs->glDrawArrays(GL_TRIANGLES, 0, pointList.count() * COORD_COUNT); texProgram->disableAttributeArray("texCoord"); texProgram->disableAttributeArray("vertCoord"); texProgram->release(); } /* override */ void DisplayGLWidget::initializeGL() { qDebug("initialize GL: OpenGL %d.%d(Profile: %d)", format().majorVersion(), format().minorVersion(), format().profile()); mFuncs = QOpenGLContext::currentContext()->functions(); QOpenGLShader *vShader = new QOpenGLShader(QOpenGLShader::Vertex, this); QOpenGLShader *dpyFgShader = new QOpenGLShader(QOpenGLShader::Fragment, this); QOpenGLShader *scaleFgShader = new QOpenGLShader(QOpenGLShader::Fragment, this); if (format().majorVersion() > 2) { vShader->compileSourceCode(vs_tex_source_gl3); dpyFgShader->compileSourceCode(fs_dpy_source_gl3); scaleFgShader->compileSourceCode(fs_scale_source_gl3); } else { vShader->compileSourceCode(vs_tex_source_gl2); dpyFgShader->compileSourceCode(fs_dpy_source_gl2); scaleFgShader->compileSourceCode(fs_scale_source_gl2); } texProgram = new QOpenGLShaderProgram(this); texProgram->addShader(vShader); texProgram->addShader(dpyFgShader); scaleProgram = new QOpenGLShaderProgram(this); scaleProgram->addShader(vShader); scaleProgram->addShader(scaleFgShader); texProgram->link(); scaleProgram->link(); mVAO = new QOpenGLVertexArrayObject; if (mVAO->create()) { mVAO->bind(); } mVBO = new QOpenGLBuffer; mVBO->setUsagePattern(QOpenGLBuffer::StreamDraw); mVBO->create(); mVBO->bind(); mVBO->allocate(FULL_COORDS_SIZE); mVBO->release(); mVertCoords[6] = mVertCoords[0] = 0; mVertCoords[7] = mVertCoords[1] = mGuestResolution.height(); mVertCoords[2] = mGuestResolution.width(); mVertCoords[3] = mGuestResolution.height(); mVertCoords[8] = mVertCoords[4] = mGuestResolution.width(); mVertCoords[9] = mVertCoords[5] = 0; mVertCoords[10] = 0; mVertCoords[11] = 0; mTexCoords[6] = mTexCoords[0] = 0; mTexCoords[7] = mTexCoords[1] = 0; mTexCoords[2] = 1; mTexCoords[3] = 0; mTexCoords[8] = mTexCoords[4] = 1; mTexCoords[9] = mTexCoords[5] = 1; mTexCoords[10] = 0; mTexCoords[11] = 1; if (isTsEnabled) { GLuint curTexture = 0; mFuncs->glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *)&curTexture); const QImage img = getTouchScreenHelper()->getMtTracker()->getPointImage(); mFuncs->glGenTextures(1, &mtTexture); mFuncs->glBindTexture(GL_TEXTURE_2D, mtTexture); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); mFuncs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (const void *)img.constBits()); mFuncs->glBindTexture(GL_TEXTURE_2D, curTexture); } if (!maskQImage.isNull()) { GLuint curTexture = 0; mFuncs->glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *)&curTexture); mFuncs->glGenTextures(1, &maskTexture); mFuncs->glBindTexture(GL_TEXTURE_2D, maskTexture); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); mFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); mFuncs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, maskQImage.width(), maskQImage.height(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (const void *)maskQImage.constBits()); mFuncs->glBindTexture(GL_TEXTURE_2D, curTexture); } mFuncs->glDisable(GL_DEPTH_TEST); mFuncs->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); mFuncs->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } /* override */ void DisplayGLWidget::paintGL() { mFuncs->glEnable(GL_BLEND); if (maskTexture) { mFuncs->glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_SRC_ALPHA, GL_ZERO); drawQuad(maskTexture, true); } mFuncs->glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); mOrtho.setToIdentity(); mOrtho.rotate((float)-rotateAngle, 0.0f, 0.0f, 1.0f); mOrtho.ortho(0.0f, (float)mGuestResolution.width(), 0.0f, (float)mGuestResolution.height(), -1.0f, 1.0f); needScale = (mGuestResolution.width() != width()) || (mGuestResolution.height() != height()); // wait while "next" is being updated since it is fast enough. textureListMutex.lock(); if (next) { if (current) { qemu_mutex_lock(¤t->mutex); current->available = true; qemu_mutex_unlock(¤t->mutex); } current = next; next = NULL; } textureListMutex.unlock(); if (current) { drawQuad(current->tex, false); } if (isTsEnabled) { drawMultiTouchPoints(); } mFuncs->glDisable(GL_BLEND); mFuncs->glFinish(); } /* override */ void DisplayGLWidget::resizeGL(int w, int h) { qDebug("resizeGL"); handleResize(); // TODO: check changing of the guest resolution // for 'Runtime Resolution Change' } /* override */ void DisplayGLWidget::dragEnterEvent(QDragEnterEvent *event) { handleDragEnterEvent(event); } /* override */ void DisplayGLWidget::dropEvent(QDropEvent *event) { handleDropEvent(event); } /* override */ void DisplayGLWidget::mousePressEvent(QMouseEvent *event) { handleMousePress(event); if (isMovingMode() == true) { /* do nothing */ return; } } /* override */ void DisplayGLWidget::mouseReleaseEvent(QMouseEvent *event) { handleMouseRelease(event); if (isMovingMode() == true) { /* do nothing */ return; } } /* override */ void DisplayGLWidget::mouseMoveEvent(QMouseEvent *event) { handleMouseMove(event); if (isMovingMode() == true) { /* do nothing */ return; } } /* override */ void DisplayGLWidget::enterEvent(QEvent *event) { handleMouseEnter(event); if (isMovingMode() == true) { /* do nothing */ return; } } /* override */ void DisplayGLWidget::leaveEvent(QEvent *event) { handleMouseLeave(event); if (isMovingMode() == true) { /* do nothing */ return; } } // TODO: To correct releasing resources DisplayGLWidget::~DisplayGLWidget() { makeCurrent(); if (mtTexture) { mFuncs->glDeleteTextures(1, &mtTexture); } if (maskTexture) { mFuncs->glDeleteTextures(1, &maskTexture); } mVAO->destroy(); mVBO->destroy(); texProgram->removeAllShaders(); scaleProgram->removeAllShaders(); delete mVAO; delete mVBO; delete texProgram; delete scaleProgram; doneCurrent(); }