PATHMac OS 8 Developer Documentation > Mutlimedia and Graphics > ColorSync Manager >

Managing Color With ColorSync


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 .

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

The sample code in Listing 3-18 to Listing 3-29 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;
    CMProfileAccessUPP  proc;
    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 3-18 sets up a CMProfileLocation structure for procedure access to a resource-based profile. The MyDisposeProcedureProfileAccess routine, shown in Listing 3-19 , 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 Manager routine such as CMCopyProfile , you call the MyCreateProcedureProfileAccess routine to set up a CMProfileLocation structure that you can pass to the ColorSync Manager 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 3-20 .
  2. During the course of its operations, the ColorSync Manager may call your profile-access procedure many times.
  3. After the ColorSync Manager 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 3-18 , 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 3-18 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 3-18 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               theErr = noErr;
    MyResourceLocPtr    resourceInfo;
    
    /* Allocate memory for our private resource info structure. */
    resourceInfo = (MyResourceLocPtr) NewPtrClear(sizeof(MyResourceLocRec));
    if (!resourceInfo)
        theErr = MemError();
        
    if (!theErr)
    {
        /* 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]+1);
        
        /* set up the profile location structure */
        profileLocation->locType = cmProcedureBasedProfile;
        profileLocation->u.procLoc.refCon = (void*) resourceInfo;
        profileLocation->u.procLoc.proc = resourceInfo->proc;
    }
    return theErr;
}

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 ( Listing 3-19 ) to dispose of any memory allocated by the MyCreateProcedureProfileAccess routine ( Listing 3-18 ).

Listing 3-19 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 information on the procedure declaration for a profile access procedure, see MyCMProfileAccessProc . 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, which are described in Profile Access Procedure Operation Codes :

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 3-20 , 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 3-20 , and each refers back to Listing 3-20 .

Listing 3-20 Responding to a procedure-based profile command

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

    return theErr;
}

Note that 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 3-18 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, which is described in Defining a Data Structure for a Resource-Based Profile . 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 your application calls the CMOpenProfile routine, specifying as a location a procedure-based profile, the ColorSync Manager invokes your specified profile access procedure with the cmBeginAccess command. This gives your procedure an opportunity to perform 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 3-20 calls the DoBeginAccess routine, shown in Listing 3-21 . 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 3-21 Handling the begin access command

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

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

Handling the Create New Access Command

When your application calls the CMCopyProfile or CMUpdateProfile routine, specifying as a location a procedure-based profile, the ColorSync Manager invokes the specified profile access procedure with the cmBeginAccess command, as described in Handling the Begin Access Command .

If your profile access procedure returns without error, ColorSync calls the procedure again with the cmCreateNewAccess command. Your procedure should create 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 3-20 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.

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

Listing 3-22 Handling the create new access command

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

Handling the Open Read Access Command

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

The DoOpenReadAccess routine shown in Listing 3-23 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 3-23 Handling the open read access command

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

    /* Restore previous resource file. */
    UseResFile(currentResFile);
    
    return theErr;
}

Handling the Open Write Access Command

When your application calls the CMUpdateProfile routine to update a procedure-based profile or the CMCopyProfile routine to copy a profile, the ColorSync Manager calls your profile access procedure with the cmOpenWriteAccess command. The sample profile access procedure shown in Listing 3-20 calls the DoOpenWriteAccess routine.

The DoOpenWriteAccess routine shown in Listing 3-24 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, the ColorSync Manager 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 3-24 Handling the open write access command

static OSErr DoOpenWriteAccess (long *setProfileSize, void *refcon)
{
    OSErr               theErr;
   MyResourceLocPtr    resourceInfo = refcon;
    Size                resourceSize;
    short               currentResFile;
    
    /* Save current resource file. */
    currentResFile = CurResFile();

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

    /* Change the resource size to fit the profile. */
    if (!theErr)
    {
        SetResourceSize(resourceInfo->resHandle, *setProfileSize);
        theErr = ResError();
    }
    
    /* Force an update of the resource file. */
    if (!theErr)
    {
        UpdateResFile(resourceInfo->resFileRef);
        theErr = ResError();
    }
    
    /* Restore previous resource file. */
    UseResFile(currentResFile);
    
    return theErr;
}

Handling the Read Access Command

When your application calls a ColorSync Manager 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 . 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 3-20 receives the cmReadAccess command, it calls the DoReadAccess routine.

The DoReadAccess routine shown in Listing 3-25 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 3-25 Handling the read access command

static OSErr DoReadAccess ( long offset,
                            long *sizePtr,
                            void *dataPtr,
                            void *refcon)
{
    OSErr               theErr;
   MyResourceLocPtr    resourceInfo = refcon;

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

Handling the Write Access Command

When your application calls the CMUpdateProfile routine to update a procedure-based profile, the ColorSync Manager first calls your profile access procedure with the cmOpenWriteAccess command. The DoOpenWriteAccess routine shown in Listing 3-24 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 3-20 receives the cmWriteAccess command, it calls the DoWriteAccess routine.

The DoWriteAccess routine shown in Listing 3-26 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 3-26 Handling the write access command

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

    return theErr;
}

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 3-20 calls the DoCloseAccess routine.

The DoCloseAccess routine shown in Listing 3-27 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 3-27 Handling the close access command

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

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 3-20 calls the DoAbortWriteAccess routine.

The DoAbortWriteAccess routine shown in Listing 3-28 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 3-28 Handling the abort write access command

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

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 3-20 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 3-29 does nothing.

Note

The MyCreateProcedureProfileAccess routine, shown in Listing 3-18 , does allocate memory, which is freed by a call to the MyDisposeProcedureProfileAccess routine, shown in Listing 3-19 . Your application calls the MyCreateProcedureProfileAccess routine before calling a ColorSync Manager 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 3-29 Handling the end access command

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

© 1988-1999 Apple Computer, Inc. — (Last Updated 20 Jan 99)