Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
MacGamma.cpp
/* |
File: MacGamma.cpp |
Contains: Functions to enable Mac OS device gamma adjustments using 3 channel 256 element 8 bit gamma ramps |
Written by: Geoff Stahl (ggs) |
Copyright: Copyright © 1999 Apple Computer, Inc., All Rights Reserved |
Change History (most recent first): |
<7> 8/25/99 ggs Added usage notes |
<6> 8/25/99 ggs Fixed handling of 1 -> 3 channel gamma conversions in get and set |
<5> 7/14/99 ggs Added sample code notice |
<4> 5/20/99 ggs Added handling for gamma tables with different data widths, |
number of entries, and channels. Forced updates to 3 channels |
(poss. could break on rare card, but very unlikely). Added |
quick update with BlockMove for 3x256x8 tables. Updated function |
names. |
<3> 5/20/99 ggs Cleaned up and commented |
<2> 5/20/99 ggs Added system wide get and restore gamma functions to enable |
restoration of original for all devices. Modified functionality |
to return pointers vice squirreling away the memory. |
<1> 5/20/99 ggs Initial Add |
Disclaimer: You may incorporate this sample code into your applications without |
restriction, though the sample code has been provided "AS IS" and the |
responsibility for its operation is 100% yours. However, what you are |
not permitted to do is to redistribute the source as "DSC Sample Code" |
after having made changes. If you're going to re-distribute the source, |
we require that you make it clear in the source that the code was |
descended from Apple Sample Code, but that you've made changes. |
*/ |
// Usage notes: |
// SetDeviceGammaRampXX may not return until the VBL following their call, depending on the display driver, |
// this is due to the handling of the Control call by the driver. |
// Startup and Shutdown: |
// GetSystemGammas (...) must be called to save the system gammas prior to using MacGamma |
// RestoreSystemGamms (...) must be called before exiting to restore original gamma |
// DisposeSystemGammas (...) can then be used to dispose of the system gammas |
// Suspend and Resume: |
// RestoreSystemGamms (...); DisposeSystemGammas (...); should also be called prior to suspend and |
// GetSystemGammas (...) should be called when resuming |
// this prevents users from saving display settings with your gammas |
// system includes ---------------------------------------------------------- |
#include <Devices.h> |
#include <Files.h> |
#include <MacTypes.h> |
#include <QDOffscreen.h> |
#include <Quickdraw.h> |
#include <video.h> |
// project includes --------------------------------------------------------- |
#include "MacGamma.h" |
// structures (internal/private) ----------------------------------------------- |
struct recDeviceGamma // storage for device handle and gamma table |
{ |
GDHandle hGD; // handle to device |
GammaTblPtr pDeviceGamma; // pointer to device gamma table |
}; |
typedef recDeviceGamma * precDeviceGamma; |
struct recSystemGamma // storage for system devices and gamma tables |
{ |
short numDevices; // number of devices |
precDeviceGamma * devGamma; // array of pointers to device gamma records |
}; |
typedef recSystemGamma * precSystemGamma; |
// prototypes (internal/private) -------------------------------------------- |
// 5/20/99: (ggs) changed functional specification |
OSErr GetGammaTable(GDHandle gd, GammaTblPtr * ppTableGammaOut); // Returns the device gamma table pointer in ppDeviceTable |
Ptr CreateEmptyGammaTable (short channels, short entries, short bits); // creates an empty gamma table of a given size, assume no formula data will be used |
Ptr CopyGammaTable (GammaTblPtr pTableGammaIn); // given a pointer toa device gamma table properly iterates and copies |
void DisposeGammaTable (Ptr pGamma); // disposes gamma table returned from GetGammaTable, GetDeviceGamma, or CopyGammaTable |
Ptr GetDeviceGamma (GDHandle hGD); // returns pointer to copy of orginal device gamma table in native format |
void RestoreDeviceGamma (GDHandle hGD, Ptr pGammaTable); // sets device to saved table |
// functions (internal/private) --------------------------------------------- |
// GetGammaTable |
// Returns the device gamma table pointer in ppTableGammaOut |
OSErr GetGammaTable (GDHandle hGD, GammaTblPtr * ppTableGammaOut) |
{ |
VDGammaRecord DeviceGammaRec; |
CntrlParam cParam; |
OSErr err; |
cParam.ioCompletion = NULL; // set up control params |
cParam.ioNamePtr = NULL; |
cParam.ioVRefNum = 0; |
cParam.ioCRefNum = (**hGD).gdRefNum; |
cParam.csCode = cscGetGamma; // Get Gamma commnd to device |
*(Ptr *)cParam.csParam = (Ptr) &DeviceGammaRec; // record for gamma |
err = PBStatus( (ParmBlkPtr)&cParam, 0 ); // get gamma |
*ppTableGammaOut = (GammaTblPtr)(DeviceGammaRec.csGTable); // pull table out of record |
return err; |
} |
// -------------------------------------------------------------------------- |
// CreateEmptyGammaTable |
// creates an empty gamma table of a given size, assume no formula data will be used |
Ptr CreateEmptyGammaTable (short channels, short entries, short bits) |
{ |
GammaTblPtr pTableGammaOut = NULL; |
short tableSize, dataWidth; |
dataWidth = (bits + 7) / 8; // number of bytes per entry |
tableSize = sizeof (GammaTbl) + (channels * entries * dataWidth); |
pTableGammaOut = (GammaTblPtr) NewPtrClear (tableSize); // allocate new tabel |
if (pTableGammaOut) // if we successfully allocated |
{ |
pTableGammaOut->gVersion = 0; // set parameters based on input |
pTableGammaOut->gType = 0; |
pTableGammaOut->gFormulaSize = 0; |
pTableGammaOut->gChanCnt = channels; |
pTableGammaOut->gDataCnt = entries; |
pTableGammaOut->gDataWidth = bits; |
} |
return (Ptr)pTableGammaOut; // return whatever we allocated |
} |
// -------------------------------------------------------------------------- |
// CopyGammaTable |
// given a pointer toa device gamma table properly iterates and copies |
Ptr CopyGammaTable (GammaTblPtr pTableGammaIn) |
{ |
GammaTblPtr pTableGammaOut = NULL; |
short tableSize, dataWidth; |
if (pTableGammaIn) // if there is a table to copy |
{ |
dataWidth = (pTableGammaIn->gDataWidth + 7) / 8; // number of bytes per entry |
tableSize = sizeof (GammaTbl) + pTableGammaIn->gFormulaSize + |
(pTableGammaIn->gChanCnt * pTableGammaIn->gDataCnt * dataWidth); |
pTableGammaOut = (GammaTblPtr) NewPtr (tableSize); // allocate new table |
if (pTableGammaOut) |
BlockMove( (Ptr)pTableGammaIn, (Ptr)pTableGammaOut, tableSize); // move everything |
} |
return (Ptr)pTableGammaOut; // return whatever we allocated, could be NULL |
} |
// -------------------------------------------------------------------------- |
// DisposeGammaTable |
// disposes gamma table returned from GetGammaTable, GetDeviceGamma, or CopyGammaTable |
// 5/20/99: (ggs) added |
void DisposeGammaTable (Ptr pGamma) |
{ |
if (pGamma) |
DisposePtr((Ptr) pGamma); // get rid of it |
} |
// -------------------------------------------------------------------------- |
// GetDeviceGamma |
// returns pointer to copy of orginal device gamma table in native format (allocates memory for gamma table, call DisposeDeviceGamma to delete) |
// 5/20/99: (ggs) change spec to return the allocated pointer vice storing internally |
Ptr GetDeviceGamma (GDHandle hGD) |
{ |
GammaTblPtr pTableGammaDevice = NULL; |
GammaTblPtr pTableGammaReturn = NULL; |
OSErr err; |
err = GetGammaTable (hGD, &pTableGammaDevice); // get a pointer to the devices table |
if ((noErr == err) && pTableGammaDevice) // if succesful |
pTableGammaReturn = (GammaTblPtr) CopyGammaTable (pTableGammaDevice); // copy to global |
return (Ptr) pTableGammaReturn; |
} |
// -------------------------------------------------------------------------- |
// RestoreDeviceGamma |
// sets device to saved table |
// 5/20/99: (ggs) now does not delete table, avoids confusion |
void RestoreDeviceGamma (GDHandle hGD, Ptr pGammaTable) |
{ |
VDSetEntryRecord setEntriesRec; |
VDGammaRecord gameRecRestore; |
CTabHandle hCTabDeviceColors; |
Ptr csPtr; |
OSErr err = noErr; |
if (pGammaTable) // if we have a table to restore |
{ |
gameRecRestore.csGTable = pGammaTable; // setup restore record |
csPtr = (Ptr) &gameRecRestore; |
err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma |
if ((noErr == err) && (8 == (**(**hGD).gdPMap).pixelSize)) // if successful and on an 8 bit device |
{ |
hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update |
setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; |
setEntriesRec.csStart = 0; |
setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; |
csPtr = (Ptr) &setEntriesRec; |
err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT |
} |
} |
} |
// functions (external/public) ---------------------------------------------- |
// GetSystemGammas |
// returns a pointer to a set of all current device gammas in native format (returns NULL on failure, which means reseting gamma will not be possible) |
// 5/20/99: (ggs) added |
Ptr GetSystemGammas (void) |
{ |
precSystemGamma pSysGammaOut; // return pointer to system device gamma info |
short devCount = 0; // number of devices attached |
Boolean fail = false; |
pSysGammaOut = (precSystemGamma) NewPtr (sizeof (recSystemGamma)); // allocate for structure |
GDHandle hGDevice = GetDeviceList (); // top of device list |
do // iterate |
{ |
devCount++; // count devices |
hGDevice = GetNextDevice (hGDevice); // next device |
} while (hGDevice); |
pSysGammaOut->devGamma = (precDeviceGamma *) NewPtr (sizeof (precDeviceGamma) * devCount); // allocate for array of pointers to device records |
if (pSysGammaOut) |
{ |
pSysGammaOut->numDevices = devCount; // stuff count |
devCount = 0; // reset iteration |
hGDevice = GetDeviceList (); |
do |
{ |
pSysGammaOut->devGamma [devCount] = (precDeviceGamma) NewPtr (sizeof (recDeviceGamma)); // new device record |
if (pSysGammaOut->devGamma [devCount]) // if we actually allocated memory |
{ |
pSysGammaOut->devGamma [devCount]->hGD = hGDevice; // stuff handle |
pSysGammaOut->devGamma [devCount]->pDeviceGamma = (GammaTblPtr)GetDeviceGamma (hGDevice); // copy gamma table |
} |
else // otherwise dump record on exit |
fail = true; |
devCount++; // next device |
hGDevice = GetNextDevice (hGDevice); |
} while (hGDevice); |
} |
if (!fail) // if we did not fail |
return (Ptr) pSysGammaOut; // return pointer to structure |
else |
{ |
DisposeSystemGammas (&(Ptr)pSysGammaOut); // otherwise dump the current structures (dispose does error checking) |
return NULL; // could not complete |
} |
} |
// -------------------------------------------------------------------------- |
// RestoreSystemGammas |
// restores all system devices to saved gamma setting |
// 5/20/99: (ggs) added |
void RestoreSystemGammas (Ptr pSystemGammas) |
{ |
precSystemGamma pSysGammaIn = (precSystemGamma) pSystemGammas; |
if (pSysGammaIn) |
for (short i = 0; i < pSysGammaIn->numDevices; i++) // for all devices |
RestoreDeviceGamma (pSysGammaIn->devGamma [i]->hGD, (Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); // restore gamma |
} |
// -------------------------------------------------------------------------- |
// DisposeSystemGammas |
// iterates through and deletes stored gamma settings |
// 5/20/99: (ggs) added |
void DisposeSystemGammas (Ptr* ppSystemGammas) |
{ |
precSystemGamma pSysGammaIn; |
if (ppSystemGammas) |
{ |
pSysGammaIn = (precSystemGamma) *ppSystemGammas; |
if (pSysGammaIn) |
{ |
for (short i = 0; i < pSysGammaIn->numDevices; i++) // for all devices |
if (pSysGammaIn->devGamma [i]) // if pointer is valid |
{ |
DisposeGammaTable ((Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); // dump gamma table |
DisposePtr ((Ptr) pSysGammaIn->devGamma [i]); // dump device info |
} |
DisposePtr ((Ptr) pSysGammaIn->devGamma); // dump device pointer array |
DisposePtr ((Ptr) pSysGammaIn); // dump system structure |
*ppSystemGammas = NULL; |
} |
} |
} |
// -------------------------------------------------------------------------- |
// GetDeviceGammaRampGD |
// retrieves the gamma ramp from a graphics device (pRamp: 3 arrays of 256 elements each) |
Boolean GetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp) |
{ |
GammaTblPtr pTableGammaTemp = NULL; |
long indexChan, indexEntry; |
OSErr err; |
if (pRamp) // ensure pRamp is allocated |
{ |
err = GetGammaTable (hGD, &pTableGammaTemp); // get a pointer to the current gamma |
if ((noErr == err) && pTableGammaTemp) // if successful |
{ |
// fill ramp |
unsigned char * pEntry = (unsigned char *) &pTableGammaTemp->gFormulaData + pTableGammaTemp->gFormulaSize; // base of table |
short bytesPerEntry = (pTableGammaTemp->gDataWidth + 7) / 8; // size, in bytes, of the device table entries |
short shiftRightValue = pTableGammaTemp->gDataWidth - 8; // number of right shifts device -> ramp |
short channels = pTableGammaTemp->gChanCnt; |
short entries = pTableGammaTemp->gDataCnt; |
if (3 == channels) // RGB format |
{ // note, this will create runs of entries if dest. is bigger (not linear interpolate) |
for (indexChan = 0; indexChan < channels; indexChan++) |
for (indexEntry = 0; indexEntry < 256; indexEntry++) |
*((unsigned char *) pRamp + (indexChan * 256) + indexEntry) = |
*(pEntry + indexChan * entries * bytesPerEntry + indexEntry * entries * bytesPerEntry / 256) >> shiftRightValue; |
} |
else // single channel format |
{ |
for (indexChan = 0; indexChan < 768; indexChan += 256) // repeat for all 3 channels (step by ramp size) |
for (indexEntry = 0; indexEntry < 256; indexEntry++) // for all entries set vramp value |
*((unsigned char *) pRamp + indexChan + indexEntry) = |
*(pEntry + indexEntry * entries * bytesPerEntry / 256) >> shiftRightValue; |
} |
return true; |
} |
} |
return false; |
} |
// -------------------------------------------------------------------------- |
// GetDeviceGammaRampGW |
// retrieves the gamma ramp from a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each) |
Boolean GetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp) |
{ |
GDHandle hGD = GetGWorldDevice (pGW); |
return GetDeviceGammaRampGD (hGD, pRamp); |
} |
// -------------------------------------------------------------------------- |
// GetDeviceGammaRampCGP |
// retrieves the gamma ramp from a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each) |
Boolean GetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp) |
{ |
CGrafPtr pGrafSave; |
GDHandle hGDSave; |
GetGWorld (&pGrafSave, &hGDSave); |
SetGWorld (pGraf, NULL); |
GDHandle hGD = GetGDevice (); |
Boolean fResult = GetDeviceGammaRampGD (hGD, pRamp); |
SetGWorld (pGrafSave, hGDSave); |
return fResult; |
} |
// -------------------------------------------------------------------------- |
// SetDeviceGammaRampGD |
// sets the gamma ramp for a graphics device (pRamp: 3 arrays of 256 elements each (R,G,B)) |
Boolean SetDeviceGammaRampGD (GDHandle hGD, Ptr pRamp) |
{ |
VDSetEntryRecord setEntriesRec; |
VDGammaRecord gameRecRestore; |
GammaTblPtr pTableGammaNew; |
GammaTblPtr pTableGammaCurrent = NULL; |
CTabHandle hCTabDeviceColors; |
Ptr csPtr; |
OSErr err; |
short dataBits, entries, channels = 3; // force three channels in the gamma table |
if (pRamp) // ensure pRamp is allocated |
{ |
err= GetGammaTable (hGD, &pTableGammaCurrent); // get pointer to current table |
if ((noErr == err) && pTableGammaCurrent) |
{ |
dataBits = pTableGammaCurrent->gDataWidth; // table must have same data width |
entries = pTableGammaCurrent->gDataCnt; // table must be same size |
pTableGammaNew = (GammaTblPtr) CreateEmptyGammaTable (channels, entries, dataBits); // our new table |
if (pTableGammaNew) // if successful fill table |
{ |
unsigned char * pGammaBase = (unsigned char *) &pTableGammaNew->gFormulaData + pTableGammaNew->gFormulaSize; // base of table |
if ((256 == entries) && (8 == dataBits)) // simple case: direct mapping |
BlockMove ((Ptr)pRamp, (Ptr)pGammaBase, channels * entries); // move everything |
else // tough case handle entry, channel and data size disparities |
{ |
short bytesPerEntry = (dataBits + 7) / 8; // size, in bytes, of the device table entries |
short shiftRightValue = 8 - dataBits; // number of right shifts ramp -> device |
shiftRightValue += ((bytesPerEntry - 1) * 8); // multibyte entries and the need to map a byte at a time most sig. to least sig. |
for (short indexChan = 0; indexChan < channels; indexChan++) // for all the channels |
for (short indexEntry = 0; indexEntry < entries; indexEntry++) // for all the entries |
{ |
short currentShift = shiftRightValue; // reset current bit shift |
long temp = *((unsigned char *)pRamp + (indexChan << 8) + (indexEntry << 8) / entries); // get data from ramp |
for (short indexByte = 0; indexByte < bytesPerEntry; indexByte++) // for all bytes |
{ |
if (currentShift < 0) // shift data correctly for current byte |
*(pGammaBase++) = temp << -currentShift; |
else |
*(pGammaBase++) = temp >> currentShift; |
currentShift -= 8; // increment shift to align to next less sig. byte |
} |
} |
} |
// set gamma |
gameRecRestore.csGTable = (Ptr) pTableGammaNew; // setup restore record |
csPtr = (Ptr) &gameRecRestore; |
err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma (note, display drivers may delay returning from this until VBL) |
if ((8 == (**(**hGD).gdPMap).pixelSize) && (noErr == err)) // if successful and on an 8 bit device |
{ |
hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update |
setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; |
setEntriesRec.csStart = 0; |
setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; |
csPtr = (Ptr) &setEntriesRec; |
err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT |
} |
DisposeGammaTable ((Ptr) pTableGammaNew); // dump table |
if (noErr == err) |
return true; |
} |
} |
} |
else // set NULL gamma -> results in linear map |
{ |
gameRecRestore.csGTable = (Ptr) NULL; // setup restore record |
csPtr = (Ptr) &gameRecRestore; |
err = Control((**hGD).gdRefNum, cscSetGamma, (Ptr) &csPtr); // restore gamma |
if ((8 == (**(**hGD).gdPMap).pixelSize) && (noErr == err)) // if successful and on an 8 bit device |
{ |
hCTabDeviceColors = (**(**hGD).gdPMap).pmTable; // do SetEntries to force CLUT update |
setEntriesRec.csTable = (ColorSpec *) &(**hCTabDeviceColors).ctTable; |
setEntriesRec.csStart = 0; |
setEntriesRec.csCount = (**hCTabDeviceColors).ctSize; |
csPtr = (Ptr) &setEntriesRec; |
err = Control((**hGD).gdRefNum, cscSetEntries, (Ptr) &csPtr); // SetEntries in CLUT |
} |
if (noErr == err) |
return true; |
} |
return false; // memory allocation or device control failed if we get here |
} |
// -------------------------------------------------------------------------- |
// SetDeviceGammaRampGW |
// sets the gamma ramp for a graphics device associated with a GWorld pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) |
Boolean SetDeviceGammaRampGW (GWorldPtr pGW, Ptr pRamp) |
{ |
GDHandle hGD = GetGWorldDevice (pGW); |
return SetDeviceGammaRampGD (hGD, pRamp); |
} |
// -------------------------------------------------------------------------- |
// SetDeviceGammaRampCGP |
// sets the gamma ramp for a graphics device associated with a CGraf pointer (pRamp: 3 arrays of 256 elements each (R,G,B)) |
Boolean SetDeviceGammaRampCGP (CGrafPtr pGraf, Ptr pRamp) |
{ |
CGrafPtr pGrafSave; |
GDHandle hGDSave; |
GetGWorld (&pGrafSave, &hGDSave); |
SetGWorld (pGraf, NULL); |
GDHandle hGD = GetGDevice (); |
Boolean fResult = SetDeviceGammaRampGD (hGD, pRamp); |
SetGWorld (pGrafSave, hGDSave); |
return fResult; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22