Important: The information in this document is obsolete and should not be used for new development.
Extracting Profiles Embedded in Pictures
To color match or gamut check a picture embedded in a document, your application must first extract the source profile used to create the image, if the profile is embedded in the document, then open a reference to the profile. This process entails locating and identifying the profile for the image within the document and transferring the profile data from the document file.
To extract an embedded profile, your application can use the
- Note
- If you use the high-level
NCMDrawMatchedPicturefunction, you do not need to extract the source profile from the PICT file.![]()
CMUnflattenProfilefunction. This function takes a pointer to a low-level data-transfer function that your application must supply to transfer the profile data from the document containing it. This function assumes that your low-level data-transfer function is informed about the context of the profile. After all of the profile data has been transferred, theCMUnflattenProfilefunction returns the file specification for the profile.When your application calls the
CMUnflattenProfilefunction, the ColorSync Manager uses the Component Manager to pass the pointer to your low-level data-transfer function along with the reference constant your application can use as it desires. If available, the preferred CMM calls your low-level data-transfer function. (If the preferred CMM is not available, the ColorSync Manager follows the CMM selection algorithm described in "How the ColorSync Manager Selects a CMM" (page 4-7), to determine which CMM to use.) The CMM calls your low-level data-transfer function, directing it to open the file containing the profile, read segments of the profile data, and return the data to the CMM's calling function.The CMM communicates with your low-level data-transfer function using a command parameter to identify the operation to perform. To facilitate the transfer of profile data from the file to the CMM, the CMM passes to your function a pointer to a data buffer for data, the size in bytes of the profile data your function should return, and the reference constant passed from the calling application.
On return, your function passes to the CMM segments of the profile data and the number of bytes of profile data you actually return.
Listing 4-7 and Listing 4-8 show portions of a sample application called CSDemo, available as part of the ColorSync SDK. You can find the complete sample application at the following web site:
ftp://ftp.apple.com/developer/Development_Kits/These listings assume that all variables beginning with a lowercase letter "g" are global variables previously defined. The application uses global variables to pass data between functions that do not include reference constant parameters. The listings implement two primary steps:
- count the profiles in the PICT file
- extract a profile
Step 1: Count the Profiles in the PICT File
Given apicHandlevalue to the picture containing the embedded profile, the sample code shown in Listing 4-7 counts the number of profiles in the picture to identify the profile to extract.The
MyCountProfilesInPicHandlefunction sets up the port and its bottlenecks and initializes its global counter, which holds a single count summing both ColorSync 1.0 profiles and version 2.x profiles.MyCountProfilesInPicHandlecounts the number of profiles as the picture is being drawn, using the MyCountProfilesCommentProc bottleneck procedure.MyCountProfilesInPicHandledoesn't use any other bottlenecks, so it defines nonoperational routines for them. For example, theTextProcbottleneck can be defined as follows:
static pascal void TextProc (short byteCount, Ptr textAddr, Point numer, Point denom);MyCountProfilesInPicHandlecalls its ownMyDrawPicHandleUsingBottleneckfunction, not shown here, to draw the picture using the bottleneck routines. Because it must increment thegCountglobal counter for both ColorSync 1.0 profiles and version 2.x profiles,MyCountProfilesCommentProcchecks for both types of profiles.Listing 4-7 Counting the number of profiles in a picture
CMError MyCountProfilesInPicHandle (PicHandle pict, unsigned long *count) { OSErr err = noErr; CQDProcs procs; /* set up bottleneck for picComments so we can count the profiles */ SetStdCProcs(&procs); procs.textProc= NewQDTextProc (MyNoOpTextProc); procs.lineProc= NewQDLineProc (MyNoOpLineProc); procs.rectProc= NewQDRectProc (MyNoOpRectProc); procs.rRectProc = NewQDRRectProc (MyNoOpRRectProc); procs.ovalProc = NewQDOvalProc (MyNoOpOvalProc); procs.arcProc = NewQDArcProc (MyNoOpArcProc); procs.polyProc = NewQDPolyProc (MyNoOpPolyProc); procs.rgnProc = NewQDRgnProc (MyNoOpRgnProc); procs.bitsProc = NewQDBitsProc (MyNoOpBitsProc); procs.commentProc = NewQDCommentProc (MyCountProfilesCommentProc); procs.txMeasProc = NewQDTxMeasProc (MyNoOpTxMeasProc); /* initialize the global counter to be incremented by the commentProc*/ gCount = 0; /* draw the picture and count the profiles while drawing */ err = MyDrawPicHandleUsingBottlenecks (pict, procs, nil); /* obtain the result from the count global variable */ *count = gCount; /* clean up and return*/ DisposeRoutineDescriptor(procs.textProc); DisposeRoutineDescriptor(procs.lineProc); DisposeRoutineDescriptor(procs.rectProc); DisposeRoutineDescriptor(procs.rRectProc); DisposeRoutineDescriptor(procs.ovalProc); DisposeRoutineDescriptor(procs.arcProc); DisposeRoutineDescriptor(procs.polyProc); DisposeRoutineDescriptor(procs.rgnProc); DisposeRoutineDescriptor(procs.bitsProc); DisposeRoutineDescriptor(procs.commentProc); DisposeRoutineDescriptor(procs.txMeasProc); } pascal void MyCountProfilesCommentProc (short kind, short dataSize, Handle dataHandle) { long selector; switch (kind) { case cmBeginProfile gCount ++; /* we found a ColorSync 1.0 profile */ /* increment the counter*/ break; case cmComment; if (dataSize <= 4) break;/* dataSize is too small for selector so break and get the selector from the first long */ selector = *((long *)(*dataHandle)); if (selector == cmBeginProfileSel) gCount ++; /* we found a version 2 profile; increment the counter */ break; } }Step 2: Extract the Profile
This part of the sample application identifies the profile to flatten, flattens the profile, and creates a temporary profile disposing of the original one. To perform these tasks, the code must again draw the picture using the bottleneck routines.Part A: Calling the Unflatten Function
Listing 4-8 shows the MyGetIndexedProfileFromPicHandle entry point function that drives the process of unflattening the profile. The function creates a universal procedure pointer (UPP),MyflattenUPP, that points to the low-level data-transfer procedure.A PICT handle may contain more than one profile. To identify the profile to unflatten, the
MyGetIndexedProfileFromPicHandlefunction contains anindexparameter that passes in the profile's index. The function stores the index in the global variablegIndexso that the value is accessible by the application's other functions that check for the correct profile and extract it. Then, the function calls theCMUnflattenProfilefunction, passing it theMyflattenUPPpointer. This invokes theMyUnflattenProcfunction shown in Listing 4-9.After calling
CMUnflattenProfile, the MyGetIndexedProfileFromPicHandle function calls theCMOpenProfilefunction to open a reference to the file-based profile; then it callsCMCopyProfileto create a temporary profile. Finally, the function disposes of the original profile. To adhere to the copyright protection for embedded profiles specified by theflagsfield in the profile header, MyGetIndexedProfileFromPicHandle creates a temporary profile and disposes of the original.Listing 4-8 Calling the unflatten profile function to extract an embedded profile
CMError MyGetIndexedProfileFromPicHandle (PicHandle pict, unsigned long index, CMProfileRef *prof, CMProfileLocation *profLoc) { unsigned long refCon; CMFlattenUPP MyflattenUPP; CMError cmErr = noErr; Boolean preferredCMMNotFound; FSSpec tempSpec; CMProfileRef tempProf; CMProfileLocation tempProfLoc; /* create a universal procedure pointer for unflatten procedure */ MyflattenUPP = NewCMFlattenProc(MyUnflattenProc); /* following assumes that index <= count */ refCon = (unsigned long) pict; gIndex = index; /* next call invokes the MyUnflattenProc shown in Listing 4-9 */ cmErr = CMUnflattenProfile(&tempSpec, MyflattenUPP,(void*)&refCon, &preferredCMMNotFound); DisposeRoutineDescriptor(MyflattenUPP); if (cmErr) return cmErr; tempProfLoc.locType = cmFileBasedProfile; tempProfLoc.u.fileLoc.spec = tempSpec; cmErr = CMOpenProfile(&tempProf, &tempProfLoc); if (cmErr) return cmErr; cmErr = CMCopyProfile(prof, profLoc, tempProf); cmErr = CMCloseProfile(tempProf); cmErr = FSpDelete(&tempSpec); return cmErr; }Part B: Calling the Unflatten Function
When the code in MyGetIndexedProfileFromPicHandle (Listing 4-8) calls theCMUnflattenProcfunction, passing it a pointer to theMyUnflattenProcfunction, theMyUnflattenProcfunction (Listing 4-9) is called by the CMM to perform the low-level profile data transfer from the document file.When the CMM calls this function with an open command, the function initializes global variables, creates a graphics world, and installs bottleneck procedures in the graphics world. The only bottleneck procedure actually used is
MyUnflattenProfilesCommentProc, which checks the picture comments as the picture is drawn offscreen to identify the desired profile.When the CMM calls the MyUnflattenPro
cfunction with a read command, the function reads the appropriate segment of data from a chunk and returns it. To accomplish this, it calls the MyDrawPicHandleUsingBottlenecks function with the appropriate bottleneck procedure installed. In turn, this invokes theMyUnflattenProfilesCommentProcshown in Listing 4-10.When the CMM calls MyUnflattenProc with a close command, the function releases any memory it allocated and disposes of the graphics world and bottlenecks.
Listing 4-9 The unflatten procedure
pascal OSErr MyUnflattenProc (long command, long *sizePtr, void *dataPtr, void *refConPtr) { OSErr err = noErr; static CQDProcs procs; static GWorldPtr offscreen; PicHandle pict; switch (command) { case cmOpenReadSpool: err = NewSmallGWorld(&offscreen); if (err) return err; SetStdCProcs(&procs); procs.textProc = NewQDTextProc (MyNoOpTextProc); procs.lineProc = NewQDLineProc (MyNoOpLineProc); procs.rectProc = NewQDRectProc (MyNoOpRectProc); procs.rRectProc = NewQDRRectProc (MyNoOpRRectProc); procs.ovalProc = NewQDOvalProc (MyNoOpOvalProc); procs.arcProc = NewQDArcProc (MyNoOpArcProc); procs.polyProc = NewQDPolyProc (MyNoOpPolyProc); procs.rgnProc = NewQDRgnProc (MyNoOpRgnProc); procs.bitsProc = NewQDBitsProc(MyNoOpBitsProc); procs.commentProc = NewQDCommentProc (MyUnflattenProfilesCommentProc); procs.txMeasProc = NewQDTxMeasProc (MyNoOpTxMeasProc); gChunkBaseHndl = nil; gChunkIndex = 0; gChunkOffset = 0; gChunkSize = 0; break; case cmReadSpool: if (gChunkOffset > gChunkSize)/* if we overread the last chunk */ { return ioErr; /* use system I/O error value */ } if (gChunkOffset == gChunkSize)/* if we used up the last chunk */ { if (gChunkBaseHndl !=nil) { HUnlock(gChunkBaseHndl);/* dispose of the previous chunk */ DisposeHandle(gChunkBaseHndl); gChunkBaseHndl = nil; } gChunkIndex++; /* read in a new chunk */ gChunkOffset = 0; gCount = 0; gChunkCount = 0; pict = *((PicHandle *)refConPtr); err = MyDrawPicHandleUsingBottlenecks (pict, procs, offscreen); /* this invokes MyUnflattenProfilesCommentProc shown in Listing 4-10 */ if (gChunkBaseHndl==nil)/* check to see if we're overread */ return ioErr; /* if so, return system I/O error value */ HLock(gChunkBaseHndl); } if (gChunkOffset < gChunkSize) { *sizePtr = MIN(gChunkSize-gChunkOffset, *sizePtr); BlockMove((Ptr)(&((*gChunkBaseHndl)[gChunkOffset])), (Ptr)dataPtr, *sizePtr); gChunkOffset += (*sizePtr); } break; case cmCloseSpool: if (gChunkBaseHndl != nil) { HUnlock(gChunkBaseHndl);/* dispose of the previous chunk */ DisposeHandle(gChunkBaseHndl); gChunkBaseHndl = nil; } DisposeGWorld(offscreen); DisposeRoutineDescriptor(procs.TextProc); DisposeRoutineDescriptor(procs.LineProc); DisposeRoutineDescriptor(procs.RectProc); DisposeRoutineDescriptor(procs.RRectProc); DisposeRoutineDescriptor(procs.OvalProc); DisposeRoutineDescriptor(procs.ArcProc); DisposeRoutineDescriptor(procs.PolyProc); DisposeRoutineDescriptor(procs.RgnProc); DisposeRoutineDescriptor(procs.BitsProc); DisposeRoutineDescriptor(procs.MyUnflattenProfilesCommentProc); DisposeRoutineDescriptor(procs.txMeasProc); break; default: break; } return err; }Part C: Calling the Comment Procedure
When theMyUnflattenProcfunction'sMyDrawPicHandleUsingBottlenecksfunction calls theMyUnflattenProfilesCommentProcfunction, the function shown in Listing 4-10 finds the profile identified by the index, finds the correct segment of data within the profile, and stores the data in thegChunkBaseHndlglobal variable.Listing 4-10 The comment procedure
pascal void MyUnflattenProfilesCommentProc (short kind, short dataSize, Handle dataHandle) { long selector; OSErr err; if (gChunkBaseHndl != nil) return; /* the handle is in use; this shouldn't happen */ if (gCount > gIndex) return; /* we have already found the profile */ switch (kind) { case cmBeginProfile: gCount ++; /* we found a version 1 profile */ gChunkCount = 1;/* v1 profiles should only have 1 chunk */ if (gCount != gIndex) break; /* this is not the profile we're looking for */ if (gChunkCount != gChunkIndex) break; /* this is not the chunk we're looking for */ gChunkBaseHndl = dataHandle; err = HandToHand(&gChunkBaseHndl); gChunkSize = dataSize; gChunkOffset = 0; break; case cmComment: if (dataSize <= 4) break; /* the dataSize too small for selector, so break */ selector = *((long *)(*dataHandle)); /* get the selector from the first long in data */ switch (selector) { case cmBeginProfileSel: gCount ++; /* we found a version 2 profile */ gChunkCount = 1; if (gCount != gIndex) break; /* this is not the profile we're looking for */ if (gChunkCount!=gChunkIndex) break; /* this is not the chunk we're looking for */ gChunkBaseHndl = dataHandle; err = HandToHand(&gChunkBaseHndl); gChunkSize = dataSize; gChunkOffset = 4; break; case cmContinueProfileSel: gChunkCount ++; if (gCount != gIndex) break; /* this is not the profile we're looking for */ if (gChunkCount!=gChunkIndex) break; /* this is not the chunk we're looking for */ gChunkBaseHndl = dataHandle; err = HandToHand(&gChunkBaseHndl); gChunkSize = dataSize; gChunkOffset = 4; break; case cmEndProfileSel: /* check to see if we're overreading */ gChunkCount = 0; break; } break; } }