about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/Makefile2
-rw-r--r--editor/pamhue.c209
2 files changed, 210 insertions, 1 deletions
diff --git a/editor/Makefile b/editor/Makefile
index de5ae666..08822ede 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -19,7 +19,7 @@ SUBDIRS = pamflip specialty
 PORTBINARIES = pamaddnoise pamaltsat pambackground pamcomp pamcut \
 	       pamdice pamditherbw pamedge \
 	       pamenlarge \
-	       pamfunc pamlevels pammasksharpen pammixmulti \
+	       pamfunc pamhue pamlevels pammasksharpen pammixmulti \
 	       pamperspective pamrecolor pamrubber \
 	       pamscale pamsistoaglyph pamstretch pamthreshold pamundice \
 	       pamwipeout \
diff --git a/editor/pamhue.c b/editor/pamhue.c
new file mode 100644
index 00000000..7dddedd8
--- /dev/null
+++ b/editor/pamhue.c
@@ -0,0 +1,209 @@
+/*=============================================================================
+                                  pamhue
+===============================================================================
+  Change the hue, the Hue-Saturation-Value model, every pixel in an image
+  by a specified angle.
+=============================================================================*/
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* '-' if stdin */
+    float        huechange;
+};
+
+
+
+static void
+parseCommandLine(int                        argc,
+                 const char **              argv,
+                 struct CmdlineInfo * const cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int huechangeSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,  "huechange",          OPT_FLOAT,
+            &cmdlineP->huechange,           &huechangeSpec,             0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;    /* No negative arguments */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!huechangeSpec)
+        pm_error("You must specify -huechange");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  file specification");
+}
+
+
+
+static float
+positiveMod(float const arg,
+            float const modulus) {
+/*----------------------------------------------------------------------------
+   'arg' mod 'modulus', but positive (i.e. in the range 0.0 - 'modulus').
+-----------------------------------------------------------------------------*/
+    float const mod = fmodf(arg, modulus);
+
+    return mod >= 0.0 ? mod : 360 + mod;
+}
+
+
+
+static void
+changeHue(tuple   const tupleval,
+          float   const huechange,
+          sample  const maxval) {
+
+    pixel oldRgb, newRgb;
+    struct hsv oldHsv, newHsv;
+
+    PPM_PUTR(oldRgb, tupleval[PAM_RED_PLANE]);
+    PPM_PUTG(oldRgb, tupleval[PAM_GRN_PLANE]);
+    PPM_PUTB(oldRgb, tupleval[PAM_BLU_PLANE]);
+
+    oldHsv = ppm_hsv_from_color(oldRgb, maxval);
+
+    newHsv.h = positiveMod(oldHsv.h + huechange, 360.0);
+    newHsv.s = oldHsv.s;
+    newHsv.v = oldHsv.v;
+
+    newRgb = ppm_color_from_hsv(newHsv, maxval);
+
+    tupleval[PAM_RED_PLANE] = PPM_GETR(newRgb);
+    tupleval[PAM_GRN_PLANE] = PPM_GETG(newRgb);
+    tupleval[PAM_BLU_PLANE] = PPM_GETB(newRgb);
+}
+
+
+
+static void
+convertRow(tuple *            const tuplerow,
+           float              const huechange,
+           const struct pam * const pamP) {
+
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col)  {
+        if ((pamP->format == PPM_FORMAT) || (pamP->format == RPPM_FORMAT) ||
+                 ((pamP->format == PAM_FORMAT) && (pamP->depth >= 3))) {
+            /* It's a color image, so there is a hue to change */
+
+            changeHue(tuplerow[col], huechange, pamP->maxval);
+        } else {
+            /* It's black and white or grayscale, which means fully
+               desaturated, so hue is meaningless.  Nothing to change.
+            */
+        }
+    }
+}
+
+
+
+static void
+pamhue(struct CmdlineInfo const cmdline,
+       FILE *             const ifP,
+       FILE *             const ofP) {
+
+    struct pam inpam, outpam;
+    tuple * tuplerow;
+    unsigned int row;
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    outpam = inpam;
+    outpam.file = ofP;
+
+    pnm_writepaminit(&outpam);
+
+    tuplerow = pnm_allocpamrow(&inpam);
+
+    for (row = 0; row < inpam.height; ++row) {
+        pnm_readpamrow(&inpam, tuplerow);
+
+        convertRow(tuplerow, cmdline.huechange, &inpam);
+
+        pnm_writepamrow(&outpam, tuplerow);
+    }
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pamhue(cmdline, ifP, stdout);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
+
+
+
+/* This was derived by Bryan Henderson from code by Willem van Schaik
+   (willem@schaik.com), which was derived from Ppmbrighten code written by Jef
+   Poskanzer and Brian Moffet.
+
+   Copyright (C) 1989 by Jef Poskanzer.
+   Copyright (C) 1990 by Brian Moffet.
+   Copyright (C) 2019 by Willem van Schaik (willem@schaik.com)
+
+   Permission to use, copy, modify, and distribute this software and its
+   documentation for any purpose and without fee is hereby granted, provided
+   that the above copyright notice appear in all copies and that both that
+   copyright notice and this permission notice appear in supporting
+   documentation.  This software is provided "as is" without express or
+   implied warranty.
+
+   Bryan contributes his work to the public domain.
+*/