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) { // pull in new params to gegl dt_iop_colorzones_data_t *d = (dt_iop_colorzones_data_t *)(piece->data); dt_iop_colorzones_params_t *p = (dt_iop_colorzones_params_t *)p1; #ifdef HAVE_GEGL // TODO #else #if 0 // print new preset printf("p.channel = %d;\n", p->channel); for(int k=0; k<3; k++) for(int i=0; i<DT_IOP_COLORZONES_BANDS; i++) { printf("p.equalizer_x[%d][%i] = %f;\n", k, i, p->equalizer_x[k][i]); printf("p.equalizer_y[%d][%i] = %f;\n", k, i, p->equalizer_y[k][i]); } #endif d->channel = (dt_iop_colorzones_channel_t)p->channel; for(int ch=0; ch<3; ch++) { if(d->channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(d->curve[ch], 0, p->equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p->equalizer_y[ch][DT_IOP_COLORZONES_BANDS-2]); else dt_draw_curve_set_point(d->curve[ch], 0, p->equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p->equalizer_y[ch][0]); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) dt_draw_curve_set_point(d->curve[ch], k+1, p->equalizer_x[ch][k], p->equalizer_y[ch][k]); if(d->channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(d->curve[ch], DT_IOP_COLORZONES_BANDS+1, p->equalizer_x[ch][1]+1.0, p->equalizer_y[ch][1]); else dt_draw_curve_set_point(d->curve[ch], DT_IOP_COLORZONES_BANDS+1, p->equalizer_x[ch][1]+1.0, p->equalizer_y[ch][DT_IOP_COLORZONES_BANDS-1]); dt_draw_curve_calc_values(d->curve[ch], 0.0, 1.0, DT_IOP_COLORZONES_LUT_RES, d->lut[3], d->lut[ch]); } #endif }
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_lowlight_data_t *d = (dt_iop_lowlight_data_t *)(piece->data); dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)p1; dt_draw_curve_set_point(d->curve, 0, p->transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p->transition_y[0]); for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(d->curve, k+1, p->transition_x[k], p->transition_y[k]); dt_draw_curve_set_point(d->curve, DT_IOP_LOWLIGHT_BANDS+1, p->transition_x[1]+1.0, p->transition_y[DT_IOP_LOWLIGHT_BANDS-1]); dt_draw_curve_calc_values(d->curve, 0.0, 1.0, DT_IOP_LOWLIGHT_LUT_RES, NULL, d->lut); d->blueness = p->blueness; }
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) { // pull in new params to gegl dt_iop_tonecurve_data_t *d = (dt_iop_tonecurve_data_t *)(piece->data); dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)p1; for(int k=0; k<6; k++) dt_draw_curve_set_point(d->curve, k, p->tonecurve_x[k], p->tonecurve_y[k]); dt_draw_curve_calc_values(d->curve, 0.0f, 1.0f, 0x10000, NULL, d->table); for(int k=0; k<0x10000; k++) d->table[k] *= 100.0f; // now the extrapolation stuff: const float x[4] = {0.7f, 0.8f, 0.9f, 1.0f}; const float y[4] = {d->table[CLAMP((int)(x[0]*0x10000ul), 0, 0xffff)], d->table[CLAMP((int)(x[1]*0x10000ul), 0, 0xffff)], d->table[CLAMP((int)(x[2]*0x10000ul), 0, 0xffff)], d->table[CLAMP((int)(x[3]*0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs); }
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_tonecurve_data_t *d = (dt_iop_tonecurve_data_t *)(piece->data); dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)p1; for(int ch=0; ch<ch_max; ch++) { // take care of possible change of curve type or number of nodes (not yet implemented in UI) if(d->curve_type[ch] != p->tonecurve_type[ch] || d->curve_nodes[ch] != p->tonecurve_nodes[ch]) { dt_draw_curve_destroy(d->curve[ch]); d->curve[ch] = dt_draw_curve_new(0.0, 1.0, p->tonecurve_type[ch]); d->curve_nodes[ch] = p->tonecurve_nodes[ch]; d->curve_type[ch] = p->tonecurve_type[ch]; for(int k=0; k<p->tonecurve_nodes[ch]; k++) (void)dt_draw_curve_add_point(d->curve[ch], p->tonecurve[ch][k].x, p->tonecurve[ch][k].y); } else { for(int k=0; k<p->tonecurve_nodes[ch]; k++) dt_draw_curve_set_point(d->curve[ch], k, p->tonecurve[ch][k].x, p->tonecurve[ch][k].y); } dt_draw_curve_calc_values(d->curve[ch], 0.0f, 1.0f, 0x10000, NULL, d->table[ch]); } for(int k=0; k<0x10000; k++) d->table[ch_L][k] *= 100.0f; for(int k=0; k<0x10000; k++) d->table[ch_a][k] = d->table[ch_a][k]*256.0f - 128.0f; for(int k=0; k<0x10000; k++) d->table[ch_b][k] = d->table[ch_b][k]*256.0f - 128.0f; d->autoscale_ab = p->tonecurve_autoscale_ab; // now the extrapolation stuff (for L curve only): const float xm = p->tonecurve[ch_L][p->tonecurve_nodes[ch_L]-1].x; const float x[4] = {0.7f*xm, 0.8f*xm, 0.9f*xm, 1.0f*xm}; const float y[4] = {d->table[ch_L][CLAMP((int)(x[0]*0x10000ul), 0, 0xffff)], d->table[ch_L][CLAMP((int)(x[1]*0x10000ul), 0, 0xffff)], d->table[ch_L][CLAMP((int)(x[2]*0x10000ul), 0, 0xffff)], d->table[ch_L][CLAMP((int)(x[3]*0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs); }
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_basecurve_data_t *d = (dt_iop_basecurve_data_t *)(piece->data); dt_iop_basecurve_params_t *p = (dt_iop_basecurve_params_t *)p1; const int ch = 0; // take care of possible change of curve type or number of nodes (not yet implemented in UI) if(d->basecurve_type != p->basecurve_type[ch] || d->basecurve_nodes != p->basecurve_nodes[ch]) { if(d->curve) // catch initial init_pipe case dt_draw_curve_destroy(d->curve); d->curve = dt_draw_curve_new(0.0, 1.0, p->basecurve_type[ch]); d->basecurve_nodes = p->basecurve_nodes[ch]; d->basecurve_type = p->basecurve_type[ch]; for(int k = 0; k < p->basecurve_nodes[ch]; k++) { // printf("p->basecurve[%i][%i].x = %f;\n", ch, k, p->basecurve[ch][k].x); // printf("p->basecurve[%i][%i].y = %f;\n", ch, k, p->basecurve[ch][k].y); (void)dt_draw_curve_add_point(d->curve, p->basecurve[ch][k].x, p->basecurve[ch][k].y); } } else { for(int k = 0; k < p->basecurve_nodes[ch]; k++) dt_draw_curve_set_point(d->curve, k, p->basecurve[ch][k].x, p->basecurve[ch][k].y); } dt_draw_curve_calc_values(d->curve, 0.0f, 1.0f, 0x10000, NULL, d->table); // now the extrapolation stuff: const float xm = p->basecurve[0][p->basecurve_nodes[0] - 1].x; const float x[4] = { 0.7f * xm, 0.8f * xm, 0.9f * xm, 1.0f * xm }; const float y[4] = { d->table[CLAMP((int)(x[0] * 0x10000ul), 0, 0xffff)], d->table[CLAMP((int)(x[1] * 0x10000ul), 0, 0xffff)], d->table[CLAMP((int)(x[2] * 0x10000ul), 0, 0xffff)], d->table[CLAMP((int)(x[3] * 0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x, y, 4, d->unbounded_coeffs); }
static gboolean dt_iop_tonecurve_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; dt_iop_tonecurve_gui_data_t *c = (dt_iop_tonecurve_gui_data_t *)self->gui_data; dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)self->params; dt_develop_t *dev = darktable.develop; const float color_labels_left[3][3] = { { 0.3f, 0.3f, 0.3f }, { 0.0f, 0.34f, 0.27f }, { 0.0f, 0.27f, 0.58f } }; const float color_labels_right[3][3] = {{ 0.3f, 0.3f, 0.3f }, { 0.53f, 0.08f, 0.28f}, { 0.81f, 0.66f, 0.0f } }; int ch = c->channel; int nodes = p->tonecurve_nodes[ch]; dt_iop_tonecurve_node_t *tonecurve = p->tonecurve[ch]; int autoscale_ab = p->tonecurve_autoscale_ab; if(c->minmax_curve_type[ch] != p->tonecurve_type[ch] || c->minmax_curve_nodes[ch] != p->tonecurve_nodes[ch]) { dt_draw_curve_destroy(c->minmax_curve[ch]); c->minmax_curve[ch] = dt_draw_curve_new(0.0, 1.0, p->tonecurve_type[ch]); c->minmax_curve_nodes[ch] = p->tonecurve_nodes[ch]; c->minmax_curve_type[ch] = p->tonecurve_type[ch]; for(int k=0; k<p->tonecurve_nodes[ch]; k++) (void)dt_draw_curve_add_point(c->minmax_curve[ch], p->tonecurve[ch][k].x, p->tonecurve[ch][k].y); } else { for(int k=0; k<p->tonecurve_nodes[ch]; k++) dt_draw_curve_set_point(c->minmax_curve[ch], k, p->tonecurve[ch][k].x, p->tonecurve[ch][k].y); } dt_draw_curve_t *minmax_curve = c->minmax_curve[ch]; dt_draw_curve_calc_values(minmax_curve, 0.0, 1.0, DT_IOP_TONECURVE_RES, c->draw_xs, c->draw_ys); const float xm = tonecurve[nodes-1].x; const float x[4] = {0.7f*xm, 0.8f*xm, 0.9f*xm, 1.0f*xm}; const float y[4] = {c->draw_ys[CLAMP((int)(x[0]*DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES-1)], c->draw_ys[CLAMP((int)(x[1]*DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES-1)], c->draw_ys[CLAMP((int)(x[2]*DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES-1)], c->draw_ys[CLAMP((int)(x[3]*DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES-1)] }; float unbounded_coeffs[3]; dt_iop_estimate_exp(x, y, 4, unbounded_coeffs); const int inset = DT_GUI_CURVE_EDITOR_INSET; int width = widget->allocation.width, height = widget->allocation.height; cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); // clear bg cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, inset, inset); width -= 2*inset; height -= 2*inset; #if 0 // draw shadow around float alpha = 1.0f; for(int k=0; k<inset; k++) { cairo_rectangle(cr, -k, -k, width + 2*k, height + 2*k); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.6f; cairo_fill(cr); } #else cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); #endif cairo_set_source_rgb (cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); // draw color labels const int cells = 8; for(int j=0; j<cells; j++) { for(int i=0; i<cells; i++) { const float f = (cells-1-j+i)/(2.0f*cells-2.0f); cairo_set_source_rgba (cr, (1.0f-f)*color_labels_left[ch][0] + f*color_labels_right[ch][0], (1.0f-f)*color_labels_left[ch][1] + f*color_labels_right[ch][1], (1.0f-f)*color_labels_left[ch][2] + f*color_labels_right[ch][2], .5f); // blend over to make colors darker, so the overlay is more visible cairo_rectangle(cr, width*i/(float)cells, height*j/(float)cells, width/(float)cells, height/(float)cells); cairo_fill(cr); } } // draw grid cairo_set_line_width(cr, .4); cairo_set_source_rgb (cr, .1, .1, .1); if(dev->histogram_type == DT_DEV_HISTOGRAM_WAVEFORM) dt_draw_waveform_lines(cr, 0, 0, width, height); else dt_draw_grid(cr, 4, 0, 0, width, height); // if autoscale_ab is on: do not display a and b curves if (autoscale_ab && ch != ch_L) goto finally; // draw nodes positions cairo_set_line_width(cr, 1.); cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_translate(cr, 0, height); for(int k=0; k<nodes; k++) { cairo_arc(cr, tonecurve[k].x*width, -tonecurve[k].y*height, 3, 0, 2.*M_PI); cairo_stroke(cr); } // draw selected cursor cairo_set_line_width(cr, 1.); // draw histogram in background // only if module is enabled if (self->enabled) { float *hist, hist_max; float *raw_mean, *raw_min, *raw_max; float *raw_mean_output; float picker_mean[3], picker_min[3], picker_max[3]; char text[256]; raw_mean = self->picked_color; raw_min = self->picked_color_min; raw_max = self->picked_color_max; raw_mean_output = self->picked_output_color; hist = self->histogram; hist_max = dev->histogram_type == DT_DEV_HISTOGRAM_LINEAR?self->histogram_max[ch]:logf(1.0 + self->histogram_max[ch]); if(hist && hist_max > 0) { cairo_save(cr); cairo_scale(cr, width/63.0, -(height-5)/(float)hist_max); cairo_set_source_rgba(cr, .2, .2, .2, 0.5); dt_draw_histogram_8(cr, hist, ch, dev->histogram_type == DT_DEV_HISTOGRAM_WAVEFORM?DT_DEV_HISTOGRAM_LOGARITHMIC:dev->histogram_type); // TODO: make draw handle waveform histograms cairo_restore(cr); } if(self->request_color_pick) { // the global live samples ... GSList *samples = darktable.lib->proxy.colorpicker.live_samples; dt_colorpicker_sample_t *sample = NULL; while(samples) { sample = samples->data; picker_scale(sample->picked_color_lab_mean, picker_mean); picker_scale(sample->picked_color_lab_min, picker_min); picker_scale(sample->picked_color_lab_max, picker_max); cairo_set_source_rgba(cr, 0.5, 0.7, 0.5, 0.15); cairo_rectangle(cr, width*picker_min[ch], 0, width*fmax(picker_max[ch]-picker_min[ch], 0.0f), -height); cairo_fill(cr); cairo_set_source_rgba(cr, 0.5, 0.7, 0.5, 0.5); cairo_move_to(cr, width*picker_mean[ch], 0); cairo_line_to(cr, width*picker_mean[ch], -height); cairo_stroke(cr); samples = g_slist_next(samples); } // ... and the local sample picker_scale(raw_mean, picker_mean); picker_scale(raw_min, picker_min); picker_scale(raw_max, picker_max); cairo_set_source_rgba(cr, 0.7, 0.5, 0.5, 0.33); cairo_rectangle(cr, width*picker_min[ch], 0, width*fmax(picker_max[ch]-picker_min[ch], 0.0f), -height); cairo_fill(cr); cairo_set_source_rgba(cr, 0.9, 0.7, 0.7, 0.5); cairo_move_to(cr, width*picker_mean[ch], 0); cairo_line_to(cr, width*picker_mean[ch], -height); cairo_stroke(cr); snprintf(text, 256, "%.1f → %.1f", raw_mean[ch], raw_mean_output[ch]); cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, 0.06*height); cairo_move_to (cr, 0.02f*width, -0.94*height); cairo_show_text(cr, text); cairo_stroke(cr); } } if(c->selected >= 0) { cairo_set_source_rgb(cr, .9, .9, .9); cairo_arc(cr, tonecurve[c->selected].x*width, -tonecurve[c->selected].y*height, 4, 0, 2.*M_PI); cairo_stroke(cr); } // draw curve cairo_set_line_width(cr, 2.); cairo_set_source_rgb(cr, .9, .9, .9); // cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); cairo_move_to(cr, 0, -height*c->draw_ys[0]); for(int k=1; k<DT_IOP_TONECURVE_RES; k++) { const float xx = k/(DT_IOP_TONECURVE_RES-1.0); if(xx > xm) { const float yy = dt_iop_eval_exp(unbounded_coeffs, xx); cairo_line_to(cr, xx*width, - height*yy); } else { cairo_line_to(cr, xx*width, - height*c->draw_ys[k]); } } cairo_stroke(cr); finally: cairo_destroy(cr); cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); cairo_set_source_surface (cr_pixmap, cst, 0, 0); cairo_paint(cr_pixmap); cairo_destroy(cr_pixmap); cairo_surface_destroy(cst); return TRUE; }
static gboolean lowlight_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data; dt_iop_lowlight_params_t p = *(dt_iop_lowlight_params_t *)self->params; dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0, p.transition_y[0]); for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]); const int inset = DT_IOP_LOWLIGHT_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width, height = allocation.height; cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); cairo_set_source_rgb(cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, inset, inset); width -= 2 * inset; height -= 2 * inset; cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0)); cairo_set_source_rgb(cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); cairo_set_source_rgb(cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); // draw grid cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4)); cairo_set_source_rgb(cr, .1, .1, .1); dt_draw_grid(cr, 8, 0, 0, width, height); if(c->mouse_y > 0 || c->dragging) { // draw min/max curves: dt_iop_lowlight_get_params(&p, c->mouse_x, 1., c->mouse_radius); dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0, p.transition_y[0]); for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]); dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_min_xs, c->draw_min_ys); p = *(dt_iop_lowlight_params_t *)self->params; dt_iop_lowlight_get_params(&p, c->mouse_x, .0, c->mouse_radius); dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0, p.transition_y[0]); for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]); dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_max_xs, c->draw_max_ys); } cairo_save(cr); // draw x positions cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.)); const float arrw = DT_PIXEL_APPLY_DPI(7.0f); for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) { cairo_move_to(cr, width * p.transition_x[k], height + inset - DT_PIXEL_APPLY_DPI(1)); cairo_rel_line_to(cr, -arrw * .5f, 0); cairo_rel_line_to(cr, arrw * .5f, -arrw); cairo_rel_line_to(cr, arrw * .5f, arrw); cairo_close_path(cr); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } // draw selected cursor cairo_translate(cr, 0, height); // cairo_set_operator(cr, CAIRO_OPERATOR_ADD); // cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.)); cairo_set_source_rgba(cr, .7, .7, .7, 1.0); p = *(dt_iop_lowlight_params_t *)self->params; dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS - 2] - 1.0, p.transition_y[0]); for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k + 1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS + 1, p.transition_x[1] + 1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS - 1]); dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_xs, c->draw_ys); cairo_move_to(cr, 0 * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_ys[0]); for(int k = 1; k < DT_IOP_LOWLIGHT_RES; k++) cairo_line_to(cr, k * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_ys[k]); cairo_stroke(cr); // draw dots on knots cairo_set_source_rgb(cr, 0.7, 0.7, 0.7); cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.)); for(int k = 0; k < DT_IOP_LOWLIGHT_BANDS; k++) { cairo_arc(cr, width * p.transition_x[k], -height * p.transition_y[k], DT_PIXEL_APPLY_DPI(3.0), 0.0, 2.0 * M_PI); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } if(c->mouse_y > 0 || c->dragging) { // draw min/max, if selected cairo_set_source_rgba(cr, .7, .7, .7, .6); cairo_move_to(cr, 0, -height * c->draw_min_ys[0]); for(int k = 1; k < DT_IOP_LOWLIGHT_RES; k++) cairo_line_to(cr, k * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_min_ys[k]); for(int k = DT_IOP_LOWLIGHT_RES - 1; k >= 0; k--) cairo_line_to(cr, k * width / (float)(DT_IOP_LOWLIGHT_RES - 1), -height * c->draw_max_ys[k]); cairo_close_path(cr); cairo_fill(cr); // draw mouse focus circle cairo_set_source_rgba(cr, .9, .9, .9, .5); const float pos = DT_IOP_LOWLIGHT_RES * c->mouse_x; int k = (int)pos; const float f = k - pos; if(k >= DT_IOP_LOWLIGHT_RES - 1) k = DT_IOP_LOWLIGHT_RES - 2; float ht = -height * (f * c->draw_ys[k] + (1 - f) * c->draw_ys[k + 1]); cairo_arc(cr, c->mouse_x * width, ht, c->mouse_radius * width, 0, 2. * M_PI); cairo_stroke(cr); } cairo_restore(cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // draw labels: PangoLayout *layout; PangoRectangle ink; PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc); pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); pango_font_description_set_absolute_size(desc,(.06 * height) * PANGO_SCALE); layout = pango_cairo_create_layout(cr); pango_layout_set_font_description(layout, desc); cairo_set_source_rgb(cr, .1, .1, .1); pango_layout_set_text(layout, _("dark"), -1); pango_layout_get_pixel_extents(layout, &ink, NULL); cairo_move_to(cr, .02 * width - ink.y, .5 * (height + ink.width)); cairo_save(cr); cairo_rotate(cr, -M_PI * .5f); pango_cairo_show_layout(cr, layout); cairo_restore(cr); pango_layout_set_text(layout, _("bright"), -1); pango_layout_get_pixel_extents(layout, &ink, NULL); cairo_move_to(cr, .98 * width - ink.height, .5 * (height + ink.width)); cairo_save(cr); cairo_rotate(cr, -M_PI * .5f); pango_cairo_show_layout(cr, layout); cairo_restore(cr); pango_layout_set_text(layout, _("day vision"), -1); pango_layout_get_pixel_extents(layout, &ink, NULL); cairo_move_to(cr, .5 * (width - ink.width), .08 * height - ink.height); pango_cairo_show_layout(cr, layout); pango_layout_set_text(layout, _("night vision"), -1); pango_layout_get_pixel_extents(layout, &ink, NULL); cairo_move_to(cr, .5 * (width - ink.width), .97 * height - ink.height); pango_cairo_show_layout(cr, layout); pango_font_description_free(desc); g_object_unref(layout); cairo_destroy(cr); cairo_set_source_surface(crf, cst, 0, 0); cairo_paint(crf); cairo_surface_destroy(cst); return TRUE; }
static gboolean dt_iop_basecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; dt_iop_basecurve_gui_data_t *c = (dt_iop_basecurve_gui_data_t *)self->gui_data; dt_iop_basecurve_params_t *p = (dt_iop_basecurve_params_t *)self->params; int nodes = p->basecurve_nodes[0]; dt_iop_basecurve_node_t *basecurve = p->basecurve[0]; if(c->minmax_curve_type != p->basecurve_type[0] || c->minmax_curve_nodes != p->basecurve_nodes[0]) { dt_draw_curve_destroy(c->minmax_curve); c->minmax_curve = dt_draw_curve_new(0.0, 1.0, p->basecurve_type[0]); c->minmax_curve_nodes = p->basecurve_nodes[0]; c->minmax_curve_type = p->basecurve_type[0]; for(int k = 0; k < p->basecurve_nodes[0]; k++) (void)dt_draw_curve_add_point(c->minmax_curve, p->basecurve[0][k].x, p->basecurve[0][k].y); } else { for(int k = 0; k < p->basecurve_nodes[0]; k++) dt_draw_curve_set_point(c->minmax_curve, k, p->basecurve[0][k].x, p->basecurve[0][k].y); } dt_draw_curve_t *minmax_curve = c->minmax_curve; dt_draw_curve_calc_values(minmax_curve, 0.0, 1.0, DT_IOP_TONECURVE_RES, c->draw_xs, c->draw_ys); const float xm = basecurve[nodes - 1].x; const float x[4] = { 0.7f * xm, 0.8f * xm, 0.9f * xm, 1.0f * xm }; const float y[4] = { c->draw_ys[CLAMP((int)(x[0] * DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES - 1)], c->draw_ys[CLAMP((int)(x[1] * DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES - 1)], c->draw_ys[CLAMP((int)(x[2] * DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES - 1)], c->draw_ys[CLAMP((int)(x[3] * DT_IOP_TONECURVE_RES), 0, DT_IOP_TONECURVE_RES - 1)] }; float unbounded_coeffs[3]; dt_iop_estimate_exp(x, y, 4, unbounded_coeffs); const int inset = DT_GUI_CURVE_EDITOR_INSET; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int width = allocation.width, height = allocation.height; cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); // clear bg cairo_set_source_rgb(cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, inset, inset); width -= 2 * inset; height -= 2 * inset; #if 0 // draw shadow around float alpha = 1.0f; for(int k=0; k<inset; k++) { cairo_rectangle(cr, -k, -k, width + 2*k, height + 2*k); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.6f; cairo_fill(cr); } #else cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0)); cairo_set_source_rgb(cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); #endif cairo_set_source_rgb(cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); cairo_translate(cr, 0, height); cairo_scale(cr, 1.0f, -1.0f); // draw grid cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4)); cairo_set_source_rgb(cr, .1, .1, .1); if(c->loglogscale) dt_draw_loglog_grid(cr, 4, 0, 0, width, height, c->loglogscale); else dt_draw_grid(cr, 4, 0, 0, width, height); // draw nodes positions cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.)); cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); for(int k = 0; k < nodes; k++) { const float x = to_log(basecurve[k].x, c->loglogscale), y = to_log(basecurve[k].y, c->loglogscale); cairo_arc(cr, x * width, y * height, DT_PIXEL_APPLY_DPI(3), 0, 2. * M_PI); cairo_stroke(cr); } // draw selected cursor cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.)); if(c->selected >= 0) { cairo_set_source_rgb(cr, .9, .9, .9); const float x = to_log(basecurve[c->selected].x, c->loglogscale), y = to_log(basecurve[c->selected].y, c->loglogscale); cairo_arc(cr, x * width, y * height, DT_PIXEL_APPLY_DPI(4), 0, 2. * M_PI); cairo_stroke(cr); } // draw curve cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.)); cairo_set_source_rgb(cr, .9, .9, .9); // cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); cairo_move_to(cr, 0, height * to_log(c->draw_ys[0], c->loglogscale)); for(int k = 1; k < DT_IOP_TONECURVE_RES; k++) { const float xx = k / (DT_IOP_TONECURVE_RES - 1.0); if(xx > xm) { const float yy = dt_iop_eval_exp(unbounded_coeffs, xx); const float x = to_log(xx, c->loglogscale), y = to_log(yy, c->loglogscale); cairo_line_to(cr, x * width, height * y); } else { const float yy = c->draw_ys[k]; const float x = to_log(xx, c->loglogscale), y = to_log(yy, c->loglogscale); cairo_line_to(cr, x * width, height * y); } } cairo_stroke(cr); cairo_destroy(cr); cairo_set_source_surface(crf, cst, 0, 0); cairo_paint(crf); cairo_surface_destroy(cst); return TRUE; }
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_tonecurve_data_t *d = (dt_iop_tonecurve_data_t *)(piece->data); dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)p1; if(pipe->type == DT_DEV_PIXELPIPE_PREVIEW) piece->request_histogram |= (DT_REQUEST_ON); else piece->request_histogram &= ~(DT_REQUEST_ON); for(int ch = 0; ch < ch_max; ch++) { // take care of possible change of curve type or number of nodes (not yet implemented in UI) if(d->curve_type[ch] != p->tonecurve_type[ch] || d->curve_nodes[ch] != p->tonecurve_nodes[ch]) { dt_draw_curve_destroy(d->curve[ch]); d->curve[ch] = dt_draw_curve_new(0.0, 1.0, p->tonecurve_type[ch]); d->curve_nodes[ch] = p->tonecurve_nodes[ch]; d->curve_type[ch] = p->tonecurve_type[ch]; for(int k = 0; k < p->tonecurve_nodes[ch]; k++) (void)dt_draw_curve_add_point(d->curve[ch], p->tonecurve[ch][k].x, p->tonecurve[ch][k].y); } else { for(int k = 0; k < p->tonecurve_nodes[ch]; k++) dt_draw_curve_set_point(d->curve[ch], k, p->tonecurve[ch][k].x, p->tonecurve[ch][k].y); } dt_draw_curve_calc_values(d->curve[ch], 0.0f, 1.0f, 0x10000, NULL, d->table[ch]); } for(int k = 0; k < 0x10000; k++) d->table[ch_L][k] *= 100.0f; for(int k = 0; k < 0x10000; k++) d->table[ch_a][k] = d->table[ch_a][k] * 256.0f - 128.0f; for(int k = 0; k < 0x10000; k++) d->table[ch_b][k] = d->table[ch_b][k] * 256.0f - 128.0f; d->autoscale_ab = p->tonecurve_autoscale_ab; d->unbound_ab = p->tonecurve_unbound_ab; // extrapolation for L-curve (right hand side only): const float xm_L = p->tonecurve[ch_L][p->tonecurve_nodes[ch_L] - 1].x; const float x_L[4] = { 0.7f * xm_L, 0.8f * xm_L, 0.9f * xm_L, 1.0f * xm_L }; const float y_L[4] = { d->table[ch_L][CLAMP((int)(x_L[0] * 0x10000ul), 0, 0xffff)], d->table[ch_L][CLAMP((int)(x_L[1] * 0x10000ul), 0, 0xffff)], d->table[ch_L][CLAMP((int)(x_L[2] * 0x10000ul), 0, 0xffff)], d->table[ch_L][CLAMP((int)(x_L[3] * 0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x_L, y_L, 4, d->unbounded_coeffs_L); // extrapolation for a-curve right side: const float xm_ar = p->tonecurve[ch_a][p->tonecurve_nodes[ch_a] - 1].x; const float x_ar[4] = { 0.7f * xm_ar, 0.8f * xm_ar, 0.9f * xm_ar, 1.0f * xm_ar }; const float y_ar[4] = { d->table[ch_a][CLAMP((int)(x_ar[0] * 0x10000ul), 0, 0xffff)], d->table[ch_a][CLAMP((int)(x_ar[1] * 0x10000ul), 0, 0xffff)], d->table[ch_a][CLAMP((int)(x_ar[2] * 0x10000ul), 0, 0xffff)], d->table[ch_a][CLAMP((int)(x_ar[3] * 0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x_ar, y_ar, 4, d->unbounded_coeffs_ab); // extrapolation for a-curve left side (we need to mirror the x-axis): const float xm_al = 1.0f - p->tonecurve[ch_a][0].x; const float x_al[4] = { 0.7f * xm_al, 0.8f * xm_al, 0.9f * xm_al, 1.0f * xm_al }; const float y_al[4] = { d->table[ch_a][CLAMP((int)((1.0f - x_al[0]) * 0x10000ul), 0, 0xffff)], d->table[ch_a][CLAMP((int)((1.0f - x_al[1]) * 0x10000ul), 0, 0xffff)], d->table[ch_a][CLAMP((int)((1.0f - x_al[2]) * 0x10000ul), 0, 0xffff)], d->table[ch_a][CLAMP((int)((1.0f - x_al[3]) * 0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x_al, y_al, 4, d->unbounded_coeffs_ab + 3); // extrapolation for b-curve right side: const float xm_br = p->tonecurve[ch_b][p->tonecurve_nodes[ch_b] - 1].x; const float x_br[4] = { 0.7f * xm_br, 0.8f * xm_br, 0.9f * xm_br, 1.0f * xm_br }; const float y_br[4] = { d->table[ch_b][CLAMP((int)(x_br[0] * 0x10000ul), 0, 0xffff)], d->table[ch_b][CLAMP((int)(x_br[1] * 0x10000ul), 0, 0xffff)], d->table[ch_b][CLAMP((int)(x_br[2] * 0x10000ul), 0, 0xffff)], d->table[ch_b][CLAMP((int)(x_br[3] * 0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x_br, y_br, 4, d->unbounded_coeffs_ab + 6); // extrapolation for b-curve left side (we need to mirror the x-axis): const float xm_bl = 1.0f - p->tonecurve[ch_b][0].x; const float x_bl[4] = { 0.7f * xm_bl, 0.8f * xm_bl, 0.9f * xm_bl, 1.0f * xm_bl }; const float y_bl[4] = { d->table[ch_b][CLAMP((int)((1.0f - x_bl[0]) * 0x10000ul), 0, 0xffff)], d->table[ch_b][CLAMP((int)((1.0f - x_bl[1]) * 0x10000ul), 0, 0xffff)], d->table[ch_b][CLAMP((int)((1.0f - x_bl[2]) * 0x10000ul), 0, 0xffff)], d->table[ch_b][CLAMP((int)((1.0f - x_bl[3]) * 0x10000ul), 0, 0xffff)] }; dt_iop_estimate_exp(x_bl, y_bl, 4, d->unbounded_coeffs_ab + 9); }
static gboolean dt_iop_tonecurve_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; dt_iop_tonecurve_gui_data_t *c = (dt_iop_tonecurve_gui_data_t *)self->gui_data; dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)self->params; for(int k=0; k<6; k++) dt_draw_curve_set_point(c->minmax_curve, k, p->tonecurve_x[k], p->tonecurve_y[k]); const int inset = DT_GUI_CURVE_EDITOR_INSET; int width = widget->allocation.width, height = widget->allocation.height; cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); // clear bg cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, inset, inset); width -= 2*inset; height -= 2*inset; #if 0 // draw shadow around float alpha = 1.0f; for(int k=0; k<inset; k++) { cairo_rectangle(cr, -k, -k, width + 2*k, height + 2*k); cairo_set_source_rgba(cr, 0, 0, 0, alpha); alpha *= 0.6f; cairo_fill(cr); } #else cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); #endif cairo_set_source_rgb (cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); if(c->mouse_y > 0 || c->dragging) { float oldx1, oldy1; oldx1 = p->tonecurve_x[c->selected]; oldy1 = p->tonecurve_y[c->selected]; if(c->selected == 0) dt_draw_curve_set_point(c->minmax_curve, 1, p->tonecurve_x[1], fmaxf(c->selected_min, p->tonecurve_y[1])); if(c->selected == 2) dt_draw_curve_set_point(c->minmax_curve, 1, p->tonecurve_x[1], fminf(c->selected_min, fmaxf(0.0, p->tonecurve_y[1] + DT_GUI_CURVE_INFL*(c->selected_min - oldy1)))); if(c->selected == 3) dt_draw_curve_set_point(c->minmax_curve, 4, p->tonecurve_x[4], fmaxf(c->selected_min, fminf(1.0, p->tonecurve_y[4] + DT_GUI_CURVE_INFL*(c->selected_min - oldy1)))); if(c->selected == 5) dt_draw_curve_set_point(c->minmax_curve, 4, p->tonecurve_x[4], fminf(c->selected_min, p->tonecurve_y[4])); dt_draw_curve_set_point(c->minmax_curve, c->selected, oldx1, c->selected_min); dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, DT_IOP_TONECURVE_RES, c->draw_min_xs, c->draw_min_ys); if(c->selected == 0) dt_draw_curve_set_point(c->minmax_curve, 1, p->tonecurve_x[1], fmaxf(c->selected_max, p->tonecurve_y[1])); if(c->selected == 2) dt_draw_curve_set_point(c->minmax_curve, 1, p->tonecurve_x[1], fminf(c->selected_max, fmaxf(0.0, p->tonecurve_y[1] + DT_GUI_CURVE_INFL*(c->selected_max - oldy1)))); if(c->selected == 3) dt_draw_curve_set_point(c->minmax_curve, 4, p->tonecurve_x[4], fmaxf(c->selected_max, fminf(1.0, p->tonecurve_y[4] + DT_GUI_CURVE_INFL*(c->selected_max - oldy1)))); if(c->selected == 5) dt_draw_curve_set_point(c->minmax_curve, 4, p->tonecurve_x[4], fminf(c->selected_max, p->tonecurve_y[4])); dt_draw_curve_set_point (c->minmax_curve, c->selected, oldx1, c->selected_max); dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, DT_IOP_TONECURVE_RES, c->draw_max_xs, c->draw_max_ys); } for(int k=0; k<6; k++) dt_draw_curve_set_point(c->minmax_curve, k, p->tonecurve_x[k], p->tonecurve_y[k]); dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, DT_IOP_TONECURVE_RES, c->draw_xs, c->draw_ys); // draw grid cairo_set_line_width(cr, .4); cairo_set_source_rgb (cr, .1, .1, .1); dt_draw_grid(cr, 4, 0, 0, width, height); // draw x positions cairo_set_line_width(cr, 1.); cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); const float arrw = 7.0f; for(int k=1; k<5; k++) { cairo_move_to(cr, width*p->tonecurve_x[k], height+inset-1); cairo_rel_line_to(cr, -arrw*.5f, 0); cairo_rel_line_to(cr, arrw*.5f, -arrw); cairo_rel_line_to(cr, arrw*.5f, arrw); cairo_close_path(cr); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } // draw selected cursor cairo_set_line_width(cr, 1.); cairo_translate(cr, 0, height); // draw lum h istogram in background dt_develop_t *dev = darktable.develop; float *hist, hist_max; hist = dev->histogram_pre_tonecurve; hist_max = dev->histogram_pre_tonecurve_max; if(hist_max > 0) { cairo_save(cr); cairo_scale(cr, width/63.0, -(height-5)/(float)hist_max); cairo_set_source_rgba(cr, .2, .2, .2, 0.5); dt_draw_histogram_8(cr, hist, 3); cairo_restore(cr); } if(c->mouse_y > 0 || c->dragging) { // draw min/max, if selected cairo_set_source_rgba(cr, .6, .6, .6, .5); cairo_move_to(cr, 0, - height*c->draw_min_ys[0]); for(int k=1; k<DT_IOP_TONECURVE_RES; k++) cairo_line_to(cr, k*width/(DT_IOP_TONECURVE_RES-1.0), - height*c->draw_min_ys[k]); cairo_line_to(cr, width, - height*c->draw_min_ys[DT_IOP_TONECURVE_RES-1]); cairo_line_to(cr, width, - height*c->draw_max_ys[DT_IOP_TONECURVE_RES-1]); for(int k=DT_IOP_TONECURVE_RES-2; k>=0; k--) cairo_line_to(cr, k*width/(DT_IOP_TONECURVE_RES-1.0), - height*c->draw_max_ys[k]); cairo_close_path(cr); cairo_fill(cr); // draw mouse focus circle cairo_set_source_rgb(cr, .9, .9, .9); const float pos = MAX(0, (DT_IOP_TONECURVE_RES-1) * c->mouse_x/(float)width - 1); int k = (int)pos; const float f = k - pos; if(k >= DT_IOP_TONECURVE_RES-1) k = DT_IOP_TONECURVE_RES - 2; float ht = -height*(f*c->draw_ys[k] + (1-f)*c->draw_ys[k+1]); cairo_arc(cr, c->mouse_x, ht, 4, 0, 2.*M_PI); cairo_stroke(cr); } // draw curve cairo_set_line_width(cr, 2.); cairo_set_source_rgb(cr, .9, .9, .9); // cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE); cairo_move_to(cr, 0, -height*c->draw_ys[0]); for(int k=1; k<DT_IOP_TONECURVE_RES; k++) cairo_line_to(cr, k*width/(DT_IOP_TONECURVE_RES-1.0), - height*c->draw_ys[k]); cairo_stroke(cr); cairo_destroy(cr); cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); cairo_set_source_surface (cr_pixmap, cst, 0, 0); cairo_paint(cr_pixmap); cairo_destroy(cr_pixmap); cairo_surface_destroy(cst); return TRUE; }
static gboolean colorzones_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; dt_iop_colorzones_gui_data_t *c = (dt_iop_colorzones_gui_data_t *)self->gui_data; dt_iop_colorzones_params_t p = *(dt_iop_colorzones_params_t *)self->params; int ch = (int)c->channel; if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-2]); else dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][0]); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) dt_draw_curve_set_point(c->minmax_curve, k+1, p.equalizer_x[ch][k], p.equalizer_y[ch][k]); if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][1]); else dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-1]); const int inset = DT_IOP_COLORZONES_INSET; int width = widget->allocation.width, height = widget->allocation.height; cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); // clear bg, match color of the notebook tabs: GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(c->channel_tabs)); cairo_set_source_rgb (cr, style->bg[GTK_STATE_NORMAL].red/65535.0f, style->bg[GTK_STATE_NORMAL].green/65535.0f, style->bg[GTK_STATE_NORMAL].blue/65535.0f); cairo_paint(cr); cairo_translate(cr, inset, inset); width -= 2*inset; height -= 2*inset; cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); cairo_set_source_rgb (cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); if(c->mouse_y > 0 || c->dragging) { // draw min/max curves: dt_iop_colorzones_get_params(&p, c->channel, c->mouse_x, 1., c->mouse_radius); if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-2]); else dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][0]); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) dt_draw_curve_set_point(c->minmax_curve, k+1, p.equalizer_x[ch][k], p.equalizer_y[ch][k]); if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][1]); else dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-1]); dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, DT_IOP_COLORZONES_RES, c->draw_min_xs, c->draw_min_ys); p = *(dt_iop_colorzones_params_t *)self->params; dt_iop_colorzones_get_params(&p, c->channel, c->mouse_x, .0, c->mouse_radius); if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-2]); else dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][0]); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) dt_draw_curve_set_point(c->minmax_curve, k+1, p.equalizer_x[ch][k], p.equalizer_y[ch][k]); if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][1]); else dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-1]); dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, DT_IOP_COLORZONES_RES, c->draw_max_xs, c->draw_max_ys); } if(self->picked_color[0] == 0.0) { self->picked_color[0] = 50.0f; self->picked_color[1] = 0.0f; self->picked_color[2] = -5.0f; } const float pickC = sqrtf(self->picked_color[1]*self->picked_color[1] + self->picked_color[2]*self->picked_color[2]); const int cellsi = 16, cellsj = 9; for(int j=0; j<cellsj; j++) for(int i=0; i<cellsi; i++) { double rgb[3] = {0.5, 0.5, 0.5}; float jj = 1.0 - (j-.5)/(cellsj-1.), ii = (i+.5)/(cellsi-1.); cmsCIELab Lab; switch(p.channel) { // select by channel, abscissa: case DT_IOP_COLORZONES_L: Lab.L = ii * 100.0; Lab.a = self->picked_color[1]; Lab.b = self->picked_color[2]; break; case DT_IOP_COLORZONES_C: Lab.L = 50.0; Lab.a = 64.0*ii*self->picked_color[1]/pickC; Lab.b = 64.0*ii*self->picked_color[2]/pickC; break; default: // case DT_IOP_COLORZONES_h: Lab.L = 50.0; Lab.a = cosf(2.0*M_PI*ii) * 64.0f; Lab.b = sinf(2.0*M_PI*ii) * 64.0f; break; } const float L0 = Lab.L; const float angle = atan2f(Lab.b, Lab.a); switch(c->channel) { // channel to be altered: case DT_IOP_COLORZONES_L: Lab.L += - 50.0 + 100.0*jj; break; case DT_IOP_COLORZONES_C: Lab.a *= 2.0f*jj; Lab.b *= 2.0f*jj; break; default: // DT_IOP_COLORZONES_h Lab.a = cosf(angle + 2.0*M_PI*(jj-.5f)) * 64.0; Lab.b = sinf(angle + 2.0*M_PI*(jj-.5f)) * 64.0; if(p.channel == DT_IOP_COLORZONES_C) { Lab.a *= ii; Lab.b *= ii; } break; } // gammut mapping magic from iop/exposure.c: const float Lwhite = 100.0f, Lclip = 20.0f; const float Lcap = fminf(100.0, Lab.L); const float clip = 1.0 - (Lcap - L0)*(1.0/100.0)*fminf(Lwhite-Lclip, fmaxf(0.0, Lab.L - Lclip))/(Lwhite-Lclip); const float clip2 = clip*clip*clip; Lab.a *= Lab.L/L0 * clip2; Lab.b *= Lab.L/L0 * clip2; cmsDoTransform(c->xform, &Lab, rgb, 1); cairo_set_source_rgb (cr, rgb[0], rgb[1], rgb[2]); cairo_rectangle(cr, width*i/(float)cellsi, height*j/(float)cellsj, width/(float)cellsi-1, height/(float)cellsj-1); cairo_fill(cr); } // draw marker for currently selected color: float picked_i = -1.0; switch(p.channel) { // select by channel, abscissa: case DT_IOP_COLORZONES_L: picked_i = self->picked_color[0]/100.0; break; case DT_IOP_COLORZONES_C: picked_i = pickC / 128.0; break; default: // case DT_IOP_COLORZONES_h: picked_i = fmodf(atan2f(self->picked_color[2], self->picked_color[1]) + 2.0*M_PI, 2.0*M_PI)/(2.0*M_PI); break; } cairo_save(cr); cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_set_operator(cr, CAIRO_OPERATOR_XOR); cairo_set_line_width(cr, 2.); cairo_move_to(cr, width*picked_i, 0.0); cairo_line_to(cr, width*picked_i, height); cairo_stroke(cr); cairo_restore(cr); // draw x positions cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_set_line_width(cr, 1.); const float arrw = 7.0f; for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) { cairo_move_to(cr, width*p.equalizer_x[c->channel][k], height+inset-1); cairo_rel_line_to(cr, -arrw*.5f, 0); cairo_rel_line_to(cr, arrw*.5f, -arrw); cairo_rel_line_to(cr, arrw*.5f, arrw); cairo_close_path(cr); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } // draw selected cursor cairo_translate(cr, 0, height); // cairo_set_operator(cr, CAIRO_OPERATOR_ADD); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_line_width(cr, 2.); for(int i=0; i<3; i++) { // draw curves, selected last. int ch = ((int)c->channel+i+1)%3; if(i == 2) cairo_set_source_rgba(cr, .7, .7, .7, 1.0); else cairo_set_source_rgba(cr, .7, .7, .7, 0.3); p = *(dt_iop_colorzones_params_t *)self->params; if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-2]); else dt_draw_curve_set_point(c->minmax_curve, 0, p.equalizer_x[ch][DT_IOP_COLORZONES_BANDS-2]-1.0, p.equalizer_y[ch][0]); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) dt_draw_curve_set_point(c->minmax_curve, k+1, p.equalizer_x[ch][k], p.equalizer_y[ch][k]); if(p.channel == DT_IOP_COLORZONES_h) dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][1]); else dt_draw_curve_set_point(c->minmax_curve, DT_IOP_COLORZONES_BANDS+1, p.equalizer_x[ch][1]+1.0, p.equalizer_y[ch][DT_IOP_COLORZONES_BANDS-1]); dt_draw_curve_calc_values(c->minmax_curve, 0.0, 1.0, DT_IOP_COLORZONES_RES, c->draw_xs, c->draw_ys); cairo_move_to(cr, 0*width/(float)(DT_IOP_COLORZONES_RES-1), - height*c->draw_ys[0]); for(int k=1; k<DT_IOP_COLORZONES_RES; k++) cairo_line_to(cr, k*width/(float)(DT_IOP_COLORZONES_RES-1), - height*c->draw_ys[k]); cairo_stroke(cr); } // draw dots on knots cairo_set_source_rgb(cr, 0.7, 0.7, 0.7); cairo_set_line_width(cr, 1.); for(int k=0; k<DT_IOP_COLORZONES_BANDS; k++) { cairo_arc(cr, width*p.equalizer_x[c->channel][k], - height*p.equalizer_y[c->channel][k], 3.0, 0.0, 2.0*M_PI); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } if(c->mouse_y > 0 || c->dragging) { // draw min/max, if selected cairo_set_source_rgba(cr, .7, .7, .7, .6); cairo_move_to(cr, 0, - height*c->draw_min_ys[0]); for(int k=1; k<DT_IOP_COLORZONES_RES; k++) cairo_line_to(cr, k*width/(float)(DT_IOP_COLORZONES_RES-1), - height*c->draw_min_ys[k]); for(int k=DT_IOP_COLORZONES_RES-1; k>=0; k--) cairo_line_to(cr, k*width/(float)(DT_IOP_COLORZONES_RES-1), - height*c->draw_max_ys[k]); cairo_close_path(cr); cairo_fill(cr); // draw mouse focus circle cairo_set_source_rgba(cr, .9, .9, .9, .5); const float pos = DT_IOP_COLORZONES_RES * c->mouse_x; int k = (int)pos; const float f = k - pos; if(k >= DT_IOP_COLORZONES_RES-1) k = DT_IOP_COLORZONES_RES - 2; float ht = -height*(f*c->draw_ys[k] + (1-f)*c->draw_ys[k+1]); cairo_arc(cr, c->mouse_x*width, ht, c->mouse_radius*width, 0, 2.*M_PI); cairo_stroke(cr); } cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_destroy(cr); cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); cairo_set_source_surface (cr_pixmap, cst, 0, 0); cairo_paint(cr_pixmap); cairo_destroy(cr_pixmap); cairo_surface_destroy(cst); return TRUE; }
static gboolean lowlight_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data; dt_iop_lowlight_params_t p = *(dt_iop_lowlight_params_t *)self->params; dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]); for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]); const int inset = DT_IOP_LOWLIGHT_INSET; int width = widget->allocation.width, height = widget->allocation.height; cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(cst); cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); cairo_translate(cr, inset, inset); width -= 2*inset; height -= 2*inset; cairo_set_line_width(cr, 1.0); cairo_set_source_rgb (cr, .1, .1, .1); cairo_rectangle(cr, 0, 0, width, height); cairo_stroke(cr); cairo_set_source_rgb (cr, .3, .3, .3); cairo_rectangle(cr, 0, 0, width, height); cairo_fill(cr); // draw grid cairo_set_line_width(cr, .4); cairo_set_source_rgb (cr, .1, .1, .1); dt_draw_grid(cr, 8, 0, 0, width, height); if(c->mouse_y > 0 || c->dragging) { // draw min/max curves: dt_iop_lowlight_get_params(&p, c->mouse_x, 1., c->mouse_radius); dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]); for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]); dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_min_xs, c->draw_min_ys); p = *(dt_iop_lowlight_params_t *)self->params; dt_iop_lowlight_get_params(&p, c->mouse_x, .0, c->mouse_radius); dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]); for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]); dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_max_xs, c->draw_max_ys); } cairo_save(cr); // draw x positions cairo_set_source_rgb(cr, 0.6, 0.6, 0.6); cairo_set_line_width(cr, 1.); const float arrw = 7.0f; for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) { cairo_move_to(cr, width*p.transition_x[k], height+inset-1); cairo_rel_line_to(cr, -arrw*.5f, 0); cairo_rel_line_to(cr, arrw*.5f, -arrw); cairo_rel_line_to(cr, arrw*.5f, arrw); cairo_close_path(cr); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } // draw selected cursor cairo_translate(cr, 0, height); // cairo_set_operator(cr, CAIRO_OPERATOR_ADD); //cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_line_width(cr, 2.); cairo_set_source_rgba(cr, .7, .7, .7, 1.0); p = *(dt_iop_lowlight_params_t *)self->params; dt_draw_curve_set_point(c->transition_curve, 0, p.transition_x[DT_IOP_LOWLIGHT_BANDS-2]-1.0, p.transition_y[0]); for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) dt_draw_curve_set_point(c->transition_curve, k+1, p.transition_x[k], p.transition_y[k]); dt_draw_curve_set_point(c->transition_curve, DT_IOP_LOWLIGHT_BANDS+1, p.transition_x[1]+1.0, p.transition_y[DT_IOP_LOWLIGHT_BANDS-1]); dt_draw_curve_calc_values(c->transition_curve, 0.0, 1.0, DT_IOP_LOWLIGHT_RES, c->draw_xs, c->draw_ys); cairo_move_to(cr, 0*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_ys[0]); for(int k=1; k<DT_IOP_LOWLIGHT_RES; k++) cairo_line_to(cr, k*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_ys[k]); cairo_stroke(cr); // draw dots on knots cairo_set_source_rgb(cr, 0.7, 0.7, 0.7); cairo_set_line_width(cr, 1.); for(int k=0; k<DT_IOP_LOWLIGHT_BANDS; k++) { cairo_arc(cr, width*p.transition_x[k], - height*p.transition_y[k], 3.0, 0.0, 2.0*M_PI); if(c->x_move == k) cairo_fill(cr); else cairo_stroke(cr); } if(c->mouse_y > 0 || c->dragging) { // draw min/max, if selected cairo_set_source_rgba(cr, .7, .7, .7, .6); cairo_move_to(cr, 0, - height*c->draw_min_ys[0]); for(int k=1; k<DT_IOP_LOWLIGHT_RES; k++) cairo_line_to(cr, k*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_min_ys[k]); for(int k=DT_IOP_LOWLIGHT_RES-1; k>=0; k--) cairo_line_to(cr, k*width/(float)(DT_IOP_LOWLIGHT_RES-1), - height*c->draw_max_ys[k]); cairo_close_path(cr); cairo_fill(cr); // draw mouse focus circle cairo_set_source_rgba(cr, .9, .9, .9, .5); const float pos = DT_IOP_LOWLIGHT_RES * c->mouse_x; int k = (int)pos; const float f = k - pos; if(k >= DT_IOP_LOWLIGHT_RES-1) k = DT_IOP_LOWLIGHT_RES - 2; float ht = -height*(f*c->draw_ys[k] + (1-f)*c->draw_ys[k+1]); cairo_arc(cr, c->mouse_x*width, ht, c->mouse_radius*width, 0, 2.*M_PI); cairo_stroke(cr); } cairo_restore (cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // draw labels: cairo_text_extents_t ext; cairo_set_source_rgb(cr, .1, .1, .1); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, .06*height); cairo_text_extents (cr, _("dark"), &ext); cairo_move_to (cr, .02*width+ext.height, .5*(height+ext.width)); cairo_save (cr); cairo_rotate (cr, -M_PI*.5f); cairo_show_text(cr, _("dark")); cairo_restore (cr); cairo_text_extents (cr, _("bright"), &ext); cairo_move_to (cr, .98*width, .5*(height+ext.width)); cairo_save (cr); cairo_rotate (cr, -M_PI*.5f); cairo_show_text(cr, _("bright")); cairo_restore (cr); cairo_text_extents (cr, _("day vision"), &ext); cairo_move_to (cr, .5*(width-ext.width), .08*height); cairo_show_text(cr, _("day vision")); cairo_text_extents (cr, _("night vision"), &ext); cairo_move_to (cr, .5*(width-ext.width), .97*height); cairo_show_text(cr, _("night vision")); cairo_destroy(cr); cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget)); cairo_set_source_surface (cr_pixmap, cst, 0, 0); cairo_paint(cr_pixmap); cairo_destroy(cr_pixmap); cairo_surface_destroy(cst); return TRUE; }