void gui_cleanup(struct dt_iop_module_t *self) { dt_iop_colortransfer_gui_data_t *g = (dt_iop_colortransfer_gui_data_t *)self->gui_data; dt_colorspaces_cleanup_profile(g->hsRGB); dt_colorspaces_cleanup_profile(g->hLab); cmsDeleteTransform(g->xform); free(self->gui_data); self->gui_data = NULL; }
void gui_cleanup(struct dt_iop_module_t *self) { dt_iop_colorzones_gui_data_t *c = (dt_iop_colorzones_gui_data_t *)self->gui_data; dt_conf_set_int("plugins/darkroom/colorzones/gui_channel", c->channel); dt_colorspaces_cleanup_profile(c->hsRGB); dt_colorspaces_cleanup_profile(c->hLab); cmsDeleteTransform(c->xform); dt_draw_curve_destroy(c->minmax_curve); free(self->gui_data); self->gui_data = NULL; }
void cleanup_pipe (struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { dt_iop_colorout_data_t *d = (dt_iop_colorout_data_t *)piece->data; if(d->output) dt_colorspaces_cleanup_profile(d->output); dt_colorspaces_cleanup_profile(d->Lab); if (d->xform) { cmsDeleteTransform(d->xform); d->xform = 0; } free(piece->data); }
int dt_imageio_jpeg_write_with_icc_profile(const char *filename, const uint8_t *in, const int width, const int height, const int quality, 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_create_output_profile(imgid); 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); } dt_colorspaces_cleanup_profile(out_profile); } 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 *jpg_tmp, const char *filename, const void *in_tmp, void *exif, int exif_len, int imgid) { 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 = fopen(filename, "wb"); if(!f) return 1; jpeg_stdio_dest(&(jpg->cinfo), f); jpg->cinfo.image_width = jpg->width; jpg->cinfo.image_height = jpg->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 :/ 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_create_output_profile(imgid); 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); } dt_colorspaces_cleanup_profile(out_profile); } if(exif && exif_len > 0 && exif_len < 65534) jpeg_write_marker(&(jpg->cinfo), JPEG_APP0 + 1, exif, exif_len); uint8_t row[3 * jpg->width]; 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->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_tiff_t *d, const char *filename, const void *in_void, void *exif, int exif_len, int imgid) { // Fetch colorprofile into buffer if wanted uint8_t *profile = NULL; uint32_t profile_len = 0; int rc = 0; if(imgid > 0) { cmsHPROFILE out_profile = dt_colorspaces_create_output_profile(imgid); cmsSaveProfileToMem(out_profile, 0, &profile_len); if (profile_len > 0) { profile=malloc(profile_len); cmsSaveProfileToMem(out_profile, profile, &profile_len); } dt_colorspaces_cleanup_profile(out_profile); } // Create tiff image TIFF *tif=TIFFOpen(filename,"wb"); if(d->bpp == 8) TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); else TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16); TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); if(profile!=NULL) TIFFSetField(tif, TIFFTAG_ICCPROFILE, profile_len, profile); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, d->width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, d->height); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_PREDICTOR, 1); // Reference www.awaresystems.be/imaging/tiff/tifftags/predictor.html TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, DT_TIFFIO_STRIPE); TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); TIFFSetField(tif, TIFFTAG_XRESOLUTION, 300.0); TIFFSetField(tif, TIFFTAG_YRESOLUTION, 300.0); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9); const uint8_t *in8 =(const uint8_t *)in_void; const uint16_t *in16=(const uint16_t *)in_void; if(d->bpp == 16) { uint32_t rowsize=(d->width*3)*sizeof(uint16_t); uint32_t stripesize=rowsize*DT_TIFFIO_STRIPE; uint16_t *rowdata = (uint16_t *)malloc(stripesize); uint16_t *wdata = rowdata; uint32_t stripe=0; // uint32_t insize=((d->width*d->height)*3)*sizeof(uint16_t); // while(stripedata<(in8+insize)-(stripesize)) { // TIFFWriteEncodedStrip(tif,stripe++,stripedata,stripesize); // stripedata+=stripesize; // } for (int y = 0; y < d->height; y++) { for(int x=0; x<d->width; x++) for(int k=0; k<3; k++) { (wdata)[0] = in16[4*d->width*y + 4*x + k]; wdata++; } if((wdata-stripesize/sizeof(uint16_t))==rowdata) { TIFFWriteEncodedStrip(tif,stripe++,rowdata,rowsize*DT_TIFFIO_STRIPE); wdata=rowdata; } } if((wdata-stripesize/sizeof(uint16_t))!=rowdata) TIFFWriteEncodedStrip(tif,stripe,rowdata,(wdata-rowdata)*sizeof(uint16_t)); TIFFClose(tif); free(rowdata); } else { uint32_t rowsize=(d->width*3)*sizeof(uint8_t); uint32_t stripesize=rowsize*DT_TIFFIO_STRIPE; uint8_t *rowdata = (uint8_t *)malloc(stripesize); uint8_t *wdata = rowdata; uint32_t stripe=0; for (int y = 0; y < d->height; y++) { for(int x=0; x<d->width; x++) for(int k=0; k<3; k++) { (wdata)[0] = in8[4*d->width*y + 4*x + k]; wdata++; } if((wdata-stripesize)==rowdata) { TIFFWriteEncodedStrip(tif,stripe++,rowdata,rowsize*DT_TIFFIO_STRIPE); wdata=rowdata; } } if((wdata-stripesize)!=rowdata) TIFFWriteEncodedStrip(tif,stripe,rowdata,wdata-rowdata); TIFFClose(tif); free(rowdata); } if(exif) rc = dt_exif_write_blob(exif,exif_len,filename); free(profile); /* * Until we get symbolic error status codes, if rc is 1, return 0. */ return ((rc == 1) ? 0 : 1); }
void commit_params (struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)p1; dt_iop_colorout_data_t *d = (dt_iop_colorout_data_t *)piece->data; gchar *overprofile = dt_conf_get_string("plugins/lighttable/export/iccprofile"); const int overintent = dt_conf_get_int("plugins/lighttable/export/iccintent"); const int high_quality_processing = dt_conf_get_bool("plugins/lighttable/export/force_lcms2"); gchar *outprofile=NULL; int outintent = 0; /* cleanup profiles */ if (d->output) dt_colorspaces_cleanup_profile(d->output); d->output = NULL; if (d->softproof_enabled) dt_colorspaces_cleanup_profile(d->softproof); d->softproof = NULL; d->softproof_enabled = p->softproof_enabled; if(self->dev->gui_attached && self->gui_data != NULL) { dt_iop_colorout_gui_data_t *g = (dt_iop_colorout_gui_data_t *)self->gui_data; g->softproof_enabled = p->softproof_enabled; } if (d->xform) { cmsDeleteTransform(d->xform); d->xform = 0; } d->cmatrix[0] = NAN; d->lut[0][0] = -1.0f; d->lut[1][0] = -1.0f; d->lut[2][0] = -1.0f; piece->process_cl_ready = 1; /* if we are exporting then check and set usage of override profile */ if (pipe->type == DT_DEV_PIXELPIPE_EXPORT) { if (overprofile && strcmp(overprofile, "image")) snprintf(p->iccprofile, DT_IOP_COLOR_ICC_LEN, "%s", overprofile); if (overintent >= 0) p->intent = overintent; outprofile = p->iccprofile; outintent = p->intent; } else { /* we are not exporting, using display profile as output */ outprofile = p->displayprofile; outintent = p->displayintent; } /* * Setup transform flags */ uint32_t transformFlags = 0; /* creating output profile */ d->output = _create_profile(outprofile); /* creating softproof profile if softproof is enabled */ if (d->softproof_enabled && pipe->type == DT_DEV_PIXELPIPE_FULL) { d->softproof = _create_profile(p->softproofprofile); /* TODO: the use of bpc should be userconfigurable either from module or preference pane */ /* softproof flag and black point compensation */ transformFlags |= cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE|cmsFLAGS_BLACKPOINTCOMPENSATION; if(d->softproof_enabled == DT_SOFTPROOF_GAMUTCHECK) transformFlags |= cmsFLAGS_GAMUTCHECK; } /* get matrix from profile, if softproofing or high quality exporting always go xform codepath */ if (d->softproof_enabled || (pipe->type == DT_DEV_PIXELPIPE_EXPORT && high_quality_processing) || dt_colorspaces_get_matrix_from_output_profile (d->output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(d->Lab, TYPE_Lab_FLT, d->output, TYPE_RGB_FLT, d->softproof, outintent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } // user selected a non-supported output profile, check that: if (!d->xform && isnan(d->cmatrix[0])) { dt_control_log(_("unsupported output profile has been replaced by sRGB!")); if (d->output) dt_colorspaces_cleanup_profile(d->output); d->output = dt_colorspaces_create_srgb_profile(); if (d->softproof_enabled || dt_colorspaces_get_matrix_from_output_profile (d->output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(d->Lab, TYPE_Lab_FLT, d->output, TYPE_RGB_FLT, d->softproof, outintent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } } // now try to initialize unbounded mode: // we do extrapolation for input values above 1.0f. // unfortunately we can only do this if we got the computation // in our hands, i.e. for the fast builtin-dt-matrix-profile path. for(int k=0; k<3; k++) { // omit luts marked as linear (negative as marker) if(d->lut[k][0] >= 0.0f) { const float x[4] = {0.7f, 0.8f, 0.9f, 1.0f}; const float y[4] = {lerp_lut(d->lut[k], x[0]), lerp_lut(d->lut[k], x[1]), lerp_lut(d->lut[k], x[2]), lerp_lut(d->lut[k], x[3]) }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs[k]); } else d->unbounded_coeffs[k][0] = -1.0f; } //fprintf(stderr, " Output profile %s, softproof %s%s%s\n", outprofile, d->softproof_enabled?"enabled ":"disabled",d->softproof_enabled?"using profile ":"",d->softproof_enabled?p->softproofprofile:""); g_free(overprofile); }
int write_image (dt_imageio_module_data_t *jpg_tmp, const char *filename, const void *in_tmp, void *exif, int exif_len, int imgid) { 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 = fopen(filename, "wb"); if(!f) return 1; jpeg_stdio_dest(&(jpg->cinfo), f); jpg->cinfo.image_width = jpg->width; jpg->cinfo.image_height = jpg->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; jpeg_start_compress(&(jpg->cinfo), TRUE); if(imgid > 0) { cmsHPROFILE out_profile = dt_colorspaces_create_output_profile(imgid); 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); } dt_colorspaces_cleanup_profile(out_profile); } if(exif && exif_len > 0 && exif_len < 65534) jpeg_write_marker(&(jpg->cinfo), JPEG_APP0+1, exif, exif_len); uint8_t row[3*jpg->width]; 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->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) { 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_create_output_profile(imgid); 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); } dt_colorspaces_cleanup_profile(out_profile); } // 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); // 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; }
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)p1; dt_iop_colorout_data_t *d = (dt_iop_colorout_data_t *)piece->data; d->type = p->type; const dt_colorspaces_color_profile_type_t over_type = dt_conf_get_int("plugins/lighttable/export/icctype"); gchar *over_filename = dt_conf_get_string("plugins/lighttable/export/iccprofile"); const dt_iop_color_intent_t over_intent = dt_conf_get_int("plugins/lighttable/export/iccintent"); const int force_lcms2 = dt_conf_get_bool("plugins/lighttable/export/force_lcms2"); dt_colorspaces_color_profile_type_t out_type = DT_COLORSPACE_SRGB; gchar *out_filename = NULL; dt_iop_color_intent_t out_intent = DT_INTENT_PERCEPTUAL; const cmsHPROFILE Lab = dt_colorspaces_get_profile(DT_COLORSPACE_LAB, "", DT_PROFILE_DIRECTION_ANY)->profile; cmsHPROFILE output = NULL; cmsHPROFILE softproof = NULL; d->mode = pipe->type == DT_DEV_PIXELPIPE_FULL ? darktable.color_profiles->mode : DT_PROFILE_NORMAL; if(d->xform) { cmsDeleteTransform(d->xform); d->xform = NULL; } d->cmatrix[0] = NAN; d->lut[0][0] = -1.0f; d->lut[1][0] = -1.0f; d->lut[2][0] = -1.0f; piece->process_cl_ready = 1; /* if we are exporting then check and set usage of override profile */ if(pipe->type == DT_DEV_PIXELPIPE_EXPORT) { if(over_type != DT_COLORSPACE_NONE) { p->type = over_type; g_strlcpy(p->filename, over_filename, sizeof(p->filename)); } if((unsigned int)over_intent < DT_INTENT_LAST) p->intent = over_intent; out_type = p->type; out_filename = p->filename; out_intent = p->intent; } else if(pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL) { out_type = dt_mipmap_cache_get_colorspace(); out_filename = (out_type == DT_COLORSPACE_DISPLAY ? darktable.color_profiles->display_filename : ""); out_intent = darktable.color_profiles->display_intent; } else { /* we are not exporting, using display profile as output */ out_type = darktable.color_profiles->display_type; out_filename = darktable.color_profiles->display_filename; out_intent = darktable.color_profiles->display_intent; } // when the output type is Lab then process is a nop, so we can avoid creating a transform // and the subsequent error messages if(out_type == DT_COLORSPACE_LAB) goto end; /* * Setup transform flags */ uint32_t transformFlags = 0; /* creating output profile */ if(out_type == DT_COLORSPACE_DISPLAY) pthread_rwlock_rdlock(&darktable.color_profiles->xprofile_lock); const dt_colorspaces_color_profile_t *out_profile = dt_colorspaces_get_profile(out_type, out_filename, DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY); if(out_profile) output = out_profile->profile; else { output = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile; dt_control_log(_("missing output profile has been replaced by sRGB!")); fprintf(stderr, "missing output profile `%s' has been replaced by sRGB!\n", dt_colorspaces_get_name(out_type, out_filename)); } /* creating softproof profile if softproof is enabled */ if(d->mode != DT_PROFILE_NORMAL && pipe->type == DT_DEV_PIXELPIPE_FULL) { const dt_colorspaces_color_profile_t *prof = dt_colorspaces_get_profile( darktable.color_profiles->softproof_type, darktable.color_profiles->softproof_filename, DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY); if(prof) softproof = prof->profile; else { softproof = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile; dt_control_log(_("missing softproof profile has been replaced by sRGB!")); fprintf(stderr, "missing softproof profile `%s' has been replaced by sRGB!\n", dt_colorspaces_get_name(darktable.color_profiles->softproof_type, darktable.color_profiles->softproof_filename)); } // some of our internal profiles are what lcms considers ideal profiles as they have a parametric TRC so // taking a roundtrip through those profiles during softproofing has no effect. as a workaround we have to // make lcms quantisize those gamma tables to get the desired effect. // in case that fails we don't enable softproofing. softproof = _make_clipping_profile(softproof); if(softproof) { /* TODO: the use of bpc should be userconfigurable either from module or preference pane */ /* softproof flag and black point compensation */ transformFlags |= cmsFLAGS_SOFTPROOFING | cmsFLAGS_NOCACHE | cmsFLAGS_BLACKPOINTCOMPENSATION; if(d->mode == DT_PROFILE_GAMUTCHECK) transformFlags |= cmsFLAGS_GAMUTCHECK; } } /* * NOTE: theoretically, we should be passing * UsedDirection = LCMS_USED_AS_PROOF into * dt_colorspaces_get_matrix_from_output_profile() so that * dt_colorspaces_get_matrix_from_profile() knows it, but since we do not try * to use our matrix codepath when softproof is enabled, this seemed redundant. */ /* get matrix from profile, if softproofing or high quality exporting always go xform codepath */ if(d->mode != DT_PROFILE_NORMAL || force_lcms2 || dt_colorspaces_get_matrix_from_output_profile(output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES, out_intent)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(Lab, TYPE_LabA_FLT, output, TYPE_RGBA_FLT, softproof, out_intent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } // user selected a non-supported output profile, check that: if(!d->xform && isnan(d->cmatrix[0])) { dt_control_log(_("unsupported output profile has been replaced by sRGB!")); fprintf(stderr, "unsupported output profile `%s' has been replaced by sRGB!\n", out_profile->name); output = dt_colorspaces_get_profile(DT_COLORSPACE_SRGB, "", DT_PROFILE_DIRECTION_OUT)->profile; if(d->mode != DT_PROFILE_NORMAL || dt_colorspaces_get_matrix_from_output_profile(output, d->cmatrix, d->lut[0], d->lut[1], d->lut[2], LUT_SAMPLES, out_intent)) { d->cmatrix[0] = NAN; piece->process_cl_ready = 0; d->xform = cmsCreateProofingTransform(Lab, TYPE_LabA_FLT, output, TYPE_RGBA_FLT, softproof, out_intent, INTENT_RELATIVE_COLORIMETRIC, transformFlags); } } if(out_type == DT_COLORSPACE_DISPLAY) pthread_rwlock_unlock(&darktable.color_profiles->xprofile_lock); // now try to initialize unbounded mode: // we do extrapolation for input values above 1.0f. // unfortunately we can only do this if we got the computation // in our hands, i.e. for the fast builtin-dt-matrix-profile path. for(int k = 0; k < 3; k++) { // omit luts marked as linear (negative as marker) if(d->lut[k][0] >= 0.0f) { const float x[4] = { 0.7f, 0.8f, 0.9f, 1.0f }; const float y[4] = { lerp_lut(d->lut[k], x[0]), lerp_lut(d->lut[k], x[1]), lerp_lut(d->lut[k], x[2]), lerp_lut(d->lut[k], x[3]) }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs[k]); } else d->unbounded_coeffs[k][0] = -1.0f; } // softproof is never the original but always a copy that went through _make_clipping_profile() dt_colorspaces_cleanup_profile(softproof); end: g_free(over_filename); }