/* * $Id: ddc2xdccc.c,v 1.8 2000/01/29 20:19:49 werdna Exp werdna $ * ddc2xdccc.c * * Convert EDID DDC data to XDCCC format. * * EDID data can come from several sources: * + root window properties (currently XFree86_DDC_EDID1_RAWDATA) * + /var/log/XFree86.0.log or other XFree86 v4.0 logfiles * containing DDC output * + compiled in sample data generated from * xc/programs/xcmsdb/datafiles/sample[12].dcc * * (c) Andrew C Aitchison, 1999. * email A.C.Aitchison@dpmms.cam.ac.uk * * To build: * cc -g -O2 -Wall ddc2xdccc.c -L/usr/X11R6/lib -lX11 -lm -o ddc2xdccc * * To use: * ddc2xdccc | xcmsdb root window properties * ddc2xdccc v < /var/log/XFree86.0.log | xcmsdb */ #include #include #include #include #include #include #define FALSE 0 #define TRUE 1 #define DEBUG 0 typedef struct { float rX, rY, gX, gY, bX, bY, wX, wY; float gamma; char manuf[5], model[5]; int EDIDversion, EDIDrevision; char *name, *part; } EDID1; /* * Convert dcc color matrix into values used by DDC / EDID1. */ int dccMatrix_to_edid1(const float M[3][3], EDID1 *edid) { float r, g, b, w, wZ; /* rX = M[0][0]; rY = M[0][1]; rZ = M[0][2]; * gX = M[1][0]; gY = M[1][1]; gZ = M[1][2]; * bX = M[2][0]; bY = M[2][1]; bZ = M[2][2]; */ /* White point is full R + full G + full B * true at least for a CRT */ edid->wX = M[0][0]+M[1][0]+M[2][0]; edid->wY = M[0][1]+M[1][1]+M[2][1]; wZ = M[0][2]+M[1][2]+M[2][2]; /* DDC / EDID1 normalizes to X+Y+Z=1, but doesn't store Z */ r = M[0][0]+M[0][1]+M[0][2]; edid->rX = M[0][0]/r; edid->rY = M[0][1]/r; g = M[1][0]+M[1][1]+M[1][2]; edid->gX = M[1][0]/g; edid->gY = M[1][1]/g; b = M[2][0]+M[2][1]+M[2][2]; edid->bX = M[2][0]/b; edid->bY = M[2][1]/b; w = edid->wX + edid->wY + wZ; edid->wX /= w; edid->wY /= w; return TRUE; } /* * r,g,b from xc/programs/xcmsdb/datafiles/sample1.dcc */ const float TEK_SONY_matrix[3][3] = { { 0.38106149108714790, 0.32025712365352110, 0.24834578525933100}, { 0.20729745115140850, 0.68054638776373240, 0.11215616108485920}, { 0.02133944350088028, 0.14297193020246480, 1.24172892629665500} }; /* * r,g,b from xc/programs/xcmsdb/datafiles/sample2.dcc */ const float TEK_PANA_matrix[3][3] = { {0.333300, 0.337244, 0.221834}, {0.189464, 0.706136, 0.104400}, {0.021668, 0.142028, 1.138384}, }; int readEDID1(FILE*fd, EDID1 *edid) { int count; do { do { count = fgetc(fd); if (EOF == count) return FALSE; } while (count != 'M'); count = fscanf(fd, "anufacturer: %4s", edid->manuf); if (EOF == count) return FALSE; } while (count<1); count = fscanf(fd, " Model: %4s", edid->model); if (count<1) return FALSE; do { do { count = fgetc(fd); if (EOF == count) return FALSE; } while (count != 'G'); if (1==(count=fscanf(fd, "amma: %g", &edid->gamma))) break; } while (EOF != count); if (EOF == count) return FALSE; do { do { count = fgetc(fd); if (EOF == count) return FALSE; } while (count != 'r'); count = fscanf(fd, "edX: %g", &edid->rX); if (EOF == count) return FALSE; } while (count<1); count = fscanf(fd, " redY: %g", &edid->rY); if (count<1) return FALSE; count = fscanf(fd, " greenX: %g", &edid->gX); if (count<1) return FALSE; count = fscanf(fd, " greenY: %g", &edid->gY); if (count<1) return FALSE; count = fscanf(fd, " blueX: %g", &edid->bX); if (count<1) return FALSE; count = fscanf(fd, " blueY: %g", &edid->bY); if (count<1) return FALSE; count = fscanf(fd, " whiteX: %g", &edid->wX); if (count<1) return FALSE; count = fscanf(fd, " whiteY: %g", &edid->wY); if (count<1) return FALSE; return TRUE; } void fprint3(FILE *stream, const float internal[3][3]) { int i, j; for (i=0; i<3; i++) { for (j=0; j<3; j++) { fprintf(stream, " %9g", internal[i][j]); } fprintf(stream, "\n"); } fprintf(stream, "\n"); } double det3(const float M[3][3]) { double t, pos, neg; int i; /* accumulate positive and negative terms separately * (this makes the result stable wrt row and column shifts). * * Idea taken from Mesa code, which may attribute it to Graphics Gems II */ pos = neg = 0.0; for (i=0; i<3; i++) { t = M[0][i]*M[1][(i+1)%3]*M[2][(i+2)%3]; if (t >= 0.0) pos += t; else neg += t; t = - M[0][i]*M[1][(i+2)%3]*M[2][(i+1)%3]; if (t >= 0.0) pos += t; else neg += t; } t = pos + neg; #if DEBUG fprintf(stderr,"pos=%g\tneg=%g\tdet3(M)=%g\n", pos, neg, t); #endif return t; } void mult3(const float M[3][3], const float N[3][3], float prod[3][3]) { int i, j, k; for (i=0; i<3; i++) { for (j=0; j<3; j++) { prod[i][j] = 0.0; for (k=0; k<3; k++) { prod[i][j] += M[i][k]*N[k][j]; } } } return; } /* 3x3 matrix inversion * returns TRUE iff M is non singular */ int invert3(const float M[3][3], float inv[3][3]) { double det; #if DEBUG double calcDet; #endif det = det3(M); #if DEBUG fprintf(stderr,"det3(M)=%g\n", det); #endif if (det*det < 1e-25) return FALSE; det = 1.0 / det; inv[0][0] = ( (M[1][1]*M[2][2] - M[2][1]*M[1][2] )*det); inv[0][1] = (- (M[0][1]*M[2][2] - M[2][1]*M[0][2] )*det); inv[0][2] = ( (M[0][1]*M[1][2] - M[1][1]*M[0][2] )*det); inv[1][0] = (- (M[1][0]*M[2][2] - M[2][0]*M[1][2] )*det); inv[1][1] = ( (M[0][0]*M[2][2] - M[2][0]*M[0][2] )*det); inv[1][2] = (- (M[0][0]*M[1][2] - M[1][0]*M[0][2] )*det); inv[2][0] = ( (M[1][0]*M[2][1] - M[2][0]*M[1][1] )*det); inv[2][1] = (- (M[0][0]*M[2][1] - M[2][0]*M[0][1] )*det); inv[2][2] = ( (M[0][0]*M[1][1] - M[1][0]*M[0][1] )*det); #if DEBUG calcDet = det3(inv); fprintf(stderr,"det3(inv)=%.10g=%.10g", det, calcDet); if (fabs(1-(det/calcDet)) > 1e-7 ) { fprintf(stderr, "\t??? not equal ???"); } fprintf(stderr, "\n"); #if (DEBUG>1) { float prod[3][3]; fprintf(stderr,"M =\n"); fprint3(stderr,M); fprintf(stderr,"Minv =\n"); fprint3(stderr,inv); mult3(M, inv, prod); fprintf(stderr,"M*Minv =\n"); fprint3(stderr,prod); } #endif #endif return TRUE; } /* * Convert values used by DDC / EDID1 into dcc color matrix. */ int edid1_to_dccMatrix(const EDID1 *edid, float M[3][3]) { float Minv[3][3]; float rX, rY, rZ; float gX, gY, gZ; float bX, bY, bZ; float wX, wY, wZ; float R, G, B; int i; rX = edid->rX; rY = edid->rY; gX = edid->gX; gY = edid->gY; bX = edid->bX; bY = edid->bY; wX = edid->wX; wY = edid->wY; #if DEBUG fprintf(stderr," edid1_to_dccMatrix(\n\trX=%g, rY=%g\n\tgX=%g, gY=%g\n\tbX=%g, bY=%g\n\twX=%g, wY=%g\n )\n", rX,rY, gX,gY, bX,bY, wX,wY); #endif /* DDC only supplies 2 components of the phospher colors * calculute the 3rd, given that they are normalised to X+Y+Z=1 */ rZ = 1.0 - rX -rY; gZ = 1.0 - gX -gY; bZ = 1.0 - bX -bY; wZ = 1.0 - wX -wY; #if DEBUG fprintf(stderr,"\trZ= %g, gZ= %g, bZ= %g, wZ= %g\n", rZ, gZ, bZ, wZ); #endif /* * The DDC colours are normalized; calculate R,G,B, the scale factors * required to restore them to their original lengths, * given that the white point is generated by all 3 CRT guns at 1.0 (max). */ M[0][0] = rX; M[0][1] = rY; M[0][2] = rZ; M[1][0] = gX; M[1][1] = gY; M[1][2] = gZ; M[2][0] = bX; M[2][1] = bY; M[2][2] = bZ; if (!invert3(M, Minv)) return FALSE; R = Minv[0][0]*wX + Minv[1][0]*wY + Minv[2][0]*wZ; G = Minv[0][1]*wX + Minv[1][1]*wY + Minv[2][1]*wZ; B = Minv[0][2]*wX + Minv[1][2]*wY + Minv[2][2]*wZ; /* The dcc matrix is scaled so that G = 1 * where G is the green component of the white point. */ R /= G; B /= G; /* do G last or B will be wrong */ G /= G; #if DEBUG fprintf(stderr,"R=%9g G=%9g B=%9g\n", R, G, B); #endif /* Multiply the rows of the matrix by the scale factors * that we have just calculated. */ for (i=0; i<3; i++) { M[0][i] *= R; M[1][i] *= G; M[2][i] *= B; } return TRUE; } int print_xdccc( float RGBtoXYZ[3][3], float XYZtoRGB[3][3], float gamma, char *name, char *part, char *model ) { int i, ind; float val; #if DEBUG fprintf(stderr,"gamma = %9g\n", gamma); #endif printf("SCREENDATA_BEGIN 0.3\n"); printf("\n"); printf(" NAME %s\n", name); printf(" PART_NUMBER %s\n", part); printf(" MODEL %s\n", model); printf(" SCREEN_CLASS VIDEO_RGB\n"); printf(" REVISION 1.0\n"); printf("\n"); printf(" COLORIMETRIC_BEGIN\n"); printf(" XYZtoRGB_MATRIX_BEGIN\n"); printf(" %9f %9f %9f\n %9f %9f %9f\n %9f %9f %9f\n", XYZtoRGB[0][0], XYZtoRGB[0][1], XYZtoRGB[0][2], XYZtoRGB[1][0], XYZtoRGB[1][1], XYZtoRGB[1][2], XYZtoRGB[2][0], XYZtoRGB[2][1], XYZtoRGB[2][2] ); printf(" XYZtoRGB_MATRIX_END\n"); printf(" RGBtoXYZ_MATRIX_BEGIN\n"); printf(" %9f %9f %9f\n %9f %9f %9f\n %9f %9f %9f\n", RGBtoXYZ[0][0], RGBtoXYZ[0][1], RGBtoXYZ[0][2], RGBtoXYZ[1][0], RGBtoXYZ[1][1], RGBtoXYZ[1][2], RGBtoXYZ[2][0], RGBtoXYZ[2][1], RGBtoXYZ[2][2] ); printf(" RGBtoXYZ_MATRIX_END\n"); printf(" COLORIMETRIC_END\n"); printf("\n"); #if 0 /* RED, GREEN and BLUE profiles */ printf(" INTENSITY_PROFILE_BEGIN 0 3\n"); printf(" INTENSITY_TBL_BEGIN RED 256\n"); for(i=0;i<256;i++) { ind = i*257; val = pow((double)i/255.0, (double)gamma); printf(" 0x%04x %9f\n", ind, val); } printf(" INTENSITY_TBL_END\n"); printf(" INTENSITY_TBL_BEGIN GREEN 256\n"); for(i=0;i<256;i++) { ind = i*257; val = pow((double)i/255.0, (double)gamma); printf(" 0x%04x %9f\n", ind, val); } printf(" INTENSITY_TBL_END\n"); printf(" INTENSITY_TBL_BEGIN BLUE 256\n"); for(i=0;i<256;i++) { ind = i*257; val = pow((double)i/255.0, (double)gamma); printf(" 0x%04x %9f\n", ind, val); } printf(" INTENSITY_TBL_END\n"); printf(" INTENSITY_PROFILE_END\n"); #else /* A single RGB intensity profile */ printf(" INTENSITY_PROFILE_BEGIN 0 1\n"); printf(" INTENSITY_TBL_BEGIN RGB 256\n"); for(i=0;i<256;i++) { ind = i*257; val = pow((double)i/255.0, (double)gamma); printf(" 0x%04x %9f\n", ind, val); } printf(" INTENSITY_TBL_END\n"); printf(" INTENSITY_PROFILE_END\n"); #endif printf("SCREENDATA_END\n"); printf("\n"); return 0; } #define EDID1_ATOM_NAME "XFree86_DDC_EDID1_RAWDATA" #define EDID2_ATOM_NAME "XFree86_DDC_EDID2_RAWDATA" #define VDIF_ATOM_NAME "XFree86_DDC_VDIF_RAWDATA" int getEDID1fromXProps(EDID1 *edid) { unsigned char *ret_prop; unsigned long ret_len, ret_after; int ret_format; Atom EDID1Atom, ret_atom; Display *dpy; Window root; int i, j; if (!(dpy = XOpenDisplay (NULL))) { fprintf(stderr, "failed to open display\n"); return FALSE; } root = DefaultRootWindow(dpy); #if DEBUG fprintf(stderr, "root window %ld\n", root); #endif EDID1Atom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE); if (None==EDID1Atom) { fprintf(stderr, "first attempt to find atom %s failed\n", EDID1_ATOM_NAME); #if 0 { Atom *PropList; int i, size; fprintf(stderr,"searching list of properties of root window:\n"); PropList = XListProperties(dpy, root, &size); for (i=0; i1) fprintf(stderr, "%ld %s\n", PropList[i], XGetAtomName(dpy, PropList[i])); #endif if (strcmp(EDID1_ATOM_NAME, XGetAtomName(dpy, PropList[i]))==0) { EDID1Atom = PropList[i]; } } XFree(PropList); if (None==EDID1Atom) { fprintf(stderr, "still could not find atom %s\n", EDID1_ATOM_NAME); return FALSE; } } #else return FALSE; #endif } #if DEBUG fprintf(stderr, "EDID1Atom = %ld\n", EDID1Atom); #endif if (Success != XGetWindowProperty (dpy, root, EDID1Atom, 0, 32, False, AnyPropertyType, &ret_atom, &ret_format, &ret_len, &ret_after, &ret_prop) ) { fprintf(stderr, "could not find property %s\n", EDID1_ATOM_NAME); return FALSE; } if (ret_len != 128) { fprintf(stderr, "Property %s has length %ld - 128 expected\n", EDID1_ATOM_NAME, ret_len); } #if DEBUG fprintf(stderr, "ret_atom %ld, ret_format %d , ret_len %ld , ret_after%ld , ret_prop %p\n", ret_atom, ret_format, ret_len, ret_after, ret_prop); fprintf(stderr, "\n"); for (i=0; iEDIDversion = ret_prop[0x12]; edid->EDIDrevision = ret_prop[0x13]; edid->gamma = ((float)(100.0+ret_prop[0x17]))/100.0; edid->rX = (float)((ret_prop[0x1b]<<2) + (ret_prop[0x19]>>6 & 3))/1024.0; edid->rY = (float)((ret_prop[0x1c]<<2) + (ret_prop[0x19]>>4 & 3))/1024.0; edid->gX = (float)((ret_prop[0x1d]<<2) + (ret_prop[0x19]>>2 & 3))/1024.0; edid->gY = (float)((ret_prop[0x1e]<<2) + (ret_prop[0x19]>>0 & 3))/1024.0; edid->bX = (float)((ret_prop[0x1f]<<2) + (ret_prop[0x1a]>>6 & 3))/1024.0; edid->bY = (float)((ret_prop[0x20]<<2) + (ret_prop[0x1a]>>4 & 3))/1024.0; edid->wX = (float)((ret_prop[0x21]<<2) + (ret_prop[0x1a]>>2 & 3))/1024.0; edid->wY = (float)((ret_prop[0x22]<<2) + (ret_prop[0x1a]>>0 & 3))/1024.0; edid->manuf[0] = '@' | (ret_prop[0x08]>>2 & 0x1f); edid->manuf[1] = '@' | (ret_prop[0x08]<<3 & 0x18) | (ret_prop[0x09]>>5 & 0x7); edid->manuf[2] = '@' | (ret_prop[0x09] & 0x1f); edid->manuf[3] = 0; snprintf(edid->model, 5, "%04x", ret_prop[0x0a] | (ret_prop[0x0b]<<8) ); edid->name = malloc(19); if (! edid->name) { fprintf(stderr, "out of memory\n"); exit(-1); } edid->name[0] = 0; edid->name[18] = 0; if (edid->EDIDversion==1 && edid->EDIDrevision > 0) { for (i=0;i<4;i++) { unsigned char *monDescriptor = ret_prop + 0x36 + i*18; if (monDescriptor[0]==0 && monDescriptor[1]==0 && monDescriptor[3]==0) { switch (monDescriptor[2]) { default: break; case 0xFF: /* Serial Number */ break; case 0xFC: /* Monitor Name */ for (j=0; j<5; j++) { edid->name[j] = edid->manuf[j]; } edid->name[4] = ' '; strncpy(&(edid->name[5]), &(monDescriptor[5]), 13); for (j=5; j<18; j++) { if (edid->name[j] == 0x0a) edid->name[j] = 0; } break; } } } } if (!edid->name[0]) { snprintf( edid->name, 18, "%s %s", edid->manuf, edid->model); } XFree(ret_prop); return TRUE; } int getEDID2fromXProps(EDID1 *edid) { unsigned char *ret_prop; unsigned long ret_len, ret_after; int ret_format; Atom EDID2Atom, ret_atom; Display *dpy; Window root; if (!(dpy = XOpenDisplay (NULL))) { fprintf(stderr, "failed to open display\n"); return FALSE; } root = DefaultRootWindow(dpy); #if DEBUG fprintf(stderr, "root window %ld\n", root); #endif EDID2Atom = XInternAtom(dpy, EDID2_ATOM_NAME, TRUE); if (None==EDID2Atom) { fprintf(stderr, "first attempt to find atom %s failed\n", EDID2_ATOM_NAME); #if 0 { Atom *PropList; int i, size; fprintf(stderr,"searching list of properties of root window:\n"); PropList = XListProperties(dpy, root, &size); for (i=0; i1) fprintf(stderr, "%ld %s\n", PropList[i], XGetAtomName(dpy, PropList[i])); #endif if (strcmp(EDID2_ATOM_NAME, XGetAtomName(dpy, PropList[i]))==0) { EDID2Atom = PropList[i]; } } XFree(PropList); if (None==EDID2Atom) { fprintf(stderr, "still could not find atom %s\n", EDID2_ATOM_NAME); return FALSE; } } #else return FALSE; #endif } #if DEBUG fprintf(stderr, "EDID2Atom = %ld\n", EDID2Atom); #endif if (Success != XGetWindowProperty (dpy, root, EDID2Atom, 0, 32, False, AnyPropertyType, &ret_atom, &ret_format, &ret_len, &ret_after, &ret_prop) ) { fprintf(stderr, "could not find property %s\n", EDID2_ATOM_NAME); return FALSE; } if (ret_len != 256) { fprintf(stderr, "Property %s has length %ld - 256 expected\n", EDID1_ATOM_NAME, ret_len); } #if DEBUG fprintf(stderr, "ret_atom %ld, ret_format %d , ret_len %ld , ret_after%ld , ret_prop %p\n", ret_atom, ret_format, ret_len, ret_after, ret_prop); fprintf(stderr, "\n"); for (i=0; i<256; i++) { if (0==i%8) fprintf(stderr,"\t"); fprintf(stderr," %02x", ret_prop[i]); if (15==i%16) fprintf(stderr,"\n"); } #endif fprintf(stderr, "property %s found but not yet understood\n", EDID2_ATOM_NAME); XFree(ret_prop); return FALSE; } int main (int argc, char* argv[]) { int selection=0; float RGBtoXYZ[3][3]; float XYZtoRGB[3][3]; int retval = 0; char unknown[]="unknown"; char *name=unknown, *part=unknown, *model=unknown; float gamma; EDID1 edid; #if DEBUG EDID1 verifyEdid; #endif edid.model[0] = 0; edid.name = NULL; if (argc>1) { selection = argv[1][0]; } switch (selection) { case 0: /* no args, thus not character '0' */ case 'p': case 'P': case 'x': case 'X': /* Find data amongst properties of root window */ if (getEDID2fromXProps(&edid)) { fprintf(stderr, "found EDID v2 properties\n"); break; } if (getEDID1fromXProps(&edid)) { fprintf(stderr, "found EDID v%d.%d properties\n", edid.EDIDversion, edid.EDIDrevision); break; } fprintf(stderr, "failed while reading properties\n"); exit(1); break; case '-': case 's': case 'S': case 'l': case 'L': case 'v': case 'V': /* read standard input, and parse as if /var/log/XFree86.?.log */ if (!readEDID1(stdin, &edid)) { fprintf(stderr, "failed while reading data from stdin\n"); exit(1); } break; case '1': dccMatrix_to_edid1(TEK_SONY_matrix, &edid); edid.gamma = 1.0; name = "Tektronix 19\" (Sony) CRT"; part = "119-2451-00"; model = "Tek4300, XD88"; break; case '2': dccMatrix_to_edid1(TEK_PANA_matrix, &edid); edid.gamma = 1.0; name = "Tektronix 19\" (Panasonic) CRT"; part = "119-3916-00"; model = "XP27"; break; default: fprintf(stderr, "option %c not supported\n", selection); exit(-1); break; } gamma = edid.gamma; if (edid.model[0]) model = edid.model; if (edid.name) name = edid.name; #if DEBUG fprintf(stderr, " rX=%g, rY=%g\n gX=%g, gY=%g\n bX=%g, bY=%g\n wX=%g, wY=%g\n", edid.rX,edid.rY, edid.gX,edid.gY, edid.bX,edid.bY, edid.wX,edid.wY ); #endif /* Calculate the RGBtoXYZ matrix from the DDC data */ edid1_to_dccMatrix(&edid, RGBtoXYZ); #if DEBUG dccMatrix_to_edid1(RGBtoXYZ, &verifyEdid); fprintf(stderr,"converting dcc matrix back to DDC for verification:\n"); fprintf(stderr," rX=%g, rY=%g\n gX=%g, gY=%g\n bX=%g, bY=%g\n wX=%g, wY=%g\n", verifyEdid.rX,verifyEdid.rY, verifyEdid.gX,verifyEdid.gY, verifyEdid.bX,verifyEdid.bY, verifyEdid.wX,verifyEdid.wY ); #endif /* The XYZtoRGB matrix is the inverse of the RGBtoXYZ */ retval = !invert3(RGBtoXYZ, XYZtoRGB); print_xdccc( RGBtoXYZ, XYZtoRGB, gamma, name, part, model); return retval; }