Logo Search packages:      
Sourcecode: darkroom version File versions  Download package

JobExport.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 "JobExport.h"

#include <png.h>

#include <QFileInfo>
#include <qendian.h>

#include <KDebug>
#include <KMessageBox>
#include <KLocale>

#include <threadweaver/ThreadWeaver.h>

#include <libkdcraw/kdcraw.h>
#include <libkdcraw/rawfiles.h>
#include <libkexiv2/kexiv2.h>

#include "ColorManager.h"
#include "PostProcessor.h"
#include "RawImageInfo.h"

using namespace KDcrawIface;

struct JobExport::Private {
  Private(const PostProcessor& _postProcessor) : processor(_postProcessor)
  {
  }
  RawImageInfo* rawImageInfo;
  QString dstFileName;
  RawDecodingSettings settings;
  static ThreadWeaver::Weaver* weaver;
  PostProcessor processor;
};

ThreadWeaver::Weaver* JobExport::Private::weaver = 0;

JobExport::JobExport( RawImageInfo* _rawImageInfo, const QString& _dstFileName, const KDcrawIface::RawDecodingSettings& _settings, const PostProcessor& _processor) : d(new Private(_processor))
{
  d->rawImageInfo = _rawImageInfo;
  d->dstFileName = _dstFileName;
  d->settings = _settings;
}

JobExport::~JobExport()
{
  delete d;
}

ThreadWeaver::Weaver* JobExport::weaver()
{
  if(not Private::weaver)
  {
    Private::weaver = new ThreadWeaver::Weaver;
  }
  return Private::weaver;
}

#define TELL_ERROR( msg ) \
  d->rawImageInfo->setStatus( RawImageInfo::ERROR ); \
  KMessageBox::error( 0, msg, i18n("An error has occured while saving.")); \
  return;

#define COND_TELL_ERROR( cond, msg ) \
  if( not (cond ) ) \
  { \
    TELL_ERROR( msg ); \
  }

long formatStringList(char *string, const size_t length, const char *format, va_list operands)
{
    int n = vsnprintf(string, length, format, operands);

    if (n < 0)
        string[length-1] = '\0';

    return((long) n);
}

long formatString(char *string, const size_t length, const char *format,...)
{
    long n;

    va_list operands;

    va_start(operands,format);
    n = (long) formatStringList(string, length, format, operands);
    va_end(operands);
    return(n);
}

void writeRawProfile(png_struct *ping, png_info *ping_info, QString profile_type, QByteArray profile_data)
{
    
    png_textp      text;

    png_uint_32    allocated_length, description_length;

    const uchar hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};

    kDebug() << "Writing Raw profile: type=" << profile_type << ", length=" << profile_data.length() << endl;

    text               = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text));
    description_length = profile_type.length();
    allocated_length   = (png_uint_32) (profile_data.length()*2 + (profile_data.length() >> 5) + 20 + description_length);

    text[0].text   = (png_charp) png_malloc(ping, allocated_length);

    QString key = "Raw profile type " + profile_type.toLatin1();
    QByteArray keyData = key.toLatin1();
    text[0].key = keyData.data();

    uchar* sp = (uchar*)profile_data.data();
    png_charp dp = text[0].text;
    *dp++='\n';

    memcpy(dp, (const char *) profile_type.toLatin1().data(), profile_type.length());

    dp += description_length;
    *dp++='\n';

    formatString(dp, allocated_length-strlen(text[0].text), "%8lu ", profile_data.length());

    dp += 8;

    for(long i=0; i < (long) profile_data.length(); i++)
    {
        if (i%36 == 0)
            *dp++='\n';

        *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
        *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
    }

    *dp++='\n';
    *dp='\0';
    text[0].text_length = (png_size_t) (dp-text[0].text);
    text[0].compression = -1;

    if (text[0].text_length <= allocated_length)
        png_set_text(ping, ping_info,text, 1);

    png_free(ping, text[0].text);
    png_free(ping, text);
}

void JobExport::run()
{
  d->rawImageInfo->setStatus( RawImageInfo::DECODING );
  QByteArray imageData;
  int width, height, rgbmax;
  KDcrawIface::KDcraw dcraw;  
  if( dcraw.decodeRAWImage( d->rawImageInfo->fileInfo().absoluteFilePath(), d->settings, imageData, width, height, rgbmax) )
  {
    d->rawImageInfo->setStatus( RawImageInfo::SAVING );
    // Now, do the PNG save dance
    FILE *fp = fopen(d->dstFileName.toLatin1(), "wb");
    COND_TELL_ERROR( fp, "Can't open file: " + d->dstFileName);
    
    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
    COND_TELL_ERROR( png_ptr, "Can't initialize libpng." );
    
    // Create info structure
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if(not info_ptr)
    {
      png_destroy_write_struct(&png_ptr, 0);
      fclose(fp);
      TELL_ERROR( "Can't initialize libpng." );
    }
    
    // Set error handler
    if (setjmp(png_jmpbuf(png_ptr))) 
    {
      png_destroy_write_struct(&png_ptr, &info_ptr); 
      fclose(fp); 
      TELL_ERROR( "Can't initialize libpng." );
    }
    
    int bit_depth = d->settings.sixteenBitsImage ? 16 : 8;
    int color_type = PNG_COLOR_TYPE_RGB;
    
    png_set_IHDR( png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    
    png_init_io(png_ptr, fp );
    
    // Save ICC profile
    
    if( d->settings.sixteenBitsImage and not d->processor.convertToSRGB() )
    {
      QByteArray profile = ColorManager::instance()->sRGBLinearProfile();
      png_set_iCCP( png_ptr, info_ptr, "icc", PNG_COMPRESSION_TYPE_BASE, profile.data(), profile.size());
    }
    
    // Save metadata
    
    KExiv2Iface::KExiv2 exiv2;
    if(exiv2.load( d->rawImageInfo->fileInfo().absoluteFilePath()))
    {
      // Save exif
      if(exiv2.hasExif()) {
        QByteArray exifArray = exiv2.getExif(true);
        writeRawProfile(png_ptr, info_ptr, "exif", exifArray);
      }
      // Save IPTC
      if(exiv2.hasIptc()) {
        QByteArray exifArray = exiv2.getIptc(true);
        writeRawProfile(png_ptr, info_ptr, "iptc", exifArray);
      }
      // Save XMP
      if(exiv2.hasXmp()) {
        QByteArray exifArray = exiv2.getXmp();
        writeRawProfile(png_ptr, info_ptr, "xmp", exifArray);
      }
    }
    
    png_write_info(png_ptr, info_ptr);
    
    // Save data
    int pixelSize = ( d->settings.sixteenBitsImage ? sizeof( quint16 ) : sizeof( quint8 ) ) * 3;
    int lineWidth = pixelSize * width;
    
    char* line = new char[lineWidth];
    
    for( int y = 0; y < height; ++y)
    {
      memcpy( line, (imageData.data() + y * lineWidth ), lineWidth );
      for(int i = 0; i < width; ++i)
      {
        if( d->settings.sixteenBitsImage )
        {
          quint16* ptr = (quint16*)(line + i * pixelSize );
          for( int i = 0; i < 3; ++i)
          {
            ptr[i] = qFromBigEndian(ptr[i]);
          }
          d->processor.apply16( ptr );
          for( int i = 0; i < 3; ++i)
          {
            ptr[i] = qToBigEndian(ptr[i]);
          }
        } else {
          d->processor.apply8( (quint8*)(line + i * pixelSize ));
        }
      }
      png_write_row(png_ptr, (png_byte*)line );
    }
    delete[] line;
    png_write_end(png_ptr, info_ptr);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    d->rawImageInfo->setStatus( RawImageInfo::DONE );
  } else {
    d->rawImageInfo->setStatus( RawImageInfo::ERROR );
  }
}

Generated by  Doxygen 1.6.0   Back to index