Document 6495055
Transcription
Document 6495055
. Journeyman '-_ 11 Fast Bezier C esin Windows Michael Bertrand ezier curves are widely used in computer graphics. PostScript uses these curves as building blocks, for example; defining even circles in terms of Beziers. Many modern PC and Mac illustration programs implement Bezier curves in a direct way, allowing users to create and interactively edit the curves to create complex images. These curves possess several properties that have led to their widespread adoption in computer graphics applications: B • Because they are defined in terms of a few points, Bezier curves can be identified with these points in the graphics database. • Efficient algorithms generate the entire curve from the defining points. • The defining points intuitively describe the curve. I Four defining points-A Bezier curve is defined by four points called controll, handle1, handle2, and control2 (abbreviated here as ctrll, hand1, hand2, and ctrl2). The curve begins at ctrll and proceeds toward hand1; at the other end, we can think of it as starting from ctrl2 and proceeding toward hand2. In each case, the curve gradually pulls away from the associated handle in its movement toward Here's how to draw Bezier curves quickly enough to rubber-band them on-screen. the other handle/control. The handles are points of attraction for the curve; the curve starts from ctrll toward hand 1, but gradually pulls toward hand2 as the attractive force of hand1 diminishes and the attractive force of hand2 increases. The further away hand1 is from ctrll, the longer the curve will be pulled toward ctrll before breaking toward hand2. Bezier curves take several forms, as shown in the screen shot given in Figure 1. Vectors connecting each control point to its handle are shown as well as the Bezier curve they define. This example is about as complex as four-point Beziers get; more complex Bezier curves require a series of Beziers joined together in a continuous path. four original defining points. The calculated points can then be connected by line segments to give the impression of a smooth curve. The de Casteljau algorithm breaks a Bezier curve into two separate pieces, left and right, each of which is itself a Bezier curve. The key to the efficiency of this algorithm is the astounding simplicity of the math, which involves taking simple averages to calculate de Casteljau construction points: First average the original defining points (the q's shown below are the averages), then average the averages (r's), then take a final average (sO): ctrll - pO de Casteljau Construction-Beziers must be drawn quickly to be dragged, or rubber-banded, on the screen, since the curves are being drawn and erased constantly with each mouse movement. We also need fast Bezierdrawing routines to render a complex image comprising perhaps hundreds of individual Beziers in a reasonable time. The de Casteljau algorithm is a fast integer-based method for calculating points along a Bezier curve, given the handl - pl qO rO sa ql hand2 - p2 ctr12 - p3 rl q2 That is: qO = (pO + p1)/2, rO = (qO + q1)/2, and so on. The actual calculations will be with coordinates, not points, but it helps to think in terms of points, keeping in mind that the "average" of two points is the point midway between the two. It turns out that sO is the midpoint of the Bezier Feb/March 1992 25 ; C: FAST BEllER CURVES 1=1 original Bezier curve. 3. (p3, q2, r1, sO)are (ctrll, hand1, hand2, ctrl2) of a sub-Bezier coinciding with the right half of the original Bezier curve. Bezier Help ~ Left Button dOI,WI, drag, button up fur 1st hendle. Left Button do •... en. drag, button up for 2nd handle and Bez Right dick to dear ,.·!indo, .. ...·} x x After the first step illustrated here, we have 3 points on the curve: The original The midpoint The original • FIGURE 2-The de Caster au Construction ~~~~------------------~ p1 rO r1 p2 q2 pO curve. What's more, (pO, qO, rO, sO) are (ctrl, hand, hand, ctrl) for a Bezier coinciding with the left half of the original Bezier; and (p3, q2, rl, sO) are (ctrl, hand, hand, ctrl) for another Bezier coinciding with the right half of the original Bezier. The procedure can be repeated: the midpoint of the left sub-Bezier is 1/4 of the way along the original curve; and the midpoint of the right sub-Bezieris 3/4 of the way along the original curve. The subdivision process can be repeated indefinitely to generate 2, 4, 8, 16,...2n sub-Beziers and 3,5,9,17, ...2"+1 points along the original curve. Like other subdivision processes, the de Casteljau algo26 PC TECHNIQUES p3 rithm lends itself to recursive implementation. Averaging entails dividing by 2, which can be done quickly as a shift right operation because we are dealing with integers. The de Casteljau construction is illustrated in Figure 2, where the p's are the controls and handles of the original Bezier curve. qO is midway between pO and p l , rO is midway between qO and q l , and so on. Observe that: 1. sOis the midpoint of the original Bezier curve. 2. (pO, qO, rO, sO)are (ctrll, hand1, hand2, ctrl2) of a sub-Bezier coinCiding with the left half of the point. original point. pO Bezier p3 curve Applying the procedure again to the left and right sub-Beziers generates their midpoints, giving five points on the original Bezier. Subdividing these four sub-Beziers then gives us nine points on the original curve, and so on. Stopping at four subdivision levels and 17 points produces smooth curves at VGA resolution. The number of subdivisions, or recursive depth, is BEZ_DEPTH in the program . NUM_BEZPTS is the number of points generated along the Bezier curve, and is used to allocate an array to hold the Bezier points. Therefore, make sure that: NUM BEZPTS .......... control of the control >- 2"" DEPI' + 1 Increasing BEZ_DEPTH results in more line segments in the Bezier curve, hence a smoother curve-at least up to a point. We reach diminishing returns in increasing BEZ_DEPTH too much, since the accumulated error of repeated averaging eventually throws the calculations off by one or more pixels. Remember that BEZ_DEPTH is an exponent, so increasing BEZ_DEPTH by 1 doubles the number of-segments. Since the recursion proceeds to the maximum depth down the far left branch, the first curve point actually generated is the point immediately following pO = ctr11 along the Bezier. The remaining points are also generated in order (from pO = ctrll to p3 = ctrI2), a nice side effect of the recursive implementation. Another point is collected into an array every time the recursion reaches its finest subdivision level, and the points are in order! Writing tools in Windows-We want to show off our fast Bezier-drawing through an interactive Bezier Tool. If the curve rubber-bands well on the screen, then we can claim to have a good algorithm. Interactive tools in Windows are constructed with the concept of "system state." The Window procedure passes mouse messages to Bez'Toolf), which maintains a key static variable, iState, which takes four values summarized in Table 1. BezToolO's action depends on iState, which in turn depends on the sequence of mouse messages that have recently streamed into the tool. Until the first WM_LBUITONDOWN message is received, iState remains NOT_STARTED because nothing has been done. The first WM_LBUITONDOWN triggers a state transition to DRAG_HANDl. In this state, the tool responds to WM_MOUSEMOVEs by rubber-banding the first handle in XOR mode. WM_LBUITONUP then causes a state transition to WAIT_FOR_CTRL2. Nothing happens until another WM_LBUITONDOWN is received, which changes iState to DRAG_HAND2; in this state, WM_MOUSEMOVE messages cause rubber-banding of both the second handle and the Bezier curve as a whole. WM_LBUITONUP now causes a final state transition back to NOT_STARTED. The final handle and Bezier are frozen, and the tool is again ready to start another Bezier. BezToolO calls DrawHandleO and DrawBezO to draw the figures (which in turn call Windows' GDI calls Move'Tof), Line'I'ot), and Polylinetj). Each mouse move causes two calls to these routines. The first call draws over the figure exactly where it had been drawn the first time. Since we are in XOR drawing mode, drawing over the original figure erases it. The second call then draws at the new location. Static variables must be used if the user points are to be remembered for the next pass through the tool so previous figures can be erased. Windows and graphics programming-Windows is a natural medium for this kind of programming. Mouse events are sent to our window procedure automatically, enabling us to build interactive mouse-driven tools. The GDI system provides line drawing, including the R2_NOTXORPEN ROP code which allows us to draw in XOR mode. Windows has a built-in coordinate system and mapping modes so we can change our working range of numbers. The Beziers would not dis- play nearly as nicely were we restricted to screen coordinates of about 500x500 pixels. By setting the MM_ISOTROPIC mapping mode and adjusting the Window Extent and Viewport Extent in Bez'Toolt), we can expand the range to [-15,000, +15,000] which minimizes the negative side effectsof calculations with small integers. • Editor's note: To rebuild BEZ.EXE, you'll need several files in addition to BEZ.C, including some with no ASCII representation. These files are present in a file called BEZ.ZIP, contained within the listings archive (or this issue, either on a Disk Subscription disk or from one of the online services and BBS systems that carry our listings. References Foley, James D., Andries van Dam, Steven K. Feiner, and John F. Hughes, Computer Graphics: Principles and Practice, Addison-Wesley (2nd ed., 1990), pp 507ff. Michael Bertrand teaches Mathematics and programming at Madison Area Technical College, Madison, WI 53704. COMPUTER LANGUAGE 5.0 presents C Bug # 651 '#.W@WJit rwJ.l;/· struct { int a[3]. b: } w[] = { { L 2. 3 }. 2 }: Do you see any problems with this declaration? Chances are your compiler will not report any difficulties and yet, you may be surprised to learn how many elements are in w[] (hint: it's not 1). If you need help, give us a call; refer to bug #651. PC-lint will catch this and many other C bugs. Unlike your compiler, PC-lint looks across all modules of your application for bugs and inconsistencies. New - Optional Strong Type Checking and variables possibly not initialized. More than 330 error messages. More than 105 options for complete customization. Suppress error messages, locally or globally, by symbol name, by message number, by filename, etc. Check for portability problems. Alter size of scalars. Adjust format of error messages. Automatically generate ANSI prototypes for your K&R functions. AUn: Power users with huge programs. PC-lint 386 uses DOS Extender Technology to access the full storage and flat model speed of your 386. Now fully compatible with Windows 3.0 and DOS 5.0 PC-lint 386 --$239 PC-lint DOS - OS/2 - $139 Mainframe & Mini Programmers Fiexelsmt in obfuscated source form, is available for Unix, OS-9, VAXIVMS, QNX, IBM VM/MVS, etc. Requires only K&R C to compile but supports ANSI. Callfor pricing. 3207 Hogarth Lane, Collegeville, PA 19426 CALL TODAY (215) 584-4261 Or FAX (215) 584-4266 30 Day Money-back Guarantee. PA add 6% sales lax. PC·linl and FlexeLint are trademarks of Gimpel Software Feb/March 1992 27 C: FAST BEZIER CURVES A TABLE l-IState's Four Possible Values NOT_STARTED : tool DRAG_HANOI : dragging has not been WAlTJORJTRL2 : waiting for DRAG_HAND2 : draggi ng handl CW_USEDEFAULT, ShowWindow(hWnd, started contro12 e2 to and : Program to vely. User i nteraeti handle; the handle. Demonstrates Bezier calculation of Copyright draw (c) draws <wi ndows . h) Iii ncl ude "bez. curves and handl e by fi rst the 1991. Iii ne 1 ude while curve Bezier on. reeei Bezier their together de algorithm nq , then with the for fast ves app Conti *1 window client NULl); area; send *1 WM_PAINT and unti di spatch them 1 GetMessage() to appropri returns ate wi n NULL when it *1 WM_OUIT. NULL, NULL, NULl) 1* process ateMessage(&msg); Di spatchMessage(&msg); /* pass char input message to from window */ keyboard *1 function } return(msg.wParam) A. que nues (GetMessage(&msg, Transl second second points. Michael hInstance, ( handles draggi rubber-bands Casteljau the 1* update 1* Read msgs from Bezi er .•. LISTING I-BEZ.C BEZ.C display /* NULL, be entered functi /* nCmdShow); UpdateWindow(hWnd); handlel NULL, ; *1 Bertrand. 1ong FAR PASCAL WndProc (HWND hWnd, uns i gned WORD wParam, h" i Message, LONG I Param) 1* HPEN hRedPen; /* red pen int LogPerDevice; /* Illogical WORD cxCl i ent; WORD cyCl i ent; /* size of /* size of 1* current POINT BelPts[NUM_BEZPTS] POINT *PtrBezPts; char Instrl[] "* Instr2[] int IN: client area (x ) , client area (y). along Bezier BezPts[] curve array down, drag, button up for 1st handle."; down, drag, button up for 2nd handle - */ */ app's messages come hWnd, iMessage,wParam,lParam : standard Windows proc parameters HOC hOC; PAINTSTRUCT ps; /* must FARPROC lpProcAbout; generate our 1* needed when own handle recei 1* pointer to to DC to ve WM_PAINT "AboutBez" draw */ message *1 *1 function (i Message) ( "* Right click to clear hlnstance, LPSTR lpszCmdLine, and Create /* Bez."; window."; hRedPen wi ndow int and set break; 1* WM_CREATE */ case nCmdShow) di spach message cyClient standard static char szAppName static char static char as *1 global. l , RGB(255, 0, 0»; a rea size into - LOWORD(lParam); g I oba 1 s when wi ndow - HIWORD(IParam); res i zed. *1 1* WM_SIZE */ break; parms store CreatePen(PS_SOLID, WM_SIZE: cxClient loop. hInstance,hPrevInstance,lpszCmdLine,nCmdShow: WinMain and - 1* Get c 1 i ent Regi ster once hRedPen HANDLE hPrevlnstance, /* IN: all ca se WM_CREATE; PASCAL WinMain(HANDLE USE: procedure: */ */ */ *1 instance window here. Button Instr3[] Application's unit switch Button "* Left char device ~ Left char per */ axes). /* array of pts /* pointer into ; USE: *1 handles. units (both HANDLE hInst; for */ case [] WMJOMMAND: 1f (wParam - - "Bezier"; szlconName[] - "BezIeon"; ( szMenuName[] - "Bezhenu": /* "About" call HWND hWnd; MSG msg; 1* for registering window menu item Dialog80x - by user : *1 function. MakeProcInstance(AboutBez, (hlnst, "AboutBez", FreeProclnstance( *1 chosen "AboutBez" I pProcAbout /* handle to WinMain's window */ 1* message di spached to wi ndow *1 WNDCLASS we; 10M ABOUTl hlnst); hWnd, lpProcAbout); I pProcAbout); ) 1* Save so hlnst instance can - use handle for in "About" global box. *1 case hlnstance; /* /* Register if application window WfLPAINT: Repa i nt hOC - *1 class. 1* WM_COMMAND*1 brea k; var dialog Se 1 ectObj (!hPrevInstance) I wc.cbClsExtra - 0; EndPaint(hWnd, wc. cbWndExtra - 0; break; wc. hI nstance - */ wi ndow' s messages wc.hleon - LoadIcon(hlnstance, - LoadCursor(NULL, wc. hbrBackground - GetStoekObject wc. I pszMenuName - slMenuName; wc. 1 pszCI assName - szAppName; case szlconName); GetStockObj ect (ANS I_VARJONTl ); 0, 30, Instr3, lstrlen(lnstr3)); &ps); 1* WM_PAINT */ WfLLBUTTONDOWN: ca se WM_RBUTTONDOWN: IDC_ARROW); (WHITCBRUSH); 1* menu resource in RC fil 1* name used inca 11 to CreateWindow() *1 e *1 case WM_MOUSEMOVE: case WM_LBUTTONUP: 1* Mouse events BezTool(hWnd, passed on iMessage, to BezTool () for processing. */ lParam); 1* WM_LBUTTONDOWN... break; */ (!RegisterClass(&wc» return (FALSE) case ; WM_DESTROY: /* Destroy window De 1eteObject 1* Initialize hWnd - CreateWi specific instance. ndow( szAppName, PC TECHNIQUES WS_OVERLAPPEOWINDOW, CW_USEDEFAULT, CW_USEOEFAULT, : 0); 1* WM_DESTROY */ break; defaul & delete (hRedPen) Pas tOui tfiessage( *1 szAppName, CW_USEDEFAULT, 28 */ wi ndow. hInstance; we. hCursor if of 0, WndProc; get 1 eft TextOut(hDC, CS_HREDRAW - to upper Textout(hDC, - wc. I pfnWndProc CS_VREORAW; at &ps); 0, Ins t r l . lstrlen(lnstr))); D, 15, Instr2, l s tr l ent Ins tr z j ) :" we. style /* fn ions ect (hOC, TextOut(hDC, ( instruct BeginPaint(hWnd, t: pen when application terminated. *1 return( } DefWi ndowProc( hWnd, 1* switch(iMessage) return(Ol) i Message, wParam, 1 Param)); SetROPZ(hDC, RLNOTXORPEN); DrawBez( ctrll, *1 (HWND hWnd, unsi gned i Message, case IN: hWnd NOTE: mouse event handle to to : mouse event 1 Param : mouse coords is the handles and Bezier BezTool current state iState. iState's the as tool's () of the (x -- 1 award, Bezier by remembers called tool value, action input is y -- drawing this the the is are next last time in time thru. also i State) and the Bezier key the draws. static and statics The handle so hand1.x - inPt.x; hand1.y - inPt.y; DrawHandle(hDC, determines points, case DrawBez(hDC, HOC hOC; WORD maxClient; POINT inPt; POINT pts[ZJ; 1* must generate our own handle 1* larger of (cxClient, cyClientl 1* incoming point *1 1* to get Bez control 1* user-entered static POINT static POINT int /flogical DC to (lst): *1 handle ( Znd )': *1 *1 *1 Bez ctrlZ, inPt.x; handZ.y - inPt.y; state: DRAG_HANOI. etc. 1* draw in *f f* erase handZ); handl. handZ, ctrlZ, handZ, *f new handle f* draw handZ); handl, *f *1 ct r l Z) : f* get ctrll. XOR old new *f ct r l Z) : break; } case 1* BezTool()'s new f* DRAG_HANOI *f f* switch(iState) *f *1 handZ; iState; - f* WM_MOUSEMOVE *f break; control f* draw ctrl!, handZ.x DrawBez(hDC, unit handl); ctrlZ, DrawHandle(hDC, units/dev. handle draw ct r l l , handI; 1* user-entered static LogPerDevice, to ctrl!, RZ_NOTXORPEN); DrawHandle(hDC, *1 f* draw in XOR *f /* erase old */ 1* get new handl e * f DRAG_HANDZ: SetROPZ(hDC, thru. handll; 1* DRAG_HANOI *f break; BezTool() ctrl!. RZ_NOTXORPEN); DrawHandle(hDC. variable tool, control as processes WM_LBUTTONUP user DRAG_HANOI: SetROPZ (hOC, which the thru maintained case hi word) tool as maintained set time user, them repeatedly as WM_MOUSEMOVE: switch( (WM_LBUTTONDOWN, etc.) interactive *f ( WM_RBUTTONDOWN, WM_LBUTTONDOWN, WM_MOUSEMOV E, messages. curve. window iMessage This draw XOR LONG 1 Param) 1* Process in ctrlZ); f* WM_LBUTTONDOWN *1 break; voi d NEAR PASCAL BezTool handZ, *f break; } f* draw handl, f* NOCSTARTED 1* switch(iState) *f ; USE: hOC, *1 WM_LBUTTONUP: switch(iState) ( hOC - case GetDC(hWnd); DRAG_HANOI: i State 1* Set in extents range and [-15000, SetMapMode(hDC, - so will *1 +I5000J. 30000, > (cxClient SetViewportOrg(hDC, cxClient» RZ_COPYPEN); ? cxClient -maxCl 1, : cyClient; cyClient» iState 1); - will Illogical need units later when pts[OJ.x - pts[OJ.y pt sl I'l .x - pts[1].y - 0; - 1; DPtoLP(hDC, pts, 2); LogPerDevice - (pts[1].x 1* Incomi ng poi nt in LOWORD(lParam); inPt.y - HIWORD(lParam); RZJOPYPEN); DrawHandle(hDC, hOC, &i nPt, swi tch( i Message) little 3x3 boxes in *f DrawHandle(). pts[O].x) coordi nates. (pts[l] .x pts[O].x) (pts[OJ .x pts[1].x) *1 if *f coordinates. ( t State - area case if not in middle of Bez. *f hWnd, NULL, : ReleaseDC(hWnd, Application's IN: hDlg handle iMessage message auxiliary 1 Param unused TRUE if processed "About" box - ctrl1.x - inPt.x; hand1.y - ctrl1.y - inPt.y; break; f* NOT_STARTED *1 1* starting drag *1 1* store user poi nt in statics user on lOOK, message, clicks IDCANCEl) FALSE otherwise. OK button *f close. f* initialize dialog box *f (TRUE); OK box (wParam f* recei selected; -- lOOK IDCANCEL II wParam ved if - a command system menu *f close command *1 IOCANCEl) ( */ EndDi al og( hDl g, return TRUE); (TRUE) ; f* exit dialog f* did proccess f* did not box */ message *f } handZ.x - ctrlZ.x - inPt.x; 1* starti 1* store handZ.y - ctr12.y - inPt.y; in - when WM_COMMAND: if WAITJOR_CTRLZ: i State (act appropriate only WM_INITDIALOG: f* lOOK if NOT_STARTED: hand1.x info (i Message) case DRAG_HANOI; message Return system function. box type Closes case case LONG 1 Param) box dialog NOTE: return - hDlg, dialog to RET: i State) i State "About" wParam switch ( case hOC); USE: or WM_LBUTTONDOWN: swi tch( iMessage, *f *f TRUE); f* WM_RBUTTONDOWN * f brea k; unsigned handl, handle COPY mode ; NOT_STARTED) Inva 1 i dateRect( hand2, 1* draw in ctr l z i ; hand2); WORD wParam, 1); client ctrl!, BOOL FAR PASCAL AbouJ;Bez(HWND WM_RBUTTONDOWN: f* Erase ctr12, f* switch(iState) *f f* WM_LBUTTONUP *f f* switch(iMessage) *1 ( case *f *1 f* DRAG_HANDZ *f break; unit final handle COPY mode f* logical DPtoLP( device /* COpy pen for final break; devi ce - to draw > inPt.x f* Convert per in NOT_STARTED; DrawBez(hDC, 1* Calculate 1* COPY pen for 1* draw hand l ) : DRAG_HANDZ: SetROPZ(hDC, ient); ctrl!, f* DRAG_HANOI *f break; case cyClient) maxClient, WAIT_FOR_CTRLZ; DrawHandle(hDC, 30000); SetviewportExt(hDC, - SetROPZ(hDC. be working MM_ISOTROPIC); SetWindowExt(hDC, maxClient origin DRAG_HANDZ: drag *1 user poi nt ng statics *f break; } return f* switch 1* WM_CDMMAND *f (iMessage) *f (FALSE); process Feb/March message *f 1992 29 C: FAST BEZIER CURVES void NEAR PASCAL DrawBez(HDC hOC, POINT ctrll, POINT handl. POINT hand2, POINT ctr12) 1* USE: Draw Bezier IN: ctrll,handl.hand2,ctr12: curve given Bez as a polygon. and handle control NOTE: Set up, then call routine, generate points the control points for depth *1 1* depth ~ means we are at the finest grab point into global array and return, Bezier the recursive de Casteljau Windows' Polyline() displays BEl_DEPTH - recursive on poi nts: sa; ° points, and handle SubDivideBez(), along the Bez. 1* de Castel j au constructi POINT qO, ql, q2, rO, rl, subdivision level: breaking off recursion. *1 if (!depth) of de Casteljau. ( Initial POINT ctrll loaded here, then recursive routine calculates and loads the remaining 2 BECDEPTH - (NUM_BElPTS - 1) de Casteljau pts. *1 *PtrBezPts++ h ~ p3: return; ( PtrBezPts - BezPts: *PtrBezPts++ - ctrll: 1* calc pts *1 1* init 1* first *1 case 1* Calculate previous *1 fast SubDi vi deBez( ctrll, Polyline(hDC, ptr to start of array control po i rrt special handl, BezPts, hand2, ctr12, NUM_BEZPTS): BEl_DEPTH): 1* call q2.x depth) Calculate de Casteljau construction points in two. pO,pl.p2,p3: control/handle/handle/control IN: subdivide depth: current the de Casteljau NOTE: Calculates recursive Bezi er can be subdi v i ded into depth construction 2 pa rts (l eft, recursive calls to this routine. Recursion depth, decremented once for each recursion and break for Bez Bez to points note s as averages shi ft ri ght - (p2.x » + p3.x) - (p2.y + p3.y) » I: 3 q' s . *1 rO.y - (qO.y rl.y - (ql.y + ql.y) + q2.y) » » 1: 1: I; q2.y 1* sa is midway between 2 r's and is in middle of original sO.x - (rO.x + rl.x) »1: sO.y - (rO.y + rl.y) » I: of algorithm. then po i nt of is by 2. *1 1* r' s are mi dway between rO.x - (qO.x + ql.x) »1: rl.x - (q l .« + q2.xl »1; 1* USE: division construction midway paints): 1* q's are midway between 4 incoming control and handl e points. qO.x - (pO.x + pl.x) » I: qO.y - (pO.y + pl.y) » 1: ql.x - (pl.x + p2.x) » I: ql.y - (pLy + p2.y) »1; Windows to draw *1 NEAR PASCAL SubDivideBez(POINT pO, POINT pl , POINT p2, POINT p3, int void de Casteljau points (ie., *1 Bez. so the ri ght) by 1* Decrement depth: subdivide incoming 1 eft, then ri ght. * 1 SubDivideBez(pO, qo , rO, sO, --depth): is broken off when level. becomes O. This is the finest level of subdivision: the right-most point on the small subdivided Bezier is also a point on the original Bezier, so we load it into global array BezPts[] (thru PtrBezPts which points into the array). *1 r l , q2, SubDivideBez(sO, void p3, Bez into 2 parts: depth): NEAR PASCAL DrawHandle(HDC POINT q) hOC, POINT p, 1* Draws handl e on screen IN: hOC: handle from p to q with to display Victor Image Processing Library powerful Handles are drawn with hRedPen. context p,q : handle start and end points NOTE: Don't CreatePen or delete--these only. Announcing version 2: Use Victor to develop USE: little are 3x3 pixel done globally boxes at each Each pixel is LogPerDevice logical units: logical units used for the boxes since we are in M~tISOTROP1C mapping image applications once end. must be mode. *1 Work with images of any size -- use conventional, expanded, and extended memory int int Now your applications can support 8-bit color and gray scale images of any size because Victor gives you complete control over conventional, expanded, and extended memory. Display on Super VGA Display images on EGA/VGA and super VGA up to 1024 x 768 256 colors. and Turbo C environments. PCX/TlFF/GlF/BIN Handle images from any source, or create translation programs between the popular. file formats. Gray scale and color images 1* Init Catenary Systems 470 Belleview for scanner and laser printer. VI~NMC/COD St Louis MO 63119 Circle 76 on reader service card 30 PC TECHNIQUES pen, select - SelectObject(hDC, - q.x red #1 pen. hRedPen): LineTo(hDC, « + (LogPerDevice y coord I pixel (314) 962-7833 *1 of 1 t t t l e box *1 q.x, q.Yl: Remember 1): below q.y. *1 LogPerDevi ce : 1* Ora" little box: 3x3 pixels. *1 MoveTo(hDC, xLeft, y): LineTo(hDC, xRight, MoveTo(hDC, xLeft, y): LineTo(hDC, xRight, MoveTo(hDC, xLeft, y): LineTo(hDC, xRight, Give YOllr applications support to order y coord 1* Draw handle. *1 MoveTo(hDC, p.x, p.y): y - q.y ScanJet and LaserJet support Cali (314) 962-7833 origPen xRight You can offer device control for gray scale scanning -- AND print halftones at any size. Victor supports Microsoft C, QuickC, and TurboC, includes demonstration and prototyping sofrware, and full documentation .. Victor Library version 2, $195 i* box at end of handle box *1 1* Set left and right coords around q.x (3 pixels). Windows 1i nes do not draw 1as t pi xe 1. *1 xLeft ~ q. x - LogPerDevi ce: Powerful image processing for all images -your sofrware can have features like: zoom, resize, brighten, contrast, sharpen, outline, linearize, matrix conv, colorize, & more. code available. y: /* Save original Image-based applications can be developed in MSC, QuickC, Load & save Source xRi ght: 1* DC's orinal pen * / 1* left coord of little 1* right coord of little HPEN ori gPen: int xLeft : 1* Re-select original pen. SelectObject(hDC, origPen): *1 y): y); y): y += LogPerDevice: y += LogPerDevice: y += LogPerDevice: *1