[Bf-blender-cvs] [02c618f] decklink: VideoTexture: Add an optional parameter to the refresh method of image sources.

Benoit Bolsee noreply at git.blender.org
Sun Aug 30 23:04:18 CEST 2015


Commit: 02c618fb9453df2754a03c9f7aba1455c5317384
Author: Benoit Bolsee
Date:   Sun Aug 30 22:37:13 2015 +0200
Branches: decklink
https://developer.blender.org/rB02c618fb9453df2754a03c9f7aba1455c5317384

VideoTexture: Add an optional parameter to the refresh method of image sources.

If provided, the parameter must be an object that supports the buffer protocol
(bytearray, memoryview, etc) otherwise a runtime error is generated. If the
buffer is sufficiently large, the image is copied in the buffer before being
refreshed (i.e. invalidated). If the image was not already available, it will
be updated first. In the later case, it is possible that the image is loaded
directly in the user's buffer wihtout an intermediate copy in the internal
image buffer. This is currently the case for ImageViewport and ImageRender
sources when the OGL format matches the buffer format (alpha, no filter,
no flip, no scaling). Note that the image format in the buffer is always RGBA.
If no parameter is provided, the method works as before: the image is
invalidated without any attempt to updated it first.

The function returns False if a buffer was provided but could not be updated
for any reason (source not ready, buffer too small). It returns True in all
other situations.

The purpose of this function is to efficiently retrieve the OGL frame buffer
directly into a user buffer by skiping an extra copy to the internal image
buffer if it's not needed.

===================================================================

M	source/gameengine/VideoTexture/ImageBase.cpp
M	source/gameengine/VideoTexture/ImageBase.h
M	source/gameengine/VideoTexture/ImageMix.cpp
M	source/gameengine/VideoTexture/ImageRender.cpp
M	source/gameengine/VideoTexture/ImageRender.h
M	source/gameengine/VideoTexture/ImageViewport.cpp
M	source/gameengine/VideoTexture/ImageViewport.h
M	source/gameengine/VideoTexture/VideoBase.cpp
M	source/gameengine/VideoTexture/VideoBase.h
M	source/gameengine/VideoTexture/VideoDeckLink.cpp
M	source/gameengine/VideoTexture/VideoFFmpeg.cpp

===================================================================

diff --git a/source/gameengine/VideoTexture/ImageBase.cpp b/source/gameengine/VideoTexture/ImageBase.cpp
index 23da528..a60b9f6 100644
--- a/source/gameengine/VideoTexture/ImageBase.cpp
+++ b/source/gameengine/VideoTexture/ImageBase.cpp
@@ -111,6 +111,15 @@ unsigned int * ImageBase::getImage (unsigned int texId, double ts)
 	return m_avail ? m_image : NULL;
 }
 
+bool ImageBase::loadImage(unsigned int *buffer, unsigned int size)
+{
+	if (getImage(0, -1) != NULL && size >= getBuffSize())
+	{
+		memcpy(buffer, m_image, getBuffSize());
+		return true;
+	}
+	return false;
+}
 
 // refresh image source
 void ImageBase::refresh (void)
@@ -346,7 +355,6 @@ unsigned int * ImageSource::getImage (double ts)
 	return m_image;
 }
 
-
 // refresh source
 void ImageSource::refresh (void)
 {
@@ -500,10 +508,54 @@ PyObject *Image_getSize (PyImage *self, void *closure)
 }
 
 // refresh image
-PyObject *Image_refresh (PyImage *self)
+PyObject *Image_refresh (PyImage *self, PyObject *args)
 {
+	Py_buffer buffer;
+	bool done = true;
+
+	memset(&buffer, 0, sizeof(buffer));
+	if (PyArg_ParseTuple(args, "|s*:refresh", &buffer))
+	{
+		if (buffer.buf)
+		{
+			// a target buffer is provided, verify its format
+			if (buffer.readonly)
+			{
+				PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be writable");
+			} else if (!PyBuffer_IsContiguous(&buffer, 'C'))
+			{
+				PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be contiguous in memory");
+			}
+			else if (((intptr_t)buffer.buf & 3) != 0)
+			{
+				PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be aligned to 4 bytes boundary");
+			}
+			else
+			{
+				// ready to get the image into our buffer
+				try
+				{
+					done = self->m_image->loadImage((unsigned int *)buffer.buf, buffer.len);
+				}
+				catch (Exception & exp)
+				{
+					exp.report();
+				}
+			}
+			PyBuffer_Release(&buffer);
+			if (PyErr_Occurred())
+		        return NULL;
+		}
+	}
+	else
+	{
+		return NULL;
+	}
+
 	self->m_image->refresh();
-	Py_RETURN_NONE;
+	if (done)
+		Py_RETURN_TRUE;
+	Py_RETURN_FALSE;
 }
 
 // get scale
diff --git a/source/gameengine/VideoTexture/ImageBase.h b/source/gameengine/VideoTexture/ImageBase.h
index 50dc8c1..e91857e 100644
--- a/source/gameengine/VideoTexture/ImageBase.h
+++ b/source/gameengine/VideoTexture/ImageBase.h
@@ -104,6 +104,9 @@ public:
 	/// calculate size(nearest power of 2)
 	static short calcSize(short size);
 
+	/// calculate image from sources and send it to a target buffer instead of a texture
+	virtual bool loadImage(unsigned int *buffer, unsigned int size);
+
 	/// number of buffer pointing to m_image, public because not handled by this class
 	int m_exports;
 
@@ -348,7 +351,7 @@ PyObject *Image_getImage(PyImage *self, char *mode);
 // get image size
 PyObject *Image_getSize(PyImage *self, void *closure);
 // refresh image - invalidate current content
-PyObject *Image_refresh(PyImage *self);
+PyObject *Image_refresh(PyImage *self, PyObject *args);
 
 // get scale
 PyObject *Image_getScale(PyImage *self, void *closure);
diff --git a/source/gameengine/VideoTexture/ImageMix.cpp b/source/gameengine/VideoTexture/ImageMix.cpp
index dc93bf0..df84191 100644
--- a/source/gameengine/VideoTexture/ImageMix.cpp
+++ b/source/gameengine/VideoTexture/ImageMix.cpp
@@ -156,7 +156,7 @@ static PyMethodDef imageMixMethods[] = {
 	{"getWeight", (PyCFunction)getWeight, METH_VARARGS, "get image source weight"},
 	{"setWeight", (PyCFunction)setWeight, METH_VARARGS, "set image source weight"},
 	// methods from ImageBase class
-	{"refresh", (PyCFunction)Image_refresh, METH_NOARGS, "Refresh image - invalidate its current content"},
+	{"refresh", (PyCFunction)Image_refresh, METH_VARARGS, "Refresh image - invalidate its current content"},
 	{NULL}
 };
 // attributes structure
diff --git a/source/gameengine/VideoTexture/ImageRender.cpp b/source/gameengine/VideoTexture/ImageRender.cpp
index 4687fe0..08c8f7a 100644
--- a/source/gameengine/VideoTexture/ImageRender.cpp
+++ b/source/gameengine/VideoTexture/ImageRender.cpp
@@ -123,28 +123,43 @@ void ImageRender::setBackgroundFromScene (KX_Scene *scene)
 // capture image from viewport
 void ImageRender::calcImage (unsigned int texId, double ts)
 {
-	if (m_rasterizer->GetDrawingMode() != RAS_IRasterizer::KX_TEXTURED ||   // no need for texture
-	        m_camera->GetViewport() ||        // camera must be inactive
-	        m_camera == m_scene->GetActiveCamera())
+	// render the scene from the camera
+	if (!Render())
 	{
-		// no need to compute texture in non texture rendering
 		m_avail = false;
 		return;
 	}
-	// render the scene from the camera
-	Render();
+
 	// get image from viewport
 	ImageViewport::calcImage(texId, ts);
 	// restore OpenGL state
 	m_canvas->EndFrame();
 }
 
-void ImageRender::Render()
+bool ImageRender::loadImage(unsigned int *buffer, unsigned int size)
+{
+	bool ret;
+	if (!Render())
+	{
+		return false;
+	}
+	ret = ImageViewport::loadImage(buffer, size);
+	m_canvas->EndFrame();
+	return ret;
+}
+
+bool ImageRender::Render()
 {
 	RAS_FrameFrustum frustrum;
 
-	if (!m_render)
-		return;
+	if (!m_render ||
+	    m_rasterizer->GetDrawingMode() != RAS_IRasterizer::KX_TEXTURED ||   // no need for texture
+        m_camera->GetViewport() ||        // camera must be inactive
+        m_camera == m_scene->GetActiveCamera())
+	{
+		// no need to compute texture in non texture rendering
+		return false;
+	}
 
 	if (m_mirror)
 	{
@@ -164,7 +179,7 @@ void ImageRender::Render()
 		MT_Scalar observerDistance = mirrorPlaneDTerm - observerWorldPos.dot(mirrorWorldZ);
 		// if distance < 0.01 => observer is on wrong side of mirror, don't render
 		if (observerDistance < 0.01)
-			return;
+			return false;
 		// set camera world position = observerPos + normal * 2 * distance
 		MT_Point3 cameraWorldPos = observerWorldPos + (MT_Scalar(2.0)*observerDistance)*mirrorWorldZ;
 		m_camera->GetSGNode()->SetLocalPosition(cameraWorldPos);
@@ -310,6 +325,8 @@ void ImageRender::Render()
 
 	// restore the canvas area now that the render is completed
 	m_canvas->GetWindowArea() = area;
+
+	return true;
 }
 
 
@@ -406,7 +423,7 @@ static int setBackground(PyImage *self, PyObject *value, void *closure)
 // methods structure
 static PyMethodDef imageRenderMethods[] =
 { // methods from ImageBase class
-	{"refresh", (PyCFunction)Image_refresh, METH_NOARGS, "Refresh image - invalidate its current content"},
+	{"refresh", (PyCFunction)Image_refresh, METH_VARARGS, "Refresh image - invalidate its current content after optionally transferring its content to a target buffer"},
 	{NULL}
 };
 // attributes structure
diff --git a/source/gameengine/VideoTexture/ImageRender.h b/source/gameengine/VideoTexture/ImageRender.h
index ef55e4d..26d1f04 100644
--- a/source/gameengine/VideoTexture/ImageRender.h
+++ b/source/gameengine/VideoTexture/ImageRender.h
@@ -97,7 +97,10 @@ protected:
 	/// render 3d scene to image
 	virtual void calcImage (unsigned int texId, double ts);
 
-	void Render();
+	/// render 3d scene to user buffer
+	virtual bool loadImage(unsigned int *buffer, unsigned int size);
+
+	bool Render();
 	void SetupRenderFrame(KX_Scene *scene, KX_Camera* cam);
 	void RenderFrame(KX_Scene* scene, KX_Camera* cam);
 	void setBackgroundFromScene(KX_Scene *scene);
diff --git a/source/gameengine/VideoTexture/ImageViewport.cpp b/source/gameengine/VideoTexture/ImageViewport.cpp
index cd77abf..6169b57 100644
--- a/source/gameengine/VideoTexture/ImageViewport.cpp
+++ b/source/gameengine/VideoTexture/ImageViewport.cpp
@@ -206,6 +206,38 @@ void ImageViewport::calcImage (unsigned int texId, double ts)
 	}
 }
 
+bool ImageViewport::loadImage(unsigned int *buffer, unsigned int size)
+{
+	unsigned int *tmp_image;
+	bool ret;
+
+	// if scale was changed
+	if (m_scaleChange)
+		// reset image
+		init(m_capSize[0], m_capSize[1]);
+
+	// size must be identical
+	if (size < getBuffSize())
+		return false;
+
+	if (m_avail)
+	{
+		// just copy
+		memcpy(buffer, m_image, getBuffSize());
+		ret = true;
+	}
+	else
+	{
+		tmp_image = m_image;
+		m_image = buffer;
+		calcImage(0, -1);
+		ret = m_avail;
+		m_image = tmp_image;
+		// since the image was not loaded to our buffer, it's not valid
+		m_avail = false;
+	}
+	return ret;
+}
 
 
 // cast Image pointer to ImageViewport
@@ -348,7 +380,7 @@ int ImageViewport_setCaptureSize(PyImage *self, PyObject *value, void *closure)
 // methods structure
 static PyMethodDef imageViewportMethods[] =
 { // methods from ImageBase class
-	{"refresh", (PyCFunction)Image_refresh, METH_NOARGS, "Refresh image - invalidate its current content"},
+	{"refresh", (PyCFunction)Image_refresh, METH_VARARGS, "Refresh image - invalidate its current content"},
 	{NULL}
 };
 // attributes structure
diff --git a/source/gameengine/VideoTexture/ImageViewport.h b/source/gameengine/VideoTexture/ImageViewport.h
index 10d894a..37df47d 100644
--- a/source/gameengine/VideoTexture/ImageViewport.h
+++ b/source/gameengine/VideoTexture/ImageViewport.h
@@ -67,6 +67,9 @@ public:
 	/// set position in viewport
 	void setPosition (GLint pos[2] = NULL);
 
+	/// capture image from viewport to user buffer
+	virtual bool loadImage(unsigned int *buffer, unsigned int size);
+
 protected:
 	/// frame buffer rectangle
 	GLint m_viewport[4];
diff --git a/source/gameengine/VideoTexture/VideoBase.cpp b/source/gameengine/VideoTexture/VideoBase.cpp
index 9c8df0c..2238aae 100644
--- a/source/gameengine/VideoTexture/VideoBase.cpp
+++ b/source/gameengine/VideoTexture/VideoBase.cpp
@@ -137,8 +137,49 @@ PyObject *Video_getStatus(PyImage *self, void *closure)
 }
 
 // refresh video
-PyObject *Video_refresh(PyImage *self)
+PyObject *Video_refresh(PyImage *self, PyObject *args)
 {
+	Py_buffer buffer;
+
+	memset(&buffer, 0, sizeof(buffer));
+	if (PyArg_ParseTuple(args, "|s*:refresh", &buffer))
+	{
+		if (buffer.buf)
+		{
+			// a target buffer is provided, verify its format
+			if (buffer.readonly)
+			{
+				PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be writable");
+			} else if (!PyBuffer_IsContiguous(&buffer, 'C'))
+			{
+				PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be contiguous in memory");
+			}
+			else if (((intptr_t)buffer.buf & 3)

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list