1
0
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:
kirtijindal14
2026-07-26 17:33:35 +05:30
committed by GitHub
parent 6e70e5d2a9
commit 4ccee4a65f
12 changed files with 1397 additions and 87 deletions
@@ -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
View File
@@ -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.
+5
View File
@@ -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);
+41
View File
@@ -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 =========================
+130
View File
@@ -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();
+17
View File
@@ -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;
+91 -6
View File
@@ -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 )
{
+53
View File
@@ -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.
+61
View File
@@ -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
-10
View File
@@ -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()
+57 -69
View File
@@ -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);