mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 00:03:03 +04:00
Merge pull request #29485 from kirtijindal14:opengl-core-highgui
Core, Highgui: OpenGL wrappers & window free-callback API #29485 This PR is a continuation of the work started in [20371](https://github.com/opencv/opencv/pull/20371) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -472,6 +472,244 @@ private:
|
||||
Buffer texCoord_;
|
||||
};
|
||||
|
||||
/** @brief Struct used to describe a vertex array attribute.
|
||||
*/
|
||||
struct CV_EXPORTS Attribute
|
||||
{
|
||||
/** @brief The target defines how you intend to use the buffer object.
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
BYTE = 0x1400, //!< 8-bit integer.
|
||||
UNSIGNED_BYTE = 0x1401, //!< 8-bit unsigned integer.
|
||||
SHORT = 0x1402, //!< 16-bit integer.
|
||||
UNSIGNED_SHORT = 0x1403, //!< 16-bit unsigned integer.
|
||||
INT = 0x1404, //!< 32-bit integer.
|
||||
UNSIGNED_INT = 0x1405, //!< 32-bit unsigned integer.
|
||||
FLOAT = 0x1406, //!< 32-bit floating point.
|
||||
};
|
||||
|
||||
//! The buffer this attribute takes data from.
|
||||
Buffer buffer_;
|
||||
//! Number of bytes between each element described by this attribute.
|
||||
size_t stride_;
|
||||
//! Position of the first element in the buffer in bytes.
|
||||
size_t offset_;
|
||||
//! Number of components of the type (eg.: float = 1 component, vec3 = 3 components).
|
||||
int size_;
|
||||
//! Type of the vertex attribute. See cv::ogl::Attribute::Type.
|
||||
Type type_;
|
||||
//! Is the vertex attribute an integer?
|
||||
bool integer_;
|
||||
//! If the vertex attribute isn't an integer, should it be normalized?
|
||||
bool normalized_;
|
||||
//! Location of the attribute in the program. See cv::ogl::Program::getAttributeLocation.
|
||||
unsigned int shader_loc_;
|
||||
};
|
||||
|
||||
/** @brief Smart pointer for OpenGL vertex arrays with reference counting.
|
||||
*/
|
||||
class CV_EXPORTS VertexArray
|
||||
{
|
||||
public:
|
||||
/** @brief The constructors.
|
||||
|
||||
Creates empty ogl::VertexArray object, creates ogl::VertexArray object from a list of attributes.
|
||||
*/
|
||||
VertexArray();
|
||||
|
||||
/** @overload
|
||||
@param attributes List of attributes.
|
||||
@param autoRelease Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
VertexArray(std::initializer_list<Attribute> attributes, bool autoRelease = false);
|
||||
|
||||
/** @brief Creates ogl::VertexArray object from a list of attributes.
|
||||
|
||||
@param attributes List of attributes.
|
||||
@param autoRelease Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
void create(std::initializer_list<Attribute> attributes, bool autoRelease = false);
|
||||
|
||||
/** @brief Decrements the reference counter and destroys the vertex array object if needed.
|
||||
|
||||
The function will call setAutoRelease(true) .
|
||||
*/
|
||||
void release();
|
||||
|
||||
/** @brief Sets auto release mode.
|
||||
|
||||
The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was
|
||||
bound to a window it could be released at any time (user can close a window). If object's destructor
|
||||
is called after destruction of the context it will cause an error. Thus ogl::VertexArray doesn't destroy
|
||||
OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL context).
|
||||
This function can force ogl::VertexArray destructor to destroy OpenGL object.
|
||||
@param flag Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
void setAutoRelease(bool flag);
|
||||
|
||||
/** @brief Binds OpenGL vertex array.
|
||||
*/
|
||||
void bind() const;
|
||||
|
||||
/** @brief Unbind any vertex array.
|
||||
*/
|
||||
static void unbind();
|
||||
|
||||
//! get OpenGL object id
|
||||
unsigned int vaId() const;
|
||||
|
||||
class Impl;
|
||||
|
||||
private:
|
||||
Ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
/** @brief Smart pointer for OpenGL shader with reference counting.
|
||||
*/
|
||||
class CV_EXPORTS Shader
|
||||
{
|
||||
public:
|
||||
/** @brief The target defines how you intend to use the buffer object.
|
||||
*/
|
||||
enum ShaderType
|
||||
{
|
||||
FRAGMENT = 0x8B30, //!< The shader will be a fragment shader
|
||||
VERTEX = 0x8B31, //!< The shader will be a vertex shader
|
||||
};
|
||||
|
||||
/** @brief The constructors.
|
||||
|
||||
Creates empty ogl::Shader object, creates ogl::Shader object from source code.
|
||||
*/
|
||||
Shader();
|
||||
|
||||
/** @overload
|
||||
@param src Shader source code.
|
||||
@param type Shader type. See cv::ogl::Shader::ShaderType.
|
||||
@param autoRelease Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
Shader(const char* src, ShaderType type, bool autoRelease = false);
|
||||
|
||||
/** @brief Creates ogl::Shader object from source code.
|
||||
|
||||
@param src Shader source code.
|
||||
@param type Shader type. See cv::ogl::Shader::ShaderType.
|
||||
@param autoRelease Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
void create(const char* src, ShaderType type, bool autoRelease = false);
|
||||
|
||||
/** @brief Decrements the reference counter and destroys the shader object if needed.
|
||||
|
||||
The function will call setAutoRelease(true) .
|
||||
*/
|
||||
void release();
|
||||
|
||||
/** @brief Sets auto release mode.
|
||||
|
||||
The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was
|
||||
bound to a window it could be released at any time (user can close a window). If object's destructor
|
||||
is called after destruction of the context it will cause an error. Thus ogl::Shader doesn't destroy
|
||||
OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL context).
|
||||
This function can force ogl::Shader destructor to destroy OpenGL object.
|
||||
@param flag Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
void setAutoRelease(bool flag);
|
||||
|
||||
ShaderType type() const;
|
||||
|
||||
//! get OpenGL object id
|
||||
unsigned int shaderId() const;
|
||||
|
||||
class Impl;
|
||||
|
||||
private:
|
||||
Ptr<Impl> impl_;
|
||||
ShaderType type_;
|
||||
};
|
||||
|
||||
/** @brief Smart pointer for OpenGL program with reference counting.
|
||||
*/
|
||||
class CV_EXPORTS Program
|
||||
{
|
||||
public:
|
||||
/** @brief The constructors.
|
||||
|
||||
Creates empty ogl::Program object, creates ogl::Program object from a group of shader objects.
|
||||
*/
|
||||
Program();
|
||||
|
||||
/** @overload
|
||||
@param vert Vertex shader. See cv::ogl::Shader.
|
||||
@param frag Fragment shader. See cv::ogl::Shader.
|
||||
@param autoRelease Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
Program(const Shader& vert, const Shader& frag, bool autoRelease = false);
|
||||
|
||||
/** @brief Creates ogl::Program object from a group of shader objects.
|
||||
|
||||
@param vert Vertex shader. See cv::ogl::Shader.
|
||||
@param frag Fragment shader. See cv::ogl::Shader.
|
||||
@param autoRelease Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
void create(const Shader& vert, const Shader& frag, bool autoRelease = false);
|
||||
|
||||
/** @brief Decrements the reference counter and destroys the program object if needed.
|
||||
|
||||
The function will call setAutoRelease(true) .
|
||||
*/
|
||||
void release();
|
||||
|
||||
/** @brief Sets auto release mode.
|
||||
|
||||
The lifetime of the OpenGL object is tied to the lifetime of the context. If OpenGL context was
|
||||
bound to a window it could be released at any time (user can close a window). If object's destructor
|
||||
is called after destruction of the context it will cause an error. Thus ogl::Program doesn't destroy
|
||||
OpenGL object in destructor by default (all OpenGL resources will be released with OpenGL context).
|
||||
This function can force ogl::Program destructor to destroy OpenGL object.
|
||||
@param flag Auto release mode (if true, release will be called in object's destructor).
|
||||
*/
|
||||
void setAutoRelease(bool flag);
|
||||
|
||||
/** @brief Binds OpenGL shader program.
|
||||
*/
|
||||
void bind() const;
|
||||
|
||||
/** @brief Unbind any shader program.
|
||||
*/
|
||||
static void unbind();
|
||||
|
||||
/** @brief Returns the shader location of an attribute from its name.
|
||||
@param name Attribute name.
|
||||
*/
|
||||
int getAttributeLocation(const char* name) const;
|
||||
|
||||
/** @brief Returns the shader location of an uniform from its name.
|
||||
@param name Uniform name.
|
||||
*/
|
||||
int getUniformLocation(const char* name) const;
|
||||
|
||||
/** @brief Sets a uniform vector value.
|
||||
@param location Uniform location.
|
||||
@param vec Vector value.
|
||||
*/
|
||||
static void setUniformVec3(int location, Vec3f vec);
|
||||
|
||||
/** @brief Sets a uniform matrix value.
|
||||
@param location Uniform location.
|
||||
@param mat Matrix value.
|
||||
*/
|
||||
static void setUniformMat4x4(int location, Matx44f mat);
|
||||
|
||||
//! get OpenGL object id
|
||||
unsigned int programId() const;
|
||||
|
||||
class Impl;
|
||||
|
||||
private:
|
||||
Ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
/////////////////// Render Functions ///////////////////
|
||||
|
||||
//! render mode
|
||||
@@ -488,6 +726,18 @@ enum RenderModes {
|
||||
POLYGON = 0x0009
|
||||
};
|
||||
|
||||
//! index type
|
||||
enum IndexType {
|
||||
UNSIGNED_BYTE = 0x1401,
|
||||
UNSIGNED_SHORT = 0x1403,
|
||||
UNSIGNED_INT = 0x1405,
|
||||
};
|
||||
|
||||
//! capability
|
||||
enum Capability {
|
||||
DEPTH_TEST = 0x0B71,
|
||||
};
|
||||
|
||||
/** @brief Render OpenGL texture or primitives.
|
||||
@param tex Texture to draw.
|
||||
@param wndRect Region of window, where to draw a texture (normalized coordinates).
|
||||
@@ -512,6 +762,41 @@ CV_EXPORTS void render(const Arrays& arr, int mode = POINTS, Scalar color = Scal
|
||||
*/
|
||||
CV_EXPORTS void render(const Arrays& arr, InputArray indices, int mode = POINTS, Scalar color = Scalar::all(255));
|
||||
|
||||
/** @brief Clears the current target to a single color.
|
||||
@param color New color.
|
||||
*/
|
||||
CV_EXPORTS void clearColor(Scalar color = Scalar::all(0));
|
||||
|
||||
/** @brief Clears the current target depth to a single value.
|
||||
@param depth New depth.
|
||||
*/
|
||||
CV_EXPORTS void clearDepth(float depth);
|
||||
|
||||
/** @brief Renders primitives from array data.
|
||||
@param first Index of the first vertex to draw.
|
||||
@param count Number of vertices to draw.
|
||||
@param mode Rendering mode. See cv::ogl::RenderModes.
|
||||
*/
|
||||
CV_EXPORTS void drawArrays(int first, int count, int mode);
|
||||
|
||||
/** @brief Renders primitives from array data with index buffer set.
|
||||
@param first Byte offset of the first index in the bound element buffer.
|
||||
@param count Number of indices to draw.
|
||||
@param type Index type. See cv::ogl::IndexType.
|
||||
@param mode Rendering mode. See cv::ogl::RenderModes.
|
||||
*/
|
||||
CV_EXPORTS void drawElements(int first, int count, int type, int mode);
|
||||
|
||||
/** @brief Enable server-side GL capabilities.
|
||||
@param cap The capability to enable.
|
||||
*/
|
||||
CV_EXPORTS void enable(int cap);
|
||||
|
||||
/** @brief Disable server-side GL capabilities.
|
||||
@param cap The capability to disable.
|
||||
*/
|
||||
CV_EXPORTS void disable(int cap);
|
||||
|
||||
/////////////////// CL-GL Interoperability Functions ///////////////////
|
||||
|
||||
namespace ocl {
|
||||
|
||||
+632
-2
@@ -316,6 +316,8 @@ public:
|
||||
void copyFrom(GLsizeiptr size, const GLvoid* data);
|
||||
void copyTo(GLsizeiptr size, GLvoid* data) const;
|
||||
|
||||
void resize(GLsizeiptr size, GLenum target);
|
||||
|
||||
void* mapHost(GLenum access);
|
||||
void unmapHost();
|
||||
|
||||
@@ -407,6 +409,19 @@ void cv::ogl::Buffer::Impl::copyFrom(GLsizeiptr size, const GLvoid* data)
|
||||
CV_CheckGlError();
|
||||
}
|
||||
|
||||
void cv::ogl::Buffer::Impl::resize(GLsizeiptr size, GLenum target)
|
||||
{
|
||||
gl::BindBuffer(target, bufId_);
|
||||
CV_CheckGlError();
|
||||
|
||||
// keep the same buffer id so bound vertex arrays stay valid
|
||||
gl::BufferData(target, size, 0, gl::DYNAMIC_DRAW);
|
||||
CV_CheckGlError();
|
||||
|
||||
gl::BindBuffer(target, 0);
|
||||
CV_CheckGlError();
|
||||
}
|
||||
|
||||
void cv::ogl::Buffer::Impl::copyTo(GLsizeiptr size, GLvoid* data) const
|
||||
{
|
||||
gl::BindBuffer(gl::COPY_READ_BUFFER, bufId_);
|
||||
@@ -547,8 +562,15 @@ void cv::ogl::Buffer::create(int arows, int acols, int atype, Target target, boo
|
||||
#else
|
||||
if (rows_ != arows || cols_ != acols || type_ != atype)
|
||||
{
|
||||
const GLsizeiptr asize = arows * acols * CV_ELEM_SIZE(atype);
|
||||
impl_.reset(new Impl(asize, 0, target, autoRelease));
|
||||
const GLsizeiptr asize = (GLsizeiptr)arows * acols * CV_ELEM_SIZE(atype);
|
||||
// resize in place when sole owner (keeps the GL id); else allocate fresh (copy-on-write)
|
||||
if (impl_->bufId() != 0 && impl_.use_count() == 1)
|
||||
{
|
||||
impl_->resize(asize, target);
|
||||
impl_->setAutoRelease(autoRelease);
|
||||
}
|
||||
else
|
||||
impl_.reset(new Impl(asize, 0, target, autoRelease));
|
||||
rows_ = arows;
|
||||
cols_ = acols;
|
||||
type_ = atype;
|
||||
@@ -1411,6 +1433,551 @@ void cv::ogl::Arrays::bind() const
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ogl::VertexArray
|
||||
|
||||
#ifndef HAVE_OPENGL
|
||||
|
||||
class cv::ogl::VertexArray::Impl
|
||||
{
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class cv::ogl::VertexArray::Impl
|
||||
{
|
||||
public:
|
||||
static const Ptr<Impl>& empty();
|
||||
|
||||
Impl(GLuint vaId, bool autoRelease);
|
||||
Impl(std::initializer_list<Attribute> attributes, bool autoRelease);
|
||||
~Impl();
|
||||
|
||||
void bind();
|
||||
|
||||
void setAutoRelease(bool flag) { autoRelease_ = flag; }
|
||||
|
||||
GLuint vaId() const { return vaId_; }
|
||||
|
||||
private:
|
||||
Impl();
|
||||
|
||||
GLuint vaId_;
|
||||
bool autoRelease_;
|
||||
};
|
||||
|
||||
const Ptr<cv::ogl::VertexArray::Impl>& cv::ogl::VertexArray::Impl::empty()
|
||||
{
|
||||
static Ptr<Impl> p(new Impl);
|
||||
return p;
|
||||
}
|
||||
|
||||
cv::ogl::VertexArray::Impl::Impl() : vaId_(0), autoRelease_(false)
|
||||
{
|
||||
}
|
||||
|
||||
cv::ogl::VertexArray::Impl::Impl(GLuint vaId, bool autoRelease) : vaId_(vaId), autoRelease_(autoRelease)
|
||||
{
|
||||
CV_Assert( gl::IsVertexArray(vaId) == gl::TRUE_ );
|
||||
}
|
||||
|
||||
cv::ogl::VertexArray::Impl::Impl(std::initializer_list<Attribute> attributes, bool autoRelease) : vaId_(0), autoRelease_(autoRelease)
|
||||
{
|
||||
gl::GenVertexArrays(1, &vaId_);
|
||||
CV_CheckGlError();
|
||||
|
||||
CV_Assert( vaId_ != 0 );
|
||||
gl::BindVertexArray(vaId_);
|
||||
CV_CheckGlError();
|
||||
|
||||
for (auto& attribute : attributes)
|
||||
{
|
||||
attribute.buffer_.bind(Buffer::ARRAY_BUFFER);
|
||||
if (attribute.integer_)
|
||||
{
|
||||
gl::VertexAttribIPointer(
|
||||
attribute.shader_loc_,
|
||||
attribute.size_,
|
||||
attribute.type_,
|
||||
static_cast<GLsizei>(attribute.stride_),
|
||||
reinterpret_cast<const void*>(attribute.offset_)
|
||||
);
|
||||
CV_CheckGlError();
|
||||
}
|
||||
else
|
||||
{
|
||||
gl::VertexAttribPointer(
|
||||
attribute.shader_loc_,
|
||||
attribute.size_,
|
||||
attribute.type_,
|
||||
attribute.normalized_,
|
||||
static_cast<GLsizei>(attribute.stride_),
|
||||
reinterpret_cast<const void*>(attribute.offset_)
|
||||
);
|
||||
CV_CheckGlError();
|
||||
}
|
||||
|
||||
gl::EnableVertexAttribArray(attribute.shader_loc_);
|
||||
CV_CheckGlError();
|
||||
}
|
||||
}
|
||||
|
||||
cv::ogl::VertexArray::Impl::~Impl()
|
||||
{
|
||||
if (autoRelease_ && vaId_)
|
||||
gl::DeleteVertexArrays(1, &vaId_);
|
||||
}
|
||||
|
||||
void cv::ogl::VertexArray::Impl::bind()
|
||||
{
|
||||
gl::BindVertexArray(vaId_);
|
||||
CV_CheckGlError();
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
cv::ogl::VertexArray::VertexArray()
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_ = Impl::empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
cv::ogl::VertexArray::VertexArray(std::initializer_list<Attribute> attributes, bool autoRelease)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(attributes); CV_UNUSED(autoRelease);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_.reset(new Impl(attributes, autoRelease));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::VertexArray::create(std::initializer_list<Attribute> attributes, bool autoRelease)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(attributes); CV_UNUSED(autoRelease);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_.reset(new Impl(attributes, autoRelease));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::VertexArray::release()
|
||||
{
|
||||
#ifdef HAVE_OPENGL
|
||||
if (impl_)
|
||||
impl_->setAutoRelease(true);
|
||||
impl_ = Impl::empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::VertexArray::setAutoRelease(bool flag)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(flag);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_->setAutoRelease(flag);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::VertexArray::bind() const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_->bind();
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::VertexArray::unbind()
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::BindVertexArray(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int cv::ogl::VertexArray::vaId() const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
return impl_->vaId();
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ogl::Shader
|
||||
|
||||
#ifndef HAVE_OPENGL
|
||||
|
||||
class cv::ogl::Shader::Impl
|
||||
{
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class cv::ogl::Shader::Impl
|
||||
{
|
||||
public:
|
||||
static const Ptr<Impl>& empty();
|
||||
|
||||
Impl(GLuint shaderId, bool autoRelease);
|
||||
Impl(const char* src, GLenum type, bool autoRelease);
|
||||
~Impl();
|
||||
|
||||
void setAutoRelease(bool flag) { autoRelease_ = flag; }
|
||||
|
||||
GLuint shaderId() const { return shaderId_; }
|
||||
|
||||
private:
|
||||
Impl();
|
||||
|
||||
GLuint shaderId_;
|
||||
bool autoRelease_;
|
||||
};
|
||||
|
||||
const Ptr<cv::ogl::Shader::Impl>& cv::ogl::Shader::Impl::empty()
|
||||
{
|
||||
static Ptr<Impl> p(new Impl);
|
||||
return p;
|
||||
}
|
||||
|
||||
cv::ogl::Shader::Impl::Impl() : shaderId_(0), autoRelease_(false)
|
||||
{
|
||||
}
|
||||
|
||||
cv::ogl::Shader::Impl::Impl(GLuint shaderId, bool autoRelease) : shaderId_(shaderId), autoRelease_(autoRelease)
|
||||
{
|
||||
CV_Assert( gl::IsShader(shaderId) == gl::TRUE_ );
|
||||
}
|
||||
|
||||
cv::ogl::Shader::Impl::Impl(const char* src, GLenum type, bool autoRelease) : shaderId_(0), autoRelease_(autoRelease)
|
||||
{
|
||||
shaderId_ = gl::CreateShader(type);
|
||||
CV_CheckGlError();
|
||||
|
||||
CV_Assert( shaderId_ != 0 );
|
||||
|
||||
GLint status;
|
||||
gl::ShaderSource(shaderId_, 1, &src, 0);
|
||||
gl::CompileShader(shaderId_);
|
||||
gl::GetShaderiv(shaderId_, gl::COMPILE_STATUS, &status);
|
||||
if (!status) {
|
||||
String info_log;
|
||||
gl::GetShaderiv(shaderId_, gl::INFO_LOG_LENGTH, &status);
|
||||
info_log.resize(status);
|
||||
gl::GetShaderInfoLog(shaderId_, GLsizei(info_log.size()) + 1, nullptr, &info_log[0]);
|
||||
CV_Error(Error::OpenGlApiCallError, info_log);
|
||||
}
|
||||
|
||||
CV_CheckGlError();
|
||||
}
|
||||
|
||||
cv::ogl::Shader::Impl::~Impl()
|
||||
{
|
||||
if (autoRelease_ && shaderId_)
|
||||
gl::DeleteShader(shaderId_);
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
cv::ogl::Shader::Shader() : type_(ShaderType::VERTEX)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_ = Impl::empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
cv::ogl::Shader::Shader(const char* src, ShaderType type, bool autoRelease) : type_(type)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(src); CV_UNUSED(type); CV_UNUSED(autoRelease);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_.reset(new Impl(src, type, autoRelease));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Shader::create(const char* src, ShaderType type, bool autoRelease)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(src); CV_UNUSED(type); CV_UNUSED(autoRelease);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_.reset(new Impl(src, type, autoRelease));
|
||||
type_ = type;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Shader::release()
|
||||
{
|
||||
#ifdef HAVE_OPENGL
|
||||
if (impl_)
|
||||
impl_->setAutoRelease(true);
|
||||
impl_ = Impl::empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Shader::setAutoRelease(bool flag)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(flag);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_->setAutoRelease(flag);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int cv::ogl::Shader::shaderId() const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
return impl_->shaderId();
|
||||
#endif
|
||||
}
|
||||
|
||||
cv::ogl::Shader::ShaderType cv::ogl::Shader::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// ogl::Program
|
||||
|
||||
#ifndef HAVE_OPENGL
|
||||
|
||||
class cv::ogl::Program::Impl
|
||||
{
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class cv::ogl::Program::Impl
|
||||
{
|
||||
public:
|
||||
static const Ptr<Impl>& empty();
|
||||
|
||||
Impl(GLuint programId, bool autoRelease);
|
||||
Impl(const Shader& vert, const Shader& frag, bool autoRelease);
|
||||
~Impl();
|
||||
|
||||
void bind();
|
||||
|
||||
int getAttributeLocation(const char* name) const;
|
||||
int getUniformLocation(const char* name) const;
|
||||
|
||||
void setAutoRelease(bool flag) { autoRelease_ = flag; }
|
||||
|
||||
GLuint programId() const { return programId_; }
|
||||
|
||||
private:
|
||||
Impl();
|
||||
|
||||
Shader vert_;
|
||||
Shader frag_;
|
||||
GLuint programId_;
|
||||
bool autoRelease_;
|
||||
};
|
||||
|
||||
const Ptr<cv::ogl::Program::Impl>& cv::ogl::Program::Impl::empty()
|
||||
{
|
||||
static Ptr<Impl> p(new Impl);
|
||||
return p;
|
||||
}
|
||||
|
||||
cv::ogl::Program::Impl::Impl() : programId_(0), autoRelease_(false)
|
||||
{
|
||||
}
|
||||
|
||||
cv::ogl::Program::Impl::Impl(GLuint programId, bool autoRelease) : programId_(programId), autoRelease_(autoRelease)
|
||||
{
|
||||
CV_Assert( gl::IsProgram(programId) == gl::TRUE_ );
|
||||
}
|
||||
|
||||
cv::ogl::Program::Impl::Impl(const Shader& vert, const Shader& frag, bool autoRelease) : programId_(0), autoRelease_(autoRelease)
|
||||
{
|
||||
programId_ = gl::CreateProgram();
|
||||
CV_CheckGlError();
|
||||
|
||||
CV_Assert( programId_ != 0 );
|
||||
|
||||
gl::AttachShader(programId_, vert.shaderId());
|
||||
CV_CheckGlError();
|
||||
|
||||
gl::AttachShader(programId_, frag.shaderId());
|
||||
CV_CheckGlError();
|
||||
|
||||
GLint status;
|
||||
gl::LinkProgram(programId_);
|
||||
gl::GetProgramiv(programId_, gl::LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
String info_log;
|
||||
gl::GetProgramiv(programId_, gl::INFO_LOG_LENGTH, &status);
|
||||
info_log.resize(status);
|
||||
gl::GetProgramInfoLog(programId_, GLsizei(info_log.size()) + 1, nullptr, &info_log[0]);
|
||||
CV_Error(Error::OpenGlApiCallError, info_log);
|
||||
}
|
||||
|
||||
CV_CheckGlError();
|
||||
|
||||
vert_ = vert;
|
||||
frag_ = frag;
|
||||
}
|
||||
|
||||
cv::ogl::Program::Impl::~Impl()
|
||||
{
|
||||
if (autoRelease_ && programId_)
|
||||
gl::DeleteProgram(programId_);
|
||||
}
|
||||
|
||||
void cv::ogl::Program::Impl::bind()
|
||||
{
|
||||
gl::UseProgram(programId_);
|
||||
CV_CheckGlError();
|
||||
}
|
||||
|
||||
int cv::ogl::Program::Impl::getAttributeLocation(const char* name) const
|
||||
{
|
||||
// -1 if absent (e.g. optimized out); caller decides.
|
||||
int location = gl::GetAttribLocation(programId_, name);
|
||||
CV_CheckGlError();
|
||||
return location;
|
||||
}
|
||||
|
||||
int cv::ogl::Program::Impl::getUniformLocation(const char* name) const
|
||||
{
|
||||
// -1 if absent (e.g. optimized out); caller decides.
|
||||
int location = gl::GetUniformLocation(programId_, name);
|
||||
CV_CheckGlError();
|
||||
return location;
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
cv::ogl::Program::Program()
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_ = Impl::empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
cv::ogl::Program::Program(const Shader& vert, const Shader& frag, bool autoRelease)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(vert); CV_UNUSED(frag); CV_UNUSED(autoRelease);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_.reset(new Impl(vert, frag, autoRelease));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::create(const Shader& vert, const Shader& frag, bool autoRelease)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(vert); CV_UNUSED(frag); CV_UNUSED(autoRelease);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_.reset(new Impl(vert, frag, autoRelease));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::release()
|
||||
{
|
||||
#ifdef HAVE_OPENGL
|
||||
if (impl_)
|
||||
impl_->setAutoRelease(true);
|
||||
impl_ = Impl::empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::setAutoRelease(bool flag)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(flag);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_->setAutoRelease(flag);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::bind() const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
impl_->bind();
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::unbind()
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::UseProgram(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int cv::ogl::Program::getAttributeLocation(const char* name) const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(name);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
return impl_->getAttributeLocation(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
int cv::ogl::Program::getUniformLocation(const char* name) const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(name);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
return impl_->getUniformLocation(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::setUniformVec3(int loc, Vec3f vec)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(loc); CV_UNUSED(vec);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::Uniform3f(loc, vec(0), vec(1), vec(2));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::Program::setUniformMat4x4(int loc, Matx44f mat)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(loc); CV_UNUSED(mat);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::UniformMatrix4fv(loc, 1, true, mat.val);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int cv::ogl::Program::programId() const
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
throw_no_ogl();
|
||||
#else
|
||||
return impl_->programId();
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Rendering
|
||||
|
||||
@@ -1577,6 +2144,69 @@ void cv::ogl::render(const ogl::Arrays& arr, InputArray indices, int mode, Scala
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::clearColor(Scalar color)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(color);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::ClearColor(static_cast<GLfloat>(color[0] / 255.0), static_cast<GLfloat>(color[1] / 255.0),
|
||||
static_cast<GLfloat>(color[2] / 255.0), static_cast<GLfloat>(color[3] / 255.0));
|
||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||
#endif // HAVE_OPENGL
|
||||
}
|
||||
|
||||
void cv::ogl::clearDepth(float depth)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(depth);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::ClearDepth(depth);
|
||||
gl::Clear(gl::DEPTH_BUFFER_BIT);
|
||||
#endif // HAVE_OPENGL
|
||||
}
|
||||
|
||||
void cv::ogl::drawArrays(int first, int count, int mode)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(first); CV_UNUSED(count); CV_UNUSED(mode);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::DrawArrays(mode, first, count);
|
||||
#endif // HAVE_OPENGL
|
||||
}
|
||||
|
||||
void cv::ogl::drawElements(int first, int count, int type, int mode)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(first); CV_UNUSED(count); CV_UNUSED(type); CV_UNUSED(mode);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::DrawElements(mode, count, type, reinterpret_cast<const void*>(static_cast<size_t>(first)));
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::enable(int cap)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(cap);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::Enable(cap);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cv::ogl::disable(int cap)
|
||||
{
|
||||
#ifndef HAVE_OPENGL
|
||||
CV_UNUSED(cap);
|
||||
throw_no_ogl();
|
||||
#else
|
||||
gl::Disable(cap);
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// CL-GL Interoperability
|
||||
|
||||
|
||||
@@ -201,6 +201,11 @@ typedef void (*TrackbarCallback)(int pos, void* userdata);
|
||||
*/
|
||||
typedef void (*OpenGlDrawCallback)(void* userdata);
|
||||
|
||||
/** @brief Callback function defined to be called when a window's resources should be freed. See cv::setOpenGlFreeCallback
|
||||
@param userdata The window's OpenGL user data to be freed.
|
||||
*/
|
||||
typedef void (*OpenGlFreeCallback)(void* userdata);
|
||||
|
||||
/** @brief Callback function for a button created by cv::createButton
|
||||
@param state current state of the button. It could be -1 for a push button, 0 or 1 for a check/radio box button.
|
||||
@param userdata The optional parameter.
|
||||
@@ -617,6 +622,26 @@ prototyped as void Foo(void\*) .
|
||||
*/
|
||||
CV_EXPORTS void setOpenGlDrawCallback(const String& winname, OpenGlDrawCallback onOpenGlDraw, void* userdata = 0);
|
||||
|
||||
/** @brief Sets a callback function to be called when OpenGL resources related to a window should be freed.
|
||||
|
||||
@param winname Name of the window.
|
||||
@param onOpenGlFree Pointer to the function to be called when resources should be freed. This
|
||||
function should be prototyped as void Foo(void\*) .
|
||||
*/
|
||||
CV_EXPORTS void setOpenGlFreeCallback(const String& winname, OpenGlFreeCallback onOpenGlFree);
|
||||
|
||||
/** @brief This function returns the pointer to the function associated to a window to be called every frame. See cv::setOpenGlDrawCallback.
|
||||
|
||||
@param winname Name of the window.
|
||||
*/
|
||||
CV_EXPORTS OpenGlDrawCallback getOpenGlDrawCallback(const String& winname);
|
||||
|
||||
/** @brief This function returns the pointer to the OpenGL user data associated to a window.
|
||||
|
||||
@param winname Name of the window.
|
||||
*/
|
||||
CV_EXPORTS void* getOpenGlUserData(const String& winname);
|
||||
|
||||
/** @brief Sets the specified window as current OpenGL context.
|
||||
|
||||
@param winname Name of the window.
|
||||
|
||||
@@ -116,6 +116,11 @@ void setOpenGLDrawCallbackImpl(const char* window_name, CvOpenGlDrawCallback cal
|
||||
void setOpenGLContextImpl(const char* window_name);
|
||||
void updateWindowImpl(const char* window_name);
|
||||
|
||||
typedef void (CV_CDECL *CvOpenGlFreeCallback)(void* userdata);
|
||||
void setOpenGLFreeCallbackImpl(const char* window_name, CvOpenGlFreeCallback callback);
|
||||
CvOpenGlDrawCallback getOpenGLDrawCallbackImpl(const char* window_name);
|
||||
void* getOpenGLUserDataImpl(const char* window_name);
|
||||
|
||||
|
||||
//Yannick Verdie 2010, Max Kostin 2015
|
||||
void cvSetModeWindow_W32(const char* name, double prop_value);
|
||||
|
||||
@@ -915,6 +915,24 @@ void cv::setOpenGlDrawCallback(const String& name, OpenGlDrawCallback callback,
|
||||
setOpenGLDrawCallbackImpl(name.c_str(), callback, userdata);
|
||||
}
|
||||
|
||||
void cv::setOpenGlFreeCallback(const String& name, OpenGlFreeCallback callback)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
setOpenGLFreeCallbackImpl(name.c_str(), callback);
|
||||
}
|
||||
|
||||
cv::OpenGlDrawCallback cv::getOpenGlDrawCallback(const String& name)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
return getOpenGLDrawCallbackImpl(name.c_str());
|
||||
}
|
||||
|
||||
void* cv::getOpenGlUserData(const String& name)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
return getOpenGLUserDataImpl(name.c_str());
|
||||
}
|
||||
|
||||
void cv::setOpenGlContext(const String& windowName)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
@@ -994,6 +1012,14 @@ void cv::imshow( const String& winname, InputArray _img )
|
||||
}
|
||||
else
|
||||
{
|
||||
auto prevCallback = getOpenGlDrawCallback(winname);
|
||||
|
||||
if (prevCallback != nullptr && prevCallback != glDrawTextureCallback)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "OpenCV/UI: can't show image because the OpenGL callback is already being used on window: '" << winname << "'");
|
||||
return;
|
||||
}
|
||||
|
||||
const double autoSize = getWindowProperty(winname, WND_PROP_AUTOSIZE);
|
||||
|
||||
if (autoSize > 0)
|
||||
@@ -1113,6 +1139,21 @@ void updateWindowImpl(const char*)
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
}
|
||||
|
||||
void setOpenGLFreeCallbackImpl(const char*, CvOpenGlFreeCallback)
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
}
|
||||
|
||||
CvOpenGlDrawCallback getOpenGLDrawCallbackImpl(const char*)
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
}
|
||||
|
||||
void* getOpenGLUserDataImpl(const char*)
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "The library is compiled without OpenGL support");
|
||||
}
|
||||
|
||||
#endif // !HAVE_OPENGL
|
||||
|
||||
//========================= Qt fallback =========================
|
||||
|
||||
@@ -727,6 +727,53 @@ void setOpenGLDrawCallbackImpl(const char* window_name, CvOpenGlDrawCallback cal
|
||||
}
|
||||
|
||||
|
||||
void setOpenGLFreeCallbackImpl(const char* window_name, CvOpenGlFreeCallback callback)
|
||||
{
|
||||
if (!guiMainThread)
|
||||
CV_Error( cv::Error::StsNullPtr, "NULL guiReceiver (please create a window)" );
|
||||
|
||||
QMetaObject::invokeMethod(guiMainThread,
|
||||
"setOpenGlFreeCallback",
|
||||
autoBlockingConnection(),
|
||||
Q_ARG(QString, QString(window_name)),
|
||||
Q_ARG(void*, (void*)callback));
|
||||
}
|
||||
|
||||
|
||||
CvOpenGlDrawCallback getOpenGLDrawCallbackImpl(const char* window_name)
|
||||
{
|
||||
if (!guiMainThread)
|
||||
CV_Error( cv::Error::StsNullPtr, "NULL guiReceiver (please create a window)" );
|
||||
|
||||
void* callback;
|
||||
|
||||
QMetaObject::invokeMethod(guiMainThread,
|
||||
"getOpenGlDrawCallback",
|
||||
autoBlockingConnection(),
|
||||
Q_RETURN_ARG(void*, callback),
|
||||
Q_ARG(QString, QString(window_name)));
|
||||
|
||||
return (CvOpenGlDrawCallback)callback;
|
||||
}
|
||||
|
||||
|
||||
void* getOpenGLUserDataImpl(const char* window_name)
|
||||
{
|
||||
if (!guiMainThread)
|
||||
CV_Error( cv::Error::StsNullPtr, "NULL guiReceiver (please create a window)" );
|
||||
|
||||
void* data;
|
||||
|
||||
QMetaObject::invokeMethod(guiMainThread,
|
||||
"getOpenGlUserData",
|
||||
autoBlockingConnection(),
|
||||
Q_RETURN_ARG(void*, data),
|
||||
Q_ARG(QString, QString(window_name)));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
void setOpenGLContextImpl(const char* window_name)
|
||||
{
|
||||
if (!guiMainThread)
|
||||
@@ -1238,6 +1285,34 @@ void GuiReceiver::setOpenGlDrawCallback(QString name, void* callback, void* user
|
||||
w->setOpenGlDrawCallback((CvOpenGlDrawCallback) callback, userdata);
|
||||
}
|
||||
|
||||
void GuiReceiver::setOpenGlFreeCallback(QString name, void* callback)
|
||||
{
|
||||
QPointer<CvWindow> w = icvFindWindowByName(name);
|
||||
|
||||
if (w)
|
||||
w->setOpenGlFreeCallback((CvOpenGlDrawCallback)callback);
|
||||
}
|
||||
|
||||
void* GuiReceiver::getOpenGlDrawCallback(QString name)
|
||||
{
|
||||
QPointer<CvWindow> w = icvFindWindowByName(name);
|
||||
|
||||
if (!w)
|
||||
return nullptr;
|
||||
|
||||
return reinterpret_cast<void*>(w->getOpenGlDrawCallback());
|
||||
}
|
||||
|
||||
void* GuiReceiver::getOpenGlUserData(QString name)
|
||||
{
|
||||
QPointer<CvWindow> w = icvFindWindowByName(name);
|
||||
|
||||
if (!w)
|
||||
return nullptr;
|
||||
|
||||
return w->getOpenGlUserData();
|
||||
}
|
||||
|
||||
void GuiReceiver::setOpenGlContext(QString name)
|
||||
{
|
||||
QPointer<CvWindow> w = icvFindWindowByName(name);
|
||||
@@ -1893,6 +1968,24 @@ void CvWindow::setOpenGlDrawCallback(CvOpenGlDrawCallback callback, void* userda
|
||||
}
|
||||
|
||||
|
||||
void CvWindow::setOpenGlFreeCallback(CvOpenGlFreeCallback callback)
|
||||
{
|
||||
myView->setOpenGlFreeCallback(callback);
|
||||
}
|
||||
|
||||
|
||||
CvOpenGlDrawCallback CvWindow::getOpenGlDrawCallback()
|
||||
{
|
||||
return myView->getOpenGlDrawCallback();
|
||||
}
|
||||
|
||||
|
||||
void* CvWindow::getOpenGlUserData()
|
||||
{
|
||||
return myView->getOpenGlUserData();
|
||||
}
|
||||
|
||||
|
||||
void CvWindow::makeCurrentOpenGlContext()
|
||||
{
|
||||
myView->makeCurrentOpenGlContext();
|
||||
@@ -2587,6 +2680,24 @@ void DefaultViewPort::setOpenGlDrawCallback(CvOpenGlDrawCallback /*callback*/, v
|
||||
}
|
||||
|
||||
|
||||
void DefaultViewPort::setOpenGlFreeCallback(CvOpenGlFreeCallback /*callback*/)
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "Window doesn't support OpenGL");
|
||||
}
|
||||
|
||||
|
||||
CvOpenGlDrawCallback DefaultViewPort::getOpenGlDrawCallback()
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "Window doesn't support OpenGL");
|
||||
}
|
||||
|
||||
|
||||
void* DefaultViewPort::getOpenGlUserData()
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "Window doesn't support OpenGL");
|
||||
}
|
||||
|
||||
|
||||
void DefaultViewPort::makeCurrentOpenGlContext()
|
||||
{
|
||||
CV_Error(cv::Error::OpenGlNotSupported, "Window doesn't support OpenGL");
|
||||
@@ -3201,11 +3312,15 @@ void DefaultViewPort::setSize(QSize /*size_*/)
|
||||
OpenGlViewPort::OpenGlViewPort(QWidget* _parent) : OpenCVQtWidgetBase(_parent), OCVViewPort(), size(-1, -1)
|
||||
{
|
||||
glDrawCallback = 0;
|
||||
glFreeCallback = 0;
|
||||
glDrawData = 0;
|
||||
}
|
||||
|
||||
OpenGlViewPort::~OpenGlViewPort()
|
||||
{
|
||||
// Fire the free callback so user GL resources are released (parity with GTK/w32).
|
||||
if (glFreeCallback && glDrawData)
|
||||
glFreeCallback(glDrawData);
|
||||
}
|
||||
|
||||
QWidget* OpenGlViewPort::getWidget()
|
||||
@@ -3245,6 +3360,21 @@ void OpenGlViewPort::setOpenGlDrawCallback(CvOpenGlDrawCallback callback, void*
|
||||
glDrawData = userdata;
|
||||
}
|
||||
|
||||
void OpenGlViewPort::setOpenGlFreeCallback(CvOpenGlFreeCallback callback)
|
||||
{
|
||||
glFreeCallback = callback;
|
||||
}
|
||||
|
||||
CvOpenGlDrawCallback OpenGlViewPort::getOpenGlDrawCallback()
|
||||
{
|
||||
return glDrawCallback;
|
||||
}
|
||||
|
||||
void* OpenGlViewPort::getOpenGlUserData()
|
||||
{
|
||||
return glDrawData;
|
||||
}
|
||||
|
||||
void OpenGlViewPort::makeCurrentOpenGlContext()
|
||||
{
|
||||
makeCurrent();
|
||||
|
||||
@@ -156,6 +156,9 @@ public slots:
|
||||
void enablePropertiesButtonEachWindow();
|
||||
|
||||
void setOpenGlDrawCallback(QString name, void* callback, void* userdata);
|
||||
void setOpenGlFreeCallback(QString name, void* callback);
|
||||
void* getOpenGlDrawCallback(QString name);
|
||||
void* getOpenGlUserData(QString name);
|
||||
void setOpenGlContext(QString name);
|
||||
void updateWindow(QString name);
|
||||
double isOpenGl(QString name);
|
||||
@@ -327,6 +330,9 @@ public:
|
||||
static void addSlider2(CvWindow* w, QString name, int* value, int count, CvTrackbarCallback2 on_change, void* userdata);
|
||||
|
||||
void setOpenGlDrawCallback(CvOpenGlDrawCallback callback, void* userdata);
|
||||
void setOpenGlFreeCallback(CvOpenGlFreeCallback callback);
|
||||
CvOpenGlDrawCallback getOpenGlDrawCallback();
|
||||
void* getOpenGlUserData();
|
||||
void makeCurrentOpenGlContext();
|
||||
void updateGl();
|
||||
bool isOpenGl();
|
||||
@@ -413,6 +419,9 @@ public:
|
||||
virtual void startDisplayInfo(QString text, int delayms) = 0;
|
||||
|
||||
virtual void setOpenGlDrawCallback(CvOpenGlDrawCallback callback, void* userdata) = 0;
|
||||
virtual void setOpenGlFreeCallback(CvOpenGlFreeCallback callback) = 0;
|
||||
virtual CvOpenGlDrawCallback getOpenGlDrawCallback() = 0;
|
||||
virtual void* getOpenGlUserData() = 0;
|
||||
virtual void makeCurrentOpenGlContext() = 0;
|
||||
virtual void updateGl() = 0;
|
||||
|
||||
@@ -465,6 +474,9 @@ public:
|
||||
void startDisplayInfo(QString text, int delayms) CV_OVERRIDE;
|
||||
|
||||
void setOpenGlDrawCallback(CvOpenGlDrawCallback callback, void* userdata) CV_OVERRIDE;
|
||||
void setOpenGlFreeCallback(CvOpenGlFreeCallback callback) CV_OVERRIDE;
|
||||
CvOpenGlDrawCallback getOpenGlDrawCallback() CV_OVERRIDE;
|
||||
void* getOpenGlUserData() CV_OVERRIDE;
|
||||
void makeCurrentOpenGlContext() CV_OVERRIDE;
|
||||
void updateGl() CV_OVERRIDE;
|
||||
|
||||
@@ -487,6 +499,7 @@ private:
|
||||
QSize size;
|
||||
|
||||
CvOpenGlDrawCallback glDrawCallback;
|
||||
CvOpenGlFreeCallback glFreeCallback;
|
||||
void* glDrawData;
|
||||
};
|
||||
|
||||
@@ -514,6 +527,10 @@ public:
|
||||
void startDisplayInfo(QString text, int delayms) CV_OVERRIDE;
|
||||
|
||||
void setOpenGlDrawCallback(CvOpenGlDrawCallback callback, void* userdata) CV_OVERRIDE;
|
||||
void setOpenGlFreeCallback(CvOpenGlFreeCallback callback) CV_OVERRIDE;
|
||||
CvOpenGlDrawCallback getOpenGlDrawCallback() CV_OVERRIDE;
|
||||
void* getOpenGlUserData() CV_OVERRIDE;
|
||||
|
||||
void makeCurrentOpenGlContext() CV_OVERRIDE;
|
||||
void updateGl() CV_OVERRIDE;
|
||||
|
||||
|
||||
@@ -557,7 +557,7 @@ struct CvWindow : CvUIBase
|
||||
last_key(0), flags(0), status(0),
|
||||
on_mouse(NULL), on_mouse_param(NULL)
|
||||
#ifdef HAVE_OPENGL
|
||||
,useGl(false), glDrawCallback(NULL), glDrawData(NULL), glArea(NULL)
|
||||
,useGl(false), glDrawCallback(NULL), glFreeCallback(NULL), glDrawData(NULL), glArea(NULL)
|
||||
#endif
|
||||
{
|
||||
CV_LOG_INFO(NULL, "OpenCV/UI: creating GTK window: " << window_name);
|
||||
@@ -583,6 +583,7 @@ struct CvWindow : CvUIBase
|
||||
bool useGl;
|
||||
|
||||
CvOpenGlDrawCallback glDrawCallback;
|
||||
CvOpenGlFreeCallback glFreeCallback;
|
||||
void* glDrawData;
|
||||
GtkWidget* glArea;
|
||||
#endif
|
||||
@@ -728,8 +729,10 @@ static Rect getImageRect_(const std::shared_ptr<CvWindow>& window)
|
||||
gint wx, wy;
|
||||
#ifdef HAVE_OPENGL
|
||||
if (window->useGl) {
|
||||
gtk_widget_translate_coordinates(window->widget, gtk_widget_get_toplevel(window->widget), 0, 0, &wx, &wy);
|
||||
return Rect(wx, wy, gtk_widget_get_allocated_width(window->widget), gtk_widget_get_allocated_height(window->widget));
|
||||
// OpenGL windows render into the GtkGLArea, not window->widget
|
||||
GtkWidget* surf = window->glArea ? window->glArea : window->widget;
|
||||
gtk_widget_translate_coordinates(surf, gtk_widget_get_toplevel(surf), 0, 0, &wx, &wy);
|
||||
return Rect(wx, wy, gtk_widget_get_allocated_width(surf), gtk_widget_get_allocated_height(surf));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -846,8 +849,14 @@ double cvGetRatioWindow_GTK(const char* name)
|
||||
|
||||
static double getRatioWindow_(const std::shared_ptr<CvWindow>& window)
|
||||
{
|
||||
#ifdef HAVE_OPENGL
|
||||
// OpenGL windows render into the GtkGLArea, not window->widget.
|
||||
GtkWidget* surf = (window->useGl && window->glArea) ? window->glArea : window->widget;
|
||||
#else
|
||||
GtkWidget* surf = window->widget;
|
||||
#endif
|
||||
double result = static_cast<double>(
|
||||
gtk_widget_get_allocated_width(window->widget)) / gtk_widget_get_allocated_height(window->widget);
|
||||
gtk_widget_get_allocated_width(surf)) / gtk_widget_get_allocated_height(surf);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1085,6 +1094,7 @@ static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags
|
||||
createGlContext(window);
|
||||
|
||||
window->glDrawCallback = 0;
|
||||
window->glFreeCallback = 0;
|
||||
window->glDrawData = 0;
|
||||
#endif
|
||||
|
||||
@@ -1118,6 +1128,22 @@ static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags
|
||||
gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK) ;
|
||||
#endif //GTK_VERSION3_4
|
||||
|
||||
#if defined(HAVE_OPENGL) && defined(GTK_VERSION3)
|
||||
// For OpenGL windows the GtkGLArea is the visible widget; deliver mouse events from it.
|
||||
if ((flags & cv::WINDOW_OPENGL) && window->glArea)
|
||||
{
|
||||
g_signal_connect( window->glArea, "button-press-event", G_CALLBACK(icvOnMouse), window );
|
||||
g_signal_connect( window->glArea, "button-release-event", G_CALLBACK(icvOnMouse), window );
|
||||
g_signal_connect( window->glArea, "motion-notify-event", G_CALLBACK(icvOnMouse), window );
|
||||
g_signal_connect( window->glArea, "scroll-event", G_CALLBACK(icvOnMouse), window );
|
||||
#if defined(GTK_VERSION3_4)
|
||||
gtk_widget_add_events( window->glArea, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK );
|
||||
#else
|
||||
gtk_widget_add_events( window->glArea, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK );
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
gtk_widget_show( window->frame );
|
||||
gtk_window_set_title(GTK_WINDOW(window->frame), name.c_str());
|
||||
|
||||
@@ -1226,6 +1252,54 @@ void setOpenGLDrawCallbackImpl(const char* name, CvOpenGlDrawCallback callback,
|
||||
window->glDrawData = userdata;
|
||||
}
|
||||
|
||||
void setOpenGLFreeCallbackImpl(const char* name, CvOpenGlFreeCallback callback)
|
||||
{
|
||||
CV_Assert(name && "NULL name string");
|
||||
|
||||
CV_LOCK_MUTEX();
|
||||
|
||||
auto window = icvFindWindowByName(name);
|
||||
if( !window )
|
||||
return;
|
||||
|
||||
if (!window->useGl)
|
||||
CV_Error( cv::Error::OpenGlNotSupported, "Window was created without OpenGL context" );
|
||||
|
||||
window->glFreeCallback = callback;
|
||||
}
|
||||
|
||||
CvOpenGlDrawCallback getOpenGLDrawCallbackImpl(const char* name)
|
||||
{
|
||||
CV_Assert(name && "NULL name string");
|
||||
|
||||
CV_LOCK_MUTEX();
|
||||
|
||||
auto window = icvFindWindowByName(name);
|
||||
if (!window)
|
||||
return NULL;
|
||||
|
||||
if (!window->useGl)
|
||||
return NULL;
|
||||
|
||||
return window->glDrawCallback;
|
||||
}
|
||||
|
||||
void* getOpenGLUserDataImpl(const char* name)
|
||||
{
|
||||
CV_Assert(name && "NULL name string");
|
||||
|
||||
CV_LOCK_MUTEX();
|
||||
|
||||
auto window = icvFindWindowByName(name);
|
||||
if( !window )
|
||||
return NULL;
|
||||
|
||||
if (!window->useGl)
|
||||
return NULL;
|
||||
|
||||
return window->glDrawData;
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
|
||||
@@ -1239,6 +1313,12 @@ CvWindow::~CvWindow()
|
||||
inline void CvWindow::destroy()
|
||||
{
|
||||
CV_LOG_INFO(NULL, "OpenCV/UI: destroying GTK window: " << name);
|
||||
#ifdef HAVE_OPENGL
|
||||
if (glFreeCallback && glDrawData) {
|
||||
glFreeCallback(glDrawData);
|
||||
glDrawData = 0;
|
||||
}
|
||||
#endif // HAVE_OPENGL
|
||||
gtk_widget_destroy(frame);
|
||||
frame = nullptr;
|
||||
}
|
||||
@@ -1837,16 +1917,21 @@ static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_da
|
||||
// TODO move this logic to CvImageWidget
|
||||
// TODO add try-catch wrappers into all callbacks
|
||||
CvWindow* window = (CvWindow*)user_data;
|
||||
bool is_gl_area = false;
|
||||
#if defined(HAVE_OPENGL) && defined(GTK_VERSION3)
|
||||
is_gl_area = (window && window->glArea && widget == window->glArea);
|
||||
#endif
|
||||
if (!window || !widget ||
|
||||
window->signature != CV_WINDOW_MAGIC_VAL ||
|
||||
window->widget != widget ||
|
||||
(window->widget != widget && !is_gl_area) ||
|
||||
!window->on_mouse)
|
||||
return FALSE;
|
||||
|
||||
Point2f pt32f = {-1., -1.};
|
||||
Point pt = {-1,-1};
|
||||
int cv_event = -1, state = 0, flags = 0;
|
||||
CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
|
||||
// for OpenGL windows the image state lives on window->widget
|
||||
CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
|
||||
|
||||
if( event->type == GDK_MOTION_NOTIFY )
|
||||
{
|
||||
|
||||
@@ -229,6 +229,7 @@ struct CvWindow : public std::enable_shared_from_this<CvWindow>
|
||||
HGLRC hGLRC = 0;
|
||||
|
||||
CvOpenGlDrawCallback glDrawCallback = nullptr;
|
||||
CvOpenGlFreeCallback glFreeCallback = nullptr;
|
||||
void* glDrawData = nullptr;
|
||||
#endif
|
||||
};
|
||||
@@ -1084,6 +1085,7 @@ static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags
|
||||
}
|
||||
|
||||
window->glDrawCallback = 0;
|
||||
window->glFreeCallback = 0;
|
||||
window->glDrawData = 0;
|
||||
#endif
|
||||
|
||||
@@ -1157,6 +1159,51 @@ void setOpenGLDrawCallbackImpl(const char* name, CvOpenGlDrawCallback callback,
|
||||
window->glDrawData = userdata;
|
||||
}
|
||||
|
||||
void setOpenGLFreeCallbackImpl(const char* name, CvOpenGlFreeCallback callback)
|
||||
{
|
||||
AutoLock lock(getWindowMutex());
|
||||
|
||||
if (!name)
|
||||
CV_Error(Error::StsNullPtr, "NULL name string");
|
||||
|
||||
auto window = icvFindWindowByName(name);
|
||||
if (!window)
|
||||
CV_Error_(Error::StsNullPtr, ("NULL window: '%s'", name));
|
||||
|
||||
if (!window->useGl)
|
||||
CV_Error(Error::OpenGlNotSupported, "Window was created without OpenGL context");
|
||||
|
||||
window->glFreeCallback = callback;
|
||||
}
|
||||
|
||||
CvOpenGlDrawCallback getOpenGLDrawCallbackImpl(const char* name)
|
||||
{
|
||||
AutoLock lock(getWindowMutex());
|
||||
|
||||
if (!name)
|
||||
CV_Error(Error::StsNullPtr, "NULL name string");
|
||||
|
||||
auto window = icvFindWindowByName(name);
|
||||
if (!window || !window->useGl)
|
||||
return NULL;
|
||||
|
||||
return window->glDrawCallback;
|
||||
}
|
||||
|
||||
void* getOpenGLUserDataImpl(const char* name)
|
||||
{
|
||||
AutoLock lock(getWindowMutex());
|
||||
|
||||
if (!name)
|
||||
CV_Error(Error::StsNullPtr, "NULL name string");
|
||||
|
||||
auto window = icvFindWindowByName(name);
|
||||
if (!window || !window->useGl)
|
||||
return NULL;
|
||||
|
||||
return window->glDrawData;
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENGL
|
||||
|
||||
static void icvRemoveWindow(const std::shared_ptr<CvWindow>& window_)
|
||||
@@ -1179,6 +1226,8 @@ static void icvRemoveWindow(const std::shared_ptr<CvWindow>& window_)
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
if (window.glFreeCallback && window.glDrawData)
|
||||
window.glFreeCallback(window.glDrawData);
|
||||
if (window.useGl)
|
||||
releaseGlContext(window);
|
||||
#endif
|
||||
@@ -1800,7 +1849,11 @@ static LRESULT CALLBACK HighGUIProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
|
||||
pt.x = GET_X_LPARAM(lParam);
|
||||
pt.y = GET_Y_LPARAM(lParam);
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
if ((window.flags & cv::WINDOW_AUTOSIZE) || window.glFreeCallback)
|
||||
#else
|
||||
if (window.flags & cv::WINDOW_AUTOSIZE)
|
||||
#endif
|
||||
{
|
||||
// As user can't change window size, do not scale window coordinates. Underlying windowing system
|
||||
// may prevent full window from being displayed and in this case coordinates should not be scaled.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
// Copyright (C) 2026, BigVision LLC, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include <opencv2/core/opengl.hpp>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
// ogl wrappers need a current GL context; skip when none is available (headless CI).
|
||||
static void ensureGlContext(const String& w)
|
||||
{
|
||||
try
|
||||
{
|
||||
namedWindow(w, WINDOW_OPENGL);
|
||||
if (getWindowProperty(w, WND_PROP_OPENGL) <= 0)
|
||||
throw cvtest::SkipTestException("window created without an OpenGL context");
|
||||
setOpenGlContext(w);
|
||||
}
|
||||
catch (const cv::Exception&)
|
||||
{
|
||||
throw cvtest::SkipTestException("OpenGL/display not available");
|
||||
}
|
||||
}
|
||||
|
||||
// A grown sole-owner buffer must keep the same GL id (in-place resize).
|
||||
TEST(OpenGL_Buffer, resize_keeps_id)
|
||||
{
|
||||
const String w = "ogl_buf_test";
|
||||
ensureGlContext(w);
|
||||
|
||||
ogl::Buffer buf;
|
||||
buf.create(4, 3, CV_32F, ogl::Buffer::ARRAY_BUFFER, false);
|
||||
unsigned int id1 = buf.bufId();
|
||||
EXPECT_NE(id1, 0u);
|
||||
|
||||
buf.create(16, 3, CV_32F, ogl::Buffer::ARRAY_BUFFER, false); // grow -> resize in place
|
||||
EXPECT_EQ(buf.bufId(), id1);
|
||||
|
||||
destroyWindow(w);
|
||||
}
|
||||
|
||||
// Shader compile + type().
|
||||
TEST(OpenGL_Shader, compile_and_type)
|
||||
{
|
||||
const String w = "ogl_shader_test";
|
||||
ensureGlContext(w);
|
||||
|
||||
const char* vs = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }";
|
||||
ogl::Shader sh(vs, ogl::Shader::VERTEX, false);
|
||||
EXPECT_NE(sh.shaderId(), 0u);
|
||||
EXPECT_EQ(sh.type(), ogl::Shader::VERTEX);
|
||||
|
||||
destroyWindow(w);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -6,9 +6,6 @@ if(UNIX)
|
||||
find_package(X11 QUIET)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(EPOXY QUIET epoxy)
|
||||
|
||||
SET(OPENCV_OPENGL_SAMPLES_REQUIRED_DEPS
|
||||
opencv_core
|
||||
opencv_imgproc
|
||||
@@ -26,9 +23,6 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND)
|
||||
if(NOT X11_FOUND)
|
||||
ocv_list_filterout(all_samples "opengl_interop")
|
||||
endif()
|
||||
if(NOT EPOXY_FOUND)
|
||||
ocv_list_filterout(all_samples "opengl3_2")
|
||||
endif()
|
||||
foreach(sample_filename ${all_samples})
|
||||
ocv_define_sample(tgt ${sample_filename} opengl)
|
||||
ocv_target_link_libraries(${tgt} PRIVATE "${OPENGL_LIBRARIES}" "${OPENCV_OPENGL_SAMPLES_REQUIRED_DEPS}")
|
||||
@@ -36,10 +30,6 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND)
|
||||
ocv_target_link_libraries(${tgt} PRIVATE ${X11_LIBRARIES})
|
||||
ocv_target_include_directories(${tgt} ${X11_INCLUDE_DIR})
|
||||
endif()
|
||||
if(sample_filename STREQUAL "opengl3_2.cpp")
|
||||
ocv_target_link_libraries(${tgt} PRIVATE ${EPOXY_LIBRARIES})
|
||||
ocv_target_include_directories(${tgt} PRIVATE ${EPOXY_INCLUDE_DIRS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,54 +1,36 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#define NOMINMAX 1
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/opengl.hpp"
|
||||
#include "opencv2/core/cuda.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
const int win_width = 800;
|
||||
const int win_height = 640;
|
||||
|
||||
// All GL objects live for the lifetime of the window's GL context.
|
||||
// autoRelease is left at its default (false) so they are reclaimed with the
|
||||
// context on shutdown; calling glDelete* from a destructor after the context
|
||||
// is gone would raise a GL error (see cv::ogl wrapper docs).
|
||||
struct DrawData
|
||||
{
|
||||
GLuint vao, vbo, program, textureID;
|
||||
ogl::Program program;
|
||||
ogl::Buffer vbo; // referenced by the VertexArray; must outlive it
|
||||
ogl::VertexArray vao;
|
||||
ogl::Texture2D tex;
|
||||
int transformLoc;
|
||||
};
|
||||
|
||||
static cv::Mat rot(float angle)
|
||||
static cv::Matx44f rot(float angle)
|
||||
{
|
||||
cv::Mat R_y = (cv::Mat_<float>(4,4) <<
|
||||
// Row-major; ogl::Program::setUniformMat4x4 uploads with transpose enabled.
|
||||
return cv::Matx44f(
|
||||
cos(angle), 0, sin(angle), 0,
|
||||
0, 1, 0, 0,
|
||||
-sin(angle), 0, cos(angle), 0,
|
||||
0, 0, 0, 1);
|
||||
|
||||
return R_y;
|
||||
}
|
||||
|
||||
static GLuint create_shader(const char* source, GLenum type) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, NULL);
|
||||
glCompileShader(shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void draw(void* userdata) {
|
||||
@@ -56,17 +38,17 @@ static void draw(void* userdata) {
|
||||
static float angle = 0.0f;
|
||||
angle += 1.f;
|
||||
|
||||
cv::Mat trans = rot(CV_PI * angle / 360.f);
|
||||
cv::Matx44f trans = rot(CV_PI * angle / 360.f);
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
ogl::clearColor(Scalar::all(0));
|
||||
ogl::clearDepth(1.0f);
|
||||
|
||||
glUseProgram(data->program);
|
||||
glUniformMatrix4fv(glGetUniformLocation(data->program, "transform"), 1, GL_FALSE, trans.ptr<float>());
|
||||
glBindTexture(GL_TEXTURE_2D, data->textureID);
|
||||
glBindVertexArray(data->vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
data->program.bind();
|
||||
ogl::Program::setUniformMat4x4(data->transformLoc, trans);
|
||||
data->tex.bind();
|
||||
data->vao.bind();
|
||||
ogl::drawArrays(0, 4, ogl::TRIANGLE_STRIP);
|
||||
ogl::VertexArray::unbind();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
@@ -93,7 +75,8 @@ int main(int argc, char* argv[])
|
||||
|
||||
DrawData data;
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
ogl::enable(ogl::DEPTH_TEST);
|
||||
|
||||
const char *vertex_shader_source =
|
||||
"#version 330 core\n"
|
||||
"layout (location = 0) in vec3 position;\n"
|
||||
@@ -112,15 +95,14 @@ int main(int argc, char* argv[])
|
||||
"void main() {\n"
|
||||
" color = texture(ourTexture, TexCoord);\n"
|
||||
"}\n";
|
||||
data.program = glCreateProgram();
|
||||
GLuint vertex_shader = create_shader(vertex_shader_source, GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader = create_shader(fragment_shader_source, GL_FRAGMENT_SHADER);
|
||||
glAttachShader(data.program, vertex_shader);
|
||||
glAttachShader(data.program, fragment_shader);
|
||||
glLinkProgram(data.program);
|
||||
glUseProgram(data.program);
|
||||
|
||||
GLfloat vertices[] = {
|
||||
// Compile shaders and link the program via the ogl wrappers.
|
||||
ogl::Shader vertex_shader(vertex_shader_source, ogl::Shader::VERTEX);
|
||||
ogl::Shader fragment_shader(fragment_shader_source, ogl::Shader::FRAGMENT);
|
||||
data.program = ogl::Program(vertex_shader, fragment_shader);
|
||||
data.transformLoc = data.program.getUniformLocation("transform");
|
||||
|
||||
const float vertices[] = {
|
||||
// Positions // Texture Coords
|
||||
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top Right
|
||||
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // Bottom Right
|
||||
@@ -128,30 +110,36 @@ int main(int argc, char* argv[])
|
||||
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f // Bottom Left
|
||||
};
|
||||
|
||||
glGenVertexArrays(1, &data.vao);
|
||||
glGenBuffers(1, &data.vbo);
|
||||
glBindVertexArray(data.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, data.vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
// Upload the interleaved vertex data into a GL buffer.
|
||||
Mat vertexMat(4, 5, CV_32F, (void*)vertices);
|
||||
data.vbo = ogl::Buffer(vertexMat, ogl::Buffer::ARRAY_BUFFER);
|
||||
|
||||
// Position attribute
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
// Texture Coord attribute
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
|
||||
glEnableVertexAttribArray(1);
|
||||
glBindVertexArray(0); // Unbind VAO
|
||||
// Position attribute (location 0): 3 floats, no offset.
|
||||
ogl::Attribute posAttr;
|
||||
posAttr.buffer_ = data.vbo;
|
||||
posAttr.stride_ = 5 * sizeof(float);
|
||||
posAttr.offset_ = 0;
|
||||
posAttr.size_ = 3;
|
||||
posAttr.type_ = ogl::Attribute::FLOAT;
|
||||
posAttr.integer_ = false;
|
||||
posAttr.normalized_ = false;
|
||||
posAttr.shader_loc_ = 0;
|
||||
|
||||
// Texture coord attribute (location 1): 2 floats, offset past the 3 position floats.
|
||||
ogl::Attribute texAttr;
|
||||
texAttr.buffer_ = data.vbo;
|
||||
texAttr.stride_ = 5 * sizeof(float);
|
||||
texAttr.offset_ = 3 * sizeof(float);
|
||||
texAttr.size_ = 2;
|
||||
texAttr.type_ = ogl::Attribute::FLOAT;
|
||||
texAttr.integer_ = false;
|
||||
texAttr.normalized_ = false;
|
||||
texAttr.shader_loc_ = 1;
|
||||
|
||||
// Image to texture
|
||||
glGenTextures(1, &data.textureID);
|
||||
glBindTexture(GL_TEXTURE_2D, data.textureID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.cols, img.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, img.data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
data.vao = ogl::VertexArray({posAttr, texAttr});
|
||||
|
||||
// Image to texture.
|
||||
data.tex = ogl::Texture2D(img);
|
||||
|
||||
setOpenGlDrawCallback("OpenGL", draw, &data);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user