static void process_fastpath_apply_tonecurves(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { const dt_iop_colorout_data_t *const d = (dt_iop_colorout_data_t *)piece->data; const int ch = piece->colors; if(!isnan(d->cmatrix[0])) { // out is already converted to RGB from Lab. // do we have any lut to apply, or is this a linear profile? if((d->lut[0][0] >= 0.0f) && (d->lut[1][0] >= 0.0f) && (d->lut[2][0] >= 0.0f)) { // apply profile float *const out = (float *const)ovoid; #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) #endif for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch) { for(int c = 0; c < 3; c++) { out[k + c] = (out[k + c] < 1.0f) ? lerp_lut(d->lut[c], out[k + c]) : dt_iop_eval_exp(d->unbounded_coeffs[c], out[k + c]); } } } else if((d->lut[0][0] >= 0.0f) || (d->lut[1][0] >= 0.0f) || (d->lut[2][0] >= 0.0f)) { // apply profile float *const out = (float *const)ovoid; #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) #endif for(size_t k = 0; k < (size_t)ch * roi_out->width * roi_out->height; k += ch) { for(int c = 0; c < 3; c++) { if(d->lut[c][0] >= 0.0f) { out[k + c] = (out[k + c] < 1.0f) ? lerp_lut(d->lut[c], out[k + c]) : dt_iop_eval_exp(d->unbounded_coeffs[c], out[k + c]); } } } } } }
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); }
void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *ivoid, void *ovoid, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out) { const dt_iop_colorout_data_t *const d = (dt_iop_colorout_data_t *)piece->data; const int ch = piece->colors; const int gamutcheck = (d->softproof_enabled == DT_SOFTPROOF_GAMUTCHECK); if(!isnan(d->cmatrix[0])) { //fprintf(stderr,"Using cmatrix codepath\n"); // convert to rgb using matrix #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(roi_in,roi_out, ivoid, ovoid) #endif for(int j=0; j<roi_out->height; j++) { float *in = (float*)ivoid + ch*roi_in->width *j; float *out = (float*)ovoid + ch*roi_out->width*j; const __m128 m0 = _mm_set_ps(0.0f,d->cmatrix[6],d->cmatrix[3],d->cmatrix[0]); const __m128 m1 = _mm_set_ps(0.0f,d->cmatrix[7],d->cmatrix[4],d->cmatrix[1]); const __m128 m2 = _mm_set_ps(0.0f,d->cmatrix[8],d->cmatrix[5],d->cmatrix[2]); for(int i=0; i<roi_out->width; i++, in+=ch, out+=ch ) { const __m128 xyz = dt_Lab_to_XYZ_SSE(_mm_load_ps(in)); const __m128 t = _mm_add_ps(_mm_mul_ps(m0,_mm_shuffle_ps(xyz,xyz,_MM_SHUFFLE(0,0,0,0))),_mm_add_ps(_mm_mul_ps(m1,_mm_shuffle_ps(xyz,xyz,_MM_SHUFFLE(1,1,1,1))),_mm_mul_ps(m2,_mm_shuffle_ps(xyz,xyz,_MM_SHUFFLE(2,2,2,2))))); _mm_stream_ps(out,t); } } _mm_sfence(); // apply profile #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(roi_in,roi_out, ivoid, ovoid) #endif for(int j=0; j<roi_out->height; j++) { float *in = (float*)ivoid + ch*roi_in->width *j; float *out = (float*)ovoid + ch*roi_out->width*j; for(int i=0; i<roi_out->width; i++, in+=ch, out+=ch ) { for(int i=0; i<3; i++) if (d->lut[i][0] >= 0.0f) { out[i] = (out[i] < 1.0f) ? lerp_lut(d->lut[i], out[i]) : dt_iop_eval_exp(d->unbounded_coeffs[i], out[i]); } } } } else { float *in = (float*)ivoid; float *out = (float*)ovoid; const int rowsize=roi_out->width * 3; //fprintf(stderr,"Using xform codepath\n"); #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(out, roi_out, in) #endif for (int k=0; k<roi_out->height; k++) { float Lab[rowsize]; float rgb[rowsize]; const int m=(k*(roi_out->width*ch)); for (int l=0; l<roi_out->width; l++) { int li=3*l,ii=ch*l; Lab[li+0] = in[m+ii+0]; Lab[li+1] = in[m+ii+1]; Lab[li+2] = in[m+ii+2]; } cmsDoTransform (d->xform, Lab, rgb, roi_out->width); for (int l=0; l<roi_out->width; l++) { int oi=ch*l, ri=3*l; if(gamutcheck && (rgb[ri+0] < 0.0f || rgb[ri+1] < 0.0f || rgb[ri+2] < 0.0f)) { out[m+oi+0] = 0.0f; out[m+oi+1] = 1.0f; out[m+oi+2] = 1.0f; } else { out[m+oi+0] = rgb[ri+0]; out[m+oi+1] = rgb[ri+1]; out[m+oi+2] = rgb[ri+2]; } } } } if(piece->pipe->mask_display) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height); }
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; 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; } /* * 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); output = out_profile->profile; /* creating softproof profile if softproof is enabled */ if(d->mode != DT_PROFILE_NORMAL && pipe->type == DT_DEV_PIXELPIPE_FULL) { softproof = dt_colorspaces_get_profile(darktable.color_profiles->softproof_type, darktable.color_profiles->softproof_filename, DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile; /* 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; } g_free(over_filename); }
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *ivoid, void *ovoid, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out) { const dt_iop_colorout_data_t *const d = (dt_iop_colorout_data_t *)piece->data; const int ch = piece->colors; const int gamutcheck = (d->mode == DT_PROFILE_GAMUTCHECK); if(!isnan(d->cmatrix[0])) { // fprintf(stderr,"Using cmatrix codepath\n"); // convert to rgb using matrix #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(roi_in, roi_out, ivoid, ovoid) #endif for(int j = 0; j < roi_out->height; j++) { float *in = (float *)ivoid + (size_t)ch * roi_in->width * j; float *out = (float *)ovoid + (size_t)ch * roi_out->width * j; const __m128 m0 = _mm_set_ps(0.0f, d->cmatrix[6], d->cmatrix[3], d->cmatrix[0]); const __m128 m1 = _mm_set_ps(0.0f, d->cmatrix[7], d->cmatrix[4], d->cmatrix[1]); const __m128 m2 = _mm_set_ps(0.0f, d->cmatrix[8], d->cmatrix[5], d->cmatrix[2]); for(int i = 0; i < roi_out->width; i++, in += ch, out += ch) { const __m128 xyz = dt_Lab_to_XYZ_SSE(_mm_load_ps(in)); const __m128 t = _mm_add_ps(_mm_mul_ps(m0, _mm_shuffle_ps(xyz, xyz, _MM_SHUFFLE(0, 0, 0, 0))), _mm_add_ps(_mm_mul_ps(m1, _mm_shuffle_ps(xyz, xyz, _MM_SHUFFLE(1, 1, 1, 1))), _mm_mul_ps(m2, _mm_shuffle_ps(xyz, xyz, _MM_SHUFFLE(2, 2, 2, 2))))); _mm_stream_ps(out, t); } } _mm_sfence(); // apply profile #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(roi_in, roi_out, ivoid, ovoid) #endif for(int j = 0; j < roi_out->height; j++) { float *in = (float *)ivoid + (size_t)ch * roi_in->width * j; float *out = (float *)ovoid + (size_t)ch * roi_out->width * j; for(int i = 0; i < roi_out->width; i++, in += ch, out += ch) { for(int i = 0; i < 3; i++) if(d->lut[i][0] >= 0.0f) { out[i] = (out[i] < 1.0f) ? lerp_lut(d->lut[i], out[i]) : dt_iop_eval_exp(d->unbounded_coeffs[i], out[i]); } } } } else { // fprintf(stderr,"Using xform codepath\n"); const __m128 outofgamutpixel = _mm_set_ps(0.0f, 1.0f, 1.0f, 0.0f); #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(ivoid, ovoid, roi_out) #endif for(int k = 0; k < roi_out->height; k++) { const float *in = ((float *)ivoid) + (size_t)ch * k * roi_out->width; float *out = ((float *)ovoid) + (size_t)ch * k * roi_out->width; if(!gamutcheck) { cmsDoTransform(d->xform, in, out, roi_out->width); } else { void *rgb = dt_alloc_align(16, 4 * sizeof(float) * roi_out->width); cmsDoTransform(d->xform, in, rgb, roi_out->width); float *rgbptr = (float *)rgb; for(int j = 0; j < roi_out->width; j++, rgbptr += 4, out += 4) { const __m128 pixel = _mm_load_ps(rgbptr); __m128 ingamut = _mm_cmplt_ps(pixel, _mm_set_ps(-FLT_MAX, 0.0f, 0.0f, 0.0f)); ingamut = _mm_or_ps(_mm_unpacklo_ps(ingamut, ingamut), _mm_unpackhi_ps(ingamut, ingamut)); ingamut = _mm_or_ps(_mm_unpacklo_ps(ingamut, ingamut), _mm_unpackhi_ps(ingamut, ingamut)); const __m128 result = _mm_or_ps(_mm_and_ps(ingamut, outofgamutpixel), _mm_andnot_ps(ingamut, pixel)); _mm_stream_ps(out, result); } dt_free_align(rgb); } } _mm_sfence(); } if(piece->pipe->mask_display) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height); }