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

ufraw_writer.c

/*
 * UFRaw - Unidentified Flying Raw converter for digital camera images
 *
 * ufraw_writer.c - functions to output image files in different formats.
 * Copyright 2004-2009 by Udi Fuchs
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>      /* for errno */
#include <string.h>
#include <lcms.h>
#include "uf_glib.h"
#include <glib/gi18n.h>
#ifdef HAVE_LIBTIFF
#include <tiffio.h>
#endif
#ifdef HAVE_LIBJPEG
#include <jpeglib.h>
#include <jerror.h>
#include "iccjpeg.h"
#endif
#ifdef HAVE_LIBPNG
#include <png.h>
#endif

#ifdef _OPENMP
#include <omp.h>
#define uf_omp_get_thread_num() omp_get_thread_num() 
#else
#define uf_omp_get_thread_num() 0
#endif
   
#include "ufraw.h"

#ifdef HAVE_LIBCFITSIO
#include <fitsio.h>
#endif

#define DEVELOP_BATCH 64

static void grayscale_buffer(void *graybuf, int width, int bitDepth)
{
    int i;
    if (bitDepth > 8) {
      guint16 *pixbuf16 = graybuf;
      guint16 *graybuf16 = graybuf;
      for (i = 0; i < width; ++i, ++graybuf16, pixbuf16 += 3)
          *graybuf16 = pixbuf16[1];
    } else {
      guint8 *pixbuf8 = graybuf;
      guint8 *graybuf8 = graybuf;
      for (i = 0; i < width; ++i, ++graybuf8, pixbuf8 += 3)
          *graybuf8 = pixbuf8[1];
    }
}

static int ppm_row_writer(ufraw_data *uf, void *volatile out, void *pixbuf,
    int row, int width, int height, int grayscale, int bitDepth)
{
    (void)row;
    int rowStride = width * (grayscale ? 1 : 3) * (bitDepth>8 ? 2 : 1);
    int i;
    if ( bitDepth>8 ) {
      guint16 *pixbuf16 = (guint16 *)pixbuf;
      for (i=0; i<3*width*height; i++)
          pixbuf16[i] = g_htons(pixbuf16[i]);
    }
    for (i=0; i<height; i++) {
      if ((int)fwrite(pixbuf+i*width*(bitDepth>8?6:3), rowStride, 1, out)<1) {
          ufraw_set_error(uf, _("Error creating file '%s'."),
            uf->conf->outputFilename);
          ufraw_set_error(uf, g_strerror(errno));
          return UFRAW_ERROR;
      }
    }
    return UFRAW_SUCCESS;
}

#ifdef HAVE_LIBTIFF
// There seem to be no way to get the libtiff message without a static variable
// Therefore the folloing code is not thread-safe.
static char ufraw_tiff_message[max_path];

static void tiff_messenger(const char *module, const char *fmt, va_list ap)
{
    (void)module;
    vsnprintf(ufraw_tiff_message, max_path, fmt, ap);
}

int tiff_row_writer(ufraw_data *uf, void *volatile out, void *pixbuf,
    int row, int width, int height, int grayscale, int bitDepth)
{
    (void)grayscale;
    int rowStride = width*( bitDepth>8 ? 6 : 3);
    int i;
    for (i=0; i<height; i++) {
      if (TIFFWriteScanline(out, pixbuf+i*rowStride, row+i, 0)<0) {
          // 'errno' does seem to contain useful information
          ufraw_set_error(uf, _("Error creating file."));
          ufraw_set_error(uf, ufraw_tiff_message);
          ufraw_tiff_message[0] = '\0';
          return UFRAW_ERROR;
      }
    }
    return UFRAW_SUCCESS;
}
#endif /*HAVE_LIBTIFF*/

#ifdef HAVE_LIBJPEG
static void jpeg_warning_handler(j_common_ptr cinfo)
{
    ufraw_data *uf = cinfo->client_data;
    ufraw_set_warning(uf,
          cinfo->err->jpeg_message_table[cinfo->err->msg_code],
          cinfo->err->msg_parm.i[0],
          cinfo->err->msg_parm.i[1],
          cinfo->err->msg_parm.i[2],
          cinfo->err->msg_parm.i[3]);
}

static void jpeg_error_handler(j_common_ptr cinfo)
{
    /* We ignore the SOI error if second byte is 0xd8 since Minolta's
     * SOI is known to be wrong */
    ufraw_data *uf = cinfo->client_data;
    if (cinfo->err->msg_code==JERR_NO_SOI &&
      cinfo->err->msg_parm.i[1]==0xd8) {
          ufraw_set_info(uf,
                cinfo->err->jpeg_message_table[cinfo->err->msg_code],
                cinfo->err->msg_parm.i[0],
                cinfo->err->msg_parm.i[1],
                cinfo->err->msg_parm.i[2],
                cinfo->err->msg_parm.i[3]);
      return;
    }
    ufraw_set_error(uf,
          cinfo->err->jpeg_message_table[cinfo->err->msg_code],
          cinfo->err->msg_parm.i[0],
          cinfo->err->msg_parm.i[1],
          cinfo->err->msg_parm.i[2],
          cinfo->err->msg_parm.i[3]);
}

static int jpeg_row_writer(ufraw_data *uf, void *volatile out, void *pixbuf,
    int row, int width, int height, int grayscale, int bitDepth)
{
    (void)row;
    (void)grayscale;
    (void)bitDepth;
    int i;
    for (i=0; i<height; i++) {
      guint8 *pixbuf8 = pixbuf + 3*width*i;
      jpeg_write_scanlines((struct jpeg_compress_struct *)out, &pixbuf8, 1);
      if ( ufraw_is_error(uf) )
          return UFRAW_ERROR;
    }
    return UFRAW_SUCCESS;
}
#endif /*HAVE_LIBJPEG*/

#ifdef HAVE_LIBPNG
static void png_error_handler(png_structp png,
    png_const_charp error_msg)
{
    ufraw_data *uf = png_get_error_ptr(png);
    ufraw_set_error(uf, "%s: %s.", error_msg, g_strerror(errno));
    longjmp(png_jmpbuf(png), 1);
}

static void png_warning_handler(png_structp png,
    png_const_charp warning_msg)
{
    ufraw_data *uf = png_get_error_ptr(png);
    ufraw_set_warning(uf, "%s.", warning_msg);
}

static void PNGwriteRawProfile(png_struct *ping,
    png_info *ping_info, char *profile_type, guint8 *profile_data,
    png_uint_32 length);

int png_row_writer(ufraw_data *uf, void *volatile out, void *pixbuf,
    int row, int width, int height, int grayscale, int bitDepth)
{
    (void)uf;
    (void)row;
    (void)grayscale;
    int rowStride = width * (bitDepth>8 ? 6 : 3);

    int i;
    for (i=0; i<height; i++)
      png_write_row(out, (guint8 *)pixbuf + rowStride*i);

    return UFRAW_SUCCESS;
}
#endif /*HAVE_LIBPNG*/

void ufraw_write_image_data(
    ufraw_data *uf,
    void * volatile out,
    int width, int height, int left, int top, int bitDepth, int grayscaleMode,
    int (*row_writer) (ufraw_data *, void * volatile, void *, int, int, int, int, int))
{
    int row, row0;
    int rowStride = uf->Images[ufraw_first_phase].width;
    image_type *rawImage = (image_type *)uf->Images[ufraw_first_phase].buffer;
    int byteDepth = (bitDepth+7)/8;
    guint8 pixbuf8[width * 3 * byteDepth * DEVELOP_BATCH];

    if (uf->conf->rotationAngle != 0) {
      // Buffer for unrotated image.
      ufraw_image_data image;
      image.width = uf->Images[ufraw_first_phase].width;
      image.height = uf->Images[ufraw_first_phase].height;
      image.depth = 6;
      image.rowstride = image.width * image.depth;
      image.buffer = g_new(guint8, image.height * image.rowstride);
      // Develop complete raw image into buffer.
#ifdef _OPENMP
#pragma omp parallel for schedule(static,1) default(shared) private(row0)
#endif
      for (row0 = 0; row0 < image.height; row0 += DEVELOP_BATCH) {
          if (uf_omp_get_thread_num() == 0)
            preview_progress(uf->widget, _("Converting image"),
                0.5 * row0/image.height);
          guint8 *rowbuf = &image.buffer[row0*image.rowstride];
          develop(rowbuf, rawImage[row0*rowStride],
                 uf->developer, 16,
                 MIN(image.height - row0, DEVELOP_BATCH) * image.width);
      }
      // Write rotated image to output.
      for (row0 = 0; row0 < height; row0 += DEVELOP_BATCH) {
          preview_progress(uf->widget, _("Saving image"),
            0.5 + 0.5*row0/height);
#ifdef _OPENMP
#pragma omp parallel for default(shared) private(row)
#endif
          for (row = 0; row < DEVELOP_BATCH; row++) {
            if (row + row0 >= height)
                continue;
            guint8 *rowbuf = &pixbuf8[row * width * 3 * byteDepth];
            ufraw_rotate_row(&image, rowbuf, uf->conf->rotationAngle,
                bitDepth, top+row+row0, left, width);
            if (grayscaleMode)
                grayscale_buffer(rowbuf, width, bitDepth);
          }
          int batchHeight = MIN(height-row0, DEVELOP_BATCH);
          if ( row_writer(uf, out, pixbuf8, row0, width, batchHeight,
                grayscaleMode, bitDepth) != UFRAW_SUCCESS )
            break;
      }
      g_free(image.buffer);
    } else {
      // No rotation required. Develop straight to output.
      for (row0 = 0; row0 < height; row0 += DEVELOP_BATCH) {
          preview_progress(uf->widget, _("Saving image"),
            0.5 + 0.5*row0/height);
#ifdef _OPENMP
#pragma omp parallel for default(shared) private(row)
#endif
          for (row = 0; row < DEVELOP_BATCH; row++) {
            if (row + row0 >= height)
                continue;
            guint8 *rowbuf = &pixbuf8[row * width * 3 * byteDepth];
            develop(rowbuf, rawImage[(top+row+row0)*rowStride+left],
                  uf->developer, bitDepth, width);
            if (grayscaleMode)
                grayscale_buffer(rowbuf, width, bitDepth);
          }
          int batchHeight = MIN(height-row0, DEVELOP_BATCH);
          if ( row_writer(uf, out, pixbuf8, row0, width, batchHeight,
                grayscaleMode, bitDepth) != UFRAW_SUCCESS )
            break;
      }
    }
}

int ufraw_write_image(ufraw_data *uf)
{
    /* 'volatile' supresses clobbering warning */
    void * volatile out; /* out is a pointer to FILE or TIFF */
#ifdef HAVE_LIBCFITSIO
    fitsfile *fitsFile;
#endif
    int width, height, left, top;
    char * volatile confFilename=NULL;
    int grayscaleMode = uf->conf->grayscaleMode != grayscale_none;
    ufraw_message_reset(uf);

    if ( uf->conf->createID==only_id ||
          uf->conf->createID==also_id) {
      confFilename = uf_file_set_type(uf->conf->outputFilename, ".ufraw");
      if (!strcmp(confFilename, uf->conf->outputFilename)) {
          ufraw_set_error(uf, _("Image filename can not be the "
                "same as ID filename '%s'"), confFilename);
          g_free(confFilename);
          return ufraw_get_status(uf);
      }
    }
    if (uf->conf->createID==only_id) {
      int status = conf_save(uf->conf, confFilename, NULL);
      g_free(confFilename);
      return status;
    }
#ifdef HAVE_LIBTIFF
    if ( uf->conf->type==tiff_type ) {
      TIFFSetErrorHandler(tiff_messenger);
      TIFFSetWarningHandler(tiff_messenger);
      ufraw_tiff_message[0] = '\0';
      if (!strcmp(uf->conf->outputFilename, "-")) {
          out = TIFFFdOpen(fileno((FILE *)stdout),
                uf->conf->outputFilename, "w");
      } else {
          char *filename =
            uf_win32_locale_filename_from_utf8(uf->conf->outputFilename);
          out = TIFFOpen(filename, "w");
          uf_win32_locale_filename_free(filename);
      }
      if (out==NULL ) {
          ufraw_set_error(uf, _("Error creating file."));
          ufraw_set_error(uf, ufraw_tiff_message);
          ufraw_set_error(uf, g_strerror(errno));
          ufraw_tiff_message[0] = '\0';
          return ufraw_get_status(uf);
      }
    } else
#endif
#ifdef HAVE_LIBCFITSIO
    if ( uf->conf->type==fits_type ) {
      if ( strcmp(uf->conf->outputFilename, "-")!=0 ) {
          if ( g_file_test(uf->conf->outputFilename, G_FILE_TEST_EXISTS) ) {
            if ( g_unlink(uf->conf->outputFilename) ) {
                ufraw_set_error(uf, _("Error creating file '%s'."),
                      uf->conf->outputFilename);
                ufraw_set_error(uf, g_strerror(errno));
                return ufraw_get_status(uf);
            }
          }
      }
      int status = 0;
      char *filename =
            uf_win32_locale_filename_from_utf8(uf->conf->outputFilename);
      fits_create_file(&fitsFile, filename, &status);
      uf_win32_locale_filename_free(filename);
      if ( status ) {
          ufraw_set_error(uf, _("Error creating file '%s'."),
                uf->conf->outputFilename);
          char errBuffer[max_name];
          fits_get_errstatus(status, errBuffer);
          ufraw_set_error(uf, errBuffer);
          while (fits_read_errmsg(errBuffer))
            ufraw_set_error(uf, errBuffer);
          return ufraw_get_status(uf);
      }
    } else
#endif
    {
      if (!strcmp(uf->conf->outputFilename, "-")) {
          out = stdout;
      } else {
          if ( (out=g_fopen(uf->conf->outputFilename, "wb"))==NULL) {
            ufraw_set_error(uf, _("Error creating file '%s'."),
                  uf->conf->outputFilename);
            ufraw_set_error(uf, g_strerror(errno));
            return ufraw_get_status(uf);
          }
      }
    }
    // TODO: error handling
    ufraw_convert_image(uf);
    ufraw_image_data *FirstImage = &uf->Images[ufraw_first_phase];
    left = uf->conf->CropX1 * FirstImage->width / uf->initialWidth;
    top = uf->conf->CropY1 * FirstImage->height / uf->initialHeight;
    volatile int BitDepth = uf->conf->profile[out_profile]
                  [uf->conf->profileIndex[out_profile]].BitDepth;
    if ( BitDepth!=16 ) BitDepth = 8;
    width = (uf->conf->CropX2 - uf->conf->CropX1)
          * FirstImage->width / uf->initialWidth;
    height = (uf->conf->CropY2 - uf->conf->CropY1)
          * FirstImage->height / uf->initialHeight;
    if ( uf->conf->type==ppm_type && BitDepth==8 ) {
      fprintf(out, "P%c\n%d %d\n%d\n",
            grayscaleMode ? '5' : '6', width, height, 0xFF);
      ufraw_write_image_data(uf, out, width, height, left, top,
                   BitDepth, grayscaleMode, ppm_row_writer);
    } else if ( uf->conf->type==ppm_type && BitDepth==16 ) {
      fprintf(out, "P%c\n%d %d\n%d\n",
            grayscaleMode ? '5' : '6', width, height, 0xFFFF);
      ufraw_write_image_data(uf, out, width, height, left, top,
                   BitDepth, grayscaleMode, ppm_row_writer);
#ifdef HAVE_LIBTIFF
    } else if ( uf->conf->type==tiff_type ) {
      TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
      TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
      TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
      TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, grayscaleMode ? 1 : 3);
      TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitDepth);
      TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
      TIFFSetField(out, TIFFTAG_PHOTOMETRIC, grayscaleMode
                 ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB);
#ifdef HAVE_LIBZ
      if (uf->conf->losslessCompress) {
          TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE);
          TIFFSetField(out, TIFFTAG_ZIPQUALITY, 9);
          TIFFSetField(out, TIFFTAG_PREDICTOR, 2);
      }
      else
#endif
          TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
      /* Embed output profile if it is not the internal sRGB. */
      if (strcmp(uf->developer->profileFile[out_profile], "")) {
          char *buf;
          gsize len;
          if ( g_file_get_contents(uf->developer->profileFile[out_profile],
                  &buf, &len, NULL)) {
            TIFFSetField(out, TIFFTAG_ICCPROFILE, len, buf);
            g_free(buf);
          } else {
            ufraw_set_warning(uf,
                  _("Failed to embed output profile '%s' in '%s'."),
                  uf->developer->profileFile[out_profile],
                  uf->conf->outputFilename);
          }
      } else if (uf->conf->profileIndex[out_profile]==1) { // Embed sRGB.
          cmsHPROFILE hOutProfile = cmsCreate_sRGBProfile();
          gsize len = 0;
          _cmsSaveProfileToMem(hOutProfile, 0, &len); // Calculate len.
          if (len > 0) {
            unsigned char buf[len];
            _cmsSaveProfileToMem(hOutProfile, buf, &len);
            TIFFSetField(out, TIFFTAG_ICCPROFILE, len, buf);
          } else {
                ufraw_set_warning(uf,
                    _("Failed to embed output profile '%s' in '%s'."),
                uf->conf->profile[out_profile]
                      [uf->conf->profileIndex[out_profile]].name,
                    uf->conf->outputFilename);
          }
          cmsCloseProfile(hOutProfile);
      }
      TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, 0));

      ufraw_write_image_data(uf, out, width, height, left, top,
                   BitDepth, grayscaleMode, tiff_row_writer);

#endif /*HAVE_LIBTIFF*/
#ifdef HAVE_LIBJPEG
    } else if (uf->conf->type==jpeg_type) {
      if ( BitDepth!=8 )
            ufraw_set_warning(uf,
                  _("Unsupported bit depth '%d' ignored."), BitDepth);
      struct jpeg_compress_struct cinfo;
      struct jpeg_error_mgr jerr;

      cinfo.err = jpeg_std_error(&jerr);
      cinfo.err->output_message = jpeg_warning_handler;
      cinfo.err->error_exit = jpeg_error_handler;
      cinfo.client_data = uf;
      jpeg_create_compress(&cinfo);
      jpeg_stdio_dest(&cinfo, out);
      cinfo.image_width = width;
      cinfo.image_height = height;
      if (grayscaleMode) {
          cinfo.input_components = 1;
          cinfo.in_color_space = JCS_GRAYSCALE;
      }
      else {
          cinfo.input_components = 3;
          cinfo.in_color_space = JCS_RGB;
      }
      jpeg_set_defaults(&cinfo);
      jpeg_set_quality(&cinfo, uf->conf->compression, TRUE);
      if ( uf->conf->compression>90 )
          cinfo.comp_info[0].v_samp_factor = 1;
      if ( uf->conf->compression>92 )
          cinfo.comp_info[0].h_samp_factor = 1;
      if (uf->conf->progressiveJPEG)
          jpeg_simple_progression(&cinfo);

      cinfo.optimize_coding = 1;

      jpeg_start_compress(&cinfo, TRUE);

      /* Embed output profile if it is not the internal sRGB. */
      if (strcmp(uf->developer->profileFile[out_profile], "")) {
          char *buf;
          gsize len;
          if (g_file_get_contents(uf->developer->profileFile[out_profile],
                &buf, &len, NULL)) {
            write_icc_profile(&cinfo, (unsigned char *)buf, len);
            g_free(buf);
          } else {
            ufraw_set_warning(uf,
                  _("Failed to embed output profile '%s' in '%s'."),
                  uf->developer->profileFile[out_profile],
                  uf->conf->outputFilename);
          }
      } else if (uf->conf->profileIndex[out_profile]==1) { // Embed sRGB.
          cmsHPROFILE hOutProfile = cmsCreate_sRGBProfile();
          gsize len = 0;
          _cmsSaveProfileToMem(hOutProfile, 0, &len); // Calculate len.
          if (len > 0) {
            unsigned char buf[len];
            _cmsSaveProfileToMem(hOutProfile, buf, &len);
            write_icc_profile(&cinfo, buf, len);
          } else {
                ufraw_set_warning(uf,
                    _("Failed to embed output profile '%s' in '%s'."),
                uf->conf->profile[out_profile]
                      [uf->conf->profileIndex[out_profile]].name,
                    uf->conf->outputFilename);
          }
          cmsCloseProfile(hOutProfile);
      }
      if ( uf->conf->embedExif ) {
          ufraw_exif_prepare_output(uf);
          if ( uf->outputExifBuf!=NULL ) {
            if (uf->outputExifBufLen>65533) {
                ufraw_set_warning(uf,
                      _("EXIF buffer length %d, too long, ignored."),
                      uf->outputExifBufLen);
            } else {
                jpeg_write_marker(&cinfo, JPEG_APP0+1,
                      uf->outputExifBuf, uf->outputExifBufLen);
            }
          }
      }

      ufraw_write_image_data(uf, &cinfo, width, height, left, top,
                   8, grayscaleMode, jpeg_row_writer);

      if ( ufraw_is_error(uf) ) {
          char *message = g_strdup(ufraw_get_message(uf));
          ufraw_message_reset(uf);
          ufraw_set_error(uf, _("Error creating file '%s'."),
                uf->conf->outputFilename);
          ufraw_set_error(uf, message);
          g_free(message);
      } else
          jpeg_finish_compress(&cinfo);
      jpeg_destroy_compress(&cinfo);
#endif /*HAVE_LIBJPEG*/
#ifdef HAVE_LIBPNG
    } else if ( uf->conf->type==png_type ) {
      png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
            uf, png_error_handler, png_warning_handler);
      png_infop info = png_create_info_struct(png);
      if ( setjmp(png_jmpbuf(png)) ) {
          char *message = g_strdup(ufraw_get_message(uf));
          ufraw_message_reset(uf);
          ufraw_set_error(uf, _("Error creating file '%s'."),
                uf->conf->outputFilename);
          ufraw_set_error(uf, message);
          g_free(message);
          png_destroy_write_struct(&png, &info);
      } else {
          png_init_io(png, out);
          png_set_IHDR(png, info, width, height, BitDepth,
                grayscaleMode ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB,
                PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                PNG_FILTER_TYPE_BASE);
          png_set_compression_level(png, Z_BEST_COMPRESSION);
          png_text text[2];
          text[0].compression = PNG_TEXT_COMPRESSION_NONE;
          text[0].key = "Software";
          text[0].text = "UFRaw";
          text[1].compression = PNG_TEXT_COMPRESSION_NONE;
          text[1].key = "Source";
          text[1].text = g_strdup_printf("%s%s",
                uf->conf->make, uf->conf->model);
          png_set_text(png, info, text, 2);
          g_free(text[1].text);
          /* Embed output profile if it is not the internal sRGB. */
          if (strcmp(uf->developer->profileFile[out_profile], "")) {
            char *buf;
            gsize len;
            if (g_file_get_contents(uf->developer->profileFile[out_profile],
                  &buf, &len, NULL)) {
                png_set_iCCP(png, info,
                      uf->developer->profileFile[out_profile],
                      PNG_COMPRESSION_TYPE_BASE,
                      buf, len);
                g_free(buf);
            } else {
                ufraw_set_warning(uf,
                  _("Failed to embed output profile '%s' in '%s'."),
                  uf->developer->profileFile[out_profile],
                  uf->conf->outputFilename);
            }
          } else if (uf->conf->profileIndex[out_profile]==1) { // Embed sRGB.
            cmsHPROFILE hOutProfile = cmsCreate_sRGBProfile();
            gsize len = 0;
            _cmsSaveProfileToMem(hOutProfile, 0, &len); // Calculate len.
            if (len > 0) {
                char buf[len];
                _cmsSaveProfileToMem(hOutProfile, buf, &len);
                png_set_iCCP(png, info,
                      uf->conf->profile[out_profile]
                            [uf->conf->profileIndex[out_profile]].name,
                      PNG_COMPRESSION_TYPE_BASE,
                      buf, len);
            } else {
                ufraw_set_warning(uf,
                  _("Failed to embed output profile '%s' in '%s'."),
                  uf->conf->profile[out_profile]
                        [uf->conf->profileIndex[out_profile]].name,
                  uf->conf->outputFilename);
            }
            cmsCloseProfile(hOutProfile);
          }
          if ( uf->conf->embedExif ) {
            ufraw_exif_prepare_output(uf);
            if ( uf->outputExifBuf!=NULL )
                PNGwriteRawProfile(png, info, "exif",
                      uf->outputExifBuf, uf->outputExifBufLen);
          }
          png_write_info(png, info);
          if (BitDepth != 8 && G_BYTE_ORDER==G_LITTLE_ENDIAN )
            png_set_swap(png); // Swap byte order to big-endian

          ufraw_write_image_data(uf, png, width, height, left, top,
                       BitDepth, grayscaleMode, png_row_writer);

          png_write_end(png, NULL);
          png_destroy_write_struct(&png, &info);
      }
#endif /*HAVE_LIBPNG*/
#ifdef HAVE_LIBCFITSIO
    } else if ( uf->conf->type==fits_type ) {

      // image data and min/max values
      guint16 *image;
      guint16 max[3] = { 0, 0, 0 }, min[3] = { 65535, 65535, 65535 };
      guint64 sum[3] = { 0, 0, 0 };

      // FITS Header (taken from cookbook.c)
      int bitpix = USHORT_IMG;    // Use float format
      int naxis  = 3;             // 3-dimensional image
      int status = 0;             // status variable for fitsio

      long naxes[3]  = { width, height, 3 };
      long dim = width * height;
      long offset = 0;

      image = g_new(guint16, 3 * dim);

      int row;
      int i;
      image_type *rawImage =
            (image_type *)uf->Images[ufraw_first_phase].buffer;
      int rowStride = uf->Images[ufraw_first_phase].width;
      guint16 pixbuf16[3];

      // Avoid FITS images being saved upside down
      ufraw_flip_image(uf, 2);

      for (row=0; row<height; row++) {
          if (row%100==99)
            preview_progress(uf->widget, _("Saving image"),
                  0.5 + 0.5*row/height);
          for (i=0; i < width; i++)
          {
            offset = row*width + i;
            develop_linear(rawImage[(top+row)*rowStride+left+i], pixbuf16,
                  uf->developer);
            int c;
            for (c=0; c<3; c++) {
                sum[c] += image[c*dim + offset] = pixbuf16[c];
                max[c] = MAX(pixbuf16[c], max[c]);
                min[c] = MIN(pixbuf16[c], min[c]);
            }
          }
      }
      // calculate averages
      float average[3];
      int c;
      for (c=0; c<3; c++)
          average[c] = (float)sum[c] / dim;

      guint16 maxAll = MAX(MAX(max[0],max[1]),max[2]);
      guint16 minAll = MIN(MIN(min[0],min[1]),min[2]);

      fits_create_img(fitsFile, bitpix, naxis, naxes, &status);

      fits_write_img(fitsFile, TUSHORT, 1, 3*dim, image, &status);
      g_free(image);

      fits_update_key(fitsFile, TUSHORT, "DATAMIN", &minAll,
            "minimum data (overall)", &status);
      fits_update_key(fitsFile, TUSHORT, "DATAMAX", &maxAll,
            "maximum data (overall)", &status);

      fits_update_key(fitsFile, TUSHORT, "DATAMINR", &min[0],
            "minimum data (red channel)", &status);
      fits_update_key(fitsFile, TUSHORT, "DATAMAXR", &max[0],
            "maximum data (red channel)", &status);

      fits_update_key(fitsFile, TUSHORT, "DATAMING", &min[1],
            "minimum data (green channel)", &status);
      fits_update_key(fitsFile, TUSHORT, "DATAMAXG", &max[1],
            "maximum data (green channel)", &status);

      fits_update_key(fitsFile, TUSHORT, "DATAMINB", &min[2],
            "minimum data (blue channel)", &status);
      fits_update_key(fitsFile, TUSHORT, "DATAMAXB", &max[2],
            "maximum data (blue channel)", &status);

      fits_update_key(fitsFile, TFLOAT, "AVERAGER", &average[0],
            "average (red channel)", &status);
      fits_update_key(fitsFile, TFLOAT, "AVERAGEG", &average[1],
            "average (green channel)", &status);
      fits_update_key(fitsFile, TFLOAT, "AVERAGEB", &average[2],
            "average (blue channel)", &status);

      // Save known EXIF properties
      if ( strlen(uf->conf->shutterText) > 0 )
          fits_update_key(fitsFile, TSTRING, "EXPOSURE",
                &uf->conf->shutterText, "Exposure Time", &status);

      if ( strlen(uf->conf->isoText) > 0 )
          fits_update_key(fitsFile, TSTRING, "ISO", &uf->conf->isoText,
                "ISO Speed", &status);

      if (strlen(uf->conf->apertureText) > 0 )
          fits_update_key(fitsFile, TSTRING, "APERTURE",
                &uf->conf->apertureText, "Aperture", &status);

      if (strlen(uf->conf->focalLenText) > 0 )
          fits_update_key(fitsFile, TSTRING, "FOCALLEN",
                &uf->conf->focalLenText, "Focal Length", &status);

      if (strlen(uf->conf->focalLen35Text) > 0 )
          fits_update_key(fitsFile, TSTRING, "FOCALLE2",
                &uf->conf->focalLen35Text, "Focal Length (resp. 35mm)",
                &status);

      if (strlen(uf->conf->lensText) > 0 )
          fits_update_key(fitsFile, TSTRING, "LENS",
                &uf->conf->lensText, "Lens", &status);

      // formating the date according to the FITS standard
      // http://archive.stsci.edu/fits/fits_standard/node40.html#s:dhist
      if (uf->conf->timestamp != 0 ) {
          char *time = g_new(char, 40);
          struct tm tmStamp;
          strftime(time, 40, "%Y-%m-%dT%H:%M:%S",
                localtime_r(&uf->conf->timestamp, &tmStamp));
          fits_update_key(fitsFile, TSTRING, "DATE", time,
                "Image taken at this date", &status);
          g_free(time);
      }

      if (strlen(uf->conf->make) > 0 )
          fits_update_key(fitsFile, TSTRING, "MANUFACT", &uf->conf->make,
                "Camera Manufacturer", &status);

      if (strlen(uf->conf->model) > 0 )
          fits_update_key(fitsFile, TSTRING, "INSTRUME", &uf->conf->model,
                "Camera Model", &status);

      fits_write_comment(fitsFile, "This file contains one RGB color image.",
            &status);

      // Creator Ufraw
      fits_update_key(fitsFile, TSTRING, "CREATOR",  "UFRaw " VERSION,
            "Creator Software", &status);

      fits_close_file(fitsFile, &status);

      if ( status ) {
          ufraw_set_error(uf, _("Error creating file '%s'."),
                uf->conf->outputFilename);
          char errBuffer[max_name];
          fits_get_errstatus(status, errBuffer);
          ufraw_set_error(uf, errBuffer);
          while (fits_read_errmsg(errBuffer))
            ufraw_set_error(uf, errBuffer);
          return ufraw_get_status(uf);
      }
#endif /* HAVE_LIBCFITSIO */
    } else {
      ufraw_set_error(uf, _("Error creating file '%s'."),
            uf->conf->outputFilename);
      ufraw_set_error(uf, _("Unknown file type %d."), uf->conf->type);
    }
#ifdef HAVE_LIBTIFF
    if ( uf->conf->type==tiff_type ) {
      TIFFClose(out);
      if ( ufraw_tiff_message[0]!='\0' ) {
          if ( !ufraw_is_error(uf) ) { // Error was not already set before
            ufraw_set_error(uf, _("Error creating file."));
            ufraw_set_error(uf, ufraw_tiff_message);
          }
          ufraw_tiff_message[0] = '\0';
      } else { 
          if ( uf->conf->embedExif )
            ufraw_exif_write(uf);
      }
    } else
#endif
#ifdef HAVE_LIBCFITSIO
    // Dummy to prevent fclose
    if ( uf->conf->type==fits_type ) {}
    else
#endif
    {
      if (strcmp(uf->conf->outputFilename, "-"))
          if ( fclose(out)!=0 ) {
            if ( !ufraw_is_error(uf) ) { // Error was not already set before
                ufraw_set_error(uf, _("Error creating file '%s'."),
                      uf->conf->outputFilename);
                ufraw_set_error(uf, g_strerror(errno));
            }
          }
    }
    if (uf->conf->createID==also_id) {
      if ( ufraw_get_message(uf)!=NULL )
          ufraw_message(UFRAW_SET_LOG, ufraw_get_message(uf));
      // TODO: error handling
      conf_save(uf->conf, confFilename, NULL);
      g_free(confFilename);
    }
    return ufraw_get_status(uf);
}


/* Write EXIF data to PNG file.
 * Code copied from DigiKam's libs/dimg/loaders/pngloader.cpp.
 * The EXIF embeding is defined by ImageMagicK.
 * It is documented in the ExifTool page:
 * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html
 */

#ifdef HAVE_LIBPNG
static void PNGwriteRawProfile(png_struct *ping,
    png_info *ping_info, char *profile_type, guint8 *profile_data,
    png_uint_32 length)
{
    png_textp text;
    long i;
    guint8 *sp;
    png_charp dp;
    png_uint_32 allocated_length, description_length;

    const guint8 hex[16] =
          {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    text = png_malloc(ping, sizeof(png_text));
    description_length = strlen(profile_type);
    allocated_length = length*2 + (length >> 5) + 20 + description_length;

    text[0].text = png_malloc(ping, allocated_length);
    text[0].key = png_malloc(ping, 80);
    text[0].key[0] = '\0';

    g_strlcat(text[0].key, "Raw profile type ", 80);
    g_strlcat(text[0].key, profile_type, 80);

    sp = profile_data;
    dp = text[0].text;
    *dp++='\n';

    g_strlcpy(dp, profile_type, allocated_length);

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

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

    dp += 8;

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

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

    *dp++='\n';
    *dp='\0';
    text[0].text_length = (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[0].key);
    png_free(ping, text);
}
#endif /*HAVE_LIBPNG*/

Generated by  Doxygen 1.6.0   Back to index