home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / sys / mac / programm / 14944 < prev    next >
Encoding:
Text File  |  1992-09-03  |  26.0 KB  |  832 lines

  1. Newsgroups: comp.sys.mac.programmer
  2. Path: sparky!uunet!gumby!kzoo!k044477
  3. From: k044477@hobbes.kzoo.edu (Jamie R. McCarthy)
  4. Subject: ColorTables:  misc. tidbits and code
  5. Message-ID: <1992Sep3.163209.14917@hobbes.kzoo.edu>
  6. Organization: Kalamazoo College
  7. Date: Thu, 3 Sep 1992 16:32:09 GMT
  8. Lines: 822
  9.  
  10. This article starts off with some AppleLink mail from Forrest Tanaka,
  11. telling you more than you ever wanted to know about color tables, and
  12. ends with a fair amount of ANSI C source code for manipulating them.
  13. This is, in short, a hodgepodge of answers and solutions.  If you're
  14. a little confused about color graphics on the Mac (who isn't?!),
  15. well, who knows--this may clear up something for you.
  16.  
  17. Please note that the code is copyrighted and may not be used directly
  18. in a for-profit program.  That's 'cause my company paid me to write
  19. it and it's not fair for you to steal it.  Sorry.  If you want to use
  20. it, fix a bug for me.  (There must be at least one.)  Otherwise,
  21. you're free to use it directly in private or free programs, and to
  22. write your own code based on mine.
  23.  
  24. Comments, especially on the source code, are heartily welcomed.
  25.  
  26.  Jamie McCarthy      Internet: k044477@kzoo.edu      AppleLink: j.mccarthy
  27.  
  28.  
  29.  
  30.  
  31.  
  32. Item    5744291                        10-July-92        16:02PDT
  33. From:   DEVSUPPORTJ                    Developer Support Center
  34. To:     J.MCCARTHYJ                    MCE-Lawrence Prod, J McCarthy
  35. Sub:    RE-ColorTable code-level Q's
  36.  
  37. July 10, 1992
  38.  
  39. Hi Jamie
  40.  
  41. I received your questions about color tables, and since there are
  42. several questions, IUll put the text of your question in a narrow
  43. paragraph and respond to each one in the normal paragraphs
  44. afterwards.  
  45.  
  46.         When drawing offscreen, every CQD routine will handle both
  47.         indexed and sequential tables properly;  in addition,
  48.         CopyBits will handle a "paletted" table for its source
  49.         PixMap.  Right?
  50.  
  51. Yes.  If the source PixMap to CopyBits is paletted and the destination
  52. is a window with a palette, then CopyBits uses the value field of
  53. each ColorSpec record as a palette index for each pixel, which is of
  54. course the whole idea behind paletted color tables.  If the
  55. destination is anything else, then bit 14 of ctFlags is ignored and
  56. the color table is treated as either indexed or sequential depending
  57. on the value of bit 15.  Bit 14 is also ignored when its color
  58. tableUs PixMap is used as the destination to CopyBits.  
  59.  
  60.         IM V-136, contradictorially, says "The ctFlags field is
  61.         significant for gDevices only," and "Color tables that are
  62.         part of [non-GDevice] pixMaps have this bit clear."  What
  63.         this means is, "Clear your own ctFlags, use indexed tables,
  64.         and don't mess with a GDevice's ctFlags."  Right?
  65.  
  66. Inside Macintosh is simply wrong on this point, though it was the
  67. popular belief for quite a while.  The truth is that QuickDraw reads
  68. ctFlags regardless of whether the color tableUs PixMap belongs to a
  69. GDevice, a GWorld, or is one that you created.  If you create your
  70. own PixMap and want to use a sequential color table, set this bit. 
  71. If you want an indexed color table, clear this bit. QuickDraw will
  72. read this bit and do the right thing.  I personally always make
  73. indexed color tables because I happen to be more comfortable with
  74. them, but you can make a sequential one if you want, and QuickDraw
  75. will have no problems with it.
  76.  
  77. ItUs true that all screen GDevices have color tables which are
  78. sequential, but that doesnUt mean that all GDevices must have
  79. sequential color tables.  If you make a GDevice yourself, say for
  80. off-screen drawing, then you can have either a sequential or indexed
  81. color table.  QuickDraw will read the most significant bit of ctFlags
  82. and act accordingly.
  83.  
  84.  
  85.         I understand that video drivers expect sequential tables.
  86.         But, is there anything at all wrong with mixing and
  87.         matching sequential and indexed tables when they don't
  88.         belong to GDevices?
  89.  
  90. No, no problem at all.  In fact, as I mentioned, you can mix and match
  91. even if a color table belongs to a GDevice.  The only thing you canUt
  92. do is change the color table of a screenUs GDevice directly.
  93.  
  94.  
  95.         Must an indexed table be sorted?
  96.  
  97. No.  You can arrange an indexed color table in random order if you
  98. want to, as long as you set the value field of each the color
  99. specification records to the pixel value that you want for each
  100. color.  For example, these two four-entry color tables are identical
  101. assuming ctFlags is zero:
  102.  
  103. Entry  value rgb          Entry  value rgb
  104. 0      0     white        0      2     mauve
  105. 1      1     green        1      1     green
  106. 2      2     mauve        2      3     black
  107. 3      3     black        3      0     white
  108.  
  109.  
  110.         IM V-138 says "...when the Color2Index routine is called,
  111.         it can find the best match to the full 48-bit resolution
  112.         available in a colorSpec."  Really?  Even if the inverse
  113.         table's only 3 bits and my color table consists of (for
  114.         example) a 256-shade gradation from black to white?
  115.         Doesn't that require appending a whole lot of information
  116.         after the inverse table?
  117.  
  118. Inside Macintosh is right, and so are you.  If you have two colors
  119. that differ only in the least significant bit of each of their
  120. components, Color2Index will be able to tell the difference.  The
  121. inverse-table look-up is just the first step; itUs a coarse
  122. estimation of what the final pixel value is. At the end of the
  123. inverse table is a linked list of colors, called the hidden-color
  124. list.  After the inverse-table look-up is complete, this coarse
  125. estimate is fine-tuned using the hidden-color list so that
  126. Color2Index can tell the difference between two colors down to the
  127. last bit.  Color QuickDraw doesnUt always use the hidden-color list
  128. though.  If you use the arithmetic modes with CopyBits, only the
  129. coarse, inverse-table look-up is done.  If you use dithering with
  130. CopyBits, the hidden-color list isnUt used either.
  131.  
  132.  
  133.         What will happen if you ask CQD to manipulate a table whose
  134.         top two ctFlags bits are 11?  (Just out of curiosity.)
  135.  
  136. IUve done this, and itUs fine.  If the PixMap that owns this color
  137. table is used as a source to CopyBits and if the destination is a
  138. window with a palette, this PixMap is treated as a paletted color
  139. table.  If the destination is a window without a palette or a PixMap
  140. thatUs not associated with a window at all, the color table is
  141. treated as a sequential color table.  If the PixMap is used as the
  142. destination to CopyBits, then itUs just treated as a sequential color
  143. table.  Think of it this way: bit 14 has an effect if the color table
  144. belongs to the source PixMap to CopyBits and a window with a palette
  145. is the destination.  In any other case, bit 14 is ignored and bit 15
  146. tells QuickDraw what to do.
  147.  
  148.  
  149. Well, I hope that covers everything.  If you need any clarifications
  150. or any more help, just let me know!
  151.  
  152. -- Forrest
  153.  
  154.  
  155.  
  156.  
  157.  
  158. /*
  159.  * ColorTableUtils.h
  160.  *
  161.  * Copyright ) 1992 by Lawrence Productions, Inc.  All Rights Reserved.
  162.  *
  163.  * This code may not be used in any for-profit program without the
  164.  * written permission of Lawrence Productions, Inc.
  165.  *
  166.  */
  167.  
  168.  
  169.  
  170. /********************************/
  171.  
  172. #pragma once
  173.  
  174. /********************************/
  175.  
  176.    /*
  177.     * Note that the values of these three constants happen to equal the
  178.     * top two bits of the ctFlags field.  The macro for getColorTableMode()
  179.     * is the only thing that depends on this fact.
  180.     */
  181.    
  182. enum {
  183.    kIndexedColorTable = 0x00,
  184.    kPalettedColorTable = 0x01,
  185.    kSequentialColorTable = 0x02
  186. } ;
  187.  
  188. /********************************/
  189.  
  190.    /*
  191.     * Determine the mode of a color table.
  192.     */
  193.    
  194. short getColorTableMode(CTabPtr theTablePtr);
  195. #define getColorTableMode(theTablePtr) ( \
  196.    ((unsigned short)(theTablePtr)->ctFlags) >> 14 )
  197. #define colorTableIsIndexed(theTablePtr) \
  198.    (getColorTableMode(theTablePtr) == kIndexedColorTable)
  199. #define colorTableIsPaletted(theTablePtr) \
  200.    (getColorTableMode(theTablePtr) == kPalettedColorTable)
  201. #define colorTableIsSequential(theTablePtr) \
  202.    (getColorTableMode(theTablePtr) == kSequentialColorTable)
  203.  
  204.  
  205.    /*
  206.     * Determine whether a color table is sorted.  Naturally, it's designed
  207.     * for indexed tables, but works (trivially) with sequential and
  208.     * paletted tables too.
  209.     */
  210.    
  211. Boolean colorTableIsSorted(CTabPtr theTable);
  212. #define assertColorTableIsSorted(theTable) ASSERT(colorTableIsSorted(theTable));
  213.  
  214.  
  215.    /*
  216.     * Find an entry's offset in a color table.  Naturally, it's designed
  217.     * for indexed tables, but works (trivially) on sequential tables too.
  218.     * Paletted tables may have none or several entries that point to a
  219.     * particular palette entry, and this routine will not attempt to
  220.     * search them, returning kNoEntryFound instead.
  221.     * 
  222.     * For all tables, the output is the index into the ctTable field.
  223.     */
  224.    
  225. #define kNoEntryFound (-1)
  226. short findEntryInSortedTable(CTabPtr theTable, short wEntry);
  227.  
  228.  
  229.    /*
  230.     * Add an entry to a color table.  Returns TRUE if the entry was
  231.     * actually added (i.e. if the table's size went up), and FALSE if
  232.     * the entry wasn't added.  The entry number is of course in the
  233.     * value field.
  234.     *
  235.     * For this one, the table really _does_ have to be indexed.
  236.     */
  237.    
  238. Boolean addEntryToSortedIndexedTable(CTabHandle theIndexedTable, ColorSpec *theColorSpec);
  239.  
  240.  
  241.    /*
  242.     * Convert color storage modes back 'n' forth.  These allocate memory
  243.     * and don't alter the original table.
  244.     *
  245.     * You can't switch to or from a paletted color table without knowing
  246.     * what the palette is, so no routines are provided for that.
  247.     */
  248.    
  249. CTabHandle makeIndexedFromSequential(CTabHandle theSequentialTable);
  250. CTabHandle makeSequentialFromSortedIndexed(CTabHandle theIndexedTable, RGBColor *fillColor);
  251.  
  252.  
  253.    /*
  254.     * Sort a table's entries.  Does nothing if the table is sequential
  255.     * or paletted.
  256.     */
  257.    
  258. void sortIndexedTable(CTabHandle theTable);
  259.  
  260.  
  261.    /*
  262.     * Compare two tables, even if they use different modes.  Paletted
  263.     * tables can't be compared to anything, and so always return FALSE.
  264.     * If both tables are indexed, they must both be sorted.
  265.     */
  266.    
  267. Boolean colorTablesAreEquivalent(CTabHandle table1, CTabHandle table2, RGBColor *fillColor);
  268.  
  269.  
  270.    /*
  271.     * Functions to manipulate the number of entries of a color table.
  272.     * The "get" function is simple, so it comes with a macro.  The
  273.     * others alter the ctSize field and resize the handle;  "set"
  274.     * sets the number of entries directly, while "change" adds or
  275.     * subtracts from the current number of entries.  If the table's
  276.     * size changes, it (and memory) may be moved, even if it's
  277.     * locked.  If the table is lengthened, the new entries contain
  278.     * garbage.
  279.     */
  280.    
  281. short getNCTEntries(CTabPtr theTable);
  282. #define getNCTEntries(theTable) ((theTable)->ctSize+1)
  283. void setNCTEntries(CTabHandle theTable, short nEntries);
  284. void changeNCTEntries(CTabHandle theTable, short nEntries);
  285.  
  286.  
  287.    /*
  288.     * Quickie macros to convert a table's depth to the maximum number of
  289.     * entries, and to convert a number of entries to a minimum depth.
  290.     *
  291.     * The function prototypes are only there to do a little parameter
  292.     * type-checking;  since the macros do their conversion in place,
  293.     * the functions wouldn't actually do anything if you were to call
  294.     * them.  (And if they took pointers instead of doing the work in
  295.     * place, you couldn't use register variables, ick.)  If the functions
  296.     * ever actually _do_ get called, they pop into the debugger with
  297.     * an appropriate message.
  298.     */
  299.  
  300. void convertDepthToMaxNEntries(short d, short n);  
  301. #define convertDepthToMaxNEntries(d,n)                \
  302.    {  short ___depth=(d);                             \
  303.       for ((n)=1; ___depth>=1; --___depth)            \
  304.          (n) <<= 1;                    }
  305.  
  306. void convertNEntriesToMinDepth(short n, short d);
  307. #define convertNEntriesToMinDepth(n,d)                \
  308.    {  short ___nEntries=(n);                          \
  309.       for ((d)=0; ___nEntries>1; ___nEntries>>=1)     \
  310.          ++(d);                        }
  311.  
  312.  
  313.  
  314.  
  315.  
  316. /*
  317.  * ColorTableUtils.c
  318.  *
  319.  * Copyright ) 1992 by Lawrence Productions, Inc.  All Rights Reserved.
  320.  *
  321.  * This code may not be used in any for-profit program without the
  322.  * written permission of Lawrence Productions, Inc.
  323.  *
  324.  */
  325.  
  326.  
  327.  
  328. /********************************/
  329.  
  330. #include "ColorTableUtils.h"
  331.  
  332. /********************************/
  333.  
  334.    /*
  335.     * You'll need to put qsort.c in your project.
  336.     */
  337. #include <stdlib.h>
  338.  
  339. /********************************/
  340.  
  341.    /*
  342.     * The only thing used from JamieUtilities.c is "jmemcmp()," which is
  343.     * just a clone of memcmp().  (I save 100 bytes of object code by
  344.     * taking it out of mem.c, because memcmp() is the only function in
  345.     * there that I would ever use.  I guess I'm a fanatic.)  Anyway, if
  346.     * you don't have JamieUtilities, just #include <string.h>, drop mem.c
  347.     * in your project, and change "jmemcmp" to "memcmp".
  348.     */
  349. #include <JamieUtilities.h>
  350.  
  351. /********************************/
  352.  
  353.  
  354.  
  355. short (getColorTableMode)(CTabPtr theTable)
  356. {
  357.    ASSERT(theTable != NULL);
  358.    
  359.    switch ( ((unsigned short) (theTable)->ctFlags) >> 14 ) {
  360.       
  361.       case 0x00:     return kIndexedColorTable;       break;
  362.       case 0x01:     return kPalettedColorTable;      break;
  363.       case 0x02:     return kSequentialColorTable;    break;
  364.       default:       ASSERT(0);     return -1;        break;
  365.       
  366.    }
  367. }
  368.  
  369.  
  370.  
  371. Boolean colorTableIsSorted(CTabPtr theTable)
  372. {
  373.    register ColorSpec *theCTTable;
  374.    register short cPosition, nEntriesMinusOne, lastEntry;
  375.    
  376.    ASSERT(theTable != NULL);
  377.    
  378.    if (!colorTableIsIndexed(theTable)) {
  379.       return TRUE;
  380.    }
  381.    
  382.    theCTTable = &theTable->ctTable[0];
  383.    nEntriesMinusOne = theTable->ctSize;
  384.    cPosition = 0;
  385.    lastEntry = -1;
  386.    for (cPosition = 0; cPosition <= nEntriesMinusOne; ++cPosition) {
  387.       
  388.       register short thisEntry;
  389.       
  390.       if ( (thisEntry=theCTTable[cPosition].value) < lastEntry) {
  391.          return FALSE;
  392.       } else {
  393.          lastEntry = thisEntry;
  394.       }
  395.       
  396.    }
  397.    
  398.    return TRUE;
  399. }
  400.  
  401.  
  402.  
  403.       /*
  404.        * Returns TRUE iff the entry was found.  If the entry was not found,
  405.        * it returns the position where it should be inserted.
  406.        */
  407.       
  408.    Boolean engineFindEntryInSortedTable(CTabPtr tablePtr, register short wEntry, short *thePosition);
  409.    Boolean engineFindEntryInSortedTable(CTabPtr tablePtr, register short wEntry, short *thePosition)
  410.    {
  411.          /*
  412.           * I'm 99.9% sure this code will find an existing entry, but only
  413.           * 80% sure that it'll return the proper position for a nonexisting
  414.           * one, the proper position being the entry to the right.  I suppose
  415.           * I should test it rigorously one of these days...
  416.           */
  417.          
  418.       register unsigned short cPosition;
  419.       register unsigned short leftBounds, rightBounds;
  420.       register ColorSpec *theCSpecArray;
  421.       
  422.       if (!colorTableIsIndexed(tablePtr)) {
  423.          if (wEntry <= tablePtr->ctSize) {
  424.             *thePosition = wEntry;
  425.             return TRUE;
  426.          } else {
  427.             *thePosition = tablePtr->ctSize;
  428.             return FALSE;
  429.          }
  430.       }
  431.       
  432.       assertColorTableIsSorted(tablePtr);
  433.       
  434.       theCSpecArray = tablePtr->ctTable;
  435.       leftBounds = 0;  rightBounds = tablePtr->ctSize + 1 - 1;
  436.       cPosition = rightBounds / 2;
  437.       
  438.       while (leftBounds < rightBounds) {
  439.          
  440.          if (theCSpecArray[cPosition].value == wEntry) {
  441.             *thePosition = cPosition;
  442.             return TRUE;
  443.          } else {
  444.             if (theCSpecArray[cPosition].value < wEntry) {
  445.                leftBounds = cPosition;
  446.                cPosition += rightBounds + 1;
  447.                cPosition >>= 1;
  448.             } else {
  449.                rightBounds = cPosition;
  450.                cPosition += leftBounds;
  451.                cPosition >>= 1;
  452.             }
  453.          }
  454.          
  455.       }
  456.       
  457.          /*
  458.           * If the requested entry is not there, we want the entry that's
  459.           * right _after_ where it would go.
  460.           */
  461.          
  462.       if (theCSpecArray[cPosition].value < wEntry) {
  463.          ++cPosition;
  464.       }
  465.       
  466.       ASSERT(cPosition == tablePtr->ctSize+1 || theCSpecArray[cPosition].value > wEntry);
  467.       ASSERT(cPosition == 0 || theCSpecArray[cPosition-1].value < wEntry);
  468.       
  469.       *thePosition = cPosition;
  470.       return FALSE;
  471.    }
  472.    
  473.    
  474. short findEntryInSortedTable(CTabPtr theTable, short wEntry)
  475. {
  476.    short thePosition;
  477.    
  478.    if (engineFindEntryInSortedTable(theTable, wEntry, &thePosition)) {
  479.       return thePosition;
  480.    } else {
  481.       return kNoEntryFound;
  482.    }
  483. }
  484.  
  485.  
  486.  
  487. Boolean addEntryToSortedIndexedTable(CTabHandle theIndexedTable, ColorSpec *theColorSpec)
  488. {
  489.    register CTabPtr theIndexedTabPtr;
  490.    short wEntry;
  491.    short thePosition;
  492.    
  493.    ASSERT( colorTableIsIndexed(*theIndexedTable) );
  494.    
  495.    wEntry = theColorSpec->value;
  496.    
  497.    theIndexedTabPtr = *theIndexedTable;
  498.    if (engineFindEntryInSortedTable(theIndexedTabPtr, wEntry, &thePosition)) {
  499.       
  500.          /* No need to add the entry, it's already there. */
  501.       
  502.       theIndexedTabPtr->ctTable[thePosition] = *theColorSpec;
  503.       
  504.       return FALSE;
  505.       
  506.    } else {
  507.       
  508.       long newSize;
  509.       
  510.       newSize = sizeof(ColorTable)
  511.          + sizeof(ColorSpec) * ( theIndexedTabPtr->ctSize + 1 );
  512.       SetHandleSize( (Handle) theIndexedTable, newSize );
  513.       theIndexedTabPtr = *theIndexedTable;
  514.       
  515.       BlockMove( &theIndexedTabPtr->ctTable[thePosition],
  516.          &theIndexedTabPtr->ctTable[thePosition]+1,
  517.          (theIndexedTabPtr->ctSize+1 - thePosition) * sizeof(ColorSpec) );
  518.       
  519.       theIndexedTabPtr->ctTable[thePosition] = *theColorSpec;
  520.       
  521.       return TRUE;
  522.       
  523.    }
  524. }
  525.  
  526.  
  527.  
  528. CTabHandle makeIndexedFromSequential(CTabHandle theSequentialTable)
  529. {
  530.    CTabHandle theIndexedTable;
  531.    register ColorSpec *theSequentialColorSpecPtr;
  532.    register ColorSpec *theIndexedColorSpecPtr;
  533.    register CTabPtr theSequentialTablePtr;
  534.    register CTabPtr theIndexedTablePtr;
  535.    register short cIndexedEntry;
  536.    register short nSequentialEntriesMinusOne;
  537.    
  538.    ASSERT(theSequentialTable != NULL);
  539.    ASSERT(colorTableIsSequential(*theSequentialTable));
  540.    
  541.    nSequentialEntriesMinusOne = (**theSequentialTable).ctSize;
  542.    
  543.    theIndexedTable = (CTabHandle) NewHandle( sizeof(ColorTable)
  544.       + sizeof(ColorSpec) * nSequentialEntriesMinusOne );
  545.    theIndexedTablePtr = *theIndexedTable;
  546.    theSequentialTablePtr = *theSequentialTable;
  547.    
  548.    theIndexedTablePtr->ctSeed = theSequentialTablePtr->ctSeed;
  549.    theIndexedTablePtr->ctFlags = theSequentialTablePtr->ctFlags
  550.       & 0x3FFF
  551.       | 0x0000;
  552.    theIndexedTablePtr->ctSize = nSequentialEntriesMinusOne;
  553.    theIndexedColorSpecPtr = &theIndexedTablePtr->ctTable[0];
  554.    theSequentialColorSpecPtr = &theSequentialTablePtr->ctTable[0];
  555.    
  556.    for (cIndexedEntry = 0;
  557.       cIndexedEntry <= nSequentialEntriesMinusOne;
  558.       ++cIndexedEntry) {
  559.       
  560.       theIndexedColorSpecPtr->value = cIndexedEntry;
  561.       (theIndexedColorSpecPtr++)->rgb = (theSequentialColorSpecPtr++)->rgb;
  562.       
  563.    }
  564.    
  565.    return theIndexedTable;
  566. }
  567.  
  568.  
  569.  
  570. CTabHandle makeSequentialFromSortedIndexed(CTabHandle theIndexedTable, RGBColor *fillColor)
  571. {
  572.    CTabHandle theSequentialTable;
  573.    register ColorSpec *theIndexedColorSpecPtr;
  574.    register ColorSpec *theSequentialColorSpecPtr;
  575.    register CTabPtr theIndexedTablePtr;
  576.    register CTabPtr theSequentialTablePtr;
  577.    register short cSequentialEntry, nextIndexedEntry, nextIndexedPosition;
  578.    register short nIndexedEntriesMinusOne, nSequentialEntriesMinusOne;
  579.    
  580.    ASSERT(theIndexedTable != NULL);
  581.    ASSERT(colorTableIsIndexed(*theIndexedTable));
  582.    assertColorTableIsSorted(*theIndexedTable);
  583.    
  584.    nIndexedEntriesMinusOne = (**theIndexedTable).ctSize;
  585.    nSequentialEntriesMinusOne =
  586.       (**theIndexedTable).ctTable[nIndexedEntriesMinusOne].value;
  587.    
  588.    theSequentialTable = (CTabHandle) NewHandle( sizeof(ColorTable)
  589.       + sizeof(ColorSpec) * nSequentialEntriesMinusOne );
  590.    theSequentialTablePtr = *theSequentialTable;
  591.    theIndexedTablePtr = *theIndexedTable;
  592.    
  593.    theSequentialTablePtr->ctSeed = theIndexedTablePtr->ctSeed;
  594.    theSequentialTablePtr->ctFlags = theIndexedTablePtr->ctFlags
  595.       & 0x3FFF
  596.       | 0x8000;
  597.    theSequentialTablePtr->ctSize = nSequentialEntriesMinusOne;
  598.    theSequentialColorSpecPtr = &theSequentialTablePtr->ctTable[0];
  599.    theIndexedColorSpecPtr = &theIndexedTablePtr->ctTable[0];
  600.    
  601.    nextIndexedPosition = 0;
  602.    nextIndexedEntry = theIndexedColorSpecPtr[0].value;
  603.    for (cSequentialEntry = 0;
  604.       cSequentialEntry <= nSequentialEntriesMinusOne;
  605.       ++cSequentialEntry) {
  606.       
  607.       ASSERT(cSequentialEntry <= nextIndexedEntry);
  608.       
  609.       if (cSequentialEntry == nextIndexedEntry) {
  610.          theSequentialColorSpecPtr->value = cSequentialEntry;
  611.          (theSequentialColorSpecPtr++)->rgb = (theIndexedColorSpecPtr++)->rgb;
  612.          if (++nextIndexedPosition > nIndexedEntriesMinusOne) {
  613.             nextIndexedEntry = MAXINT;
  614.          } else {
  615.             nextIndexedEntry = theIndexedColorSpecPtr->value;
  616.          }
  617.       } else {
  618.          theSequentialColorSpecPtr->value = 0;
  619.          (theSequentialColorSpecPtr++)->rgb = *fillColor;
  620.       }
  621.       
  622.    }
  623.    
  624.    return theSequentialTable;
  625. }
  626.  
  627.  
  628.  
  629.    static int compareColorSpecValues(const ColorSpec *color1, const ColorSpec *color2);
  630.    static int compareColorSpecValues(register const ColorSpec *color1,
  631.       register const ColorSpec *color2)
  632.    {
  633.       if (color1->value != color2->value) {
  634.          if (color1->value < color2->value) {
  635.             return -1; 
  636.          } else {
  637.             return 1;
  638.          }
  639.       }
  640.       return 0;
  641.    }
  642.    
  643. void sortIndexedTable(CTabHandle theTable)
  644. {
  645.    register CTabPtr theTablePtr;
  646.    short oldState;
  647.    
  648.    ASSERT(theTable != NULL);
  649.    
  650.    theTablePtr = *theTable;
  651.    if (!colorTableIsIndexed(theTablePtr)) {
  652.       return ;
  653.    }
  654.    
  655.    oldState = HGetState( (Handle) theTable );
  656.    HLock( (Handle) theTable);
  657.    qsort( &theTablePtr->ctTable[0],
  658.       theTablePtr->ctSize+1,
  659.       sizeof(ColorSpec),
  660.       (__cmp_func) compareColorSpecValues);
  661.    HSetState( (Handle) theTable, oldState );
  662.    
  663.    assertColorTableIsSorted(theTablePtr);
  664. }
  665.  
  666.  
  667.  
  668. Boolean colorTablesAreEquivalent(CTabHandle table1, CTabHandle table2, RGBColor *fillColor)
  669. {
  670.    Boolean areEquivalent;
  671.    
  672.    ASSERT(table1 != NULL);
  673.    ASSERT(table2 != NULL);
  674.    
  675.    areEquivalent = TRUE;
  676.    
  677.    if (colorTableIsPaletted(*table1) || colorTableIsPaletted(*table2)) {
  678.          /* Paletted tables can't be compared to anything. */
  679.       return FALSE;
  680.    }
  681.    
  682.    if (colorTableIsSequential(*table1) || colorTableIsSequential(*table2)) {
  683.       
  684.          /*
  685.           * Convert one of the tables to sequential, if need be, and compare.
  686.           */
  687.       
  688.       register CTabPtr t1, t2;
  689.       register short nEntriesMinusOne;
  690.       CTabHandle convertedTable;
  691.       
  692.       convertedTable = NULL;
  693.       if (colorTableIsIndexed(*table1)) {
  694.          assertColorTableIsSorted(*table1);
  695.          convertedTable = makeSequentialFromSortedIndexed(table1, fillColor);
  696.          t1 = *convertedTable;
  697.          t2 = *table2;
  698.       } else if (colorTableIsIndexed(*table2)) {
  699.          assertColorTableIsSorted(*table2);
  700.          convertedTable = makeSequentialFromSortedIndexed(table2, fillColor);
  701.          t1 = *table1;
  702.          t2 = *convertedTable;
  703.       }
  704.       
  705.       if ( (nEntriesMinusOne=t1->ctSize) != t2->ctSize) {
  706.          
  707.          areEquivalent = FALSE;
  708.          
  709.       } else {
  710.          
  711.          register short cEntry;
  712.          
  713.          for (cEntry = 0; cEntry <= nEntriesMinusOne; ++cEntry) {
  714.             
  715.                /*
  716.                 * When comparing sequential tables, ignore the value field.
  717.                 */
  718.                
  719.             if ( t1->ctTable[cEntry].rgb.red != t2->ctTable[cEntry].rgb.red
  720.                || t1->ctTable[cEntry].rgb.green != t2->ctTable[cEntry].rgb.green
  721.                || t1->ctTable[cEntry].rgb.blue != t2->ctTable[cEntry].rgb.blue ) {
  722.                
  723.                areEquivalent = FALSE;
  724.                break;
  725.                
  726.             }
  727.             
  728.          }
  729.          
  730.       }
  731.       
  732.       DisposHandle( (Handle) convertedTable );
  733.       
  734.    } else {
  735.       
  736.          /*
  737.           * Leave both tables indexed, and compare.  Assume they're sorted.
  738.           */
  739.       
  740.       register CTabPtr t1, t2;
  741.       register short nEntriesMinusOne;
  742.       
  743.       t1 = *table1;
  744.       t2 = *table2;
  745.       
  746.       assertColorTableIsSorted(t1);
  747.       assertColorTableIsSorted(t2);
  748.       
  749.       if ( (nEntriesMinusOne=t1->ctSize) != t2->ctSize) {
  750.          
  751.          areEquivalent = FALSE;
  752.          
  753.       } else {
  754.          
  755.          register short cEntry;
  756.          
  757.          areEquivalent = ( jmemcmp(       // jmemcmp() is just a memcmp() clone
  758.             &t1->ctTable[0],
  759.             &t2->ctTable[0],
  760.             sizeof(ColorSpec)*(nEntriesMinusOne+1)
  761.             ) == 0);
  762.          
  763. #if you_want_it_spelled_out_for_you
  764.          for (cEntry = 0; cEntry <= nEntriesMinusOne; ++cEntry) {
  765.             if ( t1->ctTable[cEntry].value != t2->ctTable[cEntry].value
  766.                || t1->ctTable[cEntry].rgb.red != t2->ctTable[cEntry].rgb.red
  767.                || t1->ctTable[cEntry].rgb.green != t2->ctTable[cEntry].rgb.green
  768.                || t1->ctTable[cEntry].rgb.blue != t2->ctTable[cEntry].rgb.blue ) {
  769.                areEquivalent = FALSE;
  770.                break;
  771.             }
  772.          }
  773. #endif
  774.          
  775.       }
  776.       
  777.    }
  778.    
  779.    return areEquivalent;
  780. }
  781.  
  782.  
  783.  
  784. short (getNCTEntries)(CTabPtr theTable)
  785. {
  786.    return theTable->ctSize + 1;
  787. }
  788.  
  789.  
  790.  
  791. void setNCTEntries(CTabHandle theTable, short nEntries)
  792. {
  793.    short oldState;
  794.    
  795.    ASSERT(nEntries >= 0);
  796.    ASSERT(nEntries <= 256); // arbitrary
  797.    
  798.    if (getNCTEntries(*theTable) == nEntries) return;
  799.    
  800.    (**theTable).ctSize = nEntries-1;
  801.    
  802.    oldState = HGetState( (Handle) theTable );
  803.    HUnlock( (Handle) theTable );
  804.    SetHandleSize( (Handle) theTable,
  805.       sizeof(ColorTable) + (nEntries-1)*sizeof(ColorSpec));
  806.    HSetState( (Handle) theTable, oldState);
  807. }
  808.  
  809.  
  810.  
  811. void changeNCTEntries(CTabHandle theTable, short nEntries)
  812. {
  813.    if (nEntries == 0) return;
  814.    
  815.    setNCTEntries(theTable, getNCTEntries(*theTable) + nEntries);
  816. }
  817.  
  818.  
  819.  
  820. void (convertDepthToMaxNEntries)(short d, short n)
  821. {
  822.    DebugStr("\pconvertDepthToMaxNEntries was somehow called!");
  823. }
  824.  
  825.  
  826.  
  827. void (convertNEntriesToMinDepth)(short n, short d)
  828. {
  829.    DebugStr("\pconvertNEntriesToMinDepth was somehow called!");
  830. }
  831.  
  832.