2106 lines
62 KiB
C++
2106 lines
62 KiB
C++
/******************************************************************************
|
|
QtAV: Multimedia framework based on Qt and FFmpeg
|
|
Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
|
|
|
|
* This file is part of QtAV (from 2014)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
******************************************************************************/
|
|
|
|
#include "../OpenGLVideo.h"
|
|
#include <QtGui/QColor>
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
#include <QtGui/QGuiApplication>
|
|
#include <QtGui/QScreen>
|
|
#include <QtGui/QSurface>
|
|
#endif //5.0
|
|
#include "../SurfaceInterop.h"
|
|
#include "../VideoShader.h"
|
|
#include "ShaderManager.h"
|
|
#include "../GeometryRenderer.h"
|
|
#include "OpenGLHelper.h"
|
|
#include "../Logger.h"
|
|
|
|
namespace FAV {
|
|
|
|
#if (RM_MODEL_360) // EMT SINGLE 360
|
|
#define TEST_EQUIRECTANGULAR 0
|
|
inline double snapAngle(double a) {
|
|
if(a > 360.0) {
|
|
return a - 360.0;
|
|
}
|
|
else if (a < 0.0) {
|
|
a = 360.0 + a;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
// FIXME: why crash if inherits both QObject and DPtrPrivate?
|
|
class OpenGLVideoPrivate : public DPtrPrivate<OpenGLVideo>
|
|
{
|
|
public:
|
|
OpenGLVideoPrivate()
|
|
: ctx(0)
|
|
, manager(0)
|
|
, material(new VideoMaterial())
|
|
, material_type(0)
|
|
, norm_viewport(true)
|
|
, update_geo(true)
|
|
, tex_target(0)
|
|
, valiad_tex_width(1.0)
|
|
, mesh_type(OpenGLVideo::RectMesh) // OpenGLVideo::RectMesh,SphereMesh
|
|
, geometry(NULL)
|
|
, user_shader(NULL)
|
|
, dAngleX(0)
|
|
, dAngleY(0)
|
|
, mode(0)
|
|
, bVFlip(false)
|
|
, bHFlip(false)
|
|
{
|
|
}
|
|
~OpenGLVideoPrivate() {
|
|
if (material) {
|
|
delete material;
|
|
material = 0;
|
|
}
|
|
delete geometry;
|
|
}
|
|
|
|
// 0~360 = 시계방향
|
|
// double dAngle;
|
|
// double dPitch;
|
|
double dAngleX; // 0~360
|
|
double dAngleY; // 0~180 (아래쪽만 회전 가능)
|
|
int mode;
|
|
|
|
bool bVFlip; // 상하 반전
|
|
bool bHFlip; // 좌우반전
|
|
|
|
void resetGL() {
|
|
ctx = 0;
|
|
gr.updateGeometry(NULL);
|
|
if (!manager)
|
|
return;
|
|
manager->setParent(0);
|
|
delete manager;
|
|
manager = 0;
|
|
if (material) {
|
|
delete material;
|
|
material = 0;
|
|
}
|
|
|
|
}
|
|
// update geometry(vertex array) set attributes or bind VAO/VBO.
|
|
void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r);
|
|
public:
|
|
QOpenGLContext *ctx;
|
|
ShaderManager *manager;
|
|
VideoMaterial *material;
|
|
qint64 material_type;
|
|
bool norm_viewport;
|
|
bool has_a;
|
|
bool update_geo;
|
|
int tex_target;
|
|
qreal valiad_tex_width;
|
|
QSize video_size;
|
|
QRectF target;
|
|
QRectF roi; //including invalid padding width
|
|
OpenGLVideo::MeshType mesh_type;
|
|
TexturedGeometry *geometry;
|
|
GeometryRenderer gr;
|
|
|
|
//TexturedGeometry *geometry2;
|
|
//GeometryRenderer gr2;
|
|
QRectF rect;
|
|
QMatrix4x4 matrix;
|
|
VideoShader *user_shader;
|
|
};
|
|
|
|
void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r)
|
|
{
|
|
// also check size change for normalizedROI computation if roi is not normalized
|
|
const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize();
|
|
const int tc = shader->textureLocationCount();
|
|
if (roi_changed) {
|
|
roi = r;
|
|
valiad_tex_width = material->validTextureWidth();
|
|
video_size = material->frameSize();
|
|
}
|
|
if (tex_target != shader->textureTarget()) {
|
|
tex_target = shader->textureTarget();
|
|
update_geo = true;
|
|
}
|
|
// QRectF(-1, 1, 2, -2) = 정상 V-FLIP = QRectF(-1, -1, 2, 2) -> 3D 모드에서는 동작하지 않는다 각도로 조정
|
|
qreal top = bVFlip ? -1 : 1;
|
|
qreal bottom = bVFlip ? 2: -2;
|
|
qreal left = bHFlip ? 1 : -1;
|
|
qreal right = bHFlip ? -2 : 2;
|
|
|
|
//qInfo() << top << bottom << left << right << __FUNCTION__;
|
|
|
|
const QRectF gp = QRectF(left,top,right,bottom); //bVFlip ? QRectF(-1, -1, 2, 2) : QRectF(-1, 1, 2, -2);
|
|
QRectF target_rect = norm_viewport ? gp : rect;
|
|
|
|
if (target.isValid()) {
|
|
if (roi_changed || target != t) {
|
|
target = t;
|
|
update_geo = true;
|
|
//target_rect = target (if valid). // relate to gvf bug?
|
|
}
|
|
} else {
|
|
if (roi_changed) {
|
|
update_geo = true;
|
|
}
|
|
}
|
|
if (!update_geo)
|
|
return;
|
|
delete geometry;
|
|
geometry = NULL;
|
|
|
|
if (mesh_type == OpenGLVideo::SphereMesh)
|
|
{
|
|
geometry = new Sphere();
|
|
}
|
|
else
|
|
{
|
|
geometry = new TexturedGeometry();
|
|
}
|
|
|
|
//qDebug() << "target rect: " << target_rect ;
|
|
geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1);
|
|
geometry->setGeometryRect(target_rect);
|
|
//geometry->setTextureRect(QRectF(0,0,1.0,1.0));
|
|
geometry->setTextureRect(material->mapToTexture(0, roi));
|
|
if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) {
|
|
for (int i = 1; i < tc; ++i) {
|
|
// tc can > planes, but that will compute chroma plane
|
|
geometry->setTextureRect(material->mapToTexture(i, roi), i);
|
|
}
|
|
}
|
|
|
|
|
|
geometry->create();
|
|
update_geo = false;
|
|
gr.updateGeometry(geometry);
|
|
}
|
|
|
|
OpenGLVideo::OpenGLVideo() {
|
|
bVRFlip = false;
|
|
zoomLevel = 0;
|
|
}
|
|
|
|
|
|
|
|
bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt)
|
|
{
|
|
return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid;
|
|
}
|
|
void OpenGLVideo::getRenderInfo(RENDER_INFO_360* info)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
info->dAngleX = d.dAngleX;
|
|
info->dAngleY = d.dAngleY;
|
|
info->mode = d.mode;
|
|
info->bVFlip = d.bVFlip;
|
|
info->bHFlip = d.bHFlip;
|
|
}
|
|
|
|
void OpenGLVideo::setRenderInfo(RENDER_INFO_360* info)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
zoomLevel = 0;
|
|
zoomLevel2D = 0;
|
|
setMode(info->mode); // RESET ZOOM
|
|
if(info->mode == 2) {
|
|
setAngle(info->dAngleX,info->dAngleY);
|
|
}
|
|
d.bHFlip = info->bHFlip; // 설정만
|
|
setVFlip(info->bVFlip); // 적용
|
|
resetAngle();
|
|
}
|
|
|
|
void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if (d.ctx == ctx)
|
|
return;
|
|
qreal b = 0, c = 0, h = 0, s = 0;
|
|
if (d.material) {
|
|
b = d.material->brightness();
|
|
c = d.material->contrast();
|
|
h = d.material->hue();
|
|
s = d.material->saturation();
|
|
delete d.material;
|
|
d.material = 0;
|
|
}
|
|
d.resetGL(); //TODO: is it ok to destroygl resources in another context?
|
|
d.ctx = ctx; // Qt4: set to null in resetGL()
|
|
if (!ctx) {
|
|
return;
|
|
}
|
|
d.material = new VideoMaterial();
|
|
d.material->setBrightness(b);
|
|
d.material->setContrast(c);
|
|
d.material->setHue(h);
|
|
d.material->setSaturation(s);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
d.manager = ctx->findChild<ShaderManager*>(QStringLiteral("__qtav_shader_manager"));
|
|
QSizeF surfaceSize = ctx->surface()->size();
|
|
//qInfo() << "SURFACE SIZE:(0)" << surfaceSize << __FUNCTION__;
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
|
|
surfaceSize *= ctx->screen()->devicePixelRatio();
|
|
#else
|
|
surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's
|
|
#endif
|
|
#else
|
|
QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height());
|
|
#endif
|
|
//qInfo() << "SURFACE SIZE:(2)" << surfaceSize << __FUNCTION__;
|
|
|
|
//qInfo() << "DEBUG_OPENGL(13)" << " ctx->surface():" << surfaceSize; // 1,1
|
|
//qInfo() << "DEBUG_OPENGL(12)" << " ctx->surface():" << ctx->surface()->size(); // 1,1
|
|
//qInfo() << "DEBUG_OPENGL(11)" << " ctx->screen():" << ctx->screen()->availableSize() << __FUNCTION__; // 전체 화면 해상도
|
|
|
|
setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize));
|
|
if (d.manager)
|
|
return;
|
|
// TODO: what if ctx is delete?
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
d.manager = new ShaderManager(ctx);
|
|
QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()?
|
|
#else
|
|
d.manager = new ShaderManager(this);
|
|
#endif
|
|
d.manager->setObjectName(QStringLiteral("__qtav_shader_manager"));
|
|
/// get gl info here because context is current(qt ensure it)
|
|
//const QByteArray extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
|
|
bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms();
|
|
qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL);
|
|
static bool sInfo = true;
|
|
if (sInfo) {
|
|
sInfo = false;
|
|
qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION)));
|
|
qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR)));
|
|
qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER)));
|
|
qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION)));
|
|
/// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null
|
|
bool v = OpenGLHelper::isOpenGLES();
|
|
qDebug("Is OpenGLES: %d", v);
|
|
v = OpenGLHelper::isEGL();
|
|
qDebug("Is EGL: %d", v);
|
|
const int glsl_ver = OpenGLHelper::GLSLVersion();
|
|
qDebug("GLSL version: %d", glsl_ver);
|
|
v = OpenGLHelper::isPBOSupported();
|
|
qDebug("Has PBO: %d", v);
|
|
v = OpenGLHelper::has16BitTexture();
|
|
qDebug("Has 16bit texture: %d", v);
|
|
v = OpenGLHelper::hasRG();
|
|
qDebug("Has RG texture: %d", v);
|
|
qDebug() << ctx->format();
|
|
}
|
|
}
|
|
|
|
QOpenGLContext* OpenGLVideo::openGLContext()
|
|
{
|
|
return d_func().ctx;
|
|
}
|
|
|
|
void OpenGLVideo::setCurrentFrame(const VideoFrame &frame)
|
|
{
|
|
d_func().material->setCurrentFrame(frame);
|
|
d_func().has_a = frame.format().hasAlpha();
|
|
}
|
|
|
|
void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v)
|
|
{
|
|
setViewport(v);
|
|
}
|
|
|
|
void OpenGLVideo::setViewport(const QRectF &r)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.rect = r;
|
|
if (d.norm_viewport)
|
|
{
|
|
d.matrix.setToIdentity();
|
|
if (d.mesh_type == SphereMesh)
|
|
{
|
|
//setAngle(d.dAngle);
|
|
setAngle(d.dAngleX,d.dAngleY);
|
|
|
|
// float verticalAngle, float aspectRatio, float nearPlane, float farPlane
|
|
//d.matrix.perspective(120.0f, 0.8f, 0.1f, 100.0f); // for sphere
|
|
//d.matrix.perspective(90.0f, 2.0f, 0.0f, 1.0f); // for sphere
|
|
//d.matrix.rotate(90.0,0,0,1.0);
|
|
|
|
//qInfo() << "lookAt";
|
|
//d.matrix.lookAt(QVector3D(0,0,0),QVector3D(120,0,120),QVector3D(0,1,0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
d.matrix.setToIdentity();
|
|
d.matrix.ortho(r);
|
|
d.update_geo = true; // even true for target_rect != d.rect
|
|
}
|
|
// Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
|
|
//mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0;
|
|
if (d.ctx && d.ctx == QOpenGLContext::currentContext()) {
|
|
//qInfo() << "DEBUG_OPENGL(21)" << " glViewport" << d.rect << __FUNCTION__; // 663x373
|
|
DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height()));
|
|
}
|
|
}
|
|
|
|
void OpenGLVideo::setBrightness(qreal value)
|
|
{
|
|
d_func().material->setBrightness(value);
|
|
}
|
|
|
|
void OpenGLVideo::setContrast(qreal value)
|
|
{
|
|
d_func().material->setContrast(value);
|
|
}
|
|
|
|
void OpenGLVideo::setHue(qreal value)
|
|
{
|
|
d_func().material->setHue(value);
|
|
}
|
|
|
|
void OpenGLVideo::setSaturation(qreal value)
|
|
{
|
|
d_func().material->setSaturation(value);
|
|
}
|
|
|
|
void OpenGLVideo::setUserShader(VideoShader *shader)
|
|
{
|
|
d_func().user_shader = shader;
|
|
}
|
|
|
|
VideoShader* OpenGLVideo::userShader() const
|
|
{
|
|
return d_func().user_shader;
|
|
}
|
|
|
|
void OpenGLVideo::setMeshType(MeshType value)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if (d.mesh_type == value)
|
|
return;
|
|
d.mesh_type = value;
|
|
d.update_geo = true;
|
|
d.matrix.setToIdentity();
|
|
if (d.mesh_type == SphereMesh && d.norm_viewport) {
|
|
|
|
//setAngle(0.0);
|
|
//d.matrix.setToIdentity();
|
|
//d.matrix.perspective(90.0f, 2.0f, 0.0f, 1.0f);
|
|
|
|
//d.matrix.perspective(55.0f, 1.0f, 0.1f, 1.0f); // for sphere
|
|
}
|
|
}
|
|
|
|
OpenGLVideo::MeshType OpenGLVideo::meshType() const
|
|
{
|
|
return d_func().mesh_type;
|
|
}
|
|
|
|
void OpenGLVideo::fill(const QColor &color)
|
|
{
|
|
//DYGL(glClearColor(1.0, 0.0,0.0, 1.0));
|
|
DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()));
|
|
DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
|
}
|
|
|
|
//void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform)
|
|
//{
|
|
// DPTR_D(OpenGLVideo);
|
|
// Q_ASSERT(d.manager);
|
|
// Q_EMIT beforeRendering();
|
|
// DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly)
|
|
// const qint64 mt = d.material->type();
|
|
// if (d.material_type != mt)
|
|
// {
|
|
//#if !(OFF_OTHER_DEBUG)
|
|
// qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt);
|
|
//#endif
|
|
// d.material_type = mt;
|
|
// }
|
|
// if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it
|
|
// {
|
|
// return;
|
|
// }
|
|
// VideoShader *shader = d.user_shader;
|
|
// if (!shader)
|
|
// shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code)
|
|
// shader->update(d.material);
|
|
// shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix);
|
|
// // uniform end. attribute begin
|
|
// d.updateGeometry(shader, target, roi);
|
|
// // normalize?
|
|
// const bool blending = d.has_a;
|
|
// if (blending) {
|
|
// DYGL(glEnable(GL_BLEND));
|
|
// gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); //
|
|
// }
|
|
// //if (d.mesh_type == OpenGLVideo::SphereMesh)
|
|
// //DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf
|
|
// d.gr.render();
|
|
// if (blending)
|
|
// DYGL(glDisable(GL_BLEND));
|
|
// // d.shader->program()->release(); //glUseProgram(0)
|
|
// d.material->unbind();
|
|
|
|
// Q_EMIT afterRendering();
|
|
//}
|
|
#if (!USE_POINT_ZOOM || USE_HYBRID_ZOOM)
|
|
void OpenGLVideo::setROI(double x, double y, double w, double h)
|
|
{
|
|
bROI = true;
|
|
rROI = QRectF(x,y,w,h);
|
|
}
|
|
#endif //USE_POINT_ZOOM
|
|
void OpenGLVideo::clearROI()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
#if (USE_POINT_ZOOM)
|
|
zoomLevel2D = 0;
|
|
centerZoom = QPoint(0.5,0.5);
|
|
if(d.mode == 2) {
|
|
zoomLevel = 0;
|
|
setAngle(d.dAngleX,d.dAngleY);
|
|
}
|
|
#endif
|
|
|
|
#if (!USE_POINT_ZOOM || USE_HYBRID_ZOOM)
|
|
rROI = QRectF(0.0,0.0,1.0,1.0);
|
|
bROI = false;
|
|
#endif
|
|
}
|
|
bool OpenGLVideo::isVFlip()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.bVFlip;
|
|
}
|
|
void OpenGLVideo::setHFlip(bool flip)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.bHFlip = flip;
|
|
d.update_geo = true;
|
|
}
|
|
bool OpenGLVideo::isHFlip()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.bHFlip;
|
|
}
|
|
void OpenGLVideo::setVFlip(bool flip)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.bVFlip = flip;
|
|
if(d.mode == 2) {
|
|
// BOUND Y ANGLE 200 ~ 270 ~ 340
|
|
double dy;
|
|
if(d.dAngleY > 270)
|
|
{
|
|
dy = 270 - (d.dAngleY - 270);
|
|
}
|
|
else
|
|
{
|
|
dy = 270 + (270 - d.dAngleY);
|
|
}
|
|
|
|
setAngle(d.dAngleX+180, dy);
|
|
}
|
|
d.update_geo = true;
|
|
}
|
|
#if (USE_POINT_ZOOM)
|
|
QRectF OpenGLVideo::getROI(QRectF src)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
|
|
float zw = getZoomScale();// 1.0f / (float)(zoomLevel2D + 1);
|
|
float w = src.width() * zw;
|
|
float h = src.height() * zw;
|
|
float cx = src.width() * centerZoom.x();
|
|
float cy = src.height() * centerZoom.y();
|
|
|
|
float xmin = cx - w/2.0f;
|
|
float ymin = cy - h/2.0f;
|
|
|
|
|
|
// SNAP
|
|
if(xmin + w > src.width()) {
|
|
xmin = src.width() - w;
|
|
}
|
|
if(ymin + h > src.height()) {
|
|
ymin = src.height() - h;
|
|
}
|
|
|
|
// SNAP
|
|
xmin = qMax(xmin,0.0f);
|
|
ymin = qMax(ymin,0.0f);
|
|
|
|
centerZoom.setX((xmin + w/2.0f)/src.width());
|
|
centerZoom.setY((ymin + h/2.0f)/src.height());
|
|
|
|
if(d.mode == 0) {
|
|
float dw = d.rect.width();
|
|
float dh = d.rect.height();
|
|
|
|
if(dw > dh) {
|
|
float ow = w * dw/dh * h/w; // 1:1 영상이 아닐경우 비율..
|
|
xmin -= ((ow - w) * 0.5f);
|
|
w = ow;
|
|
}
|
|
//qInfo() << QRectF(xmin, ymin,w,h) << w/h << dw/dh;
|
|
}
|
|
else if (d.mode == 1)
|
|
{
|
|
float dw = d.rect.width();
|
|
float dh = d.rect.height();
|
|
|
|
// 가로세로 비율이 16:9 보다 클 경우
|
|
if((dw / dh) > (16.0f / 9.0f))
|
|
{
|
|
float ow = w * (dw / dh) * (9.0 / 16.0);
|
|
xmin -= ((ow - w) * 0.5f);
|
|
w = ow;
|
|
}
|
|
// else {
|
|
// float oh = h * (dh / dw) * (16.0 / 9.0);
|
|
// ymin -= ((oh - h) * 0.5f);
|
|
// h = oh;
|
|
// }
|
|
}
|
|
//qInfo() << xmin << ymin << w << h << __FUNCTION__ << __LINE__;
|
|
return QRectF(xmin, ymin,w,h);
|
|
}
|
|
float OpenGLVideo::getZoomScale()
|
|
{
|
|
float zs[] = {1.0f, 0.7f, 0.5f, 0.3f, 0.2f};
|
|
if(sizeof(zs) / sizeof(float) > zoomLevel2D) {
|
|
return zs[zoomLevel2D];
|
|
}
|
|
return 1.0f;
|
|
}
|
|
QPointF OpenGLVideo::getZoomCenter(float rx, float ry)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
|
|
if(d.bVFlip) {
|
|
ry = 1.0 - ry;
|
|
}
|
|
|
|
if(d.bHFlip) {
|
|
rx = 1.0 - rx;
|
|
}
|
|
|
|
if(zoomLevel2D == 0)
|
|
{
|
|
return QPointF(rx,ry);
|
|
}
|
|
float zwh = getZoomScale();
|
|
|
|
// 현재 Zoom Level 에서의 xmin
|
|
float xmin = centerZoom.x() - (zwh / 2.0);
|
|
float ymin = centerZoom.y() - (zwh / 2.0);
|
|
|
|
// 현재 영역에서의 XY
|
|
float nx = xmin + rx * zwh;
|
|
float ny = ymin + ry * zwh;
|
|
return QPointF(nx,ny);
|
|
}
|
|
#endif // USE_POINT_ZOOM
|
|
|
|
void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform)
|
|
{
|
|
#if(TEST_EQUIRECTANGULAR)
|
|
Q_UNUSED(transform)
|
|
#endif
|
|
|
|
DPTR_D(OpenGLVideo);
|
|
Q_ASSERT(d.manager);
|
|
Q_EMIT beforeRendering();
|
|
// viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly)
|
|
// 663x373, 1918x959
|
|
DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height()));
|
|
//qInfo() << "VP:" << d.rect << "transform:" << transform;
|
|
const qint64 mt = d.material->type();
|
|
if (d.material_type != mt)
|
|
{
|
|
#if !(OFF_OTHER_DEBUG)
|
|
qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt);
|
|
#endif
|
|
d.material_type = mt;
|
|
}
|
|
if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it
|
|
{
|
|
return;
|
|
}
|
|
// TEXTURE SIZE = 2280x2280 영상에서 -> 2304, 2280 문제로 녹색선 발생
|
|
//qInfo() << "frameSize:" << d.material->textureSize(0);
|
|
|
|
VideoShader *shader = d.user_shader;
|
|
if (!shader)
|
|
shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code)
|
|
shader->update(d.material);
|
|
|
|
// WIDTH 처리
|
|
float ew = d.material->validTextureWidth();
|
|
if(ew < 1.0) {
|
|
ew *= 0.999f;
|
|
}
|
|
shader->program()->setUniformValue(shader->effectiveLocation(),ew);
|
|
|
|
#if (SUB_MODEL_TB5000)
|
|
//qInfo() << "RECT SIZE:" << d.rect.size() << __FUNCTION__;
|
|
// 원본 texture 의 크기를 전달해야함..???
|
|
// 16:9 크기가 넘을 경우 중복 발생
|
|
float ww = (float)d.rect.width();
|
|
if(ww / 16.0f * 9.0f > (float)d.rect.height()) {
|
|
ww = (float)d.rect.height() * (16.0f / 9.0f);
|
|
}
|
|
//qInfo() << ww << ew << __FUNCTION__;
|
|
shader->program()->setUniformValue(shader->realScreenWidthLocation(),(float)d.rect.width()); // 사용하지 않는다...
|
|
shader->program()->setUniformValue(shader->screenWidthLocation(),ww);
|
|
shader->program()->setUniformValue(shader->screenHeightLocation(),(float)d.rect.height());
|
|
#endif // SUB_MODEL_TB5000
|
|
|
|
|
|
#if (TOP_DOWN_2D_360)
|
|
shader->program()->setUniformValue(shader->angleLocation(),(float)d.dAngleX);
|
|
|
|
shader->program()->setUniformValue(shader->whratioLocation(),(float)(d.rect.width() / d.rect.height()));
|
|
#endif // TOP_DOWN_2D_360
|
|
// (w - x) / h = 16.0/9.0;
|
|
// w - ((16.0/9.0) * h);
|
|
// float wo = ((d.rect.width() - ((16.0 / 9.0) * d.rect.height())) / d.rect.width()) * 0.5;
|
|
// if(d.mode == 1) {
|
|
// qInfo() << d.rect.width() << d.rect.height() << wo << __FUNCTION__ << __LINE__;
|
|
// }
|
|
// float clipbound = (d.mode == 1 && wo > 0.0) ? wo : 0.0f;
|
|
shader->program()->setUniformValue(shader->clipBoundLocation(),d.mode == 1 ? 1.0f : 0.0f);
|
|
|
|
shader->program()->setUniformValue(shader->modeLocation(),d.mode);
|
|
if(d.mode != 0 && d.mode != 1)
|
|
{
|
|
QMatrix4x4 m(1,0,0,0,
|
|
0,1,0,0,
|
|
0,0,1,0,
|
|
0,0,0,1);
|
|
|
|
shader->program()->setUniformValue(shader->matrixLocation(), m*d.matrix);
|
|
|
|
// 파노라마 모드에서는 RAD 사용???
|
|
shader->program()->setUniformValue(shader->angleLocation(), (float)(DEG2RAD(d.dAngleX)));
|
|
}
|
|
else
|
|
{
|
|
shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix);
|
|
}
|
|
|
|
//qInfo() << roi;
|
|
//QRectF testROI = QRectF(1440,720,1440,1440);
|
|
//#if (USE_POINT_ZOOM)
|
|
if(bROI && d.mode != 2) {
|
|
double w = roi.width();
|
|
double h = roi.height();
|
|
|
|
//double ry = d.bVFlip ? 1.0 - rROI.y() - rROI.height() : rROI.y();
|
|
// 상하반전은 ROI 설정시 반영 (하지 않으면 rROI 값이 반전시 틀려짐..)
|
|
QRectF tr = QRectF(rROI.x() * w, rROI.y() * h, rROI.width() * w, rROI.height() * h);
|
|
|
|
//qInfo() << "ROI:" << tr << " from:" << w * rROI.x();
|
|
d.updateGeometry(shader, target, tr);
|
|
}
|
|
else if((zoomLevel2D > 0 && d.mode == 0) || d.mode == 1) // MODE 1 에서는 영역외 그림..
|
|
{
|
|
|
|
// double w = roi.width();
|
|
// double h = roi.height();
|
|
// double ry = d.bVFlip ? 1.0 - rROI.y() - rROI.height() : rROI.y();
|
|
// // 상하반전은 ROI 설정시 반영 (하지 않으면 rROI 값이 반전시 틀려짐..)
|
|
// QRectF tr = QRectF(rROI.x() * w, rROI.y() * h, rROI.width() * w, rROI.height() * h);
|
|
// qInfo() << "ROI:" << tr << " from:" << w * rROI.x();
|
|
|
|
d.updateGeometry(shader, target, getROI(roi));
|
|
}
|
|
//#else // USE_POINT_ZOOM
|
|
|
|
//#endif // USE_POINT_ZOOM
|
|
else {
|
|
d.updateGeometry(shader, target, roi);
|
|
}
|
|
|
|
//qInfo() << "FLIP" << bVFlip;
|
|
//shader->program()->setUniformValue(shader->flipLocation(), bVFlip ? 1 : 0);
|
|
|
|
d.gr.render();
|
|
d.material->unbind();
|
|
|
|
Q_EMIT afterRendering();
|
|
}
|
|
QSize OpenGLVideo::frameSize()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.material->frameSize();
|
|
}
|
|
|
|
#define HEMISPHERE_3 1
|
|
void OpenGLVideo::angle(double* ax, double* ay)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
*ax = d.dAngleX;
|
|
*ay = d.dAngleY;
|
|
}
|
|
void OpenGLVideo::startDrag()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
sx = d.dAngleX;
|
|
sy = d.dAngleY;
|
|
// bDragAxis = true;
|
|
}
|
|
void OpenGLVideo::drag(double rx,double ry)
|
|
{
|
|
//DPTR_D(OpenGLVideo);
|
|
// 화면 좌우넓이는 90 * 2 = 180 (파노라마 모드는 360)
|
|
// 화며 상하길이는 90
|
|
//double widthX = (d.mode == 1 || d.mode == 2) ? -360 : 180;
|
|
double dr = bVRFlip ? 1.0 : -1.0;
|
|
// 180, 90 -> 45, 22
|
|
setAngle(sx + (rx * 45) * dr,sy + (ry * 22));
|
|
//qInfo() << "setAngle:" << sx + (rx * 45) * dr, sy + (ry * 22);
|
|
}
|
|
void OpenGLVideo::scroll(double rx,double ry)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
double dr = bVRFlip ? 1.0 : -1.0;
|
|
// 180, 90 -> 45, 22
|
|
setAngle(d.dAngleX + (rx * 45) * dr,d.dAngleY + (ry * 22));
|
|
//qInfo() << "setAngle:" << sx + (rx * 45) * dr, sy + (ry * 22);
|
|
}
|
|
|
|
void OpenGLVideo::resetAngle()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
zoomLevel = 0;
|
|
#if (TANDF_360_TEST || MH_360_TEST)
|
|
setAngle(d.bVFlip ? 0 : 180,d.bVFlip ? 210.0 : 330.0);
|
|
#else
|
|
setAngle(d.bVFlip ? 180 : 0,d.bVFlip ? 210.0 : 330.0);
|
|
#endif
|
|
}
|
|
void OpenGLVideo::setZoom(int level)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
zoomLevel = qMax(qMin(level,20),0);
|
|
setAngle(d.dAngleX,d.dAngleY);
|
|
}
|
|
void OpenGLVideo::zoomPos(double rx, double ry)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
|
|
// 360 VR
|
|
if(d.mode == 2) {
|
|
double perspective = 45.0f / (1.0 + (((double)zoomLevel) / 10.0));
|
|
double whr = d.rect.width() / (d.rect.height() * 0.75);
|
|
//double whr = d.rect.width() / (d.rect.height() * 1.0);
|
|
|
|
double ax = (rx - 0.5) * perspective * whr;
|
|
double ay = (ry - 0.5) * perspective;
|
|
|
|
// 0.5,0.5 에서 멀어질 수록 각도 변화는 줄어듬
|
|
double arx = (fabs(rx - 0.5) * 2.0) * 0.3 + 1.0;
|
|
ax /= arx;
|
|
|
|
double ary = (fabs(ry - 0.5) * 2.0) * 0.3 + 1.0;
|
|
ay /= ary;
|
|
|
|
if(bVRFlip) {
|
|
ax *= -1.0;
|
|
}
|
|
|
|
int cz = ((zoomLevel / 5) + 1) * 5;
|
|
// 처음으로 돌아가기
|
|
if (cz > 20) {
|
|
zoomLevel = 0;
|
|
}
|
|
else {
|
|
zoomLevel = cz;
|
|
}
|
|
//qInfo() << d.dAngleX << d.dAngleY << ax << ay;
|
|
setAngle(d.dAngleX+ax,d.dAngleY-ay);
|
|
}
|
|
else // 2D MODE
|
|
{
|
|
// 실제 화면내의 중심 좌표를 설정해야함
|
|
// WIDE 모드는 동일, FISHEYE 모드는 빈공간 처리해야함.
|
|
centerZoom = getZoomCenter(rx,ry);
|
|
zoomLevel2D = (zoomLevel2D + 1) % 5;
|
|
}
|
|
|
|
}
|
|
|
|
void OpenGLVideo::setAngle(double ax, double ay)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
#if (TOP_DOWN_2D_360)
|
|
d.dAngleX = ax;
|
|
d.dAngleY = ay;
|
|
// d.dAngleX = snapAngle(d.dAngleX);
|
|
// d.dAngleY = snapAngle(d.dAngleY);
|
|
#else // #if (TOP_DOWN_2D_360)
|
|
d.dAngleX = ax;
|
|
d.dAngleY = ay;
|
|
|
|
|
|
// 확대에 따라 최대, 최소 Y 각이 변경되고 perspective 도 변경됨
|
|
// 1.0 ~ 3.0
|
|
double zoomRatio = (0.0 + (((double)zoomLevel) / 10.0));
|
|
|
|
// BOUND Y ANGLE 200 ~ 270 ~ 340
|
|
|
|
double yAngleOffset = zoomRatio * 8.0; // 15도 변화?
|
|
if(d.dAngleY > 340.0 + yAngleOffset) {
|
|
d.dAngleY = 340 + yAngleOffset;
|
|
}
|
|
else if (d.dAngleY < 200.0 - yAngleOffset) {
|
|
d.dAngleY = 200 - yAngleOffset;
|
|
}
|
|
|
|
|
|
// ANGLE SNAP
|
|
d.dAngleX = snapAngle(d.dAngleX);
|
|
d.dAngleY = snapAngle(d.dAngleY);
|
|
|
|
//qInfo() << d.dAngleX << d.dAngleY << __FUNCTION__;
|
|
|
|
// 3D(VR) MODE
|
|
#if (REMOVE_360_WIDE_SINGLE)
|
|
if(d.mode == 2)
|
|
#else
|
|
if(d.mode == 3)
|
|
#endif
|
|
{
|
|
d.update_geo = true;
|
|
|
|
// https://mathinsight.org/spherical_coordinates
|
|
// x = 거리 * sin()cos()
|
|
|
|
double theta = DEG2RAD(d.dAngleX) + DEG_PI;
|
|
double phi = DEG2RAD(d.dAngleY);
|
|
|
|
// 화면 XY + Z: 깊이
|
|
double x = cos(phi) * cos(theta) * SPHERE_SCALE;
|
|
double y = sin(phi) * SPHERE_SCALE;
|
|
double z = cos(phi) * sin(theta) * SPHERE_SCALE;
|
|
//y = qMin(y,-7.0);
|
|
|
|
// 화면 반전 계산
|
|
double td = 1.0;
|
|
if (d.dAngleY > 90.0 && d.dAngleY < 270.0) {
|
|
td = -1.0;
|
|
bVRFlip = true; // X 축 이동을 역으로 계산
|
|
}
|
|
else {
|
|
bVRFlip = false;
|
|
}
|
|
|
|
|
|
// Y 각도에 따른 높이 조절
|
|
double ds = fabs(d.dAngleY - 270.0) / 70.0 * -0.4;
|
|
|
|
//qInfo() << "ANGLE:" << d.dAngleX << d.dAngleY << ax << ay << zoomLevel << __FUNCTION__ << __LINE__;
|
|
|
|
// 1~10 1:1x, 2:1.1~~~
|
|
//double perspective = 45.0f / (1.0 + (((double)zoomLevel) / 10.0));
|
|
double perspective = 45.0f / (1.0 + (((double)zoomLevel) / 10.0));
|
|
|
|
//qInfo() << "XYZ:" << x << y << z << ":" << d.dAngleX << d.dAngleY << bVRFlip << "zoomLevel:" << zoomLevel << "perspective:" << perspective << "ds:" << ds << __FUNCTION__;
|
|
|
|
d.matrix.setToIdentity();
|
|
//d.matrix.perspective(85.0f, 1.63f, 0.0f, 1.0f); // 확대시 화각 축소
|
|
d.matrix.perspective(perspective, d.rect.width() / (d.rect.height() * 0.75), 0.0f, 1.0); // 확대시 화각 축소
|
|
d.matrix.lookAt(QVector3D(0,SPHERE_SCALE*ds,0),QVector3D(x,y,z),QVector3D(0,td,0));
|
|
}
|
|
#endif // #if (TOP_DOWN_2D_360)
|
|
|
|
}
|
|
|
|
/*
|
|
void OpenGLVideo::setAngle(double a)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
#if (HEMISPHERE_3)
|
|
if(d.mode == 2)
|
|
{
|
|
if(a>1.0)
|
|
{
|
|
a = a - 1.0;
|
|
}
|
|
else if (a < 0.0)
|
|
{
|
|
a = 1.0 + a;
|
|
}
|
|
}
|
|
#endif
|
|
d.dAngle = a;
|
|
|
|
#if (HEMISPHERE_3)
|
|
if(d.mode == 2)
|
|
{
|
|
d.update_geo = true;
|
|
float aa = ((d.dAngle * 360) - 180) * 20.0 * atan (1.0) / 180.0;
|
|
//qInfo() << "d.dAngle" << aa;
|
|
|
|
float x = cos(aa);
|
|
float y = sin(aa);
|
|
|
|
//qInfo() << "(d.dAngle * 360):" << (d.dAngle * 360) << ":" << x << ":" << y;
|
|
qInfo() << "(dPitch):" << d.dPitch;
|
|
|
|
//const float h = 18.0 + d.dPitch;
|
|
const float h = d.dPitch - 15;
|
|
d.matrix.setToIdentity();
|
|
d.matrix.perspective(85.0f, 1.63f, 0.0f, 1.0f); // for sphere
|
|
d.matrix.lookAt(QVector3D(0,0,0.0),QVector3D(x,h,y),QVector3D(0,1.0,0));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
double OpenGLVideo::angle()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.dAngle;
|
|
}
|
|
void OpenGLVideo::setPitch(double a)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
|
|
// // ZOOM 이 아닐 경우 상하단 이동 제한
|
|
// double limit = d.bZoom ? 1.0 : 0.2;
|
|
|
|
// if(a>limit)
|
|
// {
|
|
// a = limit;
|
|
// }
|
|
// else if (a < -limit)
|
|
// {
|
|
// a = -limit;
|
|
// }
|
|
d.dPitch = a;
|
|
setAngle(d.dAngle);
|
|
}
|
|
|
|
double OpenGLVideo::pitch()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.dPitch;
|
|
}
|
|
*/
|
|
void OpenGLVideo::setMode(int mode)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
//qInfo() << "MODE:" << mode << __FUNCTION__;
|
|
#if (HEMISPHERE_3)
|
|
#if (REMOVE_360_WIDE_SINGLE)
|
|
if(d.mode == 2 || mode == 2) // VR
|
|
#else // REMOVE_360_WIDE_SINGLE
|
|
if(d.mode == 3 || mode == 3) // VR
|
|
#endif // REMOVE_360_WIDE_SINGLE
|
|
{
|
|
d.mode = mode;
|
|
#if (TOP_DOWN_2D_360)
|
|
setMeshType(RectMesh);
|
|
#else // TOP_DOWN_2D_360
|
|
#if (REMOVE_360_WIDE_SINGLE)
|
|
setMeshType(mode == 2 ? SphereMesh : RectMesh);
|
|
#else
|
|
setMeshType(mode == 3 ? SphereMesh : RectMesh);
|
|
#endif
|
|
#endif // TOP_DOWN_2D_360
|
|
//setAngle(d.dAngle);
|
|
//setAngle(d.dAngleX,d.dAngleY);
|
|
d.update_geo = true;
|
|
}
|
|
#endif // HEMISPHERE_3
|
|
|
|
d.mode = mode;
|
|
#if (USE_POINT_ZOOM)
|
|
zoomLevel2D = 0;
|
|
centerZoom = QPointF(0.5,0.5);
|
|
#endif
|
|
|
|
#if (!USE_POINT_ZOOM || USE_HYBRID_ZOOM)
|
|
bROI = false;
|
|
#endif
|
|
resetAngle();
|
|
//setAngle(0,0);
|
|
}
|
|
int OpenGLVideo::mode()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.mode;
|
|
}
|
|
void OpenGLVideo::resetGL()
|
|
{
|
|
qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender());
|
|
d_func().resetGL();
|
|
}
|
|
#elif (MODEL_360) // T&F DUAL 360
|
|
#define TEST_EQUIRECTANGULAR 0
|
|
|
|
#define TUNE_360_FIXED 1
|
|
|
|
static const float T360_FHD[] = {-0.0358f,0.042f,-0.00225f,0.03625f,
|
|
0.0464f,0.006f,0.0f,0.0f,
|
|
-0.1192f,0.012f,-0.0025f,0.1695f};
|
|
|
|
static const float T360_FHD_SD[] = {0.0976f,0.0412f,0.0f,0.04475f,
|
|
0.0592f,0.0f,0.0f,0.0f,
|
|
0.0f,0.0154f,0.15825f,0.5f};
|
|
|
|
|
|
|
|
static const QMatrix4x4 M360_FHS = QMatrix4x4(T360_FHD);
|
|
static const QMatrix4x4 M360_SD = QMatrix4x4(T360_FHD_SD);
|
|
|
|
|
|
// FIXME: why crash if inherits both QObject and DPtrPrivate?
|
|
class OpenGLVideoPrivate : public DPtrPrivate<OpenGLVideo>
|
|
{
|
|
public:
|
|
OpenGLVideoPrivate()
|
|
: ctx(0)
|
|
, manager(0)
|
|
, material(new VideoMaterial())
|
|
, material_type(0)
|
|
, norm_viewport(true)
|
|
, update_geo(true)
|
|
, tex_target(0)
|
|
, valiad_tex_width(1.0)
|
|
#if (TEST_EQUIRECTANGULAR)
|
|
, mesh_type(OpenGLVideo::RectMesh) // OpenGLVideo::RectMesh,SphereMesh
|
|
#else
|
|
, mesh_type(OpenGLVideo::SphereMesh) // OpenGLVideo::RectMesh,SphereMesh
|
|
#endif
|
|
|
|
, geometry(NULL)
|
|
//, geometry2(NULL)
|
|
, user_shader(NULL)
|
|
, dAngle(0.5)
|
|
, dPitch(0.0)
|
|
, bZoom(false)
|
|
, dualMode(0)
|
|
, resolutionMode(0)
|
|
, bDualFront(true) // dual front
|
|
|
|
{
|
|
tune360 = M360_SD;//QMatrix4x4(T360_FHD_SD); // T360_FHD // T360_FHD
|
|
//#else
|
|
//#if(TUNE4)
|
|
// // [-0.035800,0.048400,-0.002250,0.000000,0.046400,0.022000,0.000000,0.000000,-0.112000,0.008600];
|
|
// float tv[] = {0.0f,0.0f,0.0f,0.0f,
|
|
// 0.0f,0.0f,0.0f,0.0f,
|
|
// 0.0f,0.0f,0.0f,0.0f};
|
|
//#else
|
|
// float tv[] = {0.077400f,-0.002600f,-0.095500f,0.045000f,0.002000f,-0.104500f,0.000000f,0.000000f,0.000000f};
|
|
//#endif
|
|
// tune360 = QMatrix4x4(tv);
|
|
//#endif
|
|
//uvOffset = QVector4D(0,0,0,0);
|
|
//#endif
|
|
}
|
|
~OpenGLVideoPrivate() {
|
|
if (material) {
|
|
delete material;
|
|
material = 0;
|
|
}
|
|
delete geometry;
|
|
}
|
|
|
|
// 0~360 = 시계방향
|
|
double dAngle;
|
|
|
|
double dPitch; // 상하각
|
|
bool bZoom;
|
|
bool bDualFront; // Dual Mode 에서 전방
|
|
int resolutionMode;
|
|
|
|
void resetGL() {
|
|
ctx = 0;
|
|
gr.updateGeometry(NULL);
|
|
if (!manager)
|
|
return;
|
|
manager->setParent(0);
|
|
delete manager;
|
|
manager = 0;
|
|
if (material) {
|
|
delete material;
|
|
material = 0;
|
|
}
|
|
|
|
}
|
|
// update geometry(vertex array) set attributes or bind VAO/VBO.
|
|
void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r);
|
|
public:
|
|
QOpenGLContext *ctx;
|
|
ShaderManager *manager;
|
|
VideoMaterial *material;
|
|
qint64 material_type;
|
|
bool norm_viewport;
|
|
bool has_a;
|
|
bool update_geo;
|
|
int tex_target;
|
|
qreal valiad_tex_width;
|
|
QSize video_size;
|
|
QRectF target;
|
|
QRectF roi; //including invalid padding width
|
|
OpenGLVideo::MeshType mesh_type;
|
|
TexturedGeometry *geometry;
|
|
GeometryRenderer gr;
|
|
|
|
QRectF rect;
|
|
QMatrix4x4 matrix;
|
|
VideoShader *user_shader;
|
|
|
|
QMatrix4x4 tune360;
|
|
QMatrix4x4 dual360;
|
|
int dualMode;
|
|
};
|
|
|
|
void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r)
|
|
{
|
|
|
|
// also check size change for normalizedROI computation if roi is not normalized
|
|
const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize();
|
|
const int tc = shader->textureLocationCount();
|
|
if (roi_changed) {
|
|
roi = r;
|
|
valiad_tex_width = material->validTextureWidth();
|
|
video_size = material->frameSize();
|
|
|
|
}
|
|
if (tex_target != shader->textureTarget()) {
|
|
tex_target = shader->textureTarget();
|
|
update_geo = true;
|
|
}
|
|
// (-1, -1, 2, 2) must flip y
|
|
QRectF target_rect = norm_viewport ? QRectF(-1, 1, 2, -2) : rect;
|
|
if (target.isValid()) {
|
|
if (roi_changed || target != t) {
|
|
target = t;
|
|
update_geo = true;
|
|
//target_rect = target (if valid). // relate to gvf bug?
|
|
}
|
|
} else {
|
|
if (roi_changed) {
|
|
update_geo = true;
|
|
}
|
|
}
|
|
if (!update_geo)
|
|
{
|
|
return;
|
|
}
|
|
|
|
delete geometry;
|
|
geometry = NULL;
|
|
|
|
if (mesh_type == OpenGLVideo::SphereMesh)
|
|
{
|
|
geometry = new Sphere();
|
|
geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1);
|
|
geometry->setGeometryRect(target_rect); // 좌측 (=-2)
|
|
geometry->setTextureRect(QRectF(0,0,1.0,1.0)); // 전방 SRC (고정)
|
|
}
|
|
else
|
|
{
|
|
geometry = new TexturedGeometry();
|
|
geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1);
|
|
geometry->setGeometryRect(target_rect);
|
|
//qInfo() << target_rect;
|
|
if(dualMode == 1)
|
|
{
|
|
|
|
geometry->setTextureRect(material->mapToTexture(0, roi));
|
|
}
|
|
else
|
|
{
|
|
geometry->setTextureRect(QRectF(0.0,0.0,1.0,1.0));
|
|
}
|
|
|
|
// if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) {
|
|
// for (int i = 1; i < tc; ++i) {
|
|
// // tc can > planes, but that will compute chroma plane
|
|
// geometry->setTextureRect(material->mapToTexture(i, roi), i);
|
|
// }
|
|
// }
|
|
}
|
|
|
|
//qDebug("updating geometry...");
|
|
// setTextureCount may change the vertex data. Call it before setRect()
|
|
//qDebug() << "target rect: " << target_rect ;
|
|
|
|
// 동작하지 않음...
|
|
|
|
geometry->create();
|
|
update_geo = false;
|
|
gr.updateGeometry(geometry);
|
|
|
|
}
|
|
|
|
OpenGLVideo::OpenGLVideo() {}
|
|
|
|
QMatrix4x4& OpenGLVideo::tune360()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.tune360;
|
|
}
|
|
|
|
void OpenGLVideo::setTune360(QMatrix4x4 m)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.tune360 = m;
|
|
}
|
|
|
|
#if (TUNE_360 && MODEL_360)
|
|
void OpenGLVideo::setMode(int mode)
|
|
{
|
|
setMeshType(mode == 0 ? RectMesh : SphereMesh);
|
|
}
|
|
#endif // TUNE_360 && MODEL_360
|
|
|
|
void OpenGLVideo::setZoom(bool a)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.bZoom = a;
|
|
d.dPitch = 0.0;
|
|
setAngle(d.dAngle);
|
|
}
|
|
bool OpenGLVideo::zoom()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.bZoom;
|
|
}
|
|
void OpenGLVideo::setPitch(double a)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
|
|
// ZOOM 이 아닐 경우 상하단 이동 제한
|
|
double limit = d.bZoom ? 1.0 : 0.2;
|
|
|
|
if(a>limit)
|
|
{
|
|
a = limit;
|
|
}
|
|
else if (a < -limit)
|
|
{
|
|
a = -limit;
|
|
}
|
|
d.dPitch = a;
|
|
setAngle(d.dAngle);
|
|
}
|
|
double OpenGLVideo::pitch()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.dPitch;
|
|
}
|
|
|
|
void OpenGLVideo::setAngle(double a)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if(d.dualMode == 1) {
|
|
return;
|
|
}
|
|
|
|
if(a>1.0)
|
|
{
|
|
a = a - 1.0;
|
|
}
|
|
else if (a < 0.0)
|
|
{
|
|
a = 1.0 + a;
|
|
}
|
|
|
|
d.dAngle = a;
|
|
//d.update_geo = true;
|
|
|
|
//float aa = ((d.dAngle * 360) - 90) * 4.0 * atan (1.0) / 180.0;
|
|
float aa = ((d.dAngle * 360) - 270) * 4.0 * atan (1.0) / 180.0;
|
|
//qInfo() << "d.dAngle" << aa;
|
|
|
|
float x = cos(aa);
|
|
float y = sin(aa);
|
|
|
|
//qInfo() << "(d.dAngle * 360):" << (d.dAngle * 360) << ":" << x << ":" << y;
|
|
|
|
float aspect = d.rect.width() / d.rect.height();
|
|
d.matrix.setToIdentity();
|
|
float zf = d.bZoom ? 45.0 : 90.0; // 45,90 -> 범위를 (105도로) 늘리면 상하 전체가 보임..
|
|
d.matrix.perspective(zf, aspect, 0.0f, 1.0f); // for sphere
|
|
//float pitch = d.bZoom ? d.dPitch : 0.0;
|
|
// 범위를 줄이고 상/하 이동 활성화
|
|
float pitch = d.dPitch;//d.bZoom ? d.dPitch : d.dPitch * 0.3;
|
|
d.matrix.lookAt(QVector3D(0,0,0),QVector3D(x,pitch,y),QVector3D(0,1,0));
|
|
}
|
|
double OpenGLVideo::angle()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.dAngle;
|
|
}
|
|
|
|
bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt)
|
|
{
|
|
return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid;
|
|
}
|
|
|
|
void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if (d.ctx == ctx)
|
|
return;
|
|
qreal b = 0, c = 0, h = 0, s = 0;
|
|
if (d.material) {
|
|
b = d.material->brightness();
|
|
c = d.material->contrast();
|
|
h = d.material->hue();
|
|
s = d.material->saturation();
|
|
delete d.material;
|
|
d.material = 0;
|
|
}
|
|
d.resetGL(); //TODO: is it ok to destroygl resources in another context?
|
|
d.ctx = ctx; // Qt4: set to null in resetGL()
|
|
if (!ctx) {
|
|
return;
|
|
}
|
|
d.material = new VideoMaterial();
|
|
d.material->setBrightness(b);
|
|
d.material->setContrast(c);
|
|
d.material->setHue(h);
|
|
d.material->setSaturation(s);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
d.manager = ctx->findChild<ShaderManager*>(QStringLiteral("__qtav_shader_manager"));
|
|
QSizeF surfaceSize = ctx->surface()->size();
|
|
//qInfo() << "SURFACE SIZE:(0)" << surfaceSize;
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
|
|
surfaceSize *= ctx->screen()->devicePixelRatio();
|
|
#else
|
|
surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's
|
|
#endif
|
|
#else
|
|
QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height());
|
|
#endif
|
|
//qInfo() << "SURFACE SIZE:(2)" << surfaceSize;
|
|
|
|
//qInfo() << "DEBUG_OPENGL(13)" << " ctx->surface():" << surfaceSize; // 1,1
|
|
//qInfo() << "DEBUG_OPENGL(12)" << " ctx->surface():" << ctx->surface()->size(); // 1,1
|
|
//qInfo() << "DEBUG_OPENGL(11)" << " ctx->screen():" << ctx->screen()->availableSize(); // 전체 화면 해상도
|
|
|
|
setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize));
|
|
if (d.manager)
|
|
return;
|
|
// TODO: what if ctx is delete?
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
d.manager = new ShaderManager(ctx);
|
|
QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()?
|
|
#else
|
|
d.manager = new ShaderManager(this);
|
|
#endif
|
|
d.manager->setObjectName(QStringLiteral("__qtav_shader_manager"));
|
|
/// get gl info here because context is current(qt ensure it)
|
|
//const QByteArray extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
|
|
bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms();
|
|
qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL);
|
|
static bool sInfo = true;
|
|
if (sInfo) {
|
|
sInfo = false;
|
|
qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION)));
|
|
qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR)));
|
|
qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER)));
|
|
qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION)));
|
|
/// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null
|
|
bool v = OpenGLHelper::isOpenGLES();
|
|
qDebug("Is OpenGLES: %d", v);
|
|
v = OpenGLHelper::isEGL();
|
|
qDebug("Is EGL: %d", v);
|
|
const int glsl_ver = OpenGLHelper::GLSLVersion();
|
|
qDebug("GLSL version: %d", glsl_ver);
|
|
v = OpenGLHelper::isPBOSupported();
|
|
qDebug("Has PBO: %d", v);
|
|
v = OpenGLHelper::has16BitTexture();
|
|
qDebug("Has 16bit texture: %d", v);
|
|
v = OpenGLHelper::hasRG();
|
|
qDebug("Has RG texture: %d", v);
|
|
qDebug() << ctx->format();
|
|
}
|
|
}
|
|
|
|
QOpenGLContext* OpenGLVideo::openGLContext()
|
|
{
|
|
return d_func().ctx;
|
|
}
|
|
|
|
void OpenGLVideo::setCurrentFrame(const VideoFrame &frame)
|
|
{
|
|
d_func().material->setCurrentFrame(frame);
|
|
d_func().has_a = frame.format().hasAlpha();
|
|
}
|
|
|
|
void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v)
|
|
{
|
|
setViewport(v);
|
|
}
|
|
|
|
void OpenGLVideo::setViewport(const QRectF &r)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.rect = r;
|
|
if (d.norm_viewport)
|
|
{
|
|
d.matrix.setToIdentity();
|
|
if (d.mesh_type == SphereMesh)
|
|
{
|
|
setAngle(d.dAngle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
d.matrix.setToIdentity();
|
|
d.matrix.ortho(r);
|
|
d.update_geo = true; // even true for target_rect != d.rect
|
|
}
|
|
// Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
|
|
//mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0;
|
|
if (d.ctx && d.ctx == QOpenGLContext::currentContext()) {
|
|
//qInfo() << "DEBUG_OPENGL(21)" << " glViewport" << d.rect << d.matrix;
|
|
DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height()));
|
|
}
|
|
}
|
|
|
|
void OpenGLVideo::setBrightness(qreal value)
|
|
{
|
|
d_func().material->setBrightness(value);
|
|
}
|
|
|
|
void OpenGLVideo::setContrast(qreal value)
|
|
{
|
|
d_func().material->setContrast(value);
|
|
}
|
|
|
|
void OpenGLVideo::setHue(qreal value)
|
|
{
|
|
d_func().material->setHue(value);
|
|
}
|
|
|
|
void OpenGLVideo::setSaturation(qreal value)
|
|
{
|
|
d_func().material->setSaturation(value);
|
|
}
|
|
|
|
void OpenGLVideo::setUserShader(VideoShader *shader)
|
|
{
|
|
d_func().user_shader = shader;
|
|
}
|
|
|
|
VideoShader* OpenGLVideo::userShader() const
|
|
{
|
|
return d_func().user_shader;
|
|
}
|
|
|
|
void OpenGLVideo::setMeshType(MeshType value)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if (d.mesh_type == value)
|
|
{
|
|
return;
|
|
}
|
|
d.mesh_type = value;
|
|
d.update_geo = true;
|
|
d.matrix.setToIdentity();
|
|
if (d.mesh_type == SphereMesh && d.norm_viewport) {
|
|
d.matrix.perspective(90.0f, 2.0f, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
OpenGLVideo::MeshType OpenGLVideo::meshType() const
|
|
{
|
|
return d_func().mesh_type;
|
|
}
|
|
|
|
void OpenGLVideo::fill(const QColor &color)
|
|
{
|
|
//DYGL(glClearColor(0.0, 1.0,0.0, 1.0));
|
|
DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()));
|
|
DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
|
}
|
|
void OpenGLVideo::setDualMode(int mode)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
setMeshType(mode == 0 ? SphereMesh : RectMesh);
|
|
d.dualMode = mode;
|
|
}
|
|
void OpenGLVideo::setDualFront(bool front)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.bDualFront = front;
|
|
const float df[16] = {front ? 1.0 : 0.0,};
|
|
d.dual360 = QMatrix4x4(df);
|
|
}
|
|
|
|
void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform)
|
|
{
|
|
#if(TEST_EQUIRECTANGULAR)
|
|
Q_UNUSED(transform)
|
|
#endif
|
|
|
|
QRectF rroi = roi;
|
|
|
|
DPTR_D(OpenGLVideo);
|
|
Q_ASSERT(d.manager);
|
|
Q_EMIT beforeRendering();
|
|
// viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly)
|
|
// 663x373, 1918x959
|
|
DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height()));
|
|
//qInfo() << "roi:" << roi;
|
|
const qint64 mt = d.material->type();
|
|
if (d.material_type != mt)
|
|
{
|
|
#if !(OFF_OTHER_DEBUG)
|
|
qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt);
|
|
#endif
|
|
d.material_type = mt;
|
|
}
|
|
if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it
|
|
{
|
|
return;
|
|
}
|
|
VideoShader *shader = d.user_shader;
|
|
if (!shader) {
|
|
d.manager->dualMode = d.dualMode;
|
|
shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code)
|
|
|
|
// 1920x2176, 1280x1440, 736x960 (2020/07/22 변경)
|
|
if(d.material->frameSize().width() > 1281) // FHD
|
|
{
|
|
d.resolutionMode = 0;
|
|
}
|
|
else if(d.material->frameSize().width() > 736) // HD
|
|
{
|
|
d.resolutionMode = 1;
|
|
}
|
|
else // SD
|
|
{
|
|
// D1 1PIXEL 제거
|
|
d.resolutionMode = 2;
|
|
rroi.setWidth(roi.width()-1.0f); // 신규 1.4 펌웨어에서도 발생
|
|
//qInfo() << "frame size:" << d.material->frameSize() << rroi;
|
|
}
|
|
|
|
}
|
|
shader->update(d.material);
|
|
|
|
//qInfo() << "VS" << shader->vertexShader();
|
|
//qInfo() << "----------------------------------\n" << shader->fragmentShader() << "-----------------------------\n";
|
|
|
|
//float fr = d.rect.height() / d.rect.width();
|
|
|
|
//float r = d.material->frameSize().height() / d.material->frameSize().width();
|
|
QMatrix4x4 m(1,0,0,0,
|
|
0,1,0,0,
|
|
0,0,1,0,
|
|
0,0,0,1);
|
|
|
|
|
|
|
|
|
|
//d.matrix.lookAt(QVector3D(0,0,0),QVector3D(0,0,1),QVector3D(0,1,0));
|
|
|
|
//m.scale(QVector3D(0.7f,0.7f,1));
|
|
//m.flipCoordinates();
|
|
//m.setToIdentity();
|
|
//qInfo() << "target999:" << target;
|
|
//d.matrix.perspective(45.0f, 1.0f, 0.1f, 100.0f);
|
|
|
|
//qInfo() << d.matrix;
|
|
|
|
//m.perspective(70.0f, 1.0f, 0.1f, 100.0f);
|
|
//shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix);
|
|
shader->program()->setUniformValue(shader->resolutionLocation(), d.resolutionMode);
|
|
if(d.dualMode)
|
|
{
|
|
shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix); // transform*d.matrix
|
|
}
|
|
else
|
|
{
|
|
shader->program()->setUniformValue(shader->matrixLocation(), m*d.matrix); // transform*d.matrix
|
|
}
|
|
|
|
d.updateGeometry(shader, target, rroi);
|
|
|
|
#if (TUNE_360 && MODEL_360)
|
|
//shader->program()->setUniformValue(shader->uvOffsetLocation(),d.uvOffset);
|
|
//shader->program()->setUniformValue(shader->radiusOffsetLocation(),d.radiusOffset);
|
|
#endif
|
|
if(d.dualMode) {
|
|
shader->program()->setUniformValue(shader->tune360Location(),d.dual360);
|
|
}
|
|
else
|
|
{
|
|
#if (TUNE_360_FIXED)
|
|
shader->program()->setUniformValue(shader->tune360Location(),d.resolutionMode == 2 ? M360_SD : M360_FHS);
|
|
#else
|
|
shader->program()->setUniformValue(shader->tune360Location(),d.tune360);
|
|
#endif
|
|
}
|
|
|
|
//d.geometry->dumpVertexData();
|
|
|
|
// normalize?
|
|
// const bool blending = d.has_a;
|
|
// if (blending) {
|
|
// DYGL(glEnable(GL_BLEND));
|
|
// gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); //
|
|
// }
|
|
//if (d.mesh_type == OpenGLVideo::SphereMesh)
|
|
//DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf
|
|
//shader->program()->setUniformValue(shader->yOffsetLocation(), 0.0f); // transform*d.matrix
|
|
d.gr.render();
|
|
// shader->program()->setUniformValue(shader->yOffsetLocation(), 0.5f); // transform*d.matrix
|
|
// d.gr2.render();
|
|
// if (blending)
|
|
// DYGL(glDisable(GL_BLEND));
|
|
// d.shader->program()->release(); //glUseProgram(0)
|
|
d.material->unbind();
|
|
|
|
Q_EMIT afterRendering();
|
|
}
|
|
|
|
void OpenGLVideo::resetGL()
|
|
{
|
|
qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender());
|
|
d_func().resetGL();
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
// FIXME: why crash if inherits both QObject and DPtrPrivate?
|
|
class OpenGLVideoPrivate : public DPtrPrivate<OpenGLVideo>
|
|
{
|
|
public:
|
|
OpenGLVideoPrivate()
|
|
: ctx(0)
|
|
, manager(0)
|
|
, material(new VideoMaterial())
|
|
, material_type(0)
|
|
, norm_viewport(true)
|
|
, update_geo(true)
|
|
, tex_target(0)
|
|
, valiad_tex_width(1.0)
|
|
, mesh_type(OpenGLVideo::RectMesh)
|
|
, geometry(NULL)
|
|
, user_shader(NULL)
|
|
#if (SUPPORT_WIDE_MODE)
|
|
, mode(0)
|
|
, bVFlip(false)
|
|
, bHFlip(false)
|
|
#endif // SUPPORT_WIDE_MODE
|
|
|
|
{
|
|
}
|
|
~OpenGLVideoPrivate() {
|
|
if (material) {
|
|
delete material;
|
|
material = 0;
|
|
}
|
|
delete geometry;
|
|
}
|
|
|
|
void resetGL() {
|
|
ctx = 0;
|
|
gr.updateGeometry(NULL);
|
|
if (!manager)
|
|
return;
|
|
manager->setParent(0);
|
|
delete manager;
|
|
manager = 0;
|
|
if (material) {
|
|
delete material;
|
|
material = 0;
|
|
}
|
|
}
|
|
// update geometry(vertex array) set attributes or bind VAO/VBO.
|
|
void updateGeometry(VideoShader* shader, const QRectF& t, const QRectF& r);
|
|
public:
|
|
QOpenGLContext *ctx;
|
|
ShaderManager *manager;
|
|
VideoMaterial *material;
|
|
qint64 material_type;
|
|
bool norm_viewport;
|
|
bool has_a;
|
|
bool update_geo;
|
|
int tex_target;
|
|
qreal valiad_tex_width;
|
|
QSize video_size;
|
|
QRectF target;
|
|
QRectF roi; //including invalid padding width
|
|
OpenGLVideo::MeshType mesh_type;
|
|
TexturedGeometry *geometry;
|
|
GeometryRenderer gr;
|
|
QRectF rect;
|
|
QMatrix4x4 matrix;
|
|
VideoShader *user_shader;
|
|
#if (SUPPORT_WIDE_MODE || USE_SHADER_FLIP)
|
|
int mode;
|
|
bool bVFlip; // 상하 반전
|
|
bool bHFlip; // 좌우반전
|
|
#endif // USE_SHADER_FLIP
|
|
};
|
|
|
|
void OpenGLVideoPrivate::updateGeometry(VideoShader* shader, const QRectF &t, const QRectF &r)
|
|
{
|
|
// also check size change for normalizedROI computation if roi is not normalized
|
|
const bool roi_changed = valiad_tex_width != material->validTextureWidth() || roi != r || video_size != material->frameSize();
|
|
const int tc = shader->textureLocationCount();
|
|
if (roi_changed) {
|
|
roi = r;
|
|
valiad_tex_width = material->validTextureWidth();
|
|
video_size = material->frameSize();
|
|
}
|
|
if (tex_target != shader->textureTarget()) {
|
|
tex_target = shader->textureTarget();
|
|
update_geo = true;
|
|
}
|
|
#if (SUPPORT_WIDE_MODE || USE_SHADER_FLIP)
|
|
qreal top = bVFlip ? -1 : 1;
|
|
qreal bottom = bVFlip ? 2: -2;
|
|
qreal left = bHFlip ? 1 : -1;
|
|
qreal right = bHFlip ? -2 : 2;
|
|
|
|
const QRectF gp = QRectF(left,top,right,bottom);
|
|
QRectF target_rect = norm_viewport ? gp : rect;
|
|
#else // SUPPORT_WIDE_MODE
|
|
// (-1, -1, 2, 2) must flip y
|
|
QRectF target_rect = norm_viewport ? QRectF(-1, 1, 2, -2) : rect;
|
|
#endif // SUPPORT_WIDE_MODE
|
|
if (target.isValid()) {
|
|
if (roi_changed || target != t) {
|
|
target = t;
|
|
update_geo = true;
|
|
//target_rect = target (if valid). // relate to gvf bug?
|
|
}
|
|
} else {
|
|
if (roi_changed) {
|
|
update_geo = true;
|
|
}
|
|
}
|
|
if (!update_geo)
|
|
return;
|
|
delete geometry;
|
|
geometry = NULL;
|
|
if (mesh_type == OpenGLVideo::SphereMesh)
|
|
geometry = new Sphere();
|
|
else
|
|
geometry = new TexturedGeometry();
|
|
//qDebug("updating geometry...");
|
|
// setTextureCount may change the vertex data. Call it before setRect()
|
|
qDebug() << "target rect: " << target_rect ;
|
|
geometry->setTextureCount(shader->textureTarget() == GL_TEXTURE_RECTANGLE ? tc : 1);
|
|
geometry->setGeometryRect(target_rect);
|
|
geometry->setTextureRect(material->mapToTexture(0, roi));
|
|
if (shader->textureTarget() == GL_TEXTURE_RECTANGLE) {
|
|
for (int i = 1; i < tc; ++i) {
|
|
// tc can > planes, but that will compute chroma plane
|
|
geometry->setTextureRect(material->mapToTexture(i, roi), i);
|
|
}
|
|
}
|
|
geometry->create();
|
|
update_geo = false;
|
|
gr.updateGeometry(geometry);
|
|
}
|
|
|
|
OpenGLVideo::OpenGLVideo() {}
|
|
|
|
bool OpenGLVideo::isSupported(VideoFormat::PixelFormat pixfmt)
|
|
{
|
|
return pixfmt != VideoFormat::Format_RGB48BE && pixfmt != VideoFormat::Format_Invalid;
|
|
}
|
|
|
|
void OpenGLVideo::setOpenGLContext(QOpenGLContext *ctx)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if (d.ctx == ctx)
|
|
return;
|
|
qreal b = 0, c = 0, h = 0, s = 0;
|
|
if (d.material) {
|
|
b = d.material->brightness();
|
|
c = d.material->contrast();
|
|
h = d.material->hue();
|
|
s = d.material->saturation();
|
|
delete d.material;
|
|
d.material = 0;
|
|
}
|
|
d.resetGL(); //TODO: is it ok to destroygl resources in another context?
|
|
d.ctx = ctx; // Qt4: set to null in resetGL()
|
|
if (!ctx) {
|
|
return;
|
|
}
|
|
d.material = new VideoMaterial();
|
|
d.material->setBrightness(b);
|
|
d.material->setContrast(c);
|
|
d.material->setHue(h);
|
|
d.material->setSaturation(s);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
d.manager = ctx->findChild<ShaderManager*>(QStringLiteral("__qtav_shader_manager"));
|
|
QSizeF surfaceSize = ctx->surface()->size();
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
|
|
surfaceSize *= ctx->screen()->devicePixelRatio();
|
|
#else
|
|
surfaceSize *= qApp->devicePixelRatio(); //TODO: window()->devicePixelRatio() is the window screen's
|
|
#endif
|
|
#else
|
|
QSizeF surfaceSize = QSizeF(ctx->device()->width(), ctx->device()->height());
|
|
#endif
|
|
setProjectionMatrixToRect(QRectF(QPointF(), surfaceSize));
|
|
if (d.manager)
|
|
return;
|
|
// TODO: what if ctx is delete?
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
|
d.manager = new ShaderManager(ctx);
|
|
QObject::connect(ctx, SIGNAL(aboutToBeDestroyed()), this, SLOT(resetGL()), Qt::DirectConnection); // direct to make sure there is a valid context. makeCurrent in window.aboutToBeDestroyed()?
|
|
#else
|
|
d.manager = new ShaderManager(this);
|
|
#endif
|
|
d.manager->setObjectName(QStringLiteral("__qtav_shader_manager"));
|
|
/// get gl info here because context is current(qt ensure it)
|
|
//const QByteArray extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
|
|
bool hasGLSL = QOpenGLShaderProgram::hasOpenGLShaderPrograms();
|
|
qDebug("OpenGL version: %d.%d hasGLSL: %d", ctx->format().majorVersion(), ctx->format().minorVersion(), hasGLSL);
|
|
static bool sInfo = true;
|
|
if (sInfo) {
|
|
sInfo = false;
|
|
qDebug("GL_VERSION: %s", DYGL(glGetString(GL_VERSION)));
|
|
qDebug("GL_VENDOR: %s", DYGL(glGetString(GL_VENDOR)));
|
|
qDebug("GL_RENDERER: %s", DYGL(glGetString(GL_RENDERER)));
|
|
qDebug("GL_SHADING_LANGUAGE_VERSION: %s", DYGL(glGetString(GL_SHADING_LANGUAGE_VERSION)));
|
|
/// check here with current context can ensure the right result. If the first check is in VideoShader/VideoMaterial/decoder or somewhere else, the context can be null
|
|
bool v = OpenGLHelper::isOpenGLES();
|
|
qDebug("Is OpenGLES: %d", v);
|
|
v = OpenGLHelper::isEGL();
|
|
qDebug("Is EGL: %d", v);
|
|
const int glsl_ver = OpenGLHelper::GLSLVersion();
|
|
qDebug("GLSL version: %d", glsl_ver);
|
|
v = OpenGLHelper::isPBOSupported();
|
|
qDebug("Has PBO: %d", v);
|
|
v = OpenGLHelper::has16BitTexture();
|
|
qDebug("Has 16bit texture: %d", v);
|
|
v = OpenGLHelper::hasRG();
|
|
qDebug("Has RG texture: %d", v);
|
|
qDebug() << ctx->format();
|
|
}
|
|
}
|
|
|
|
QOpenGLContext* OpenGLVideo::openGLContext()
|
|
{
|
|
return d_func().ctx;
|
|
}
|
|
|
|
void OpenGLVideo::setCurrentFrame(const VideoFrame &frame)
|
|
{
|
|
d_func().material->setCurrentFrame(frame);
|
|
d_func().has_a = frame.format().hasAlpha();
|
|
}
|
|
|
|
void OpenGLVideo::setProjectionMatrixToRect(const QRectF &v)
|
|
{
|
|
setViewport(v);
|
|
}
|
|
|
|
void OpenGLVideo::setViewport(const QRectF &r)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.rect = r;
|
|
if (d.norm_viewport)
|
|
{
|
|
d.matrix.setToIdentity();
|
|
if (d.mesh_type == SphereMesh)
|
|
d.matrix.perspective(45.0f, 1.0f, 0.1f, 100.0f); // for sphere
|
|
}
|
|
else
|
|
{
|
|
d.matrix.setToIdentity();
|
|
d.matrix.ortho(r);
|
|
d.update_geo = true; // even true for target_rect != d.rect
|
|
}
|
|
// Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
|
|
//mirrored = mat(0, 0) * mat(1, 1) - mat(0, 1) * mat(1, 0) > 0;
|
|
if (d.ctx && d.ctx == QOpenGLContext::currentContext()) {
|
|
DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height()));
|
|
}
|
|
}
|
|
|
|
void OpenGLVideo::setBrightness(qreal value)
|
|
{
|
|
d_func().material->setBrightness(value);
|
|
}
|
|
|
|
void OpenGLVideo::setContrast(qreal value)
|
|
{
|
|
d_func().material->setContrast(value);
|
|
}
|
|
|
|
void OpenGLVideo::setHue(qreal value)
|
|
{
|
|
d_func().material->setHue(value);
|
|
}
|
|
|
|
void OpenGLVideo::setSaturation(qreal value)
|
|
{
|
|
d_func().material->setSaturation(value);
|
|
}
|
|
|
|
void OpenGLVideo::setUserShader(VideoShader *shader)
|
|
{
|
|
d_func().user_shader = shader;
|
|
}
|
|
|
|
VideoShader* OpenGLVideo::userShader() const
|
|
{
|
|
return d_func().user_shader;
|
|
}
|
|
|
|
void OpenGLVideo::setMeshType(MeshType value)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
if (d.mesh_type == value)
|
|
return;
|
|
d.mesh_type = value;
|
|
d.update_geo = true;
|
|
if (d.mesh_type == SphereMesh && d.norm_viewport) {
|
|
d.matrix.setToIdentity();
|
|
d.matrix.perspective(45.0f, 1.0f, 0.1f, 100.0f); // for sphere
|
|
}
|
|
}
|
|
|
|
OpenGLVideo::MeshType OpenGLVideo::meshType() const
|
|
{
|
|
return d_func().mesh_type;
|
|
}
|
|
|
|
void OpenGLVideo::fill(const QColor &color)
|
|
{
|
|
DYGL(glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()));
|
|
DYGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
|
}
|
|
#if (SUPPORT_WIDE_MODE)
|
|
bool OpenGLVideo::isVFlip()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.bVFlip;
|
|
}
|
|
void OpenGLVideo::setHFlip(bool flip)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.bHFlip = flip;
|
|
d.update_geo = true;
|
|
}
|
|
bool OpenGLVideo::isHFlip()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.bHFlip;
|
|
}
|
|
void OpenGLVideo::setVFlip(bool flip)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.bVFlip = flip;
|
|
d.update_geo = true;
|
|
}
|
|
int OpenGLVideo::mode()
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
return d.mode;
|
|
}
|
|
void OpenGLVideo::setMode(int mode)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
d.update_geo = true; //?
|
|
d.mode = mode;
|
|
}
|
|
#endif // SUPPORT_WIDE_MODE
|
|
void OpenGLVideo::render(const QRectF &target, const QRectF& roi, const QMatrix4x4& transform)
|
|
{
|
|
DPTR_D(OpenGLVideo);
|
|
Q_ASSERT(d.manager);
|
|
Q_EMIT beforeRendering();
|
|
DYGL(glViewport(d.rect.x(), d.rect.y(), d.rect.width(), d.rect.height())); // viewport was used in gpu filters is wrong, qt quick fbo item's is right(so must ensure setProjectionMatrixToRect was called correctly)
|
|
const qint64 mt = d.material->type();
|
|
if (d.material_type != mt)
|
|
{
|
|
#if !(OFF_OTHER_DEBUG)
|
|
qDebug() << "material changed: " << VideoMaterial::typeName(d.material_type) << " => " << VideoMaterial::typeName(mt);
|
|
#endif
|
|
d.material_type = mt;
|
|
}
|
|
if (!d.material->bind()) // bind first because texture parameters(target) mapped from native buffer is unknown before it
|
|
{
|
|
return;
|
|
}
|
|
VideoShader *shader = d.user_shader;
|
|
if (!shader) {
|
|
shader = d.manager->prepareMaterial(d.material, mt); //TODO: print shader type name if changed. prepareMaterial(,sample_code, pp_code)
|
|
}
|
|
shader->update(d.material);
|
|
shader->program()->setUniformValue(shader->matrixLocation(), transform*d.matrix);
|
|
#if (SUPPORT_WIDE_MODE)
|
|
shader->program()->setUniformValue(shader->modeLocation(),d.mode);
|
|
float aspect = (float)(16.0/9.0);//(float)(d.rect.width()) / (float)(d.rect.height());
|
|
shader->program()->setUniformValue(shader->aspectLocation(),aspect);
|
|
#endif // SUPPORT_WIDE_MODE
|
|
|
|
// uniform end. attribute begin
|
|
//#if (REMOVE_RIGHT_GREEN_LINE)
|
|
// QRectF rroi = roi;
|
|
// rroi.setWidth(roi.width()-1.0f);
|
|
// d.updateGeometry(shader, target, rroi);
|
|
//#else
|
|
d.updateGeometry(shader, target, roi);
|
|
//#endif
|
|
// normalize?
|
|
const bool blending = d.has_a;
|
|
if (blending) {
|
|
DYGL(glEnable(GL_BLEND));
|
|
gl().BlendFuncSeparate(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); //
|
|
}
|
|
|
|
//if (d.mesh_type == OpenGLVideo::SphereMesh)
|
|
//DYGL(glEnable(GL_CULL_FACE)); // required for sphere! FIXME: broken in qml and qgvf
|
|
d.gr.render();
|
|
if (blending)
|
|
DYGL(glDisable(GL_BLEND));
|
|
// d.shader->program()->release(); //glUseProgram(0)
|
|
d.material->unbind();
|
|
|
|
Q_EMIT afterRendering();
|
|
}
|
|
|
|
void OpenGLVideo::resetGL()
|
|
{
|
|
qDebug("~~~~~~~~~resetGL %p. from sender %p", d_func().manager, sender());
|
|
d_func().resetGL();
|
|
}
|
|
|
|
#endif // #if (MODEL_360)
|
|
|
|
} //namespace FAV
|