From c69d205d5c58fcb3a8cc0e819b27fb3701e6e228 Mon Sep 17 00:00:00 2001 From: Hanno Schwalm Date: Sat, 30 Nov 2024 15:49:54 +0100 Subject: [PATCH] Crop module gets orientation proxy support While developing an image in darkroom we might have set a cropping area, this commit implements functionality to keep the cropped area if we change orientation via the flip module. Three parts in the codebase required additions without changing existing code. 1. `dt_develop_t` got two additions in cropping proxy, `struct dt_iop_module_t *flip_handler` points to the crop module and is setup there. We can't use `exposer` as the proxy because that is dynamically set in pixelpipe code only if enabled and we want to change crop parameters even if crop is disabled. `void (*flip_callback)` is the callback function changing crop parameters, defined in crop. 2. Orientation module uses the `flip_callback(self, orientation)` requesting changes in crop. 3. In crop we have `_crop_handle_flip()` as proxy `flip_callback` with proper logs about action. - It gets the data from self `dt_iop_crop_params_t`, - does the requested action, - updates gui from parameters - adds a new history stack entry (respecting the current `crop->enabled` status). --- src/develop/develop.h | 7 ++++++- src/iop/crop.c | 26 ++++++++++++++++++++++---- src/iop/flip.c | 22 ++++++++++++++++++---- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/develop/develop.h b/src/develop/develop.h index 9218201f466d..8e4836c9eb4b 100644 --- a/src/develop/develop.h +++ b/src/develop/develop.h @@ -289,11 +289,16 @@ typedef struct dt_develop_t dt_dev_chroma_t chroma; - // for exposing the crop + // for exposing and handling the crop struct { // set by dt_dev_pixelpipe_synch() if an enabled crop module is included in history struct dt_iop_module_t *exposer; + + // proxy to change crop settings via flip module + struct dt_iop_module_t *flip_handler; + void (*flip_callback)(struct dt_iop_module_t *crop, + const dt_image_orientation_t flipmode); } cropping; // for the overexposure indicator diff --git a/src/iop/crop.c b/src/iop/crop.c index 07e065c8a32a..231908faf449 100644 --- a/src/iop/crop.c +++ b/src/iop/crop.c @@ -236,8 +236,7 @@ static gboolean _set_max_clip(dt_iop_module_t *self) // we want to know the size of the actual buffer dt_dev_pixelpipe_t *fpipe = self->dev->full.pipe; - dt_dev_pixelpipe_iop_t *piece = - dt_dev_distort_get_iop_pipe(self->dev, fpipe, self); + const dt_dev_pixelpipe_iop_t *piece = dt_dev_distort_get_iop_pipe(self->dev, fpipe, self); if(!piece) return FALSE; const float wp = piece->buf_out.width; @@ -536,8 +535,7 @@ static float _aspect_ratio_get(dt_iop_module_t *self, GtkWidget *combo) } // we want to know the size of the actual buffer - dt_dev_pixelpipe_iop_t *piece = - dt_dev_distort_get_iop_pipe(self->dev, self->dev->preview_pipe, self); + const dt_dev_pixelpipe_iop_t *piece = dt_dev_distort_get_iop_pipe(self->dev, self->dev->preview_pipe, self); if(!piece) return 0.0f; const int iwd = piece->buf_in.width, iht = piece->buf_in.height; @@ -1117,6 +1115,23 @@ static gchar *_aspect_format(gchar *original, return g_strdup_printf("%s %4.2f", original, (float)adim / (float)bdim); } +static void _crop_handle_flip(dt_iop_module_t *self, const dt_image_orientation_t mode) +{ + dt_iop_crop_params_t *p = self ? self->params : NULL; + if(!p || (p->cx == 0.f && p->cy == 0.f && p->cw == 1.f && p->ch == 1.f)) + return; + + const float ocx = p->cx; + const float ocy = p->cy; + if(mode == ORIENTATION_FLIP_HORIZONTALLY) {p->cx = 1.f-p->cw; p->cw = 1.f-ocx;} + else if(mode == ORIENTATION_FLIP_VERTICALLY) {p->cy = 1.f-p->ch; p->ch = 1.f-ocy;} + else if(mode == ORIENTATION_ROTATE_CW_90_DEG) {p->cx = 1.f-p->ch; p->ch = p->cw; p->cw = 1.f-p->cy; p->cy = ocx;} + else if(mode == ORIENTATION_ROTATE_CCW_90_DEG) {p->cx = p->cy; p->cy = 1.f-p->cw; p->cw = p->ch; p->ch = 1.f-ocx;} + + dt_iop_gui_update(self); + dt_dev_add_history_item(darktable.develop, self, self->enabled); +} + void gui_init(dt_iop_module_t *self) { dt_iop_crop_gui_data_t *g = IOP_GUI_ALLOC(crop); @@ -1308,6 +1323,9 @@ void gui_init(dt_iop_module_t *self) _("the bottom margin cannot overlap with the top margin")); self->widget = box_enabled; + + darktable.develop->cropping.flip_handler = self; + darktable.develop->cropping.flip_callback = _crop_handle_flip; } static void _aspect_free(gpointer data) diff --git a/src/iop/flip.c b/src/iop/flip.c index 12750303dad2..6d76a931871a 100644 --- a/src/iop/flip.c +++ b/src/iop/flip.c @@ -431,7 +431,7 @@ void commit_params(dt_iop_module_t *self, d->orientation = p->orientation; if(d->orientation == ORIENTATION_NONE) - piece->enabled = 0; + piece->enabled = FALSE; } void init_pipe(dt_iop_module_t *self, @@ -518,6 +518,20 @@ void reload_defaults(dt_iop_module_t *self) } } +static void _crop_callback(dt_iop_module_t *self, + const dt_image_orientation_t mode) +{ + dt_develop_t *dev = darktable.develop; + dt_iop_module_t *cropper = dev->cropping.flip_handler; + + // FIXME: can we "compress" history here by checking for a flip/crop pair on top of history + // and a) drop the crop on top of history and b) compress flip in one step + + dt_dev_add_history_item(dev, self, TRUE); + if(cropper && dev->cropping.flip_callback) + dev->cropping.flip_callback(cropper, mode); +} + static void do_rotate(dt_iop_module_t *self, uint32_t cw) { dt_iop_flip_params_t *p = self->params; @@ -543,7 +557,7 @@ static void do_rotate(dt_iop_module_t *self, uint32_t cw) orientation ^= ORIENTATION_SWAP_XY; p->orientation = orientation; - dt_dev_add_history_item(darktable.develop, self, TRUE); + _crop_callback(self, cw ? ORIENTATION_ROTATE_CW_90_DEG : ORIENTATION_ROTATE_CCW_90_DEG); } static void rotate_cw(GtkWidget *widget, dt_iop_module_t *self) @@ -569,7 +583,7 @@ static void _flip_h(GtkWidget *widget, dt_iop_module_t *self) else p->orientation = orientation ^ ORIENTATION_FLIP_HORIZONTALLY; - dt_dev_add_history_item(darktable.develop, self, TRUE); + _crop_callback(self, ORIENTATION_FLIP_HORIZONTALLY); } static void _flip_v(GtkWidget *widget, dt_iop_module_t *self) @@ -586,7 +600,7 @@ static void _flip_v(GtkWidget *widget, dt_iop_module_t *self) else p->orientation = orientation ^ ORIENTATION_FLIP_VERTICALLY; - dt_dev_add_history_item(darktable.develop, self, TRUE); + _crop_callback(self, ORIENTATION_FLIP_VERTICALLY); } void gui_init(dt_iop_module_t *self)