A Drawing Sample


The sample code for this chapter includes a program called Sunset, which arranges the word Sunset on a semi-circle. As each glyph is drawn, it is also used as a clipping path, and a shaded background is drawn inside the clipped path. The output of the program is shown in Figure 11.4.

Figure 11.4. The Output of the Sunset Sample


For this sample, ATSUI is used to convert the characters into glyphs and typeset those glyphs along a line. Instead of drawing the glyphs as ATSUI has arranged them, however, the code will use Quartz 2D to arrange and draw the glyphs in a context. The sample code contains the complete code for the Sunset sample, two routines are examined here. The first is the routine that extracts glyph information from the ATSUI layout. The second is the routine that arranges and draws the glyphs on the context.

Listing 11.1 is the routine that extracts glyph information from ATSUI.

Listing 11.1. Obtaining Glyphs for the Sunset Sample

void CreateGlyphDrawingData() {     // Use ATSUGetUnjustifiedBounds to find out the full length     // of the text.     ATSUTextMeasurement before, after, fixedAscent, fixedDescent;     ATSUGetUnjustifiedBounds(            gTextLayout,            kATSUFromTextBeginning,            kATSUToTextEnd,            &before,            &after,            &fixedAscent,            &fixedDescent);     float width = FixedToFloat(after - before);     // Call GetGlyphInfo once to figure out how big     // a buffer we need to hold the glyph info     ByteCount bufferSize;     ATSUGetGlyphInfo ( gTextLayout, kATSUFromTextBeginning,             kATSUToTextEnd, &bufferSize, NULL);     // Allocate the memory for the glyph info     ATSUGlyphInfoArray *glyphInfo =            (ATSUGlyphInfoArray *) malloc(bufferSize);     // Now that we have a buffer ask for the arranged glyphs     // info "for real"     ATSUGetGlyphInfo( gTextLayout, kATSUFromTextBeginning,             kATSUToTextEnd, &bufferSize, glyphInfo);     // Loop over the glyphs and create our drawing info based on     // their spacing     gGlyphDrawingInfo.numGlyphs = glyphInfo->numGlyphs;     for(short glyphCtr = 0;             glyphCtr < glyphInfo->numGlyphs; glyphCtr++) {             float glyphWidth;             // We use the spacing information to calculate the             // advance width of each glyph.             if(glyphCtr == (glyphInfo->numGlyphs - 1)) {                     glyphWidth = (width -                             glyphInfo->glyphs[glyphCtr].idealX);             } else {                     glyphWidth = (glyphInfo->glyphs[glyphCtr + 1].idealX                             - glyphInfo->glyphs[glyphCtr].idealX);             }             // We calculate the position of the middle of each advance             // width. From the position we calculate the             // angular position for the glyph             float glyphPosition = glyphInfo->glyphs[glyphCtr].idealX +                     (glyphWidth / 2.0);             float scaledAngle = (glyphPosition / width) * 180.0;             // Record the drawing info in our static array.             gGlyphDrawingInfo.glyphInfo[glyphCtr].glyphID =                     glyphInfo->glyphs[glyphCtr].glyphID;             gGlyphDrawingInfo.glyphInfo[glyphCtr].glyphWidth =                     glyphWidth;             gGlyphDrawingInfo.glyphInfo[glyphCtr].angle =                     (90.0 - scaledAngle);     }     free(glyphInfo); } 

This code sample creates an ATSUI text layout that contains the string "Sunset" and stores that layout in the global gTextLayout where it can be used repeatedly. The code treats the text as a single, long line. The first thing this routine does is measure the width of the text. The width returned is the width of the drawn glyphs as they would appear before the computer applies justification (e.g., center justification) to them. We will use this width to space the glyphs in their corresponding positions on an arc that subtends 180 degrees.

After measuring the text, the computer needs to get the positions and glyph IDs of each glyph that ATSUI has typeset. It calls ATSUGetGlyphInfo twiceonce to figure out how much data the routine will return and once to retrieve the data itself. ATSUGetGlyphInfo returns a lot of information about the layout of text. The only information being used here is the positions of the glyphs on a line. After getting the glyphs, the code loops over each glyph and uses the glyph's position and the width of the layout to determine the position of each glyph on the arc. The code records the glyph positioning information in a global array. A more complete program would probably allocate this array dynamically rather than using a fixed-size, statically allocated data structure.

After recording the drawing information, the computer needs to draw that information in a context. The routine that draws the glyph info is given in Listing 11.2.

Listing 11.2. Drawing the Sunset Sample

void DrawSunsetSample(CGContextRef inContext, CGRect bounds) {     // If the text layout has never been created. Make it     if(NULL == gTextLayout) {             gTextLayout = CreateTextLayout(inContext);     }     // Create a CGFont from the ATSUI Font used in the text layout     ATSFontRef atsFontRef = FMGetATSFontRefFromFont(GetFontID());     CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFontRef);     // Save the GState and move the origin to the center of the view.     CGContextSaveGState(inContext);     CGContextTranslateCTM(inContext,            CGRectGetMidX(bounds), CGRectGetMidY(bounds));     // Set up the font environment.     CGContextSetFont(inContext, cgFont);     CGContextSetFontSize(inContext, 72);     CGContextSetTextDrawingMode(inContext, kCGTextStrokeClip);     CFRelease(cgFont);     cgFont = NULL;     // Calculate the positioning information of the glyph     CreateGlyphDrawingData();     // Run through the glyph positions we've calculated and     // draw the glyphs     for(short ctr = 0; ctr < gGlyphDrawingInfo.numGlyphs; ctr++) {             // Save the current rotation angle and the clipping area             // then draw a glyph. Because of the text mode this clips             // the context to the glyph and strokes the glyph             // simultaneously.             CGContextSaveGState(inContext);             CGContextRotateCTM(inContext,                     gGlyphDrawingInfo.glyphInfo[ctr].angle * pi / 180.0);             CGContextShowGlyphsAtPoint( inContext,                     -(gGlyphDrawingInfo.glyphInfo[ctr].glyphWidth / 2.0),                     75, &gGlyphDrawingInfo.glyphInfo[ctr].glyphID, 1);             // Draw the background of the glyphs while the             // glyph clip is in force             DrawGlyphBackground(inContext, bounds);             CGContextRestoreGState(inContext);      }      // We're done drawing so restore the graphics state      CGContextRestoreGState(inContext);      // We're done with the text layout      ATSUDisposeTextLayout(gTextLayout);      gTextLayout = NULL; } 

After creating the text layout that will arrange the text, this code needs to set the font attributes of the context. To create a CGFont, the computer needs an ATSFontRef and obtains it by converting the ATSUFontID used by the ATSUI layout. After setting the font and size of the context, the code sets the text drawing mode to kCGTextStrokeClip. As mentioned earlier, this means that when you draw text it will be outlined with a stroke but also added to the clipping region of the context.

You then enter a loop that iterates over the glyph drawing information you've calculated. This code rotates the y axis of the context to the angle calculated for the particular glyph you are drawing and draws the glyph centered on the y axis. As the computer draws each glyph, it fills in the clipping area inside each with the background pattern. For our sample, the background image is drawn using a short chain of Core Image filters that give a pleasing gradient effect.

This sample shows one simple example of using Quartz to create a complex text effect. For mundane text drawing needs, your application should rely on ATSUI, Cocoa Text, or similar high-level text technologies. These technologies insulate you from a lot of complex processing that the computer must do when rendering text. However, if you are willing to work more intimately within the complexities of the text rendering process, the low-level text drawing routines in Quartz 2D can be very effective tools.




Quartz 2D Graphics for Mac OS X Developers
Quartz 2D Graphics for Mac OS X Developers
ISBN: 0321336631
EAN: 2147483647
Year: 2006
Pages: 100

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net