summaryrefslogtreecommitdiff
path: root/src/player/Canvas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/player/Canvas.cpp')
-rw-r--r--src/player/Canvas.cpp325
1 files changed, 325 insertions, 0 deletions
diff --git a/src/player/Canvas.cpp b/src/player/Canvas.cpp
new file mode 100644
index 0000000..ae0723e
--- /dev/null
+++ b/src/player/Canvas.cpp
@@ -0,0 +1,325 @@
+//
+// libavg - Media Playback Engine.
+// Copyright (C) 2003-2014 Ulrich von Zadow
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Current versions can be found at www.libavg.de
+//
+
+#include "Canvas.h"
+
+#include "Player.h"
+#include "AVGNode.h"
+#include "Shape.h"
+#include "OffscreenCanvas.h"
+
+#include "../base/Exception.h"
+#include "../base/Logger.h"
+#include "../base/ScopeTimer.h"
+
+#include "../graphics/StandardShader.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace boost;
+
+namespace avg {
+
+Canvas::Canvas(Player * pPlayer)
+ : m_pPlayer(pPlayer),
+ m_bIsPlaying(false),
+ m_PlaybackEndSignal(&IPlaybackEndListener::onPlaybackEnd),
+ m_FrameEndSignal(&IFrameEndListener::onFrameEnd),
+ m_PreRenderSignal(&IPreRenderListener::onPreRender),
+ m_ClipLevel(0)
+{
+}
+
+Canvas::~Canvas()
+{
+}
+
+void Canvas::setRoot(NodePtr pRootNode)
+{
+ assert(!m_pRootNode);
+ m_pRootNode = dynamic_pointer_cast<CanvasNode>(pRootNode);
+ CanvasPtr pThis = dynamic_pointer_cast<Canvas>(shared_from_this());
+ m_pRootNode->setParent(0, Node::NS_CONNECTED, pThis);
+ registerNode(m_pRootNode);
+}
+
+void Canvas::initPlayback(int multiSampleSamples)
+{
+ m_bIsPlaying = true;
+ m_pRootNode->connectDisplay();
+ m_MultiSampleSamples = multiSampleSamples;
+ m_pVertexArray = VertexArrayPtr(new VertexArray(2000, 3000));
+}
+
+void Canvas::stopPlayback(bool bIsAbort)
+{
+ if (m_bIsPlaying) {
+ if (!bIsAbort) {
+ m_PlaybackEndSignal.emit();
+ }
+ m_pRootNode->disconnect(true);
+ m_pRootNode = CanvasNodePtr();
+ m_IDMap.clear();
+ m_bIsPlaying = false;
+ m_pVertexArray = VertexArrayPtr();
+ }
+}
+
+NodePtr Canvas::getElementByID(const std::string& id)
+{
+ if (m_IDMap.find(id) != m_IDMap.end()) {
+ return m_IDMap.find(id)->second;
+ } else {
+ return NodePtr();
+ }
+}
+
+void Canvas::registerNode(NodePtr pNode)
+{
+ addNodeID(pNode);
+ DivNodePtr pDivNode = boost::dynamic_pointer_cast<DivNode>(pNode);
+ if (pDivNode) {
+ for (unsigned i=0; i<pDivNode->getNumChildren(); i++) {
+ registerNode(pDivNode->getChild(i));
+ }
+ }
+}
+
+void Canvas::addNodeID(NodePtr pNode)
+{
+ const string& id = pNode->getID();
+ if (id != "") {
+ if (m_IDMap.find(id) != m_IDMap.end() &&
+ m_IDMap.find(id)->second != pNode)
+ {
+ throw (Exception (AVG_ERR_XML_DUPLICATE_ID,
+ string("Error: duplicate id ")+id));
+ }
+ m_IDMap.insert(NodeIDMap::value_type(id, pNode));
+ }
+}
+
+void Canvas::removeNodeID(const string& id)
+{
+ if (id != "") {
+ map<string, NodePtr>::iterator it;
+ it = m_IDMap.find(id);
+ if (it != m_IDMap.end()) {
+ m_IDMap.erase(it);
+ } else {
+ cerr << "removeNodeID(\"" << id << "\") failed." << endl;
+ AVG_ASSERT(false);
+ }
+ }
+}
+
+CanvasNodePtr Canvas::getRootNode() const
+{
+ return m_pRootNode;
+}
+
+static ProfilingZoneID RenderProfilingZone("Render");
+
+void Canvas::doFrame(bool bPythonAvailable)
+{
+ emitPreRenderSignal();
+ if (!m_pPlayer->isStopping()) {
+ ScopeTimer Timer(RenderProfilingZone);
+ Player::get()->startTraversingTree();
+ if (bPythonAvailable) {
+ Py_BEGIN_ALLOW_THREADS;
+ try {
+ renderTree();
+ } catch(...) {
+ Py_BLOCK_THREADS;
+ Player::get()->endTraversingTree();
+ throw;
+ }
+ Py_END_ALLOW_THREADS;
+ } else {
+ renderTree();
+ }
+ Player::get()->endTraversingTree();
+ }
+ emitFrameEndSignal();
+}
+
+IntPoint Canvas::getSize() const
+{
+ return IntPoint(m_pRootNode->getSize());
+}
+static ProfilingZoneID PushClipRectProfilingZone("pushClipRect");
+
+void Canvas::pushClipRect(const glm::mat4& transform, SubVertexArray& va)
+{
+ ScopeTimer timer(PushClipRectProfilingZone);
+ m_ClipLevel++;
+ clip(transform, va, GL_INCR);
+}
+
+static ProfilingZoneID PopClipRectProfilingZone("popClipRect");
+
+void Canvas::popClipRect(const glm::mat4& transform, SubVertexArray& va)
+{
+ ScopeTimer timer(PopClipRectProfilingZone);
+ m_ClipLevel--;
+ clip(transform, va, GL_DECR);
+}
+
+void Canvas::registerPlaybackEndListener(IPlaybackEndListener* pListener)
+{
+ m_PlaybackEndSignal.connect(pListener);
+}
+
+void Canvas::unregisterPlaybackEndListener(IPlaybackEndListener* pListener)
+{
+ m_PlaybackEndSignal.disconnect(pListener);
+}
+
+void Canvas::registerFrameEndListener(IFrameEndListener* pListener)
+{
+ m_FrameEndSignal.connect(pListener);
+}
+
+void Canvas::unregisterFrameEndListener(IFrameEndListener* pListener)
+{
+ m_FrameEndSignal.disconnect(pListener);
+}
+
+void Canvas::registerPreRenderListener(IPreRenderListener* pListener)
+{
+ m_PreRenderSignal.connect(pListener);
+}
+
+void Canvas::unregisterPreRenderListener(IPreRenderListener* pListener)
+{
+ m_PreRenderSignal.disconnect(pListener);
+}
+
+Player* Canvas::getPlayer() const
+{
+ return m_pPlayer;
+}
+
+vector<NodePtr> Canvas::getElementsByPos(const glm::vec2& pos) const
+{
+ vector<NodePtr> elements;
+ m_pRootNode->getElementsByPos(pos, elements);
+ return elements;
+}
+
+static ProfilingZoneID PreRenderProfilingZone("PreRender");
+static ProfilingZoneID VATransferProfilingZone("VA Transfer");
+
+void Canvas::preRender()
+{
+ ScopeTimer Timer(PreRenderProfilingZone);
+ m_pVertexArray->reset();
+ m_pRootNode->preRender(m_pVertexArray, true, 1.0f);
+ {
+ ScopeTimer Timer(VATransferProfilingZone);
+ m_pVertexArray->update();
+ }
+}
+
+void Canvas::render(IntPoint windowSize, bool bOffscreen)
+{
+ clearGLBuffers(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
+ !bOffscreen);
+ glViewport(0, 0, windowSize.x, windowSize.y);
+ GLContext::checkError("Canvas::render: glViewport()");
+ glm::vec2 size = m_pRootNode->getSize();
+ glm::mat4 projMat;
+ if (bOffscreen) {
+ projMat = glm::ortho(0.f, size.x, 0.f, size.y);
+ } else {
+ projMat = glm::ortho(0.f, size.x, size.y, 0.f);
+ }
+ m_pVertexArray->activate();
+ m_pRootNode->maybeRender(projMat);
+
+ renderOutlines(projMat);
+}
+
+void Canvas::renderOutlines(const glm::mat4& transform)
+{
+ GLContext* pContext = GLContext::getMain();
+ VertexArrayPtr pVA(new VertexArray);
+ pContext->setBlendMode(GLContext::BLEND_BLEND, false);
+ m_pRootNode->renderOutlines(pVA, Pixel32(0,0,0,0));
+ StandardShaderPtr pShader = pContext->getStandardShader();
+ pShader->setTransform(transform);
+ pShader->setUntextured();
+ pShader->setAlpha(0.5f);
+ pShader->activate();
+ if (pVA->getNumVerts() != 0) {
+ pVA->update();
+ pVA->draw();
+ }
+}
+
+void Canvas::clip(const glm::mat4& transform, SubVertexArray& va, GLenum stencilOp)
+{
+ // Disable drawing to color buffer
+ glColorMask(0, 0, 0, 0);
+
+ // Enable drawing to stencil buffer
+ glStencilMask(~0);
+
+ // Draw clip rectangle into stencil buffer
+ glStencilFunc(GL_ALWAYS, 0, 0);
+ glStencilOp(stencilOp, stencilOp, stencilOp);
+
+ StandardShaderPtr pShader = GLContext::getMain()->getStandardShader();
+ pShader->setUntextured();
+ pShader->setTransform(transform);
+ pShader->activate();
+ va.draw();
+
+ // Set stencil test to only let
+ glStencilFunc(GL_LEQUAL, m_ClipLevel, ~0);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ // Disable drawing to stencil buffer
+ glStencilMask(0);
+
+ // Enable drawing to color buffer
+ glColorMask(~0, ~0, ~0, ~0);
+}
+
+static ProfilingZoneID PreRenderSignalProfilingZone("PreRender signal");
+
+void Canvas::emitPreRenderSignal()
+{
+ ScopeTimer Timer(PreRenderSignalProfilingZone);
+ m_PreRenderSignal.emit();
+}
+
+static ProfilingZoneID FrameEndProfilingZone("OnFrameEnd");
+
+void Canvas::emitFrameEndSignal()
+{
+ ScopeTimer Timer(FrameEndProfilingZone);
+ m_FrameEndSignal.emit();
+}
+
+}