|
Volume Number: | 10 | |
Issue Number: | 10 | |
Column Tag: | Getting Started |
Related Info: Color Quickdraw
Working With Color
By Dave Mark, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
This month’s column combines two of my favorite activities: working with color and rewriting Primer, Volume II code (bringing it from the Pliocene era to full PowerPC squishiness). This month’s program is a floor to ceiling rewrite of ColorTutor. ColorTutor is a hands-on color blending environment. You specify the foreground and background colors and patterns, then select a Color Quickdraw drawing mode. ColorTutor uses CopyBits() to mix the foreground and background colors. Figure 1 shows a sample.
Figure 1. The ColorTutor window.
ColorTutor first copies the Background image to the lower-right rectangle, then copies the Source image on top of the Background using the current Mode and OpColor. Since this program is so large, we’ll get into the details in next month’s column. For now, we’ll focus on putting the project together and getting ColorTutor up and running.
The ColorTutor Resources
ColorTutor uses six different resource types: an ALRT, a CNTL, a DITL, an MBAR, a MENU, and a WIND. Start by creating a folder named ColorTutor in your Projects folder. Next, fire up ResEdit or Resorcerer and create a new file named ColorTutor.Π.rsrc in the ColorTutor folder.
Create an ALRT resource with an ID of 128, a top of 40, left of 40, bottom of 156 and right of 332. Make sure the DITL ID is set to 128.
Next, create a DITL with an ID of 128. Figure 2 shows the specifications for item 1, the OK button, and Figure 3 shows the specs for item 2, the static text field. The alert you just created is used to display an error message.
Figure 2. Specifications for the OK button.
Figure 3. Specifications for item 2, the static text field.
Next, you’ll create a CNTL resource with an ID of 128. The CNTL will be used to implement the OpColor button in the lower-left corner of the ColorTutor window. The ProcID of 0 specifies a pushButtonProc control.
Figure 4. Specifications for the CNTL resource.
Now create an MBAR resource with an ID of 128. Add the menu IDs 128, 129, and 130 (the •, File, and Edit menus) to the MBAR. Though we’ll be creating 5 menus, don’t be fooled. Only the first three will be added to the menu bar.
Next, you’ll create five MENU resources. The first four are shown in Figure 5, and the fifth in Figure 6. MENUs 128, 129, and 130 will be used to create the menu bar. The last two implement the ColorTutor popup menus. Note that the popup menus don’t have titles. Note also that MENU 132 has 17 items including the separator line (the 9th item).
Figure 5. Specifications for the first four MENUs.
Figure 6. Specifications for the two popup MENUs.
The last resource is a WIND with a resource ID of 128. Figure 7 shows the ResEdit WIND editing screen for my WIND. This WIND implements the main ColorTutor window.
Figure 7. Specifications for the WIND resource.
Finally, save your changes and quit your resource editor.
The ColorTutor Project
Next, pick your development environment and create a new project. From now on, I’ll test all my source code to make sure it compiles in both THINK C and CodeWarrior, so it shouldn’t matter which environment you pick. Create your new project with the name ColorTutor.Π inside the ColorTutor folder.
Next, add MacTraps to the project if you are using THINK C, or MacOS.lib if you are using CodeWarrior.
Finally, create a new source code file, save it as ColorTutor.c, and add it to the project. Here’s the source code:
/* 1 */ #include <Picker.h> #include <GestaltEqu.h> #define kBaseResID 128 #define kErrorALRTid 128 #define kNullFilterProc NULL #define kMoveToFront (WindowPtr)-1L #define kNotNormalMenu -1 #define kSleep 60L #define mApple kBaseResID #define iAbout 1 #define mFile kBaseResID+1 #define iQuit 1 #define mColorsPopup kBaseResID+3 #define iBlackPattern1 #define iGrayPattern 2 #define iColorRamp 4 #define iGrayRamp5 #define iSingleColor 6 #define mModePopup kBaseResID+4 Globals Boolean gDone; Rect gSrcRect, gBackRect, gDestRect, gSrcMenuRect, gBackMenuRect, gModeMenuRect, gOpColorRect; intgSrcPattern, gBackPattern, gCopyMode, gSrcType, gBackType; RGBColorgSrcColor, gBackColor, gOpColor; MenuHandlegSrcMenu, gBackMenu, gModeMenu; Functions void ToolboxInit( void ); void MenuBarInit( void ); void CreateWindow( void ); void SetUpGlobals( void ); void EventLoop( void ); void DoEvent( EventRecord *eventPtr ); void HandleMouseDown( EventRecord *eventPtr ); void HandleMenuChoice( long menuChoice ); void HandleAppleChoice( short item ); void HandleFileChoice( short item ); void DoUpdate( WindowPtr window ); void DrawContents( WindowPtr window ); void DrawColorRamp( Rect *rPtr ); void DrawGrayRamp( Rect *rPtr ); void DrawLabel( Rect *boundsPtr, Str255 s ); void DoContent( WindowPtr window, Point globalPoint ); void UpdateSrcMenu( void ); void UpdateBackMenu( void ); void UpdateModeMenu( void ); void DoSrcChoice( short item ); void DoBackChoice( short item ); void DoModeChoice( short item ); short DoPopup( MenuHandle menu, Rect *boundsPtr ); Boolean PickColor( RGBColor *colorPtr ); Boolean HasColorQD( void ); void DoError( Str255 errorString ); main void main( void ) { ToolboxInit(); MenuBarInit(); if ( ! HasColorQD() ) DoError( "\pThis machine does not support Color QuickDraw!" ); CreateWindow(); SetUpGlobals(); EventLoop(); } ToolboxInit void ToolboxInit( void ) { InitGraf( &qd.thePort ); InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDialogs( 0L ); InitCursor(); } MenuBarInit void MenuBarInit( void ) { Handle menuBar; MenuHandle menu; menuBar = GetNewMBar( kBaseResID ); if ( menuBar == NULL ) DoError( "\pCouldn't load the MBAR resource..." ); SetMenuBar( menuBar ); menu = GetMHandle( mApple ); AddResMenu( menu, 'DRVR' ); DrawMenuBar(); } CreateWindow void CreateWindow( void ) { WindowPtrwindow; window = GetNewCWindow( kBaseResID, NULL, kMoveToFront ); GetNewControl( kBaseResID, window ); SetPort( window ); TextFont( systemFont ); } SetUpGlobals void SetUpGlobals( void ) { SetRect( &gSrcRect, 15, 6, 95, 86 ); SetRect( &gBackRect, 125, 6, 205, 86 ); SetRect( &gDestRect, 125, 122, 205, 202 ); SetRect( &gOpColorRect, 15, 122, 95, 202 ); SetRect( &gSrcMenuRect, 7, 90, 103, 108 ); SetRect( &gBackMenuRect, 117, 90, 213, 108 ); SetRect( &gModeMenuRect, 117, 206, 213, 224 ); gSrcPattern = iBlackPattern; gBackPattern = iBlackPattern; gCopyMode = srcCopy; gSrcColor.red = 65535; gSrcColor.green = gSrcColor.blue = 0; gSrcType = iSingleColor; gBackColor.blue = 65535; gBackColor.red = gBackColor.green = 0; gBackType = iSingleColor; gOpColor.green = 32767; gOpColor.red = 32767; gOpColor.blue = 32767; OpColor( &gOpColor ); gSrcMenu = GetMenu( mColorsPopup ); InsertMenu( gSrcMenu, kNotNormalMenu ); gBackMenu = GetMenu( mColorsPopup ); InsertMenu( gBackMenu, kNotNormalMenu ); gModeMenu = GetMenu( mModePopup ); InsertMenu( gModeMenu, kNotNormalMenu ); } EventLoop void EventLoop( void ) { EventRecordevent; gDone = false; while ( gDone == false ) { if ( WaitNextEvent( everyEvent, &event, kSleep, NULL ) ) DoEvent( &event ); } } DoEvent void DoEvent( EventRecord *eventPtr ) { char theChar; switch( eventPtr->what ) { case mouseDown: HandleMouseDown( eventPtr ); break; case keyDown: case autoKey: theChar = eventPtr->message & charCodeMask; if ( (eventPtr->modifiers & cmdKey) != 0 ) HandleMenuChoice( MenuKey( theChar ) ); break; case updateEvt: DoUpdate( (WindowPtr)eventPtr->message ); break; } } HandleMouseDown void HandleMouseDown( EventRecord *eventPtr ) { WindowPtrwindow; short thePart; long menuChoice; thePart = FindWindow( eventPtr->where, &window ); switch ( thePart ) { case inMenuBar: menuChoice = MenuSelect( eventPtr->where ); HandleMenuChoice( menuChoice ); break; case inSysWindow : SystemClick( eventPtr, window ); break; case inContent: if ( window != FrontWindow() ) SelectWindow( window ); else DoContent( window, eventPtr->where ); break; case inDrag : DragWindow( window, eventPtr->where, &qd.screenBits.bounds ); break; } } HandleMenuChoice void HandleMenuChoice( long menuChoice ) { short menu; short item; if ( menuChoice != 0 ) { menu = HiWord( menuChoice ); item = LoWord( menuChoice ); switch ( menu ) { case mApple: HandleAppleChoice( item ); break; case mFile: HandleFileChoice( item ); break; } HiliteMenu( 0 ); } } HandleAppleChoice void HandleAppleChoice( short item ) { MenuHandle appleMenu; Str255 accName; short accNumber; switch ( item ) { case iAbout: SysBeep( 20 ); break; default: appleMenu = GetMHandle( mApple ); GetItem( appleMenu, item, accName ); accNumber = OpenDeskAcc( accName ); break; } } HandleFileChoice void HandleFileChoice( short item ) { switch ( item ) { case iQuit: gDone = true; break; } } DoUpdate void DoUpdate( WindowPtr window ) { BeginUpdate( window ); DrawContents( window ); DrawControls( window ); EndUpdate( window ); } DrawContents void DrawContents( WindowPtr window ) { RGBColor rgbBlack; Rect source, dest; rgbBlack.red = rgbBlack.green = rgbBlack.blue = 0; if ( gSrcPattern == iBlackPattern ) PenPat( &qd.black ); else PenPat( &qd.gray ); if ( gSrcType == iColorRamp ) DrawColorRamp( &gSrcRect ); else if ( gSrcType == iGrayRamp ) DrawGrayRamp( &gSrcRect ); else { RGBForeColor( &gSrcColor ); PaintRect( &gSrcRect ); } if ( gBackPattern == iBlackPattern ) PenPat( &qd.black ); else PenPat( &qd.gray ); if ( gBackType == iColorRamp ) DrawColorRamp( &gBackRect ); else if ( gBackType == iGrayRamp ) DrawGrayRamp( &gBackRect ); else { RGBForeColor( &gBackColor ); PaintRect( &gBackRect ); } PenPat( &qd.black ); RGBForeColor( &gOpColor ); PaintRect( &gOpColorRect ); RGBForeColor( &rgbBlack ); DrawLabel( &gSrcMenuRect, "\pSource" ); DrawLabel( &gBackMenuRect, "\pBackground" ); DrawLabel( &gModeMenuRect, "\pMode" ); PenSize( 2, 2 ); FrameRect( &gSrcRect ); FrameRect( &gBackRect ); FrameRect( &gDestRect ); FrameRect( &gOpColorRect ); PenNormal(); source = gBackRect; InsetRect( &source, 2, 2 ); dest = gDestRect; InsetRect( &dest, 2, 2 ); CopyBits( (BitMap *)&(((CGrafPtr)window)->portPixMap), (BitMap *)&(((CGrafPtr)window)->portPixMap), &source, &dest, srcCopy, NULL ); source = gSrcRect; InsetRect( &source, 2, 2 ); CopyBits( (BitMap *)&(((CGrafPtr)window)->portPixMap), (BitMap *)&(((CGrafPtr)window)->portPixMap), &source, &dest, gCopyMode, NULL ); } DrawColorRamp void DrawColorRamp( Rect *rPtr ) { long numColors, i; HSVColor hsvColor; RGBColor rgbColor; Rect r; r = *rPtr; InsetRect( &r, 2, 2 ); numColors = ( rPtr->right - rPtr->left - 2 ) / 2; hsvColor.value = hsvColor.saturation = 65535; for ( i = 0; i < numColors; i++ ) { hsvColor.hue = i * 65535 / numColors; HSV2RGB( &hsvColor, &rgbColor ); RGBForeColor( &rgbColor ); FrameRect( &r ); InsetRect( &r, 1, 1 ); } } DrawGrayRamp void DrawGrayRamp( Rect *rPtr ) { long numColors, i; RGBColor rgbColor; Rect r; r = *rPtr; InsetRect( &r, 2, 2 ); numColors = ( rPtr->right - rPtr->left - 2 ) / 2; for ( i = 0; i < numColors; i++ ) { rgbColor.red = i * 65535 / numColors; rgbColor.green = rgbColor.red; rgbColor.blue = rgbColor.red; RGBForeColor( &rgbColor ); FrameRect( &r ); InsetRect( &r, 1, 1 ); } } DrawLabel void DrawLabel( Rect *boundsPtr, Str255 s ) { Rect r; int size; r = *boundsPtr; r.bottom -= 1; r.right -= 1; FrameRect( &r ); MoveTo( r.left + 1, r.bottom ); LineTo( r.right, r.bottom ); LineTo( r.right, r.top + 1 ); size = boundsPtr->right - boundsPtr->left - StringWidth(s); MoveTo( boundsPtr->left + size / 2, boundsPtr->bottom - 6); DrawString( s ); } DoContent void DoContent( WindowPtr window, Point globalPoint ) { int choice; ControlHandle control; RGBColor rgbColor; Point p; p = globalPoint; GlobalToLocal( &p ); if ( FindControl( p, window, &control ) ) { if ( TrackControl( control, p, NULL ) ) { rgbColor = gOpColor; if ( PickColor( &rgbColor ) ) { gOpColor = rgbColor; InvalRect( &gOpColorRect ); InvalRect( &gDestRect ); OpColor( &gOpColor ); } } } else if ( PtInRect( p, &gSrcMenuRect ) ) { UpdateSrcMenu(); choice = DoPopup( gSrcMenu, &gSrcMenuRect ); if ( choice > 0 ) { DoSrcChoice( choice ); InvalRect( &gSrcRect ); InvalRect( &gDestRect ); } } else if ( PtInRect( p, &gBackMenuRect ) ) { UpdateBackMenu(); choice = DoPopup( gBackMenu, &gBackMenuRect ); if ( choice > 0 ) { DoBackChoice( choice ); InvalRect( &gBackRect ); InvalRect( &gDestRect ); } } else if ( PtInRect( p, &gModeMenuRect ) ) { UpdateModeMenu(); choice = DoPopup( gModeMenu, &gModeMenuRect ); if ( choice > 0 ) { DoModeChoice( choice ); InvalRect( &gDestRect ); } } } UpdateSrcMenu void UpdateSrcMenu( void ) { int i; for ( i = 1; i <= 6; i++ ) CheckItem( gSrcMenu, i, false ); if ( gSrcPattern == iBlackPattern ) CheckItem( gSrcMenu, iBlackPattern, true ); else CheckItem( gSrcMenu, iGrayPattern, true ); if ( gSrcType == iColorRamp ) CheckItem( gSrcMenu, iColorRamp, true ); else if ( gSrcType == iGrayRamp ) CheckItem( gSrcMenu, iGrayRamp, true ); else if ( gSrcType == iSingleColor ) CheckItem( gSrcMenu, iSingleColor, true ); } UpdateBackMenu void UpdateBackMenu( void ) { int i; for ( i = 1; i <= 6; i++ ) CheckItem( gBackMenu, i, false ); if ( gBackPattern == iBlackPattern ) CheckItem( gBackMenu, iBlackPattern, true ); else CheckItem( gBackMenu, iGrayPattern, true ); if ( gBackType == iColorRamp ) CheckItem( gBackMenu, iColorRamp, true ); else if ( gBackType == iGrayRamp ) CheckItem( gBackMenu, iGrayRamp, true ); else if ( gBackType == iSingleColor ) CheckItem( gBackMenu, iSingleColor, true ); } UpdateModeMenu void UpdateModeMenu( void ) { int i; for ( i = 1; i <= 17; i++ ) CheckItem( gModeMenu, i, false ); if ( ( gCopyMode >= 0 ) && ( gCopyMode <= 7 ) ) CheckItem( gModeMenu, gCopyMode + 1, true ); else CheckItem( gModeMenu, gCopyMode - 22, true ); } DoSrcChoice void DoSrcChoice( short item ) { RGBColor rgbColor; switch ( item ) { case iBlackPattern: case iGrayPattern: gSrcPattern = item; break; case iColorRamp: case iGrayRamp: gSrcType = item; break; case iSingleColor: gSrcType = iSingleColor; rgbColor = gSrcColor; if ( PickColor( &rgbColor ) ) gSrcColor = rgbColor; break; } } DoBackChoice void DoBackChoice( short item ) { RGBColor rgbColor; switch ( item ) { case iBlackPattern: case iGrayPattern: gBackPattern = item; break; case iColorRamp: case iGrayRamp: gBackType = item; break; case iSingleColor: gBackType = iSingleColor; rgbColor = gBackColor; if ( PickColor( &rgbColor ) ) gBackColor = rgbColor; break; } } DoModeChoice void DoModeChoice( short item ) { if ( ( item >= 1 ) && ( item <= 8 ) ) gCopyMode = item - 1; else gCopyMode = item + 22; } DoPopup short DoPopup( MenuHandle menu, Rect *boundsPtr ) { Point corner; long theChoice = 0L; corner.h = boundsPtr->left; corner.v = boundsPtr->bottom; LocalToGlobal( &corner ); InvertRect( boundsPtr ); theChoice = PopUpMenuSelect(menu,corner.v-1,corner.h+1,0); InvertRect( boundsPtr ); return( LoWord( theChoice ) ); } PickColor Boolean PickColor( RGBColor *colorPtr ) { Point where; where.h = -1; where.v = -1; return( GetColor( where, "\pChoose a color...", colorPtr, colorPtr ) ); } HasColorQD Boolean HasColorQD( void ) { unsigned char version[ 4 ]; OSErr err; err = Gestalt( gestaltQuickdrawVersion, (long *)version ); if ( version[ 2 ] > 0 ) return( true ); else return( false ); } DoError void DoError( Str255 errorString ) { ParamText( errorString, "\p", "\p", "\p" ); StopAlert( kErrorALRTid, kNullFilterProc ); ExitToShell(); }
Running ColorTutor
Save your code, and run ColorTutor. The ColorTutor window will appear, as shown in Figure 8.
Figure 8. The ColorTutor WIndow.
The Source and Background menus are identical, as shown in Figure 9. Play with these selections till you get the source and background that you want.
Figure 9. The Source and Background menus.
The real fun comes when you play with the Mode popup (Figure 10). Basically, the mode is passed as the fifth parameter to the CopyBits() call that copies the source rectangles over the destination rectangle which had been previously copied to the lower right corner of the ColorTutor window. Some of the modes take an OpColor, which you can set using the OpColor button.
Figure 10. The Mode popup menu.
Till Next Month
Confused? Experiment! We’ll get into all the hows and whys next month. Till then, read up on the Color Quickdraw transfer modes in THINK Reference and Inside Macintosh.
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine