Logo Search packages:      
Sourcecode: darkroom version File versions

PostProcessor.cpp

/*
 * Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
 * 
 * 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.1 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, see <http://www.gnu.org/licenses/>. */

#include "PostProcessor.h"

#include <cmath>
#include <cstring>

#include <KDebug>

#include <QMutex>
#include <QPointF>

#include "ColorManager.h"
#include "ProcessingOptions.h"

#include <config-openctl.h>
#ifdef HAVE_OPENCTL
#include <OpenCTL/Module.h>
#include <OpenCTL/Program.h>
#include <GTLCore/PixelDescription.h>
#include <GTLCore/Buffer.h>
#include <GTLCore/Type.h>
#endif

QList<QPointF> validateList( const QList<QPointF>& list )
{
  if( list.size() >= 2)
  {
    return list;
  } else {
    QList<QPointF> list;
    list.push_back(QPointF(0,0));
    list.push_back(QPointF(1,1));
    return list;
  }
}

#ifndef HAVE_OPENCTL

float interpolateCubic1D (const QList<QPointF>& table, double p)
{
  if( p <= table[0].x() ) return table[0].y();
  if( p >= table[table.size()-1].x() ) return table[table.size()-1].y();
  
  for( int i = 0; i < table.size() - 1; ++i )
  {
    if( table[i].x() <= p && p < table[i+1].x() )
    {
      double s = (p - table[i].x()) / (table[i+1].x() - table[i].x());
      double dx1 = (table[i+1].x() - table[i].x());
      double dy1 = (table[i+1].y() - table[i].y());
      
      double m0;
      double m1;
      if( i > 0 )
      {
        double dy0 = (table[i].y() - table[i-1].y());
        double dx0 = (table[i].x() - table[i-1].x());
        m0 = (dy1 + dx1 * dy0 / dx0) / 2;
      }
      if( i < table.size()-2 )
      {
        double dx2 = (table[i+2].x() - table[i+1].x());
        double dy2 = (table[i+2].y() - table[i+1].y());
        m1 = (dy1 + dx1 * dy2 / dx2) / 2;
      }
      if( i == 0) {
        m0 = (3 * dy1 - m1) / 2;
      }
      if( i == table.size()-2 )
      {
        m1 = (3 * dy1 - m0) / 2;
      }
      return table[i].y() * (2 * s*s*s - 3 * s*s + 1) +
          m0 * (s*s*s - 2 * s*s + s) +
          table[i+1].y() * (-2 * s*s*s + 3 * s*s) +
          m1 * (s*s*s - s*s);
    }
  }
  return 0.0;
}

#endif

struct PostProcessor::Private {
  double exposure;
  bool convertToSRGB;
#ifdef HAVE_OPENCTL
  OpenCTL::Program* program16;
  static QMutex mutex;
#else
  QList<QPointF> table;
#endif
};

#ifdef HAVE_OPENCTL
QMutex PostProcessor::Private::mutex;

QString valueListToCTL( const QList<QPointF>& _points, double _scale )
{
  QString result = "{ {";
  for(int i = 0; i < _points.size(); ++i)
  {
    QPointF point = _points[i];
    result += QString::number(point.x() * _scale) + "," + QString::number(point.y() * _scale) + "}";
    if( i != _points.size() - 1)
    {
      result += ",{";
    }
    kDebug() << i;
  }
//   kDebug() << result;
  return result + " }";
}
#endif

PostProcessor::PostProcessor( const ProcessingOptions& processingOptions) : d(new Private)
{
  d->exposure = pow(2, processingOptions.asDouble("Exposure") + 2.47393 + 1) * 0.0883883;
//   kDebug() << "Exposure: " << d->exposure;
  setConvertToSRGB( processingOptions.asBool("ConvertToSRGB") );
  
#if 1
#ifdef HAVE_OPENCTL
  QList<QPointF> lightPoints = validateList( processingOptions.asPointFList( "LightnessCurve" ) );
  QList<QPointF> redPoints = validateList( processingOptions.asPointFList( "RedCurve" ) );
  QList<QPointF> greenPoints = validateList( processingOptions.asPointFList( "GreenCurve" ) );
  QList<QPointF> bluePoints = validateList( processingOptions.asPointFList( "BlueCurve" ) );
  QString beginProgram = "const float exposure = " + QString::number( d->exposure ) + ";\n";
  QString program16Src = beginProgram + "const float lightTable[][2] = " + valueListToCTL( lightPoints, 1.0 ) + ";";
  program16Src += "const float redTable[][2] = " + valueListToCTL( redPoints, 1.0 ) + ";";
  program16Src += "const float greenTable[][2] = " + valueListToCTL( greenPoints, 1.0 ) + ";";
  program16Src += "const float blueTable[][2] = " + valueListToCTL( bluePoints, 1.0 ) + ";";
  program16Src += "const float inputBlackValue = " + QString::number( processingOptions.asDouble("InputBlackValue") ) + ";";
  program16Src += "const float inputWhiteValue = " + QString::number( processingOptions.asDouble("InputWhiteValue") ) + ";";
  program16Src += "const float inputGamma = " + QString::number( processingOptions.asDouble("InputGamma") ) + ";";
  program16Src += "const float outputBlackValue = " + QString::number( processingOptions.asDouble("OutputBlackValue") ) + ";";
  program16Src += "const float outputWhiteValue = " + QString::number( processingOptions.asDouble("OutputWhiteValue") ) + ";";
  QString endProg = "\
float intToFloat( int v ) \
{ \
  return v / 65535.0; \
} \
int floatToInt( float v ) \
{ \
  if( v > 1.0 ) return 65535; \
  else if( v < 0.0 ) return 0; \
  return v * 65535; \
} \
float adjustLevel( float v ) \
{ \
  if( v < inputBlackValue ) \
  { \
    return outputBlackValue; \
  } else if( v > inputWhiteValue ) { \
    return outputWhiteValue; \
  } \
  float a = (v - inputBlackValue) / (inputWhiteValue - inputBlackValue); \
  a = (outputWhiteValue - outputBlackValue) * pow(a, (1.0 / inputGamma)); \
  return (outputBlackValue + a); \
} \
void apply( int rIn, int gIn, int bIn, output int rOut, output int gOut, output int bOut) \
{ \
  rOut = floatToInt( interpolateCubic1D( redTable, interpolateCubic1D( lightTable, adjustLevel( intToFloat(rIn) ) * exposure ) ) ); \
  gOut = floatToInt( interpolateCubic1D( greenTable, interpolateCubic1D( lightTable, adjustLevel( intToFloat(gIn) ) * exposure ) ) ); \
  bOut = floatToInt( interpolateCubic1D( blueTable, interpolateCubic1D( lightTable, adjustLevel( intToFloat(bIn) ) * exposure ) ) ); \
}";
  
  
  program16Src += endProg;
  
  QMutexLocker l(&Private::mutex );
  OpenCTL::Module p16("apply");
  p16.setSource( program16Src.toAscii ().data());
  kDebug() << "======== Compile OpenCTL's module";
  p16.compile();
  kDebug() << "======== Compilation Finished";
  if(p16.isCompiled())
  {
    kDebug() << "======== Create OpenCTL's program";
    d->program16 = new OpenCTL::Program( "apply", &p16, GTLCore::PixelDescription( GTLCore::Type::UnsignedInteger16, 3));
    kDebug() << "======== Creation Finished";
  } else {
    kDebug() << p16.compilationErrorsMessage().c_str();
    d->program16 = 0;
  }
#else
  d->table = validateList( processingOptions.asPointFList( "LightnessCurve" ) );
  Q_ASSERT( d->table.size() >= 2 );
#endif
#endif
  kDebug() << "PostProcessor has been initialized";
}

PostProcessor::PostProcessor( const PostProcessor& _rhs ) : d(new Private(*_rhs.d))
{
  
}

PostProcessor::~PostProcessor()
{
  kDebug() << "======== Deletion of PostProcessor";
#ifdef HAVE_OPENCTL
  {
    QMutexLocker l(&Private::mutex );
    delete d->program16;
  }
#endif
  delete d;
  kDebug() << "======== Deletion finished";
}

void PostProcessor::setConvertToSRGB( bool _v )
{
  d->convertToSRGB = _v;
}

bool PostProcessor::convertToSRGB() const
{
  return d->convertToSRGB;
}

template< typename T>
inline T clamp( T v, T min, T max )
{
  if ( v > max ) return max;
  else if( v < min ) return min;
  else return v;
}
#ifdef HAVE_OPENCTL

class ExistingBuffer : public GTLCore::Buffer {
  public:
    ExistingBuffer( char* buffer, int size) : m_buffer(buffer), m_size(size)
    {
    }
    virtual char * rawData() { return m_buffer; }
    virtual const char * rawData() const { return m_buffer; }
    virtual int size() const { return m_size; }
  private:
    char* m_buffer;
    int m_size;
};
#endif

void PostProcessor::apply16( QByteArray& _rgb )
{
#ifdef HAVE_OPENCTL
  ExistingBuffer buffer((char*)_rgb.data(), _rgb.size());
  d->program16->apply(buffer,buffer);
  if( d->convertToSRGB )
  {
    ColorManager::instance()->RGB16linearToSRGB16( (quint16*)_rgb.data(), (quint16*)_rgb.data(), _rgb.size() / (3 *sizeof(quint16) ) );
  }
#else
  for( int i = 0; i < _rgb.size(); i += 3 * sizeof(quint16))
  {
    quint16* rgb = (quint16*)(_rgb.data() + i);
    rgb[0] = interpolateCubic1D( d->table, rgb[0] * d->exposure / (double) 0xFFFF) * 0xFFFF;
    rgb[1] = interpolateCubic1D( d->table, rgb[1] * d->exposure / (double) 0xFFFF) * 0xFFFF;
    rgb[2] = interpolateCubic1D( d->table, rgb[2] * d->exposure / (double) 0xFFFF) * 0xFFFF;
    if( d->convertToSRGB )
    {
      ColorManager::instance()->RGB16linearToSRGB16( rgb, rgb, 1 );
    }
  }
#endif
}

Generated by  Doxygen 1.6.0   Back to index