Logo Search packages:      
Sourcecode: darkroom version File versions

PngExportCodec.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 "codecs/PngExportCodec.h"

#include <png.h>

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

#include <KDebug>
#include <KMessageBox>

#include <libkexiv2/kexiv2.h>

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

#include "ui_PngOptions.h"

struct PngExportCodec::Private {
  Ui::PngOptions pngOptions;
};

PngExportCodec::PngExportCodec() : ExportCodec("PNG", i18n("Png"), "png"), d(new Private)
{
  QWidget* widget = new QWidget;
  d->pngOptions.setupUi( widget );
  connect( d->pngOptions.sixteenBitsDepth, SIGNAL(clicked(bool)), SIGNAL(optionsChanged()));
  connect( d->pngOptions.interlacing, SIGNAL(clicked(bool)), SIGNAL(optionsChanged()));
  connect( d->pngOptions.compressionLevel, SIGNAL(valueChanged(int)), SIGNAL(optionsChanged()));
  setConfigurationWidget( widget );
}

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

void PngExportCodec::fillProcessingOptions( ProcessingOptions* processingOptions ) const
{
  processingOptions->setOption( "SixteenBits", d->pngOptions.sixteenBitsDepth->isChecked() );
  processingOptions->setOption( "PngInterlacing", d->pngOptions.interlacing->isChecked() );
  processingOptions->setOption( "PngCompress", d->pngOptions.compressionLevel->value() );
}

void PngExportCodec::setProcessingOptions( const ProcessingOptions& _processingOptions ) const
{
  d->pngOptions.sixteenBitsDepth->setChecked( _processingOptions.asBool( "SixteenBits") );
  d->pngOptions.interlacing->setChecked( _processingOptions.asBool("PngInterlacing") );
  d->pngOptions.compressionLevel->setValue( _processingOptions.asInteger("PngCompress") );
}

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);
}

#define TELL_ERROR( msg ) \
  KMessageBox::error( 0, msg, i18n("An error has occured while saving.")); \
  return false;

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

bool PngExportCodec::writeFile( RawImageInfoSP rawImageInfo, const QString& _fileName, const QByteArray& _imageData, int width, int height, const ProcessingOptions& processingOptions )
{
  // Now, do the PNG save dance
  FILE *fp = fopen(_fileName.toLatin1(), "wb");
  COND_TELL_ERROR( fp, "Can't open file: " + _fileName);
  
  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." );
  }
  
  bool sixteenBitsImage = processingOptions.asBool("SixteenBits");
  
  int bit_depth = sixteenBitsImage ? 16 : 8;
  int color_type = PNG_COLOR_TYPE_RGB;
  
  int interlacetype = processingOptions.asBool("PngInterlacing") ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;

  png_set_IHDR( png_ptr, info_ptr, width, height, bit_depth, color_type, interlacetype, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  png_set_compression_level(png_ptr, processingOptions.asInteger("PngCompress"));
 
  png_init_io(png_ptr, fp );
  
  // Save ICC profile
  
  if( not processingOptions.asBool("ConvertToSRGB") )
  {
    QByteArray profile = ColorManager::instance()->sRGBLinearProfile();
    png_set_iCCP( png_ptr, info_ptr, "icc", PNG_COMPRESSION_TYPE_BASE, profile.data(), profile.size());
  }
  
/*  // Save metadata
  {
    QMutexLocker l( ExportCodec::exiv2Mutex() );
    KExiv2Iface::KExiv2 exiv2;
    if(exiv2.load( rawImageInfo->fileInfo().absoluteFilePath()))
    {
      exiv2.setImageOrientation( KExiv2Iface::KExiv2::ORIENTATION_NORMAL );
      // 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 = sizeof( quint16 ) * 3;
  int lineWidth = pixelSize * width;
  
  QByteArray line;
  line.resize( lineWidth);
  PostProcessor processor(processingOptions);
  
  for( int y = 0; y < height; ++y)
  {
    memcpy( line.data(), (_imageData.data() + y * lineWidth ), lineWidth );
    for(int i = 0; i < width; ++i)
    {
      quint16* ptr = (quint16*)(line.data() + i * pixelSize );
      for( int i = 0; i < 3; ++i)
      {
        ptr[i] = qFromBigEndian(ptr[i]);
      }
    }
    processor.apply16( line );
    for(int i = 0; i < width; ++i)
    {
      quint16* ptr = (quint16*)(line.data() + i * pixelSize );
      for( int i = 0; i < 3; ++i)
      {
        ptr[i] = qToBigEndian(ptr[i]);
      }
    }
    if( not sixteenBitsImage )
    {
      quint16* srcIt = (quint16*)line.data();
      quint8* dstIt = (quint8*)line.data();
      for( int i = 0; i < 3 * width; ++i)
      {
        dstIt[ i ] = srcIt[ i ] & 0xFF;
      }
          
    }
    png_write_row(png_ptr, (png_byte*)line.data() );
  }
  png_write_end(png_ptr, info_ptr);
  png_destroy_write_struct(&png_ptr, &info_ptr);
  fclose(fp);
  return true;
}

Generated by  Doxygen 1.6.0   Back to index