Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
crop.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2021-2022 Aldric Renaudin.
4 Copyright (C) 2021-2022 Chris Elston.
5 Copyright (C) 2021-2022 Diederik Ter Rahe.
6 Copyright (C) 2021-2022 Hanno Schwalm.
7 Copyright (C) 2021 Hubert Kowalski.
8 Copyright (C) 2021-2022 Pascal Obry.
9 Copyright (C) 2021 Ralf Brown.
10 Copyright (C) 2022 Martin Bařinka.
11 Copyright (C) 2022 Philipp Lutz.
12 Copyright (C) 2023 Alynx Zhou.
13 Copyright (C) 2023-2026 Aurélien PIERRE.
14 Copyright (C) 2023 Luca Zulberti.
15 Copyright (C) 2025-2026 Guillaume Stutin.
16
17 darktable is free software: you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation, either version 3 of the License, or
20 (at your option) any later version.
21
22 darktable is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with darktable. If not, see <http://www.gnu.org/licenses/>.
29*/
30
31#ifdef HAVE_CONFIG_H
32#include "common/darktable.h"
33#include "config.h"
34#endif
35#include "bauhaus/bauhaus.h"
36#include "common/debug.h"
37#include "common/imagebuf.h"
39#include "common/math.h"
40#include "common/opencl.h"
41#include "control/conf.h"
42#include "control/control.h"
43#include "develop/develop.h"
44#include "develop/imageop.h"
45#include "develop/imageop_gui.h"
46#include "dtgtk/expander.h"
47
48#include "gui/draw.h"
49#include "gui/gtk.h"
50#include "gui/guides.h"
51#include "gui/presets.h"
52#include "iop/iop_api.h"
53
54#include <assert.h>
55#include <gdk/gdkkeysyms.h>
56#include <gtk/gtk.h>
57#include <inttypes.h>
58#include <stdlib.h>
59#include <string.h>
60
62
63
69
71{
72 char *name;
73 int d, n;
75
77{
78 float cx; // $MIN: 0.0 $MAX: 1.0 $DESCRIPTION: "left"
79 float cy; // $MIN: 0.0 $MAX: 1.0 $DESCRIPTION: "top"
80 float cw; // $MIN: 0.0 $MAX: 1.0 $DESCRIPTION: "right"
81 float ch; // $MIN: 0.0 $MAX: 1.0 $DESCRIPTION: "bottom"
82 int ratio_n; // $DEFAULT: -1
83 int ratio_d; // $DEFAULT: -1
85
102
135
136typedef struct dt_iop_crop_data_t
137{
138 float aspect; // forced aspect ratio
139 float cx, cy, cw, ch; // crop window
141
142int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
143 const int new_version)
144{
145 return 0;
146}
147
148const char *name()
149{
150 return _("crop");
151}
152
153const char *aliases()
154{
155 return _("reframe|distortion");
156}
157
158const char **description(struct dt_iop_module_t *self)
159{
160 return dt_iop_set_description(self, _("change the framing"), _("corrective or creative"),
161 _("linear, RGB, scene-referred"), _("geometric, RGB"),
162 _("linear, RGB, scene-referred"));
163}
164
166{
167 return IOP_GROUP_EFFECTS;
168}
169
175
177{
179}
180
182{
183 // switch off watermark, it gets confused.
184 return IOP_TAG_DECORATION;
185}
186
188{
189 return IOP_CS_RGB;
190}
191
193{
194 g->clip_x = p->cx;
195 g->clip_w = p->cw - p->cx;
196 g->clip_y = p->cy;
197 g->clip_h = p->ch - p->cy;
198}
199
200/*
201* FIXME: Why do we set gui -> params bounding box going through distort_backtransform (below)
202* but we set params -> gui bounding box directly without using distort_transform (above) ???
203* Does that roundrip ? If so, distort_backtransform is actually a no-op ?
204*/
205
207{
209 if(IS_NULL_PTR(piece)) return;
210 // we want value in iop space
211 const float wd = (float)piece->buf_out.width; //self->dev->roi.preview_width;
212 const float ht = (float)piece->buf_out.height; //self->dev->roi.preview_height;
213
214 const float bbox_left = g->clip_x * wd;
215 const float bbox_top = g->clip_y * ht;
216 const float bbox_right = (g->clip_x + g->clip_w) * wd;
217 const float bbox_bottom = (g->clip_y + g->clip_h) * ht;
218
219 dt_boundingbox_t points = { bbox_left, bbox_top, bbox_right, bbox_bottom };
220
223 {
224 //dt_dev_pixelpipe_iop_t *piece = dt_dev_distort_get_iop_pipe(self->dev->virtual_pipe, self);
225 //if(piece)
226 //{
227 //fprintf(stderr, "buf_out size: %dx%d\n", piece->buf_out.width, piece->buf_out.height);
228
229 if(piece->buf_out.width < 1 || piece->buf_out.height < 1) return;
230 /*p->cx = CLAMPF(points[0] / (float)piece->buf_out.width, 0.0f, 0.9f);
231 p->cy = CLAMPF(points[1] / (float)piece->buf_out.height, 0.0f, 0.9f);
232 p->cw = CLAMPF(points[2] / (float)piece->buf_out.width, 0.1f, 1.0f);
233 p->ch = CLAMPF(points[3] / (float)piece->buf_out.height, 0.1f, 1.0f);
234 */
235 p->cx = CLAMPF(points[0] / wd, 0.0f, 0.9f);
236 p->cy = CLAMPF(points[1] / ht, 0.0f, 0.9f);
237 p->cw = CLAMPF(points[2] / wd, 0.1f, 1.0f);
238 p->ch = CLAMPF(points[3] / ht, 0.1f, 1.0f);
239
240 /*fprintf(stderr, "Divisions: cx=%.1f/%d=%.4f, cy=%.1f/%d=%.4f, cw=%.1f/%d=%.4f, ch=%.1f/%d=%.4f\n",
241 points[0], piece->buf_out.width, p->cx,
242 points[1], piece->buf_out.height, p->cy,
243 points[2], piece->buf_out.width, p->cw,
244 points[3], piece->buf_out.height, p->ch);
245 */
246 //}
247 }
248
249 //fprintf(stderr, "_commit_box: cx:%.4f cy:%.4f cw:%.4f ch:%.4f\n", p->cx, p->cy, p->cw, p->ch);
250 //fprintf(stderr, "_commit_box: x:%.4f y:%.4f w:%.4f h:%.4f\n", g->clip_x, g->clip_y, g->clip_w, g->clip_h);
251}
252
262static gboolean _set_max_clip(dt_dev_pixelpipe_t *pipe, struct dt_iop_module_t *self)
263{
266
267 // we want to know the size of the actual buffer
269 if(IS_NULL_PTR(piece)) return FALSE;
270
271 float wp = piece->buf_out.width;
272 float hp = piece->buf_out.height;
273 float points[8] = { 0.0f, 0.0f, wp, hp, p->cx * wp, p->cy * hp, p->cw * wp, p->ch * hp };
276 return FALSE;
278
279 g->clip_max_x = fmaxf(points[0], 0.0f);
280 g->clip_max_y = fmaxf(points[1], 0.0f);
281 g->clip_max_w = fminf(points[2] - points[0], 1.0f);
282 g->clip_max_h = fminf(points[3] - points[1], 1.0f);
283
284 /*// if clipping values are not null, this is undistorted values...
285 g->clip_x = fmaxf(points[4], g->clip_max_x);
286 g->clip_y = fmaxf(points[5], g->clip_max_y);
287 g->clip_w = fminf(points[6] - points[4], g->clip_max_w);
288 g->clip_h = fminf(points[7] - points[5], g->clip_max_h);
289*/
290 return TRUE;
291}
292
294 float *const restrict points, size_t points_count)
295{
297
298 const float crop_top = piece->buf_in.height * d->cy;
299 const float crop_left = piece->buf_in.width * d->cx;
300
301 // nothing to be done if parameters are set to neutral values (no top/left border)
302 if(crop_top == 0 && crop_left == 0) return 1;
303 __OMP_PARALLEL_FOR_SIMD__(if(points_count > 100) aligned(points : 64))
304 for(size_t i = 0; i < points_count * 2; i += 2)
305 {
306 points[i] -= crop_left;
307 points[i + 1] -= crop_top;
308 }
309
310 return 1;
311}
312
314 float *const restrict points, size_t points_count)
315{
317
318 const float crop_top = piece->buf_in.height * d->cy;
319 const float crop_left = piece->buf_in.width * d->cx;
320
321 // nothing to be done if parameters are set to neutral values (no top/left border)
322 if(crop_top == 0 && crop_left == 0) return 1;
323 __OMP_PARALLEL_FOR_SIMD__(if(points_count > 100) aligned(points : 64))
324 for(size_t i = 0; i < points_count * 2; i += 2)
325 {
326 points[i] += crop_left;
327 points[i + 1] += crop_top;
328 }
329
330 return 1;
331}
332
333void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
334 struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out,
335 const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
336{
337 (void)self;
338 (void)pipe;
339 (void)piece;
340 dt_iop_copy_image_roi(out, in, 1, roi_in, roi_out, TRUE);
341}
342
343// 1st pass: how large would the output be, given this input roi?
344// this is always called with the full buffer before processing.
345void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
346 struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
347 const dt_iop_roi_t *roi_in)
348{
349 *roi_out = *roi_in;
351
352 roi_out->width = roi_in->width * (d->cw - d->cx);
353 roi_out->height = roi_in->height * (d->ch - d->cy);
354 roi_out->x = roi_in->width * d->cx;
355 roi_out->y = roi_in->height * d->cy;
356
357 // sanity check.
358 if(roi_out->x < 0) roi_out->x = 0;
359 if(roi_out->y < 0) roi_out->y = 0;
360 if(roi_out->width < 5) roi_out->width = 5;
361 if(roi_out->height < 5) roi_out->height = 5;
362}
363
364// 2nd pass: which roi would this operation need as input to fill the given output region?
365void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
366 struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
367{
369 *roi_in = *roi_out;
370
371 const float iw = piece->buf_in.width * roi_out->scale;
372 const float ih = piece->buf_in.height * roi_out->scale;
373
374 roi_in->x += iw * d->cx;
375 roi_in->y += ih * d->cy;
376
377 roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(iw));
378 roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(ih));
379}
380
381int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
382 void *const ovoid)
383{
384 const dt_iop_roi_t *const roi_in = &piece->roi_in;
385 const dt_iop_roi_t *const roi_out = &piece->roi_out;
386 dt_iop_copy_image_roi(ovoid, ivoid, 4, roi_in, roi_out, TRUE);
387 return 0;
388}
389
390#ifdef HAVE_OPENCL
391int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
392{
393 const dt_iop_roi_t *const roi_out = &piece->roi_out;
394 cl_int err = -999;
395
396 size_t origin[] = { 0, 0, 0 };
397 size_t region[] = { roi_out->width, roi_out->height, 1 };
398 err = dt_opencl_enqueue_copy_image(pipe->devid, dev_in, dev_out, origin, origin, region);
399 if(err != CL_SUCCESS) goto error;
400
401 return TRUE;
402
403error:
404 dt_print(DT_DEBUG_OPENCL, "[opencl_crop] couldn't enqueue kernel! %d\n", err);
405 return FALSE;
406}
407#endif
408
411{
415
416 if(!IS_NULL_PTR(g) && g->editing)
417 {
418 // In editing mode, we need to see the full uncropped image
419 // to setup the frame.
420 d->cx = 0.0f;
421 d->cy = 0.0f;
422 d->cw = 1.0f;
423 d->ch = 1.0f;
424 }
425 else
426 {
427 d->cx = CLAMPF(p->cx, 0.0f, 0.9f);
428 d->cy = CLAMPF(p->cy, 0.0f, 0.9f);
429 d->cw = CLAMPF(p->cw, 0.1f, 1.0f);
430 d->ch = CLAMPF(p->ch, 0.1f, 1.0f);
431 }
432}
433
435 const dt_dev_pixelpipe_iop_t *piece)
436{
437 (void)self;
438 (void)pipe;
439 (void)piece;
440 return TRUE;
441}
442
444{
445 piece->data = dt_calloc_align(sizeof(dt_iop_crop_data_t));
446 piece->data_size = sizeof(dt_iop_crop_data_t);
447}
448
450{
451 dt_free_align(piece->data);
452 piece->data = NULL;
453}
454
455static float _aspect_ratio_get(dt_iop_module_t *self, GtkWidget *combo)
456{
458
459 // retrieve full image dimensions to calculate aspect ratio if "original image" specified
460 const char *text = dt_bauhaus_combobox_get_text(combo);
461 if(text && !g_strcmp0(text, _("original image")))
462 {
463 int proc_iwd = 0, proc_iht = 0;
464 dt_dev_get_processed_size(darktable.develop, &proc_iwd, &proc_iht);
465
466 if(!(proc_iwd > 0 && proc_iht > 0)) return 0.0f;
467
468 if((p->ratio_d > 0 && proc_iwd > proc_iht) || (p->ratio_d < 0 && proc_iwd < proc_iht))
469 return (float)proc_iwd / (float)proc_iht;
470 else
471 return (float)proc_iht / (float)proc_iwd;
472 }
473
474 // we want to know the size of the actual buffer
476 if(IS_NULL_PTR(piece)) return 0.0f;
477
478 const int iwd = piece->buf_in.width, iht = piece->buf_in.height;
479
480 // if we do not have yet computed the aspect ratio, let's do it now
481 if(p->ratio_d == -2 && p->ratio_n == -2)
482 {
483 if(p->cw == 1.0 && p->cx == 0.0 && p->ch == 1.0 && p->cy == 0.0)
484 {
485 p->ratio_d = -1;
486 p->ratio_n = -1;
487 }
488 else
489 {
491 const float whratio = ((float)(iwd - 2 * interpolation->width) * (p->cw - p->cx))
492 / ((float)(iht - 2 * interpolation->width) * (p->ch - p->cy));
493 const float ri = (float)iwd / (float)iht;
494
495 const float prec = 0.0003f;
496 if(fabsf(whratio - 3.0f / 2.0f) < prec)
497 {
498 p->ratio_d = 3;
499 p->ratio_n = 2;
500 }
501 else if(fabsf(whratio - 2.0f / 1.0f) < prec)
502 {
503 p->ratio_d = 2;
504 p->ratio_n = 1;
505 }
506 else if(fabsf(whratio - 7.0f / 5.0f) < prec)
507 {
508 p->ratio_d = 7;
509 p->ratio_n = 5;
510 }
511 else if(fabsf(whratio - 4.0f / 3.0f) < prec)
512 {
513 p->ratio_d = 4;
514 p->ratio_n = 3;
515 }
516 else if(fabsf(whratio - 5.0f / 4.0f) < prec)
517 {
518 p->ratio_d = 5;
519 p->ratio_n = 4;
520 }
521 else if(fabsf(whratio - 1.0f / 1.0f) < prec)
522 {
523 p->ratio_d = 1;
524 p->ratio_n = 1;
525 }
526 else if(fabsf(whratio - 16.0f / 9.0f) < prec)
527 {
528 p->ratio_d = 16;
529 p->ratio_n = 9;
530 }
531 else if(fabsf(whratio - 16.0f / 10.0f) < prec)
532 {
533 p->ratio_d = 16;
534 p->ratio_n = 10;
535 }
536 else if(fabsf(whratio - 244.5f / 203.2f) < prec)
537 {
538 p->ratio_d = 2445;
539 p->ratio_n = 2032;
540 }
541 else if(fabsf(whratio - sqrtf(2.0f)) < prec)
542 {
543 p->ratio_d = 14142136;
544 p->ratio_n = 10000000;
545 }
546 else if(fabsf(whratio - PHI) < prec)
547 {
548 p->ratio_d = 16180340;
549 p->ratio_n = 10000000;
550 }
551 else if(fabsf(whratio - ri) < prec)
552 {
553 p->ratio_d = 1;
554 p->ratio_n = 0;
555 }
556 else
557 {
558 p->ratio_d = 0;
559 p->ratio_n = 0;
560 }
561 }
562 }
563
564 if(p->ratio_d == 0 && p->ratio_n == 0) return -1.0f;
565 float d = 1.0f, n = 1.0f;
566 if(p->ratio_n == 0)
567 {
568 d = copysignf(iwd, p->ratio_d);
569 n = iht;
570 }
571 else
572 {
573 d = p->ratio_d;
574 n = p->ratio_n;
575 }
576
577 // make aspect ratios like 3:2 and 2:3 to be the same thing
578 const float dn = copysignf(MAX(fabsf(d), fabsf(n)), d);
579 const float nn = copysignf(MIN(fabsf(d), fabsf(n)), n);
580
581 if(dn < 0)
582 return -nn / dn;
583 else
584 return dn / nn;
585}
586
588{
589 if(grab == GRAB_NONE) return;
590
592
593 // enforce aspect ratio.
594 float aspect = _aspect_ratio_get(self, g->aspect_presets);
595 if(aspect <= 0) return;
596
597 int iwd, iht;
599
600 // since one rarely changes between portrait and landscape by cropping,
601 // long side of the crop box should match the long side of the image.
602 if(iwd < iht) aspect = 1.0f / aspect;
603
604 // if only one side changed, force aspect by two adjacent in equal parts
605 // 1 2 4 8 : x y w h
606 double clip_x = MAX(iwd * g->clip_x / (float)iwd, 0.0f);
607 double clip_y = MAX(iht * g->clip_y / (float)iht, 0.0f);
608 double clip_w = MIN(iwd * g->clip_w / (float)iwd, 1.0f);
609 double clip_h = MIN(iht * g->clip_h / (float)iht, 1.0f);
610
611 // if we only modified one dim, respectively, we wanted these values:
612 const double target_h = (double)iwd * g->clip_w / ((double)iht * aspect);
613 const double target_w = (double)iht * g->clip_h * aspect / (double)iwd;
614 // i.e. target_w/h = w/target_h = aspect
615 // first fix aspect ratio:
616
617 // corners: move two adjacent
618 if(grab == GRAB_TOP_LEFT)
619 {
620 // move x y
621 clip_x = clip_x + clip_w - (target_w + clip_w) * .5;
622 clip_y = clip_y + clip_h - (target_h + clip_h) * .5;
623 clip_w = (target_w + clip_w) * .5;
624 clip_h = (target_h + clip_h) * .5;
625 }
626 else if(grab == GRAB_TOP_RIGHT) // move y w
627 {
628 clip_y = clip_y + clip_h - (target_h + clip_h) * .5;
629 clip_w = (target_w + clip_w) * .5;
630 clip_h = (target_h + clip_h) * .5;
631 }
632 else if(grab == GRAB_BOTTOM_RIGHT) // move w h
633 {
634 clip_w = (target_w + clip_w) * .5;
635 clip_h = (target_h + clip_h) * .5;
636 }
637 else if(grab == GRAB_BOTTOM_LEFT) // move h x
638 {
639 clip_h = (target_h + clip_h) * .5;
640 clip_x = clip_x + clip_w - (target_w + clip_w) * .5;
641 clip_w = (target_w + clip_w) * .5;
642 }
643 else if(grab & GRAB_HORIZONTAL) // dragged either x or w (1 4)
644 {
645 // change h and move y, h equally
646 const double off = target_h - clip_h;
647 clip_h = clip_h + off;
648 clip_y = clip_y - .5 * off;
649 }
650 else if(grab & GRAB_VERTICAL) // dragged either y or h (2 8)
651 {
652 // change w and move x, w equally
653 const double off = target_w - clip_w;
654 clip_w = clip_w + off;
655 clip_x = clip_x - .5 * off;
656 }
657 // now fix outside boxes:
658 if(clip_x < g->clip_max_x)
659 {
660 const double prev_clip_h = clip_h;
661 clip_h *= (clip_w + clip_x - g->clip_max_x) / clip_w;
662 clip_w = clip_w + clip_x - g->clip_max_x;
663 clip_x = g->clip_max_x;
664 if(grab & GRAB_TOP) clip_y += prev_clip_h - clip_h;
665 }
666 if(clip_y < g->clip_max_y)
667 {
668 const double prev_clip_w = clip_w;
669 clip_w *= (clip_h + clip_y - g->clip_max_y) / clip_h;
670 clip_h = clip_h + clip_y - g->clip_max_y;
671 clip_y = g->clip_max_y;
672 if(grab & GRAB_LEFT) clip_x += prev_clip_w - clip_w;
673 }
674 if(clip_x + clip_w > g->clip_max_x + g->clip_max_w)
675 {
676 const double prev_clip_h = clip_h;
677 clip_h *= (g->clip_max_x + g->clip_max_w - clip_x) / clip_w;
678 clip_w = g->clip_max_x + g->clip_max_w - clip_x;
679 if(grab & GRAB_TOP) clip_y += prev_clip_h - clip_h;
680 }
681 if(clip_y + clip_h > g->clip_max_y + g->clip_max_h)
682 {
683 const double prev_clip_w = clip_w;
684 clip_w *= (g->clip_max_y + g->clip_max_h - clip_y) / clip_h;
685 clip_h = g->clip_max_y + g->clip_max_h - clip_y;
686 if(grab & GRAB_LEFT) clip_x += prev_clip_w - clip_w;
687 }
688 g->clip_x = fmaxf(clip_x, 0.0f);
689 g->clip_y = fmaxf(clip_y, 0.0f);
690 g->clip_w = fminf(clip_w, 1.0f);
691 g->clip_h = fminf(clip_h, 1.0f);
692}
693
695{
696 const dt_image_t *img = &self->dev->image_storage;
697
699
700 d->cx = img->usercrop[1];
701 d->cy = img->usercrop[0];
702 d->cw = img->usercrop[3];
703 d->ch = img->usercrop[2];
704}
705
706gboolean has_defaults(struct dt_iop_module_t *self)
707{
710 // p->ratio_d and p->ratio_n are inited in GUI, so they are unset in d.
711 return d->cx == p->cx && d->cy == p->cy && d->cw == p->cw && d->ch && p->cw;
712}
713
714static void _float_to_fract(const char *num, int *n, int *d)
715{
716 char tnum[100];
717 gboolean sep_found = FALSE;
718 char *p = (char *)num;
719 int k = 0;
720
721 *d = 1;
722
723 while(*p)
724 {
725 if(sep_found) *d *= 10;
726
727 // look for decimal sep
728 if(!sep_found && ((*p == ',') || (*p == '.')))
729 {
730 sep_found = TRUE;
731 }
732 else if(*p < '0' || *p > '9')
733 {
734 *n = *d = 0;
735 return;
736 }
737 else
738 {
739 tnum[k++] = *p;
740 }
741
742 p++;
743 }
744
745 tnum[k] = '\0';
746
747 *n = atoi(tnum);
748}
749
751{
754 const int which = dt_bauhaus_combobox_get(combo);
755 int d = abs(p->ratio_d), n = p->ratio_n;
756 const char *text = dt_bauhaus_combobox_get_text(combo);
757 if(which < 0)
758 {
759 if(!IS_NULL_PTR(text))
760 {
761 const char *c = text;
762 const char *end = text + strlen(text);
763 while(*c != ':' && *c != '/' && c < end) c++;
764 if(c < end - 1)
765 {
766 // input the exact fraction
767 c++;
768 const int dd = atoi(text);
769 const int nn = atoi(c);
770 // some sanity check
771 if(nn == 0 || dd == 0)
772 {
773 dt_control_log(_("invalid ratio format. it should be \"number:number\""));
774 dt_bauhaus_combobox_set(combo, 0);
775 return;
776 }
777 d = MAX(dd, nn);
778 n = MIN(dd, nn);
779 }
780 else
781 {
782 // find the closest fraction from the input ratio
783 int nn = 0, dd = 0;
784 _float_to_fract(text, &nn, &dd);
785
786 // some sanity check
787 if(dd == 0 || nn == 0)
788 {
789 dt_control_log(_("invalid ratio format. it should be a positive number"));
790 dt_bauhaus_combobox_set(combo, 0);
791 return;
792 }
793
794 d = MAX(dd, nn);
795 n = MIN(dd, nn);
796 }
797
798 // simplify the fraction with binary GCD - https://en.wikipedia.org/wiki/Greatest_common_divisor
799 // search g and d such that g is odd and gcd(nn, dd) = g x 2^d
800 int e = 0;
801 int nn = abs(n);
802 int dd = abs(d);
803 while((nn % 2 == 0) && (dd % 2 == 0))
804 {
805 nn /= 2;
806 dd /= 2;
807 e++;
808 }
809 while(nn != dd)
810 {
811 if(nn % 2 == 0)
812 nn /= 2;
813 else if(dd % 2 == 0)
814 dd /= 2;
815 else if(nn > dd)
816 nn = (nn - dd) / 2;
817 else
818 dd = (dd - nn) / 2;
819 }
820
821 // reduce the fraction with the GCD
822 n /= (nn * 1 << e);
823 d /= (nn * 1 << e);
824 }
825 }
826 else
827 {
828 d = n = 0;
829
830 for(const GList *iter = g->aspect_list; iter; iter = g_list_next(iter))
831 {
832 const dt_iop_crop_aspect_t *aspect = iter->data;
833 if(g_strcmp0(aspect->name, text) == 0)
834 {
835 d = aspect->d;
836 n = aspect->n;
837 break;
838 }
839 }
840 }
841
842 // now we save all that if it has changed
843 if(d != abs(p->ratio_d) || n != p->ratio_n)
844 {
845 if(p->ratio_d >= 0)
846 p->ratio_d = d;
847 else
848 p->ratio_d = -d;
849
850 p->ratio_n = n;
851 dt_conf_set_int("plugins/darkroom/crop/ratio_d", abs(p->ratio_d));
852 dt_conf_set_int("plugins/darkroom/crop/ratio_n", abs(p->ratio_n));
853 if(darktable.gui->reset) return;
854 }
855
856 // Search if current aspect ratio matches something known
857 int act = -1;
858 int i = 0;
859
860 for(const GList *iter = g->aspect_list; iter; iter = g_list_next(iter))
861 {
862 const dt_iop_crop_aspect_t *aspect = iter->data;
863 if((aspect->d == d) && (aspect->n == n))
864 {
865 act = i;
866 break;
867 }
868 i++;
869 }
870
871 // Update combobox label
873
874 if(act == -1)
875 {
876 // we got a custom ratio
877 char str[128];
878 snprintf(str, sizeof(str), "%d:%d %2.2f", abs(p->ratio_d), abs(p->ratio_n),
879 (float)abs(p->ratio_d) / (float)abs(p->ratio_n));
880 dt_bauhaus_combobox_set_text(g->aspect_presets, str);
881 }
882 else if(dt_bauhaus_combobox_get(g->aspect_presets) != act)
883 // we got a default ratio
884 dt_bauhaus_combobox_set(g->aspect_presets, act);
885
887
888 if(!darktable.gui->reset) gui_changed(self, g->aspect_presets, NULL);
889}
890
891void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
892{
895
897
898 if(w == g->cx)
899 {
900 g->clip_w = g->clip_x + g->clip_w - p->cx;
901 g->clip_x = p->cx;
903 }
904 else if(w == g->cw)
905 {
906 g->clip_w = p->cw - g->clip_x;
908 }
909 else if(w == g->cy)
910 {
911 g->clip_h = g->clip_y + g->clip_h - p->cy;
912 g->clip_y = p->cy;
913 _aspect_apply(self, GRAB_TOP);
914 }
915 else if(w == g->ch)
916 {
917 g->clip_h = p->ch - g->clip_y;
919 }
920 else if(w == g->aspect_presets)
921 {
922 _aspect_apply(self, GRAB_ALL);
923 }
924
925 // update all sliders, as their values may have change to keep aspect ratio
926 dt_bauhaus_slider_set(g->cx, g->clip_x);
927 dt_bauhaus_slider_set_soft_min(g->cw, g->clip_x + 0.10);
928 dt_bauhaus_slider_set(g->cy, g->clip_y);
929 dt_bauhaus_slider_set_soft_min(g->ch, g->clip_y + 0.10);
930 dt_bauhaus_slider_set(g->cw, g->clip_x + g->clip_w);
931 dt_bauhaus_slider_set_soft_max(g->cx, g->clip_x + g->clip_w - 0.10);
932 dt_bauhaus_slider_set(g->ch, g->clip_y + g->clip_h);
933 dt_bauhaus_slider_set_soft_max(g->cy, g->clip_y + g->clip_h - 0.10);
934
936
937 _commit_box(self, g, p);
940}
941
942void gui_reset(struct dt_iop_module_t *self)
943{
944 /* reset aspect preset to default */
945 dt_conf_set_int("plugins/darkroom/crop/ratio_d", 0);
946 dt_conf_set_int("plugins/darkroom/crop/ratio_n", 0);
947}
948
949void gui_update(struct dt_iop_module_t *self)
950{
953
954 // set aspect ratio based on the current image, if not found let's default
955 // to free aspect.
956
957 if(p->ratio_d == -2 && p->ratio_n == -2) _aspect_ratio_get(self, g->aspect_presets);
958
959 if(p->ratio_d == -1 && p->ratio_n == -1)
960 {
961 p->ratio_d = dt_conf_get_int("plugins/darkroom/crop/ratio_d");
962 p->ratio_n = dt_conf_get_int("plugins/darkroom/crop/ratio_n");
963 }
964
965 const int d = abs(p->ratio_d);
966 const int n = p->ratio_n;
967
968 int act = -1;
969 int i = 0;
970 for(const GList *iter = g->aspect_list; iter; iter = g_list_next(iter))
971 {
972 const dt_iop_crop_aspect_t *aspect = iter->data;
973 if((aspect->d == d) && (aspect->n == n))
974 {
975 act = i;
976 break;
977 }
978 i++;
979 }
980
981 /* special handling the combobox when current act is already selected
982 callback is not called, let do it our self then..
983 */
984 if(act == -1)
985 {
986 char str[128];
987 snprintf(str, sizeof(str), "%d:%d %2.2f", abs(p->ratio_d), abs(p->ratio_n),
988 (float)abs(p->ratio_d) / (float)abs(p->ratio_n));
989 dt_bauhaus_combobox_set_text(g->aspect_presets, str);
990 }
991 if(dt_bauhaus_combobox_get(g->aspect_presets) == act)
992 _event_aspect_presets_changed(g->aspect_presets, self);
993 else
994 dt_bauhaus_combobox_set(g->aspect_presets, act);
995
996 // reset gui draw box to what we have in the parameters:
998
1000}
1001
1003{
1005 p->ratio_d = -p->ratio_d;
1006 _aspect_apply(self, GRAB_ALL);
1007 gui_changed(self, NULL, NULL);
1008}
1009
1010static void _enter_edit_mode(GtkToggleButton *button, struct dt_iop_module_t *self)
1011{
1014 if(!self->enabled) dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1015
1016 g->editing = gtk_toggle_button_get_active(button);
1017 dt_control_change_cursor(GDK_LEFT_PTR);
1019
1020 if(g->editing)
1021 {
1023
1024 // Take a backup of current params
1025 memcpy(&g->previous_params, p, sizeof(dt_iop_crop_params_t));
1026 g->cropping = GRAB_CENTER;
1027 g->dragging = FALSE;
1028 gtk_button_set_label(GTK_BUTTON(button), _("Cancel"));
1029 gtk_widget_set_sensitive(g->commit_button, TRUE);
1032 }
1033 else
1034 {
1036
1037 // Restore the params backup
1038 memcpy(p, &g->previous_params, sizeof(dt_iop_crop_params_t));
1039 g->cropping = GRAB_NONE;
1040
1041 // Commit the params backup
1042 _params_to_gui(p, g);
1043 gui_changed(self, NULL, NULL);
1044
1045 // Update GUI
1046 gtk_button_set_label(GTK_BUTTON(button), _("Edit"));
1047 gtk_widget_set_sensitive(g->commit_button, FALSE);
1048 }
1049
1050 // It sucks that we need to invalidate the preview too but we need its final dimension.
1053}
1054
1055static void _event_commit_clicked(GtkButton *button, dt_iop_module_t *self)
1056{
1058
1059 // Close edit mode on commit
1060 g->editing = FALSE;
1061 gtk_widget_set_sensitive(g->commit_button, FALSE);
1063
1064 // Commit history and refresh view
1067
1068 // The following will de-activate the edit button and trigger the callback.
1069 // Prevent the callback to revert the param change.
1070 g_signal_handlers_block_by_func(g->edit_button, _enter_edit_mode, self);
1071 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->edit_button), FALSE);
1072 gtk_button_set_label(GTK_BUTTON(g->edit_button), _("Edit"));
1073 g_signal_handlers_unblock_by_func(g->edit_button, _enter_edit_mode, self);
1074}
1075
1077{
1078 _event_key_swap(self);
1079}
1080
1082{
1083 // want most square at the end, and the most non-square at the beginning
1084
1085 if((a->d == 0 || a->d == 1) && a->n == 0) return -1;
1086
1087 const float ad = MAX(a->d, a->n);
1088 const float an = MIN(a->d, a->n);
1089 const float bd = MAX(b->d, b->n);
1090 const float bn = MIN(b->d, b->n);
1091 const float aratio = ad / an;
1092 const float bratio = bd / bn;
1093
1094 if(aratio < bratio) return -1;
1095
1096 const float prec = 0.0003f;
1097 if(fabsf(aratio - bratio) < prec) return 0;
1098
1099 return 1;
1100}
1101
1102static gchar *_aspect_format(gchar *original, int adim, int bdim)
1103{
1104 // Special ratios: freehand, original image
1105 if(bdim == 0) return g_strdup(original);
1106
1107 return g_strdup_printf("%s %4.2f", original, (float)adim / (float)bdim);
1108}
1109
1110void gui_init(struct dt_iop_module_t *self)
1111{
1113
1114 g->aspect_list = NULL;
1115 g->clip_x = g->clip_y = g->handle_x = g->handle_y = 0.0;
1116 g->clip_w = g->clip_h = 1.0;
1117 g->clip_max_x = g->clip_max_y = 0.0;
1118 g->clip_max_w = g->clip_max_h = 1.0;
1119 //g->clip_max_pipe_hash = 0;
1120 g->cropping = GRAB_CENTER;
1121 g->shift_hold = FALSE;
1122 g->ctrl_hold = FALSE;
1123 g->editing = FALSE;
1124 g->dragging = FALSE;
1125
1126 GtkWidget *box_enabled = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1127
1128 dt_iop_crop_aspect_t aspects[] = {
1129 { _("freehand"), 0, 0 },
1130 { _("original image"), 1, 0 },
1131 { _("square"), 1, 1 },
1132 { _("7:6, 6x7"), 7, 6 },
1133 { _("10:8 in print"), 2445, 2032 },
1134 { _("6:5, 5x6"), 6, 5 },
1135 { _("5:4, 4x5, 8x10"), 5, 4 },
1136 { _("11x14"), 14, 11 },
1137 { _("8.5x11, letter"), 110, 85 },
1138 { _("4:3, VGA, TV"), 4, 3 },
1139 { _("5x7"), 7, 5 },
1140 { _("ISO 216, DIN 476, A4"), 14142136, 10000000 },
1141 { _("3:2, 4x6, 35mm"), 3, 2 },
1142 { _("16:10, 8x5"), 16, 10 },
1143 { _("golden cut"), 16180340, 10000000 },
1144 { _("5:3, 12x20"), 5, 3 },
1145 { _("16:9, HDTV"), 16, 9 },
1146 { _("widescreen"), 185, 100 },
1147 { _("2:1, Univisium"), 2, 1 },
1148 { _("Cinemascope"), 235, 100 },
1149 { _("21:9"), 7, 3 },
1150 { _("Anamorphic"), 239, 100 },
1151 { _("65:24, XPan"), 65, 24 },
1152 { _("3:1, panorama"), 3, 1 },
1153 { _("4:1, Polyvision"), 4, 1 },
1154 };
1155
1156 const int aspects_count = sizeof(aspects) / sizeof(dt_iop_crop_aspect_t);
1157
1158 for(int i = 0; i < aspects_count; i++)
1159 {
1160 dt_iop_crop_aspect_t *aspect = g_malloc(sizeof(dt_iop_crop_aspect_t));
1161 aspect->name = _aspect_format(aspects[i].name, aspects[i].d, aspects[i].n);
1162 aspect->d = aspects[i].d;
1163 aspect->n = aspects[i].n;
1164 g->aspect_list = g_list_append(g->aspect_list, aspect);
1165 }
1166
1167 // add custom presets from config to the list
1168 GSList *custom_aspects = dt_conf_all_string_entries("plugins/darkroom/clipping/extra_aspect_ratios");
1169 for(GSList *iter = custom_aspects; iter; iter = g_slist_next(iter))
1170 {
1172
1173 const char *c = nv->value;
1174 const char *end = nv->value + strlen(nv->value);
1175 while(*c != ':' && *c != '/' && c < end) c++;
1176 if(c < end - 1)
1177 {
1178 c++;
1179 int d = atoi(nv->value);
1180 int n = atoi(c);
1181 // some sanity check
1182 if(n == 0 || d == 0)
1183 {
1184 fprintf(stderr, "invalid ratio format for `%s'. it should be \"number:number\"\n", nv->key);
1185 dt_control_log(_("invalid ratio format for `%s'. it should be \"number:number\""), nv->key);
1186 continue;
1187 }
1188 dt_iop_crop_aspect_t *aspect = g_malloc(sizeof(dt_iop_crop_aspect_t));
1189 aspect->name = _aspect_format(nv->key, d, n);
1190 aspect->d = d;
1191 aspect->n = n;
1192 g->aspect_list = g_list_append(g->aspect_list, aspect);
1193 }
1194 else
1195 {
1196 fprintf(stderr, "invalid ratio format for `%s'. it should be \"number:number\"\n", nv->key);
1197 dt_control_log(_("invalid ratio format for `%s'. it should be \"number:number\""), nv->key);
1198 continue;
1199 }
1200 }
1201 g_slist_free_full(custom_aspects, dt_conf_string_entry_free);
1202
1203
1204 g->aspect_list = g_list_sort(g->aspect_list, (GCompareFunc)_aspect_ratio_cmp);
1205
1206 // remove duplicates from the aspect ratio list
1207 int d = ((dt_iop_crop_aspect_t *)g->aspect_list->data)->d + 1;
1208 int n = ((dt_iop_crop_aspect_t *)g->aspect_list->data)->n + 1;
1209 for(GList *iter = g->aspect_list; iter; iter = g_list_next(iter))
1210 {
1211 dt_iop_crop_aspect_t *aspect = (dt_iop_crop_aspect_t *)iter->data;
1212 int dd = MIN(aspect->d, aspect->n);
1213 int nn = MAX(aspect->d, aspect->n);
1214 if(dd == d && nn == n)
1215 {
1216 // same as the last one, remove this entry
1217 dt_free(aspect->name);
1218 GList *prev = g_list_previous(iter);
1219 g->aspect_list = g_list_delete_link(g->aspect_list, iter);
1220 // it should never be NULL as the 1st element can't be a duplicate, but better safe than sorry
1221 iter = prev ? prev : g->aspect_list;
1222 }
1223 else
1224 {
1225 d = dd;
1226 n = nn;
1227 }
1228 }
1229
1230 g->aspect_presets = dt_bauhaus_combobox_new(darktable.bauhaus, DT_GUI_MODULE(self));
1231 dt_bauhaus_combobox_set_editable(g->aspect_presets, 1);
1232 dt_bauhaus_widget_set_label(g->aspect_presets, N_("aspect"));
1233
1234 for(GList *iter = g->aspect_list; iter; iter = g_list_next(iter))
1235 {
1236 const dt_iop_crop_aspect_t *aspect = iter->data;
1237 dt_bauhaus_combobox_add(g->aspect_presets, aspect->name);
1238 }
1239
1240 dt_bauhaus_combobox_set(g->aspect_presets, 0);
1241
1242 g_signal_connect(G_OBJECT(g->aspect_presets), "value-changed", G_CALLBACK(_event_aspect_presets_changed), self);
1243 gtk_widget_set_tooltip_text(g->aspect_presets, _("set the aspect ratio\n"
1244 "the list is sorted: from most square to least square\n"
1245 "to enter custom aspect ratio open the combobox and type ratio in x:y or decimal format"));
1246 dt_bauhaus_widget_set_quad_toggle(g->aspect_presets, 1);
1248 g_signal_connect(G_OBJECT(g->aspect_presets), "quad-pressed", G_CALLBACK(_event_aspect_flip), self);
1249 gtk_box_pack_start(GTK_BOX(box_enabled), g->aspect_presets, TRUE, TRUE, 0);
1250
1251 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1252
1253 g->edit_button = gtk_toggle_button_new_with_label(_("Edit"));
1254 g_signal_connect(GTK_TOGGLE_BUTTON(g->edit_button), "toggled", G_CALLBACK(_enter_edit_mode), self);
1255 gtk_box_pack_start(GTK_BOX(box), g->edit_button, TRUE, TRUE, 0);
1256
1257 g->commit_button = dt_action_button_new((dt_lib_module_t *)self, N_("Apply"), _event_commit_clicked, self, _("Apply changes"), 0, 0);
1258 gtk_box_pack_start(GTK_BOX(box), g->commit_button, TRUE, TRUE, 0);
1259 gtk_widget_set_sensitive(g->commit_button, FALSE);
1260
1261 gtk_box_pack_end(GTK_BOX(box_enabled), GTK_WIDGET(box), TRUE, TRUE, 0);
1262
1263
1264 // we put margins values under an expander
1266 (&g->cs,
1267 "plugins/darkroom/crop/expand_margins",
1268 _("margins"),
1269 GTK_BOX(box_enabled), GTK_PACK_END);
1270
1271 self->widget = GTK_WIDGET(g->cs.container);
1272
1273 g->cx = dt_bauhaus_slider_from_params(self, "cx");
1276 gtk_widget_set_tooltip_text(g->cx, _("the left margin cannot overlap with the right margin"));
1277
1278 g->cw = dt_bauhaus_slider_from_params(self, "cw");
1280 dt_bauhaus_slider_set_factor(g->cw, -100.0);
1281 dt_bauhaus_slider_set_offset(g->cw, 100.0);
1283 gtk_widget_set_tooltip_text(g->cw, _("the right margin cannot overlap with the left margin"));
1284
1285 g->cy = dt_bauhaus_slider_from_params(self, "cy");
1288 gtk_widget_set_tooltip_text(g->cy, _("the top margin cannot overlap with the bottom margin"));
1289
1290 g->ch = dt_bauhaus_slider_from_params(self, "ch");
1292 dt_bauhaus_slider_set_factor(g->ch, -100.0);
1293 dt_bauhaus_slider_set_offset(g->ch, 100.0);
1295 gtk_widget_set_tooltip_text(g->ch, _("the bottom margin cannot overlap with the top margin"));
1296
1297 self->widget = box_enabled;
1298}
1299
1300static void _aspect_free(gpointer data)
1301{
1303 dt_free(aspect->name);
1304 dt_free(aspect);
1305}
1306
1308{
1310 g_list_free_full(g->aspect_list, _aspect_free);
1311 g->aspect_list = NULL;
1312
1314}
1315
1316static _grab_region_t _gui_get_grab(float pzx, float pzy, dt_iop_crop_gui_data_t *g, const float border,
1317 const float wd, const float ht)
1318{
1319 if(wd == 0.f || ht == 0.f) return GRAB_NONE;
1320
1322 if(!(pzx < g->clip_x || pzx > g->clip_x + g->clip_w || pzy < g->clip_y || pzy > g->clip_y + g->clip_h))
1323 {
1324 // we are inside the crop box
1325
1326 const float x_border = (border == 0.f) ? 0.f : border / wd;
1327 const float y_border = (border == 0.f) ? 0.f : border / ht;
1328
1329 grab = GRAB_CENTER;
1330
1331 if((pzx >= g->clip_x) && pzx < g->clip_x + x_border)
1332 grab |= GRAB_LEFT; // left border
1333
1334 if((pzy >= g->clip_y) && pzy < g->clip_y + y_border)
1335 grab |= GRAB_TOP; // top border
1336
1337 if((pzx <= g->clip_x + g->clip_w) && (pzx > g->clip_w + g->clip_x - x_border))
1338 grab |= GRAB_RIGHT; // right border
1339
1340 if((pzy <= g->clip_y + g->clip_h) && (pzy > g->clip_h + g->clip_y - y_border))
1341 grab |= GRAB_BOTTOM; // bottom border
1342 }
1343 return grab;
1344}
1345
1346// draw guides and handles over the image
1347void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx,
1348 int32_t pointery)
1349{
1350 dt_develop_t *dev = self->dev;
1352 if(IS_NULL_PTR(g)) return;
1353
1355
1356 g->wd = dev->roi.preview_width;
1357 g->ht = dev->roi.preview_height;
1358 if(g->wd < 1.0 || g->ht < 1.0) return;
1359
1360 const float zoom_scale = dt_dev_get_overlay_scale(dev);
1361
1362 dt_dev_clip_roi(dev, cr, width, height);
1363 dt_dev_rescale_roi(dev, cr, width, height);
1364
1365 // draw crop area guides
1366 const float guide_x = g->editing ? g->clip_x * g->wd : 0;
1367 const float guide_y = g->editing ? g->clip_y * g->ht : 0;
1368 const float guide_w = g->editing ? g->clip_w * g->wd : g->wd;
1369 const float guide_h = g->editing ? g->clip_h * g->ht : g->ht;
1370
1371 dt_guides_draw(cr, guide_x, guide_y, guide_w, guide_h, zoom_scale);
1372
1373 if(!g->editing) return;
1374
1375 double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale;
1376 double border_width = dashes / 2.;
1377
1378 // draw cropping window
1379 float pzxpy[2] = { (float)pointerx, (float)pointery };
1381 float pzx = pzxpy[0];
1382 float pzy = pzxpy[1];
1383 cairo_set_line_width(cr, border_width);
1384
1386 {
1387 cairo_set_source_rgba(cr, .1, .1, .1, .8);
1388 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1389 const float max_x = g->clip_max_x * g->wd;
1390 const float max_y = g->clip_max_y * g->ht;
1391 const float max_w = g->clip_max_w * g->wd;
1392 const float max_h = g->clip_max_h * g->ht;
1393 cairo_rectangle(cr, max_x, max_y, max_w, max_h);
1394
1395 const float clip_x = g->clip_x * g->wd - border_width / 2.;
1396 const float clip_y = g->clip_y * g->ht - border_width / 2.;
1397 const float clip_w = g->clip_w * g->wd + border_width;
1398 const float clip_h = g->clip_h * g->ht + border_width;
1399 cairo_rectangle(cr, clip_x, clip_y, clip_w, clip_h);
1400 cairo_fill(cr);
1401 }
1402 if(g->clip_x > .0f || g->clip_y > .0f || g->clip_w < 1.0f || g->clip_h < 1.0f)
1403 {
1404 const float rect_x = g->clip_x * g->wd - border_width / 2.;
1405 const float rect_y = g->clip_y * g->ht - border_width / 2.;
1406 const float rect_w = g->clip_w * g->wd + border_width;
1407 const float rect_h = g->clip_h * g->ht + border_width;
1408 cairo_rectangle(cr, rect_x, rect_y, rect_w, rect_h);
1410 cairo_stroke(cr);
1411 }
1412
1413 // draw cropping window dimensions if first mouse button is pressed
1415 {
1416 char dimensions[16];
1417 dimensions[0] = '\0';
1418 PangoLayout *layout;
1419 PangoRectangle ext;
1420 PangoFontDescription *desc = pango_font_description_copy_static(darktable.bauhaus->pango_font_desc);
1421 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
1422 pango_font_description_set_absolute_size(desc, DT_PIXEL_APPLY_DPI(16) * PANGO_SCALE / zoom_scale);
1423 layout = pango_cairo_create_layout(cr);
1424 pango_layout_set_font_description(layout, desc);
1425
1426 int procw;
1427 int proch;
1428 dt_dev_get_processed_size(dev, &procw, &proch);
1429 snprintf(dimensions, sizeof(dimensions), "%.0f x %.0f", (float)procw * g->clip_w, (float)proch * g->clip_h);
1430
1431 pango_layout_set_text(layout, dimensions, -1);
1432 pango_layout_get_pixel_extents(layout, NULL, &ext);
1433 const float text_w = ext.width;
1434 const float text_h = DT_PIXEL_APPLY_DPI(16 + 2) / zoom_scale;
1435 const float margin = DT_PIXEL_APPLY_DPI(6) / zoom_scale;
1436 float xp = (g->clip_x + g->clip_w * .5f) * g->wd - text_w * .5f;
1437 float yp = (g->clip_y + g->clip_h * .5f) * g->ht - text_h * .5f;
1438
1439 // ensure that the rendered string remains visible within the window bounds
1440 double x1;
1441 double y1;
1442 double x2;
1443 double y2;
1444 cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1445 xp = CLAMPF(xp, x1 + 2.0 * margin, x2 - text_w - 2.0 * margin);
1446 yp = CLAMPF(yp, y1 + 2.0 * margin, y2 - text_h - 2.0 * margin);
1447
1448 cairo_set_source_rgba(cr, .5, .5, .5, .9);
1449 dt_gui_draw_rounded_rectangle(cr, text_w + 2 * margin, text_h + 2 * margin, xp - margin, yp - margin);
1450 cairo_set_source_rgb(cr, .7, .7, .7);
1451 cairo_move_to(cr, xp, yp);
1452 pango_cairo_show_layout(cr, layout);
1453 pango_font_description_free(desc);
1454 g_object_unref(layout);
1455 }
1456
1457 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.0) / zoom_scale);
1459 const int border = DT_PIXEL_APPLY_DPI(30.0) / zoom_scale;
1460
1461 const _grab_region_t grab = g->cropping ? g->cropping : _gui_get_grab(pzx, pzy, g, border, g->wd, g->ht);
1462
1463 // TODO: check if the variables change from the same calculation some lines above
1464 const float clip_x_px = g->clip_x * g->wd;
1465 const float clip_y_px = g->clip_y * g->ht;
1466 const float clip_w_px = g->clip_w * g->wd;
1467 const float clip_h_px = g->clip_h * g->ht;
1468 const float clip_right_px = clip_x_px + clip_w_px;
1469 const float clip_bottom_px = clip_y_px + clip_h_px;
1470
1471 if(grab == GRAB_LEFT) cairo_rectangle(cr, clip_x_px, clip_y_px, border, clip_h_px);
1472 if(grab == GRAB_TOP) cairo_rectangle(cr, clip_x_px, clip_y_px, clip_w_px, border);
1473 if(grab == GRAB_TOP_LEFT) cairo_rectangle(cr, clip_x_px, clip_y_px, border, border);
1474 if(grab == GRAB_RIGHT) cairo_rectangle(cr, clip_right_px - border, clip_y_px, border, clip_h_px);
1475 if(grab == GRAB_BOTTOM) cairo_rectangle(cr, clip_x_px, clip_bottom_px - border, clip_w_px, border);
1476 if(grab == GRAB_BOTTOM_RIGHT) cairo_rectangle(cr, clip_right_px - border, clip_bottom_px - border, border, border);
1477 if(grab == GRAB_TOP_RIGHT) cairo_rectangle(cr, clip_right_px - border, clip_y_px, border, border);
1478 if(grab == GRAB_BOTTOM_LEFT) cairo_rectangle(cr, clip_x_px, clip_bottom_px - border, border, border);
1479 cairo_stroke(cr);
1480}
1481
1482int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
1483{
1484 dt_develop_t *dev = self->dev;
1486 if(!g->editing) return 0;
1487
1488 float pzxpy[2] = { (float)x, (float)y };
1490 float pzx = pzxpy[0];
1491 float pzy = pzxpy[1];
1492 const float zoom_scale = dt_dev_get_overlay_scale(dev);
1493 const int border = DT_PIXEL_APPLY_DPI(30.0) / zoom_scale;
1494
1495 const _grab_region_t grab = _gui_get_grab(pzx, pzy, g, border, g->wd, g->ht);
1496
1498
1499 if(g->dragging)
1500 {
1501 // draw a light gray frame, to show it's not stored yet:
1502 // first mouse button, adjust cropping frame, but what do we do?
1503 const float bzx = g->button_down_zoom_x;
1504 const float bzy = g->button_down_zoom_y;
1505
1506 if(g->cropping == GRAB_ALL)
1507 {
1508 /* moving the crop window */
1509 if(!g->shift_hold)
1510 g->clip_x
1511 = fminf(g->clip_max_w + g->clip_max_x - g->clip_w,
1512 fmaxf(g->clip_max_x, g->handle_x + pzx - bzx));
1513
1514 if(!g->ctrl_hold)
1515 g->clip_y
1516 = fminf(g->clip_max_h + g->clip_max_y - g->clip_h,
1517 fmaxf(g->clip_max_y, g->handle_y + pzy - bzy));
1518 }
1519 else
1520 {
1521 /* changing the crop window */
1522 if(g->shift_hold)
1523 {
1524 /* the center is locked, scale crop radial with locked ratio */
1525 float xx = 0.0f;
1526 float yy = 0.0f;
1527
1528 if(g->cropping & GRAB_LEFT || g->cropping & GRAB_RIGHT)
1529 xx = (g->cropping & GRAB_LEFT) ? (pzx - bzx) : (bzx - pzx);
1530 if(g->cropping & GRAB_TOP || g->cropping & GRAB_BOTTOM)
1531 yy = (g->cropping & GRAB_TOP) ? (pzy - bzy) : (bzy - pzy);
1532
1533 float ratio = fmaxf((g->prev_clip_w - 2.0f * xx) / g->prev_clip_w,
1534 (g->prev_clip_h - 2.0f * yy) / g->prev_clip_h);
1535
1536 // ensure we don't get too small crop size
1537 if(g->prev_clip_w * ratio < 0.1f) ratio = 0.1f / g->prev_clip_w;
1538 if(g->prev_clip_h * ratio < 0.1f) ratio = 0.1f / g->prev_clip_h;
1539
1540 // ensure we don't have too big crop size
1541 if(g->prev_clip_w * ratio > g->clip_max_w) ratio = g->clip_max_w / g->prev_clip_w;
1542 if(g->prev_clip_h * ratio > g->clip_max_h) ratio = g->clip_max_h / g->prev_clip_h;
1543
1544 // now that we are sure that the crop size is correct, we have to adjust top & left
1545 float nx = g->prev_clip_x - (g->prev_clip_w * ratio - g->prev_clip_w) / 2.0f;
1546 float ny = g->prev_clip_y - (g->prev_clip_h * ratio - g->prev_clip_h) / 2.0f;
1547 float nw = g->prev_clip_w * ratio;
1548 float nh = g->prev_clip_h * ratio;
1549
1550 // move crop area to the right if needed
1551 nx = fmaxf(nx, g->clip_max_x);
1552 // move crop area to the left if needed
1553 nx = fminf(nx, g->clip_max_w + g->clip_max_x - nw);
1554 // move crop area to the bottom if needed
1555 ny = fmaxf(ny, g->clip_max_y);
1556 // move crop area to the top if needed
1557 ny = fminf(ny, g->clip_max_h + g->clip_max_y - nh);
1558
1559 g->clip_x = nx;
1560 g->clip_y = ny;
1561 g->clip_w = nw;
1562 g->clip_h = nh;
1563 }
1564 else
1565 {
1566 if(g->cropping & GRAB_LEFT)
1567 {
1568 const float old_clip_x = g->clip_x;
1569 g->clip_x = fminf(fmaxf(g->clip_max_x, pzx - g->handle_x), g->clip_x + g->clip_w - 0.1f);
1570 g->clip_w = old_clip_x + g->clip_w - g->clip_x;
1571 }
1572 if(g->cropping & GRAB_TOP)
1573 {
1574 const float old_clip_y = g->clip_y;
1575 g->clip_y = fminf(fmaxf(g->clip_max_y, pzy - g->handle_y), g->clip_y + g->clip_h - 0.1f);
1576 g->clip_h = old_clip_y + g->clip_h - g->clip_y;
1577 }
1578 if(g->cropping & GRAB_RIGHT)
1579 g->clip_w = fmaxf(0.1f, fminf(g->clip_max_w + g->clip_max_x, pzx - g->clip_x - g->handle_x));
1580 if(g->cropping & GRAB_BOTTOM)
1581 g->clip_h = fmaxf(0.1f, fminf(g->clip_max_h + g->clip_max_y, pzy - g->clip_y - g->handle_y));
1582 }
1583
1584 if(g->clip_x + g->clip_w > g->clip_max_w + g->clip_max_x)
1585 g->clip_w = g->clip_max_w + g->clip_max_x - g->clip_x;
1586 if(g->clip_y + g->clip_h > g->clip_max_h + g->clip_max_y)
1587 g->clip_h = g->clip_max_h + g->clip_max_y - g->clip_y;
1588 }
1589
1590 _aspect_apply(self, g->cropping);
1591 gui_changed(self, NULL, NULL);
1593 return 1;
1594 }
1595 else if(grab)
1596 {
1597 // hover over active borders, no button pressed
1598 // change mouse pointer
1599 if(grab == GRAB_LEFT)
1600 dt_control_queue_cursor(GDK_LEFT_SIDE);
1601 else if(grab == GRAB_TOP)
1602 dt_control_queue_cursor(GDK_TOP_SIDE);
1603 else if(grab == GRAB_RIGHT)
1604 dt_control_queue_cursor(GDK_RIGHT_SIDE);
1605 else if(grab == GRAB_BOTTOM)
1606 dt_control_queue_cursor(GDK_BOTTOM_SIDE);
1607 else if(grab == GRAB_TOP_LEFT)
1608 dt_control_queue_cursor(GDK_TOP_LEFT_CORNER);
1609 else if(grab == GRAB_TOP_RIGHT)
1610 dt_control_queue_cursor(GDK_TOP_RIGHT_CORNER);
1611 else if(grab == GRAB_BOTTOM_RIGHT)
1612 dt_control_queue_cursor(GDK_BOTTOM_RIGHT_CORNER);
1613 else if(grab == GRAB_BOTTOM_LEFT)
1614 dt_control_queue_cursor(GDK_BOTTOM_LEFT_CORNER);
1615 else if(grab == GRAB_NONE)
1616 {
1618 dt_control_queue_cursor(GDK_LEFT_PTR);
1619 }
1620 if(grab != GRAB_NONE)
1621 dt_control_hinter_message(darktable.control, _("<b>resize</b>: drag, <b>keep aspect ratio</b>: shift+drag"));
1623 }
1624 else
1625 {
1626 dt_control_queue_cursor(GDK_FLEUR);
1627 g->cropping = GRAB_CENTER;
1628 dt_control_hinter_message(darktable.control, _("<b>move</b>: drag, <b>move vertically</b>: shift+drag, <b>move horizontally</b>: ctrl+drag"));
1630 }
1631 return 0;
1632}
1633
1634int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
1635{
1637 if(!g->editing) return 0;
1638
1639 /* reset internal ui states*/
1640 g->shift_hold = FALSE;
1641 g->ctrl_hold = FALSE;
1642 g->cropping = GRAB_CENTER;
1643 g->dragging = FALSE;
1644
1645 // FIXME: is this the right cursor? there should be a "restore_cursor" function
1646 dt_control_change_cursor(GDK_LEFT_PTR);
1647
1648 // we save the crop into the params now so params are kept in synch with gui settings
1649 gui_changed(self, NULL, NULL);
1650 return 1;
1651}
1652
1653int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type,
1654 uint32_t state)
1655{
1657 if(!g->editing) return 0;
1658
1659 // avoid unexpected back to lt mode:
1660 if(type == GDK_2BUTTON_PRESS && which == 1)
1661 return 1;
1662
1663 if(which == 1)
1664 {
1665 dt_develop_t *dev = self->dev;
1666 float pzxpy[2] = { (float)x, (float)y };
1668 float pzx = pzxpy[0];
1669 float pzy = pzxpy[1];
1670 const float zoom_scale = dt_dev_get_overlay_scale(dev);
1671
1672 g->button_down_x = x;
1673 g->button_down_y = y;
1674
1675 g->button_down_zoom_x = pzx;
1676 g->button_down_zoom_y = pzy;
1677
1678 /* update prev clip box with current */
1679 g->prev_clip_x = g->clip_x;
1680 g->prev_clip_y = g->clip_y;
1681 g->prev_clip_w = g->clip_w;
1682 g->prev_clip_h = g->clip_h;
1683
1684 /* if shift is pressed, then lock crop on center */
1685 if(dt_modifiers_include(state, GDK_SHIFT_MASK)) g->shift_hold = TRUE;
1686 if(dt_modifiers_include(state, GDK_CONTROL_MASK)) g->ctrl_hold = TRUE;
1687
1688 /* store grabbed area */
1689
1690 const float border = DT_PIXEL_APPLY_DPI(30.0) / zoom_scale;
1691
1692 g->cropping = _gui_get_grab(pzx, pzy, g, border, g->wd, g->ht);
1693
1694 if(g->cropping != GRAB_NONE)
1695 {
1696 g->dragging = TRUE;
1697
1698 if(g->cropping == GRAB_CENTER)
1699 {
1700 g->cropping = GRAB_ALL;
1701 g->handle_x = g->clip_x;
1702 g->handle_y = g->clip_y;
1703 }
1704 else
1705 {
1706 if(g->cropping & GRAB_LEFT) g->handle_x = pzx - g->clip_x;
1707 if(g->cropping & GRAB_TOP) g->handle_y = pzy - g->clip_y;
1708 if(g->cropping & GRAB_RIGHT) g->handle_x = pzx - (g->clip_w + g->clip_x);
1709 if(g->cropping & GRAB_BOTTOM) g->handle_y = pzy - (g->clip_h + g->clip_y);
1710 }
1711 }
1712 return 1;
1713 }
1714 else if(which == 3)
1715 {
1716 // we reset cropping
1717 g->clip_x = 0.0f;
1718 g->clip_y = 0.0f;
1719 g->clip_w = 1.0f;
1720 g->clip_h = 1.0f;
1722 return 1;
1723 }
1724 else
1725 return 0;
1726}
1727
1729{
1731 if(!g->editing) return 0;
1732
1733 g->shift_hold = FALSE;
1734 g->ctrl_hold = FALSE;
1735 g->cropping = GRAB_NONE;
1736 g->dragging = FALSE;
1737
1738 dt_control_change_cursor(GDK_LEFT_PTR);
1739
1740 return 1;
1741}
1742
1743// #undef PHI
1744// #undef INVPHI
1745
1746// clang-format off
1747// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1748// vim: shiftwidth=2 expandtab tabstop=2 cindent
1749// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1750// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3534
void dt_bauhaus_combobox_set_editable(GtkWidget *widget, int editable)
Definition bauhaus.c:2074
void dt_bauhaus_combobox_set_text(GtkWidget *widget, const char *text)
Definition bauhaus.c:2209
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2347
void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, int toggle)
Definition bauhaus.c:1720
void dt_bauhaus_slider_set_offset(GtkWidget *widget, float offset)
Definition bauhaus.c:3618
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1624
void dt_bauhaus_slider_set_soft_min(GtkWidget *widget, float val)
Definition bauhaus.c:1608
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:2162
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, int paint_flags, void *paint_data)
Definition bauhaus.c:1702
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3611
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
_grab_region_t
Definition clipping.c:142
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
int type
void dt_conf_string_entry_free(gpointer data)
void dt_conf_set_int(const char *name, int val)
GSList * dt_conf_all_string_entries(const char *dir)
int dt_conf_get_int(const char *name)
void dt_control_log(const char *msg,...)
Definition control.c:761
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
Definition control.c:861
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
Definition control.c:918
void dt_control_navigation_redraw()
request redraw of the navigation widget. This redraws the wiget of the navigation module.
Definition control.c:866
#define dt_control_change_cursor(cursor)
Definition control.h:116
#define dt_control_queue_cursor(cursor)
Definition control.h:135
int operation_tags()
Definition crop.c:176
int operation_tags_filter()
Definition crop.c:181
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)
Definition crop.c:409
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
Definition crop.c:333
const char ** description(struct dt_iop_module_t *self)
Definition crop.c:158
int default_group()
Definition crop.c:165
int distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
Definition crop.c:313
static gchar * _aspect_format(gchar *original, int adim, int bdim)
Definition crop.c:1102
int distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
Definition crop.c:293
static void _float_to_fract(const char *num, int *n, int *d)
Definition crop.c:714
const char * aliases()
Definition crop.c:153
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition crop.c:443
const char * name()
Definition crop.c:148
void gui_reset(struct dt_iop_module_t *self)
Definition crop.c:942
void gui_update(struct dt_iop_module_t *self)
Definition crop.c:949
void gui_init(struct dt_iop_module_t *self)
Definition crop.c:1110
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
Definition crop.c:1653
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition crop.c:891
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
Definition crop.c:1634
static void _aspect_apply(dt_iop_module_t *self, _grab_region_t grab)
Definition crop.c:587
void reload_defaults(dt_iop_module_t *self)
Definition crop.c:694
static void _enter_edit_mode(GtkToggleButton *button, struct dt_iop_module_t *self)
Definition crop.c:1010
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition crop.c:187
int flags()
Definition crop.c:170
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
Definition crop.c:1347
static void _commit_box(dt_iop_module_t *self, dt_iop_crop_gui_data_t *g, dt_iop_crop_params_t *p)
Definition crop.c:206
void gui_cleanup(struct dt_iop_module_t *self)
Definition crop.c:1307
int mouse_leave(struct dt_iop_module_t *self)
Definition crop.c:1728
static void _aspect_free(gpointer data)
Definition crop.c:1300
dt_iop_crop_flip_t
Definition crop.c:65
@ FLAG_FLIP_VERTICAL
Definition crop.c:67
@ FLAG_FLIP_HORIZONTAL
Definition crop.c:66
int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition crop.c:381
static void _params_to_gui(dt_iop_crop_params_t *p, dt_iop_crop_gui_data_t *g)
Definition crop.c:192
static void _event_aspect_presets_changed(GtkWidget *combo, dt_iop_module_t *self)
Definition crop.c:750
static gboolean _set_max_clip(dt_dev_pixelpipe_t *pipe, struct dt_iop_module_t *self)
this function initializes the maximum clip rectangle from crop parameters and correct the clip rectan...
Definition crop.c:262
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition crop.c:449
static void _event_key_swap(dt_iop_module_t *self)
Definition crop.c:1002
gboolean has_defaults(struct dt_iop_module_t *self)
Definition crop.c:706
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition crop.c:391
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
Definition crop.c:1482
_grab_region_t
Definition crop.c:87
@ GRAB_VERTICAL
Definition crop.c:98
@ GRAB_ALL
Definition crop.c:99
@ GRAB_BOTTOM
Definition crop.c:92
@ GRAB_BOTTOM_RIGHT
Definition crop.c:95
@ GRAB_NONE
Definition crop.c:100
@ GRAB_HORIZONTAL
Definition crop.c:97
@ GRAB_TOP_RIGHT
Definition crop.c:94
@ GRAB_TOP_LEFT
Definition crop.c:93
@ GRAB_RIGHT
Definition crop.c:91
@ GRAB_TOP
Definition crop.c:90
@ GRAB_BOTTOM_LEFT
Definition crop.c:96
@ GRAB_LEFT
Definition crop.c:89
@ GRAB_CENTER
Definition crop.c:88
static _grab_region_t _gui_get_grab(float pzx, float pzy, dt_iop_crop_gui_data_t *g, const float border, const float wd, const float ht)
Definition crop.c:1316
static void _event_commit_clicked(GtkButton *button, dt_iop_module_t *self)
Definition crop.c:1055
static void _event_aspect_flip(GtkWidget *button, dt_iop_module_t *self)
Definition crop.c:1076
void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
Definition crop.c:365
void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
Definition crop.c:345
static gint _aspect_ratio_cmp(const dt_iop_crop_aspect_t *a, const dt_iop_crop_aspect_t *b)
Definition crop.c:1081
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition crop.c:142
static float _aspect_ratio_get(dt_iop_module_t *self, GtkWidget *combo)
Definition crop.c:455
gboolean runtime_data_hash(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition crop.c:434
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
static gboolean dt_modifiers_include(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:901
@ DT_DEBUG_OPENCL
Definition darktable.h:722
float dt_boundingbox_t[4]
Definition darktable.h:709
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
#define dt_dev_pixelpipe_resync_history_all(dev)
void dt_dev_get_processed_size(const dt_develop_t *dev, int *procw, int *proch)
Definition develop.c:979
dt_dev_pixelpipe_iop_t * dt_dev_distort_get_iop_pipe(struct dt_dev_pixelpipe_t *pipe, struct dt_iop_module_t *module)
Definition develop.c:1593
void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1159
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
Definition develop.c:309
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1557
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:1712
gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Clip the view to the ROI. WARNING: this must be done before any translation.
Definition develop.c:1781
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
Definition develop.c:1824
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
Definition develop.c:1003
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1586
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
Definition develop.h:104
static void dt_draw_set_color_overlay(cairo_t *cr, gboolean bright, double alpha)
Definition draw.h:106
void dtgtk_cairo_paint_aspectflip(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dt_gui_new_collapsible_section(dt_gui_collapsible_section_t *cs, const char *confname, const char *label, GtkBox *parent, GtkPackType pack)
Create a collapsible section and pack it into the parent box.
Definition gtk.c:3102
void dt_gui_draw_rounded_rectangle(cairo_t *cr, float width, float height, float x, float y)
Definition gtk.c:2992
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
Definition gtk.c:3080
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
#define DT_GUI_MODULE(x)
void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height, const float zoom_scale)
Definition guides.c:783
void dt_iop_copy_image_roi(float *const __restrict__ out, const float *const __restrict__ in, const size_t ch, const dt_iop_roi_t *const __restrict__ roi_in, const dt_iop_roi_t *const __restrict__ roi_out, const int zero_pad)
Definition imagebuf.c:159
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2169
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
Definition imageop.c:2915
#define IOP_GUI_FREE
Definition imageop.h:602
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_FLAGS_ONE_INSTANCE
Definition imageop.h:172
@ IOP_FLAGS_GUIDES_SPECIAL_DRAW
Definition imageop.h:178
@ IOP_FLAGS_TILING_FULL_ROI
Definition imageop.h:171
@ IOP_GROUP_EFFECTS
Definition imageop.h:142
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
@ IOP_TAG_DECORATION
Definition imageop.h:152
@ IOP_TAG_CLIPPING
Definition imageop.h:153
@ IOP_TAG_DISTORT
Definition imageop.h:151
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
void *const ovoid
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
@ DT_INTERPOLATION_USERPREF_WARP
static const float x
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
Definition lib.c:1563
float *const restrict const size_t k
#define PHI
Definition math.h:67
#define CLAMPF(a, mn, mx)
Definition math.h:89
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2261
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
struct dt_control_t * control
Definition darktable.h:773
PangoFontDescription * pango_font_desc
Definition bauhaus.h:274
Definition conf.h:89
char * key
Definition conf.h:90
char * value
Definition conf.h:91
int button_down_which
Definition control.h:216
int button_down
Definition control.h:216
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
dt_image_t image_storage
Definition develop.h:259
int32_t preview_height
Definition develop.h:213
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
struct dt_develop_t::@17 roi
int32_t preview_width
Definition develop.h:213
int32_t reset
Definition gtk.h:172
dt_boundingbox_t usercrop
Definition image.h:365
GList * aspect_list
Definition crop.c:106
GtkWidget * cy
Definition crop.c:105
float button_down_zoom_y
Definition crop.c:112
gboolean dragging
Definition crop.c:129
gboolean shift_hold
Definition crop.c:127
GtkWidget * aspect_presets
Definition crop.c:107
dt_gui_collapsible_section_t cs
Definition crop.c:130
GtkWidget * edit_button
Definition crop.c:108
GtkWidget * cx
Definition crop.c:105
float button_down_zoom_x
Definition crop.c:112
gboolean ctrl_hold
Definition crop.c:128
GtkWidget * commit_button
Definition crop.c:109
GtkWidget * ch
Definition crop.c:105
GtkWidget * cw
Definition crop.c:105
dt_iop_crop_params_t previous_params
Definition crop.c:132
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
gboolean enabled
Definition imageop.h:298
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29