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

📄 3paint.html

📁 C ++ in action
💻 HTML
📖 第 1 页 / 共 4 页
字号:

<p>Creating a colored pen doesn't, by itself, change the color of lines being drawn. You still have to <i>select</i> your pen into the device context. According to the principles of Resource Management, we will encapsulate such a selection in an object called <var>Pen::Holder</var>--yes, that's the same class we used for embedding the classes <var>White</var> and <var>Black</var>. Defining a <i>named</i> object of the type <var>Pen::Holder</var> will temporarily change the color of all subsequent line drawings.

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>namespace Pen
{
    class Holder
    {
    public:
        Holder (HDC hdc, HPEN hPen)
            : _hdc (hdc)
        {
            _hPenOld = reinterpret_cast&lt;HPEN&gt; (
                           ::SelectObject (_hdc, hPen)); 
        }
        ~Holder ()
        {
            ::SelectObject (_hdc, _hPenOld);
        }
    private:
        HDC     _hdc;
        HPEN    _hPenOld;
    public:
        class White : public Win::StockObjectHolder
        ...
        class Black : public Win::StockObjectHolder
        ...
    };
}</pre>
</td></tr></table><!-- End Code -->

<p>Notice that, since a <var>Win::Canvas</var> can be implicitly converted to <var>HDC</var>, and a <var>Pen::Color</var> to <var>HPEN</var>; the holder's constructor is normally called with a <var>Win::Canvas</var> and a <var>Pen::Color</var>.

<p>The standard way to use pens and holders is to define and store your pens in the <var>View</var> object and then "hold" them for the duration of a particular drawing operation. Let's define two pen colors, dark red and light blue, for our drawings.

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>class View
{
public:
    View () 
        : _x (0), _y (0), 
          <b>_redPen</b> (128, 0, 0),
          <b>_bluePen</b> (0, 0, 255)
    {}
    ...
private:
    int _x, _y;
    Pen::Color <b>_redPen</b>;
    Pen::Color <b>_bluePen</b>;
};</pre>
</td></tr></table><!-- End Code -->

<p>We'll use the blue pen for our regular drawing:

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>void View::MoveTo (Win::Canvas &amp; canvas, int x, int y, bool visible)
{
    if (visible)
    {
        <b>Pen::Holder</b> ph (canvas, _bluePen);
        canvas.Line (_x, _y, x, y);
    }
    PrintPos (canvas, x, y);
}</pre>
</td></tr></table><!-- End Code -->
<p class="continue">and the red pen for re-drawing:

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>void View::Paint (Win::Canvas &amp; canvas, 
                  Model::iter beg, 
                  Model::iter end)
{
    <b>Pen::Holder</b> ph (canvas, _redPen);
    PrintPos (canvas, _x, _y);
    if (beg != end)
    {
        DrawLine draw (canvas, *beg);
        ++beg;
        std::for_each (beg, end, draw);
    }
}</pre>
</td></tr></table><!-- End Code -->

<p>This is not what you would normally do--visually distinguishing between active drawing and passive redrawing--but here we are trying to learn something. Play with this program and observe what areas are being redrawn when you uncover parts of your window or resize it. You'll notice that the red lines appear only in the freshly uncovered areas, even though our algorithm seems to be repainting the whole client area in response to <var>WM_PAINT</var> messages. 

<p>Well, not everyting your program draws is displayed by Windows. For instance, you must be aware of the fact that parts of the lines that fall outside of the boundaries of the client area are never displayed (they would overwrite other windows!). Windows <i>clips</i> all drawings down to the client area. In fact, during the processing of <var>WM_PAINT</var>, Windows clips your drawings even further--down to the invalidated area. And even though in <var>PaintCanvas</var>, Windows sends us a single bounding rectangle for our convenience, the invalidated area may be more complicated (e.g., multiple rectangles) and the <var>PaintCanvas</var> will clip all subsequent drawings down to this area. If you really want to draw outside of this area, you have to create an additional <var>UpdateCanvas</var> and work with it.

<p>But what should you do if, every time you draw, you want to use a different color? You can't possibly prepare all possible pens in the constructor of View! In that case, you are stuck with creating a pen on the spot, selecting it into the DC, and discarding it immediatly after deselecting. This use is common enough to call for a separate encapsulator, <var>Pen::Holder::Instant</var>.

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>namespace Pen
{
    class Holder
    {
    ...
    public:
    ...
        class Instant
        {
        public:
            Instant (HDC hdc, int r, int g, int b)
                : _hdc (hdc)
            {
                _hPen = ::CreatePen (PS_SOLID, 0, RGB (r, g, b));
                _hPenOld = reinterpret_cast&lt;HPEN&gt; (
                                   ::SelectObject (_hdc, _hPen));
            }
            ~Instant ()
            {
                ::SelectObject (_hdc, _hPenOld);
                ::DeleteObject (_hPen);
            }
        private:
            HDC     _hdc;
            HPEN    _hPen;
            HPEN    _hPenOld;
        };
    };
}</pre>
</td></tr></table><!-- End Code -->

<p>The same thing we've done with pens, you can do with brushes. A brush is used for filling areas--for instance if you draw a solid rectangle or ellipse. If you substitute the word Brush for Pen, you can almost directly copy the above code and use it for brushes. The only real difference is the use of the API <var>CreateSolidBrush</var> in place of <var>CreatePen</var>. There are other types of brushes besides solid--hatched and patterned--that you can use as well. Just provide the appropriate overloaded constructors for <var>Brush::Color</var>. 

<p>There is one special place where a brush is used--in the definition of a window class. This is the brush that is used for painting the window background. For instance, Windows automatically paints a fresh background over all the invalidated areas, in response to <var>BeginPaint</var> (called in the constructor of our <var>PaintCanvas</var>). By the way, <var>EndPaint</var> does the validation of this area, so if you forget to call it, Windows will keep sending <var>WM_PAINT</var> messages forever.

<p>To choose a non-default background color for the windows of a particular class, we have to create a brush and pass it to the <var>WNDCLASSEX</var> structure. The brush will be automatically destroyed when the application exits (or the class is discarded).


<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>void ClassMaker::SetBkColor (int r, int g, int b)
{
    _class.hbrBackground = ::<b>CreateSolidBrush</b> (RGB (r, g, b));
}</pre>
</td></tr></table><!-- End Code -->

<p>We call this method in main, setting the window background to light blue.

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>    Win::ClassMaker winClass (className, hInst);
    winClass.SetIcons (IDI_MAIN);
    winClass.<b>SetBkColor</b> (128, 128, 255);
    winClass.Register ();</pre>
</td></tr></table><!-- End Code -->

<p>Finally, we can play similar games with fonts. We can change the color of the printed text, the color of its background and even the type of the font. This last option, in particular, might help us with our incredible shrinking string. Changing the font from <i>proportional</i> to <i>fixed</i> does the trick. In a fixed-pitch font, all characters, including spaces, are the same size. We'll be able to get rid of the spurious spaces at the end of our string.

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>namespace Font
{
    class Stock: public Win::StockObject
    {
    public:
        Stock (int type) : Win::StockObject (type) {}
        operator HFONT () const 
        {
            return reinterpret_cast&lt;HFONT&gt; (_obj);
        }
    };

    class SysFixed: public Stock
    {
    public:
        SysFixed () : Stock (SYSTEM_FIXED_FONT) {}
    };

    class Holder
    {
    public:
        class Color
        {
        public:
            Color (HDC hdc, int r, int g, int b)
                : _hdc (hdc), 
                  _oldColor (::<b>SetTextColor</b> (_hdc, RGB (r, g, b)))
            {}
            ~Color ()
            {
                ::SetTextColor (_hdc, _oldColor);
            }
        private:
            HDC            _hdc;
            COLORREF    _oldColor;
        };
    
        class Background
        {
        public:
            Background (HDC hdc, int r, int g, int b)
                : _hdc (hdc),
                  _oldColor (::<b>SetBkColor</b> (_hdc, RGB (r, g, b)))
            {}
            ~Background ()
            {
                ::SetBkColor (_hdc, _oldColor);
            }
        private:
            HDC            _hdc;
            COLORREF    _oldColor;
        };
    
        class SysFixed : public Win::StockObjectHolder
        {
        public:
            SysFixed (HDC hdc)
                : Win::StockObjectHolder (hdc, <b>SYSTEM_FIXED_FONT</b>) 
			{}
        };
    };
}</pre>
</td></tr></table><!-- End Code -->

<p>Equipped with all these tools, we can now print the position of the mouse in yellow on dark blue, using a fixed-pitch system font.

<!-- Code --><table width=100% cellspacing=10><tr>    <td class=codetable>
<pre>void View::PrintPos (Win::Canvas &amp; canvas, int x, int y)
{
    ...
    Font::Holder::Color fc (canvas, 255, 255, 0); // yellow
    Font::Holder::Background bk (canvas, 0, 64, 128); // dk blue
    Font::Holder::SysFixed fix (canvas); // fixed pitch
    canvas.Text (0, 0, &amp;s [0], s.length ());
}
</pre>
</td></tr></table><!-- End Code -->

<p>

<h4>Stock Objects</h4>
<p>Here's a complete list of Windows stock objects.

<ul>
<li>Brushes
<ul>
    <li>WHITE_BRUSH
    <li>LTGRAY_BRUSH
    <li>GRAY_BRUSH
    <li>DKGRAY_BRUSH
    <li>BLACK_BRUSH
    <li>NULL_BRUSH
    <li>HOLLOW_BRUSH
    <li>DC_BRUSH
</ul>

<li>Pens
<ul>
    <li>WHITE_PEN
    <li>BLACK_PEN
    <li>NULL_PEN
    <li>DC_PEN
</ul>
<li>Fonts
<ul>
    <li>OEM_FIXED_FONT
    <li>ANSI_FIXED_FONT
    <li>ANSI_VAR_FONT
    <li>SYSTEM_FONT
    <li>DEVICE_DEFAULT_FONT
    <li>SYSTEM_FIXED_FONT
    <li>DEFAULT_GUI_FONT
</ul>
<li>Misc.
<ul>
    <li>DEFAULT_PALETTE
</ul>
</ul>

</table>
</html>

⌨️ 快捷键说明

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