home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Pascal / BPASCAL.700 / D12 / PAINT.ZIP / TOOLS.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1992-10-01  |  16.9 KB  |  605 lines

  1. {************************************************}
  2. {                                                }
  3. {   ObjectWindows Paint demo                     }
  4. {   Copyright (c) 1992 by Borland International  }
  5. {                                                }
  6. {************************************************}
  7.  
  8. unit Tools;
  9.  
  10. { This unit supplies the actual painting and drawing tools for the paint
  11.   program. Each tool manipulates the bits in a display context in a
  12.   specialized manner. The behaviour of each tool is defined. The icon and
  13.   cursor associated with a tool is specified when the tool is created, but
  14.   the tool itself does not make use of this information.
  15. }
  16.  
  17. interface
  18.  
  19. uses PaintDef, Rect, Strings, WinTypes, WinProcs;
  20.  
  21. type
  22.   { A Draw Tool is a tool whose action is instigated solely by mouse input.
  23.     The action is always fully performed within a single Mouse Down, Mouse
  24.     Move, Mouse Up cycle.
  25.  
  26.     TDrawTool performs the actions necessary to maintain the drawing
  27.     environment (storing window, display context, etc.) so that each tool need
  28.     only implement those of DrawBegin (called on Mouse Down), DrawTo (called
  29.     on Mouse Move) and DrawEnd (called on MouseUp) that perform actions 
  30.     peculiar to that tool.
  31.   }
  32.   PDrawTool = ^TDrawTool;            { Defined in PaintDef }
  33.   TDrawTool = object(TPaintTool)
  34.     Pen, MemPen: HPen;                    { The pens not in use }
  35.     Brush, MemBrush: HBrush;            { The brushes not in use }
  36.  
  37.     { Mouse responses }
  38.     procedure MouseDown(AWindow: HWnd; X, Y: Integer;
  39.       AState: PState); virtual;
  40.     procedure MouseMove(X, Y: Integer); virtual;
  41.     procedure MouseUp; virtual;
  42.   end;
  43.  
  44.   { A Pen tool draws a freeform line using the currently selected pen color
  45.     and width.
  46.   }
  47.   PPenTool = ^TPenTool;
  48.   TPenTool = object(TDrawTool)
  49.  
  50.     { Actual drawing }
  51.     procedure DrawBegin(X, Y: Integer); virtual;
  52.     procedure DrawTo(X, Y: Integer); virtual;
  53.   end;
  54.  
  55.   { An Eraser tool draws a freeform white line using the currently selected
  56.     pen width.
  57.   }
  58.   PEraserTool = ^TEraserTool;
  59.   TEraserTool = object(TPenTool)
  60.     Eraser, MemEraser: HPen;            { The pens not in use }
  61.  
  62.     { Actual drawing }
  63.     procedure DrawBegin(X, Y: Integer); virtual;
  64.     procedure DrawEnd; virtual;
  65.   end;
  66.  
  67.   { A Fill tool fills an area bounded by the current pen color with the
  68.     current brush color.
  69.   }
  70.   PFillTool = ^TFillTool;
  71.   TFillTool = object(TDrawTool)
  72.  
  73.     { Actual drawing }
  74.     procedure DrawBegin(X, Y: Integer); virtual;
  75.   end;
  76.  
  77.   { A Box tool is a tool that operates on a rectangularly bounded area. These
  78.     are tools whose actual drawing calls involve specifying this bounding
  79.     rectangle, e.g., for drawing a rectangle or oval, OR that perform
  80.     rubberbanding during drawing.
  81.   }
  82.   PBoxTool = ^TBoxTool;
  83.   TBoxTool = object(TDrawTool)
  84.     Filled: Boolean;             { Should the internal area be colored }
  85.     X1, Y1, X2, Y2: Integer;         { The bounding rectangle }
  86.  
  87.     { Creation }
  88.     constructor Init(AState: PState; IconName, CursorName: PChar; 
  89.                      AFilled: Boolean);
  90.  
  91.     { Actual drawing }
  92.     procedure DrawBegin(X, Y: Integer); virtual;
  93.     procedure DrawTo(X, Y: Integer); virtual;
  94.     procedure DrawEnd; virtual;
  95.     procedure DrawObject(aDC: HDC); virtual;
  96.   end;
  97.  
  98.   { A Rect tool is a tool that draws (or manipulates) a rectangle.
  99.   }
  100.   PRectTool = ^TRectTool;
  101.   TRectTool = object(TBoxTool)
  102.  
  103.     { Actual drawing }
  104.     procedure DrawObject(aDC: HDC); virtual;
  105.   end;
  106.  
  107.   { A Select tool selects and maintains a rectangular subset (the current 
  108.     selection) of the image. The selection may serve only to specify this
  109.     subset, or it may actively be manipulated (e.g., by dragging).
  110.     If it is used for dragging a separate bitmap is created that exactly
  111.     contains the portion of the image selected.
  112.   }
  113.   PSelectTool = ^TSelectTool;
  114.   TSelectTool = object(TRectTool)
  115.     SelectionDC: HDC;        { Display context for the current selection }
  116.  
  117.     { Creation }
  118.     constructor Init(AState: PState; IconName, CursorName: PChar;
  119.       AFilled: Boolean);
  120.  
  121.     { Re-initilization }
  122.     procedure Deselect; virtual;
  123.  
  124.     { Actual drawing }
  125.     procedure DrawBegin(X, Y: Integer); virtual;
  126.     procedure DrawTo(X, Y: Integer); virtual;
  127.     procedure DrawEnd; virtual;
  128.     procedure DrawObject(aDC: HDC); virtual;
  129.  
  130.     { Utilities }
  131.     procedure PickUpSelection(aDC: HDC; Left, Top, Width, Height: Integer);
  132.       virtual;
  133.     procedure ReleaseSelection; virtual;
  134.     procedure DropSelection; virtual;
  135.   end;
  136.  
  137.   { An Ellipse tool is a tool that draws an ellipse.
  138.   }
  139.   PEllipseTool = ^TEllipseTool;
  140.   TEllipseTool = object(TBoxTool)
  141.  
  142.     { Actual drawing }
  143.     procedure DrawObject(aDC: HDC); virtual;
  144.   end;
  145.  
  146.   { A Line tool draws a straight line.
  147.   }
  148.   PLineTool = ^TLineTool;
  149.   TLineTool = object(TBoxTool)
  150.     
  151.     { Actual drawing }
  152.     procedure DrawObject(aDC: HDC); virtual;
  153.   end;
  154.  
  155.  
  156. implementation
  157.  
  158. { TDrawTool }
  159.  
  160. { Set up the drawing environment for any drawing tool. Note that the
  161.   display context for the off-screen bitmap has already been set up.
  162.   
  163.   Since shared display contexts are used for the window, they should
  164.   be held as shortly as possible. Hence the display context for the window
  165.   is retrieve on each operation.
  166.  
  167. }
  168. procedure TDrawTool.MouseDown(AWindow: HWnd; X, Y: Integer; AState: PState);
  169. begin
  170.   { Set up the window and state }
  171.   Window := AWindow;
  172.   State := AState;
  173.  
  174.   { Direct all mouse input to Window }
  175.   SetCapture(Window);
  176.  
  177.   { Create the actual pens and brushes to be used }
  178.   Pen := CreatePen(ps_Solid, State^.PenSize, State^.PenColor); 
  179.   MemPen := CreatePen(ps_Solid, State^.PenSize, State^.PenColor); 
  180.   Brush := CreateSolidBrush(State^.BrushColor);
  181.   MemBrush := CreateSolidBrush(State^.BrushColor);
  182.  
  183.   { Set up the display contexts }
  184.   DC := GetDC(Window);        
  185.   SelectObject(DC, Pen);
  186.   SelectObject(State^.MemDC, MemPen);
  187.   SelectObject(DC, Brush);
  188.   SelectObject(State^.MemDC, MemBrush);
  189.  
  190.   DrawBegin(X, Y);          { Tell the tool to start drawing }
  191. end;
  192.  
  193. procedure TDrawTool.MouseMove(X, Y: Integer);
  194. begin
  195.   DrawTo(X, Y);              { Tell the tool to do its draw thing }
  196. end;
  197.  
  198. procedure TDrawTool.MouseUp;
  199. begin
  200.   DrawEnd;              { Tell the tool to stop drawing }
  201.  
  202.   { Clean up }
  203.   { Reset mouse input }
  204.   ReleaseCapture;
  205.  
  206.   { Restore display contexts }
  207.   SelectObject(DC, GetStockObject(Black_Pen));
  208.   SelectObject(State^.MemDC, GetStockObject(Black_Pen));
  209.   SelectObject(DC, GetStockObject(White_Brush));
  210.   SelectObject(State^.MemDC, GetStockObject(White_Brush));
  211.   
  212.   { Delete the created objects }
  213.   DeleteObject(Pen);
  214.   DeleteObject(MemPen);
  215.   DeleteObject(Brush);
  216.   DeleteObject(MemBrush);
  217.  
  218.   ReleaseDC(Window, DC);
  219. end;
  220.  
  221. { TPenTool }
  222.  
  223. { Actual drawing }
  224. procedure TPenTool.DrawBegin(X, Y: Integer);
  225. begin
  226.   MoveTo(DC, X, Y);                { Move the pen position }
  227.   MoveTo(State^.MemDC, X+State^.Offset.X, Y+State^.Offset.Y);    { Echo }
  228.   DrawTo(X, Y);                    { Draw the initial pixel(s) }
  229. end;
  230.  
  231. procedure TPenTool.DrawTo(X, Y: Integer);
  232. begin
  233.   LineTo(DC, X, Y);                { Draw a line from the pen position }
  234.   LineTo(State^.MemDC, X+State^.Offset.X, Y+State^.Offset.Y);    { Echo }
  235. end;
  236.  
  237. { TEraserTool }
  238.  
  239. { Actual drawing }
  240. procedure TEraserTool.DrawBegin(X, Y: Integer);
  241. begin
  242.   { Create an erasing pen and reset the display context }
  243.   Eraser := CreatePen(ps_Solid, State^.PenSize, $FFFFFF);
  244.   MemEraser := CreatePen(ps_Solid, State^.PenSize, $FFFFFF);
  245.   SelectObject(DC, Eraser);
  246.   SelectObject(State^.MemDC, MemEraser);
  247.  
  248.   SelectObject(DC, GetStockObject(White_Brush));
  249.   SelectObject(State^.MemDC, GetStockObject(White_Brush));
  250.  
  251.   TPenTool.DrawBegin(X, Y);    { Start drawing }
  252. end;
  253.  
  254. procedure TEraserTool.DrawEnd;
  255. begin
  256.   { Clean up }
  257.   SelectObject(DC, Pen);
  258.   SelectObject(State^.MemDC, MemPen);
  259.   DeleteObject(Eraser);
  260.   DeleteObject(MemEraser);
  261. end;
  262.  
  263. { TFillTool }
  264.  
  265. procedure TFillTool.DrawBegin(X, Y: Integer);
  266. begin
  267.  FloodFill(DC, X, Y, State^.PenColor);    { Fills the area bounded by
  268.                                           PenColor }
  269.  FloodFill(State^.MemDC, X, Y, State^.PenColor); { Echo }
  270. end;
  271.  
  272. { TBoxTool }
  273.  
  274. { Creation }
  275. constructor TBoxTool.Init(AState: PState; IconName, CursorName:
  276.   PChar; AFilled: Boolean);
  277. begin
  278.   TDrawTool.Init(AState, IconName, CursorName);
  279.   Filled := AFilled;        { Record whether tool operates on outline }
  280.                                 { or outline and bounded area }
  281. end;
  282.  
  283. { Actual drawing }
  284. { During the drawing a BoxTool rubberbands a black outline of the final 
  285.   object on the screen by alternately erasing and redrawing the outline. }
  286. procedure TBoxTool.DrawBegin(X, Y: Integer);
  287. begin
  288.   X1 := X;            { Initially the rectangle is a single pixel }
  289.   Y1 := Y;
  290.   X2 := X;
  291.   Y2 := Y;
  292.  
  293.   { Set up the display context to draw a black outline during drawing }
  294.   SelectObject(DC, GetStockObject(Black_Pen));
  295.   SelectObject(DC, GetStockObject(Null_Brush));
  296.  
  297.   { Invert pixels under the pen }
  298.   SetROP2(DC, r2_Not);
  299.  
  300.   { Draw the initial outline }
  301.   DrawObject(DC);
  302. end;
  303.  
  304. procedure TBoxTool.DrawTo(X, Y: Integer);
  305. begin
  306.   { Draw over the outline last drawn. Since the pen inverts pixels and is
  307.     black this will erase the last outline. }
  308.   DrawObject(DC);
  309.  
  310.   { Update the rectangle to be operated on }
  311.   X2 := X;    
  312.   Y2 := Y;
  313.  
  314.   { Draw the new outline }
  315.   DrawObject(DC);
  316. end;
  317.  
  318. procedure TBoxTool.DrawEnd;
  319. begin
  320.   { Erase the last outline drawn }
  321.   DrawObject(DC);
  322.  
  323.   { Set up the display context to draw the real image }  
  324.   SetROP2(DC, r2_CopyPen);
  325.   SelectObject(DC, Pen);
  326.   if Filled then 
  327.     SelectObject(DC, Brush)
  328.   else
  329.     SelectObject(State^.MemDC, GetStockObject(Null_Brush));
  330.  
  331.   { Draw the actual image }
  332.   DrawObject(DC);
  333.   with State^ do
  334.   begin
  335.     X1 := X1 + Offset.X;
  336.     Y1 := Y1 + Offset.Y;
  337.     X2 := X2 + Offset.X;
  338.     Y2 := Y2 + Offset.Y;
  339.   end;
  340.   DrawObject(State^.MemDC);
  341. end;
  342.  
  343. { Allow the real tool to specify the image it draws.
  344. }
  345. procedure TBoxTool.DrawObject(aDC: HDC);
  346. begin
  347. end;
  348.  
  349. { TRectTool }
  350.  
  351. { Draw a rectangle.
  352. }
  353. procedure TRectTool.DrawObject(aDC: HDC);
  354. begin
  355.   Rectangle(aDC, X1, Y1, X2, Y2);
  356. end;
  357.  
  358. { TSelectTool }
  359.  
  360. { Creation }
  361. constructor TSelectTool.Init(AState: PState; IconName, CursorName: PChar;
  362.   AFilled: Boolean);
  363. begin
  364.   TRectTool.Init(AState, IconName, CursorName, AFilled);
  365.   SelectionDC := 0;
  366. end;
  367.  
  368. { Utility }
  369. { Make sure there is no active selection before exiting. If there is an image
  370.   in the selection paste it into the current image.
  371. }
  372. procedure TSelectTool.Deselect;
  373. begin
  374.   DropSelection;
  375. end;
  376.  
  377. { Actual drawing }
  378. { The selection tool has two states of operation: While the selection is
  379.   being made, it operates as a rectangle tool. If a selection has been made
  380.   and the mouse clicks on it, the selection is dragged with the mouse.
  381.  
  382.   SelectionDC is valid only during dragging and thus serves as the
  383.   flag to distinguish the two modes during drawing.
  384.  
  385.   Dragging the selection is effected by creating a copy (i.e., a
  386.   bitmap) of the selection and alternately restoring the screen to the
  387.   original (actually, only restoring those pieces that are revealed by
  388.   moving the selection), and copying the selection bitmap to the screen.
  389.  
  390.   Throughout dragging
  391.     X1, Y1 contains the previous mouse position
  392.     State^.Selection contains the current coordinates of the selection
  393. }
  394. procedure TSelectTool.DrawBegin(X, Y: Integer);
  395. var
  396.   Pt: TPoint;
  397. begin
  398.   { Check to see if there is a hit on the selection }
  399.   Pt.X := X;
  400.   Pt.Y := Y;
  401.   if PtInRect(State^.Selection, Pt) then
  402.     { Drag selection }
  403.   begin
  404.     { Last mouse position }
  405.     X1 := X;
  406.     Y1 := Y;
  407.  
  408.     { Create the selection bitmap if necessary. (It may already have been
  409.       created, for example through a Paste operation.) }
  410.     if State^.SelectionBM = 0 then
  411.       with State^.Selection, State^ do
  412.       begin
  413.     PickUpSelection(MemDC, Left + Offset.X, Top + Offset.Y,
  414.       Right-Left, Bottom-Top);
  415.  
  416.         { The convention is to cut the selection, so white out
  417.           the hole }
  418.     PatBlt(MemDC, Left + Offset.X, Top + Offset.Y,
  419.       Right - Left, Bottom - Top, Whiteness);
  420.       end;
  421.  
  422.     { Set up the selection display context }
  423.     SelectionDC := CreateCompatibleDC(DC);
  424.     State^.SelectionBM := SelectObject(SelectionDC, State^.SelectionBM);
  425.   end
  426.   else
  427.   { Make new selection }
  428.   begin
  429.     { Paste down the old one if there is one }
  430.     DropSelection;
  431.     TRectTool.DrawBegin(X, Y);
  432.   end;
  433. end;
  434.  
  435. procedure TSelectTool.DrawTo(X, Y: Integer);
  436. var
  437.   I, Count: Integer;        { Number of rectangles that must be restored }
  438.   MoveX, MoveY: Integer;    { Change in X, Y coordinates of selection }
  439.   Result: RectArray;        { Rectangles that must be restored }
  440.   NewCoords: TRect;        { The new coordinates of selection }
  441. begin
  442.   if SelectionDC <> 0 then    { Dragging }
  443.   begin
  444.  
  445.     { Figure out the new coordinates }
  446.     MoveX := X - X1;
  447.     MoveY := Y - Y1;
  448.     with State^.Selection do
  449.       SetRect(NewCoords, Left + MoveX, Top + MoveY, Right + MoveX,
  450.         Bottom + MoveY);
  451.  
  452.     { Determine the area that must be repainted. Note that this will always
  453.       be 0, 1, or 2 rectangles exactly }
  454.     Count := SubtractRect(Result, State^.Selection, NewCoords);
  455.  
  456.     { Repaint the rectangles revealed by the move }
  457.     for I := 0 to Count-1 do
  458.       with Result[I], State^ do
  459.     BitBlt(DC, Left, Top, Right - Left, Bottom - Top,
  460.       State^.MemDC, Left + Offset.X, Top + Offset.Y, SrcCopy);
  461.  
  462.     { Update and repaint the selection }
  463.     with NewCoords do
  464.       SetRect(State^.Selection, Left, Top, Right, Bottom);
  465.     X1 := X;
  466.     Y1 := Y;
  467.     DrawObject(DC);
  468.   end
  469.   else                { Selecting }
  470.     TRectTool.DrawTo(X, Y);
  471. end;
  472.  
  473. procedure TSelectTool.DrawEnd;
  474.  
  475.   procedure Sort(var N1, N2: Integer);
  476.   var
  477.     Temp: Integer;
  478.   begin
  479.     if N1 > N2 then
  480.     begin
  481.       Temp := N1;
  482.       N1 := N2;
  483.       N2 := Temp;
  484.     end;
  485.   end;
  486.  
  487. begin
  488.   DrawObject(DC);
  489.   if SelectionDC <> 0 then
  490.   begin
  491.     { Clean up }
  492.     State^.SelectionBM := SelectObject(SelectionDC, State^.SelectionBM);
  493.     DeleteDC(SelectionDC);
  494.     SelectionDC := 0;
  495.   end
  496.   else
  497.   begin
  498.     { Update the selection }
  499.     Sort(X1, X2);
  500.     Sort(Y1, Y2);
  501.     SetRect(State^.Selection, X1, Y1, X2, Y2);
  502.   end;
  503. end;
  504.  
  505. procedure TSelectTool.DrawObject(aDC: HDC);
  506. begin
  507.   if SelectionDC <> 0 then
  508.     { Draw the selection bitmap }
  509.     with State^.Selection, State^ do
  510.       BitBlt(aDC, Left, Top, Right-Left, Bottom-Top,
  511.     SelectionDC, 0, 0, SrcCopy)
  512.   else
  513.     { Pretend to be a rectangle }
  514.     TRectTool.DrawObject(aDC)
  515. end;
  516.  
  517. { Utilities }
  518. { Set the selection bitmap to be a bitmap that contains a copy of the
  519.   bits contained in the indicated rectangle of the bitmap in a drawing
  520.   context.
  521. }
  522. procedure TSelectTool.PickUpSelection(aDC: HDC; Left, Top, Width,
  523.   Height: Integer);
  524. var
  525.   SelDC: HDC;            { For copying into the selection bitmap }
  526. begin
  527.   { Paste down the current selection if there is one }
  528.   if State^.SelectionBM <> 0 then DropSelection;
  529.  
  530.   { Set the default screen coordinates for the selection if necessary }
  531.   if IsRectEmpty(State^.Selection) then 
  532.     SetRect(State^.Selection, 0, 0, Width, Height);
  533.   
  534.   { Create the selection bitmap and copy the bits }
  535.   SelDC := CreateCompatibleDC(aDC);
  536.   State^.SelectionBM := CreateCompatibleBitmap(aDC, Width, Height);
  537.   State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  538.   BitBlt(SelDC, 0, 0, Width, Height, aDC, Left, Top, SrcCopy);
  539.  
  540.   { Clean up }
  541.   State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  542.   DeleteDC(SelDC);
  543. end;
  544.  
  545. { Set the current selection to none without copying back the selection bitmap.
  546. }
  547. procedure TSelectTool.ReleaseSelection;
  548. begin
  549.   if not IsRectEmpty(State^.Selection) then
  550.   begin
  551.     InvalidateRect(Window, @State^.Selection, False);
  552.     SetRectEmpty(State^.Selection);
  553.     if State^.SelectionBM <> 0 then
  554.     begin
  555.       DeleteObject(State^.SelectionBM);
  556.       State^.SelectionBM := 0;
  557.     end;
  558.   end;
  559. end;
  560.  
  561. { Set the current selection to none, but paste the selection bitmap down.
  562. }
  563. procedure TSelectTool.DropSelection;
  564. var
  565.   SelDC: HDC;
  566. begin
  567.   if State^.SelectionBM <> 0 then
  568.   begin
  569.     { Mark the bitmap as having been modified }
  570.     State^.IsDirtyBitmap := True;
  571.  
  572.     { Copy the selection bitmap back }
  573.     SelDC := CreateCompatibleDCW(Window);
  574.     State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  575.     with State^.Selection, State^ do
  576.       BitBlt(MemDC, Left + Offset.X, Top + Offset.Y,
  577.         Right + Offset.X, Bottom + Offset.Y, SelDC, 0, 0, SrcCopy);
  578.     State^.SelectionBM := SelectObject(SelDC, State^.SelectionBM);
  579.  
  580.     { Clean up }
  581.     DeleteDC(SelDC);
  582.   end;
  583.   ReleaseSelection;
  584. end;
  585.  
  586. { TEllipseTool }
  587.  
  588. { Draw an ellipse.
  589. }
  590. procedure TEllipseTool.DrawObject(aDC: HDC);
  591. begin
  592.   Ellipse(aDC, X1, Y1, X2, Y2);
  593. end;
  594.  
  595. { TLineTool }
  596.  
  597. { Actual drawing }
  598. procedure TLineTool.DrawObject(aDC: HDC);
  599. begin
  600.   MoveTo(aDC, X1, Y1);
  601.   LineTo(aDC, X2, Y2);
  602. end;
  603.  
  604. end.
  605.