diff src/video/maccommon/SDL_macwm.c @ 3962:7cdb1d7fc93b SDL-1.2

Support for Gamma Ramps on Mac OS Classic in both the macrom and macdsp video targets, thanks to Gregory Smith! Fixes Bugzilla #431.
author Ryan C. Gordon <icculus@icculus.org>
date Fri, 15 Jun 2007 06:29:52 +0000
parents d910939febfa
children a1b03ba2fcd0
line wrap: on
line diff
--- a/src/video/maccommon/SDL_macwm.c	Fri Jun 15 05:41:22 2007 +0000
+++ b/src/video/maccommon/SDL_macwm.c	Fri Jun 15 06:29:52 2007 +0000
@@ -30,6 +30,15 @@
 #include <Strings.h>
 #endif
 
+#if SDL_MACCLASSIC_GAMMA_SUPPORT
+#include <Devices.h>
+#include <Files.h>
+#include <MacTypes.h>
+#include <QDOffscreen.h>
+#include <Quickdraw.h>
+#include <Video.h>
+#endif
+
 #include "SDL_stdinc.h"
 #include "SDL_macwm_c.h"
 
@@ -42,3 +51,392 @@
 	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 */
+
+