aboutsummaryrefslogtreecommitdiffstats
path: root/transform.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-10-11 23:01:38 +0200
committerMattias Andrée <m@maandree.se>2026-10-11 23:29:28 +0200
commit94659550993f454e12e4bcb8cf93a5ff258bc08c (patch)
treef8e1cfe9fbcdec6f09b6cc18c761684dc34505d2 /transform.c
parentFirst commit (diff)
downloadgbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.gz
gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.bz2
gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.xz
Second commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'transform.c')
-rw-r--r--transform.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/transform.c b/transform.c
new file mode 100644
index 0000000..b9461d7
--- /dev/null
+++ b/transform.c
@@ -0,0 +1,172 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+enum transformation trans = TRANS_NONE;
+
+static GdkPixbuf *
+make_negative(GdkPixbuf *image)
+{
+ GdkPixbuf *new_image;
+ uint8_t *pixels;
+ int width, height, rowstride, channels, x, y;
+
+ new_image = gdk_pixbuf_copy(image);
+ if (!new_image)
+ exit(1);
+
+ pixels = gdk_pixbuf_get_pixels(new_image);
+ width = gdk_pixbuf_get_width(new_image);
+ height = gdk_pixbuf_get_height(new_image);
+ rowstride = gdk_pixbuf_get_rowstride(new_image);
+ channels = gdk_pixbuf_get_n_channels(new_image);
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width * channels; x += channels) {
+ /* TODO
+ * use libcolour to convert to CIE LChuv
+ * (or CIE LChab which isn't supported yet),
+ * then invert the lightness and the hue
+ * and then convert back to sRGB; however
+ * we must account for the fact that some
+ * pixels may be out of gamut
+ *
+ * TODO
+ * this may be a bit slow, so we to use
+ * opencl if available and otherwise use
+ * libcolour_convert_en_masse as much as
+ * possible and cache the negative for
+ * each file
+ *
+ * TODO
+ * it shall be a configuration in Prefernces
+ * what model is used for colour inversion
+ */
+ pixels[x + 0] = 255 - pixels[x + 0];
+ pixels[x + 1] = 255 - pixels[x + 1];
+ pixels[x + 2] = 255 - pixels[x + 2];
+ }
+ pixels = &pixels[rowstride];
+ }
+
+ return new_image;
+}
+
+static GdkPixbuf *
+apply_rotation_and_reflection(GdkPixbuf *image)
+{
+ GdkPixbuf *new_image, *newer_image;
+
+ switch (trans & 7u) {
+ case TRANS_NONE:
+ return NULL;
+ case TRANS_CLOCKWISE:
+ new_image = gdk_pixbuf_rotate_simple(image, GDK_PIXBUF_ROTATE_CLOCKWISE);
+ break;
+ case TRANS_180_DEGREES:
+ new_image = gdk_pixbuf_rotate_simple(image, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
+ break;
+ case TRANS_ANTICLOCKWISE:
+ new_image = gdk_pixbuf_rotate_simple(image, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
+ break;
+ case TRANS_FLIP:
+ case TRANS_TRANSPOSE:
+ new_image = gdk_pixbuf_flip(image, 0);
+ break;
+ case TRANS_FLOP:
+ case TRANS_ANTITRANSPOSE:
+ new_image = gdk_pixbuf_flip(image, 1);
+ break;
+ }
+ if (!new_image)
+ exit(1);
+
+ switch (trans & 7u) {
+ case TRANS_TRANSPOSE:
+ case TRANS_ANTITRANSPOSE:
+ newer_image = gdk_pixbuf_rotate_simple(new_image, GDK_PIXBUF_ROTATE_CLOCKWISE);
+ if (!newer_image)
+ exit(1);
+ g_object_unref(new_image);
+ return newer_image;
+ default:
+ return new_image;
+ }
+}
+
+GdkPixbuf *
+apply_transformations(GdkPixbuf *image)
+{
+ GdkPixbuf *negative_image, *new_image;
+ negative_image = (trans & TRANS_NEGATIVE) ? make_negative(image) : NULL;
+ new_image = apply_rotation_and_reflection(negative_image ? negative_image : image);
+ if (!new_image)
+ new_image = negative_image ? negative_image : image;
+ else if (negative_image)
+ g_object_unref(negative_image);
+ return new_image == image ? NULL : new_image;
+}
+
+static enum transformation
+inverse_transformation(enum transformation t)
+{
+ return ((4u - t) & 3u) | (t & ~3u);
+}
+
+enum transformation
+reset_transformations_without_redraw(void)
+{
+ enum transformation augment = inverse_transformation(trans & 7u);
+ trans &= ~7u;
+ return augment;
+}
+
+int
+add_transformation_without_redraw(enum transformation augment)
+{
+ enum transformation old = trans;
+ if (augment & TRANS_TRANSPOSE)
+ trans = ((augment - trans) & 3u) | ((augment ^ trans) & ~3u);
+ else
+ trans = ((augment + trans) & 3u) | ((augment ^ trans) & ~3u);
+ return trans != old;
+}
+
+void
+transform_point(enum transformation t, double *x, double *y, double *w, double *h)
+{
+ double x1 = *x, y1 = *y, x2 = *w, y2 = *h;
+
+ switch (t & 7u) {
+ case TRANS_NONE:
+ return;
+ case TRANS_CLOCKWISE:
+ *x = y2 - y1;
+ *y = x1;
+ break;
+ case TRANS_180_DEGREES:
+ *x = x2 - x1;
+ *y = y2 - y1;
+ return;
+ case TRANS_ANTICLOCKWISE:
+ *x = y1;
+ *y = x2 - x1;
+ break;
+ case TRANS_TRANSPOSE:
+ *x = y1;
+ *y = x1;
+ break;
+ case TRANS_FLIP:
+ *y = y2 - y1;
+ return;
+ case TRANS_ANTITRANSPOSE:
+ *x = y2 - y1;
+ *y = x2 - x1;
+ break;
+ case TRANS_FLOP:
+ *x = x2 - x1;
+ return;
+ }
+
+ *w = y2;
+ *h = x2;
+}