Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Advanced Color Imaging on the Mac OS /
Chapter 4 - Developing ColorSync-Supportive Applications / Developing Your ColorSync-Supportive Application


Accessing a Resource-Based Profile With a Procedure

The ColorSync Manager provides for multiple concurrent accesses to a single profile through the use of a private data structure called a profile reference. When you call the CMOpenProfile function to open a profile or the CMNewProfile, CWNewLinkProfile, or CMCopyProfile functions to create or copy a profile, you pass a profile location and the function returns a profile reference. To specify the profile location, you use a structure of type CMProfileLocation, as described in "Opening a Profile and Obtaining a Reference to It" (page 4-17).

A ColorSync profile that you open or create is typically stored in one of the following locations:

The sample code in Listing 4-12 to Listing 4-23 demonstrates how to use a profile access procedure to provide access to a resource-based profile.

Note
While the following sample code includes some error handling, more complete error handling is left as an exercise for the reader.

Defining a Data Structure for a Resource-Based Profile

The sample code listings that follow use the application-defined MyResourceLocRec data structure. It stores information to describe a resource-based profile, including

struct MyResourceLocRec {
   FSSpec         resFileSpec;
   ResType        resType;
   short          resID;
   short          resFileRef;
   Handle         resHandle;
   CMProfileAccessUPPproc;
   Str255         resName;
};
   
typedef struct MyResourceLocRec MyResourceLocRec, *MyResourceLocPtr;
The ColorSync Manager defines the CMProfileAccessUPP type as follows:

typedef UniversalProcPtr CMProfileAccessUPP;

Setting Up a Location Structure for Procedure Access to a Resource-Based Profile

The MyCreateProcedureProfileAccess routine shown in Listing 4-12 sets up a CMProfileLocation structure for procedure access to a resource-based profile. The MyDisposeProcedureProfileAccess routine, shown in Listing 4-13, disposes of memory allocated by MyCreateProcedureProfileAccess. Your application uses these routines (or similar ones that you write) in the following way:

  1. Before calling a ColorSync routine such as CMCopyProfile, you call the MyCreateProcedureProfileAccess routine to set up a CMProfileLocation structure that you can pass to the ColorSync routine. The location structure specifies your profile-access procedure and may provide other information as well. A sample profile-access procedure is shown in Listing 4-14.
  2. During the course of its operations, ColorSync may call your profile-access procedure many times.
  3. After the ColorSync routine has completed its operation, and if your application does not need to use the CMProfileLocation structure for another operation, you call the MyDisposeProcedureProfileAccess routine to dispose of memory allocated by MyCreateProcedureProfileAccess.

For the sample MyCreateProcedureProfileAccess routine shown in Listing 4-12, you pass a pointer to a CMProfileLocation structure to fill in, a pointer to a file specification for the resource file containing the profile resource, the type of the resource, the ID for the resource, and optionally the name of the resource (stored as a Pascal string, where the first byte is a length byte for the string).

Note
Listing 4-12 assumes the profile access routine, MyCMProfileAccessProc, is within the scope of the MyCreateProcedureProfileAccess routine. Optionally, you could add a parameter to pass in a procedure pointer for the profile access routine.
Listing 4-12 Setting up a location structure for procedure access to a resource-based profile

OSErr MyCreateProcedureProfileAccess (
                           CMProfileLocation *profileLocation,
                           FSSpec *resourceSpec,
                           Str255 resourceName,
                           OSType resourceType,
                           short resourceID)
{
   OSErr          err = noErr;
   MyResourceLocPtrresourceInfo;
   
   /* allocate memory for our private resource info structure */
   resourceInfo = (MyResourceLocPtr) NewPtrClear(sizeof(MyResourceLocRec));
   if (!resourceInfo)
      err = MemError();
      
   if (!err)
   {
      /* set up our private resource info structure */
      resourceInfo->resFileSpec = *resourceSpec;
      resourceInfo->resType = resourceType;
      resourceInfo->resID = resourceID;
      resourceInfo->resFileRef = 0;
      resourceInfo->resHandle = 0;
      resourceInfo->proc = NewCMProfileAccessProc(MyCMProfileAccessProc);
      /* if a resource name was passed in, copy it to the structure;
         since it's a Pascal string, first byte is length;
         note that BlockMoveData is faster than BlockMove for a
         move that involves data only */
      if (resourceName)
         BlockMoveData(resourceName, resourceInfo->resName,
                     resourceName[0]);
      
      /* set up the profile location structure */
      profileLocation->locType = cmProcedureBasedProfile;
      profileLocation->u.procLoc.refCon = (void*) resourceInfo;
      profileLocation->u.procLoc.proc = resourceInfo->proc;
   }
   return err;
}
If the MyCreateProcedureProfileAccess routine is able to set up the profile location pointer for procedure access to a resource-based profile, it returns a value of noErr.

Disposing of a Resource-Based Profile Access Structure

Your application calls the MyDisposeProcedureProfileAccess routine, shown in Listing 4-13, to dispose of any memory allocated by the MyCreateProcedureProfileAccess routine, shown in Listing 4-12.

Listing 4-13 Disposing of a resource-based profile access structure

void MyDisposeProcedureProfileAccess (CMProfileLocation *profileLocation)
{
   DisposeRoutineDescriptor(profileLocation->u.procLoc.proc);

   /* dispose of our private resource info structure */
   DisposePtr((Ptr)profileLocation->u.procLoc.refCon);
}
This routine first disposes of the universal procedure pointer to your profile access procedure, then disposes of the pointer used to store resource data in a MyResourceLocRec structure.

Responding to a Procedure-Based Profile Command

For the procedure declaration for a profile access procedure, see MyCMProfileAccessProc (page 3-172) in Advanced Color Imaging Reference. The ColorSync Manager calls your procedure when the profile is created, initialized, opened, read, updated, or closed, passing a command constant that specifies the current command. Your profile access procedure must be able to respond to each of the following command constants (described in "Profile Access Procedure Operation Codes" in Advanced Color Imaging Reference):

enum {
   cmOpenReadAccess= 1,
   cmOpenWriteAccess= 2,
   cmReadAccess   = 3,
   cmWriteAccess  = 4,
   cmCloseAccess  = 5,
   cmCreateNewAccess= 6,
   cmAbortWriteAccess= 7,
   cmBeginAccess  = 8,
   cmEndAccess    = 9
};
The profile access procedure shown in Listing 4-14, MyCMProfileAccessProc, consists of a single switch statement, which calls the appropriate routine based on the value of the command parameter. Each of the nine routines called by MyCMProfileAccessProc is described and listed in the sections that follow Listing 4-14, and each refers back to Listing 4-14.

Listing 4-14 Responding to a procedure-based profile command

pascal OSErr MyCMProfileAccessProc (long command,
                           long offset,
                           long *sizePtr,
                           void *dataPtr,
                           void *refConPtr)
{
   OSErr err = noErr;
   switch (command)
   {
      case cmBeginAccess:
         err = DoBeginAccess(refConPtr);
         break;
         
      case cmCreateNewAccess:
         err = DoCreateNewAccess(refConPtr);
         break;
      
      case cmOpenReadAccess:
         err = DoOpenReadAccess(refConPtr);
         break;
      
      case cmOpenWriteAccess:
         err = DoOpenWriteAccess(sizePtr, refConPtr);
         break;
         
      case cmReadAccess:
         err = DoReadAccess(offset, sizePtr, dataPtr, refConPtr);
         break;
         
      case cmWriteAccess:
         err = DoWriteAccess(offset, sizePtr, dataPtr, refConPtr);
         break;
      
      case cmCloseAccess:
         err = DoCloseAccess(refConPtr);
         break;
      
      case cmAbortWriteAccess:
         err = DoAbortWriteAccess(refConPtr);
         break;
      
      case cmEndAccess:
         err = DoEndAccess(refConPtr);
         break;
      
      default:
         err = paramErr;
         break;
   }

   return err;
}
The MyCMProfileAccessProc routine passes its parameter data as necessary to the routines it calls. The parameters have the following values:

command
A command value indicating the operation to perform. The possible values for command constants are shown elsewhere in this section.
offset
For read and write operations, the offset from the beginning of the profile at which to read or write data.
size
For the cmReadAccess and cmWriteAccess command constants, a pointer to a value indicating the number of bytes to read or write; for the cmOpenWriteAccess command, the total size of the profile. On output after reading or writing, the actual number of bytes read or written.
data
A pointer to a buffer containing data to read or write. On output, for a read operation, contains the data that was read.
refConPtr
A reference constant pointer that can store private data for the MyCMProfileAccessProc procedure. For example, Listing 4-12 shows how to set up a location structure for procedure access to a resource-based profile. That routine sets the location structure's refCon field to a pointer to a MyResourceLocRec structure (page 4-58). That same structure pointer is passed to the MyCMProfileAccessProc routine in the refConPtr parameter and provides access to all the stored information about the resource location.

Handling the Begin Access Command

When the ColorSync Manager needs to signal that it is time to prepare for a session of reading, writing, or both for a procedure-based profile, it invokes the specified profile access procedure with the cmBeginAccess command. This happens, for example, when your application calls the CMOpenProfile routine, specifying as a location a procedure-based profile.

When your profile-access procedure is called with the cmBeginAccess command, it performs any required initialization or validation tasks, such as determining whether the data pointed to by the refcon parameter is valid. If your procedure returns an error (any value except noErr), the ColorSync Manager will not call your profile access procedure again.

For the cmBeginAccess command, the sample profile access procedure shown in Listing 4-14 calls the DoBeginAccess routine, shown in Listing 4-15. DoBeginAccess interprets the refcon parameter as a MyResourceLocPtr type. If the parameter does not have a resource type of kProcResourceType, DoBeginAccess returns an invalid profile error, which effectively cancels the procedure-based profile access.

Listing 4-15 Handling the begin access command

static OSErr DoBeginAccess (void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   
   resourceInfo->resFileRef = 0;

   if (resourceInfo->resType != kProcResourceType)
      err = cmInvalidProfileLocation;
   else
      err = noErr;
   
   return err;
}

Handling the Create New Access Command

When the ColorSync Manager needs to signal that it is time to allocate any required new resources for a procedure-based profile, it invokes the specified profile access procedure with the cmBeginAccess command, as described in "Handling the Begin Access Command" (page 4-64). This happens, for example, when your application calls the CMCopyProfile routine, specifying as a location a procedure-based profile.

If your profile access procedure returns without error, ColorSync calls the procedure again with the cmCreateNewAccess command. Your procedure then creates a new data stream for the actual physical location of the profile. The size of the profile is not known at this point.

For the cmCreateNewAccess command, the sample profile access procedure shown in Listing 4-14 calls the DoCreateNewAccess routine. DoCreateNewAccess interprets the refcon parameter as a MyResourceLocPtr type and calls the Toolbox routine FSpCreateResFile to create an empty resource fork based on the file specification provided by the MyResourceLocPtr type. If the resource fork does not already exist and cannot be created, DoCreateNewAccess returns an error.

For this example, the file type for a resource-based profile was chosen arbitrarily to be 'rprf'.

Listing 4-16 Handling the create new access command

OSErr DoCreateNewAccess (void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   
   FSpCreateResFile(&(resourceInfo->resFileSpec), '????', 'rprf', 0);
   err = ResError();
   if (err == dupFNErr)
      err = noErr;
      
   return err;
}

Handling the Open Read Access Command

When your application calls a ColorSync routine to read information from a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenReadAccess command. ColorSync calls your profile access routine once for each read session. The sample profile access procedure shown in Listing 4-14 calls the DoOpenReadAccess routine.

The DoOpenReadAccess routine shown in Listing 4-17 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to open the resource fork for the resource-based profile with read permission. If it can open the resource file, DoOpenReadAccess then attempts to load the profile resource.

The DoOpenReadAccess routine shows good citizenship by saving the current resource file before performing its operations and restoring the resource file afterward.

Listing 4-17 Handling the open read access command

static OSErr DoOpenReadAccess (void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   short          currentResFile;
   
   /* save current resource file */
   currentResFile = CurResFile();
   
   /* open the file's resource fork */
   resourceInfo->resFileRef = FSpOpenResFile(&(resourceInfo->resFileSpec), fsRdPerm);
   err = ResError();
   
   /* get the resource handle, but don't force it to be loaded into memory */
   if (!err)
   {
      SetResLoad(false);
      resourceInfo->resHandle = GetResource(resourceInfo->resType,
                                    resourceInfo->resID);
      err = ResError();
      SetResLoad(true);
   }

   /* restore previous resource file */
   UseResFile(currentResFile);
   
   return err;
}

Handling the Open Write Access Command

When the ColorSync Manager needs to signal that a procedure-based profile should be opened for writing, it invokes the specified profile access procedure with the cmOpenWriteAccess command. This happens, for example, when your application calls the CMUpdateProfile routine to update a procedure-based profile. The sample profile access procedure shown in Listing 4-14 calls the DoOpenWriteAccess routine.

The DoOpenWriteAccess routine shown in Listing 4-18 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to open the resource fork for the resource-based profile with read/write permission. If it can open the resource file, DoOpenWriteAccess then attempts to open the specified profile resource. If it can't open the resource, DoOpenWriteAccess creates a new resource. It then sets the size of the resource based on the passed setProfileSize pointer value and updates the resource file.

The DoOpenWriteAccess routine shows good citizenship by saving the current resource file before performing its operations and restoring the resource file afterward.

Note
If the cmOpenWriteAccess command succeeds, ColorSync guarantees an eventual call to the profile access procedure with the cmCloseAccess command, possibly after multiple cmWriteAccess commands, and possibly after a cmAbortWriteAccess command.
Listing 4-18 Handling the open write access command

static OSErr DoOpenWriteAccess (long *setProfileSize, void *refcon) 
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   Size           resourceSize;
   short          currentResFile;
   
   /* save current resource file */
   currentResFile = CurResFile();

   /* open the file's resource fork */
   resourceInfo->resFileRef = FSpOpenResFile(&(resourceInfo->resFileSpec),
                                    fsRdWrPerm);
   err = ResError();
   
   /* get the resource handle, but don't force it to be loaded into memory */
   if (!err)
   {
      SetResLoad(false);
      resourceInfo->resHandle = GetResource(resourceInfo->resType,
                                    resourceInfo->resID);
      err = ResError();
      SetResLoad(true);
   }
   
   /* call GetResourceSizeOnDisk to see if resource is already there */
   if (!err)
   {
      /* get size of the resource */
      resourceSize = GetResourceSizeOnDisk(resourceInfo->resHandle);
      err = ResError();
   }
   
   /* if the above call to GetResourceSizeOnDisk returns resNotFound, */
   /* then we need to create a new resource */
   if (err == resNotFound)
   {
      /* allocate a temporary handle just so that we can call AddResource */
      resourceInfo->resHandle = NewHandle(sizeof(long));
      err = MemError();
      
      /* add resource to the file and release the temp handle */
      if (!err)
      {
         AddResource(resourceInfo->resHandle, resourceInfo->resType, 
                   resourceInfo->resID, resourceInfo->resName);
         err = ResError();
         ReleaseResource(resourceInfo->resHandle);
      }
      
   /* get the resource handle, but don't force it to be loaded into memory */
      if (!err)
      {
         SetResLoad(false);
         resourceInfo->resHandle = GetResource(resourceInfo->resType,
                                       resourceInfo->resID);
         err = ResError();
         SetResLoad(true);
      }
   }

   /* change the resource size to fit the profile */
   if (!err)
   {
      SetResourceSize(resourceInfo->resHandle, *setProfileSize);
      err = ResError();
   }
   
   /* force an update of the resource file */
   if (!err)
   {
      UpdateResFile(resourceInfo->resFileRef);
      err = ResError();
   }
   
   /* restore previous resource file */
   UseResFile(currentResFile);
   
   return err;
}

Handling the Read Access Command

When your application calls a ColorSync routine to read information from a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenReadAccess command, as described in "Handling the Open Read Access Command" (page 4-66). Your profile access routine can be called with the cmReadAccess command at any time after the cmOpenReadAccess command is called. When the sample profile access procedure shown in Listing 4-14 receives the cmReadAccess command, it calls the DoReadAccess routine.

The DoReadAccess routine shown in Listing 4-19 uses the refcon parameter, interpreted as type MyResourceLocPtr, to get a resource handle for the resource-based profile. From other parameters, it gets values for the offset at which to start reading, the number of bytes to read, and a pointer to a buffer in which to store the data that it reads. It then calls the Toolbox routine ReadPartialResource to do the actual reading.

If an error occurs while reading, DoReadAccess returns the error.

Listing 4-19 Handling the read access command

static OSErr DoReadAccess ( long offset,
                     long *sizePtr,
                     void *dataPtr,
                     void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;

   ReadPartialResource(resourceInfo->resHandle,
                     offset, dataPtr, *sizePtr);
   err = ResError();
   
   return err;
}

Handling the Write Access Command

When your application calls a ColorSync routine that writes information to a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenWriteAccess command. The DoOpenWriteAccess routine shown in Listing 4-18 performs certain operations to prepare to write a resource-based profile.

Your profile access routine can be called with the cmWriteAccess command at any time after the cmOpenWriteAccess command is called. When the sample profile access procedure shown in Listing 4-14 receives the cmWriteAccess command, it calls the DoWriteAccess routine.

The DoWriteAccess routine shown in Listing 4-20 uses the refcon parameter, interpreted as type MyResourceLocPtr, to get a resource handle for the resource-based profile. From other parameters, it gets values for the offset at which to start writing, the number of bytes to write, and a pointer to a buffer from which to get the data that it writes. It then calls the Toolbox routine WritePartialResource to do the actual writing.

If an error occurs while writing, DoWriteAccess returns the error.

Note
After ColorSync calls the profile access procedure with the cmWriteAccess command, ColorSync is guaranteed to eventually call the profile access procedure with the cmCloseAccess command--possibly after additional calls with the cmWriteAccess command, and possibly after a call with the cmAbortWriteAccess command.
Listing 4-20 Handling the write access command

static OSErr DoWriteAccess (long offset,
                     long *sizePtr,
                     void *dataPtr,
                     void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   
   WritePartialResource(resourceInfo->resHandle,
                     offset, dataPtr, *sizePtr);
   err = ResError();

   return err;
}

Handling the Close Access Command

The ColorSync Manager calls your profile access procedure with the cmCloseAccess command to indicate that reading or writing is finished for the moment. A cmCloseAccess command can be followed by a cmOpenReadAccess command to begin reading again, a cmOpenWriteAccess command to begin writing again, or a cmEndAccess command to terminate the procedure-based profile access.

The sample profile access procedure shown in Listing 4-14 calls the DoCloseAccess routine.

The DoCloseAccess routine shown in Listing 4-21 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to close and update the resource file for the resource-based profile. If DoCloseAccess is unsuccessful, it returns an error value.

Listing 4-21 Handling the close access command

static OSErr DoCloseAccess (void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   
   /* close and update resource file */
   if (resourceInfo->resFileRef)
   {   
      CloseResFile(resourceInfo->resFileRef);
      err = ResError();
      resourceInfo->resFileRef = 0;
   }
   else err = paramErr;
   
   return err;
}

Handling the Abort Write Access Command

If an error occurs between a cmOpenWriteAccess command and a cmCloseAccess command, the ColorSync Manager calls your profile access procedure with the cmAbortWriteAccess command. This allows your access procedure to perform any cleanup necessary for the partially written profile.

For the cmAbortWriteAccess command, the sample profile access procedure shown in Listing 4-14 calls the DoAbortWriteAccess routine.

The DoAbortWriteAccess routine shown in Listing 4-22 uses information from the refcon parameter, interpreted as type MyResourceLocPtr, to call the Toolbox routine RemoveResource to delete the partially written resource. If DoAbortWriteAccess is unsuccessful, it returns an error value.

Note
The ColorSync Manager will call your profile access procedure with the cmCloseAccess command after a cmAbortWriteAccess command.
Listing 4-22 Handling the abort write access command

static OSErr DoAbortWriteAccess (void *refcon)
{
   OSErr          err;
   MyResourceLocPtrresourceInfo = refcon;
   
   /* delete the resource that we started */
   if (resourceInfo->resHandle)
   {
      RemoveResource(resourceInfo->resHandle);
      err = ResError();
   }
   else err = paramErr;
   
   return err;
}

Handling the End Access Command

When access to a procedure-based profile is complete, the ColorSync Manager calls your profile access procedure with the cmEndAccess command. This allows your procedure to do any final cleanup, such as freeing memory allocated by the procedure.

For the cmEndAccess command, the sample profile access procedure shown in Listing 4-14 calls the DoEndAccess routine. Because there is no additional memory to free or other cleanup to take care of, the DoEndAccess routine shown in Listing 4-23 does nothing.

Note
The MyCreateProcedureProfileAccess routine, shown in Listing 4-12, does allocate memory, which is freed by a call to the MyDisposeProcedureProfileAccess routine, shown in Listing 4-13. Your application calls the MyCreateProcedureProfileAccess routine before calling a ColorSync routine such as CMCopyProfile with a procedure-based profile. After the copy is complete, your application calls the MyDisposeProcedureProfileAccess routine to perform any necessary deallocation.
Listing 4-23 Handling the end access command

pascal OSErr DoEndAccess (void *refcon)
{
   OSErr err = noErr;
   
   return err;
}

Previous Book Contents Book Index Next

© Apple Computer, Inc.
13 NOV 1996