opencv/modules/core/src/opengl_interop.cpp
2012-08-28 13:45:35 +04:00

1590 lines
39 KiB
C++

/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include <iostream>
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/core/gpumat.hpp"
#ifdef HAVE_OPENGL
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif
#ifdef HAVE_CUDA
#include <cuda_runtime.h>
#include <cuda_gl_interop.h>
#endif
#endif
using namespace std;
using namespace cv;
using namespace cv::gpu;
#ifndef HAVE_OPENGL
#define throw_nogl CV_Error(CV_OpenGlNotSupported, "The library is compiled without OpenGL support")
#define throw_nocuda CV_Error(CV_GpuNotSupported, "The library is compiled without CUDA support")
#else
#define throw_nogl CV_Error(CV_OpenGlNotSupported, "OpenGL context doesn't exist")
#ifndef HAVE_CUDA
#define throw_nocuda CV_Error(CV_GpuNotSupported, "The library is compiled without CUDA support")
#else
#if defined(__GNUC__)
#define cudaSafeCall(expr) ___cudaSafeCall(expr, __FILE__, __LINE__, __func__)
#else /* defined(__CUDACC__) || defined(__MSVC__) */
#define cudaSafeCall(expr) ___cudaSafeCall(expr, __FILE__, __LINE__)
#endif
namespace
{
inline void ___cudaSafeCall(cudaError_t err, const char *file, const int line, const char *func = "")
{
if (cudaSuccess != err)
cv::gpu::error(cudaGetErrorString(err), file, line, func);
}
}
#endif // HAVE_CUDA
#endif
namespace
{
class EmptyGlFuncTab : public CvOpenGlFuncTab
{
public:
void genBuffers(int, unsigned int*) const { throw_nogl; }
void deleteBuffers(int, const unsigned int*) const { throw_nogl; }
void bufferData(unsigned int, ptrdiff_t, const void*, unsigned int) const { throw_nogl; }
void bufferSubData(unsigned int, ptrdiff_t, ptrdiff_t, const void*) const { throw_nogl; }
void bindBuffer(unsigned int, unsigned int) const { throw_nogl; }
void* mapBuffer(unsigned int, unsigned int) const { throw_nogl; return 0; }
void unmapBuffer(unsigned int) const { throw_nogl; }
void generateBitmapFont(const std::string&, int, int, bool, bool, int, int, int) const { throw_nogl; }
bool isGlContextInitialized() const { return false; }
};
const CvOpenGlFuncTab* g_glFuncTab = 0;
#if defined HAVE_CUDA || defined HAVE_OPENGL
const CvOpenGlFuncTab* glFuncTab()
{
static EmptyGlFuncTab empty;
return g_glFuncTab ? g_glFuncTab : &empty;
}
#endif
}
CvOpenGlFuncTab::~CvOpenGlFuncTab()
{
if (g_glFuncTab == this)
g_glFuncTab = 0;
}
void icvSetOpenGlFuncTab(const CvOpenGlFuncTab* tab)
{
g_glFuncTab = tab;
}
#ifdef HAVE_OPENGL
#ifndef GL_DYNAMIC_DRAW
#define GL_DYNAMIC_DRAW 0x88E8
#endif
#ifndef GL_READ_WRITE
#define GL_READ_WRITE 0x88BA
#endif
#ifndef GL_BGR
#define GL_BGR 0x80E0
#endif
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
namespace
{
const GLenum gl_types[] = {GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, GL_INT, GL_FLOAT, GL_DOUBLE};
#ifdef HAVE_CUDA
bool g_isCudaGlDeviceInitialized = false;
#endif
}
#endif // HAVE_OPENGL
void cv::gpu::setGlDevice(int device)
{
#ifndef HAVE_CUDA
(void)device;
throw_nocuda;
#else
#ifndef HAVE_OPENGL
(void)device;
throw_nogl;
#else
if (!glFuncTab()->isGlContextInitialized())
throw_nogl;
cudaSafeCall( cudaGLSetGLDevice(device) );
g_isCudaGlDeviceInitialized = true;
#endif
#endif
}
////////////////////////////////////////////////////////////////////////
// CudaGlInterop
#if defined HAVE_CUDA && defined HAVE_OPENGL
namespace
{
class CudaGlInterop
{
public:
CudaGlInterop();
~CudaGlInterop();
void registerBuffer(unsigned int buffer);
void copyFrom(const GpuMat& mat, cudaStream_t stream = 0);
GpuMat map(int rows, int cols, int type, cudaStream_t stream = 0);
void unmap(cudaStream_t stream = 0);
private:
cudaGraphicsResource_t resource_;
};
inline CudaGlInterop::CudaGlInterop() : resource_(0)
{
}
CudaGlInterop::~CudaGlInterop()
{
if (resource_)
{
cudaGraphicsUnregisterResource(resource_);
resource_ = 0;
}
}
void CudaGlInterop::registerBuffer(unsigned int buffer)
{
if (!g_isCudaGlDeviceInitialized)
cvError(CV_GpuApiCallError, "registerBuffer", "cuda GL device wasn't initialized, call setGlDevice", __FILE__, __LINE__);
cudaGraphicsResource_t resource;
cudaSafeCall( cudaGraphicsGLRegisterBuffer(&resource, buffer, cudaGraphicsMapFlagsNone) );
resource_ = resource;
}
void CudaGlInterop::copyFrom(const GpuMat& mat, cudaStream_t stream)
{
CV_Assert(resource_ != 0);
cudaSafeCall( cudaGraphicsMapResources(1, &resource_, stream) );
void* dst_ptr;
size_t num_bytes;
cudaSafeCall( cudaGraphicsResourceGetMappedPointer(&dst_ptr, &num_bytes, resource_) );
const void* src_ptr = mat.ptr();
size_t widthBytes = mat.cols * mat.elemSize();
CV_Assert(widthBytes * mat.rows <= num_bytes);
if (stream == 0)
cudaSafeCall( cudaMemcpy2D(dst_ptr, widthBytes, src_ptr, mat.step, widthBytes, mat.rows, cudaMemcpyDeviceToDevice) );
else
cudaSafeCall( cudaMemcpy2DAsync(dst_ptr, widthBytes, src_ptr, mat.step, widthBytes, mat.rows, cudaMemcpyDeviceToDevice, stream) );
cudaGraphicsUnmapResources(1, &resource_, stream);
}
GpuMat CudaGlInterop::map(int rows, int cols, int type, cudaStream_t stream)
{
CV_Assert(resource_ != 0);
cudaSafeCall( cudaGraphicsMapResources(1, &resource_, stream) );
void* ptr;
size_t num_bytes;
cudaSafeCall( cudaGraphicsResourceGetMappedPointer(&ptr, &num_bytes, resource_) );
CV_Assert( static_cast<size_t>(cols) * CV_ELEM_SIZE(type) * rows <= num_bytes );
return GpuMat(rows, cols, type, ptr);
}
inline void CudaGlInterop::unmap(cudaStream_t stream)
{
cudaGraphicsUnmapResources(1, &resource_, stream);
}
}
#endif // HAVE_CUDA && HAVE_OPENGL
////////////////////////////////////////////////////////////////////////
// GlBuffer
#ifndef HAVE_OPENGL
class cv::GlBuffer::Impl
{
};
#else
class cv::GlBuffer::Impl
{
public:
static const Ptr<Impl>& empty();
Impl(int rows, int cols, int type, unsigned int target);
Impl(const Mat& m, unsigned int target);
~Impl();
void copyFrom(const Mat& m, unsigned int target);
#ifdef HAVE_CUDA
void copyFrom(const GpuMat& mat, cudaStream_t stream = 0);
#endif
void bind(unsigned int target) const;
void unbind(unsigned int target) const;
Mat mapHost(int rows, int cols, int type, unsigned int target);
void unmapHost(unsigned int target);
#ifdef HAVE_CUDA
GpuMat mapDevice(int rows, int cols, int type, cudaStream_t stream = 0);
void unmapDevice(cudaStream_t stream = 0);
#endif
private:
Impl();
unsigned int buffer_;
#ifdef HAVE_CUDA
CudaGlInterop cudaGlInterop_;
#endif
};
inline const Ptr<cv::GlBuffer::Impl>& cv::GlBuffer::Impl::empty()
{
static Ptr<Impl> p(new Impl);
return p;
}
inline cv::GlBuffer::Impl::Impl() : buffer_(0)
{
}
cv::GlBuffer::Impl::Impl(int rows, int cols, int type, unsigned int target) : buffer_(0)
{
if (!glFuncTab()->isGlContextInitialized())
throw_nogl;
CV_DbgAssert(rows > 0 && cols > 0);
CV_DbgAssert(CV_MAT_DEPTH(type) >= 0 && CV_MAT_DEPTH(type) <= CV_64F);
glFuncTab()->genBuffers(1, &buffer_);
CV_CheckGlError();
CV_Assert(buffer_ != 0);
size_t size = rows * cols * CV_ELEM_SIZE(type);
glFuncTab()->bindBuffer(target, buffer_);
CV_CheckGlError();
glFuncTab()->bufferData(target, size, 0, GL_DYNAMIC_DRAW);
CV_CheckGlError();
glFuncTab()->bindBuffer(target, 0);
#ifdef HAVE_CUDA
if (g_isCudaGlDeviceInitialized)
cudaGlInterop_.registerBuffer(buffer_);
#endif
}
cv::GlBuffer::Impl::Impl(const Mat& m, unsigned int target) : buffer_(0)
{
if (!glFuncTab()->isGlContextInitialized())
throw_nogl;
CV_DbgAssert(m.rows > 0 && m.cols > 0);
CV_DbgAssert(m.depth() >= 0 && m.depth() <= CV_64F);
CV_Assert(m.isContinuous());
glFuncTab()->genBuffers(1, &buffer_);
CV_CheckGlError();
CV_Assert(buffer_ != 0);
size_t size = m.rows * m.cols * m.elemSize();
glFuncTab()->bindBuffer(target, buffer_);
CV_CheckGlError();
glFuncTab()->bufferData(target, size, m.data, GL_DYNAMIC_DRAW);
CV_CheckGlError();
glFuncTab()->bindBuffer(target, 0);
#ifdef HAVE_CUDA
if (g_isCudaGlDeviceInitialized)
cudaGlInterop_.registerBuffer(buffer_);
#endif
}
cv::GlBuffer::Impl::~Impl()
{
try
{
if (buffer_)
glFuncTab()->deleteBuffers(1, &buffer_);
}
#ifdef _DEBUG
catch(const exception& e)
{
cerr << e.what() << endl;
}
#endif
catch(...)
{
}
}
void cv::GlBuffer::Impl::copyFrom(const Mat& m, unsigned int target)
{
CV_Assert(buffer_ != 0);
CV_Assert(m.isContinuous());
bind(target);
size_t size = m.rows * m.cols * m.elemSize();
glFuncTab()->bufferSubData(target, 0, size, m.data);
CV_CheckGlError();
unbind(target);
}
#ifdef HAVE_CUDA
void cv::GlBuffer::Impl::copyFrom(const GpuMat& mat, cudaStream_t stream)
{
if (!g_isCudaGlDeviceInitialized)
cvError(CV_GpuApiCallError, "copyFrom", "cuda GL device wasn't initialized, call setGlDevice", __FILE__, __LINE__);
CV_Assert(buffer_ != 0);
cudaGlInterop_.copyFrom(mat, stream);
}
#endif // HAVE_CUDA
inline void cv::GlBuffer::Impl::bind(unsigned int target) const
{
CV_Assert(buffer_ != 0);
glFuncTab()->bindBuffer(target, buffer_);
CV_CheckGlError();
}
inline void cv::GlBuffer::Impl::unbind(unsigned int target) const
{
glFuncTab()->bindBuffer(target, 0);
}
inline Mat cv::GlBuffer::Impl::mapHost(int rows, int cols, int type, unsigned int target)
{
void* ptr = glFuncTab()->mapBuffer(target, GL_READ_WRITE);
CV_CheckGlError();
return Mat(rows, cols, type, ptr);
}
inline void cv::GlBuffer::Impl::unmapHost(unsigned int target)
{
glFuncTab()->unmapBuffer(target);
}
#ifdef HAVE_CUDA
inline GpuMat cv::GlBuffer::Impl::mapDevice(int rows, int cols, int type, cudaStream_t stream)
{
if (!g_isCudaGlDeviceInitialized)
cvError(CV_GpuApiCallError, "copyFrom", "cuda GL device wasn't initialized, call setGlDevice", __FILE__, __LINE__);
CV_Assert(buffer_ != 0);
return cudaGlInterop_.map(rows, cols, type, stream);
}
inline void cv::GlBuffer::Impl::unmapDevice(cudaStream_t stream)
{
if (!g_isCudaGlDeviceInitialized)
cvError(CV_GpuApiCallError, "copyFrom", "cuda GL device wasn't initialized, call setGlDevice", __FILE__, __LINE__);
cudaGlInterop_.unmap(stream);
}
#endif // HAVE_CUDA
#endif // HAVE_OPENGL
cv::GlBuffer::GlBuffer(Usage _usage) : rows_(0), cols_(0), type_(0), usage_(_usage)
{
#ifndef HAVE_OPENGL
(void)_usage;
throw_nogl;
#else
impl_ = Impl::empty();
#endif
}
cv::GlBuffer::GlBuffer(int _rows, int _cols, int _type, Usage _usage) : rows_(0), cols_(0), type_(0), usage_(_usage)
{
#ifndef HAVE_OPENGL
(void)_rows;
(void)_cols;
(void)_type;
(void)_usage;
throw_nogl;
#else
impl_ = new Impl(_rows, _cols, _type, _usage);
rows_ = _rows;
cols_ = _cols;
type_ = _type;
#endif
}
cv::GlBuffer::GlBuffer(Size _size, int _type, Usage _usage) : rows_(0), cols_(0), type_(0), usage_(_usage)
{
#ifndef HAVE_OPENGL
(void)_size;
(void)_type;
(void)_usage;
throw_nogl;
#else
impl_ = new Impl(_size.height, _size.width, _type, _usage);
rows_ = _size.height;
cols_ = _size.width;
type_ = _type;
#endif
}
cv::GlBuffer::GlBuffer(InputArray mat_, Usage _usage) : rows_(0), cols_(0), type_(0), usage_(_usage)
{
#ifndef HAVE_OPENGL
(void)mat_;
(void)_usage;
throw_nogl;
#else
int kind = mat_.kind();
Size _size = mat_.size();
int _type = mat_.type();
if (kind == _InputArray::GPU_MAT)
{
#ifndef HAVE_CUDA
throw_nocuda;
#else
GpuMat d_mat = mat_.getGpuMat();
impl_ = new Impl(d_mat.rows, d_mat.cols, d_mat.type(), _usage);
impl_->copyFrom(d_mat);
#endif
}
else
{
Mat mat = mat_.getMat();
impl_ = new Impl(mat, _usage);
}
rows_ = _size.height;
cols_ = _size.width;
type_ = _type;
#endif
}
void cv::GlBuffer::create(int _rows, int _cols, int _type, Usage _usage)
{
#ifndef HAVE_OPENGL
(void)_rows;
(void)_cols;
(void)_type;
(void)_usage;
throw_nogl;
#else
if (rows_ != _rows || cols_ != _cols || type_ != _type || usage_ != _usage)
{
impl_ = new Impl(_rows, _cols, _type, _usage);
rows_ = _rows;
cols_ = _cols;
type_ = _type;
usage_ = _usage;
}
#endif
}
void cv::GlBuffer::release()
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_ = Impl::empty();
#endif
}
void cv::GlBuffer::copyFrom(InputArray mat_)
{
#ifndef HAVE_OPENGL
(void)mat_;
throw_nogl;
#else
int kind = mat_.kind();
Size _size = mat_.size();
int _type = mat_.type();
create(_size, _type);
switch (kind)
{
case _InputArray::OPENGL_BUFFER:
{
GlBuffer buf = mat_.getGlBuffer();
*this = buf;
break;
}
case _InputArray::GPU_MAT:
{
#ifndef HAVE_CUDA
throw_nocuda;
#else
GpuMat d_mat = mat_.getGpuMat();
impl_->copyFrom(d_mat);
#endif
break;
}
default:
{
Mat mat = mat_.getMat();
impl_->copyFrom(mat, usage_);
}
}
#endif
}
void cv::GlBuffer::bind() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_->bind(usage_);
#endif
}
void cv::GlBuffer::unbind() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_->unbind(usage_);
#endif
}
Mat cv::GlBuffer::mapHost()
{
#ifndef HAVE_OPENGL
throw_nogl;
return Mat();
#else
return impl_->mapHost(rows_, cols_, type_, usage_);
#endif
}
void cv::GlBuffer::unmapHost()
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_->unmapHost(usage_);
#endif
}
GpuMat cv::GlBuffer::mapDevice()
{
#ifndef HAVE_OPENGL
throw_nogl;
return GpuMat();
#else
#ifndef HAVE_CUDA
throw_nocuda;
return GpuMat();
#else
return impl_->mapDevice(rows_, cols_, type_);
#endif
#endif
}
void cv::GlBuffer::unmapDevice()
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
#ifndef HAVE_CUDA
throw_nocuda;
#else
impl_->unmapDevice();
#endif
#endif
}
template <> void cv::Ptr<cv::GlBuffer::Impl>::delete_obj()
{
if (obj) delete obj;
}
//////////////////////////////////////////////////////////////////////////////////////////
// GlTexture
#ifndef HAVE_OPENGL
class cv::GlTexture::Impl
{
};
#else
class cv::GlTexture::Impl
{
public:
static const Ptr<Impl> empty();
Impl(int rows, int cols, int type);
Impl(const Mat& mat, bool bgra);
Impl(const GlBuffer& buf, bool bgra);
~Impl();
void copyFrom(const Mat& mat, bool bgra);
void copyFrom(const GlBuffer& buf, bool bgra);
void bind() const;
void unbind() const;
private:
Impl();
GLuint tex_;
};
inline const Ptr<cv::GlTexture::Impl> cv::GlTexture::Impl::empty()
{
static Ptr<Impl> p(new Impl);
return p;
}
inline cv::GlTexture::Impl::Impl() : tex_(0)
{
}
cv::GlTexture::Impl::Impl(int rows, int cols, int type) : tex_(0)
{
if (!glFuncTab()->isGlContextInitialized())
throw_nogl;
int depth = CV_MAT_DEPTH(type);
int cn = CV_MAT_CN(type);
CV_DbgAssert(rows > 0 && cols > 0);
CV_Assert(cn == 1 || cn == 3 || cn == 4);
CV_Assert(depth >= 0 && depth <= CV_32F);
glGenTextures(1, &tex_);
CV_CheckGlError();
CV_Assert(tex_ != 0);
glBindTexture(GL_TEXTURE_2D, tex_);
CV_CheckGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
CV_CheckGlError();
GLenum format = cn == 1 ? GL_LUMINANCE : cn == 3 ? GL_BGR : GL_BGRA;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
CV_CheckGlError();
glTexImage2D(GL_TEXTURE_2D, 0, cn, cols, rows, 0, format, gl_types[depth], 0);
CV_CheckGlError();
}
cv::GlTexture::Impl::Impl(const Mat& mat, bool bgra) : tex_(0)
{
if (!glFuncTab()->isGlContextInitialized())
throw_nogl;
int depth = mat.depth();
int cn = mat.channels();
CV_DbgAssert(mat.rows > 0 && mat.cols > 0);
CV_Assert(cn == 1 || cn == 3 || cn == 4);
CV_Assert(depth >= 0 && depth <= CV_32F);
CV_Assert(mat.isContinuous());
glGenTextures(1, &tex_);
CV_CheckGlError();
CV_Assert(tex_ != 0);
glBindTexture(GL_TEXTURE_2D, tex_);
CV_CheckGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
CV_CheckGlError();
GLenum format = cn == 1 ? GL_LUMINANCE : (cn == 3 ? (bgra ? GL_BGR : GL_RGB) : (bgra ? GL_BGRA : GL_RGBA));
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
CV_CheckGlError();
glTexImage2D(GL_TEXTURE_2D, 0, cn, mat.cols, mat.rows, 0, format, gl_types[depth], mat.data);
CV_CheckGlError();
}
cv::GlTexture::Impl::Impl(const GlBuffer& buf, bool bgra) : tex_(0)
{
if (!glFuncTab()->isGlContextInitialized())
throw_nogl;
int depth = buf.depth();
int cn = buf.channels();
CV_DbgAssert(buf.rows() > 0 && buf.cols() > 0);
CV_Assert(cn == 1 || cn == 3 || cn == 4);
CV_Assert(depth >= 0 && depth <= CV_32F);
CV_Assert(buf.usage() == GlBuffer::TEXTURE_BUFFER);
glGenTextures(1, &tex_);
CV_CheckGlError();
CV_Assert(tex_ != 0);
glBindTexture(GL_TEXTURE_2D, tex_);
CV_CheckGlError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
CV_CheckGlError();
GLenum format = cn == 1 ? GL_LUMINANCE : (cn == 3 ? (bgra ? GL_BGR : GL_RGB) : (bgra ? GL_BGRA : GL_RGBA));
buf.bind();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
CV_CheckGlError();
glTexImage2D(GL_TEXTURE_2D, 0, cn, buf.cols(), buf.rows(), 0, format, gl_types[depth], 0);
CV_CheckGlError();
buf.unbind();
}
inline cv::GlTexture::Impl::~Impl()
{
if (tex_)
glDeleteTextures(1, &tex_);
}
void cv::GlTexture::Impl::copyFrom(const Mat& mat, bool bgra)
{
CV_Assert(tex_ != 0);
bind();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
CV_CheckGlError();
int cn = mat.channels();
GLenum format = cn == 1 ? GL_LUMINANCE : (cn == 3 ? (bgra ? GL_BGR : GL_RGB) : (bgra ? GL_BGRA : GL_RGBA));
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mat.cols, mat.rows, format, gl_types[mat.depth()], mat.data);
CV_CheckGlError();
unbind();
}
void cv::GlTexture::Impl::copyFrom(const GlBuffer& buf, bool bgra)
{
CV_Assert(tex_ != 0);
CV_Assert(buf.usage() == GlBuffer::TEXTURE_BUFFER);
bind();
buf.bind();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
CV_CheckGlError();
int cn = buf.channels();
GLenum format = cn == 1 ? GL_LUMINANCE : (cn == 3 ? (bgra ? GL_BGR : GL_RGB) : (bgra ? GL_BGRA : GL_RGBA));
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, buf.cols(), buf.rows(), format, gl_types[buf.depth()], 0);
CV_CheckGlError();
buf.unbind();
unbind();
}
inline void cv::GlTexture::Impl::bind() const
{
CV_Assert(tex_ != 0);
glEnable(GL_TEXTURE_2D);
CV_CheckGlError();
glBindTexture(GL_TEXTURE_2D, tex_);
CV_CheckGlError();
}
inline void cv::GlTexture::Impl::unbind() const
{
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
#endif // HAVE_OPENGL
cv::GlTexture::GlTexture() : rows_(0), cols_(0), type_(0), buf_(GlBuffer::TEXTURE_BUFFER)
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_ = Impl::empty();
#endif
}
cv::GlTexture::GlTexture(int _rows, int _cols, int _type) : rows_(0), cols_(0), type_(0), buf_(GlBuffer::TEXTURE_BUFFER)
{
#ifndef HAVE_OPENGL
(void)_rows;
(void)_cols;
(void)_type;
throw_nogl;
#else
impl_ = new Impl(_rows, _cols, _type);
rows_ = _rows;
cols_ = _cols;
type_ = _type;
#endif
}
cv::GlTexture::GlTexture(Size _size, int _type) : rows_(0), cols_(0), type_(0), buf_(GlBuffer::TEXTURE_BUFFER)
{
#ifndef HAVE_OPENGL
(void)_size;
(void)_type;
throw_nogl;
#else
impl_ = new Impl(_size.height, _size.width, _type);
rows_ = _size.height;
cols_ = _size.width;
type_ = _type;
#endif
}
cv::GlTexture::GlTexture(InputArray mat_, bool bgra) : rows_(0), cols_(0), type_(0), buf_(GlBuffer::TEXTURE_BUFFER)
{
#ifndef HAVE_OPENGL
(void)mat_;
(void)bgra;
throw_nogl;
#else
int kind = mat_.kind();
Size _size = mat_.size();
int _type = mat_.type();
switch (kind)
{
case _InputArray::OPENGL_BUFFER:
{
GlBuffer buf = mat_.getGlBuffer();
impl_ = new Impl(buf, bgra);
break;
}
case _InputArray::GPU_MAT:
{
#ifndef HAVE_CUDA
throw_nocuda;
#else
GpuMat d_mat = mat_.getGpuMat();
GlBuffer buf(d_mat, GlBuffer::TEXTURE_BUFFER);
impl_ = new Impl(buf, bgra);
#endif
break;
}
default:
{
Mat mat = mat_.getMat();
impl_ = new Impl(mat, bgra);
break;
}
}
rows_ = _size.height;
cols_ = _size.width;
type_ = _type;
#endif
}
void cv::GlTexture::create(int _rows, int _cols, int _type)
{
#ifndef HAVE_OPENGL
(void)_rows;
(void)_cols;
(void)_type;
throw_nogl;
#else
if (rows_ != _rows || cols_ != _cols || type_ != _type)
{
impl_ = new Impl(_rows, _cols, _type);
rows_ = _rows;
cols_ = _cols;
type_ = _type;
}
#endif
}
void cv::GlTexture::release()
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_ = Impl::empty();
#endif
}
void cv::GlTexture::copyFrom(InputArray mat_, bool bgra)
{
#ifndef HAVE_OPENGL
(void)mat_;
(void)bgra;
throw_nogl;
#else
int kind = mat_.kind();
Size _size = mat_.size();
int _type = mat_.type();
create(_size, _type);
switch(kind)
{
case _InputArray::OPENGL_TEXTURE:
{
GlTexture tex = mat_.getGlTexture();
*this = tex;
break;
}
case _InputArray::OPENGL_BUFFER:
{
GlBuffer buf = mat_.getGlBuffer();
impl_->copyFrom(buf, bgra);
break;
}
case _InputArray::GPU_MAT:
{
#ifndef HAVE_CUDA
throw_nocuda;
#else
GpuMat d_mat = mat_.getGpuMat();
buf_.copyFrom(d_mat);
impl_->copyFrom(buf_, bgra);
#endif
break;
}
default:
{
Mat mat = mat_.getMat();
impl_->copyFrom(mat, bgra);
}
}
#endif
}
void cv::GlTexture::bind() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_->bind();
#endif
}
void cv::GlTexture::unbind() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
impl_->unbind();
#endif
}
template <> void cv::Ptr<cv::GlTexture::Impl>::delete_obj()
{
if (obj) delete obj;
}
////////////////////////////////////////////////////////////////////////
// GlArrays
void cv::GlArrays::setVertexArray(InputArray vertex)
{
int cn = vertex.channels();
int depth = vertex.depth();
CV_Assert(cn == 2 || cn == 3 || cn == 4);
CV_Assert(depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F);
vertex_.copyFrom(vertex);
}
void cv::GlArrays::setColorArray(InputArray color, bool bgra)
{
int cn = color.channels();
CV_Assert((cn == 3 && !bgra) || cn == 4);
color_.copyFrom(color);
bgra_ = bgra;
}
void cv::GlArrays::setNormalArray(InputArray normal)
{
int cn = normal.channels();
int depth = normal.depth();
CV_Assert(cn == 3);
CV_Assert(depth == CV_8S || depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F);
normal_.copyFrom(normal);
}
void cv::GlArrays::setTexCoordArray(InputArray texCoord)
{
int cn = texCoord.channels();
int depth = texCoord.depth();
CV_Assert(cn >= 1 && cn <= 4);
CV_Assert(depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F);
texCoord_.copyFrom(texCoord);
}
void cv::GlArrays::bind() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
CV_DbgAssert(texCoord_.empty() || texCoord_.size().area() == vertex_.size().area());
CV_DbgAssert(normal_.empty() || normal_.size().area() == vertex_.size().area());
CV_DbgAssert(color_.empty() || color_.size().area() == vertex_.size().area());
if (!texCoord_.empty())
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
CV_CheckGlError();
texCoord_.bind();
glTexCoordPointer(texCoord_.channels(), gl_types[texCoord_.depth()], 0, 0);
CV_CheckGlError();
texCoord_.unbind();
}
if (!normal_.empty())
{
glEnableClientState(GL_NORMAL_ARRAY);
CV_CheckGlError();
normal_.bind();
glNormalPointer(gl_types[normal_.depth()], 0, 0);
CV_CheckGlError();
normal_.unbind();
}
if (!color_.empty())
{
glEnableClientState(GL_COLOR_ARRAY);
CV_CheckGlError();
color_.bind();
int cn = color_.channels();
int format = cn == 3 ? cn : (bgra_ ? GL_BGRA : 4);
glColorPointer(format, gl_types[color_.depth()], 0, 0);
CV_CheckGlError();
color_.unbind();
}
if (!vertex_.empty())
{
glEnableClientState(GL_VERTEX_ARRAY);
CV_CheckGlError();
vertex_.bind();
glVertexPointer(vertex_.channels(), gl_types[vertex_.depth()], 0, 0);
CV_CheckGlError();
vertex_.unbind();
}
#endif
}
void cv::GlArrays::unbind() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
if (!texCoord_.empty())
{
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
CV_CheckGlError();
}
if (!normal_.empty())
{
glDisableClientState(GL_NORMAL_ARRAY);
CV_CheckGlError();
}
if (!color_.empty())
{
glDisableClientState(GL_COLOR_ARRAY);
CV_CheckGlError();
}
if (!vertex_.empty())
{
glDisableClientState(GL_VERTEX_ARRAY);
CV_CheckGlError();
}
#endif
}
////////////////////////////////////////////////////////////////////////
// GlFont
cv::GlFont::GlFont(const string& _family, int _height, Weight _weight, Style _style)
: family_(_family), height_(_height), weight_(_weight), style_(_style), base_(0)
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
base_ = glGenLists(256);
CV_CheckGlError();
glFuncTab()->generateBitmapFont(family_, height_, weight_, (style_ & STYLE_ITALIC) != 0, (style_ & STYLE_UNDERLINE) != 0, 0, 256, base_);
#endif
}
void cv::GlFont::draw(const char* str, size_t len) const
{
#ifndef HAVE_OPENGL
(void)str;
(void)len;
throw_nogl;
#else
if (base_ && len > 0)
{
glPushAttrib(GL_LIST_BIT);
glListBase(base_);
glCallLists(static_cast<GLsizei>(len), GL_UNSIGNED_BYTE, str);
glPopAttrib();
CV_CheckGlError();
}
#endif
}
namespace
{
class FontCompare : public unary_function<Ptr<GlFont>, bool>
{
public:
inline FontCompare(const string& family, int height, GlFont::Weight weight, GlFont::Style style)
: family_(family), height_(height), weight_(weight), style_(style)
{
}
bool operator ()(const cv::Ptr<GlFont>& font)
{
return font->family() == family_ && font->height() == height_ && font->weight() == weight_ && font->style() == style_;
}
private:
string family_;
int height_;
GlFont::Weight weight_;
GlFont::Style style_;
};
}
Ptr<GlFont> cv::GlFont::get(const std::string& family, int height, Weight weight, Style style)
{
#ifndef HAVE_OPENGL
(void)family;
(void)height;
(void)weight;
(void)style;
throw_nogl;
return Ptr<GlFont>();
#else
static vector< Ptr<GlFont> > fonts;
fonts.reserve(10);
vector< Ptr<GlFont> >::iterator fontIt = find_if(fonts.begin(), fonts.end(), FontCompare(family, height, weight, style));
if (fontIt == fonts.end())
{
fonts.push_back(new GlFont(family, height, weight, style));
fontIt = fonts.end() - 1;
}
return *fontIt;
#endif
}
////////////////////////////////////////////////////////////////////////
// Rendering
void cv::render(const GlTexture& tex, Rect_<double> wndRect, Rect_<double> texRect)
{
#ifndef HAVE_OPENGL
(void)tex;
(void)wndRect;
(void)texRect;
throw_nogl;
#else
if (!tex.empty())
{
tex.bind();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBegin(GL_QUADS);
glTexCoord2d(texRect.x, texRect.y);
glVertex2d(wndRect.x, wndRect.y);
glTexCoord2d(texRect.x, texRect.y + texRect.height);
glVertex2d(wndRect.x, (wndRect.y + wndRect.height));
glTexCoord2d(texRect.x + texRect.width, texRect.y + texRect.height);
glVertex2d(wndRect.x + wndRect.width, (wndRect.y + wndRect.height));
glTexCoord2d(texRect.x + texRect.width, texRect.y);
glVertex2d(wndRect.x + wndRect.width, wndRect.y);
glEnd();
CV_CheckGlError();
tex.unbind();
}
#endif
}
void cv::render(const GlArrays& arr, int mode, Scalar color)
{
#ifndef HAVE_OPENGL
(void)arr;
(void)mode;
(void)color;
throw_nogl;
#else
glColor3d(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0);
arr.bind();
glDrawArrays(mode, 0, arr.size().area());
arr.unbind();
#endif
}
void cv::render(const string& str, const Ptr<GlFont>& font, Scalar color, Point2d pos)
{
#ifndef HAVE_OPENGL
(void)str;
(void)font;
(void)color;
(void)pos;
throw_nogl;
#else
glPushAttrib(GL_DEPTH_BUFFER_BIT);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3d(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0);
glRasterPos2d(2.0 * (viewport[0] + pos.x) / viewport[2] - 1.0, 1.0 - 2.0 * (viewport[1] + pos.y + font->height()) / viewport[3]);
font->draw(str.c_str(), str.length());
glPopAttrib();
#endif
}
////////////////////////////////////////////////////////////////////////
// GlCamera
cv::GlCamera::GlCamera() :
eye_(0.0, 0.0, -5.0), center_(0.0, 0.0, 0.0), up_(0.0, 1.0, 0.0),
pos_(0.0, 0.0, -5.0), yaw_(0.0), pitch_(0.0), roll_(0.0),
useLookAtParams_(false),
scale_(1.0, 1.0, 1.0),
projectionMatrix_(),
fov_(45.0), aspect_(0.0),
left_(0.0), right_(1.0), bottom_(1.0), top_(0.0),
zNear_(-1.0), zFar_(1.0),
perspectiveProjection_(false)
{
}
void cv::GlCamera::lookAt(Point3d eye, Point3d center, Point3d up)
{
eye_ = eye;
center_ = center;
up_ = up;
useLookAtParams_ = true;
}
void cv::GlCamera::setCameraPos(Point3d pos, double yaw, double pitch, double roll)
{
pos_ = pos;
yaw_ = yaw;
pitch_ = pitch;
roll_ = roll;
useLookAtParams_ = false;
}
void cv::GlCamera::setScale(Point3d scale)
{
scale_ = scale;
}
void cv::GlCamera::setProjectionMatrix(const Mat& projectionMatrix, bool transpose)
{
CV_Assert(projectionMatrix.type() == CV_32F || projectionMatrix.type() == CV_64F);
CV_Assert(projectionMatrix.cols == 4 && projectionMatrix.rows == 4);
projectionMatrix_ = transpose ? projectionMatrix.t() : projectionMatrix;
}
void cv::GlCamera::setPerspectiveProjection(double fov, double aspect, double zNear, double zFar)
{
fov_ = fov;
aspect_ = aspect;
zNear_ = zNear;
zFar_ = zFar;
projectionMatrix_.release();
perspectiveProjection_ = true;
}
void cv::GlCamera::setOrthoProjection(double left, double right, double bottom, double top, double zNear, double zFar)
{
left_ = left;
right_ = right;
bottom_ = bottom;
top_ = top;
zNear_ = zNear;
zFar_ = zFar;
projectionMatrix_.release();
perspectiveProjection_ = false;
}
void cv::GlCamera::setupProjectionMatrix() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (projectionMatrix_.empty())
{
if (perspectiveProjection_)
gluPerspective(fov_, aspect_, zNear_, zFar_);
else
glOrtho(left_, right_, bottom_, top_, zNear_, zFar_);
}
else
{
if (projectionMatrix_.type() == CV_32F)
glLoadMatrixf(projectionMatrix_.ptr<float>());
else
glLoadMatrixd(projectionMatrix_.ptr<double>());
}
CV_CheckGlError();
#endif
}
void cv::GlCamera::setupModelViewMatrix() const
{
#ifndef HAVE_OPENGL
throw_nogl;
#else
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (useLookAtParams_)
gluLookAt(eye_.x, eye_.y, eye_.z, center_.x, center_.y, center_.z, up_.x, up_.y, up_.z);
else
{
glRotated(-yaw_, 0.0, 1.0, 0.0);
glRotated(-pitch_, 1.0, 0.0, 0.0);
glRotated(-roll_, 0.0, 0.0, 1.0);
glTranslated(-pos_.x, -pos_.y, -pos_.z);
}
glScaled(scale_.x, scale_.y, scale_.z);
CV_CheckGlError();
#endif
}
////////////////////////////////////////////////////////////////////////
// Error handling
bool icvCheckGlError(const char* file, const int line, const char* func)
{
#ifndef HAVE_OPENGL
(void)file;
(void)line;
(void)func;
return true;
#else
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
const char* msg;
switch (err)
{
case GL_INVALID_ENUM:
msg = "An unacceptable value is specified for an enumerated argument";
break;
case GL_INVALID_VALUE:
msg = "A numeric argument is out of range";
break;
case GL_INVALID_OPERATION:
msg = "The specified operation is not allowed in the current state";
break;
case GL_STACK_OVERFLOW:
msg = "This command would cause a stack overflow";
break;
case GL_STACK_UNDERFLOW:
msg = "This command would cause a stack underflow";
break;
case GL_OUT_OF_MEMORY:
msg = "There is not enough memory left to execute the command";
break;
default:
msg = "Unknown error";
};
cvError(CV_OpenGlApiCallError, func, msg, file, line);
return false;
}
return true;
#endif
}