Files
fmviewer3/project/fm_viewer/fav/opengl/OpenGLVideo.cpp
2026-02-21 17:11:31 +09:00

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