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