⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch07.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 5 页
字号:

<UL>
	<LI>If you <TT>XOR</TT> a square to the screen, the square will show up clearly.
	<P>
	<LI>If you <TT>XOR</TT> that same image again in the same location, the image will
	disappear.
</UL>


<P>Such are the virtues of simple logical operations in graphics mode.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Aficionados of graphics logic will
	note that the logical operation employed by 
<TT>DrawShapes</TT> is a variation on
	the exclusive <TT>OR</TT> (<TT>XOR</TT>) operation. This variation ensures that the
	fill in the center of the shape to be drawn won't blot out what's beneath it. The
	Microsoft documentation explains the 
difference like this:</P>
	<PRE><FONT COLOR="#0066FF">R2_XOR:        final pixel = pen ^ screen pixel

R2_NOTXORPEN : final pixel = ~(pen ^ screen pixel)</FONT></PRE>
	<P>This code tests to see whether the pixels to be <TT>XOR</TT>ed belong to a pen.
	
Don't waste too much time worrying about logical operations and how they work. If
	they interest you, fine; if they don't, that's okay. The subject matter of this book
	is programming, not logic. <BR>
	<BR>
	If you were working directly in the Windows 
API, you would work with a constant called
	<TT>RT_NOTXORPEN</TT> rather than <TT>pmNotXor</TT>. I have to confess that the VCL's
	tendency to rename constants used by the Windows API is not a very winning trait.
	Granted, the people in Redmond who 
came up with many of those identifiers deserve
	some terrible, nameless, fate, but once the damage had been done it might have been
	simpler to stick with the original constants. That way people would not have to memorize
	two sets of identifiers, one 
for use with the VCL, and the other for use with the
	Windows API. You cannot use the Windows constants in place of the VCL constants,
	as the various identifiers do not map down to the same value. <BR>
	<BR>
	Despite these objections, I still think 
it is wise to use the VCL rather than writing
	directly to the Windows API. The VCL is much safer and much easier to use. The performance
	from most VCL objects is great, and in many cases it will be better than what most
	programmers could achieve 
writing directly to the Windows API. 
<HR>


</BLOCKQUOTE>

<P>Notice that <TT>FormMouseMove</TT> calls <TT>DrawShape</TT> twice. The first time,
it passes in the dimensions of the old figure that needs to be erased. That means
it <TT>XOR</TT>s the 
same image directly on top of the original image, thereby erasing
it. Then <TT>FormMouseMove</TT> records the location of the latest <TT>WM_MOUSEMOVE</TT>
message and passes this new information to <TT>DrawShape</TT>, which paints the new
image to the 
screen. This whole process is repeated over and over again (at incredible
speeds) until the user lifts the left mouse button.</P>
<P>In the <TT>DrawImage</TT> function, <TT>Metaphor</TT> first checks to see which
shape the user has selected and then 
proceeds to draw that shape to the screen using
the current pen and fill color:</P>
<PRE><FONT COLOR="#0066FF">void __fastcall TForm1::DrawShape()

{

  Canvas-&gt;Brush-&gt;Color = FBrushColor;

  Canvas-&gt;Brush-&gt;Style = bsSolid;

  
Canvas-&gt;Pen-&gt;Color = FPenColor;

  Canvas-&gt;Pen-&gt;Width = FPenThickness;

  switch (FCurrentShape)

  {

    case csLine:

      Canvas-&gt;MoveTo(FShapeRect.Left, FShapeRect.Top);

      Canvas-&gt;LineTo(FShapeRect.Right, 
FShapeRect.Bottom);

      break;

    case csRectangle:

      Canvas-&gt;Rectangle(FShapeRect.Left, FShapeRect.Top,

                        FShapeRect.Right, FShapeRect.Bottom);

      break;

    case csEllipse:

      
Canvas-&gt;Ellipse(FShapeRect.Left, FShapeRect.Top,

                      FShapeRect.Right, FShapeRect.Bottom);

      break;

    default:

      ;

  }

}

</FONT></PRE>
<P>This code sets the current <TT>Pen</TT> and <TT>Brush</TT> to the values 
chosen
by the user. It then uses a <TT>switch</TT> statement to select the proper type of
shape to draw to the screen. Most of these private variables such as <TT>FPenColor</TT>
are set by allowing the user to make selections from the menu. To see 
exactly how
this works, you can study the code of the application.</P>
<P>Notice that when drawing these shapes, there is no need to track the HDC of the
current <TT>Canvas</TT>. One of the primary goals of the <TT>TCanvas</TT> object
is to completely 
hide the HDC from the user. I discuss this matter in more depth
in the next section of the chapter, &quot;To GDI or not to GDI.&quot;</P>
<P>The final step in the whole operation occurs when the user lifts his finger off
the mouse:</P>
<PRE><FONT 
COLOR="#0066FF">void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,

                                    TShiftState Shift, int X, int Y)

{

  FDrawing = False;

  FShapeRect.Right = X;

  FShapeRect.Bottom = Y;

  
Canvas-&gt;Pen-&gt;Mode = pmCopy;

  DrawShape();

}

</FONT></PRE>
<P>This code performs the following actions:

<UL>
	<LI>A flag is set stating that the user has decided to stop drawing.
	<P>
	<LI>The final dimensions of the shape are recorded.
	<P>
	
<LI>The mode for the <TT>Pen</TT> is switched from <TT>pmNotXor</TT> to the default
	value, which is <TT>pmCopy</TT>.
	<P>
	<LI>The final image is painted to the screen.
</UL>

<P>The code that paints the final shape takes into account the colors and 
the pen
thickness that the user selected with the menus.</P>
<P>Well, there you have it. That's how you draw shapes to the screen using the rubber-band
technique. Overall, if you take one thing at a time, the process isn't too complicated.
Just so you 
can keep those steps clear in your mind, here they are again:

<UL>
	<LI>Remember where the <TT>WM_LBUTTONDOWN</TT> message took place.
	<P>
	<LI>Draw the shape each time you get a <TT>WM_MOUSEMOVE</TT> message.
	<P>
	<LI>Draw the final shape when you 
get a <TT>WM_LBUTTONUP</TT> message.
</UL>

<P>That's all there is to it.
<H4><A NAME="Heading13"></A><FONT COLOR="#000077">To GDI or not to GDI</FONT></H4>
<P>The VCL will never cut you off completely from the underlying Windows API code.
If you want 
to work at the Windows API level, you can do so. In fact, you can often
write code that mixes VCL and raw Windows API code.</P>
<P>If you want to access the HDC for a window, you can get at it through the <TT>Handle</TT>
property of the canvas:</P>

<PRE><FONT COLOR="#0066FF">MyOldPenHandle = SelectObject(Canvas-&gt;Handle, MyPen-&gt;Handle);

</FONT></PRE>
<P>In this case you are copying the <TT>Handle</TT> of a <TT>Pen</TT> object into
the HDC of the <TT>TCanvas</TT> object.</P>
<P>Having free 
access to the <TT>Handle</TT> of the <TT>TCanvas</TT> object can be
useful at times, but the longer I use the VCL, the less inclined I am to use it.
The simple truth of the matter is that I now believe that it is best to let an object
of some sort 
handle all chores that require serious housekeeping. This course of
action allows me to rely on the object's internal logic to correctly track the resources
involved.</P>
<P>If you are not using the VCL, whenever you select something into an HDC, you 
need
to keep track of the resources pumped out of the HDC by the selection. When you are
done, you should then copy the old <TT>Handle</TT> back into the HDC. If you accidentally
lose track of a resource, you can upset the balance of the entire 
operating system.
Clearly, this type of process is error-prone and best managed by an object that can
be debugged once and reused many times. My options, therefore, are to either write
the object myself or use the existing code found in the VCL. In 
most cases, I simply
take the simplest course and use the excellent <TT>TCanvas</TT> object provided by
the VCL.</P>
<P>When I do decide to manage an interaction with the GDI myself, I often prefer
to get hold of my own HDC and ignore the 
<TT>TCanvas</TT> object altogether. The
reason I take this course is simply that the <TT>TCanvas</TT> object will sometimes
maintain the <TT>Canvas</TT>'s HDC on its own, and I therefore can't have complete
control over what is happening to it.</P>

<P>Here is a simple example of how to use the GDI directly inside a VCL program:</P>
<PRE><FONT COLOR="#0066FF">HDC DC = GetDC(Form1-&gt;Handle);

HFONT OldFont = SelectObject(DC, Canvas-&gt;Font-&gt;Handle);

TextOut(DC, 1, 100, &quot;Text&quot;, 4);


SelectObject(DC, OldFont);

ReleaseDC(Form1-&gt;Handle, DC);

</FONT></PRE>
<P>If you look at the code shown here, you will see that I get my own DC by calling
the Windows API function <TT>GetDC</TT>. I then select a new font into the DC. Notice
that 
I use the VCL <TT>TFont</TT> object. I usually find it easier to manage <TT>Font</TT>s,
<TT>Pen</TT>s, and <TT>Brush</TT>es with VCL objects than with raw Windows API code.
However, if you want to create your own fonts with raw Windows API code, you 
are
free to do so.</P>
<P>Here is an example of creating a VCL <TT>Font</TT> object from scratch:</P>
<PRE><FONT COLOR="#0066FF">TFont *Font = new TFont();

Font-&gt;Name = &quot;Time New Roman&quot;;

Font-&gt;Size = 25;

Font-&gt;Style = 
TFontStyles() &lt;&lt; fsItalic &lt;&lt; fsBold;

HDC DC = GetDC(Form1-&gt;Handle);

HFONT OldFont = SelectObject(DC, Font-&gt;Handle);

TextOut(DC, 1, 100, &quot;Text&quot;, 4);

SelectObject(DC, OldFont);

ReleaseDC(Form1-&gt;Handle, DC);

delete 
Font;

</FONT></PRE>
<P>This code allocates memory for a <TT>Font</TT> object, assigns some values to
its key properties, and then copies the <TT>Handle</TT> of the <TT>Font</TT> object
into the current DC. Notice that I still have to save the old 
<TT>Font</TT> handle
and copy it back into the DC when I am done with it. This is the type of operation
that I prefer to have handled by an object. When I am done with the example code
shown here, I delete the <TT>Font</TT> object I created.</P>

<P>Examples such as the ones you have just seen show that you can use the Windows
API by itself inside a VCL graphics operation, or you can combine VCL code with raw
GDI code. The course you choose will be dictated by your particular needs. My 
suggestion,
however, is to use the VCL whenever possible, and to fall back on the Windows API
only when strictly necessary. The primary reason for this preference is that it is
safer to use the VCL than to write directly to the Windows API. It is also 
easier
to use the VCL than the raw Windows API, but that argument has a secondary importance
in my mind.
<H3><A NAME="Heading14"></A><FONT COLOR="#000077">Using TImage, Saving and Loading
Bitmaps</FONT></H3>
<P>If you work with the DrawShapes program 
for awhile, you will find that it has
several weaknesses that cry out for correction. In particular, the program cannot
save an image to disk, and it cannot repaint the current image if you temporarily
cover it up with another program.</P>
<P>Fixing 
these problems turns out to be remarkably simple. The key functionality
to add to the program is all bound up in a single component called <TT>TImage</TT>.
This control provides you with a <TT>Canvas</TT> on which you can draw, and gives
you the 
ability to convert this <TT>Canvas</TT> into a bitmap.</P>
<P>The BitmapShapes program, shown in Listing 7.3, shows how to proceed in the creation
of an updated DrawShapes program that can save files to disk, and can automatically
redraw an image if 
you switch back to the main form from another program.<BR>
<BR>
<A NAME="Heading15"></A><FONT COLOR="#000077"><B>Listing 7.3. The main module for
the BitmapShapes program.</B></FONT></P>
<PRE><FONT 
COLOR="#0066FF">///////////////////////////////////////

// Main.cpp

// DrawShapes Example

// Copyright (c) 1997 by Charlie Calvert

//

#include &lt;vcl\vcl.h&gt;

#pragma hdrstop

#include &quot;Main.h&quot;

#pragma resource &quot;*.dfm&quot;


TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

  FDrawing= False;

  FCurrentShape = csRectangle;

  FBrushColor = clBlue;

  FPenColor = clYellow;

  FPenThickness = 1;

}

void __fastcall 
TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,

                                      TShiftState Shift, int X, int Y)

{

  FShapeRect.Left = X;

  FShapeRect.Top = Y;

  FShapeRect.Right = - 32000;

  FDrawing = True;

}

void 
__fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,

                                    TShiftState Shift, int X, int Y)

{

  FDrawing = False;

  FShapeRect.Right = X;

  FShapeRect.Bottom = Y;

  Image1-&gt;Canvas-&gt;Pen-&gt;Mode 
= pmCopy;

  DrawShape();

}

void __fastcall TForm1::DrawShape()

{

  Image1-&gt;Canvas-&gt;Brush-&gt;Color = FBrushColor;

  Image1-&gt;Canvas-&gt;Brush-&gt;Style = bsSolid;

  Image1-&gt;Canvas-&gt;Pen-&gt;Color = FPenColor;

  
Image1-&gt;Canvas-&gt;Pen-&gt;Width = FPenThickness;

  switch (FCurrentShape)

  {

    case csLine:

      Image1-&gt;Canvas-&gt;MoveTo(FShapeRect.Left, FShapeRect.Top);

      Image1-&gt;Canvas-&gt;LineTo(FShapeRect.Right, FShapeRect.Bottom);

      
break;

    case csRectangle:

      Image1-&gt;Canvas-&gt;Rectangle(FShapeRect.Left, FShapeRect.Top,

        FShapeRect.Right, FShapeRect.Bottom);

      break;

    case csEllipse:

        Image1-&gt;Canvas-&gt;Ellipse(FShapeRect.Left, 
FShapeRect.Top,

        FShapeRect.Right, FShapeRect.Bottom);

      break;

    default:

      ;

  }

}

void __fastcall TForm1::FormMouseMove(TObject *Sender,

                                      TShiftState Shift, int X, int Y)

{

  if 
(FDrawing)

  {

    Image1-&gt;Canvas-&gt;Pen-&gt;Mode = pmNotXor;

    if (FShapeRect.Right != -32000)

      DrawShape();

    FShapeRect.Right = X;

    FShapeRect.Bottom = Y;

    DrawShape();

  }

}

void __fastcall 
TForm1::SpeedButton1Click(TObject *Sender)

{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -