Exemplo n.º 1
0
int dt_imageio_jpeg_write_with_icc_profile(const char *filename, const uint8_t *in, const int width,
                                           const int height, const int quality, const void *exif, int exif_len,
                                           int imgid)
{
  struct dt_imageio_jpeg_error_mgr jerr;
  dt_imageio_jpeg_t jpg;

  jpg.cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
  if(setjmp(jerr.setjmp_buffer))
  {
    jpeg_destroy_compress(&(jpg.cinfo));
    return 1;
  }
  jpeg_create_compress(&(jpg.cinfo));
  FILE *f = fopen(filename, "wb");
  if(!f) return 1;
  jpeg_stdio_dest(&(jpg.cinfo), f);

  jpg.cinfo.image_width = width;
  jpg.cinfo.image_height = height;
  jpg.cinfo.input_components = 3;
  jpg.cinfo.in_color_space = JCS_RGB;
  jpeg_set_defaults(&(jpg.cinfo));
  jpeg_set_quality(&(jpg.cinfo), quality, TRUE);
  if(quality > 90) jpg.cinfo.comp_info[0].v_samp_factor = 1;
  if(quality > 92) jpg.cinfo.comp_info[0].h_samp_factor = 1;
  jpeg_start_compress(&(jpg.cinfo), TRUE);

  if(imgid > 0)
  {
    cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid)->profile;
    uint32_t len = 0;
    cmsSaveProfileToMem(out_profile, 0, &len);
    if(len > 0)
    {
      unsigned char buf[len];
      cmsSaveProfileToMem(out_profile, buf, &len);
      write_icc_profile(&(jpg.cinfo), buf, len);
    }
  }

  if(exif && exif_len > 0 && exif_len < 65534) jpeg_write_marker(&(jpg.cinfo), JPEG_APP0 + 1, exif, exif_len);

  uint8_t row[3 * width];
  const uint8_t *buf;
  while(jpg.cinfo.next_scanline < jpg.cinfo.image_height)
  {
    JSAMPROW tmp[1];
    buf = in + jpg.cinfo.next_scanline * jpg.cinfo.image_width * 4;
    for(int i = 0; i < width; i++)
      for(int k = 0; k < 3; k++) row[3 * i + k] = buf[4 * i + k];
    tmp[0] = row;
    jpeg_write_scanlines(&(jpg.cinfo), tmp, 1);
  }
  jpeg_finish_compress(&(jpg.cinfo));
  jpeg_destroy_compress(&(jpg.cinfo));
  fclose(f);
  return 0;
}
Exemplo n.º 2
0
int write_image(dt_imageio_module_data_t *d_tmp, const char *filename, const void *in_void, void *exif,
                int exif_len, int imgid, int num, int total)
{
  const dt_imageio_tiff_t *d = (dt_imageio_tiff_t *)d_tmp;

  uint8_t *profile = NULL;
  uint32_t profile_len = 0;

  TIFF *tif = NULL;

  void *rowdata = NULL;

  int rc = 1; // default to error

  if(imgid > 0)
  {
    cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid)->profile;
    cmsSaveProfileToMem(out_profile, 0, &profile_len);
    if(profile_len > 0)
    {
      profile = malloc(profile_len);
      if(!profile)
      {
        rc = 1;
        goto exit;
      }
      cmsSaveProfileToMem(out_profile, profile, &profile_len);
    }
  }

  // Create little endian tiff image
  tif = TIFFOpen(filename, "wl");
  if(!tif)
  {
    rc = 1;
    goto exit;
  }

  // http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf (dated 2002)
  // "A proprietary ZIP/Flate compression code (0x80b2) has been used by some"
  // "software vendors. This code should be considered obsolete. We recommend"
  // "that TIFF implentations recognize and read the obsolete code but only"
  // "write the official compression code (0x0008)."
  // http://www.awaresystems.be/imaging/tiff/tifftags/compression.html
  // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html
  if(d->compress == 1)
  {
    TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE);
    TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)1);
    TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)9);
  }
  else if(d->compress == 2)
  {
    TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE);
    TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)2);
    TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)9);
  }
  else if(d->compress == 3)
  {
    TIFFSetField(tif, TIFFTAG_COMPRESSION, (uint16_t)COMPRESSION_ADOBE_DEFLATE);
    if(d->bpp == 32)
      TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)3);
    else
      TIFFSetField(tif, TIFFTAG_PREDICTOR, (uint16_t)2);
    TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)9);
  }
  else // (d->compress == 0)
  {
    TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
  }

  TIFFSetField(tif, TIFFTAG_FILLORDER, (uint16_t)FILLORDER_MSB2LSB);
  if(profile != NULL)
  {
    TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32_t)profile_len, profile);
  }
  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, (uint16_t)3);
  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16_t)d->bpp);
  TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, (uint16_t)(d->bpp == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT));
  TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)d->width);
  TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)d->height);
  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (uint16_t)PHOTOMETRIC_RGB);
  TIFFSetField(tif, TIFFTAG_PLANARCONFIG, (uint16_t)PLANARCONFIG_CONTIG);
  TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, (uint32_t)1);
  TIFFSetField(tif, TIFFTAG_ORIENTATION, (uint16_t)ORIENTATION_TOPLEFT);

  int resolution = dt_conf_get_int("metadata/resolution");
  if(resolution > 0)
  {
    TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)resolution);
    TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)resolution);
    TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16_t)RESUNIT_INCH);
  }

  const size_t rowsize = (d->width * 3) * d->bpp / 8;
  if((rowdata = malloc(rowsize)) == NULL)
  {
    rc = 1;
    goto exit;
  }

  if(d->bpp == 32)
  {
    for(int y = 0; y < d->height; y++)
    {
      float *in = (float *)in_void + (size_t)4 * y * d->width;
      float *out = (float *)rowdata;

      for(int x = 0; x < d->width; x++, in += 4, out += 3)
      {
        memcpy(out, in, 3 * sizeof(float));
      }

      if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
      {
        rc = 1;
        goto exit;
      }
    }
  }
  else if(d->bpp == 16)
  {
    for(int y = 0; y < d->height; y++)
    {
      uint16_t *in = (uint16_t *)in_void + (size_t)4 * y * d->width;
      uint16_t *out = (uint16_t *)rowdata;

      for(int x = 0; x < d->width; x++, in += 4, out += 3)
      {
        memcpy(out, in, 3 * sizeof(uint16_t));
      }

      if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
      {
        rc = 1;
        goto exit;
      }
    }
  }
  else
  {
    for(int y = 0; y < d->height; y++)
    {
      uint8_t *in = (uint8_t *)in_void + (size_t)4 * y * d->width;
      uint8_t *out = (uint8_t *)rowdata;

      for(int x = 0; x < d->width; x++, in += 4, out += 3)
      {
        memcpy(out, in, 3 * sizeof(uint8_t));
      }

      if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
      {
        rc = 1;
        goto exit;
      }
    }
  }

  // success
  rc = 0;

exit:
  // close the file before adding exif data
  if(tif)
  {
    TIFFClose(tif);
    tif = NULL;
  }
  if(!rc && exif)
  {
    rc = dt_exif_write_blob(exif, exif_len, filename, d->compress > 0);
    // Until we get symbolic error status codes, if rc is 1, return 0
    rc = (rc == 1) ? 0 : 1;
  }
  free(profile);
  profile = NULL;
  free(rowdata);
  rowdata = NULL;

  return rc;
}
Exemplo n.º 3
0
int write_image(dt_imageio_module_data_t *jpg_tmp, const char *filename, const void *in_tmp,
                dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
                void *exif, int exif_len, int imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe)
{
  dt_imageio_jpeg_t *jpg = (dt_imageio_jpeg_t *)jpg_tmp;
  const uint8_t *in = (const uint8_t *)in_tmp;
  struct dt_imageio_jpeg_error_mgr jerr;

  jpg->cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
  if(setjmp(jerr.setjmp_buffer))
  {
    jpeg_destroy_compress(&(jpg->cinfo));
    return 1;
  }
  jpeg_create_compress(&(jpg->cinfo));
  FILE *f = g_fopen(filename, "wb");
  if(!f) return 1;
  jpeg_stdio_dest(&(jpg->cinfo), f);

  jpg->cinfo.image_width = jpg->global.width;
  jpg->cinfo.image_height = jpg->global.height;
  jpg->cinfo.input_components = 3;
  jpg->cinfo.in_color_space = JCS_RGB;
  jpeg_set_defaults(&(jpg->cinfo));
  jpeg_set_quality(&(jpg->cinfo), jpg->quality, TRUE);
  if(jpg->quality > 90) jpg->cinfo.comp_info[0].v_samp_factor = 1;
  if(jpg->quality > 92) jpg->cinfo.comp_info[0].h_samp_factor = 1;
  if(jpg->quality > 95) jpg->cinfo.dct_method = JDCT_FLOAT;
  if(jpg->quality < 50) jpg->cinfo.dct_method = JDCT_IFAST;
  if(jpg->quality < 80) jpg->cinfo.smoothing_factor = 20;
  if(jpg->quality < 60) jpg->cinfo.smoothing_factor = 40;
  if(jpg->quality < 40) jpg->cinfo.smoothing_factor = 60;
  jpg->cinfo.optimize_coding = 1;

  // according to specs density_unit = 0, X_density = 1, Y_density = 1 should be fine and valid since it
  // describes an image with unknown unit and square pixels.
  // however, some applications (like the Telekom cloud thingy) seem to be confused by that, so let's set
  // these calues to the same as stored in exiv :/
  const int resolution = dt_conf_get_int("metadata/resolution");
  if(resolution > 0)
  {
    jpg->cinfo.density_unit = 1;
    jpg->cinfo.X_density = resolution;
    jpg->cinfo.Y_density = resolution;
  }
  else
  {
    jpg->cinfo.density_unit = 0;
    jpg->cinfo.X_density = 1;
    jpg->cinfo.Y_density = 1;
  }

  jpeg_start_compress(&(jpg->cinfo), TRUE);

  if(imgid > 0)
  {
    cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, over_type, over_filename)->profile;
    uint32_t len = 0;
    cmsSaveProfileToMem(out_profile, 0, &len);
    if(len > 0)
    {
      unsigned char *buf = malloc(len * sizeof(unsigned char));
      cmsSaveProfileToMem(out_profile, buf, &len);
      write_icc_profile(&(jpg->cinfo), buf, len);
      free(buf);
    }
  }

  uint8_t *row = malloc((size_t)3 * jpg->global.width * sizeof(uint8_t));
  const uint8_t *buf;
  while(jpg->cinfo.next_scanline < jpg->cinfo.image_height)
  {
    JSAMPROW tmp[1];
    buf = in + (size_t)jpg->cinfo.next_scanline * jpg->cinfo.image_width * 4;
    for(int i = 0; i < jpg->global.width; i++)
      for(int k = 0; k < 3; k++) row[3 * i + k] = buf[4 * i + k];
    tmp[0] = row;
    jpeg_write_scanlines(&(jpg->cinfo), tmp, 1);
  }
  jpeg_finish_compress(&(jpg->cinfo));
  free(row);
  jpeg_destroy_compress(&(jpg->cinfo));
  fclose(f);

  dt_exif_write_blob(exif, exif_len, filename, 1);

  return 0;
}
Exemplo n.º 4
0
int dt_apply_printer_profile(int imgid, void **in, uint32_t width, uint32_t height, int bpp,
                             cmsHPROFILE hOutProfile, int intent, gboolean black_point_compensation)
{
  cmsHPROFILE hInProfile;
  cmsHTRANSFORM hTransform;
  cmsUInt32Number wInput, wOutput;
  int OutputColorSpace;

  if(!hOutProfile)
    return 1;

  const dt_colorspaces_color_profile_t *in_profile = dt_colorspaces_get_output_profile(imgid);
  if(!in_profile || !in_profile->profile)
  {
    fprintf(stderr, "error getting output profile for image %d\n", imgid);
    return 1;
  }
  hInProfile = in_profile->profile;

  wInput = ComputeFormatDescriptor (PT_RGB, (bpp==8?1:2));

  OutputColorSpace = _cmsLCMScolorSpace(cmsGetColorSpace(hOutProfile));
  wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, 1);

  hTransform = cmsCreateTransform
    (hInProfile,  wInput,
     hOutProfile, wOutput,
     intent,
     black_point_compensation ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0);

  void *out = (void *)malloc(width*height*3);

  if (bpp == 8)
  {
    const uint8_t *ptr_in = (uint8_t *)*in;
    uint8_t *ptr_out = (uint8_t *)out;

#ifdef _OPENMP
#pragma omp parallel for schedule(static) default(none) shared(ptr_in, ptr_out, hTransform, height, width)
#endif
    for (int k=0; k<height; k++)
      cmsDoTransform(hTransform, (const void *)&ptr_in[k*width*3], (void *)&ptr_out[k*width*3], width);
  }
  else
  {
    const uint16_t *ptr_in = (uint16_t *)*in;
    uint8_t *ptr_out = (uint8_t *)out;

#ifdef _OPENMP
#pragma omp parallel for schedule(static) default(none) shared(ptr_in, ptr_out, hTransform, height, width)
#endif
    for (int k=0; k<height; k++)
      cmsDoTransform(hTransform, (const void *)&ptr_in[k*width*3], (void *)&ptr_out[k*width*3], width);
  }

  cmsDeleteTransform(hTransform);

  free(*in);
  *in = out;

  return 0;
}