This is xvcolor.c in view mode; [Download] [Up]
/* * xvcolor.c - color allocation/sorting/freeing code * * Author: John Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) * * Contains: * void SortColormap() * void AllocColors() * void FreeColors() * Status xvAllocColor() * void xvFreeColors() * void ApplyEditColor(); * int MakeStdCmaps(); * void MakeBrowCmap(); * void ChangeCmapMode(); */ /* Copyright Notice * ================ * Copyright 1989, 1990, 1991, 1992, 1993 by John Bradley * * Permission to use, copy, and distribute XV in its entirety, for * non-commercial purposes, is hereby granted without fee, provided that * this license information and copyright notice appear in all copies. * * Note that distributing XV 'bundled' in with ANY product is considered * to be a 'commercial purpose'. * * Also note that any copies of XV that are distributed MUST be built * and/or configured to be in their 'unregistered copy' mode, so that it * is made obvious to the user that XV is shareware, and that they should * consider donating, or at least reading this License Info. * * The software may be modified for your own purposes, but modified * versions may NOT be distributed without prior consent of the author. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the author be held liable for any damages * arising from the use of this software. * * If you would like to do something with XV that this copyright * prohibits (such as distributing it with a commercial product, * using portions of the source in some other program, etc.), please * contact the author (preferably via email). Arrangements can * probably be worked out. * * XV is shareware for PERSONAL USE only. You may use XV for your own * amusement, and if you find it nifty, useful, generally cool, or of * some value to you, your non-deductable donation would be greatly * appreciated. $25 is the suggested donation, though, of course, * larger donations are quite welcome. Folks who donate $25 or more * can receive a Real Nice bound copy of the XV manual for no extra * charge. * * Commercial, government, and institutional users MUST register their * copies of XV, for the exceedingly REASONABLE price of just $25 per * workstation/X terminal. Site licenses are available for those who * wish to run XV on a large number of machines. Contact the author * for more details. * * The author may be contacted via: * US Mail: John Bradley * 1053 Floyd Terrace * Bryn Mawr, PA 19010 * * Phone: (215) 898-8813 * EMail: bradley@cis.upenn.edu */ #include "xv.h" #ifdef __STDC__ static void allocROColors(void); static void allocRWColors(void); static void putECfirst(void); static void diverseOrder(byte *, byte *, byte *, int, byte *); static void freeStdCmaps(void); #else static void allocROColors(); static void allocRWColors(); static void putECfirst(); static void diverseOrder(); static void freeStdCmaps(); #endif static char stdCmapSuccess[80]; /************************************************/ /* structure and routine used in SortColormap() */ /************************************************/ typedef struct thing { byte r,g,b, n; /* actual value of color + alignment */ int oldindex; /* its index in the old colormap */ int use; /* # of pixels of this color */ int mindist; /* min distance to a selected color */ } CMAPENT; /***********************************/ void SortColormap() { byte *p; int i, j, mdist, entry, d, hist[256], trans[256]; static CMAPENT c[256], c1[256], *cp, *cj, *ck; if (picType != PIC8) return; /* only 8bit images *have* a colormap */ /* init some stuff */ for (i=0; i<256; i++) { colAllocOrder[i]=i; cols[i] = infobg; } /* initialize histogram and compute it */ for (i=0; i<256; i++) hist[i]=0; for (i=pWIDE*pHIGH, p=pic; i; i--, p++) hist[*p]++; if (DEBUG>1) { fprintf(stderr,"%s: Desired colormap\n",cmd); for (i=0; i<256; i++) if (hist[i]) fprintf(stderr,"(%3d %02x,%02x,%02x %d)\n", i,rMap[i],gMap[i],bMap[i], hist[i]); fprintf(stderr,"\n\n"); } /* put the actually-used colors into the 'c' array in the order they occur also, while we're at it, calculate numcols, and close up gaps in colortable */ for (i=numcols=0; i<256; i++) { if (hist[i]) { rMap[numcols] = rMap[i]; gMap[numcols] = gMap[i]; bMap[numcols] = bMap[i]; trans[i] = numcols; cp = &c[numcols]; cp->r = rMap[i]; cp->g = gMap[i]; cp->b = bMap[i]; cp->use = hist[i]; cp->oldindex = numcols; cp->mindist = 1000000; /* 255^2 * 3 = 195075 */ numcols++; } } /* modify 'pic' to reflect new (compressed, but not reordered) colormap */ for (i=pWIDE*pHIGH, p=pic; i; i--, p++) { j = trans[*p]; *p = j; } /* do the same for cpic and epic, if they exist */ if (cpic && cpic != pic) { for (i=cWIDE*cHIGH, p=cpic; i; i--, p++) { j = trans[*p]; *p = j; } } if (epic && epic != cpic) { for (i=eWIDE*eHIGH, p=epic; i; i--, p++) { j = trans[*p]; *p = j; } } /* find most-used color, put that in c1[0] */ entry = -1; mdist = -1; for (i=0; i<numcols; i++) { if (c[i].use > mdist) { mdist = c[i].use; entry=i; } } xvbcopy((char *) &c[entry], (char *) &c1[0], sizeof(CMAPENT)); c[entry].use = 0; /* and mark it dealt with */ /* sort rest of colormap. Half of the entries are allocated on the basis of distance from already allocated colors, and half on the basis of usage. (NB: 'taxicab' distance is used throughout this file.) Mod: pick first 10 colors based on maximum distance. pick remaining colors half by distance and half by usage -- JHB To obtain O(n^2) performance, we keep each unselected color (in c[], with use>0) marked with the minimum distance to any of the selected colors (in c1[]). Each time we select a color, we can update the minimum distances in O(n) time. mod by Tom Lane Tom.Lane@g.gp.cs.cmu.edu */ for (i=1; i<numcols; i++) { int ckR, ckG, ckB; /* Get RGB of color last selected and choose selection method */ ck = &c1[i-1]; /* point to just-selected color */ ckR = ck->r; ckG = ck->g; ckB = ck->b; if (i&1 || i<10) { /* Now find the i'th most different color */ /* we want to select the unused color that has the greatest mindist */ entry = -1; mdist = -1; for (j=0, cj=c; j<numcols; j++,cj++) { if (cj->use) { /* this color has not been marked already */ /* update mindist */ d = (cj->r - ckR)*(cj->r - ckR) + (cj->g - ckG)*(cj->g - ckG) + (cj->b - ckB)*(cj->b - ckB); if (cj->mindist > d) cj->mindist = d; if (cj->mindist > mdist) { mdist = cj->mindist; entry = j; } } } } else { /* Now find the i'th most different color */ /* we want to select the unused color that has the greatest usage */ entry = -1; mdist = -1; for (j=0, cj=c; j<numcols; j++,cj++) { if (cj->use) { /* this color has not been marked already */ /* update mindist */ d = (cj->r - ckR)*(cj->r - ckR) + (cj->g - ckG)*(cj->g - ckG) + (cj->b - ckB)*(cj->b - ckB); if (cj->mindist > d) cj->mindist = d; if (cj->use > mdist) { mdist = cj->use; entry = j; } } } } /* c[entry] is the next color to put in the map. do so */ xvbcopy((char *) &c[entry], (char *) &c1[i], sizeof(CMAPENT)); c[entry].use = 0; } for (i=0; i<numcols; i++) colAllocOrder[i] = c1[i].oldindex; if (DEBUG>1) { fprintf(stderr,"%s: result of sorting colormap\n",cmd); for (i=0; i<numcols; i++) fprintf(stderr,"(%3d %02x,%02x,%02x) ",i,rMap[i],gMap[i],bMap[i]); fprintf(stderr,"\n\n"); fprintf(stderr,"%s: allocation order table\n",cmd); for (i=0; i<numcols; i++) fprintf(stderr,"colAllocOrder[%d] = -> %d\n",i,colAllocOrder[i]); fprintf(stderr,"\n"); } } #define NOPIX 0xffffffff /***********************************/ void AllocColors() { int i; nfcols = 0; if (ncols == 0) { for (i=0; i<numcols; i++) { rdisp[i] = rMap[i]; gdisp[i] = gMap[i]; bdisp[i] = bMap[i]; } SetISTR(ISTR_COLOR,"Dithering with 'black' & 'white'."); SetISTR(ISTR_COLOR2,""); rwthistime = 0; RedrawCMap(); return; } if (colorMapMode == CM_STDCMAP) { /* map desired image colors to closest standard colors */ if (theVisual->class == TrueColor || theVisual->class == DirectColor) { SetISTR(ISTR_COLOR,"Using TrueColor/DirectColor visual."); SetISTR(ISTR_COLOR2,""); } else { SetISTR(ISTR_COLOR,"Using standard %s colormap.", (haveStdCmap == STD_111 ? "2x2x2" : haveStdCmap == STD_222 ? "4x4x4" : haveStdCmap == STD_666 ? "6x6x6" : "8x8x4")); if (ncols>0) SetISTR(ISTR_COLOR2,stdCmapSuccess); else SetISTR(ISTR_COLOR2,"Dithering with 'black' & 'white'."); } rwthistime = 0; for (i=0; i<numcols; i++) { int i332; i332 = (rMap[i]&0xe0) | ((gMap[i]&0xe0)>>3) | ((bMap[i]&0xc0)>>6); cols[i] = stdcols[i332]; rdisp[i] = stdrdisp[i332]; gdisp[i] = stdgdisp[i332]; bdisp[i] = stdbdisp[i332]; } } else if (allocMode == AM_READWRITE) allocRWColors(); else allocROColors(); RedrawCMap(); } /********************************/ void FreeColors() { int i; /* frees all colors allocated by 'AllocColors()'. Doesn't touch stdcmap */ /* Note: might be called multiple times. Must not free colors once it has done so */ if (LocalCmap) { XSetWindowAttributes xswa; xswa.colormap = None; XChangeWindowAttributes(theDisp,mainW,CWColormap,&xswa); if (cmapInGam) XChangeWindowAttributes(theDisp,gamW,CWColormap,&xswa); XFreeColormap(theDisp,LocalCmap); LocalCmap = 0; nfcols = 0; } else { for (i=0; i<nfcols; i++) xvFreeColors(theDisp, theCmap, &freecols[i], 1, 0L); nfcols = 0; XFlush(theDisp); /* just to make sure they're all freed right now... */ } } /***********************************/ static void allocROColors() { int i, j, c, unique, p2alloc; Colormap cmap; XColor defs[256]; XColor ctab[256]; int dc; unique = p2alloc = 0; rwthistime = 0; /* FIRST PASS COLOR ALLOCATION: for each color in the 'desired colormap', try to get it via xvAllocColor(). If for any reason it fails, mark that pixel 'unallocated' and worry about it later. Repeat. */ /* attempt to allocate first ncols entries in colormap note: On displays with less than 8 bits per RGB gun, it's quite possible that different colors in the original picture will be mapped to the same color on the screen. X does this for you silently. However, this is not-desirable for this application, because when I say 'allocate me 32 colors' I want it to allocate 32 different colors, not 32 instances of the same 4 shades... */ for (i=0; i<256; i++) cols[i] = NOPIX; cmap = (LocalCmap) ? LocalCmap : theCmap; for (i=0; i<numcols && unique<ncols; i++) { c = colAllocOrder[i]; if (mono) { int intens = MONO(rMap[c], gMap[c], bMap[c]); defs[c].red = defs[c].green = defs[c].blue = intens<<8; } else { defs[c].red = rMap[c]<<8; defs[c].green = gMap[c]<<8; defs[c].blue = bMap[c]<<8; } defs[c].flags = DoRed | DoGreen | DoBlue; if (!(colorMapMode == CM_OWNCMAP && cmap==theCmap) && xvAllocColor(theDisp,cmap,&defs[c])) { unsigned long pixel, *fcptr; pixel = cols[c] = defs[c].pixel; rdisp[c] = defs[c].red >> 8; gdisp[c] = defs[c].green >> 8; bdisp[c] = defs[c].blue >> 8; /* see if the newly allocated color is new and different */ for (j=0, fcptr=freecols; j<nfcols && *fcptr!=pixel; j++,fcptr++); if (j==nfcols) unique++; fc2pcol[nfcols] = c; freecols[nfcols++] = pixel; } else { /* the allocation failed. If we want 'perfect' color, and we haven't already created our own colormap, we'll want to do so */ if ((colorMapMode == CM_PERFECT || colorMapMode == CM_OWNCMAP) && !LocalCmap) { LocalCmap = XCreateColormap(theDisp, vrootW, theVisual, AllocNone); if (LocalCmap) { /* succeeded, presumably */ /* free all colors that were allocated, and try again with the new colormap. This is necessary because 'XCopyColormapAndFree()' has the unpleasant side effect of freeing up the various colors I need for the control panel, etc. */ for (i=0; i<nfcols; i++) xvFreeColors(theDisp, theCmap, &freecols[i], 1, 0L); if (mainW && !useroot) XSetWindowColormap(theDisp,mainW, LocalCmap); if (mainW && !useroot && cmapInGam) XSetWindowColormap(theDisp,gamW, LocalCmap); cmap = LocalCmap; /* redo ALL allocation requests */ for (i=0; i<256; i++) cols[i] = NOPIX; nfcols = unique = 0; i = -1; } } else { /* either we don't care about perfect color, or we do care, have allocated our own colormap, and have STILL run out of colors (possible, even on an 8 bit display), just mark pixel as unallocated. We'll deal with it later */ cols[c] = NOPIX; } } } /* FIRST PASS */ if (nfcols==numcols) { if (numcols != unique) SetISTR(ISTR_COLOR,"Got all %d desired colors. (%d unique)", numcols, unique); else SetISTR(ISTR_COLOR,"Got all %d desired colors.", numcols); SetISTR(ISTR_COLOR2,""); return; } /* SECOND PASS COLOR ALLOCATION: Allocating 'exact' colors failed. Now try to allocate 'closest' colors. Read entire X colormap (or first 256 entries) in from display. for each unallocated pixel, find the closest color that actually is in the X colormap. Try to allocate that color (read only). If that fails, the THIRD PASS will deal with it */ SetISTR(ISTR_COLOR,"Got %d out of %d colors. (%d unique)", nfcols,numcols,unique); /* read entire colormap (or first 256 entries) into 'ctab' */ dc = (ncells<256) ? ncells : 256; for (i=0; i<dc; i++) ctab[i].pixel = (unsigned long) i; XQueryColors(theDisp, cmap, ctab, dc); for (i=0; i<numcols && unique<ncols; i++) { c = colAllocOrder[i]; if (cols[c]==NOPIX) { /* an unallocated pixel */ int d, mdist, close; int rd, gd, bd, ri, gi, bi; mdist = 1000000; close = -1; ri = rMap[c]; gi = gMap[c]; bi = bMap[c]; for (j=0; j<dc; j++) { rd = ri - (ctab[j].red >>8); gd = gi - (ctab[j].green>>8); bd = bi - (ctab[j].blue >>8); d = rd*rd + gd*gd + bd*bd; if (d<mdist) { mdist=d; close=j; } } if (close<0) FatalError("This Can't Happen! (How reassuring.)"); if (xvAllocColor(theDisp, cmap, &ctab[close])) { xvbcopy((char *) &ctab[close], (char *) &defs[c], sizeof(XColor)); cols[c] = ctab[close].pixel; rdisp[c] = ctab[close].red >> 8; gdisp[c] = ctab[close].green >> 8; bdisp[c] = ctab[close].blue >> 8; fc2pcol[nfcols] = c; freecols[nfcols++] = cols[c]; p2alloc++; unique++; } } } /* THIRD PASS COLOR ALLOCATION: We've alloc'ed all the colors we can. Now, we have to map any remaining unalloced pixels into either the colors that we DID get */ for (i=0; i<numcols; i++) { c = colAllocOrder[i]; if (cols[c] == NOPIX) { /* an unallocated pixel */ int d, k, mdist, close; int rd,gd,bd, ri,gi,bi; mdist = 1000000; close = -1; ri = rMap[c]; gi = gMap[c]; bi = bMap[c]; /* search the alloc'd colors */ for (j=0; j<nfcols; j++) { k = fc2pcol[j]; rd = ri - (defs[k].red >>8); gd = gi - (defs[k].green>>8); bd = bi - (defs[k].blue >>8); d = rd*rd + gd*gd + bd*bd; if (d<mdist) { mdist=d; close=k; } } if (close<0) FatalError("This Can't Happen! (How reassuring.)"); xvbcopy((char *) &defs[close], (char *) &defs[c], sizeof(XColor)); cols[c] = defs[c].pixel; rdisp[c] = defs[c].red >> 8; gdisp[c] = defs[c].green >> 8; bdisp[c] = defs[c].blue >> 8; } } /* THIRD PASS */ if (p2alloc) SetISTR(ISTR_COLOR2,"Got %d 'close' color%s.", p2alloc, (p2alloc>1) ? "s" : ""); } /***********************************/ static void allocRWColors() { int i,j,c; Colormap cmap; XColor defs[256]; rwthistime = 1; cmap = (LocalCmap) ? LocalCmap : theCmap; for (i=0; i<numcols; i++) cols[colAllocOrder[i]] = NOPIX; for (i=0; i<numcols && i<ncols; i++) { unsigned long pmr[1], pix[1]; c = colAllocOrder[i]; if (cellgroup[c]) { int n; /* this color is part of a group. see if its group's been seen already, and if so, skip this */ for (n=0; n<i && cellgroup[c] != cellgroup[colAllocOrder[n]]; n++); if (n<i) { /* found one */ cols[c] = cols[colAllocOrder[n]]; rwpc2pc[c] = colAllocOrder[n]; continue; } } if (!(colorMapMode == CM_OWNCMAP && cmap==theCmap) && XAllocColorCells(theDisp, cmap, False, pmr, 0, pix, 1)) { defs[c].pixel = cols[c] = pix[0]; if (mono) { int intens = MONO(rMap[c], gMap[c], bMap[c]); defs[c].red = defs[c].green = defs[c].blue = intens<<8; } else { defs[c].red = rMap[c]<<8; defs[c].green = gMap[c]<<8; defs[c].blue = bMap[c]<<8; } defs[c].flags = DoRed | DoGreen | DoBlue; rdisp[c] = rMap[c]; gdisp[c] = gMap[c]; bdisp[c] = bMap[c]; fc2pcol[nfcols] = c; rwpc2pc[c] = c; freecols[nfcols++] = pix[0]; } else { if ((colorMapMode == CM_PERFECT || colorMapMode == CM_OWNCMAP) && !LocalCmap) { LocalCmap = XCreateColormap(theDisp, vrootW, theVisual, AllocNone); /* free all colors that were allocated, and try again with the new colormap. This is necessary because 'XCopyColormapAndFree()' has the unpleasant side effect of freeing up the various colors I need for the control panel, etc. */ for (i=0; i<nfcols; i++) xvFreeColors(theDisp, theCmap, &freecols[i], 1, 0L); if (mainW && !useroot) XSetWindowColormap(theDisp,mainW, LocalCmap); if (mainW && !useroot && cmapInGam) XSetWindowColormap(theDisp,gamW, LocalCmap); cmap = LocalCmap; /* redo ALL allocation requests */ for (i=0; i<numcols; i++) cols[colAllocOrder[i]] = NOPIX; nfcols = 0; i = -1; } else cols[c] = NOPIX; } } /* for (i=0; ... */ if (nfcols==numcols) { SetISTR(ISTR_COLOR,"Got all %d desired colors.", numcols); SetISTR(ISTR_COLOR2,""); } else { /* Failed to allocate all colors in picture. Map remaining desired colors into closest allocated desired colors */ if (nfcols==0 && !LocalCmap) { char tstr[128], *tmp, *foo = "No r/w cells available. Using r/o color."; tmp = GetISTR(ISTR_WARNING); if (strlen(tmp)>0) sprintf(tstr, "%s %s", tmp, foo); else sprintf(tstr, "%s", foo); SetISTR(ISTR_WARNING,tstr); allocROColors(); return; } SetISTR(ISTR_COLOR,"Got %d out of %d colors.", nfcols,numcols); for (i=0; i<numcols; i++) { c = colAllocOrder[i]; if (cols[c]==NOPIX) { /* an unallocated pixel */ int k, d, mdist, close; int rd, gd, bd, ri, gi, bi; mdist = 1000000; close = -1; ri = rMap[c]; gi = gMap[c]; bi = bMap[c]; for (j=0; j<nfcols; j++) { k = fc2pcol[j]; rd = ri - (defs[k].red >>8); gd = gi - (defs[k].green>>8); bd = bi - (defs[k].blue >>8); d = rd*rd + gd*gd + bd*bd; if (d<mdist) { mdist=d; close=k; } } if (close<0) FatalError("This Can't Happen! (How reassuring.)"); xvbcopy((char *) &defs[close], (char *) &defs[c], sizeof(XColor)); cols[c] = defs[c].pixel; rdisp[c] = defs[c].red >> 8; gdisp[c] = defs[c].green >> 8; bdisp[c] = defs[c].blue >> 8; rwpc2pc[c] = close; } } } /* load up the allocated colorcells */ for (i=0; i<nfcols; i++) { j = fc2pcol[i]; defs[j].pixel = freecols[i]; if (mono) { int intens = MONO(rMap[j], gMap[j], bMap[j]); defs[j].red = defs[j].green = defs[j].blue = intens<<8; } else { defs[j].red = rMap[j]<<8; defs[j].green = gMap[j]<<8; defs[j].blue = bMap[j]<<8; } defs[j].flags = DoRed | DoGreen | DoBlue; XStoreColor(theDisp, cmap, &defs[j]); } } /*******************************************************/ /* 24/32-bit TrueColor display color 'allocation' code */ /*******************************************************/ static int highbit(ul) unsigned long ul; { /* returns position of highest set bit in 'ul' as an integer (0-31), or -1 if none */ int i; for (i=31; ((ul&0x80000000) == 0) && i>=0; i--, ul<<=1); return i; } Status xvAllocColor(dp, cm, cdef) Display *dp; Colormap cm; XColor *cdef; { if (theVisual->class == TrueColor || theVisual->class == DirectColor) { unsigned long r, g, b, rmask, gmask, bmask, origr, origg, origb; int rshift, gshift, bshift; /* shift r,g,b so that high bit of 16-bit color specification is * aligned with high bit of r,g,b-mask in visual, * AND each component with its mask, * and OR the three components together */ origr = r = cdef->red; origg = g = cdef->green; origb = b = cdef->blue; rmask = theVisual->red_mask; gmask = theVisual->green_mask; bmask = theVisual->blue_mask; rshift = 15 - highbit(rmask); gshift = 15 - highbit(gmask); bshift = 15 - highbit(bmask); /* shift the bits around */ if (rshift<0) r = r << (-rshift); else r = r >> rshift; if (gshift<0) g = g << (-gshift); else g = g >> gshift; if (bshift<0) b = b << (-bshift); else b = b >> bshift; r = r & rmask; g = g & gmask; b = b & bmask; cdef->pixel = r | g | b; /* put 'exact' colors into red,green,blue fields */ /* shift the bits BACK to where they were, now that they've been masked */ if (rshift<0) r = r >> (-rshift); else r = r << rshift; if (gshift<0) g = g >> (-gshift); else g = g << gshift; if (bshift<0) b = b >> (-bshift); else b = b << bshift; cdef->red = r; cdef->green = g; cdef->blue = b; if (DEBUG > 1) { fprintf(stderr, "xvAlloc: col=%04x,%04x,%04x -> exact=%04x,%04x,%04x\n", origr, origg, origb, cdef->red, cdef->green, cdef->blue); fprintf(stderr, " mask=%04x,%04x,%04x pix=%08x\n", rmask, gmask, bmask, cdef->pixel); } return 1; } else { return (XAllocColor(dp,cm,cdef)); } } void xvFreeColors(dp, cm,pixels, npixels, planes) Display *dp; Colormap cm; unsigned long pixels[]; int npixels; unsigned long planes; { if (theVisual->class != TrueColor && theVisual->class != DirectColor) XFreeColors(dp, cm, pixels, npixels, planes); } /********************************/ void ApplyEditColor(regroup) int regroup; { int i, j; /* if regroup is set, we *must* do a full realloc, as the cols[] array isn't correct anymore. (cell groupings changed) */ ApplyECctrls(); /* set {r,g,b}cmap[editColor] based on dial settings */ Gammify1(editColor); if (curgroup) { /* do the same to all its friends */ for (i=0; i<numcols; i++) { if (cellgroup[i] == curgroup) { rcmap[i] = rcmap[editColor]; gcmap[i] = gcmap[editColor]; bcmap[i] = bcmap[editColor]; rMap[i] = rMap[editColor]; gMap[i] = gMap[editColor]; bMap[i] = bMap[editColor]; } } } /* do something clever if we're using R/W color and this colorcell isn't shared */ if (!regroup && allocMode==AM_READWRITE && rwthistime) { /* let's try to be clever */ /* determine if the editColor cell is unique, or shared (among non-group members, that is) */ for (i=j=0; i<numcols; i++) if (rwpc2pc[i] == rwpc2pc[editColor]) j++; /* if this is a group, subtract off the non-this-one pixels from group */ if (curgroup) { for (i=0; i<numcols; i++) { if (cellgroup[i] == curgroup && i!=editColor) j--; } } if (j==1) { /* we can be way cool about this one */ XColor ctab; ctab.pixel = cols[editColor]; if (mono) { int intens = MONO(rMap[editColor], gMap[editColor], bMap[editColor]); ctab.red = ctab.green = ctab.blue = intens<<8; } else { ctab.red = rMap[editColor]<<8; ctab.green = gMap[editColor]<<8; ctab.blue = bMap[editColor]<<8; } ctab.flags = DoRed | DoGreen | DoBlue; XStoreColor(theDisp, LocalCmap ? LocalCmap : theCmap, &ctab); rdisp[editColor] = rMap[editColor]; gdisp[editColor] = gMap[editColor]; bdisp[editColor] = bMap[editColor]; return; } } /* either we aren't using R/W color, or we are, but this particular color cell isn't mapped a unique X colorcell. Either way... */ FreeColors(); putECfirst(); /* make certain this one gets alloc'd */ AllocColors(); DrawEpic(); SetCursors(-1); } /**************************************/ static void putECfirst() { /* called after all colors have been freed up, but before reallocating. moves color #editColor to first in 'colAllocOrder' list, so that it is most-likely to get its desired color */ int i; /* find it in the list */ for (i=0; i<numcols; i++) { if (editColor == colAllocOrder[i]) break; } if (i==numcols || i==0) { /* didn't find it, or it's first already */ return; } /* shift 0..i-1 down one position */ xvbcopy((char *) colAllocOrder, (char *) colAllocOrder+1, i * sizeof(colAllocOrder[0])); colAllocOrder[0] = editColor; } #define CDIST(x,y,z) ((x)*(x) + (y)*(y) + (z)*(z)) /***************************************************************/ int MakeStdCmaps() { /* produces many things: * stdr,stdg,stdb[256] - a 256-entry, desired 3/3/2 colormap * stdcols[256] - 256-entry, maps 3/3/2 colors into X pixel values * stdrdisp,stdgdisp, stdbdisp[256] * - for a given 3/3/2 color, gives the rgb values * actually displayed. Since we're not going to * successfully allocate all 256 colors (generally), * several 3/3/2 colors may be mapped into the * same X pixel (and hence, same rgb value) * stdfreecols[256] - list of colors to free on exit * stdnfcols - # of colors to free * * possibly modifies browR, browG, browB, and browcols arrays * (if !browPerfect) */ /* returns '1' if the colors were reallocated, '0' otherwise */ /* * if we're on a TrueColor/DirectColor visual, it will attempt to alloc * 256 colors * otherwise, if colorMapMode == CM_STDCMAP, it will attempt to alloc * 6*6*6 = 216 colors, or 4*4*4 = 64, or 2*2*2 = 8 colors, depending * on 'ncols' variable */ /* note: * if (ncols==0) (ie, we're either on, or emulating a b/w display), * build std*[], std*disp[], colormaps, but don't actually * allocate any colors. */ int i,j,r,g,b, desMode, screwed; XColor def; byte rmap[256],gmap[256],bmap[256],order[256]; unsigned long descols[256]; int des2got[256]; int maplen, exactCnt, nearCnt; /* generate stdr,stdg,stdb cmap. Same in all cases */ for (r=0, i=0; r<8; r++) for (g=0; g<8; g++) for (b=0; b<4; b++,i++) { stdr[i] = (r*255)/7; stdg[i] = (g*255)/7; stdb[i] = (b*255)/3; } /* determine what size cmap we should build */ if (theVisual->class == TrueColor || theVisual->class == DirectColor) desMode = STD_332; else if (colorMapMode == CM_STDCMAP) desMode = STD_666; else desMode = STD_222; /* make sure that we're not exceeding 'ncols' (ignore ncols==0) */ if (ncols > 0) { if (ncols < 64) desMode = STD_111; else if (ncols < 216) desMode = STD_222; else if (ncols < 256 && desMode == STD_332) desMode = STD_666; } if (DEBUG) fprintf(stderr,"MakeStdCmaps: have = %d, des = %d, ncols = %d\n", haveStdCmap, desMode, ncols); if (haveStdCmap != STD_NONE && haveStdCmap == desMode) { /* don't need to re-alloc the same std colormap */ return 0; } freeStdCmaps(); /* init some stuff */ screwed = 0; stdnfcols = 0; for (i=0; i<256; i++) stdcols[i] = stdfreecols[i] = 0; for (i=0; i<256; i++) des2got[i] = i; exactCnt = nearCnt = 0; if (desMode == STD_111) { /* try to alloc 8 colors */ /* generate a 1/1/1 desired colormap */ maplen = 8; for (r=0, i=0; r<2; r++) for (g=0; g<2; g++) for (b=0; b<2; b++,i++) { rmap[i] = (r*255); gmap[i] = (g*255); bmap[i] = (b*255); } } else if (desMode == STD_222) { /* try to alloc 64 colors */ /* generate a 2/2/2 desired colormap */ maplen = 64; for (r=0, i=0; r<4; r++) for (g=0; g<4; g++) for (b=0; b<4; b++,i++) { rmap[i] = (r*255)/3; gmap[i] = (g*255)/3; bmap[i] = (b*255)/3; } } else if (desMode == STD_666) { /* try to alloc 216 colors */ /* generate a 6*6*6 desired colormap */ maplen = 216; for (r=0, i=0; r<6; r++) for (g=0; g<6; g++) for (b=0; b<6; b++,i++) { rmap[i] = (r*255)/5; gmap[i] = (g*255)/5; bmap[i] = (b*255)/5; } } else { /* desMode == STD_332 */ maplen = 256; for (i=0; i<maplen; i++) { rmap[i] = stdr[i]; gmap[i] = stdg[i]; bmap[i] = stdb[i]; } } /* sort the colors according to the diversity algorithm... */ diverseOrder(rmap,gmap,bmap,maplen,order); if (ncols!=0) { XColor ctab[256]; long d, mind; int dc,num,j,rd,gd,bd; /* try to allocate the desired (rmap,gmap,bmap) colormap */ for (i=0; i<maplen; i++) { def.red = rmap[order[i]] << 8; def.green = gmap[order[i]] << 8; def.blue = bmap[order[i]] << 8; def.flags = DoRed | DoGreen | DoBlue; if (xvAllocColor(theDisp, theCmap, &def)) { /* success */ des2got[order[i]] = order[i]; descols[order[i]] = def.pixel; if (DEBUG>1) fprintf(stderr,"Phase 1: Alloc %x,%x,%x succeeded!\n", rmap[order[i]], gmap[order[i]], bmap[order[i]]); /* see if the newly allocated color is new and different */ for (j=0; j<stdnfcols && stdfreecols[j]!=def.pixel; j++); if (j==stdnfcols) exactCnt++; stdfreecols[stdnfcols++] = def.pixel; } else descols[order[i]] = NOPIX; } /* PHASE 2: find 'close' colors in colormap, try to alloc those */ /* read entire colormap (or first 256 entries) into 'ctab' */ dc = (ncells<256) ? ncells : 256; for (i=0; i<dc; i++) ctab[i].pixel = (unsigned long) i; XQueryColors(theDisp, theCmap, ctab, dc); for (i=0; i<maplen; i++) { if (descols[i] == NOPIX) { /* find closest color in colormap, and try to alloc it */ mind = 1000000; /* greater than 3 * (256^2) */ for (j=0,num = -1; j<dc; j++) { rd = rmap[i] - (ctab[j].red >>8); gd = gmap[i] - (ctab[j].green>>8); bd = bmap[i] - (ctab[j].blue >>8); d = CDIST(rd, gd, bd); if (d<mind) { mind = d; num = j; } } if (DEBUG>1) fprintf(stderr,"Phase 2: closest to %x,%x,%x is %x,%x,%x (%d)", rmap[i],gmap[i],bmap[i],ctab[num].red>>8,ctab[num].green>>8, ctab[num].blue>>8, num); if (num < 0) { screwed = 1; if (DEBUG>1) fprintf(stderr," failed to alloc.\n"); } else if (xvAllocColor(theDisp, theCmap, &ctab[num])) { /* success */ des2got[i] = i; descols[i] = ctab[num].pixel; nearCnt++; if (DEBUG>1) fprintf(stderr, " got it!\n"); /* see if the newly allocated color is new and different */ for (j=0; j<stdnfcols && stdfreecols[j]!=def.pixel; j++); stdfreecols[stdnfcols++] = def.pixel; } } } /* PHASE 3: map remaining unallocated colors into closest we got */ for (i=0; i<maplen; i++) { if (descols[i] == NOPIX) { /* find closest alloc'd color */ mind = 1000000; /* greater than 3 * (256^2) */ for (j=0,num=0; j<maplen; j++) { if (descols[j] != NOPIX) { d = CDIST(rmap[i]-rmap[j], gmap[i]-gmap[j], bmap[i]-bmap[j]); if (d<mind) { mind = d; num = j; } } } if (DEBUG>1) fprintf(stderr,"Phase 3: closest to %x,%x,%x is %x,%x,%x\n", rmap[i],gmap[i],bmap[i], rmap[num], gmap[num], bmap[num]); if (descols[num] == NOPIX) screwed = 1; else { descols[i] = descols[num]; des2got[i] = num; } } } } /* at this point, we have 'descols', a maplen long array of X pixel values that maps 1/1/1, 2/2/2, 6*6*6, or 3/3/2 values into an X pixel value */ /* build stdcols and stdrdisp,stdgdisp,stdbdisp colormap */ if (desMode == STD_111) { for (r=0; r<8; r++) for (g=0; g<8; g++) for (b=0; b<4; b++) { int i332, i111; i332 = (r<<5) | (g<<2) | b; i111 = (r&0x04) | ((g&0x04)>>1) | (b>>1); stdrdisp[i332] = rmap[des2got[i111]]; stdgdisp[i332] = gmap[des2got[i111]]; stdbdisp[i332] = bmap[des2got[i111]]; stdcols[i332] = descols[des2got[i111]]; } } else if (desMode == STD_222) { for (r=0; r<8; r++) for (g=0; g<8; g++) for (b=0; b<4; b++) { int i332, i222; i332 = (r<<5) | (g<<2) | b; i222 = ((r&0x06)<<3) | ((g&0x06)<<1) | b; stdrdisp[i332] = rmap[des2got[i222]]; stdgdisp[i332] = gmap[des2got[i222]]; stdbdisp[i332] = bmap[des2got[i222]]; stdcols[i332] = descols[des2got[i222]]; } } else if (desMode == STD_666) { for (r=0,i=0; r<8; r++) for (g=0; g<8; g++) for (b=0; b<4; b++,i++) { int r6,g6,b6,i666; r6 = (((r*10) + 7) / 14); /* r6 = round(r*5 / 7) */ g6 = (((g*10) + 7) / 14); /* g6 = round(g*5 / 7) */ b6 = (((b*10) + 3) / 6); /* b6 = round(b*5 / 3) */ i666 = (36 * r6) + (6 * g6) + b6; stdrdisp[i] = rmap[des2got[i666]]; stdgdisp[i] = gmap[des2got[i666]]; stdbdisp[i] = bmap[des2got[i666]]; stdcols[i] = descols[des2got[i666]]; } } else { /* desMode == STD_332 */ for (i=0; i<256; i++) { stdrdisp[i] = rmap[des2got[i]]; stdgdisp[i] = gmap[des2got[i]]; stdbdisp[i] = bmap[des2got[i]]; stdcols[i] = descols[des2got[i]]; } } if (!browPerfect) { /* we've changed the colors the browser icons used */ for (i=0; i<256; i++) { browR[i] = stdr[i]; browG[i] = stdg[i]; browB[i] = stdb[i]; browcols[i] = stdcols[i]; } } haveStdCmap = desMode; if (DEBUG > 1) { fprintf(stderr,"MakeStdCmaps: ncols=%d maplen=%d\n", ncols, maplen); fprintf(stderr," std*[]= "); for (i=0; i<256; i++) fprintf(stderr,"%02x,%02x,%02x ",stdr[i],stdg[i],stdb[i]); fprintf(stderr,"\n\n"); fprintf(stderr," disp[]= "); for (i=0; i<256; i++) fprintf(stderr,"%02x,%02x,%02x ",stdrdisp[i],stdgdisp[i],stdbdisp[i]); fprintf(stderr,"\n\n"); fprintf(stderr," stdcols[]= "); for (i=0; i<256; i++) fprintf(stderr,"%02x ",stdcols[i]); fprintf(stderr,"\n\n"); fprintf(stderr," stdfreecols[%d] = ", stdnfcols); for (i=0; i<stdnfcols; i++) fprintf(stderr,"%02x ",stdfreecols[i]); fprintf(stderr,"\n\n"); } if (exactCnt == maplen) sprintf(stdCmapSuccess, "Got all %d colors.", exactCnt); else { if (nearCnt>0) sprintf(stdCmapSuccess, "Got %d out of %d colors. (%d close color%s)", exactCnt, maplen, nearCnt, (nearCnt>1) ? "s" : ""); else sprintf(stdCmapSuccess, "Got %d out of %d colors.", exactCnt, maplen); } if (screwed) FatalError("something nasty happened in makeStdCmap()\n"); if (ncols==0) return 0; /* as no colors were actually alloc'd */ return 1; } /***************************************************************/ void MakeBrowCmap() { /* This function should only be called once, at the start of the program. * * produces many things: * browR,browG,browB[256] * - a 3/3/2 colormap used by genIcon * browcols[256] - maps 3/3/2 values into X colors * browCmap - local cmap used in browse window, if browPerfect */ int i,j,r,g,b, screwed, num, exactCnt, nearCnt; XColor def; byte rmap[256],gmap[256],bmap[256],order[256]; u_long descols[256]; int des2got[256]; long d, mind; if (DEBUG) fprintf(stderr,"MakeBrowCmap: perfect = %d, ncols = %d\n", browPerfect, ncols); if (ncols == 0) browPerfect = 0; if (!browPerfect) { /* sharing the 'std' cmaps */ MakeStdCmaps(); return; } for (r=0, i=0; r<8; r++) for (g=0; g<8; g++) for (b=0; b<4; b++,i++) { rmap[i] = browR[i] = (r*255)/7; gmap[i] = browG[i] = (g*255)/7; bmap[i] = browB[i] = (b*255)/3; browcols[i] = 0; } screwed = exactCnt = nearCnt = 0; for (i=0; i<256; i++) des2got[i] = i; diverseOrder(rmap,gmap,bmap,256,order); browCmap = XCreateColormap(theDisp, rootW, theVisual, AllocNone); if (!browCmap) { fprintf(stderr,"Couldn't create private colormap for browser!\n"); browPerfect = 0; MakeStdCmaps(); return; } /* try to allocate the desired (rmap,gmap,bmap) colormap */ for (i=0; i<256; i++) { def.red = rmap[order[i]] << 8; def.green = gmap[order[i]] << 8; def.blue = bmap[order[i]] << 8; def.flags = DoRed | DoGreen | DoBlue; if (xvAllocColor(theDisp, browCmap, &def)) { /* success */ des2got[order[i]] = order[i]; descols[order[i]] = def.pixel; if (DEBUG>1) fprintf(stderr,"makebrowcmap: Phase 1: Alloc %x,%x,%x succeeded!\n", rmap[order[i]], gmap[order[i]], bmap[order[i]]); } else descols[order[i]] = NOPIX; } /* PHASE 2: map remaining unallocated colors into closest we got */ for (i=0; i<256; i++) { if (descols[i] == NOPIX) { /* find closest alloc'd color */ mind = 1000000; /* greater than 3 * (256^2) */ for (j=0,num=0; j<256; j++) { if (descols[j] != NOPIX) { d = CDIST(rmap[i]-rmap[j], gmap[i]-gmap[j], bmap[i]-bmap[j]); if (d<mind) { mind = d; num = j; } } } if (DEBUG>1) fprintf(stderr,"makebrowcmap: closest to %x,%x,%x = %x,%x,%x\n", rmap[i],gmap[i],bmap[i], rmap[num], gmap[num], bmap[num]); if (descols[num] == NOPIX) screwed = 1; else { descols[i] = descols[num]; des2got[i] = num; } } } for (i=0; i<256; i++) { browcols[i] = descols[des2got[i]]; } if (screwed) FatalError("something nasty happened in makeStdCmap()\n"); } /************************************/ static void diverseOrder(rmap,gmap,bmap,maplen,order) byte *rmap, *gmap, *bmap, *order; int maplen; { /* takes a colormap (maxlen 256) and produces an order array that contains the most-diverse order for allocating these colors */ int dist[256], i, pick, maxv, ocnt, d; /* arbitrarily pick the brightest color first */ pick = 0; maxv = 0; for (i=0; i<maplen; i++) { if (CDIST(rmap[i],gmap[i],bmap[i]) > maxv) { maxv = CDIST(rmap[i],gmap[i],bmap[i]); pick = i; } } ocnt = 0; order[ocnt++] = pick; /* init dist[] array */ for (i=0; i<maplen; i++) dist[i] = 1000000; while (ocnt < maplen) { /* update distances */ for (i=0; i<maplen; i++) { d = CDIST(rmap[pick]-rmap[i], gmap[pick]-gmap[i], bmap[pick]-bmap[i]); if (dist[i] > d) dist[i] = d; } /* pick greatest distance */ for (i=0, maxv=0, pick=0; i<maplen; i++) { if (dist[i] > maxv) { maxv = dist[i]; pick = i; } } order[ocnt++] = pick; } } /***************************************************************/ static void freeStdCmaps() { int i; if (DEBUG) fprintf(stderr,"freeStdCmaps: haveStdCmap = %d\n", haveStdCmap); if (haveStdCmap == STD_NONE) return; for (i=0; i<stdnfcols; i++) xvFreeColors(theDisp, theCmap, &stdfreecols[i], 1, 0L); stdnfcols = 0; haveStdCmap = STD_NONE; } /***************************************************************/ void ChangeCmapMode(cmode, genepic, freeKludge) int cmode, genepic, freeKludge; { /* note: MAY BE CALLED before there is an image or anything */ /* called whenever colormap allocation methods change (by selecting a colormap mode from the dispMB button, or by going into/outof a root display mode when the cmapmode is 'CM_PERFECT' or 'CM_OWNCMAP' Also called whenever a new pic is loaded and color realloc may need to be done. if !genepic, just do the color alloc/realloc. Don't generate or draw epic. As a general rule, frees, and reallocates colors using new strategy if cmode==CM_STDCMAP, frees 2/2/2 colormap that the icons may be using (if it exists), and allocs a bigger stdcmap (6x6x6 or 3/3/2) if cmode==CM_NORMAL, CM_PERFECT, or CM_OWNCMAP, frees all regular allocated colors (doesn't touch stdcmap), and reallocates using new method */ int i, iconCmapSize, oldmode; /* don't let it go into PERFECT or OWNCMAP mode if using root window */ if (useroot && (cmode == CM_PERFECT || cmode == CM_OWNCMAP)) cmode = CM_NORMAL; /* free all normal allocated colors, if any */ if ((pic && freeKludge==1 && noFreeCols==0) || (pic && freeKludge==0)) FreeColors(); oldmode = colorMapMode; colorMapMode = cmode; iconCmapSize = STD_222; if (ncols > 0 && ncols < 64) iconCmapSize = STD_111; if (cmode == CM_STDCMAP) { if (MakeStdCmaps() && anyBrowUp && !browPerfect) RegenBrowseIcons(); /* redraw icons */ } else if (cmode == CM_NORMAL) { if (novbrowse || browPerfect || haveStdCmap != iconCmapSize) freeStdCmaps(); /* if using browser, and killed stdcmap, make icon stdcmap */ if (!novbrowse && !browPerfect && haveStdCmap == STD_NONE) { if (MakeStdCmaps() && anyBrowUp) RegenBrowseIcons(); } } else if (cmode == CM_PERFECT) { } else if (cmode == CM_OWNCMAP) { } /* disable rwcolor if STDCMAP mode */ dispMB.dim[DMB_COLRW] = (cmode==CM_STDCMAP) ? 1 : 0; allocMode = (!dispMB.dim[DMB_COLRW] && dispMB.flags[DMB_COLRW]) ? AM_READWRITE : AM_READONLY; /* move checkmark to current selection */ for (i=DMB_COLNORM; i<=DMB_COLSTDC; i++) dispMB.flags[i] = 0; dispMB.flags[ cmode + (DMB_COLNORM - CM_NORMAL)] = 1; if (!pic) return; /* no pic, so don't alloc any colors or anything */ AllocColors(); if (cmode == CM_STDCMAP && epicMode == EM_RAW) { /* turn on dithering */ epicMode = EM_DITH; SetEpicMode(); if (genepic) GenerateEpic(eWIDE, eHIGH); } else { if (oldmode == CM_STDCMAP && cmode != CM_STDCMAP && epicMode != EM_RAW) { /* just left STDCMAP mode. Switch to using 'RAW' */ epicMode = EM_RAW; SetEpicMode(); if (genepic) GenerateEpic(eWIDE, eHIGH); } } if (genepic) DrawEpic(); SetCursors(-1); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.