diff --git a/doc/colprof.html b/doc/colprof.html
index b99f7c6..2d46b5d 100755
--- a/doc/colprof.html
+++ b/doc/colprof.html
@@ -2700,6 +2700,10 @@ White &nb
useful in creating a profile for a device that is known to have a
perfectly linear response, such as a camera in RAW mode.
+ -u:
+ Emit structured JSON progress events to stdout during profile
+ calculation (e.g. {"event": "progress", "stage": "gamut_mapping", "percent": 75}).
+
-u: Input
profiles will normally be created such that the white patch of the
test chart will be mapped to perfect white when used with any of the
diff --git a/profile/colprof.c b/profile/colprof.c
index 27c154c..14a3307 100644
--- a/profile/colprof.c
+++ b/profile/colprof.c
@@ -75,6 +75,14 @@
#define DEMPH_DEFAULT 1.0 /* Default dark region emphasis == none */
+int json_progress = 0;
+
+void emit_json_progress(const char *stage, int percent) {
+ if (!json_progress) return;
+ printf("{\"event\": \"progress\", \"stage\": \"%s\", \"percent\": %d}\n", stage, percent);
+ fflush(stdout);
+}
+
/*
Flags used:
@@ -129,6 +137,7 @@ void usage(char *diag, ...) {
fprintf(stderr," X = display XYZ cLUT + matrix, Y = display XYZ cLUT + debug matrix\n");
fprintf(stderr," g = gamma+matrix, s = shaper+matrix, m = matrix only,\n");
fprintf(stderr," G = single gamma+matrix, S = single shaper+matrix\n");
+ fprintf(stderr," -u Emit structured JSON calculation progress to stdout\n");
fprintf(stderr," -u If input profile, auto scale WP to allow extrapolation\n");
fprintf(stderr," -ua If input profile, force absolute intent\n");
fprintf(stderr," -uc If input profile, clip cLUT values above WP\n");
@@ -492,8 +501,6 @@ int main(int argc, char *argv[]) {
}
else if (argv[fa][1] == 'u') {
- autowpsc = 1;
- clipovwp = 0;
if (argv[fa][2] == 'a') {
autowpsc = 2;
clipovwp = 0;
@@ -502,12 +509,15 @@ int main(int argc, char *argv[]) {
clipovwp = 1;
} else if (argv[fa][2] != '\000') {
usage("Unknown flag '%c' after -u",argv[fa][2]);
-
- } else if (na != NULL) {
+ } else if (na != NULL && (isdigit(na[0]) || (na[0] == '.' && isdigit(na[1])))) {
fa = nfa;
iwpscale = atof(na);
if (iwpscale < 0.0 || iwpscale > 200.0)
usage("Argument '%s' to flag -u out of range",na);
+ autowpsc = 1;
+ clipovwp = 0;
+ } else {
+ json_progress = 1;
}
}
/* Clip primaries */
diff --git a/profile/prof.h b/profile/prof.h
index 6de0638..cebec55 100644
--- a/profile/prof.h
+++ b/profile/prof.h
@@ -32,6 +32,9 @@ typedef enum {
prof_matonly = 7 /* XYZ matrix, linear */
} prof_atype;
+extern int json_progress;
+void emit_json_progress(const char *stage, int percent);
+
/* Output or Display device */
void make_output_icc(
prof_atype ptype, /* Profile output type */
diff --git a/profile/profin.c b/profile/profin.c
index 2fd9a70..16f8131 100644
--- a/profile/profin.c
+++ b/profile/profin.c
@@ -229,14 +229,19 @@ void in_b2a_clut(void *cntx, double *out, double in[3]
DBG(("convert PCS' to DEV' got %f %f %f %f\n",out[0],out[1],out[2],out[3]))
DBG(("in_b2a_clut returning DEV' %f %f %f\n",out[0],out[1],out[2]))
- if (p->verb
+ if ((p->verb || json_progress)
&& tn == 0
) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
- printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ if (p->verb) {
+ printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("b2a_table", pc);
+ }
p->last = pc;
}
}
@@ -1171,7 +1176,7 @@ make_input_icc(
/* We now setup an exact inverse, colorimetric style */
/* Use helper function to do the hard work. */
- if (cx.verb) {
+ if (cx.verb || json_progress) {
unsigned int ui;
int extra;
cx.count = 0;
@@ -1182,8 +1187,13 @@ make_input_icc(
for (extra = 1, ui = 0; ui < 3; extra *= (blut_clutPoints[ui++]-1))
;
cx.total += extra;
- printf("Creating B to A tables\n");
- printf(" 0%%"); fflush(stdout);
+ if (cx.verb) {
+ printf("Creating B to A tables\n");
+ printf(" 0%%"); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("b2a_table", 0);
+ }
}
if (wr_icco->create_lut_xforms(
diff --git a/profile/profout.c b/profile/profout.c
index 0d2e709..356cc1f 100644
--- a/profile/profout.c
+++ b/profile/profout.c
@@ -431,12 +431,17 @@ void in_a2b_clut(void *cntx, double out[3], double *in, int itn) {
}
DBG(("convert PCS to PCS' got %f %f %f\n",out[0],out[1],out[2]))
- if (p->verb && itn == 0) { /* Output percent intervals */
+ if ((p->verb || json_progress) && itn == 0) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
- printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ if (p->verb) {
+ printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("a2b_table", pc);
+ }
p->last = pc;
}
}
@@ -658,12 +663,17 @@ void out_b2a_clut(void *cntx, double *out, double in[3], int itn) {
DBG(("out_b2a_clut returning DEV' %s\n",icmPdv(p->ochan, out)))
- if (p->verb && itn == 0) { /* Output percent intervals */
+ if ((p->verb || json_progress) && itn == 0) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
- printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ if (p->verb) {
+ printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("b2a_table", pc);
+ }
p->last = pc;
}
}
@@ -728,12 +738,17 @@ static void PCSp_bdist(void *cntx, double out[1], double in[3], int tn
out[0] = (gdist + 20.0)/40.0;
//printf("~1 bdist returning %f\n",out[0]);
- if (p->verb) { /* Output percent intervals */
+ if (p->verb || json_progress) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
- printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ if (p->verb) {
+ printf("%c%2d%%",cr_char,pc); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("gamut_table", pc);
+ }
p->last = pc;
}
}
@@ -2551,6 +2566,7 @@ make_output_icc(
if (csgamp == NULL) {
if (verb)
printf(" Finding Source Colorspace Perceptual Gamut\n");
+ emit_json_progress("gamut_mapping", 10);
if ((csgamp = cx.ixp->get_gamut(cx.ixp, gres)) == NULL)
error ("%d, %s",src_xicc->e.c, src_xicc->e.m);
@@ -2567,6 +2583,7 @@ make_output_icc(
if (verb)
printf(" Finding Source Colorspace Saturation Gamut\n");
+ emit_json_progress("gamut_mapping", 25);
if ((csgams = ixs->get_gamut(ixs, gres)) == NULL)
error ("%d, %s",src_xicc->e.c, src_xicc->e.m);
@@ -2614,6 +2631,7 @@ make_output_icc(
/* Creat the destination gamut surface */
if (verb)
printf(" Finding Destination Gamut\n");
+ emit_json_progress("gamut_mapping", 40);
if ((ogam = cx.ox->get_gamut(cx.ox, gres)) == NULL)
error ("%d, %s",wr_xicc->e.c, wr_xicc->e.m);
@@ -2657,6 +2675,7 @@ make_output_icc(
if (verb)
printf(" Creating Gamut match\n");
+ emit_json_progress("gamut_mapping", 75);
/* The real range of Lab 0..100,-128..128,1-28..128 cube */
/* when mapped to CAM is ridiculously large (ie. */
@@ -2673,6 +2692,7 @@ make_output_icc(
);
if (cx.pmap == NULL)
error ("Failed to make perceptual gamut map transform");
+ emit_json_progress("gamut_mapping", 100);
if (sepsat) {
@@ -2784,7 +2804,7 @@ make_output_icc(
for (i = 0; i < cx.ochan; i++)
a2bgres[i] = a2bres;
- if (cx.verb) {
+ if (cx.verb || json_progress) {
unsigned int ui;
int extra;
cx.count = 0;
@@ -2797,9 +2817,13 @@ make_output_icc(
;
cx.total += extra;
#endif
- printf("Creating inverse gamut mapped A to B tables\n");
-
- printf(" 0%%"); fflush(stdout);
+ if (cx.verb) {
+ printf("Creating inverse gamut mapped A to B tables\n");
+ printf(" 0%%"); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("a2b_table", 0);
+ }
}
#ifdef DEBUG_IGM_ONE
@@ -2879,7 +2903,7 @@ make_output_icc(
/* for perceptual and saturation intents */
/* Use helper function to do the hard work. */
- if (cx.verb) {
+ if (cx.verb || json_progress) {
unsigned int ui;
int extra;
cx.count = 0;
@@ -2892,8 +2916,13 @@ make_output_icc(
;
cx.total += extra;
#endif
- printf("Creating B to A tables\n");
- printf(" 0%%"); fflush(stdout);
+ if (cx.verb) {
+ printf("Creating B to A tables\n");
+ printf(" 0%%"); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("b2a_table", 0);
+ }
}
#ifdef DEBUG_ONE
@@ -3137,13 +3166,18 @@ make_output_icc(
for (i = 0; i < cx.ichan; i++)
clutres[i] = b2ares;
- if (cx.verb) {
+ if (cx.verb || json_progress) {
unsigned int ui;
cx.count = 0;
cx.last = -1;
for (cx.total = 1, ui = 0; ui < cx.ichan; cx.total *= clutres[ui++])
;
- printf(" 0%%"); fflush(stdout);
+ if (cx.verb) {
+ printf(" 0%%"); fflush(stdout);
+ }
+ if (json_progress) {
+ emit_json_progress("gamut_table", 0);
+ }
}
diff --git a/rspl/rev.c b/rspl/rev.c
index 4505fef..d25cb14 100644
--- a/rspl/rev.c
+++ b/rspl/rev.c
@@ -8917,7 +8917,7 @@ static void calc_ocent(rspl *s) {
/* Hmm. This isn't very reliable in detecting failure. */
if (ctx.oog)
- printf("calc_ocent failed to return in-gamut focal point!\n");
+ warning("calc_ocent failed to return in-gamut focal point!");
//printf("Final angle = %f\n", ctx.ret);