summaryrefslogtreecommitdiff
path: root/src/graphics/GPUFilter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/GPUFilter.cpp')
-rw-r--r--src/graphics/GPUFilter.cpp287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/graphics/GPUFilter.cpp b/src/graphics/GPUFilter.cpp
new file mode 100644
index 0000000..36fe811
--- /dev/null
+++ b/src/graphics/GPUFilter.cpp
@@ -0,0 +1,287 @@
+//
+// 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 "GPUFilter.h"
+#include "Bitmap.h"
+
+#include "VertexArray.h"
+#include "ImagingProjection.h"
+#include "GLContext.h"
+#include "ShaderRegistry.h"
+#include "Filterfliprgb.h"
+#include "BitmapLoader.h"
+
+#include "../base/ObjectCounter.h"
+#include "../base/Exception.h"
+#include "../base/MathHelper.h"
+
+#include <iostream>
+#include <string.h>
+
+using namespace std;
+using namespace boost;
+
+namespace avg {
+
+GPUFilter::GPUFilter(const string& sShaderID, bool bUseAlpha,
+ bool bStandalone, unsigned numTextures, bool bMipmap)
+ : m_bStandalone(bStandalone),
+ m_NumTextures(numTextures),
+ m_bMipmap(bMipmap),
+ m_SrcSize(0,0),
+ m_DestRect(0,0,0,0)
+{
+ m_PFSrc = BitmapLoader::get()->getDefaultPixelFormat(bUseAlpha);
+ m_PFDest = m_PFSrc;
+ createShader(sShaderID);
+
+ m_pShader = avg::getShader(sShaderID);
+ ObjectCounter::get()->incRef(&typeid(*this));
+}
+
+GPUFilter::GPUFilter(PixelFormat pfSrc, PixelFormat pfDest, bool bStandalone,
+ const string& sShaderID, unsigned numTextures, bool bMipmap)
+ : m_PFSrc(pfSrc),
+ m_PFDest(pfDest),
+ m_bStandalone(bStandalone),
+ m_NumTextures(numTextures),
+ m_bMipmap(bMipmap),
+ m_SrcSize(0,0),
+ m_DestRect(0,0,0,0)
+{
+ createShader(sShaderID);
+ m_pShader = avg::getShader(sShaderID);
+ ObjectCounter::get()->incRef(&typeid(*this));
+}
+
+GPUFilter::~GPUFilter()
+{
+ ObjectCounter::get()->decRef(&typeid(*this));
+}
+
+BitmapPtr GPUFilter::apply(BitmapPtr pBmpSource)
+{
+ AVG_ASSERT(m_pSrcTex);
+ AVG_ASSERT(!(m_pFBOs.empty()));
+ m_pSrcMover->moveBmpToTexture(pBmpSource, *m_pSrcTex);
+ apply(m_pSrcTex);
+ BitmapPtr pFilteredBmp = m_pFBOs[0]->getImage();
+
+ BitmapPtr pTmpBmp;
+ if (pixelFormatIsBlueFirst(pFilteredBmp->getPixelFormat()) !=
+ pixelFormatIsBlueFirst(pBmpSource->getPixelFormat()) &&
+ pFilteredBmp->getBytesPerPixel() <= 4)
+ {
+ pTmpBmp = FilterFlipRGB().apply(pFilteredBmp);
+ } else {
+ pTmpBmp = pFilteredBmp;
+ }
+
+ BitmapPtr pDestBmp;
+ if (pTmpBmp->getPixelFormat() != pBmpSource->getPixelFormat()) {
+ pDestBmp = BitmapPtr(new Bitmap(m_DestRect.size(),
+ pBmpSource->getPixelFormat()));
+ pDestBmp->copyPixels(*pTmpBmp);
+ } else {
+ pDestBmp = pTmpBmp;
+ }
+ return pDestBmp;
+}
+
+void GPUFilter::apply(GLTexturePtr pSrcTex)
+{
+ m_pFBOs[0]->activate();
+ applyOnGPU(pSrcTex);
+ m_pFBOs[0]->copyToDestTexture();
+}
+
+GLTexturePtr GPUFilter::getDestTex(int i) const
+{
+ return m_pFBOs[i]->getTex();
+}
+
+BitmapPtr GPUFilter::getImage() const
+{
+ return m_pFBOs[0]->getImage();
+}
+
+FBOPtr GPUFilter::getFBO(int i)
+{
+ return m_pFBOs[i];
+}
+
+const IntRect& GPUFilter::getDestRect() const
+{
+ return m_DestRect;
+}
+
+const IntPoint& GPUFilter::getSrcSize() const
+{
+ return m_SrcSize;
+}
+
+FRect GPUFilter::getRelDestRect() const
+{
+ glm::vec2 srcSize(m_SrcSize);
+ return FRect(m_DestRect.tl.x/srcSize.x, m_DestRect.tl.y/srcSize.y,
+ m_DestRect.br.x/srcSize.x, m_DestRect.br.y/srcSize.y);
+}
+
+void GPUFilter::setDimensions(const IntPoint& srcSize)
+{
+ setDimensions(srcSize, IntRect(IntPoint(0,0), srcSize), GL_CLAMP_TO_EDGE);
+}
+
+void GPUFilter::setDimensions(const IntPoint& srcSize, const IntRect& destRect,
+ unsigned texMode)
+{
+ bool bProjectionChanged = false;
+ if (destRect != m_DestRect) {
+ m_pFBOs.clear();
+ for (unsigned i=0; i<m_NumTextures; ++i) {
+ FBOPtr pFBO = FBOPtr(new FBO(destRect.size(), m_PFDest, 1, 1, false,
+ m_bMipmap));
+ m_pFBOs.push_back(pFBO);
+ }
+ m_DestRect = destRect;
+ bProjectionChanged = true;
+ }
+ if (m_bStandalone && srcSize != m_SrcSize) {
+ m_pSrcTex = GLTexturePtr(new GLTexture(srcSize, m_PFSrc, false, 0, texMode,
+ texMode));
+ m_pSrcMover = TextureMover::create(srcSize, m_PFSrc, GL_STREAM_DRAW);
+ bProjectionChanged = true;
+ }
+ m_SrcSize = srcSize;
+ if (bProjectionChanged) {
+ m_pProjection = ImagingProjectionPtr(new ImagingProjection(srcSize, destRect));
+ }
+}
+
+const OGLShaderPtr& GPUFilter::getShader() const
+{
+ return m_pShader;
+}
+
+void GPUFilter::draw(GLTexturePtr pTex)
+{
+ pTex->activate(GL_TEXTURE0);
+ m_pProjection->draw(m_pShader);
+}
+
+void dumpKernel(int width, float* pKernel)
+{
+ cerr << " Kernel width: " << width << endl;
+ float sum = 0;
+ for (int i = 0; i < width; ++i) {
+ sum += pKernel[i];
+ cerr << " " << pKernel[i] << endl;
+ }
+ cerr << "Sum of coefficients: " << sum << endl;
+}
+
+int GPUFilter::getBlurKernelRadius(float stdDev) const
+{
+ return int(ceil(stdDev*3));
+}
+
+GLTexturePtr GPUFilter::calcBlurKernelTex(float stdDev, float opacity, bool bUseFloat)
+ const
+{
+ AVG_ASSERT(opacity != -1);
+ int kernelWidth;
+ float* pKernel;
+ if (stdDev == 0) {
+ kernelWidth = 1;
+ pKernel = new float[1];
+ pKernel[0] = float(opacity);
+ } else {
+ float tempCoeffs[1024];
+ int i=0;
+ float coeff;
+ do {
+ coeff = float(exp(-i*i/(2*stdDev*stdDev))/sqrt(2*PI*stdDev*stdDev))
+ *float(opacity);
+ tempCoeffs[i] = coeff;
+ i++;
+ } while (coeff > 0.003 && i < 1024);
+ if (i > 1) {
+ int kernelCenter = i - 2;
+ kernelWidth = kernelCenter*2+1;
+ pKernel = new float[kernelWidth];
+ float sum = 0;
+ for (int i = 0; i <= kernelCenter; ++i) {
+ pKernel[kernelCenter+i] = tempCoeffs[i];
+ sum += tempCoeffs[i];
+ if (i != 0) {
+ pKernel[kernelCenter-i] = tempCoeffs[i];
+ sum += tempCoeffs[i];
+ }
+ }
+ // Make sure the sum of coefficients is opacity despite the inaccuracies
+ // introduced by using a kernel of finite size.
+ for (int i = 0; i < kernelWidth; ++i) {
+ pKernel[i] *= float(opacity)/sum;
+ }
+ } else {
+ // Blur is so wide that all pixels would be black at 8-bit precision
+ kernelWidth = 1;
+ pKernel = new float[1];
+ pKernel[0] = 0.;
+ }
+ }
+// dumpKernel(kernelWidth, pKernel);
+
+ IntPoint size(kernelWidth, 1);
+ PixelFormat pf;
+ if (bUseFloat) {
+ pf = R32G32B32A32F;
+ } else {
+ pf = I8;
+ }
+ GLTexturePtr pTex(new GLTexture(size, pf));
+ TextureMoverPtr pKernelMover = TextureMover::create(size, pf, GL_STREAM_DRAW);
+ BitmapPtr pBmp = pKernelMover->lock();
+ void * pPixels = pBmp->getPixels();
+ GLContext::checkError("GPUFilter::calcBlurKernelTex MapBuffer()");
+ if (bUseFloat) {
+ float * pCurFloat = (float*)pPixels;
+ for (int i = 0; i < kernelWidth; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ *pCurFloat = pKernel[i];
+ ++pCurFloat;
+ }
+ }
+ } else {
+ unsigned char * pCurPixel = (unsigned char *)pPixels;
+ for (int i = 0; i < kernelWidth; ++i) {
+ *pCurPixel = (unsigned char)(pKernel[i]*255+0.5);
+ ++pCurPixel;
+ }
+ }
+ pKernelMover->unlock();
+ pKernelMover->moveToTexture(*pTex);
+
+ delete[] pKernel;
+ return pTex;
+}
+
+}