From bec777804906ccf1ea0ee1d0786c58d5c898c89e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 15 Jun 2007 06:29:52 +0000 Subject: [PATCH] Support for Gamma Ramps on Mac OS Classic in both the macrom and macdsp video targets, thanks to Gregory Smith! Fixes Bugzilla #431. --- src/video/maccommon/SDL_macwm.c | 398 ++++++++++++++++++++++++++++++ src/video/maccommon/SDL_macwm_c.h | 13 + src/video/macdsp/SDL_dspvideo.c | 8 + src/video/macrom/SDL_romvideo.c | 8 + 4 files changed, 427 insertions(+) diff --git a/src/video/maccommon/SDL_macwm.c b/src/video/maccommon/SDL_macwm.c index 667774467..66828bfd9 100644 --- a/src/video/maccommon/SDL_macwm.c +++ b/src/video/maccommon/SDL_macwm.c @@ -30,6 +30,15 @@ #include #endif +#if SDL_MACCLASSIC_GAMMA_SUPPORT +#include +#include +#include +#include +#include +#include +#endif + #include "SDL_stdinc.h" #include "SDL_macwm_c.h" @@ -42,3 +51,392 @@ void Mac_SetCaption(_THIS, const char *title, const char *icon) if (SDL_Window) SetWTitle(SDL_Window, ptitle); /* MJS */ } + +#if SDL_MACCLASSIC_GAMMA_SUPPORT +/* + * ADC Gamma Ramp support... + * + * Mac Gamma Ramp code was originally from sample code provided by + * Apple Developer Connection, and not written specifically for SDL: + * "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 (c) 1999 Apple Computer, Inc., All Rights Reserved + * 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." + * (The sample code has been integrated into this file, and thus is modified from the original Apple sources.) + */ + +typedef struct recDeviceGamma /* storage for device handle and gamma table */ +{ + GDHandle hGD; /* handle to device */ + GammaTblPtr pDeviceGamma; /* pointer to device gamma table */ +} recDeviceGamma; +typedef recDeviceGamma * precDeviceGamma; + +typedef struct recSystemGamma /* storage for system devices and gamma tables */ +{ + short numDevices; /* number of devices */ + precDeviceGamma * devGamma; /* array of pointers to device gamma records */ +} recSystemGamma; +typedef recSystemGamma * precSystemGamma; + +static 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 */ +} + +static 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 = PBStatusSync( (ParmBlkPtr)&cParam ); /* get gamma */ + + *ppTableGammaOut = (GammaTblPtr)(DeviceGammaRec.csGTable); /* pull table out of record */ + + return err; +} + +static 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; +} + +static void DisposeGammaTable (Ptr pGamma) +{ + if (pGamma) + DisposePtr((Ptr) pGamma); /* get rid of it */ +} + +static void DisposeSystemGammas (Ptr* ppSystemGammas) +{ + precSystemGamma pSysGammaIn; + if (ppSystemGammas) + { + pSysGammaIn = (precSystemGamma) *ppSystemGammas; + if (pSysGammaIn) + { + short i; + for (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; + } + } +} + +static 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; +} + +static Ptr GetSystemGammas (void) +{ + precSystemGamma pSysGammaOut; /* return pointer to system device gamma info */ + short devCount = 0; /* number of devices attached */ + Boolean fail = false; + GDHandle hGDevice; + + pSysGammaOut = (precSystemGamma) NewPtr (sizeof (recSystemGamma)); /* allocate for structure */ + + 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 */ + } +} + +static 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 */ + } + } +} + +static void RestoreSystemGammas (Ptr pSystemGammas) +{ + short i; + precSystemGamma pSysGammaIn = (precSystemGamma) pSystemGammas; + if (pSysGammaIn) + for (i = 0; i < pSysGammaIn->numDevices; i++) /* for all devices */ + RestoreDeviceGamma (pSysGammaIn->devGamma [i]->hGD, (Ptr) pSysGammaIn->devGamma [i]->pDeviceGamma); /* restore gamma */ +} + +static 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 */ +} + +static 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 indexChan, indexEntry; + 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 (indexChan = 0; indexChan < channels; indexChan++) /* for all the channels */ + for (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 */ + short indexByte; + for (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 */ +} + +/* end of ADC Gamma Ramp support code... */ + +static Ptr systemGammaPtr; + +void Mac_QuitGamma(_THIS) +{ + if (systemGammaPtr) + { + RestoreSystemGammas(systemGammaPtr); + DisposeSystemGammas(&systemGammaPtr); + } +} + +static unsigned char shiftedRamp[3 * 256]; + +int Mac_SetGammaRamp(_THIS, Uint16 *ramp) +{ + int i; + if (!systemGammaPtr) + systemGammaPtr = GetSystemGammas(); + for (i = 0; i < 3 * 256; i++) + { + shiftedRamp[i] = ramp[i] >> 8; + } + + if (SetDeviceGammaRampGD(GetMainDevice(), (Ptr) shiftedRamp)) + return 0; + else + return -1; +} + +int Mac_GetGammaRamp(_THIS, Uint16 *ramp) +{ + if (GetDeviceGammaRampGD(GetMainDevice(), (Ptr) shiftedRamp)) + { + int i; + for (i = 0; i < 3 * 256; i++) + { + ramp[i] = shiftedRamp[i] << 8; + } + return 0; + } + else + return -1; +} + +#endif /* SDL_MACCLASSIC_GAMMA_SUPPORT */ + + diff --git a/src/video/maccommon/SDL_macwm_c.h b/src/video/maccommon/SDL_macwm_c.h index 5f56b699d..5add86120 100644 --- a/src/video/maccommon/SDL_macwm_c.h +++ b/src/video/maccommon/SDL_macwm_c.h @@ -26,3 +26,16 @@ /* Functions to be exported */ extern void Mac_SetCaption(_THIS, const char *title, const char *icon); +/* + * There's no Carbonized gamma support in Mac OS X, since PBStatusSync() and + * Control() aren't supported in OS X's Carbonlib. Use the Quartz driver + * instead. + */ +#define SDL_MACCLASSIC_GAMMA_SUPPORT ((defined(__APPLE__) && defined(__MACH__)) == 0) + +#if SDL_MACCLASSIC_GAMMA_SUPPORT +extern void Mac_QuitGamma(_THIS); +extern int Mac_SetGammaRamp(_THIS, Uint16 *ramp); +extern int Mac_GetGammaRamp(_THIS, Uint16 *ramp); +#endif + diff --git a/src/video/macdsp/SDL_dspvideo.c b/src/video/macdsp/SDL_dspvideo.c index 060833fe6..ed3f8c48f 100644 --- a/src/video/macdsp/SDL_dspvideo.c +++ b/src/video/macdsp/SDL_dspvideo.c @@ -297,6 +297,10 @@ static SDL_VideoDevice *DSp_CreateDevice(int devindex) device->UnlockHWSurface = DSp_UnlockHWSurface; device->FlipHWSurface = DSp_FlipHWSurface; device->FreeHWSurface = DSp_FreeHWSurface; +#if SDL_MACCLASSIC_GAMMA_SUPPORT + device->SetGammaRamp = Mac_SetGammaRamp; + device->GetGammaRamp = Mac_GetGammaRamp; +#endif #if SDL_VIDEO_OPENGL device->GL_MakeCurrent = Mac_GL_MakeCurrent; device->GL_SwapBuffers = DSp_GL_SwapBuffers; @@ -1381,6 +1385,10 @@ void DSp_VideoQuit(_THIS) /* Free Palette and restore original */ DSp_DestroyPalette (this); +#if SDL_MACCLASSIC_GAMMA_SUPPORT + Mac_QuitGamma(this); +#endif + /* Free list of video modes */ if ( SDL_modelist != NULL ) { for ( i=0; SDL_modelist[i]; i++ ) { diff --git a/src/video/macrom/SDL_romvideo.c b/src/video/macrom/SDL_romvideo.c index 3113b206d..f0ab8dedc 100644 --- a/src/video/macrom/SDL_romvideo.c +++ b/src/video/macrom/SDL_romvideo.c @@ -158,6 +158,10 @@ static SDL_VideoDevice *ROM_CreateDevice(int devindex) device->UnlockHWSurface = ROM_UnlockHWSurface; device->FlipHWSurface = NULL; device->FreeHWSurface = ROM_FreeHWSurface; +#if SDL_MACCLASSIC_GAMMA_SUPPORT + device->SetGammaRamp = Mac_SetGammaRamp; + device->GetGammaRamp = Mac_GetGammaRamp; +#endif #if SDL_VIDEO_OPENGL device->GL_MakeCurrent = Mac_GL_MakeCurrent; device->GL_SwapBuffers = Mac_GL_SwapBuffers; @@ -725,6 +729,10 @@ void ROM_VideoQuit(_THIS) } RestoreDeviceClut(GetMainDevice()); +#if SDL_MACCLASSIC_GAMMA_SUPPORT + Mac_QuitGamma(this); +#endif + /* Free list of video modes */ if ( SDL_modelist != NULL ) { for ( i=0; SDL_modelist[i]; ++i ) {