/* * International Color Consortium color transform expanded support * * Author: Graeme W. Gill * Date: 11/11/2004 * Version: 1.00 * * Copyright 2000-2004 Graeme W. Gill * All rights reserved. * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- * see the License.txt file for licencing details. * */ /* * This is some test code to test the CIECAM02 continuity. * This test creates file "cam02plot.tif" containing values that * have been converted to and back from CIECAM02 space. */ #include #include #include #include "copyright.h" #include "aconfig.h" #include "numlib.h" #include "icc.h" #include "xcam.h" #include "cam02.h" #include "tiffio.h" #include "cam02ref.h" #define DEFRES 100 /* Default cube resolution */ #define NORMAL_XYZ2RGB #undef NIEVE_XYZ2RGB #undef COMPLEX_XYZ2RGB #define SCALE 1.0 /* Compress RGB by scale towards grey */ #ifndef _isnan #define _isnan(x) ((x) != (x)) #define _finite(x) ((x) == 0.0 || (x) * 1.0000001 != (x)) #endif static void Lab2XYZ(double *out, double *in) { double L = in[0], a = in[1], b = in[2]; double x,y,z,fx,fy,fz; if (L > 8.0) { fy = (L + 16.0)/116.0; y = pow(fy,3.0); } else { y = L/903.2963058; fy = 7.787036979 * y + 16.0/116.0; } fx = a/500.0 + fy; if (fx > 24.0/116.0) x = pow(fx,3.0); else x = (fx - 16.0/116.0)/7.787036979; fz = fy - b/200.0; if (fz > 24.0/116.0) z = pow(fz,3.0); else z = (fz - 16.0/116.0)/7.787036979; out[0] = x * 0.9642; out[1] = y * 1.0000; out[2] = z * 0.8249; } /* CIE XYZ to perceptual Lab */ static void XYZ2Lab(double *out, double *in) { double X = in[0], Y = in[1], Z = in[2]; double x,y,z,fx,fy,fz; x = X/0.9642; y = Y/1.0000; z = Z/0.8249; if (x > 0.008856451586) fx = pow(x,1.0/3.0); else fx = 7.787036979 * x + 16.0/116.0; if (y > 0.008856451586) fy = pow(y,1.0/3.0); else fy = 7.787036979 * y + 16.0/116.0; if (z > 0.008856451586) fz = pow(z,1.0/3.0); else fz = 7.787036979 * z + 16.0/116.0; out[0] = 116.0 * fy - 16.0; out[1] = 500.0 * (fx - fy); out[2] = 200.0 * (fy - fz); } #ifdef NORMAL_XYZ2RGB static double sign(double x) { if (x < 0.0) return -1.0; else return 1.0; } /* Version with reasonable clipping. Converts to Lab, */ /* clips the Lab an then converts to sRGB */ /* Convert from XYZ to sRGB */ void XYZ2sRGB(double *out, double *in, int trace) { double lab[3]; double tmp[3]; double scale = SCALE; /* Scale RGB towards grey */ double mat[3][3] = { { 3.2410, -1.5374, -0.4986 }, { -0.9692, 1.8760, 0.0416 }, { 0.0556, -0.2040, 1.0570 } }; int i; // ~~999 trace = 0; /* Copy from input */ for(i = 0; i < 3; i++) tmp[i] = in[i]; if (trace) printf("XYZ to RGB\n"); if (trace) printf("input XYZ %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Clip to reasonable range */ // if (tmp[1] <= 0.0) // tmp[0] = tmp[2] = 0.0; XYZ2Lab(lab, tmp); if (trace) printf("Lab %f %f %f\n",tmp[0],tmp[1],tmp[2]); icmClipLab(lab, lab); if (trace) printf("Clipped Lab %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* We're going to assume that L == 0 is always black */ if (lab[0] <= 0.0) { lab[1] = lab[2] = 0.0; } if (trace) printf("Clipped Lab2 %f %f %f\n",tmp[0],tmp[1],tmp[2]); Lab2XYZ(tmp, lab); if (trace) printf("XYZ %f %f %f\n",tmp[0],tmp[1],tmp[2]); icmClipXYZ(tmp, tmp); if (trace) printf("clipped XYZ %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Now convert to sRGB assuming D50 (??) white point */ icmMulBy3x3(tmp, mat, tmp); /* Scale */ for(i = 0; i < 3; i++) { tmp[i] = ((tmp[i] - 0.5) * scale) + 0.5; } if (trace) printf("raw RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Apply gamma */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.00304) { tmp[i] = tmp[i] * 12.92; } else { tmp[i] = 1.055 * pow(tmp[i], 1.0/2.4) - 0.055; } } if (trace) printf("gamma corrected RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); #ifndef NEVER /* Clip to nearest */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.0) tmp[i] = 0.0; else if (tmp[i] > 1.0) tmp[i] = 1.0; } #else /* Clip towards center */ { double biggest = -100.0; for(i = 0; i < 3; i++) { tmp[i] -= 0.5; if (fabs(tmp[i]) > biggest) biggest = fabs(tmp[i]); } for(i = 0; i < 3; i++) { if (biggest > 0.5) tmp[i] *= 0.5/biggest; tmp[i] += 0.5; } } #endif if (trace) printf("output clipped RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Copy to output */ for(i = 0; i < 3; i++) out[i] = tmp[i]; } #endif /* NORMAL_XYZ2RGB */ #ifdef NIEVE_XYZ2RGB /* Nieve version with RGB clipping */ /* Convert from XYZ to sRGB */ void XYZ2sRGB(double *out, double *in, int trace) { double tmp[3]; double scale = SCALE; /* Scale RGB towards grey */ double mat[3][3] = { { 3.2410, -1.5374, -0.4986 }, { -0.9692, 1.8760, 0.0416 }, { 0.0556, -0.2040, 1.0570 } }; int i; if (trace) printf("XYZ to RGB\n"); if (trace) printf("input XYZ %f %f %f\n",in[0],in[1],in[2]); /* Now convert to sRGB assuming D50 (??) white point */ icmMulBy3x3(tmp, mat, in); /* Scale */ for(i = 0; i < 3; i++) { tmp[i] = ((tmp[i] - 0.5) * scale) + 0.5; } if (trace) printf("raw RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Clip to nearest */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.0) tmp[i] = 0.0; else if (tmp[i] > 1.0) tmp[i] = 1.0; } if (trace) printf("clipped RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Apply gamma */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.00304) { tmp[i] = tmp[i] * 12.92; } else { tmp[i] = 1.055 * pow(tmp[i], 1.0/2.4) - 0.055; } } if (trace) printf("output gamma corrected RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Copy to output */ for(i = 0; i < 3; i++) out[i] = tmp[i]; } #endif /* NIEVE_XYZ2RGB */ #ifdef COMPLEX_XYZ2RGB /* Complex version using powell to ensure good clipping */ /* Callback context */ typedef struct { double scale; double lab[3]; double imat[3][3]; } ctx; double clipf(void *fdata, double tp[]) { ctx *x = (ctx *)fdata; double lab[3], tmp[3], tt; int k; double rv = 0.0; //printf("\n"); //printf("~1 clipf got %f %f %f\n",tp[0],tp[1],tp[2]); /* Penalize for being out of gamut */ for (k = 0; k < 3; k++) { if (tp[k] < 0.0) { tt = 100000.0 * (0.0 - tp[k]); rv += tt * tt; } else if (tp[k] > 1.0) { tt = 100000.0 * (tp[k] - 1.0); rv += tt * tt; } } //printf("~1 rv out of gamut = %f\n",rv); /* Unscale it */ for(k = 0; k < 3; k++) tmp[k] = ((tp[k] - 0.5) / x->scale) + 0.5; //printf("~1 unscale %f %f %f\n",tmp[0],tmp[1],tmp[2]); icmMulBy3x3(tmp, x->imat, tmp); /* To XYZ */ //printf("~1 xyz %f %f %f\n",tmp[0],tmp[1],tmp[2]); XYZ2Lab(lab, tmp); /* To Lab */ //printf("~1 lab = %f %f %f\n",lab[0],lab[1],lab[2]); //printf("~1 target = %f %f %f\n",x->lab[0],x->lab[1],x->lab[2]); /* Compute an error to the goal */ tt = 10000.0 * (lab[0] - x->lab[0]); rv += tt * tt; tt = 1.0 * (lab[1] - x->lab[1]); rv += tt * tt; tt = 1.0 * (lab[2] - x->lab[2]); rv += tt * tt; //printf("~1 rv = %f\n",rv); return rv; } /* Convert from XYZ to sRGB */ void XYZ2sRGB(double *out, double *in, int trace) { double lab[3], tmp[3]; double scale = SCALE; /* Scale RGB towards grey */ double mat[3][3] = { { 3.2410, -1.5374, -0.4986 }, { -0.9692, 1.8760, 0.0416 }, { 0.0556, -0.2040, 1.0570 } }; int i; /* Copy from input */ for(i = 0; i < 3; i++) tmp[i] = in[i]; if (trace) printf("XYZ to RGB\n"); if (trace) printf("input XYZ %f %f %f\n",tmp[0],tmp[1],tmp[2]); XYZ2Lab(lab, tmp); if (trace) printf("Lab %f %f %f\n",lab[0],lab[1],lab[2]); icmClipLab(lab, lab); if (trace) printf("Clipped Lab %f %f %f\n",lab[0],lab[1],lab[2]); /* Now convert to sRGB assuming D50 (??) white point */ Lab2XYZ(tmp, lab); icmMulBy3x3(tmp, mat, tmp); /* Scale */ for(i = 0; i < 3; i++) { tmp[i] = ((tmp[i] - 0.5) * scale) + 0.5; } if (trace) printf("raw RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* See if it need clipping */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.0 || tmp[i] > 1.0) break; } /* It needs more clipping */ if (i < 3) { ctx x; double ss[3] = { 0.1, 0.1, 0.1 }; x.lab[0] = lab[0]; x.lab[1] = lab[1]; x.lab[2] = lab[2]; x.scale = scale; icmInverse3x3(x.imat, mat); if (powell(NULL, 3, tmp, ss, 1e-6, 1000, clipf, (void *)&x) < 0.0) error("RGB clip failed"); } if (trace) printf("RGB clip %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Clip to nearest */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.0) tmp[i] = 0.0; else if (tmp[i] > 1.0) tmp[i] = 1.0; } if (trace) printf("clip to nearest RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Apply gamma */ for(i = 0; i < 3; i++) { if (tmp[i] < 0.00304) { tmp[i] = tmp[i] * 12.92; } else { tmp[i] = 1.055 * pow(tmp[i], 1.0/2.4) - 0.055; } } if (trace) printf("gamma corrected RGB %f %f %f\n",tmp[0],tmp[1],tmp[2]); /* Copy to output */ for(i = 0; i < 3; i++) out[i] = tmp[i]; } #endif /* COMPLEX_XYZ2RGB */ void usage(char *diag) { fprintf(stderr,"Create a 3D slice plot of XYZ/Lab <-> Jab, Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); if (diag != NULL) fprintf(stderr,"Diagnostic '%s'\n",diag); fprintf(stderr,"usage: cam02plot [-options] x y\n"); fprintf(stderr,"'cam02plot -l -o' shows problem best\n"); fprintf(stderr," -v level Verbosity level 0 - 2 (default = 1)\n"); fprintf(stderr," -n Don't use Helmholtz-Kohlraush flag\n"); fprintf(stderr," -f Use the reference CIECAM transform\n"); fprintf(stderr," -r res Resolution (default %d)\n",DEFRES); fprintf(stderr," -l Lab source cube (default XYZ source cube)\n"); fprintf(stderr," -i XYZ/Lab -> Jab (default XYZ/Lab -> Lab)\n"); fprintf(stderr," -o Jab -> XYZ -> RGB (defult Lab -> XYZ -> RGB\n"); fprintf(stderr," -s XYZ/Lab -> ab scale as RGB\n"); fprintf(stderr," -x Cross section plots\n"); fprintf(stderr," x y Return input color for this coordinate\n"); exit(1); } int main(int argc, char *argv[]) { int fa,nfa; /* argument we're looking at */ char *tiffname = "cam02plot.tif"; TIFF *wh = NULL; uint16 resunits = RESUNIT_INCH; float resx = 75.0, resy = 75.0; tdata_t *obuf; unsigned char *ob; int verb = 0; int transl = 0; /* Translate x,y into input color */ int tx = 0, ty = 0; /* coordinate to translate */ int use_hk = 1; /* Use Helmholtz-Kohlraush flag */ int use_ref = 0; /* Use reference transform rather than working code */ int lab_plot = 0; /* Lab <-> Jab plot, else XYZ <-> Jab */ int to_jab = 0; /* Convert to Jab, else convert to Lab */ int from_jab = 0; /* Convert from Jab, else convert from Lab */ int to_ss = 0; /* Convert from XYZ/Lab to abs scale ss */ int xsect = 0; /* Cross section plots */ double white[9][3] = { { 0.964242, 1.000000, 0.825124 }, /* 0: D50 */ { 1.098494, 1.000000, 0.355908 }, /* 1: A */ { 0.908731, 1.000000, 0.987233 }, /* 2: F5 */ { 1.128981, 1.000000, 0.305862 }, /* 3: D25 */ { 0.950471, 1.000000, 1.088828 }, /* 4: D65 */ { 0.949662, 1.000000, 1.160438 }, /* 5: D80 */ { 0.949662, 1.000000, 1.160438 }, /* 6: D80 */ { 0.951297, 1.000000, 1.338196 }, /* 7: D85 */ { 0.952480, 1.000000, 1.386693 } /* 8: D90 */ }; #ifdef NEVER double La[6] = { 10.0, 31.83, 100.0, 318.31, 1000.83, 3183.1 }; ViewingCondition Vc[4] = { vc_average, vc_dark, vc_dim, vc_cut_sheet }; #endif /* NEVER */ int i, j, k, m; double xyz[3], Jab[3], rgb[3]; cam02 *cam1, *cam2; int res = DEFRES; /* Resolution of scan through space */ int ares; /* Array of 2d sub-rasters */ int w, h; /* Width and height of raster */ int x, y; int sp = 5; /* Spacing beween resxres sub-images */ error_program = argv[0]; /* Process the arguments */ for(fa = 1;fa < argc;fa++) { nfa = fa; /* skip to nfa if next argument is used */ if (argv[fa][0] == '-') { /* Look for any flags */ char *na = NULL; /* next argument after flag, null if none */ if (argv[fa][2] != '\000') na = &argv[fa][2]; /* next is directly after flag */ else { if ((fa+1) < argc) { if (argv[fa+1][0] != '-') { nfa = fa + 1; na = argv[nfa]; /* next is seperate non-flag argument */ } } } if (argv[fa][1] == '?') usage("Requested usage"); /* Verbosity */ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { fa = nfa; if (na == NULL) verb = 2; else { if (na[0] == '0') verb = 0; else if (na[0] == '1') verb = 1; else if (na[0] == '2') verb = 2; else usage("Illegal verbosity level"); } } else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') { fa = nfa; if (na == NULL) usage("Need resolution after -r"); res = atoi(na); if (res < 2 || res > 1000) usage("Resolution is out of range"); } else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { use_hk = 0; } else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') { use_ref = 1; } else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') { lab_plot = 1; } else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') { to_jab = 1; } else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { from_jab = 1; } else if (argv[fa][1] == 's' || argv[fa][1] == 'S') { to_ss = 1; } else if (argv[fa][1] == 'x' || argv[fa][1] == 'X') { xsect = 1; } else usage("Unknown flag"); } else break; } if ((fa+1) < argc) { tx = atoi(argv[fa]); ty = atoi(argv[fa+1]); transl = 1; } /* Setup cam to convert to Jab */ if (use_ref) cam1 = new_cam02ref(); else cam1 = new_cam02(); cam1->set_view( cam1, vc_average, /* Enumerated Viewing Condition */ white[4], /* Reference/Adapted White XYZ (Y range 0.0 .. 1.0) */ 1000.0, /* Adapting/Surround Luminance cd/m^2 */ 0.20, /* Relative Luminance of Background to reference white */ 0.0, /* Luminance of white in image - not used */ 0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */ white[4], /* The Glare color coordinates (typically the Ambient color) */ use_hk /* use Helmholtz-Kohlraush flag */ ); /* Setup cam to convert from Jab */ if (use_ref) cam2 = new_cam02ref(); else cam2 = new_cam02(); cam2->set_view( cam2, vc_average, /* Enumerated Viewing Condition */ white[4], /* Reference/Adapted White XYZ (Y range 0.0 .. 1.0) */ 1000.0, /* Adapting/Surround Luminance cd/m^2 */ 0.20, /* Relative Luminance of Background to reference white */ 0.0, /* Luminance of white in image - not used */ 0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */ white[4], /* The Glare color coordinates (typically the Ambient color) */ use_hk /* use Helmholtz-Kohlraush flag */ ); /* Figure out the size of the raster */ ares = (int)ceil(sqrt((double)res)); if (verb) printf("For res %d, ares = %d\n",res,ares); w = ares * res + sp * (ares+1); h = ares * res + sp * (ares+1); if (to_ss) { cam1->retss = 1; } if (transl) { int ix,iy,iz; double dx,dy,dz; double tmp[3], lab[3]; cam1->trace = 1; cam2->trace = 1; //printf("~1 translate %d %d\n",tx,ty); tx -= sp; /* Border at base */ ty -= sp; //printf("~1 offset %d %d\n",tx,ty); ix = tx % (res + sp); iy = ty % (res + sp); iz = (ty / (res + sp)) * ares + (tx / (res + sp)); //printf("~1 x y z = %d %d %d\n", ix,iy,iz); dx = ix/(res-1.0); dy = iy/(res-1.0); dz = iz/((ares * ares)-1.0); //printf("~1 X Y Z = %f %f %f\n", dx,dy,dz); /* Source range is extended L*a*b* type */ if (lab_plot) { if (xsect) { lab[0] = 180.0 * dz - 40.0; lab[1] = 300.0 * dy - 150.0; lab[2] = 300.0 * dx - 150.0; } else { lab[0] = 180.0 * dx - 40.0; lab[1] = 300.0 * dy - 150.0; lab[2] = 300.0 * dz - 150.0; } //printf("~1 Lab = %f %f %f\n", lab[0],lab[1],lab[2]); Lab2XYZ(tmp, lab); printf("======================================================\n"); printf("%d,%d -> Lab %f %f %f\n", tx+sp, ty+sp, lab[0],lab[1],lab[2]); printf(" == XYZ %f %f %f\n", tmp[0],tmp[1],tmp[2]); /* Else source range is extended XYZ type */ } else { if (xsect) { tmp[0] = 1.4 * dz - 0.2; tmp[1] = 1.4 * dy - 0.2; tmp[2] = 1.6 * dx - 0.2; } else { tmp[0] = 1.4 * dx - 0.2; tmp[1] = 1.4 * dy - 0.2; tmp[2] = 1.6 * dz - 0.2; } printf("%d,%d -> Input color XYZ %f %f %f\n", tx, ty, tmp[0],tmp[1],tmp[2]); } if (to_ss) { cam1->XYZ_to_cam(cam1, Jab, tmp); printf(" XYZ -> ss %f %f %f\n", Jab[0],Jab[1],Jab[2]); /* Convert XYZ through cam and back, then to RGB */ } else if (to_jab) { cam1->XYZ_to_cam(cam1, Jab, tmp); if (to_ss) { printf(" XYZ -> ss %f %f %f\n", Jab[0],Jab[1],Jab[2]); } else { printf(" XYZ -> ss %f %f %f\n", Jab[0],Jab[1],Jab[2]); } /* Else convert XYZ to L*a*b* */ } else { XYZ2Lab(Jab, tmp); printf(" XYZ -> Lab %f %f %f\n", Jab[0],Jab[1],Jab[2]); } /* Convert from Jab back to XYZ */ if (from_jab) { cam2->cam_to_XYZ(cam2, rgb, Jab); printf(" Jab -> XYZ %f %f %f\n", rgb[0],rgb[1],rgb[2]); /* Else convert from Lab back to XYZ */ } else { Lab2XYZ(rgb, Jab); printf(" Lab -> XYZ %f %f %f\n", rgb[0],rgb[1],rgb[2]); } XYZ2Lab(tmp, rgb); printf(" == Lab %f %f %f\n", tmp[0],tmp[1],tmp[2]); /* Interpret XYZ as RGB color */ XYZ2sRGB(rgb, rgb, 1); printf(" XYZ -> RGB %f %f %f\n", rgb[0],rgb[1],rgb[2]); cam1->trace = 0; cam2->trace = 0; printf("\n"); exit(0); } if (verb) printf("Raster width = %d, height = %d\n",w,h); /* Setup the tiff file */ if ((wh = TIFFOpen(tiffname, "w")) == NULL) error("Can\'t create TIFF file '%s'!",tiffname); TIFFSetField(wh, TIFFTAG_IMAGEWIDTH, w); TIFFSetField(wh, TIFFTAG_IMAGELENGTH, h); TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, 3); TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, 8); TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE); TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits); TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx); TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy); TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "cam02plot"); obuf = _TIFFmalloc(TIFFScanlineSize(wh)); ob = (unsigned char *)obuf; y = 0; /* Fill sp lines with black */ for (x = 0, m = 0; m < w; m++) { ob[x] = ob[x+1] = ob[x+2] = 0; x += 3; } for (j = 0; j < sp; j++, y++) { TIFFWriteScanline(wh, ob, y, 0); } for (i = 0; i < ares; i++) { /* Vertical blocks (rows) */ for (j = 0; j < res; j++, y++) { /* Vertical in block (y = Y/a*) */ xyz[1] = j/(res-1.0); x = 0; /* Fill sp pixels with black */ for (m = 0; m < sp; m++) { ob[x] = ob[x+1] = ob[x+2] = 0; x += 3; } for (k = 0; k < ares; k++) { /* Horizontal blocks (columns) */ int zv = i * ares + k; if (zv >= res) { /* Fill res pixels with black */ for (m = 0; m < res; m++) { ob[x] = ob[x+1] = ob[x+2] = 0; x += 3; } } else { xyz[2] = zv/(res-1.0); /* z = block = Z/b* */ for (m = 0; m < res; m++) { /* Horizontal in block (x = X/L*) */ double tmp[3], xyz2[3]; xyz[0] = m/(res-1.0); if (lab_plot) { if (xsect) { tmp[0] = 180.0 * xyz[2] - 40.0; tmp[1] = 300.0 * xyz[1] - 150.0; tmp[2] = 300.0 * xyz[0] - 150.0; } else { tmp[0] = 180.0 * xyz[0] - 40.0; tmp[1] = 300.0 * xyz[1] - 150.0; tmp[2] = 300.0 * xyz[2] - 150.0; } Lab2XYZ(tmp, tmp); } else { if (xsect) { tmp[0] = 1.4 * xyz[2] - 0.2; tmp[1] = 1.4 * xyz[1] - 0.2; tmp[2] = 1.6 * xyz[0] - 0.2; } else { tmp[0] = 1.4 * xyz[0] - 0.2; tmp[1] = 1.4 * xyz[1] - 0.2; tmp[2] = 1.6 * xyz[2] - 0.2; } } /* Convert XYZ through cam and back, then to RGB */ if (to_jab || to_ss) cam1->XYZ_to_cam(cam1, Jab, tmp); else XYZ2Lab(Jab, tmp); if (to_ss) { rgb[0] = Jab[0]; rgb[1] = Jab[1]; rgb[2] = Jab[2]; } else { if (from_jab) cam2->cam_to_XYZ(cam2, xyz2, Jab); else Lab2XYZ(xyz2, Jab); XYZ2sRGB(rgb, xyz2, 0); } /* Fill with pixel value */ ob[x+0] = (int)(rgb[0] * 255.0 + 0.5); ob[x+1] = (int)(rgb[1] * 255.0 + 0.5); ob[x+2] = (int)(rgb[2] * 255.0 + 0.5); x += 3; } } /* Fill sp pixels with black */ for (m = 0; m < sp; m++) { ob[x] = ob[x+1] = ob[x+2] = 0; x += 3; } } TIFFWriteScanline(wh, ob, y, 0); } /* Fill sp lines with black */ for (x = m = 0; m < w; m++) { ob[x] = ob[x+1] = ob[x+2] = 0; x += 3; } for (j = 0; j < sp; j++, y++) { TIFFWriteScanline(wh, ob, y, 0); } } cam1->del(cam1); cam2->del(cam2); /* Write TIFF file */ TIFFClose(wh); return 0; }