%ffp Title :"ColorSpace" Filename :"colorspace.8bf" Description:"Color convertion from/to different color spaces." Copyright :"© 2007 GPL" Author :"Alois Zingl" Version :"1.0" URL :"http://free.pages.at/easyfilter/filtermeister.html" SupportedModes: RGBMode, CMYKMode, HSLMode, HSBMode, LabMode, RGB48Mode, Lab48Mode, CMYK64Mode /* this version needs C preprocessor: sdcc (http://sourceforge.net/projects/sdcc/). The program sdcpp.exe (100kB, the only file needed) converts well known C preprocessor directives into FM-code. Batch call to convert a *.c file for FM: sdcpp -C -P %1 -o %~n1.ffp */ #define CTL_IN_RGB 0 #define CTL_IN_CMY 1 #define CTL_IN_CMYK 2 #define CTL_IN_HSL 3 #define CTL_IN_HSB 4 #define CTL_IN_LAB 5 #define CTL_IN_LCH 6 #define CTL_OUT_RGB 10 #define CTL_OUT_CMY 11 #define CTL_OUT_CMYK 12 #define CTL_OUT_HSL 13 #define CTL_OUT_HSB 14 #define CTL_OUT_LAB 15 #define CTL_OUT_LCH 16 #define CTL_SPACE 23 #define CTL_GAMMA 24 #define CTL_CALC_MATRIX 20 #define POS_X_IN 270 #define POS_X_OUT 370 #define MAX_COLOR (imageModeCTL_IN_HSB)^(outSpace>CTL_OUT_HSB) ? -1 : 0; enableCtl(22,i); enableCtl(CTL_GAMMA,i); enableCtl(CTL_SPACE,i); } if (n==CTL_SPACE || n==CTL_IN_LAB || n==CTL_IN_LCH || n==CTL_OUT_LAB || n==CTL_OUT_LCH) { triggerEvent(CTL_CALC_MATRIX,FME_CUSTOMEVENT,n); // set rgb->xyz->rgb matrix + Gamma(z0) setCtlVal(CTL_GAMMA,INT(gGamma*10.0)); } } return false; } OnFilterStart: { enableCtl(CTL_IN_CMYK,planesWithoutAlpha>3?-1:1); // disable CMYK if less than 4 planes enableCtl(CTL_OUT_CMYK,planesWithoutAlpha>3?-1:1); if ((ctl(CTL_IN_LAB)||ctl(CTL_IN_LCH)||ctl(CTL_OUT_LAB)||ctl(CTL_OUT_LCH)) && x0==0.0) // calc matrixes { triggerEvent(CTL_CALC_MATRIX,FME_CUSTOMEVENT,0); // set rgb->xyz->rgb matrix + Gamma(z0) setCtlVal(CTL_GAMMA,INT(gGamma*10.0)); } isTileable = true; return false; } ForEveryTile: { int C = MAX_COLOR, i,j; double xVal, yVal, zVal, rVal, gVal, bVal; // using exact values for L*a*b convertion :-) double a, f, e, pi2=atan(1.0)*8.0, gama = ctl(CTL_GAMMA)/10.0; a = ctl(CTL_SPACE)==14 ? 0.055 : 0.0; // sRGB: x-value for transition from linear to gamma function f = gama==1||a==0 ? a+1 : a*pow((1+1/a)*(1-1/gama),gama)/(gama-1); // = 12.92, sRGB linear factor e = 1.5/(1+100/16.0); // = 6/29, xyz: y-value for transition from linear to exp3 function if (inSpace-CTL_IN_RGB==outSpace-CTL_OUT_RGB) return true; // nothing to do for (y=y_start;y RGB R=r; G=g; B=b; break; case CTL_IN_CMY: // CMY -> RGB R=C-r; G=C-g; B=C-b; break; case CTL_IN_CMYK: // CMYK -> RGB R=(C-r)*(C-k)/C; G=(C-g)*(C-k)/C; B=(C-b)*(C-k)/C; break; case CTL_IN_HSL: // HSL -> RGB switch(r*3/(C+1)) // Hue { case 0: // 0..120° R = 2*C-r*6; G = r*6; B = 0; break; case 1: // 120°..240° R = 0; G = 4*C-r*6; B = r*6-2*C; break; case 2: default: // 240°..360° R = r*6-4*C; G = 0; B = 6*(C-r); break; } R = g*min(C,R)/C*2+(C-g); // S G = g*min(C,G)/C*2+(C-g); B = g*min(C,B)/C*2+(C-g); if (b+b RGB if (g==0) R=G=B=b; else { i=b*(C-g)/(C+1); r*=6; j=b*(C-g*(r%(C+1))/(C+1))/(C+1); g=b*(C-g*(C-r%(C+1))/(C+1))/(C+1); switch(r/(C+1)) { case 0: R=b; G=g; B=i; break; case 1: R=j; G=b; B=i; break; case 2: R=i; G=b; B=g; break; case 3: R=i; G=j; B=b; break; case 4: R=g; G=i; B=b; break; default: case 5: R=b; G=i; B=j; break; } } break; case CTL_IN_LAB: // L*a*b* -> RGB case CTL_IN_LCH: // LCH -> RGB /* lab2rgb http://www.brucelindbloom.com/ range L 0..C -> 0..100, a/b 0..C/2..C -> -128..0..+128 (It does not make much sense to further optimize contants since the improvements compared to pow() are negligible.) */ if (inSpace==CTL_IN_LAB) { gVal = (g-C/2)*255.0/C; // a bVal = (b-C/2)*255.0/C; // b } else // LCH { gVal = g*fcos(b*pi2/(C+1))*128.0*sqrt(2.0)/C; // a bVal = g*fsin(b*pi2/(C+1))*128.0*sqrt(2.0)/C; // b } rVal = r*100.0/C; // L yVal = (rVal+16.0)/116.0; // fy, 16=100*e/(3/2-e) xVal = gVal/500.0+yVal; // fx zVal = yVal-bVal/200.0; // fz // do gamma=3 for large or linear funtion for small values xVal = xVal > e ? xVal*xVal*xVal : e*e*(3.0*xVal-2.0*e); yVal = yVal > e ? yVal*yVal*yVal : e*e*(3.0*yVal-2.0*e); zVal = zVal > e ? zVal*zVal*zVal : e*e*(3.0*zVal-2.0*e); // convertion matrix xyz to rgb rVal = xVal * y0 + yVal * y1 + zVal * y2; gVal = xVal * y3 + yVal * y4 + zVal * y5; bVal = xVal * y6 + yVal * y7 + zVal * y8; // invoke linear part for small sRGB values otherwise gamma function rVal = rVal*(gama-1.0)*fC) g=C+C-g; //S g = b==0||b==i ? 0 : (b-i)*C/g; b = (b+i)>>1; // L break; case CTL_OUT_HSB: // RGB to HSB/HSV b=max(R,G,B); // V i=min(R,G,B); if (R==b) r=G-B; if (G==b) r=B-R+2*(b-i); if (B==b) r=R-G+4*(b-i); r = b==i ? 0 : r/6*C/(b-i); // H if (r<0) r+=C+1; g = b ? C-i*C/b : 0; // S break; case CTL_OUT_LAB: // RGB to L*a*b* case CTL_OUT_LCH: // RGB to LCH // rgb2lab http://www.brucelindbloom.com/ rVal = R/(double)C; gVal = G/(double)C; bVal = B/(double)C; // invoke linear part for small sRGB values otherwise gamma function rVal = rVal*(gama-1.0) xyz xVal = x0 * rVal + x1 * gVal + x2 * bVal; yVal = x3 * rVal + x4 * gVal + x5 * bVal; zVal = x6 * rVal + x7 * gVal + x8 * bVal; // do gamma=3 for large or linear funtion for or small values xVal = xVal > e*e*e ? pow(xVal,1.0/3.0) : (xVal/(e*e)+2.0*e)/3.0; yVal = yVal > e*e*e ? pow(yVal,1.0/3.0) : (yVal/(e*e)+2.0*e)/3.0; zVal = zVal > e*e*e ? pow(zVal,1.0/3.0) : (zVal/(e*e)+2.0*e)/3.0; // convert to L*a*b* xVal = 500.0 * (xVal - yVal); // a*-value zVal = 200.0 * (yVal - zVal); // b*-value yVal = 116.0 * yVal - 16.0; // L*-value, 16=100*e/(3/2-e) // convert real values to int // range L 0..100 -> 0..C, a/b -128..0..+128 -> 0..C/2..C r = yVal*C/100.0; if (outSpace==CTL_OUT_LAB) { g = xVal*C/255.0+C/2; // a b = zVal*C/255.0+C/2; // b } else // LCH { g = sqrt((xVal*xVal+zVal*zVal)*0.5)*C/128.0; // C b = atan2(zVal,xVal)*(C+1)/pi2; // H if (b<0) b+=C+1; if (b>C) b-=C+1; } break; } if (imageMode==CMYKMode||imageMode==CMYK64Mode) // set negated values for PS { r=C-r; g=C-g; b=C-b; k=C-k; pset(x,y,3,k); } pset(x,y,0,r); pset(x,y,1,g); pset(x,y,2,b); } } return true; } OnFilterEnd: { updateProgress(0,1); return false; }