diff options
Diffstat (limited to 'spectro/dispwin.c')
-rw-r--r-- | spectro/dispwin.c | 999 |
1 files changed, 772 insertions, 227 deletions
diff --git a/spectro/dispwin.c b/spectro/dispwin.c index 4a6acd6..74186cf 100644 --- a/spectro/dispwin.c +++ b/spectro/dispwin.c @@ -17,8 +17,6 @@ /* TTBD * - * Nice to have option to create non-square test window ? - * * Should probably check the display attributes (like visual depth) * and complain if we aren't using 24 bit color or better. * @@ -30,6 +28,9 @@ * * Is there a >8 bit way of getting/setting RAMDAC indexes ? * + * Should add dithering support to overcome 8 bit limitations of + * non-RAMDAC access or limited RAMDAC depth. (How do we easily + * determine the latter ??) */ #include <stdio.h> @@ -53,12 +54,34 @@ #include "conv.h" #include "dispwin.h" #include "webwin.h" -#if defined(UNIX_X11) && defined(USE_UCMM) -#include "ucmm.h" +#ifdef NT +# include "madvrwin.h" +#endif +#if defined(UNIX_X11) +# include <dlfcn.h> +# if defined(USE_UCMM) +# include "ucmm.h" +# endif #endif #ifdef __APPLE__ +/* + Note that the new ColorSync API is defined in + /System/Library/Frameworks/ApplicationServices.framework/Frameworks/ColorSync.framework/Headers + + in + ColorSync.h + ColorSyncBase.h + ColorSyncCMM.h + ColorSyncDeprecated.h + ColorSyncDevice.h + ColorSyncProfile.h + ColorSyncTransform.h + + and isn't documented by Apple anywhere else. + */ + #include <Foundation/Foundation.h> #include <AppKit/AppKit.h> @@ -68,13 +91,23 @@ # endif #ifndef CGFLOAT_DEFINED -#ifdef __LP64__ +#ifdef __LP64__ || NS_BUILD_32_LIKE_64 typedef double CGFloat; #else typedef float CGFloat; #endif /* defined(__LP64__) */ #endif /* !CGFLOAT_DEFINED */ +#ifndef NSINTEGER_DEFINED +#if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; +#else + typedef int NSInteger; + typedef unsigned int NSUInteger; +#endif +#endif /* !NSINTEGER_DEFINED */ + #include <IOKit/Graphics/IOGraphicsLib.h> #if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060 @@ -572,10 +605,24 @@ disppath **get_displays() { /* Go through all the screens */ for (i = 0; i < dcount; i++) { + static void *xrr_found = NULL; /* .so handle */ + static XRRScreenResources *(*_XRRGetScreenResourcesCurrent) + (Display *dpy, Window window) = NULL; XRRScreenResources *scrnres; int jj; /* Screen index */ - if ((scrnres = XRRGetScreenResources(mydisplay, RootWindow(mydisplay,i))) == NULL) { + if (minv >= 3 && xrr_found == NULL) { + if ((xrr_found = dlopen("libXrandr.so", RTLD_LAZY)) != NULL) + _XRRGetScreenResourcesCurrent = dlsym(xrr_found, "XRRGetScreenResourcesCurrent"); + } + + if (minv >= 3 && _XRRGetScreenResourcesCurrent != NULL) { + scrnres = _XRRGetScreenResourcesCurrent(mydisplay, RootWindow(mydisplay,i)); + + } else { + scrnres = XRRGetScreenResources(mydisplay, RootWindow(mydisplay,i)); + } + if (scrnres == NULL) { debugrr("XRRGetScreenResources failed\n"); XCloseDisplay(mydisplay); free_disppaths(disps); @@ -1084,10 +1131,6 @@ void free_a_disppath(disppath *path) { /* ----------------------------------------------- */ -static ramdac *dispwin_clone_ramdac(ramdac *r); -static void dispwin_setlin_ramdac(ramdac *r); -static void dispwin_del_ramdac(ramdac *r); - /* For VideoLUT/RAMDAC use, we assume that the number of entries in the RAMDAC */ /* meshes perfectly with the display raster depth, so that we can */ /* figure out how to apportion device values. We fail if they don't */ @@ -1122,9 +1165,9 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } r->pdepth = p->pdepth; r->nent = (1 << p->pdepth); - r->clone = dispwin_clone_ramdac; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; - r->del = dispwin_del_ramdac; + r->del = dispwin_del_ramdac; for (j = 0; j < 3; j++) { @@ -1139,11 +1182,13 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { /* GetDeviceGammaRamp() is hard coded for 3 x 256 entries (Quantize) */ if (r->nent != 256) { + free(r); debugr2((errout,"GetDeviceGammaRamp() is hard coded for nent == 256, and we've got nent = %d!\n",r->nent)); return NULL; } if (GetDeviceGammaRamp(p->hdc, vals) == 0) { + free(r); debugr("dispwin_get_ramdac failed on GetDeviceGammaRamp()\n"); return NULL; } @@ -1171,7 +1216,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } if (nent != (1 << p->pdepth)) { - debugr("CGGetDisplayTransferByTable number of entries mismatches screen depth\n"); + debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d\n",nent,p->pdepth)); return NULL; } @@ -1183,9 +1228,9 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { r->pdepth = p->pdepth; r->nent = (1 << p->pdepth); - r->clone = dispwin_clone_ramdac; + r->clone = dispwin_clone_ramdac; r->setlin = dispwin_setlin_ramdac; - r->del = dispwin_del_ramdac; + r->del = dispwin_del_ramdac; for (j = 0; j < 3; j++) { if ((r->v[j] = (double *)calloc(sizeof(double), r->nent)) == NULL) { @@ -1231,7 +1276,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } if (nent != (1 << p->pdepth)) { - debugr2((errout,"XRRGetCrtcGammaSize number of entries %d mismatches screen depth %d\n",nent,(1 << p->pdepth))); + debugr2((errout,"XRRGetCrtcGammaSize number of entries %d mismatches screen depth %d bits\n",nent,(1 << p->pdepth))); return NULL; } @@ -1292,7 +1337,7 @@ static ramdac *dispwin_get_ramdac(dispwin *p) { } if (nent != (1 << p->pdepth)) { - debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d\n",nent,(1 << p->pdepth))); + debugr2((errout,"CGGetDisplayTransferByTable number of entries %d mismatches screen depth %d bits\n",nent,(1 << p->pdepth))); return NULL; } } @@ -1429,7 +1474,7 @@ typedef struct { CFURLRef url; /* URL to return */ } diter_cntx_t; -bool diter_callback(CFDictionaryRef dict, void *cntx) { +static bool diter_callback(CFDictionaryRef dict, void *cntx) { diter_cntx_t *cx = (diter_cntx_t *)cntx; CFStringRef str; CFUUIDRef uuid; @@ -1475,7 +1520,7 @@ bool diter_callback(CFDictionaryRef dict, void *cntx) { /* Return the url to the given displays current profile. */ /* Return NULL on error. CFRelease when done. */ /* Optionally return the ProfileID string */ -CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) { +static CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) { diter_cntx_t cx; if ((cx.dispuuid = CGDisplayCreateUUIDFromDisplayID(p->ddid)) == NULL) { @@ -1498,7 +1543,7 @@ CFURLRef cur_profile_url(CFStringRef *idp, dispwin *p) { /* Convert a URL into a local POSIX path string */ /* Return NULL on error. Free returned string when done. */ -char *url_to_path(CFURLRef url) { +static char *url_to_path(CFURLRef url) { CFStringRef urlstr; CFIndex bufSize; char *dpath = NULL; /* return value */ @@ -1521,9 +1566,9 @@ char *url_to_path(CFURLRef url) { return dpath; } -/* Return information about the given displays current profile */ +/* Return the local POSIX path to the given displays current profile */ /* Return NULL on error. Free returned string when done. */ -char *cur_profile(dispwin *p) { +static char *cur_profile(dispwin *p) { CFURLRef url; char *dpath = NULL; /* return value */ @@ -1541,6 +1586,58 @@ char *cur_profile(dispwin *p) { #endif /* >= 10.6 */ +/* Return a CMProfileRef/ColorSyncProfileRef for the */ +/* displays profile. Return NULL on error */ +static void *cur_colorsync_ref(dispwin *p) { + void *cspr = NULL; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + CFURLRef url; + ColorSyncProfileRef ref; + CFErrorRef ev; + + if ((url = cur_profile_url(NULL, p)) == NULL) { + debugr2((errout,"cur_colorsync_ref got NULL URL\n")); + return NULL; + } + + if ((ref = ColorSyncProfileCreateWithURL(url, &ev)) == NULL) { + debugr2((errout,"ColorSyncProfileCreateWithURL failed\n")); + return NULL; + } + CFRelease(url); + + cspr = (void *)ref; + +#else /* 10.5 and prior */ + CMError ev; + CMDeviceProfileID curID; /* Current Device Default profile ID */ + CMProfileLocation cploc; /* Current profile location */ + CMProfileRef prof; + + /* Get the default ID for the display */ + if ((ev = CMGetDeviceDefaultProfileID(cmDisplayDeviceClass, (CMDeviceID)p->ddid, &curID)) != noErr) { + debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev)); + return NULL; + } + + /* Get the displays profile */ + if ((ev = CMGetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)p->ddid, curID, &cploc)) != noErr) { + debugr2((errout,"CMGetDeviceDefaultProfileID() failed with error %d\n",ev)); + return NULL; + } + + if ((ev = CMOpenProfile(&prof, &cploc)) != noErr) { + debugr2((errout,"CMOpenProfile() failed with error %d\n",ev)); + return NULL; + } + cspr = (void *)prof; + +#endif + + return cspr; +} + #endif /* __APPLE__ */ /* Set the RAMDAC values. */ @@ -1576,6 +1673,7 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { debugr2((errout,"dispwin_set_ramdac failed on SetDeviceGammaRamp() with error %d\n",GetLastError())); return 1; } + GdiFlush(); #endif /* NT */ #ifdef __APPLE__ @@ -2071,13 +2169,14 @@ static int dispwin_set_ramdac(dispwin *p, ramdac *r, int persist) { } #endif /* UNXI X11 */ - debugr("XF86VidModeSetGammaRamp returning OK\n"); + debugr("dispwin_set_ramdac returning OK\n"); + return 0; } /* Clone ourselves */ -static ramdac *dispwin_clone_ramdac(ramdac *r) { +ramdac *dispwin_clone_ramdac(ramdac *r) { ramdac *nr; int i, j; @@ -2111,7 +2210,7 @@ static ramdac *dispwin_clone_ramdac(ramdac *r) { } /* Set the ramdac values to linear */ -static void dispwin_setlin_ramdac(ramdac *r) { +void dispwin_setlin_ramdac(ramdac *r) { int i, j; debug("dispwin_setlin_ramdac called\n"); @@ -2125,7 +2224,7 @@ static void dispwin_setlin_ramdac(ramdac *r) { } /* We're done with a ramdac structure */ -static void dispwin_del_ramdac(ramdac *r) { +void dispwin_del_ramdac(ramdac *r) { int j; debug("dispwin_del_ramdac called\n"); @@ -2138,7 +2237,7 @@ static void dispwin_del_ramdac(ramdac *r) { } /* ----------------------------------------------- */ -/* Useful function for X11 profie atom settings */ +/* Useful function for X11 profile atom settings */ #if defined(UNIX_X11) /* Return NZ on error */ @@ -2216,6 +2315,50 @@ static int set_X11_atom(dispwin *p, char *fname) { #endif /* UNXI X11 */ /* ----------------------------------------------- */ +/* See if colord is available */ + +#if defined(UNIX_X11) && defined(USE_UCMM) + +/* colord libcolordcompat.so shim functions */ + +ucmm_error (*cd_edid_install_profile)(unsigned char *edid, int edid_len, + ucmm_scope scope, char *profile_fn) = NULL; +ucmm_error (*cd_edid_remove_profile)(unsigned char *edid, int edid_len, char *profile_fn) = NULL; +ucmm_error (*cd_edid_get_profile)(unsigned char *edid, int edid_len, char **profile_fn) = NULL; +int cd_init = 0; /* nz if we've looked for colord */ +void *cd_found = NULL; /* .so handle if we've found colord */ + +/* Return nz if found colord functions */ +int dispwin_checkfor_colord() { + + if (cd_init) + return (cd_found != NULL); + + cd_found = NULL; + + if ((cd_found = dlopen("libcolordcompat.so", RTLD_LAZY)) != NULL) { + + cd_edid_install_profile = dlsym(cd_found, "cd_edid_install_profile"); + cd_edid_remove_profile = dlsym(cd_found, "cd_edid_remove_profile"); + cd_edid_get_profile = dlsym(cd_found, "cd_edid_get_profile"); + + if (cd_edid_install_profile == NULL + || cd_edid_remove_profile == NULL + || cd_edid_get_profile == NULL) { + cd_found = NULL; + } + } + + cd_init = 1; + + return (cd_found != NULL); +} + +#endif + + + +/* ----------------------------------------------- */ /* Install a display profile and make */ /* it the default for this display. */ /* Set the display to the calibration in the profile */ @@ -2223,6 +2366,7 @@ static int set_X11_atom(dispwin *p, char *fname) { /* (We assume that the caller has checked that it's an ICC profile) */ /* Return nz if failed */ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { + debugr2((errout,"dispwin_install_profile '%s'\n",fname)); #ifdef NT { char *fullpath; @@ -2232,8 +2376,6 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { WCS_PROFILE_MANAGEMENT_SCOPE wcssc; unsigned short *wpath, *wbname, *wmonid; - debugr2((errout,"dispwin_install_profile got '%s'\n",fname)); - if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) { debugr2((errout,"Getting color directory failed\n")); return 1; @@ -2555,7 +2697,12 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { else sc = ucmm_user; - if ((ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname)) != ucmm_ok) { + if (cd_found) + ev = cd_edid_install_profile(p->edid, p->edid_len, sc, fname); + else + ev = ucmm_install_monitor_profile(sc, p->edid, p->edid_len, p->name, fname); + + if (ev != ucmm_ok) { debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev))); return 1; } @@ -2583,6 +2730,7 @@ int dispwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) { /* 1 if not sucessfully deleted */ /* 2 if profile not found */ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { + debugr2((errout,"dispwin_uninstall_profile '%s'\n", fname)); #ifdef NT { char *fullpath; @@ -2592,8 +2740,6 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { WCS_PROFILE_MANAGEMENT_SCOPE wcssc; unsigned short *wbname, *wmonid; - debugr2((errout,"Uninstalling '%s'\n", fname)); - if (GetColorDirectory(NULL, colpath, &colpathlen) == 0) { debugr2((errout,"Getting color directory failed\n")); return 1; @@ -2841,7 +2987,12 @@ int dispwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) { else sc = ucmm_user; - if ((ev = ucmm_uninstall_monitor_profile(sc, p->edid, p->edid_len, p->name, fname)) != ucmm_ok) { + if (cd_found) + ev = cd_edid_remove_profile(p->edid, p->edid_len, fname); + else + ev = ucmm_uninstall_monitor_profile(sc, p->edid, p->edid_len, p->name, fname); + + if (ev != ucmm_ok) { debugr2((errout,"Installing profile '%s' failed with error %d '%s'\n",fname,ev,ucmm_error_string(ev))); return 1; } @@ -3039,7 +3190,12 @@ icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) { debugr2((errout,"dispwin_get_profile called\n")); - if ((ev = ucmm_get_monitor_profile(p->edid, p->edid_len, p->name, &profile)) == ucmm_ok) { + if (cd_found) + ev = cd_edid_get_profile(p->edid, p->edid_len, &profile); + else + ev = ucmm_get_monitor_profile(p->edid, p->edid_len, p->name, &profile); + + if (ev == ucmm_ok) { if (name != NULL) { strncpy(name, profile, mxlen); @@ -3143,12 +3299,17 @@ icmFile *dispwin_get_profile(dispwin *p, char *name, int mxlen) { /* ----------------------------------------------- */ -/* Restore the display state */ +/* Restore the display state and free ramdacs */ static void restore_display(dispwin *p) { + if (p->oor != NULL) { + p->oor->del(p->oor); + p->oor = NULL; + } /* Restore the ramdac */ if (p->or != NULL) { p->set_ramdac(p, p->or, 0); + p->set_ramdac(p, p->or, 0); /* Hmm. To be sure to be sure... */ p->or->del(p->or); p->or = NULL; debugr("Restored original ramdac\n"); @@ -3288,6 +3449,9 @@ typedef struct { dispwin *p; DWWin *window; /* Our NSWindow */ DWView *view; /* Our NSView */ +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + NSColorSpace *nscs; /* Colorspace from profile */ +#endif } osx_cntx_t; static void OSX_ProcessEvents(dispwin *p); @@ -3316,9 +3480,10 @@ unsigned char emptyCursor[43] = { /* Make cursor invisible over our window */ /* * This doesn't work very well. The only way to work it properly - * is to create a CGEventTap and do a hide/unkid cursor when + * is to create a CGEventTap and do a hide/unhide cursor when * the mouse enters the window. This needs the main thread - * to be dedicated to running the event loop. + * to be dedicated to running the event loop, so would involve + * some trickiness after main() in every program. */ - (void)resetCursorRects { [super resetCursorRects]; @@ -3339,10 +3504,28 @@ unsigned char emptyCursor[43] = { frect = NSMakeRect(p->tx, p->ty, (1.0 + p->tw), (1.0 + p->th)); - [[NSColor colorWithDeviceRed: p->r_rgb[0] - green: p->r_rgb[1] - blue: p->r_rgb[2] - alpha: 1.0] setFill]; +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + /* Use matching profile to (hopefully) trigger null color transform */ + /* This doesn't work on < 10.6 though. */ + if (cx->nscs != NULL) { + CGFloat rgb[4]; + NSInteger cnt = 4; + rgb[0] = p->r_rgb[0]; + rgb[1] = p->r_rgb[1]; + rgb[2] = p->r_rgb[2]; + rgb[3] = 1.0; + cnt = [ cx->nscs numberOfColorComponents ] + 1; + [[NSColor colorWithColorSpace: cx->nscs components: rgb count: cnt] setFill]; + + /* This works for < 10.6, but not for >= 10.6 on non-primary display */ + } else +#endif + { + [[NSColor colorWithDeviceRed: p->r_rgb[0] + green: p->r_rgb[1] + blue: p->r_rgb[2] + alpha: 1.0] setFill]; + } [aPath appendBezierPathWithRect:frect]; [aPath fill]; } @@ -3388,6 +3571,8 @@ unsigned char emptyCursor[43] = { /* Create our window */ static void create_my_win(NSRect rect, osx_cntx_t *cx) { dispwin *p = cx->p; + SInt32 MacVers; + void *cspr = NULL; /* ColorSync profile ref. */ int i; /* We need to locate the NSScreen that corresponds to the */ @@ -3427,6 +3612,61 @@ static void create_my_win(NSRect rect, osx_cntx_t *cx) { [cx->window setContentView: cx->view]; [cx->window makeKeyAndOrderFront: nil]; + + /* Use a null color transform to ensure device values */ + /* on non-primary display for 10.6+ */ +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + + /* Get the ColorSync profile for this display */ + if ((cspr = cur_colorsync_ref(p)) == NULL) { + debugr2((errout,"cur_colorsync_ref failed\n")); + + } else { + +#ifdef NEVER + /* This is buggy on 10.6 (returns gray space). it does work on 10.4-10.5 & 10.7+ */ + cx->nscs = [[NSColorSpace alloc] initWithColorSyncProfile: cspr]; +#else + CGColorSpaceRef cgref; + + /* This works on 10.6+ */ + if ((cgref = CGColorSpaceCreateWithPlatformColorSpace(cspr)) == NULL) { + debugr2((errout,"CGColorSpaceCreateWithPlatformColorSpace failed\n")); + } else { + /* 10.4 doesn't declare initWithCGColorSpace, but does implement it */ + cx->nscs = [[NSColorSpace alloc] initWithCGColorSpace: cgref]; + } + CFRelease(cgref); +#endif + if (cx->nscs == NULL) { + debugr2((errout,"initWithColorSyncProfile failed\n")); + } else { + if ([ cx->nscs numberOfColorComponents ] != 3) { + [cx->nscs release]; + cx->nscs = NULL; + } + } + + if (cspr != NULL) { +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + CFRelease((ColorSyncProfileRef)cspr); +#else + CMCloseProfile((CMProfileRef)cspr); +#endif + } + } +#endif /* >= 10.6 */ + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + /* >= 10.6+ device colors don't work on secondary display, need null transform. */ + /* < 10.6 null transform doesn't work. */ + if (Gestalt(gestaltSystemVersion, &MacVers) == noErr + && MacVers >= 0x1060 + && cx->nscs == NULL) { + warning("Unable to create null color transform - test colors may be wrong!"); + } +#endif + } #endif /* __APPLE__ */ @@ -3440,40 +3680,70 @@ dispwin *p, double r, double g, double b /* Color values 0.0 - 1.0 */ ) { int j; + double orgb[3]; /* Previous RGB value */ + double kr, kf; + int update_delay = p->update_delay; + double xdelay = 0.0; /* Extra delay for response time */ debugr("dispwin_set_color called\n"); if (p->nowin) return 1; - p->rgb[0] = r; - p->rgb[1] = g; - p->rgb[2] = b; + orgb[0] = p->rgb[0]; p->rgb[0] = r; + orgb[1] = p->rgb[1]; p->rgb[1] = g; + orgb[2] = p->rgb[2]; p->rgb[2] = b; + +//printf("\n set rgb %f %f %f\n",p->rgb[0], p->rgb[1], p->rgb[2]); for (j = 0; j < 3; j++) { if (p->rgb[j] < 0.0) p->rgb[j] = 0.0; else if (p->rgb[j] > 1.0) p->rgb[j] = 1.0; - p->r_rgb[j] = p->rgb[j]; + p->r_rgb[j] = p->s_rgb[j] = p->rgb[j]; + if (p->out_tvenc) { + p->r_rgb[j] = p->s_rgb[j] = ((235.0 - 16.0) * p->s_rgb[j] + 16.0)/255.0; + + /* For video encoding the extra bits of precision are created by bit shifting */ + /* rather than scaling, so we need to scale the fp value to account for this. */ + if (p->pdepth > 8) + p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->pdepth - 8))) + /((1 << p->pdepth) - 1.0); + } } +//if (p->out_tvenc) { +//printf(" %d: 8 bit tv = s_rgb %f %f %f\n",j, p->s_rgb[0], p->s_rgb[1], p->s_rgb[2]); +//printf(" %d: %d bitraster r_rgb %f %f %f\n",j, p->pdepth,p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); +//} + /* Use ramdac for high precision native output. */ /* The ramdac is used to hold the lsb that the frame buffer */ /* doesn't hold. */ - if (p->native == 1) { + if ((p->native & 1) == 1) { double prange = p->r->nent - 1.0; + p->r->setlin(p->r); /* In case something else altered this */ + for (j = 0; j < 3; j++) { int tt; + double vv; -//printf("~1 %d: in %f, ",j,p->rgb[j]); - tt = (int)(p->rgb[j] * prange); - p->r->v[j][tt] = p->rgb[j]; + vv = p->s_rgb[j]; + + /* For video encoding the extra bits of precision are created by bit shifting */ + /* rather than scaling, so we need to scale the fp value to account for this. */ + if (p->out_tvenc && p->edepth > 8) + vv = (vv * 255 * (1 << (p->edepth - 8)))/((1 << p->edepth) - 1.0); + + tt = (int)(vv * prange + 0.5); p->r_rgb[j] = (double)tt/prange; /* RAMDAC output Quantized value */ -//printf(" cell[%d], val %f, rast val %f\n",tt, p->rgb[j], p->r_rgb[j]); + p->r->v[j][tt] = vv; + +//printf(" cell[%d] = r_rgb %f, cell val %f\n",tt, p->r_rgb[j], vv); } - if (p->set_ramdac(p,p->r, 0)) { + if (p->set_ramdac(p, p->r, 0)) { debugr("set_ramdac() failed\n"); return 1; } @@ -3487,10 +3757,15 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* Stop the system going to sleep */ - /* This used to work OK in reseting the screen saver, but not in Vista :-( */ + /* This used to work OK in reseting the screen saver on */ + /* all versions of MSWin. */ SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); - /* So we use a fake mouse non-movement reset the Vista screensaver. */ +#ifdef NEVER + /* (Some people use ES_CONTINUOUS | ES_AWAYMODE_REQUIRED as well ?? */ + /* See also setting SPI_SETSCREENSAVEACTIVE? ) */ + + /* Another approach is a fake mouse non-movementr. */ SystemParametersInfo(SPI_SETBLOCKSENDINPUTRESETS, FALSE, NULL, 0); fip.type = INPUT_MOUSE; fip.mi.dx = 0; @@ -3499,19 +3774,24 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ fip.mi.time = 0; fip.mi.dwExtraInfo = 0; SendInput(1, &fip, sizeof(INPUT)); +#endif p->colupd++; +//printf("~1 set color %f %f %f\n", p->r_rgb[0], p->r_rgb[1], p->r_rgb[2]); /* Trigger a WM_PAINT */ if (!InvalidateRect(p->hwnd, NULL, FALSE)) { debugr2((errout,"InvalidateRect failed, lasterr = %d\n",GetLastError())); return 1; } + UpdateWindow(p->hwnd); +//printf("~1 waiting for paint\n"); /* Wait for WM_PAINT to be executed */ - while (p->colupd != p->colupde) { - msec_sleep(20); + while (p->colupd != p->colupde && p->cberror == 0) { + msec_sleep(10); } +//printf("~1 paint done\n"); } #endif /* NT */ @@ -3523,6 +3803,10 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ return 2; } + /* We're creating and draining a pool here to ensure that all the */ + /* auto release objects get drained when we're finished (?) */ + NSAutoreleasePool *tpool = [NSAutoreleasePool new]; + /* Stop the system going to sleep */ UpdateSystemActivity(OverallAct); @@ -3552,6 +3836,8 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ /* Process events */ OSX_ProcessEvents(p); + [tpool release]; + #endif /* __APPLE__ */ /* - - - - - - - - - - - - - - */ @@ -3597,11 +3883,48 @@ double r, double g, double b /* Color values 0.0 - 1.0 */ free(cmd); } + /* Don't want extra delay if we're measuring update delay */ + if (update_delay != 0 && p->do_resp_time_del) { + /* Compute am expected response time for the change in level */ + kr = DISPLAY_RISE_TIME/log(1 - 0.9); /* Exponent constant */ + kf = DISPLAY_FALL_TIME/log(1 - 0.9); /* Exponent constant */ +//printf("~1 k2 = %f\n",k2); + for (j = 0; j < 3; j++) { + double el, dl, n, t; + + el = pow(p->rgb[j], 2.2); + dl = el - pow(orgb[j], 2.2); /* Change in level */ + if (fabs(dl) > 0.01) { /* More than 1% change in level */ + n = DISPLAY_SETTLE_AIM * el; + if (n < DISPLAY_ABS_AIM) + n = DISPLAY_ABS_AIM; +//printf("~1 sl %f, el %f, log (%f / %f)\n",sl,el,n,fabs(sl - el)); + if (dl > 0.0) + t = kr * log(n/dl); + else + t = kf * log(n/-dl); + + if (t > xdelay) + xdelay = t; + } + } +//printf("~1 xdelay = %f secs\n",xdelay); + xdelay *= 1000.0; /* To msec */ + /* This is kind of a fudge since update delay is after latency, */ + /* but displays with long delay (ie. CRT) have short latency, and visa versa */ + if ((int)xdelay > update_delay) + update_delay = (int)xdelay; + } + /* Allow some time for the display to update before */ /* a measurement can take place. This allows for CRT */ /* refresh, or LCD processing/update time, + */ /* display settling time (quite long for smaller LCD changes). */ - msec_sleep(p->update_delay); + msec_sleep(update_delay); + + if (p->cberror) { /* Callback routine failed */ + return 1; + } return 0; } @@ -3633,7 +3956,7 @@ char *callout /* ----------------------------------------------- */ /* Destroy ourselves */ -static void dispwin_del( +void dispwin_del( dispwin *p ) { @@ -3682,6 +4005,12 @@ dispwin *p p->winclose = 1; [cx->window release]; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 + if (cx->nscs != NULL) + [cx->nscs release]; +#endif + free(p->osx_cntx); p->osx_cntx = NULL; } @@ -3730,6 +4059,11 @@ dispwin *p #define SWP_STATECHANGED 0x8000 #endif +/* MingW doesn't seem to have this, even though it's been there sine Win2k ... */ +#ifndef ICM_DONE_OUTSIDEDC +# define ICM_DONE_OUTSIDEDC 4 +#endif + static LRESULT CALLBACK MainWndProc( HWND hwnd, UINT message, @@ -3776,29 +4110,50 @@ static LRESULT CALLBACK MainWndProc( vali[j] = (int)(255.0 * p->r_rgb[j] + 0.5); } - hdc = BeginPaint(hwnd, &ps); + if ((hdc = BeginPaint(hwnd, &ps)) == NULL) { + debugrr2l(4, (stderr,"BeginPaint failed\n")); + EndPaint(hwnd, &ps); + p->cberror = 2; + return 0; + } - SaveDC(hdc); + if (SaveDC(hdc) == 0) { + debugrr2l(4, (stderr,"SaveDC failed\n")); + EndPaint(hwnd, &ps); + p->cberror = 3; + return 0; + } - /* Try and turn ICM off */ -#ifdef ICM_DONE_OUTSIDEDC + /* Try and turn ICM & WCS off */ if (!SetICMMode(hdc, ICM_DONE_OUTSIDEDC)) { + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + osver.dwMajorVersion = 5; + GetVersionEx(&osver); /* This seems to fail with "invalid handle" under NT4 */ - /* Does it work under Win98 or Win2K ? */ - printf("SetICMMode failed, lasterr = %d\n",GetLastError()); + /* so only report an error if Win2K or more */ + if (osver.dwMajorVersion >= 5) + printf("SetICMMode failed, lasterr = %d\n",GetLastError()); } -#endif - - hbr = CreateSolidBrush(RGB(vali[0],vali[1],vali[2])); - SelectObject(hdc,hbr); - SetRect(&rect, p->tx, p->ty, p->tx + p->tw, p->ty + p->th); - FillRect(hdc, &rect, hbr); + if ((hbr = CreateSolidBrush(RGB(vali[0],vali[1],vali[2]))) == NULL) { + debugrr2l(4, (stderr,"CreateSolidBrush failed\n")); + RestoreDC(hdc,-1); + EndPaint(hwnd, &ps); + p->cberror = 4; + return 0; + } + if (SelectObject(hdc, hbr) == NULL + || SetRect(&rect, p->tx, p->ty, p->tx + p->tw, p->ty + p->th) == 0 + || FillRect(hdc, &rect, hbr) == 0) { + debugrr2l(4, (stderr,"SelectObject/SetRect/FillRect failed\n")); + p->cberror = 5; + } + DeleteObject(hbr); RestoreDC(hdc,-1); - DeleteDC(hdc); - EndPaint(hwnd, &ps); + GdiFlush(); p->colupde = p->colupd; /* We're updated to this color */ @@ -3847,7 +4202,7 @@ static void OSX_ProcessEvents(dispwin *p) { /* We're creating and draining a pool here to ensure that all the */ /* auto release objects get drained when we're finished (?) */ - NSAutoreleasePool *tpool = [NSAutoreleasePool new]; +// NSAutoreleasePool *tpool = [NSAutoreleasePool new]; /* Wait until the events are done */ to = [NSDate dateWithTimeIntervalSinceNow:0.01]; /* autorelease ? */ @@ -3860,7 +4215,7 @@ static void OSX_ProcessEvents(dispwin *p) { break; } } - [tpool release]; +// [tpool release]; } #endif /* __APPLE__ */ @@ -3890,7 +4245,7 @@ int win_message_thread(void *pp) { wc.hInstance = NULL; /* Application that owns the class. */ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_CROSS); - wc.hbrBackground = GetStockObject(BLACK_BRUSH); + wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* So full screen black works */ wc.lpszMenuName = NULL; wc.lpszClassName = p->AppName; @@ -3913,7 +4268,9 @@ int win_message_thread(void *pp) { p->AppName, "Argyll Display Calibration Window", WS_VISIBLE | WS_DISABLED | WS_POPUP, -// WS_OVERLAPPEDWINDOW | WS_VISIBLE, +// WS_EX_TOPMOST +// WS_EX_PALETTEWINDOW +// WS_OVERLAPPEDWINDOW | WS_VISIBLE // WS_POPUPWINDOW | WS_VISIBLE, p->xo, p->yo, /* Location */ p->wi, p->he, /* Size */ @@ -3978,9 +4335,13 @@ disppath *disp, /* Display to calibrate. */ double width, double height, /* Width and height in mm */ double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */ int nowin, /* NZ if no window should be created - RAMDAC access only */ -int native, /* 0 = use current current or given calibration curve */ - /* 1 = use native linear out & high precision */ -int *noramdac, /* Return nz if no ramdac access. native is set to 0 */ +int native, /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high precn. */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ +int *noramdac, /* Return nz if no ramdac access. native is set to X0 */ +int *nocm, /* Return nz if no CM cLUT access. native is set to 0X */ +int out_tvenc, /* 1 = use RGB Video Level encoding */ int blackbg, /* NZ if whole screen should be filled with black */ int override, /* NZ if override_redirect is to be used on X11 */ int ddebug /* >0 to print debug statements to stderr */ @@ -3990,6 +4351,16 @@ int ddebug /* >0 to print debug statements to stderr */ debug("new_dispwin called\n"); +#if defined(UNIX_X11) && defined(USE_UCMM) + dispwin_checkfor_colord(); /* Make colord functions available */ + if (ddebug) { + if (cd_found) + fprintf(stderr,"using colord for profile installation\n"); + else + fprintf(stderr,"using ucmm for profile installation\n"); + } +#endif + if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) { if (ddebug) fprintf(stderr,"new_dispwin failed because malloc failed\n"); return NULL; @@ -3998,6 +4369,7 @@ int ddebug /* >0 to print debug statements to stderr */ /* !!!! Make changes in webwin.c as well !!!! */ p->nowin = nowin; p->native = native; + p->out_tvenc = out_tvenc; p->blackbg = blackbg; p->ddebug = ddebug; p->get_ramdac = dispwin_get_ramdac; @@ -4027,6 +4399,8 @@ int ddebug /* >0 to print debug statements to stderr */ if (p->update_delay < p->min_update_delay) p->update_delay = p->min_update_delay; + p->do_resp_time_del = 1; /* Default this to on */ + /* Basic object is initialised, so create a window */ /* -------------------------------------------------- */ @@ -4119,6 +4493,8 @@ int ddebug /* >0 to print debug statements to stderr */ else p->pdepth = bpp/3; + p->edepth = 16; + if (nowin == 0) { /* We use a thread to process the window messages, so that */ @@ -4167,12 +4543,30 @@ int ddebug /* >0 to print debug statements to stderr */ } p->ddid = disp->ddid; /* Display we're working on */ + /* Hmm. Could we use CGDisplayGammaTableCapacity() instead ? */ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 { CGDisplayModeRef dispmode; CFStringRef pixenc; + int cap = CGDisplayGammaTableCapacity(p->ddid); + int fbdepth = 0; - p->pdepth = 0; + debugr2((errout,"new_dispwin: CGDisplayGammaTableCapacity = %d\n",cap)); + + /* Compute GammaTable depth */ + { + for (p->pdepth = 1; p->pdepth < 17; p->pdepth++) { + if ((1 << p->pdepth) == cap) + break; + } + if (p->pdepth >= 17) { + debugr2((errout,"new_dispwin: failed to extract depth from GammaTableCapacity %d\n",cap)); + dispwin_del(p); + return NULL; + } + debugr2((errout,"new_dispwin: found pixel depth %d bits\n",p->pdepth)); + } + /* Get frame buffer depth for sanity check */ dispmode = CGDisplayCopyDisplayMode(p->ddid); pixenc = CGDisplayModeCopyPixelEncoding(dispmode); @@ -4180,23 +4574,42 @@ int ddebug /* >0 to print debug statements to stderr */ /* Hmm. Don't know what to do with kIO16BitFloatPixels or kIO32BitFloatPixels */ if (CFStringCompare(pixenc, CFSTR(kIO64BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 16; + fbdepth = 16; else if (CFStringCompare(pixenc, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 10; + fbdepth = 10; else if (CFStringCompare(pixenc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 8; + fbdepth = 8; else if (CFStringCompare(pixenc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - p->pdepth = 5; + fbdepth = 5; +#ifndef DEBUG + if (p->ddebug) +#endif + { + char buf[200]; + CFStringGetCString(pixenc, buf, 200, kCFStringEncodingUTF8); + debugr2((errout,"new_dispwin: CGDisplayModePixelEncoding = '%s'\n",buf)); + } + CFRelease(pixenc); CGDisplayModeRelease(dispmode); + + if (p->pdepth != fbdepth) { + static int warned = 0; + if (!warned) { + warning("new_dispwin: frame buffer depth %d != GammaTable depth %d\n",fbdepth, p->pdepth); + warned = 1; + } + } } #else p->pdepth = CGDisplayBitsPerSample(p->ddid); #endif + p->edepth = 16; /* By experiment it seems to be 16 bits too */ + if (nowin == 0) { /* Create a window */ osx_cntx_t *cx; CGSize sz; /* Display size in mm */ @@ -4206,13 +4619,17 @@ int ddebug /* >0 to print debug statements to stderr */ debugr2((errout, "new_dispwin: About to open display '%s'\n",disp->name)); + /* We're creating and draining a pool here to ensure that all the */ + /* auto release objects get drained when we're finished (?) */ + NSAutoreleasePool *tpool = [NSAutoreleasePool new]; + /* If we don't have an application object, create one. */ /* (This should go in a common library) */ /* Note that we don't actually clean this up on exit - */ /* possibly we can't. */ if (NSApp == nil) { - static NSAutoreleasePool *pool; /* Pool used for NSApp */ - pool = [NSAutoreleasePool new]; +// static NSAutoreleasePool *pool; /* Pool used for NSApp */ +// pool = [NSAutoreleasePool new]; NSApp = [NSApplication sharedApplication]; /* Creates NSApp */ [NSApp finishLaunching]; /* We seem to need this, because otherwise we don't get focus automatically */ @@ -4220,6 +4637,7 @@ int ddebug /* >0 to print debug statements to stderr */ } if ((cx = (osx_cntx_t *)calloc(sizeof(osx_cntx_t), 1)) == NULL) { + [tpool release]; debugr2((errout,"new_dispwin: Malloc failed (osx_cntx_t)\n")); dispwin_del(p); return NULL; @@ -4268,6 +4686,8 @@ int ddebug /* >0 to print debug statements to stderr */ OSX_ProcessEvents(p); + [tpool release]; + p->winclose = 0; } @@ -4362,6 +4782,7 @@ int ddebug /* >0 to print debug statements to stderr */ //p->pdepth = DefaultDepth(p->mydisplay, p->myscreen)/3; myvisual = DefaultVisual(p->mydisplay, p->myscreen); p->pdepth = myvisual->bits_per_rgb; + p->edepth = 16; if (nowin == 0) { /* Create a window */ rootwindow = RootWindow(p->mydisplay, p->myscreen); @@ -4625,32 +5046,47 @@ int ddebug /* >0 to print debug statements to stderr */ #endif /* UNIX X11 */ /* -------------------------------------------------- */ - if (!p->nowin) { - /* Setup for native mode */ - if (p->native) { - debug("About to setup native mode\n"); - if ((p->or = p->get_ramdac(p)) == NULL - || (p->r = p->or->clone(p->or)) == NULL) { - if (noramdac != NULL) - *noramdac = 1; - debugr("new_dispwin: Accessing VideoLUT failed, so no way to guarantee that calibration is turned off!!\n"); - warning("new_dispwin: Accessing VideoLUT failed, so no way to guarantee that calibration is turned off!!"); - p->native = 0; - } else { - p->r->setlin(p->r); - if (noramdac != NULL) - *noramdac = 0; - debug("Saved original VideoLUT\n"); - } - } else { - if (p->get_ramdac(p) == NULL) { - if (noramdac != NULL) - *noramdac = 1; - } + /* Save the original ramdac, which gets restored on exit */ + if ((p->or = p->get_ramdac(p)) != NULL) { + + if (noramdac != NULL) + *noramdac = 0; + + debugr("Saved original VideoLUT\n"); + + /* Copy original ramdac that never gets altered */ + if ((p->oor = p->or->clone(p->or)) == NULL) { + dispwin_del(p); + debugr("ramdac clone failed - memory ?\n"); + return NULL; } - + + /* Create a working ramdac for native or other use */ + if ((p->r = p->or->clone(p->or)) == NULL) { + dispwin_del(p); + debugr("ramdac clone failed - memory ?\n"); + return NULL; + } + + } else { + debugr("Unable to access VideoLUT\n"); + if (noramdac != NULL) + *noramdac = 1; + p->oor = p->or = p->r = NULL; + } + + if (!p->nowin) { + /* Make sure initial test color is displayed */ dispwin_set_color(p, p->rgb[0], p->rgb[1], p->rgb[2]); + + /* Hmm. Could we add this ?? */ + /* Hard to know whether OS CM is active though. By default */ + /* dispwin disables it. */ + if (nocm != NULL) + *nocm = 1; + + p->native &= ~2; } debugr("new_dispwin: return sucessfully\n"); @@ -4705,6 +5141,16 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { && XRRQueryExtension(mydisplay, &evb, &erb) != 0 && XRRQueryVersion(mydisplay, &majv, &minv) && majv == 1 && minv >= 2) { + static void *xrr_found = NULL; /* .so handle */ + static XRRScreenResources *(*_XRRGetScreenResourcesCurrent) + (Display *dpy, Window window) = NULL; + XRRScreenResources *scrnres; + + if (minv >= 3 && xrr_found == NULL) { + if ((xrr_found = dlopen("libXrandr.so", RTLD_LAZY)) != NULL) + _XRRGetScreenResourcesCurrent = dlsym(xrr_found, "XRRGetScreenResourcesCurrent"); + } + if (verb) printf("Found XRandR 1.2 or latter\n"); XRRSelectInput(mydisplay,RootWindow(mydisplay,0), @@ -4721,7 +5167,12 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { if (update_profiles == 0) { if (dopoll) { for (;;) { - XRRGetScreenResources(mydisplay, RootWindow(mydisplay,0)); + if (minv >= 3 && _XRRGetScreenResourcesCurrent != NULL) { + _XRRGetScreenResourcesCurrent(mydisplay, RootWindow(mydisplay,0)); + + } else { + XRRGetScreenResources(mydisplay, RootWindow(mydisplay,0)); + } if(XPending(mydisplay) > 0) break; sleep(2); @@ -4777,7 +5228,7 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { break; if (verb) printf("Updating display %d = '%s'\n",i+1,dp[i]->description); - if ((dw = new_dispwin(dp[i], 0.0, 0.0, 0.0, 0.0, 1, 0, NULL, 0, 0, ddebug)) == NULL) { + if ((dw = new_dispwin(dp[i], 0.0, 0.0, 0.0, 0.0, 1, 0, NULL, NULL, 0, 0, 0, ddebug)) == NULL) { if (verb) printf("Failed to access screen %d of display '%s'\n",i+1,dnbuf); continue; } @@ -4814,7 +5265,7 @@ int x11_daemon_mode(disppath *disp, int verb, int ddebug) { } if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) { - if (verb) printf("Failed to fined vcgt tagd in profile for screen %d for display '%s' so setting linear\n",i+1,dnbuf); + if (verb) printf("Failed to find vcgt tagd in profile for screen %d for display '%s' so setting linear\n",i+1,dnbuf); for (j = 0; j < r->nent; j++) { iv = j/(r->nent-1.0); r->v[0][j] = iv; @@ -4927,14 +5378,19 @@ static void usage(char *diag, ...) { } free_disppaths(dp); fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); +#ifdef NT + fprintf(stderr," -dmadvr Display via MadVR Video Renderer\n"); +#endif + fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n"); fprintf(stderr," -F Fill whole screen with black background\n"); fprintf(stderr," -i Run forever with random values\n"); fprintf(stderr," -G filename Display RGB colors from CGATS file\n"); + fprintf(stderr," -C r.rr,g.gg,b.bb Add this RGB color to list to be displayed\n"); fprintf(stderr," -m Manually cycle through values\n"); fprintf(stderr," -f Test grey ramp fade\n"); fprintf(stderr," -r Test just Video LUT loading & Beeps\n"); - fprintf(stderr," -n Test native output (rather than through Video LUT)\n"); + fprintf(stderr," -n Test native output (rather than through Video LUT and C.M.)\n"); fprintf(stderr," -s filename Save the currently loaded Video LUT to 'filename'\n"); fprintf(stderr," -c Load a linear display calibration\n"); fprintf(stderr," -V Verify that calfile/profile cal. is currently loaded in LUT\n"); @@ -4944,7 +5400,7 @@ static void usage(char *diag, ...) { fprintf(stderr," d is one of: n = network, l = local system, u = user (default)\n"); fprintf(stderr," -L Load installed profiles cal. into Video LUT\n"); #if defined(UNIX_X11) - fprintf(stderr," -E Run in daemon loader mode for given X11 server\n"); + fprintf(stderr," -X Run in daemon loader mode for given X11 server\n"); #endif /* X11 */ fprintf(stderr," -D [level] Print debug diagnostics to stderr\n"); fprintf(stderr," calfile Load calibration (.cal or %s) into Video LUT\n",ICC_FILE_EXT); @@ -4961,19 +5417,27 @@ main(int argc, char *argv[]) { int verb = 0; /* Verbose flag */ int ddebug = 0; /* debug level */ int webdisp = 0; /* NZ for web display, == port number */ +#ifdef NT + int madvrdisp = 0; /* NZ for MadVR display */ +#endif disppath *disp = NULL; /* Display being used */ double hpatscale = 1.0, vpatscale = 1.0; /* scale factor for test patch size */ double ho = 0.0, vo = 0.0; /* Test window offsets, -1.0 to 1.0 */ + int out_tvenc = 0; /* 1 to use RGB Video Level encoding */ int blackbg = 0; /* NZ if whole screen should be filled with black */ int nowin = 0; /* Don't create test window */ int ramd = 0; /* Just test ramdac */ int fade = 0; /* Test greyramp fade */ - int native = 0; /* 0 = use current current or given calibration curve */ - /* 1 = set native linear output and use ramdac high prec'n */ - /* 2 = set native linear output */ - int noramdac = 0; /* Set to nz if there is no VideoLUT access */ + int native = 0; /* X0 = use current per channel calibration curve */ + /* X1 = set native linear output and use ramdac high precn. */ + /* 0X = use current color management cLut (MadVR) */ + /* 1X = disable color management cLUT (MadVR) */ + int noramdac = 0; /* nz if no ramdac access. native is set to X0 */ + int nocm = 0; /* nz if no CM cLUT access. native is set to 0X */ int inf = 0; /* Infnite/manual patches flag */ char pcname[MAXNAMEL+1] = "\000"; /* CGATS patch color name */ + int nmrgb = 0; /* Number of manual RGB values */ + double mrgb[10][3]; /* Manual RGB values */ int clear = 0; /* Clear any display calibration (any calname is ignored) */ char sname[MAXNAMEL+1] = "\000"; /* Current cal save name */ int verify = 0; /* Verify that calname is currently loaded */ @@ -4986,7 +5450,7 @@ main(int argc, char *argv[]) { dispwin *dw; unsigned int seed = 0x56781234; int i, j; - ramdac *or = NULL, *r = NULL; +// ramdac *r = NULL; int is_ok_icc = 0; /* The profile is OK */ error_program = "Dispwin"; @@ -5038,6 +5502,12 @@ main(int argc, char *argv[]) { usage("Web port number must be in range 1..65535"); } fa = nfa; +#ifdef NT + } else if (strncmp(na,"madvr",5) == 0 + || strncmp(na,"MADVR",5) == 0) { + madvrdisp = 1; + fa = nfa; +#endif } else { #if defined(UNIX_X11) int ix, iv; @@ -5096,10 +5566,14 @@ main(int argc, char *argv[]) { } else if (argv[fa][1] == 'F') { blackbg = 1; + /* Video mode encoding */ + } else if (argv[fa][1] == 'E') { + out_tvenc = 1; + } else if (argv[fa][1] == 'i') inf = 1; - else if (argv[fa][1] == 'm' || argv[fa][1] == 'M') + else if (argv[fa][1] == 'm') inf = 2; /* CGATS patch color file */ @@ -5108,16 +5582,28 @@ main(int argc, char *argv[]) { if (na == NULL) usage("-G parameter is missing"); strncpy(pcname,na,MAXNAMEL); pcname[MAXNAMEL] = '\000'; } + /* Manual color */ + else if (argv[fa][1] == 'C') { + fa = nfa; + if (nmrgb >= 10) + usage("Can only be up to 10 -C values"); + if (na == NULL) usage("-C parameters are missing"); + if (sscanf(na, "%lf,%lf,%lf ",&mrgb[nmrgb][0],&mrgb[nmrgb][1],&mrgb[nmrgb][2]) != 3) + usage("-C parameters '%s' are badly formatted",na); + if (mrgb[nmrgb][0] < 0.0 || mrgb[nmrgb][0] > 1.0 + || mrgb[nmrgb][1] < 0.0 || mrgb[nmrgb][1] > 1.0 + || mrgb[nmrgb][2] < 0.0 || mrgb[nmrgb][2] > 1.0) + usage("-C parameters %f %f %f are out of range 0.0 - 1.0",mrgb[nmrgb][0],mrgb[nmrgb][1],mrgb[nmrgb][2]); + nmrgb++; + } else if (argv[fa][1] == 'f') fade = 1; - else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') + else if (argv[fa][1] == 'r') ramd = 1; - else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') { - native = 1; - if (argv[fa][1] == 'N') - native = 2; + else if (argv[fa][1] == 'n') { + native = 3; /* Disable cal & any CM */ } else if (argv[fa][1] == 's') { @@ -5141,7 +5627,7 @@ main(int argc, char *argv[]) { else if (argv[fa][1] == 'L') loadprofile = 1; - else if (argv[fa][1] == 'E') + else if (argv[fa][1] == 'X') daemonmode = 1; else if (argv[fa][1] == 'S') { @@ -5162,7 +5648,11 @@ main(int argc, char *argv[]) { } /* No explicit display has been set */ - if (webdisp == 0 && disp == NULL) { + if (disp == NULL +#ifdef NT + && madvrdisp == 0 +#endif + && webdisp == 0) { int ix = 0; #if defined(UNIX_X11) char *dn, *pp; @@ -5212,22 +5702,36 @@ main(int argc, char *argv[]) { if (webdisp != 0) { - if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, blackbg, verb, ddebug)) == NULL) { - printf("Error - new_webpwin failed!\n"); + if ((dw = new_webwin(webdisp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, + &noramdac, &nocm, out_tvenc, blackbg, verb, ddebug)) == NULL) { + printf("Error - new_webwin failed!\n"); return -1; } - noramdac = 1; +#ifdef NT + } else if (madvrdisp != 0) { + if (out_tvenc) { + printf("Error - Set TV encodfing in MadVR\n"); + return -1; + } + + if ((dw = new_madvrwin(100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, + &noramdac, &nocm, out_tvenc, blackbg, verb, ddebug)) == NULL) { + printf("Error - new_madvrwin failed!\n"); + return -1; + } +#endif } else { if (verb) printf("About to open dispwin object on the display\n"); - if ((dw = new_dispwin(disp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, &noramdac, blackbg, 1, ddebug)) == NULL) { + if ((dw = new_dispwin(disp, 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, nowin, native, + &noramdac, &nocm, out_tvenc, blackbg, 1, ddebug)) == NULL) { printf("Error - new_dispwin failed!\n"); return -1; } } - if (native != 0 && noramdac) { - error("We don't have access to the VideoLUT so can't display native colors\n"); + if ((native & 1) != 0 && noramdac) { + warning("Unable to access to VideoLUTs so can't be sure colors are native"); } /* Save the current Video LUT to the calfile */ @@ -5242,7 +5746,7 @@ main(int argc, char *argv[]) { if (verb) printf("About to save current loaded calibration to file '%s'\n",sname); - if ((r = dw->get_ramdac(dw)) == NULL) { + if (dw->oor == NULL) { error("We don't have access to the VideoLUT"); } @@ -5267,16 +5771,16 @@ main(int argc, char *argv[]) { error("Malloc failed!"); /* Write the video lut curve values */ - for (i = 0; i < r->nent; i++) { - double iv = i/(r->nent-1.0); + for (i = 0; i < dw->oor->nent; i++) { + double iv = i/(dw->oor->nent-1.0); #if defined(__APPLE__) && defined(__POWERPC__) gcc_bug_fix(i); #endif setel[0].d = iv; - setel[1].d = r->v[0][i]; - setel[2].d = r->v[1][i]; - setel[3].d = r->v[2][i]; + setel[1].d = dw->oor->v[0][i]; + setel[2].d = dw->oor->v[1][i]; + setel[3].d = dw->oor->v[2][i]; ocg->add_setarr(ocg, 0, setel); } @@ -5287,8 +5791,6 @@ main(int argc, char *argv[]) { error("Write error to '%s' : %s",sname,ocg->err); ocg->del(ocg); /* Clean up */ - r->del(r); - r = NULL; /* Fall through, as we may want to do other stuff too */ } @@ -5297,28 +5799,24 @@ main(int argc, char *argv[]) { if (clear != 0) { int rv; - if ((r = dw->get_ramdac(dw)) == NULL) { - error("We don't have access to the VideoLUT"); - } + if (dw->or == NULL) + error("We don't have access to the VideoLUT for clearing"); - for (i = 0; i < r->nent; i++) { - double iv = i/(r->nent-1.0); - r->v[0][i] = iv; - r->v[1][i] = iv; - r->v[2][i] = iv; + for (i = 0; i < dw->or->nent; i++) { + double iv = i/(dw->or->nent-1.0); + dw->or->v[0][i] = iv; + dw->or->v[1][i] = iv; + dw->or->v[2][i] = iv; } if (verb) printf("About to clear the calibration\n"); - if ((rv = dw->set_ramdac(dw,r,1)) != 0) { + if ((rv = dw->set_ramdac(dw,dw->or,1)) != 0) { if (rv == 2) warning("Failed to set VideoLUTs persistently because current System Profile can't be renamed"); else error("Failed to set VideoLUTs"); } - r->del(r); - r = NULL; - /* Fall through, as we may want to do other stuff too */ } @@ -5338,15 +5836,14 @@ main(int argc, char *argv[]) { /* Get any calibration from the provided .cal file or .profile, */ /* or calibration from the current default display profile, */ - /* and put it in r */ + /* and put it in dw->or */ if (loadfile != 0 || verify != 0 || loadprofile != 0 || installprofile == 1) { icmFile *rd_fp = NULL; icc *icco = NULL; cgats *ccg = NULL; /* calibration cgats structure */ - /* Get a calibration that's compatible with the display. */ - /* This can fail and return NULL - error if we later need it */ - r = dw->get_ramdac(dw); + if (dw->r == NULL) + error("We don't have access to the VideoLUT for loading"); /* Should we load calfile instead of installed profile if it's present ??? */ if (loadprofile) { @@ -5377,46 +5874,41 @@ main(int argc, char *argv[]) { if ((wo = (icmVideoCardGamma *)icco->read_tag(icco, icSigVideoCardGammaTag)) == NULL) { warning("No vcgt tag found in profile - assuming linear\n"); - if (r != NULL) { - for (i = 0; i < r->nent; i++) { - iv = i/(r->nent-1.0); - r->v[0][i] = iv; - r->v[1][i] = iv; - r->v[2][i] = iv; - } + for (i = 0; i < dw->r->nent; i++) { + iv = i/(dw->r->nent-1.0); + dw->r->v[0][i] = iv; + dw->r->v[1][i] = iv; + dw->r->v[2][i] = iv; } } else { - /* Hmm. Perhaps we should ignore this if the vcgt is linear ?? */ - if (r == NULL) - error("We don't have access to the VideoLUT"); - if (wo->u.table.channels == 3) { - for (i = 0; i < r->nent; i++) { - iv = i/(r->nent-1.0); - r->v[0][i] = wo->lookup(wo, 0, iv); - r->v[1][i] = wo->lookup(wo, 1, iv); - r->v[2][i] = wo->lookup(wo, 2, iv); -//printf("~1 entry %d = %f %f %f\n",i,r->v[0][i],r->v[1][i],r->v[2][i]); + for (i = 0; i < dw->r->nent; i++) { + iv = i/(dw->r->nent-1.0); + dw->r->v[0][i] = wo->lookup(wo, 0, iv); + dw->r->v[1][i] = wo->lookup(wo, 1, iv); + dw->r->v[2][i] = wo->lookup(wo, 2, iv); +//printf("~1 entry %d = %f %f %f\n",i,dw->r->v[0][i],dw->r->v[1][i],dw->r->v[2][i]); } debug("Got color vcgt calibration\n"); } else if (wo->u.table.channels == 1) { - for (i = 0; i < r->nent; i++) { - iv = i/(r->nent-1.0); - r->v[0][i] = - r->v[1][i] = - r->v[2][i] = wo->lookup(wo, 0, iv); + for (i = 0; i < dw->r->nent; i++) { + iv = i/(dw->r->nent-1.0); + dw->r->v[0][i] = + dw->r->v[1][i] = + dw->r->v[2][i] = wo->lookup(wo, 0, iv); } debug("Got monochrom vcgt calibration\n"); - } else { - r->del(r); - r = NULL; } + /* ~~~ Ideally the vcgt should have been tagged if it is TV encoded, so */ + /* that the scaling can be adjusted here if the RAMDAC depth differs from */ + /* the vcgt depth. ~~~ */ } } else { /* See if it's a .cal file */ int ncal; int ii, fi, ri, gi, bi; double cal[3][256]; + int out_tvenc = 0; /* nz to use (16-235)/255 video encoding */ icco->del(icco); /* Don't need these now */ icco = NULL; @@ -5447,6 +5939,12 @@ main(int argc, char *argv[]) { if (strcmp(ccg->t[0].kdata[fi],"DISPLAY") != 0) error("Calibration file '%s' doesn't have DEVICE_CLASS of DISPLAY",calname); + if ((fi = ccg->find_kword(ccg, 0, "TV_OUTPUT_ENCODING")) >= 0) { + if (strcmp(ccg->t[0].kdata[fi], "YES") == 0 + || strcmp(ccg->t[0].kdata[fi], "yes") == 0) + out_tvenc = 1; + } + if ((ii = ccg->find_field(ccg, 0, "RGB_I")) < 0) error("Calibration file '%s' doesn't contain field RGB_I",calname); if (ccg->t[0].ftype[ii] != r_t) @@ -5469,24 +5967,38 @@ main(int argc, char *argv[]) { cal[2][i] = *((double *)ccg->t[0].fdata[i][bi]); } - if (r == NULL) - error("We don't have access to the VideoLUT"); - /* Interpolate from cal value to RAMDAC entries */ - for (i = 0; i < r->nent; i++) { + for (i = 0; i < dw->r->nent; i++) { double val, w; unsigned int ix; - val = (ncal-1.0) * i/(r->nent-1.0); + val = (ncal-1.0) * i/(dw->r->nent-1.0); ix = (unsigned int)floor(val); /* Coordinate */ if (ix > (ncal-2)) ix = (ncal-2); w = val - (double)ix; /* weight */ for (j = 0; j < 3; j++) { val = cal[j][ix]; - r->v[j][i] = val + w * (cal[j][ix+1] - val); + dw->r->v[j][i] = val + w * (cal[j][ix+1] - val); } } + /* If the calibration was created with a restricted range video encoding, */ + /* ensure that the installed calibration applies this encoding. */ + if (out_tvenc) { + for (i = 0; i < dw->r->nent; i++) { + for (j = 0; j < 3; j++) { + dw->r->v[j][i] = (dw->r->v[j][i] * (235.0-16.0) + 16.0)/255.0; + + /* For video encoding the extra bits of precision are created by bit */ + /* shifting rather than scaling, so we need to scale the fp value to */ + /* account for this. */ + if (dw->edepth > 8) + dw->r->v[j][i] = (dw->r->v[j][i] * 255 * (1 << (dw->edepth - 8))) + /((1 << dw->edepth) - 1.0); + } + } + } + debug("Got cal file calibration\n"); } if (ccg != NULL) @@ -5502,27 +6014,35 @@ main(int argc, char *argv[]) { if (is_ok_icc == 0) error("File '%s' doesn't seem to be an ICC profile!",calname); - if (verb) - printf("About to install '%s' as display's default profile\n",calname); - if (dw->install_profile(dw, calname, r, scope)) { - error("Failed to install profile '%s'!",calname); - } - if (verb) { - printf("Installed '%s' and made it the default\n",calname); + if (dw->r== NULL) { + warning("Unable to access VideoLUT so can't install calibration"); + } else { + if (verb) + printf("About to install '%s' as display's default profile\n",calname); + if (dw->or) + dw->or->del(dw->or); + if ((dw->or = dw->r->clone(dw->r)) == NULL) + error("Failed to clone VideoLUT - memory ?"); + if (dw->install_profile(dw, calname, dw->or, scope)) + error("Failed to install profile '%s'!",calname); + if (verb) + printf("Installed '%s' and made it the default\n",calname); } /* load the LUT with the calibration from the given file or the current display profile. */ - /* (But don't load profile calibration if we're verifying against it) */ - } else if (loadfile != 0 || (loadprofile != 0 && verify == 0)) { + } else if (loadfile != 0 || loadprofile != 0) { int rv; - /* r == NULL if no VideoLUT access and ICC profile without vcgt */ - if (r == NULL) { - warning("No linear calibration loaded because there is no access to the VideoLUT"); + if (dw->or == NULL) { + warning("Calibration not loaded because there is no access to the VideoLUT"); } else { if (verb) printf("About to set display to given calibration\n"); - if ((rv = dw->set_ramdac(dw,r,1)) != 0) { + if (dw->or) + dw->or->del(dw->or); + if ((dw->or = dw->r->clone(dw->r)) == NULL) + error("Failed to clone VideoLUT - memory ?"); + if ((rv = dw->set_ramdac(dw,dw->or,1)) != 0) { if (rv == 2) error("Failed to set VideoLUTs persistently because current System Profile can't be renamed"); else @@ -5536,16 +6056,20 @@ main(int argc, char *argv[]) { if (verify != 0) { int ver = 1; double berr = 0.0; - if ((or = dw->get_ramdac(dw)) == NULL) - error("Unable to get current VideoLUT for verify"); + + if (dw->oor == NULL) + error("Unable to get original VideoLUT for verify"); - if (r == NULL) + if (dw->r == NULL) error("No calibration to verify against"); + if (dw->r->nent != dw->oor->nent) + error("VideoLUTs have different size"); + for (j = 0; j < 3; j++) { - for (i = 0; i < r->nent; i++) { + for (i = 0; i < dw->oor->nent; i++) { double err; - err = fabs(r->v[j][i] - or->v[j][i]); + err = fabs(dw->oor->v[j][i] - dw->r->v[j][i]); if (err > berr) berr = err; if (err > VERIFY_TOL) { @@ -5557,12 +6081,6 @@ main(int argc, char *argv[]) { printf("Verify: '%s' IS loaded (discrepancy %.1f%%)\n", calname, berr * 100); else printf("Verify: '%s' is NOT loaded (discrepancy %.1f%%)\n", calname, berr * 100); - or->del(or); - or = NULL; - } - if (r != NULL) { - r->del(r); - r = NULL; } /* If no other command selected, do a Window or VideoLUT test */ @@ -5637,7 +6155,9 @@ main(int argc, char *argv[]) { printf("Patch no %d",i+1); printf(" color %f %f %f\n",r,g,b); - dw->set_color(dw, r, g, b); + if (dw->set_color(dw, r, g, b) != 0) { + error ("set_color failed"); + } if (inf == 2) getchar(); @@ -5646,6 +6166,37 @@ main(int argc, char *argv[]) { } icg->del(icg); + /* Manually define patche colors */ + } else if (nmrgb > 0) { + int i; + int ri, gi, bi; + + if (inf == 2) + printf("\nHit return to advance each color\n"); + + if (inf == 2) { + printf("\nHit return to start\n"); + getchar(); + } + for (i = 0; i < nmrgb; i++) { + double r, g, b; + r = mrgb[i][0]; + g = mrgb[i][1]; + b = mrgb[i][2]; + + printf("Patch no %d",i+1); + printf(" color %f %f %f\n",r,g,b); + + if (dw->set_color(dw, r, g, b) != 0) { + error ("set_color failed"); + } + + if (inf == 2) + getchar(); + else + sleep(2); + } + /* Preset and random patch colors */ } else { @@ -5796,32 +6347,30 @@ main(int argc, char *argv[]) { if (inf != 2) { /* Test out the VideoLUT access */ - if ((dw->or = dw->get_ramdac(dw)) != NULL) { /* Use dw->or so signal will restore */ + if (dw->r != NULL) { /* Working ramdac to use */ - r = dw->or->clone(dw->or); - /* Try darkening it */ for (j = 0; j < 3; j++) { - for (i = 0; i < r->nent; i++) { - r->v[j][i] = pow(dw->or->v[j][i], 2.0); + for (i = 0; i < dw->r->nent; i++) { + dw->r->v[j][i] = pow(dw->or->v[j][i], 2.0); } } printf("Darkening screen\n"); - if (dw->set_ramdac(dw,r,0)) { - dw->set_ramdac(dw,or,0); + if (dw->set_ramdac(dw, dw->r, 0)) { + dw->set_ramdac(dw, dw->or, 0); /* is this needed ? */ error("Failed to set VideoLUTs"); } sleep(1); /* Try lightening it */ for (j = 0; j < 3; j++) { - for (i = 0; i < r->nent; i++) { - r->v[j][i] = pow(dw->or->v[j][i], 0.5); + for (i = 0; i < dw->r->nent; i++) { + dw->r->v[j][i] = pow(dw->or->v[j][i], 0.5); } } printf("Lightening screen\n"); - if (dw->set_ramdac(dw,r,0)) { - dw->set_ramdac(dw,or,0); + if (dw->set_ramdac(dw,dw->r,0)) { + dw->set_ramdac(dw,dw->or,0); /* is this needed ? */ error("Failed to set VideoLUTs"); } sleep(1); @@ -5832,10 +6381,6 @@ main(int argc, char *argv[]) { error("Failed to set VideoLUTs"); } - r->del(r); - dw->or->del(dw->or); - dw->or = NULL; - } else { printf("We don't have access to the VideoLUT\n"); } |