restoring raster graphics and rubberbands to .net

17
Restoring Raster Graphics and Rubber Bands to .NET By David Ross Goben Copyright © 2011 by David Ross Goben All rights reserved. Last Update: Wednesday, June 08, 2011 This is a sample excerpt from the free PDF e-book, Enhancing Visual Basic .NET Far Beyond the Scope of Visual Basic 6.0, by David Ross Goben. Download this e-book, and its free companion, Navigating Your Way Through Visual Basic 6.0 Upgrades to Visual Basic .NET, also by David Ross Goben, at www.slideshare.net/davidrossgoben . They are also available on Google Docs at https://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout =list&num=50 . The Google site also contains the source code in a file named Restore Raster Graphics and Rubberbands Source.zip. Table of Contents Restoring Raster Graphics and Rubber Bands to .NET ................................................................................223 Getting an Image Graphics Object Without Going Through the Paint() Event ......................................................... 224 Restoring Raster Operations to .NET ...................................................................................................................... 224 Getting One’s Hands Dirty................................................................................................................................... 225 The GDI32 Class ................................................................................................................................................ 227 Emulating a Selection Rubber Band under GDI....................................................................................................... 233 Emulating a Selection Rubber Band under GDI+..................................................................................................... 234 Using the GDI+ DrawReversibleFrame() Method.................................................................................................. 234 Using the GDI+ DrawRectangle() Method and a Translucent Brush ...................................................................... 235 About the Author ............................................................................................................................................ 237

Upload: david-goben

Post on 01-Sep-2014

930 views

Category:

Technology


3 download

DESCRIPTION

Described the ins and outs of GDI32 Raster Graphics under .NET. It also demonstrates how to perform rubberband selection of objects on a form. Additional demonstrations show how to use GDI+ to draw selection rectangles. The first GDI+ example demonstrates the DrawReverseRectangle() method, and explains its shortcomings. It also demonstrates how to cleaning draw a GDI+ selection rectangle with a translucent background, as is standard on Vista and Windows 7.

TRANSCRIPT

Page 1: Restoring raster graphics and rubberbands to .net

Restoring Raster Graphics and Rubber Bands to .NET

By David Ross Goben Copyright © 2011 by David Ross Goben All rights reserved.

Last Update: Wednesday, June 08, 2011

This is a sample excerpt from the free PDF e-book, Enhancing Visual Basic .NET Far Beyond the Scope of Visual Basic 6.0, by David Ross Goben. Download this e-book, and its free companion, Navigating Your Way Through Visual Basic 6.0 Upgrades to Visual Basic .NET, also by David Ross Goben, at www.slideshare.net/davidrossgoben. They are also available on Google Docs at https://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout=list&num=50. The Google site also contains the source code in a file named Restore Raster Graphics and Rubberbands Source.zip.

Table of Contents

Restoring Raster Graphics and Rubber Bands to .NET................................................................................223 Getting an Image Graphics Object Without Going Through the Paint() Event.........................................................224 Restoring Raster Operations to .NET ......................................................................................................................224 Getting One’s Hands Dirty................................................................................................................................... 225 The GDI32 Class ................................................................................................................................................ 227 Emulating a Selection Rubber Band under GDI.......................................................................................................233 Emulating a Selection Rubber Band under GDI+.....................................................................................................234 Using the GDI+ DrawReversibleFrame() Method.................................................................................................. 234 Using the GDI+ DrawRectangle() Method and a Translucent Brush ...................................................................... 235 About the Author ............................................................................................................................................237

Page 2: Restoring raster graphics and rubberbands to .net

Page –223–

Restoring Raster Graphics and Rubber Bands to .NET You may be quick to notice that VB.NET does not support general drawing commands as a part of its

intrinsic language repertoire, as had been the case under VB6. I say thank goodness! I still cannot believe

that the very primitive DOS-BASIC-style drawing commands had managed to make their way all the way

into VB6, and especially as a part of the fundamental language, itself. I had been very fearful that the broad

and largely non-professional, albeit fiercely devoted user-base would force VB.NET to end up suffering a

similar fate during its development cycle. Just look at the damage hoards of die-hard fans had forced on

bitwise and logical operations, changing them from innovative and more clearly defined to just archaic and

so-so (“Mongo fears change,” to quote Mongo from Mel Brook’s classic comedy, Blazing Saddles).

Under .NET, any specialized features, such as drawing, are grouped under a separate namespace. This is a

very good thing. By placing them as members in a separate static class, we can use the methods in that class

to draw on graphical objects, or, later, when more advanced graphical capabilities are added to .NET, we can

instead use the members of the namespace devoted to that paradigm. In the meantime we are not cluttering

up the language, as was sadly done to VB6. For example, suppose someone had developed a class supporting

OpenGL or Direct3D drawing that often used “standard” drawing commands. If we forget to specify or

import their namespace, we would find ourselves drawing using standard features instead of enhanced

features, and we would then have to track down the bugs by finding where we forgot to reference our

graphical class. By keeping all these things in different namespaces, we have simplified the language and

allowed for easy expansion of its capabilities, and there is also less likelihood for method name collisions.

However, as easy and better as all this is, drawing is accordingly different under VB.NET than under VB6.

Granted, some stuff is easy, like changing the VB6 Cls command to Clear under VB.NET.

Others are easy to adapt to, such as using the System.Drawing.Graphics object to draw shapes, lines, arcs, circles, etc. to a PictureBox. Indeed, these are now much easier to use, and are also a lot faster.

Some others do not exist any more, because it is much easier to provide and maintain these features within

your own code; they no longer eat up valuable resource space if you do not use them, which is the case in

most situations. Cases in point are the CurrentX and CurrentY last drawing location properties. These

features are easy enough to compute and update locally without eating control resources to store them for every object, when only a few of them would actually need to make use of them.

Others are not obvious. For instance, the VB6 Point command, now GetPixel(), to get a point on an

image, must be accessed through a Bitmap object, which, logically, is where such a command should be.

And regarding Bitmap objects – most examples tell you to create a new Bitmap object and fill it will the

image from the PictureBox, but this is a waste of time and resources if we just need to access the image or

quickly draw to it. Indeed, the only time we should really need to do something like that is if we will be

making numerous changes to the image, but we want to update the actual displayed image only once, to

eliminate flicker. Otherwise, instead of chewing up resources and valuable time doing something like this:

Dim Bmp As New Bitmap(Me.PictureBox1.Image) 'instantiate a new copy of the image as a bitmap (useful for numerous changes)

We can instead simply do the following (note that a Bitmap and an Image are identical in structure):

Dim Bmp As Bitmap = DirectCast(Me.PictureBox1.Image, Bitmap) 'define a reference to the image without instantiating a new image

Certainly, the only times we would need to create a separate bitmap is when, as previously stated, we need to

make numerous changes, or if the image object might be set to Nothing, which a blank PictureBox or a form

with no background image may possess. In that case, we can declare them like this:

Dim Bmp As New Bitmap(Me.Width, Me.Height, Me.CreateGraphics) 'create blank Form bitmap 'or... Dim Bmp As New Bitmap(Me.PictureBox1.Width, Me.PictureBox1.Height, Me.PictureBox1.CreateGraphics) 'create blank PictureBox1 bitmap

Regardless of how we define it, we can then get the color at a specific point using a command like this:

Dim Clr As Color = Bmp.GetPixel(intX, intY) 'Get an ARGB color value at a specified X,Y point (duplicates the VB6 Point() method)

Page 3: Restoring raster graphics and rubberbands to .net

Page –224–

To set a single point, we are very often advised to draw a circle with a radius of 1, like this:

e.Graphics.DrawEllipse(Pens.Black, 100, 100, 1, 1) 'using Elimpse to draw a point at 100x, 100y with width and height of 1

NOTE: Rather than using e.Graphics, as shown here, which is provided by a Paint event, we can instead easily create our

own graphical interface to the object, as will be demonstrated shortly, and use it outside of Paint events.

Though slothful, it is not as wasteful as it might appear, because .NET will see the width and height are

set to 1, and so it will just draw a dot. Even so, the SetPixel() P/Invoke under gdiplus.dll is much

faster. And because of this, I find the above ellipse advice odd, because VB.NET does in fact support a

much faster SetPixel() method under the Bitmap object, complementing its GetPixel() method,

which does in fact provide an interface to the fore-mentioned SetPixel() P/Invoke, as shown here:

Bmp.SetPixel(100, 100, Color.Black) 'set a single point on the image (duplicates the VB6 graphical Set command)

Getting an Image Graphics Object Without Going Through the Paint() Event The main problem many new users to VB.NET run into is when they try to draw shapes at will to something

like a PictureBox. It may be easy to draw within its Paint() event (as demonstrated earlier in this document

– see the article, Easy Ways to Draw Lines and Shapes, and to Paint in VB.NET, on page 143, for instance),

where the PaintEventArgs “e” parameter provides us with access to a convenient Graphics object that is pre-

tied to the PictureBox. However, they seem to get a bit stuck when they want to simply draw a unique line,

shape, or text to the PictureBox, such as a drawing program might use. In fact, I have seen code where, out of

frustration, people have placed specialized hooks into a Paint() event that passes back the event argument

object, or more often, saving it off somewhere, implementing it in a customized method so that they could

apply those non-typical drawing features. But this leads only to exception errors.

But accessing this graphical interface is really not complicated. We need to understand is that the

graphical interface provided by the Paint() event is just a simple System.Drawing.Graphics object that

is tied to the PictureBox, and this is something that is quite easy to generate within our code.

For example, suppose we want to draw to PictureBox1. To access a graphical interface to it using an

object named eg (reminding us of e.graphics), we could use code like this:

'generate a graphical interface to a picture box image (same as e.Graphics provided by a System.Windows.Forms.PaintEventArgs object)

Dim eg As System.Drawing.Graphics = Me.PictureBox1.CreateGraphics

You may also have seen such objects defined like the following, which is also valid:

Dim egf As System.Drawing.Graphics = System.Drawing.Graphics.FromHwnd(Me.PictureBox1.Handle) 'a graphical interface to a picture box.

Dim egi As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(Me.PictureBox1.Image) 'useful for drawing text or copying images.

NOTE: If you are drawing shapes, use the egf definition for the control, rather than egi for the image on the control;

otherwise the drawn shapes may not display. The first alters the displayed image, the second alters its stored image.

Suppose we want to draw a simple white cross through an image when the user clicks on it. Try this:

'react to user clicking on picture Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox1.Click

Try

Dim eg As System.Drawing.Graphics = Me.PictureBox1.CreateGraphics 'generate a graphical interface to a picture box image

With Me.PictureBox1

eg.DrawLine(Pens.White, 0, 0, .Width, .Height) 'draw a white line from top-left to bottom-right

eg.DrawLine(Pens.White, 0, .Height, .Width, 0) 'draw a white line from bottom-left to top-right

End With Catch 'ignore errors (usually happens when no image data is loaded in a PictureBox, so the Image object is set to Nothing)

End Try

End Sub

Restoring Raster Operations to .NET Other graphical features seem to be lost in limbo, such as the VB6 DrawMode property, which allowed

you to specify how a pen object was to be drawn upon the background. It could be normal

(R2_CopyPen), R2_XOrPen, R2_NotXOrPen, R2_MergePen, R2_MergePenNot, or whatever raster

drawing operation you would want to perform. The reason for this is that Raster operations are not

supported by GDI+ under .NET (regardless of the fact that these are in fact simple processes).

Page 4: Restoring raster graphics and rubberbands to .net

Page –225–

GDI+, introduced in 2001 with WinXP, is faster and more powerful than standard GDI, and this is

because GDI+ is linked directly to the operations available to graphics card though its firmware

(software burned into a board-based read-only memory chip (ROM)). It therefore naturally lacks support

for Raster Operations (memory-mapped operations) because the card’s firmware lacks it.

What this means to most people is that the raster-generated selection rectangle that is used so often in

Windows is not supported by GDI+ under .NET. Indeed, GDI+ was primarily meant to be used by

C/C++ developers, but it was quite naturally incorporated into .NET.

For example, under GDI+, the System.Drawing.Graphics.CompositingMode property, which is the

closest thing it has to the GDI Raster Operations property, has only 2 enumerated settings:

Member name Description

SourceOver Specifies that when a color is rendered, it is blended with the background color. The blend is determined by the alpha component of the color being rendered. GDI has no similar command.

SourceCopy Specifies that when a color is rendered, it overwrites the background color (Default). This is just like the Raster R2_CopyPen

command.

Conversely, GDI Raster operations implemented 17 settings, as defined below:

Member name Description

R2_Black Specifies black pen color. R2_CopyPen Specifies the pen color. The pen over-writes anything it draws over (Default).

R2_MakePenNot Specifies a combination of the colors are common to both the pen and the inverse of the display. R2_MaskNotPen Specifies a combination of the colors are common to the background color and the inverse of the pen.

R2_MaskPen Specifies a combination of the colors common to both the pen and the display.

R2_MergeNotPen Specifies a combination of the display color and the inverse of the pen color. R2_MergePen Specifies a combination of the pen color and the display color.

R2_MergePenNot Specifies a combination of the pen color and the inverse of the display color.

R2_NoOperation Specifies no operation; the output remains unchanged. R2_Not Specifies the inverse of the display color.

R2_NotCopyPen Specifies the inverse of R2_CopyPen. R2_NotMaskPen Specifies the inverse of R2_MaskPen.

R2_NotMergePen Specifies the inverse of R2_MergePen.

R2_NotXOrPen Specifies an inverse of R2_XOrPen. R2_White Specifies a white pen color.

R2_XOrPen Specifies a combination of the colors in the pen and in the display color, but not in both.

As you can see, even with all this functionality, there is no alpha-channel-blending support provided by

GDI, such as the GDI+ SourceOver setting specified. The other GDI+ setting, SourceCopy, is duplicated

by the GDI R2_CopyPen setting. No other correlations between these two platforms are yet supported.

Getting One’s Hands Dirty So, if you want to implement GDI Raster operations under .NET, you need to get your hands dirty and

perform GDI operations in much the same way as they are done under C/C++. This involves obtaining the

device context (DC) of the object we want to draw to, creating pens and brushes to draw with, select them

into the DC, perform the drawing operation, and finally release these created resources.

Of course, even though this can seem to get a bit involved, it all boils down to a uniform series of simple,

repeated processes that can be performed for you in an automated way, so you can focus entirely on the

actual drawing tasks. What this would actually involve are two support methods that can be invoked “behind

the curtain”, out of sight and mind; the first will automatically set up for a drawing task you want to perform,

and the second will automatically close down from it after your drawing task.

The first method, which we will name InitPenAndBrush(), will get the Graphical interface and the Device

context of the target object, create the foreground drawing pen with a selected color and width, create the

background brush (the fill) with its color and pattern, select each of them into the object we want to draw on,

save the pen and brush that were previously assigned, and select the type of Raster Operation we need.

Next, just like we would do under GDI+, we perform the actual drawing task, such as drawing a line.

After we perform the drawing task, the other method is invoked, which we will name

DisposeResources(), that will select back in the previous pen and brush to the target, and finally

release the resources of our new pen, brush, graphical interface, and device context.

Page 5: Restoring raster graphics and rubberbands to .net

Page –226–

Of course, to make this even easier, we can create a class that performs all this added work for each of

the drawing functions for us, so all we have to do is concern ourselves with invoking its public methods

and properties; we will simply define the pen we want to draw with, as needed, the background brush, if

we need it, the type of Raster Operation, and, naturally, to invoke any required drawing operations.

The GDI32 class, listed on the next page, is derived from various C++/C# sources, and also from a lot of

my own current and previous work (actually, one of the anonymous C++/C# postings in fact credited a

sadly unremembered VB.NET author who had designed an ordered structure to the class and defined its

naming conventions – if anyone recognizes it; please let me know). Note that any additional features you

need can also be added quite easily.

Once you invoke an instance of this class, you can set the drawing pen’s color and width, the brush’s

color and hatch style (HS_SOLID is the default), if you need to change them (they are saved within the

class properties, so you do not need to set them with every drawing command), and invoke any of the

required drawing operations: SetPoint(), GetPoint(), DrawLine(), DrawCircle(), DrawEllipse(),

DrawArc(), DrawRectangle(), DrawRoundRect(), and DrawObround().

To implement this class into your own code is very simple. Somewhere within your code, perhaps as

publicly as possible, you would declare an instance of the GDI32 class:

Friend m_GDI As New GDI32 'instantiate an instace of the GDI32 class

Then, when you are ready to draw, be sure your Pen color (foreground), Brush color (background), and

the Raster operation are as you need them to be. By default, the Pen color is White, the Pen style is

PS_Solid, the Brush color is Transparent, the Brush hatch pattern is HS_Solid (no pattern), and the

Raster operation is R2_CopyPen. This will draw using a solid white pen with an invisible brush (no

background color for the pen will be painted over the target surface; typical for ellipses and rectangles).

NOTE: If the Pen color is Transparent, then the drawing operation will use a stock Null Pen (invisible). If the Brush color

is Transparent, then the drawing operation will use a stock Null Brush.

You set the properties for the drawing operation by change the properties of the class. For example:

m_GDI.PenColor = Color.LightBlue 'set pen color to Light Blue (default is Color.White)

m_GDI.PenStyle = PenStyles.PS_DASH 'alternate dashes with dots (default is PenStyles.PS_SOLID)

m_GDI.PenWidth = 3 'Set the pen width to 3 pixels (Default is 1)

m_GDI.BrushHatch = HatchStyle.HS_CROSS 'set the brush style to Crosses (default is HatchStyle.HS_SOLID)

m_GDI.BrushColor = Color.Blue 'set the brush color to Blue (default is Color.Transparent)

m_GDI.RasterOp = RasterOps.R2_XOrPen 'merge the pen and background colors (default is RasterOps.R2_CopyPen)

NOTE: GDI requires RGB colors, not the ARGB colors that are part of the standard Color Palette and GDI+. As such, we

must convert them from ARGB to RGB. Most gurus will tell you to simply perform a Clr.ToArgb And &HFFFFFF operation,

but this is not enough. ARGB is stored internally as AARRGGBB, for Alpha, Red, Green, and Blue, where each letter

represents a Hexadecimal digit. However, RGB is stored as 00BBGGRR. As such, the Red and Blue color values must also

be swapped. I do this for you automatically within the class using the ARGBtoRGB() function, defined below:

'*************************************************************************************************************

' Function: ARGBtoRGB ' Helper function to covert Alpha Color ARGB value (AARRGGBB) to RGB (00BBGGRR)

'************************************************************************************************************* Private Function ARGBtoRGB(ByVal clr As Color) As Integer Dim vARGB As Integer = clr.ToArgb 'convert color value to AARRGGBB

Return RGB((vARGB >> 16) And &HFF, (vARGB >> 8) And &HFF, vARGB And &HFF) 'return RGB color End Function

You then perform the desired drawing operation. Because several of the methods, such as DrawArc(),

DrawEllipse(), DrawRectangle(), DrawRoundRect(), and DrawObround() have overloaded methods, you can

specify them by providing Point objects to define their top-left and bottom-right bounds, or by providing a

bounding Rectangle object. Also, you must provide a graphical interface to the target object you want to

draw upon. For example:

'draw a rectangle from PictureBox coordinates 25,25 (top-left) to 100,100 (bottom-right) m_GDI.DrawRectangle(Me.PictureBox1.CreateGraphics, New Point(25, 25), New Point(100, 100))

Now draw as you see fit. You need to change colors and Raster operations only as needed, not every time.

Page 6: Restoring raster graphics and rubberbands to .net

Page –227–

The GDI32 Class What follows is the GDI32 class:

Option Strict On

Option Explicit On

'**************************************************************************************

' GDI32 - GDI Support for .NET '**************************************************************************************

#Region "GDI32 Enumerations"

'*****************************************************************************************************************************

' Enumerations '*****************************************************************************************************************************

' Pen Styles (how lines are drawn)

Public Enum PenStyles As Integer

PS_SOLID = &H0 'A pen style that is a solid color. ────── PS_DASH = &H1 'A pen style that is dashed. ------

PS_DOT = &H2 'A pen style that is dotted. ●●●●●● PS_DASHDOT = &H3 'A pen style that consists of alternating dashes and dots. -●-●-●

PS_DASHDOTDOT = &H4 'A pen style that consists of dashes and double dots. -●●-●●

PS_NULL = &H5 'A pen style that is invisible. PS_INSIDEFRAME = &H6 'A pen style that is a solid color. When this style is specified in a drawing record that takes a bounding rectangle,

' 'the dimensions of the figure are shrunk so that it fits entirely in the bounding rectangle, ' 'taking into account the width of the pen.

End Enum

'Type of Raster operation (how drawing interacts with the background)

Public Enum RasterOps As Integer R2_Black = 1 'Specifies black pen color.

R2_NotMergePen = 2 'Specifies the inverse of MergePen.

R2_MaskNotPen = 3 'Specifies a combination of the colors are common to the background color and the inverse of the pen. R2_NotCopyPen = 4 'Specifies the inverse of CopyPen.

R2_MakePenNot = 5 'Specifies a combination of the colors are common to both the pen and the inverse of the display. R2_Not = 6 'Specifies the inverse of the display color.

R2_XOrPen = 7 'Specifies a combination of the colors in the pen and in the display color, but not in both.

R2_NotMaskPen = 8 'Specifies the inverse of MaskPen. R2_MaskPen = 9 'Specifies a combination of the colors common to both the pen and the display.

R2_NotXOrPen = 10 'Specifies an inverse of XOrPen. R2_NoOperation = 11 'Specifies no operation; the output remains unchanged.

R2_MergeNotPen = 12 'Specifies a combination of the display color and the inverse of the pen color.

R2_CopyPen = 13 'Specifies the pen color. R2_MergePenNot = 14 'Specifies a combination of the pen color and the inverse of the display color.

R2_MergePen = 15 'Specifies a combination of the pen color and the display color. R2_White = 16 'Specifies a white pen color.End Enum

End Enum

'Hatch Styles

Public Enum HatchStyle As Integer HS_HORIZONTAL = 0 '----- A horizontal hatch.

HS_VERTICAL = 1 '||||| A vertical hatch.

HS_FDIAGONAL = 2 '\\\\\ A 45-degree downward, left-to-right hatch. HS_BDIAGONAL = 3 '///// A 45-degree upward, left-to-right hatch.

HS_CROSS = 4 '+++++ A horizontal and vertical cross-hatch. HS_DIAGCROSS = 5 'xxxxx A 45-degree crosshatch.

HS_FDIAGONAL1 = 6 '

HS_BDIAGONAL1 = 7 ' HS_SOLID = 8 '

HS_DENSE1 = 9 ' HS_DENSE2 = 10 '

HS_DENSE3 = 11 '

HS_DENSE4 = 12 ' HS_DENSE5 = 13 '

HS_DENSE6 = 14 ' HS_DENSE7 = 15 '

HS_DENSE8 = 16 '

HS_NOSHADE = 17 ' HS_HALFTONE = 18 '

HS_SOLIDCLR = 19 ' HS_DITHEREDCLR = 20 '

HS_SOLIDTEXTCLR = 21 '

HS_DITHEREDTEXTCLR = 22 ' HS_SOLIDBKCLR = 23 '

HS_DITHEREDBKCLR = 24 ' HS_API_MAX = 25 '

End Enum

'Stock Objects

Enum StockObjects As Integer WHITE_BRUSH = 0 'White brush.

LTGRAY_BRUSH = 1 'Light gray brush.

GRAY_BRUSH = 2 'Gray brush. DKGRAY_BRUSH = 3 'Dark gray brush.

BLACK_BRUSH = 4 'Black brush. NULL_BRUSH = 5 'Null Brush (equivalen to HOLLOW_BRUSH).

HOLLOW_BRUSH = NULL_BRUSH 'Hollow brush (equivalent to NULL_BRUSH)

WHITE_PEN = 6 'White pen. BLACK_PEN = 7 'Black pen.

NULL_PEN = 8 'Null Pen. The null pen draws nothing. OEM_FIXED_FONT = 10 'Original Equiptment Manufacturer (OEM) dependent fixed-pitch (monospace) font.

ANSI_FIXED_FONT = 11 'Windows fixed-pitch (monospace) system font.

ANSI_VAR_FONT = 12 'Windows variable-pitch (proportional space) system font. SYSTEM_FONT = 13 'System font. By default, the system uses the system font to draw menus, dialog box controls, and text.

DEVICE_DEFAULT_FONT = 14 'WinNT/Win2K/XP: Device-dependent font. DEFAULT_PALETTE = 15 'Default palette. This palette consists of the static colors in the system palette.

SYSTEM_FIXED_FONT = 16 'Fixed-pitch (monospace) system font. This stock object is provided only for compatibility with 16-bit Windows versions earlier than 3.0.

DEFAULT_GUI_FONT = 17 'Default font for user interface objects such as menus and dialog boxes. This is MS Sans Serif. Compare this with SYSTEM_FONT. DC_BRUSH = 18 'Win2K/XP: Solid color brush. The default color is white. The color can be changed by using the SetDCBrushColor() function.

DC_PEN = 19 'Win2K/XP: Solid pen color. The default color is white. The color can be changed by using the SetDCPenColor() function. End Enum

#End Region

'************************************************************************************** ' GDI Class to support GDI operations not supported by GDI+

' GDI+ (gdiplus.dll), a Graphical Design Interface used to take advantage of Graphic

' card hardware and software. It was introduced with Windows XP. It was designed for ' use by C/C++ users, but naturally was incorporated into the .NET platform.

' GDI+ offers faster operations that those provided by GDI, yet Raster operations, such ' as displaying rubberband lines was lost.

'**************************************************************************************

Friend Class GDI32

Page 7: Restoring raster graphics and rubberbands to .net

Page –228–

#Region "GDI32 Protected Fields"

'***************************************************************************************************************************** ' Protected Fields

'*****************************************************************************************************************************

Protected m_hdc As IntPtr 'handle to drawing context

Protected m_gdiPen As IntPtr 'handle to pen Protected m_oldPen As IntPtr 'hold original pen

Protected m_penColor As Color = Color.White 'Default drawing color

Protected m_penStyle As PenStyles = PenStyles.PS_SOLID 'init pen to solid Protected m_penWidth As Integer = 1 'pixel width of pen (line)

Protected m_rasterOp As RasterOps = RasterOps.R2_CopyPen 'Init raster operation to normal

Protected m_gdiBrush As IntPtr 'handle to new brush Protected m_oldBrush As IntPtr 'hold original brush

Protected m_brushColor As Color = Color.Transparent 'default brush color (fill color). Protected m_brushHatch As HatchStyle = HatchStyle.HS_SOLID 'hatch style. None = use Solid Brush

#End Region

#Region "GDI32 P/Invoke Declarations"

'***************************************************************************************************************************** ' INTEROP P/INVOKE DECLARATIONS

'*****************************************************************************************************************************

'-----------------------------------------------------------------------------------------------------------------------------

' Function: CreateSolidBrush ' The CreateSolidBrush function creates a logical brush that has the specified solid color.

'-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function CreateSolidBrush Lib "gdi32.DLL" Alias "CreateSolidBrush" ( _ ByVal crColor As Integer) As IntPtr

'-----------------------------------------------------------------------------------------------------------------------------

' Function: CreateHatchBrush

' The CreateHatchBrush function creates a logical brush that has the specified hatch pattern and color. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function CreateHatchBrush Lib "gdi32.DLL" Alias "CreateHatchBrush" ( _ ByVal Style As HatchStyle, _

ByVal crColor As Integer) As IntPtr

'-----------------------------------------------------------------------------------------------------------------------------

' Function: CreatePatternBrush ' The CreatePatternBrush function creates a logical brush with the specified

' bitmap pattern. The bitmap can be a DIB section bitmap, which is created

' by the CreateDIBSection function, or it can be a device-dependent bitmap. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function CreatePatternBrush Lib "gdi32.DLL" Alias "CreatePatternBrush" ( _ ByVal hBitmap As IntPtr) As IntPtr

'----------------------------------------------------------------------------------------------------------------------------- ' Function: GetStockObject

' The GetStockObject function retrieves a handle to one of the stock pens, brushes, fonts, or palettes. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function GetStockObject Lib "gdi32.DLL" Alias "GetStockObject" ( _

ByVal nIndex As StockObjects) As IntPtr

'----------------------------------------------------------------------------------------------------------------------------- ' Function: CreatePen

' The CreatePen function creates a logical pen that has the specified style,

' width, and color. The pen can subsequently be selected into a device context ' and used to draw lines and curves.

'----------------------------------------------------------------------------------------------------------------------------- Private Declare Function CreatePen Lib "gdi32.DLL" Alias "CreatePen" ( _

ByVal nPenStyle As PenStyles, _

ByVal nWidth As Integer, _ ByVal crColor As Integer) As IntPtr

'-----------------------------------------------------------------------------------------------------------------------------

' Function: SelectObject

' The SelectObject function selects an object into the specified device context ' (DC). The new object replaces the previous object of the same type.

'----------------------------------------------------------------------------------------------------------------------------- Private Declare Function SelectObject Lib "gdi32.DLL" Alias "SelectObject" ( _

ByVal hdc As IntPtr, _

ByVal hObject As IntPtr) As IntPtr

'----------------------------------------------------------------------------------------------------------------------------- ' Function: DeleteObject

' The DeleteObject function deletes a logical pen, brush, font, bitmap, region,

' or palette, freeing all system resources associated with the object. After ' the object is deleted, the specified handle is no longer valid.

'----------------------------------------------------------------------------------------------------------------------------- Private Declare Function DeleteObject Lib "gdi32.DLL" Alias "DeleteObject" ( _

ByVal hObject As IntPtr) As IntPtr

'-----------------------------------------------------------------------------------------------------------------------------

' Function: SetROP2 ' The SetROP2 function sets the current foreground mix mode. GDI uses the

' foreground mix mode to combine pens and interiors of filled objects with

' the colors already on the screen. The foreground mix mode defines how colors ' from the brush or pen and the colors in the existing image are to be combined.

' If the function succeeds, the return value specifies the previous mix mode. ' If the function fails, the return value is zero.

'-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function SetROP2 Lib "gdi32.DLL" Alias "SetROP2" ( _ ByVal hdc As IntPtr, _

ByVal nDrawMode As RasterOps) As RasterOps

'-----------------------------------------------------------------------------------------------------------------------------

' Function: GetROP2 ' The GetROP2 function retrieves the foreground mix mode of the specified

' device context. The mix mode specifies how the pen or interior color and ' the color already on the screen are combined to yield a new color.

' If the function succeeds, the return value specifies the previous mix mode.

' If the function fails, the return value is zero. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function GetROP2 Lib "gdi32.DLL" Alias "GetROP2" ( _ ByVal hdc As IntPtr) As Integer

'structure used by MoveToEx. It will contain the previous current position Public Structure POINTAPI

Dim x As Integer Dim y As Integer

End Structure

Page 8: Restoring raster graphics and rubberbands to .net

Page –229–

'-----------------------------------------------------------------------------------------------------------------------------

' Function: MoveToEx ' The MoveToEx function updates the current position to the specified point

' and optionally returns the previous position.

'----------------------------------------------------------------------------------------------------------------------------- 'hdc: Handle to a device context.

'X: Specifies the x-coordinate, in logical units, of the new position, in logical units. 'Y: Specifies the y-coordinate, in logical units, of the new position, in logical units.

'lpPoint: Pointer to a POINT structure that receives the previous current position. If this parameter is a NULL pointer, the previous position is not returned.

'----------------------------------------------------------------------------------------------------------------------------- Private Declare Function MoveToEx Lib "gdi32.DLL" Alias "MoveToEx" ( _

ByVal hdc As IntPtr, _ ByVal x As Integer, _

ByVal y As Integer, _

ByVal lpPoint As POINTAPI) As Boolean

'----------------------------------------------------------------------------------------------------------------------------- ' Function: MoveToEx

' Description: The MoveToEx function updates the current position to the specified point

' and optionally returns the previous position. '-----------------------------------------------------------------------------------------------------------------------------

'hdc: Handle to a device context. 'X: Specifies the x-coordinate, in logical units, of the new position, in logical units.

'Y: Specifies the y-coordinate, in logical units, of the new position, in logical units.

'lpPoint: Pointer to a POINT structure that receives the previous current position. If this parameter is a NULL pointer, the previous position is not returned. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function MoveToEx Lib "gdi32.DLL" Alias "MoveToEx" ( _ ByVal hdc As IntPtr, _

ByVal x As Integer, _

ByVal y As Integer, _ ByVal lpPoint As IntPtr) As Boolean

'-----------------------------------------------------------------------------------------------------------------------------

' Function: LineTo

' The LineTo function draws a line from the current position up to, but not ' including, the specified point.

'----------------------------------------------------------------------------------------------------------------------------- 'hdc: Handle to a device context.

'nXEnd: Specifies the x-coordinate, in logical units, of the line's ending point.

'nYEnd: Specifies the y-coordinate, in logical units, of the line's ending point. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function LineTo Lib "gdi32.DLL" Alias "LineTo" ( _ ByVal hdc As IntPtr, _

ByVal nXEnd As Integer, _

ByVal nYEnd As Integer) As Boolean

'----------------------------------------------------------------------------------------------------------------------------- ' Function: Ellipse

' The Ellipse function draws an ellipse. The center of the ellipse is the

' center of the specified bounding rectangle. The ellipse is outlined by using ' the current pen and is filled by using the current brush.

'----------------------------------------------------------------------------------------------------------------------------- 'hdc: A handle to the device context.

'nLeftRect: The x-coordinate, in logical coordinates, of the upper-left corner of the bounding rectangle.

'nTopRect: The y-coordinate, in logical coordinates, of the upper-left corner of the bounding rectangle. 'nRightRect: The x-coordinate, in logical coordinates, of the lower-right corner of the bounding rectangle.

'nBottomRect: The y-coordinate, in logical coordinates, of the lower-right corner of the bounding rectangle. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function Ellipse Lib "gdi32.DLL" Alias "Ellipse" ( _

ByVal hdc As IntPtr, _ ByVal nLeftRect As Integer, _

ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _

ByVal nBottomRect As Integer) As Boolean

'-----------------------------------------------------------------------------------------------------------------------------

' Function: Rectangle ' The Rectangle function draws a rectangle. The rectangle is outlined by using

' the current pen and filled by using the current brush.

'----------------------------------------------------------------------------------------------------------------------------- 'hdc: A handle to the device context.

'nLeftRect: The x-coordinate, in logical coordinates, of the upper-left corner of the rectangle. 'nTopRect: The y-coordinate, in logical coordinates, of the upper-left corner of the rectangle.

'nRightRect: The x-coordinate, in logical coordinates, of the lower-right corner of the rectangle.

'nBottomRect: The y-coordinate, in logical coordinates, of the lower-right corner of the rectangle. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function Rectangle Lib "gdi32.DLL" Alias "Rectangle" ( _ ByVal hdc As IntPtr, _

ByVal nLeftRect As Integer, _

ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _

ByVal nBottomRect As Integer) As Boolean

'-----------------------------------------------------------------------------------------------------------------------------

' Function: RoundRect ' The RoundRect function draws a rectangle with rounded corners. The rectangle

' is outlined by using the current pen and filled by using the current brush. '-----------------------------------------------------------------------------------------------------------------------------

'hdc: A handle to the device context.

'nLeftRect: The x-coordinate, in logical coordinates, of the upper-left corner of the rectangle. 'nTopRect: The y-coordinate, in logical coordinates, of the upper-left corner of the rectangle.

'nRightRect: The x-coordinate, in logical coordinates, of the lower-right corner of the rectangle. 'nBottomRect: The y-coordinate, in logical coordinates, of the lower-right corner of the rectangle.

'nWidth: The width, in logical coordinates, of the ellipse used to draw the rounded corners.

'nHeight: The height, in logical coordinates, of the ellipse used to draw the rounded corners. '-----------------------------------------------------------------------------------------------------------------------------

Private Declare Function RoundRect Lib "gdi32.DLL" Alias "RoundRect" ( _ ByVal hdc As IntPtr, _

ByVal nLeftRect As Integer, _

ByVal nTopRect As Integer, _ ByVal nRightRect As Integer, _

ByVal nBottomRect As Integer, _ ByVal nWidth As Integer, _

ByVal nHeight As Integer) As Boolean

'-----------------------------------------------------------------------------------------------------------------------------

' Function: Arc ' The Arc function draws an elliptical arc.

'-----------------------------------------------------------------------------------------------------------------------------

' hdc: A handle to the device context where drawing takes place. ' nLeftRect: The x-coordinate, in logical units, of the upper-left corner of the bounding rectangle.

' nTopRect: The y-coordinate, in logical units, of the upper-left corner of the bounding rectangle. ' nRightRect: The x-coordinate, in logical units, of the lower-right corner of the bounding rectangle.

' nBottomRect: The y-coordinate, in logical units, of the lower-right corner of the bounding rectangle.

' nXStartArc: The x-coordinate, in logical units, of the ending point of the radial line defining the starting point of the arc. ' nYStartArc: The y-coordinate, in logical units, of the ending point of the radial line defining the starting point of the arc.

' nXEndArc: The x-coordinate, in logical units, of the ending point of the radial line defining the ending point of the arc. ' nYEndArc: The y-coordinate, in logical units, of the ending point of the radial line defining the ending point of the arc.

'-----------------------------------------------------------------------------------------------------------------------------

Page 9: Restoring raster graphics and rubberbands to .net

Page –230–

'The points (nLeftRect, nTopRect) and (nRightRect, nBottomRect) specify the bounding rectangle. An ellipse formed by the specified

' bounding rectangle defines the curve of the arc. The arc extends in the current drawing direction from the point where it ' intersects the radial from the center of the bounding rectangle to the (nXStartArc, nYStartArc) point. The arc ends where it

' intersects the radial from the center of the bounding rectangle to the (nXEndArc, nYEndArc) point. If the starting point and

' ending point are the same, a complete ellipse is drawn. 'The arc is drawn using the current pen; it is not filled.

'----------------------------------------------------------------------------------------------------------------------------- Private Declare Function Arc Lib "gdi32.DLL" Alias "Arc" ( _

ByVal hdc As IntPtr, _

ByVal nLeftRect As Integer, _ ByVal nTopRect As Integer, _

ByVal nRightRect As Integer, _ ByVal nBottomRect As Integer, _

ByVal nXStartArc As Integer, _

ByVal nYStartArc As Integer, _ ByVal nXEndArc As Integer, _

ByVal nYEndArc As Integer) As Boolean #End Region

#Region "GDI32 Properties" '*****************************************************************************************************************************

' Properties ' BrushColor: Get/Set brush color. Default is Black. Also be sure this is black for XOR operations and you want a "Rubberband" effect.

' BrushHatch: Get/Set optional Brush Hatch Style. Default is HS_SOLID, which specifies a solid brush.

' PenColor: Get/Set pen color. Default is black. ' PenStyle: Get/Set pen style. Default is PS_SOLID.

' PenWidth: Get/Set pen pixel width. Default is 1. ' RasterOp: Get/Set type of raster operation to perform. Default is R2_CopyPen (overwrite)

'*****************************************************************************************************************************

'Get/Set brush color

Public Property BrushColor() As Color Get

Return Me.m_brushColor

End Get Set(ByVal value As Color)

Me.m_brushColor = value End Set

End Property

'Get/Set Brush Hatch Style

Public Property BrushHatch() As HatchStyle Get

Return Me.m_brushHatch

End Get Set(ByVal value As HatchStyle)

Me.m_brushHatch = value End Set

End Property

'Get/Set pen color

Public Property PenColor() As Color Get

Return Me.m_penColor

End Get Set(ByVal value As Color)

Me.m_penColor = value End Set

End Property

'Get/Set pen style (pattern)

Public Property PenStyle() As PenStyles Get

Return Me.m_penStyle

End Get Set(ByVal value As PenStyles)

Me.m_penStyle = value End Set

End Property

'Get/Set pen width in pixels

Public Property PenWidth() As Integer Get

Return Me.m_penWidth

End Get Set(ByVal value As Integer)

Me.m_penWidth = value End Set

End Property

'Get/Set Raster Operation

Public Property RasterOp() As RasterOps Get

Return m_rasterOp

End Get Set(ByVal value As RasterOps)

m_rasterOp = value End Set

End Property

#End Region

#Region "GDI32 Protected Support Methods" '*****************************************************************************************************************************

' Protected Support Methods

'*****************************************************************************************************************************

'************************************************************************************************************* ' Function: ARGBtoRGB

' Helper function to covert Alpha Color ARGB value (AARRGGBB) to RGB (00BBGGRR)

'************************************************************************************************************* Private Function ARGBtoRGB(ByVal clr As Color) As Integer

Dim vARGB As Integer = clr.ToArgb 'convert color value to AARRGGBB Return RGB((vARGB >> 16) And &HFF, (vARGB >> 8) And &HFF, vARGB And &HFF) 'return RGB color

End Function

Page 10: Restoring raster graphics and rubberbands to .net

Page –231–

'-----------------------------------------------------------------------------------------------------------------------------

' Subrouine: InitPenAndBrush ' Initialize pen annd brush

'-----------------------------------------------------------------------------------------------------------------------------

Protected Sub InitPenAndBrush(ByVal g As Graphics) Me.m_hdc = g.GetHdc 'save handle to device context

'process brush options If Me.m_brushColor = Color.Transparent Then

Me.m_gdiBrush = GetStockObject(StockObjects.NULL_BRUSH) 'hide brush if brush color is transparent

ElseIf Me.m_brushHatch = HatchStyle.HS_SOLID Then Me.m_gdiBrush = GDI32.CreateSolidBrush(Me.ARGBtoRGB(Me.m_brushColor)) 'set solid brush style and fill color)

Else Me.m_gdiBrush = GDI32.CreateHatchBrush(Me.m_brushHatch, Me.ARGBtoRGB(Me.m_brushColor)) 'create hatch brush style

End If

'process pen options If Me.m_penColor = Color.Transparent Then

Me.m_gdiPen = GetStockObject(StockObjects.NULL_PEN) 'hide pen if it is transparent Else

Me.m_gdiPen = GDI32.CreatePen(Me.PenStyle, Me.m_penWidth, Me.ARGBtoRGB(Me.PenColor)) 'set pen pattern, style, and width

End If

GDI32.SetROP2(Me.m_hdc, m_rasterOp) 'set raster operation Me.m_oldPen = GDI32.SelectObject(Me.m_hdc, Me.m_gdiPen) 'set new pen, save old

Me.m_oldBrush = GDI32.SelectObject(Me.m_hdc, Me.m_gdiBrush) 'set new background brush, save old

End Sub

'----------------------------------------------------------------------------------------------------------------------------- ' Subrouine: DisposeResources

' Dispose of created data and reset old data

'----------------------------------------------------------------------------------------------------------------------------- Protected Sub DisposeResources(ByVal g As Graphics)

GDI32.DeleteObject(GDI32.SelectObject(Me.m_hdc, Me.m_oldBrush)) 'reset old brush, delete replaced one GDI32.DeleteObject(GDI32.SelectObject(Me.m_hdc, Me.m_oldPen)) 'reset old pen, delete replaced one

g.ReleaseHdc(Me.m_hdc) 'release device context

g.Dispose() End Sub

#End Region

#Region "GDI32 Public Methods"

'***************************************************************************************************************************** ' Public Methods

'*****************************************************************************************************************************

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawCircle ' draw a circle with a uniform radius from a center point

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawCircle(ByVal g As Graphics, ByVal p1 As Point, ByVal Radius As Integer) 'p1 is center of circle

Me.InitPenAndBrush(g) 'init pen and brush

GDI32.Ellipse(Me.m_hdc, p1.X - Radius, p1.Y - Radius, p1.X + Radius, p1.Y + Radius) 'draw circle Me.DisposeResources(g) 'disposes of resources

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawEllipse ' Draw circles and ellipses from a bounds defined by a top-left point and a bottom-right point

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawEllipse(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point) 'p1 is top-left of bounds, p2 is bottom-right

Me.InitPenAndBrush(g) 'init pen and brush

GDI32.Ellipse(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y) 'draw ellipse Me.DisposeResources(g) 'disposes of resources

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawEllipse ' Draw ellipses defined within a bounding rectangle

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawEllipse(ByVal g As Graphics, ByVal Bnds As Rectangle) 'p1 is top-left of bounds, p2 is bottom-right

Me.InitPenAndBrush(g) 'init pen and brush

With Bnds GDI32.Ellipse(Me.m_hdc, .Left, .Top, .Right, .Bottom) 'draw ellipse within the rectangle

End With Me.DisposeResources(g) 'disposes of resources

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawArc ' Draw an arc within a bounding rectangle. Point ArcStart defines a point from center along start line, ArcEnd is a point along end line

'-----------------------------------------------------------------------------------------------------------------------------

Public Sub DrawArc(ByVal g As Graphics, ByVal Bnds As Rectangle, ByVal ArcStart As Point, ByVal ArcEnd As Point) Me.InitPenAndBrush(g) 'init pen and brush

With Bnds GDI32.Arc(Me.m_hdc, .Left, .Top, .Right, .Bottom, ArcStart.X, ArcStart.Y, ArcEnd.X, ArcEnd.Y) 'draw arc

End With

Me.DisposeResources(g) 'disposes of resources End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawArc

' Draw an arc within a rectangle specifyed by p1 (top-left) and p2(bottom-right). ' Point ArcStart defines a point from center along start line, ArcEnd is a point along end line

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawArc(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point, ByVal ArcStart As Point, ByVal ArcEnd As Point)

Me.InitPenAndBrush(g) 'init pen and brush

GDI32.Arc(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y, ArcStart.X, ArcStart.Y, ArcEnd.X, ArcEnd.Y) 'draw arc Me.DisposeResources(g) 'disposes of resources

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawLine ' Draw a line from point p1 to point p2

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawLine(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point)

Me.InitPenAndBrush(g) 'init pen and brush

GDI32.MoveToEx(Me.m_hdc, p1.X, p1.Y, IntPtr.Zero) 'move to starting point GDI32.LineTo(Me.m_hdc, p2.X, p2.Y) 'draw line from start point to end point

Me.DisposeResources(g) 'disposes of resources End Sub

Page 11: Restoring raster graphics and rubberbands to .net

Page –232–

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawPolygon ' Draw a polygon from an array of points

'-----------------------------------------------------------------------------------------------------------------------------

Public Sub DrawPolygon(ByVal g As Graphics, ByRef PointsArray() As Point) If PointsArray Is Nothing Then

Return 'if nothing to process End If

Dim NumPoints As Integer = UBound(PointsArray) 'get upper bounds of array

Me.InitPenAndBrush(g) 'init pen and brush GDI32.MoveToEx(Me.m_hdc, PointsArray(0).X, PointsArray(0).Y, IntPtr.Zero) 'move to starting point

If CBool(NumPoints) Then 'if more than 1 point For Idx As Integer = 1 To NumPoints 'process each point as a sequence in a chain

GDI32.LineTo(Me.m_hdc, PointsArray(Idx).X, PointsArray(Idx).Y) 'draw a line from previous point to current

Next If Not PointsArray(0).Equals(PointsArray(NumPoints)) Then 'close polygon if start and last not the same

GDI32.LineTo(Me.m_hdc, PointsArray(NumPoints).X, PointsArray(NumPoints).Y) 'draw a line from previous point to current End If

End If

Me.DisposeResources(g) 'disposes of resources End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawRectangle

' Draw a rectangle or square from a top-left point to a bottom-right point ' P1 is the top-left corner, P2 is the bottom Right corner

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRectangle(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point)

Me.InitPenAndBrush(g) 'init pen and brush

GDI32.Rectangle(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y) 'draw rectangle from point1 to point2 Me.DisposeResources(g) 'disposes of resources

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawRectangle ' Draw a rectangle or square from a bounding rectangle

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRectangle(ByVal g As Graphics, ByVal Bnds As Rectangle)

Me.InitPenAndBrush(g) 'init pen and brush

With Bnds GDI32.Rectangle(Me.m_hdc, .Left, .Top, .Right, .Bottom) 'draw rectangle within bounds

End With Me.DisposeResources(g) 'disposes of resources

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawRoundRect ' Draw a rounded rectangle (obround). Make CornerWidth and CornerHeight equal for symetrical corners

' P1 is the top-left corner, P2 is the bottom Right corner

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRoundRect(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point, ByVal CornerWidth As Integer, ByVal CornerHeight As Integer)

'if corner radius out of range If CornerWidth <= 0 OrElse CornerHeight <= 0 OrElse CornerWidth >= Math.Abs(p1.X - p2.X) OrElse CornerHeight >= Math.Abs(p1.Y - p2.Y) Then

DrawRectangle(g, p1, p2) 'just draw a rectangle

Else Me.InitPenAndBrush(g) 'init pen and brush

GDI32.RoundRect(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y, CornerWidth, CornerHeight) 'draw rounded rectangle from point1 to point2 Me.DisposeResources(g) 'disposes of resources

End If

End Sub

'----------------------------------------------------------------------------------------------------------------------------- ' Function: DrawRoundRect

' Draw a rounded rectangle (obround) from a bounding rectangle. Make CornerWidth and CornerHeight equal for symetrical corners

'----------------------------------------------------------------------------------------------------------------------------- Public Sub DrawRoundRect(ByVal g As Graphics, ByVal Bnds As Rectangle, ByVal CornerWidth As Integer, ByVal CornerHeight As Integer)

'if corner radius out of range With Bnds

If CornerWidth <= 0 OrElse CornerHeight <= 0 OrElse CornerWidth >= Math.Abs(.Left - .Right) OrElse CornerHeight >= Math.Abs(.Top - .Bottom) Then

DrawRectangle(g, Bnds) 'just draw a rectangle Else

Me.InitPenAndBrush(g) 'init pen and brush GDI32.RoundRect(Me.m_hdc, .Left, .Top, .Right, .Bottom, CornerWidth, CornerHeight) 'draw rounded rectangle from point1 to point2

Me.DisposeResources(g) 'disposes of resources

End If End With

End Sub

'-----------------------------------------------------------------------------------------------------------------------------

' Function: DrawObRound ' Draw an ObRound, allowing one to specify a uniform corner radius by specifying just a single corner radius property

' P1 is the top-left corner, P2 is the bottom Right corner '-----------------------------------------------------------------------------------------------------------------------------

Public Sub DrawObRound(ByVal g As Graphics, ByVal p1 As Point, ByVal p2 As Point, ByVal CornerRadius As Integer)

'if corner radius out of range If CornerRadius <= 0 OrElse CornerRadius >= Math.Abs(p1.X - p2.X) OrElse CornerRadius >= Math.Abs(p1.Y - p2.Y) Then

DrawRectangle(g, p1, p2) 'just draw a rectangle Else

Me.InitPenAndBrush(g) 'init pen and brush

GDI32.RoundRect(Me.m_hdc, p1.X, p1.Y, p2.X, p2.Y, CornerRadius, CornerRadius) 'draw rounded rectangle from point1 to point2 Me.DisposeResources(g) 'disposes of resources

End If End Sub

'----------------------------------------------------------------------------------------------------------------------------- ' Function: DrawObRound

' Draw an ObRound from a bounding rectangle, allowing one to specify a uniform corner radius by specifying just a single corner radius property '-----------------------------------------------------------------------------------------------------------------------------

Public Sub DrawObRound(ByVal g As Graphics, ByVal Bnds As Rectangle, ByVal CornerRadius As Integer)

'if corner radius out of range With Bnds

If CornerRadius <= 0 OrElse CornerRadius >= Math.Abs(.Left - .Right) OrElse CornerRadius >= Math.Abs(.Top - .Bottom) Then DrawRectangle(g, Bnds) 'just draw a rectangle

Else

Me.InitPenAndBrush(g) 'init pen and brush GDI32.RoundRect(Me.m_hdc, .Left, .Top, .Right, .Bottom, CornerRadius, CornerRadius) 'draw rounded rectangle from point1 to point2

Me.DisposeResources(g) 'disposes of resources End If

End With

End Sub #End Region

End Class

Page 12: Restoring raster graphics and rubberbands to .net

Page –233–

Emulating a Selection Rubber Band under GDI Suppose you wanted to define a Selection Rubber Band on an image, such as a background image on a

form. You can do this quite easily with the GDI32 class. The code required to support this is very simple

(I keep forcing myself to avoid the pun of saying it is very basic).

Create a new VB Windows project. Add a background image to it, if you wish. Also, if you choose to,

add some controls to dress it up (unlike all other rubber band examples you may have seen, this one

actually works exactly like you would expect of normal rubber band operations, where the rubber band

will also cover any controls it passes over on the form). Then, in the Form1 code, add the following:

Public Class Form1

Friend m_GDI As New GDI32 'instantiate an instace of the GDI class Friend m_StartPoint As Point 'keep track of starting point

Friend m_LastPoint As Point 'keep track of last mouse location

'**************************************************************************************

' Subroutine: Form1_MouseDown ' When the mouse select button is down, init the rectangle start and end points

'**************************************************************************************

Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown m_GDI.PenColor = Color.White 'set color to white (black will be invisible in an XOR operation)

m_GDI.BrushColor = Color.Transparent 'hide the brush m_GDI.RasterOp = RasterOps.R2_XOrPen 'use XOR to emulate rubberbanding

m_StartPoint = e.Location 'set the start and end locations to the current mouse position

m_LastPoint = e.Location End Sub

'**************************************************************************************

' Subroutine: Form1_MouseMove

' If the mouse button is down, erase the old rubberband, update the location, ' then draw the new rubberband.

'************************************************************************************** Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove

If e.Button = Windows.Forms.MouseButtons.Left Then 'if the mouse select button is down

DrawRubberBand() 'erase the old rectangle at the old location m_LastPoint = e.Location 'update the location

DrawRubberBand() 'draw the new rectange to the new location End If

End Sub

'**************************************************************************************

' Subroutine: Form1_MouseUp ' Erase the selection rubber band, report selection data

'**************************************************************************************

Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp DrawRubberBand() 'erase the old rectangle

Dim Ctls As New Collections.Generic.List(Of Control) 'list to collect controls selected

Dim Msg As String = "The selection rectangle was from (" & m_StartPoint.X.ToString & "," & m_StartPoint.Y.ToString & ") to (" & _

m_LastPoint.X.ToString & "," & m_LastPoint.Y.ToString & ")" & vbCrLf & vbCrLf

'define a new rectangle to compare against any controls on the form Dim Rect As Rectangle = NormalizeSelectRectangle()

'find which controls were selected

For Each Ctl As Control In Controls If Ctl.Visible AndAlso Ctl.Bounds.IntersectsWith(Rect) Then 'a control intersection was found

Ctls.Add(Ctl) End If

Next

If Ctls.Count = 0 Then

Msg &= "There were no controls selected" 'if no controls were selected Else

Msg &= "The follows controls were also selected:" & vbCrLf 'else list the controls selected

For Each Ctl As Control In Ctls Msg &= " " & Ctl.Name & vbCrLf

Next End If

'report results

MsgBox(Msg, MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "Selection Result") End Sub

'**************************************************************************************

' Function: NormalizeSelectRectangle

' Defind the selection rectangle and normalize its definition, where the start point is ' always upper-left, and the ending point is always lower-right

'************************************************************************************** Private Function NormalizeSelectRectangle() As Rectangle

'define the final selection rectangle

Dim X As Integer = m_StartPoint.X 'get start point Dim Y As Integer = m_StartPoint.Y

If X > m_LastPoint.X Then X = m_LastPoint.X 'NORMALIZE COORDINATES if inverted in any way If Y > m_LastPoint.Y Then Y = m_LastPoint.Y

'define a new rectangle to draw our current rectangle, and compare against any controls on the form

Return New Rectangle(X, Y, Math.Abs(m_LastPoint.X - m_StartPoint.X), Math.Abs(m_LastPoint.Y - m_StartPoint.Y)) End Function

'**************************************************************************************

' Subroutine: DrawRubberBand

' Draw or erase the rubberband, taking advantage of XOR Raster method. Also draw/erase ' over controls that may be placed on the form.

'************************************************************************************** Private Sub DrawRubberBand()

'define a new rectangle to draw our current rectangle, and compare against any controls on the form

Dim Rect As Rectangle = NormalizeSelectRectangle() m_GDI.DrawRectangle(Me.CreateGraphics, Rect) 'erase the old rectangle or draw the new one

For Each Ctl As Control In Controls 'also process any controls it intersects with If Ctl.Visible AndAlso Ctl.Bounds.IntersectsWith(Rect) Then 'a control intersection was found

'erase the old rectangle or draw the new one over control, offsetting the rubberband as needed

m_GDI.DrawRectangle(Ctl.CreateGraphics, New Rectangle(Rect.Left - Ctl.Left, Rect.Top - Ctl.Top, Rect.Width, Rect.Height)) End If

Next End Sub

End Class

Page 13: Restoring raster graphics and rubberbands to .net

Page –234–

NOTE: Unlike any other GDI example on the web, this is the first one to demonstrate a feature that many gurus have

typically thought was too difficult – the above DrawRubberBand() method draws the selection rubber band over any controls

it crosses. Most gurus considered this control-inclusive technique to be too complicated and deemed it an advanced topic

that cannot be covered in a single article. But, as you can see, my solution was unbelievably simple (actually, the solution

became simple due to techniques I had already developed to easily emulate VB6 image controls with transparent

backgrounds on a form – see the article, Emulating VB6 Image Control Features in VB.NET on page 58).

Now run the project (once you have added the GDI32 class to it). It

emulates the rubber band function very smoothly. Because it is

drawing to the form’s surface, any controls you place on the form

will be in front of the rubber band, unless we reflect it to the surface

of each control as well. The NormalizeSelectRectangle()

method normalizes coordinates, so that no matter how you make

your selection, the coordinate addressing will be standardized. The

dark-shaded code in the DrawRubberBand() method shows you

how simple it is to actually reflect the rubber band to the surface of

each control that it intersects with.

Emulating a Selection Rubber Band under GDI+ Suppose you wanted to simply emulate a Selection Rubber Band under the graphics support that is

already built into .NET, using GDI+. We can do that as well, though if you are not very careful in the

way you write it, I think it can also look much less professional than the GDI method (and I have seen

some very bad examples on the web at some blog and support sites – but these folks are just trying to get

people pointed in the right direction to solving their own problems). For instance, with some examples, I

often see the selection rectangle disappear after about one second, if you hold it still. But if it is rendered

and handled properly, as I will show you, below, it will work great and it will look clean.

Using the GDI+ DrawReversibleFrame() Method The typical technique developers have been using to perform this task

under GDI+ (and the technique I like the least) is by implementing the

ControlPaint.DrawReversibleFrame() method. Unlike the GDI method,

this method draws to the screen, over the top of everything, which is

useful for also conveniently covering form controls without the need for

additional code. But, because it draws to the whole screen, we must

translate our rectangle’s start coordinates to coordinates relative to the

screen, which we can do using the PointToScreen() method, as you will

see in the new DrawRubberBand() method, below. Unlike the previous

example, this example does not require the GDI32 class.

NOTE: This method can look goofy and lose its luster if you move the selection outside the target form, where it will leave

graphical artifacts on the screen, though no real damage is caused and it cleans easily. As such, I will check for this within

the form code and automatically correct for that. But regardless, it can also at times leave tiny graphical artifacts on certain

controls on the form as well. I do not like this method for this reason, and consider it to be the least professional method to

use. But we will cover it here because so many online examples feature it, and people are constantly asking me about it.

Like the GDI Rubber Band example, create a new VB Windows project. Add a background image to it

if you wish. Also add some controls to dress it up if you choose. And, to avoid window flashing, be sure

to also set the form’s DoubleBluffered parameter to True. Then, in the Form1 code, add the following:

Public Class Form1

Friend m_StartPoint As Point 'keep track of starting point Friend m_LastPoint As Point 'keep track of last mouse location

'************************************************************************************** ' Subroutine: Form1_MouseDown

' When the mouse select button is down, init the rectangle start and end points '**************************************************************************************

Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown

m_StartPoint = e.Location 'set the start and end locations to the current mouse position m_LastPoint = e.Location

End Sub

Page 14: Restoring raster graphics and rubberbands to .net

Page –235–

'************************************************************************************** ' Subroutine: Form1_MouseMove

' If the mouse button is down, erase the old rubberband, update the location,

' then draw the new rubberband. '**************************************************************************************

Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove If e.Button = Windows.Forms.MouseButtons.Left Then 'if the mouse select button is down

Dim Rect As Rectangle = NormalizeSelectRectangle() 'get current rectangle

Rect.Width += 1 'bump 1 for width/height because drawing edge is one higher than rectangle Rect.Height += 1

Me.Invalidate(Rect) 'invalidate that region Me.Update() 'update display of that region

m_LastPoint = e.Location 'update the location

DrawRubberBand() 'draw the new rectange to the new location End If

End Sub

'**************************************************************************************

' Subroutine: Form1_MouseUp ' Erase the selection rubber band, report selection data

'************************************************************************************** Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp

Me.Refresh() 'refresh form and controls, erasing rectangles

Dim Ctls As New Collections.Generic.List(Of Control) 'list to collect controls selected

Dim Msg As String = "The selection rectangle was from (" & m_StartPoint.X.ToString & "," & m_StartPoint.Y.ToString & ") to (" & _ m_LastPoint.X.ToString & "," & m_LastPoint.Y.ToString & ")" & vbCrLf & vbCrLf

'define a new rectangle to compare against any controls on the form Dim Rect As Rectangle = NormalizeSelectRectangle()

'find which controls were selected For Each Ctl As Control In Controls

If Ctl.Visible AndAlso Ctl.Bounds.IntersectsWith(Rect) Then 'a control intersection was found

Ctls.Add(Ctl) End If

Next

If Ctls.Count = 0 Then

Msg &= "There were no controls selected" 'if no controls were selected Else

Msg &= "The follows controls were also selected:" & vbCrLf 'else list the controls selected For Each Ctl As Control In Ctls

Msg &= " " & Ctl.Name & vbCrLf

Next End If

'report results MsgBox(Msg, MsgBoxStyle.OkOnly Or MsgBoxStyle.Information, "Selection Result")

End Sub

'**************************************************************************************

' Function: NormalizeSelectRectangle ' Defind the selection rectangle and normalize its definition, where the start point is

' always upper-left, and the ending point is always lower-right

'************************************************************************************** Private Function NormalizeSelectRectangle() As Rectangle

'define the final selection rectangle Dim X As Integer = m_StartPoint.X 'get start point

Dim Y As Integer = m_StartPoint.Y

If m_LastPoint.X < 0 Then m_LastPoint.X = -1 'prevent selection from goung outside top-left of frame If m_LastPoint.Y < 0 Then m_LastPoint.Y = -1

If X > m_LastPoint.X Then X = m_LastPoint.X 'NORMALIZE COORDINATES if inverted in any way If Y > m_LastPoint.Y Then Y = m_LastPoint.Y

'define a new rectangle to draw our current rectangle, and compare against any controls on the form

Return New Rectangle(X, Y, Math.Abs(m_LastPoint.X - m_StartPoint.X), Math.Abs(m_LastPoint.Y - m_StartPoint.Y)) End Function

'**************************************************************************************

' Subroutine: DrawRubberBand

' Draw or erase the rubberband, taking advantage of XOR Raster method. Also draw/erase ' over controls that may be placed on the form.

'************************************************************************************** Private Sub DrawRubberBand()

'define a new rectangle to draw our current rectangle

Dim Rect As Rectangle = NormalizeSelectRectangle() With Me.ClientRectangle

If Rect.Left + Rect.Width > .Width Then 'make sure selection, which is actually drown to the screen, not Rect.Width = .Width - Rect.Left + 1 'to the form, will not exceed the bounds of the form's client

End If 'area, otherwise the selection rectangle will be reflected to the

If Rect.Top + Rect.Height > .Height Then 'screen area not covered by the form, and will look very Rect.Height = .Height - Rect.Top + 1 'unprofessional. These two checks for eliminate that problem.

End If End With

'draw the selection rectangle to the actual screen, not to the form and its controls.

ControlPaint.DrawReversibleFrame(New Rectangle(PointToScreen(Rect.Location), Rect.Size), Color.White, FrameStyle.Dashed) End Sub

End Class

Using the GDI+ DrawRectangle() Method and a Translucent Brush If you want to emulate the selection rectangle that Vista and Windows

7 uses, this is also very easy to do. In fact, I was not able to invent this

method until I figured out how to make the rectangle for the

DrawReversibleFrame() method more stable. Only then did I begin

to wonder about how Vista and Windows 7 made their translucent

selection rectangle. Granted, I could have simply used the GDI+

DrawRectangle() method to emulate the previous examples, once I

discovered that the Invalidate(), Refresh(), and Update()

methods could be used to make the GDI+ DrawReversibleFrame()

method appear more professional-looking. However, because it was now so stable, especially with the

form’s DoubleBluffered property set to True, I felt that I had to figure it out.

Page 15: Restoring raster graphics and rubberbands to .net

Page –236–

NOTE: Setting the DoubleBuffered property for a form to True is important to cut down on screen flicker. With this

property set, the display will wait until it is between display refresh cycles to update the screen, so display updates while

drawing is going on will be minimized.

The only real problem I was running into was finding out how to create a translucent brush, and no one

seemed to have any answers on the internet – only questions on how to create one.

That is, until I happened upon an obscure example for using .NET’s SolidBrush() method while

studying brush creation in MSDN.

Creating a translucent brush simply involves assigning a color value to a brush. The color value is one of

the common ARGB colors that are typical to the WinXP+ operating systems. Unfortunately, most

people seem to be at a loss to as to how to do that with an alpha blended color value. Worse, most

examples of the SolidBrush() method specify just a single color parameter, and leave it at that.

The trick, though, is to realize that a color can be multiply defined. Consider the following examples:

Dim Clr As Color = Color.Navy 'assign Clr variable the color Navy Clr = Color.FromName("Navy") 'assign Clr variable the color Navy

Clr = RGB(0, 0, 80) 'assign Clr variable the color Navy (Red, Green, Blue)

Clr = Color.FromArgb(Color.Navy.ToArgb) 'assign Clr variable the integer value of Navy

Clr = Color.FromArgb(128, 0, 0, 80) 'Assign Clr variable an alpha blend value of 128 (1/2 opaque) for the color Green (A,R,G,B)

Clr = Color.FromArgb(0, 0, 80) 'Assign Clr variable the color Navy ([Alpha=255; fully opaque], Red=0, Green=0, Blue=80)

Clr = Color.FromArgb(64, Color.Navy) 'Assign Clr variable an alpha blend value of 64 (1/4 opaque, 3/4 transparent) for the color Navy

With this, we can create a brush with a ½ opaque (½ transparent) Light Blue color using the following:

Dim Brsh As New SolidBrush(Color.FromArgb(128, Color.LightBlue)) 'define translucent brush with 50% translucency

Then to use it, we need only fill a drawn rectangle with the brush:

Me.CreateGraphics.FillRectangle(Brsh, Rect) 'do fill with translucient brush (we can also define the new brush here)

Hence, replace the DrawRubberBand() method in the previous example with the following two methods:

'************************************************************************************** ' Subroutine: DrawRubberBand

' Draw the rubberband. Also draw over controls that may be placed on the form.

'**************************************************************************************

Private Sub DrawRubberBand()

'define a new rectangle to draw our current rectangle, and compare against any controls on the form

Dim Rect As Rectangle = NormalizeSelectRectangle()

DrawBand(Me.CreateGraphics(), Rect) 'draw selection rectangle for form

For Each Ctl As Control In Controls 'also process any controls it intersects with

If Ctl.Visible Then 'if control can be seen

Ctl.Refresh() 'always refresh control surface, even if not affected (otherwise artifacts may be left behind) If Ctl.Bounds.IntersectsWith(Rect) Then 'a control intersection was found

'erase the old rectangle or draw the new one over control, offsetting the rubberband as needed

DrawBand(Ctl.CreateGraphics, New Rectangle(Rect.Left - Ctl.Left, Rect.Top - Ctl.Top, Rect.Width, Rect.Height))

End If

End If

Next

End Sub

'**************************************************************************************

' Subroutine: DrawBand

' Draw using a Vista/Win7-style selection rubberband

'**************************************************************************************

Private Sub DrawBand(ByVal eg As System.Drawing.Graphics, ByVal Rect As Rectangle)

Dim VistaClr As Color = Color.FromArgb(255, 51, 153, 255) 'use Vista/Win7 Selector color (Alpha=255: fully opaque)

eg.DrawRectangle(New Pen(VistaClr), Rect) 'draw out edge of rectangle

eg.FillRectangle(New SolidBrush(Color.FromArgb(64, VistaClr)), Rect) 'do fill with translucient brush (Alpha=64: 1/4 opaque, 3/4 transparent)

End Sub

You now have Vista-style or Windows 7-style rubber band selection.

NOTE: Some people have reported that they have this type of selection rectangle under Windows XP. My XP platform uses

just a rectangle outline. Perhaps they have an enhancement patch installed, or have an option set that I have disabled,

because I thought that this translucent selection rectangle was introduced with Windows 6.0 (Vista. By the way, Windows 7

is actually Windows 6.1 – there goes the Microsoft marketing department, again).

David Ross Goben

Last update: Wednesday, June 08, 2011, 3:33:16 PM Contact: [email protected]

Page 16: Restoring raster graphics and rubberbands to .net

Page –237–

About the Author

David Ross Goben has been a professional software engineer, a writer, and an obsessive

researcher. Of Jewish descent, he has extensively explored Biblical history, ancient

cultural thinking, and ancient slang for over three decades, which has resulted in his seminal work: A Gnostic Cycle: Exploring the Origin of Christianity. He has written

numerous books, manuals, and magazine articles, many uncredited, or authored under

pen names. He has been involved in computing since long before personal computers

were available even in kit form, he was a contributing Editor to 80 Micro Magazine, and there, co-wrote the Feedback Loop column with Beve Woodbury, under the pen name, Mercedes Silver. His

interests include Cosmology, Quantum Physics, human-machine interaction, the Global Warming Myth, The

Electric Universe Theory, Perpetual Energy Technology, Quartz Technology, Dream Walking, and the study of the bio-mechanical origins of life.

He is currently exploring the possibility that Albert Einstein got his Theory of Special Relativity wrong. Just about

everyone is familiar with his equation, E=MC2, Energy (E) equals the Mass (M) times the speed of light (C)

squared. But this Mass to Energy conversion is only half of the Special Relativity expression.

The other half of Special Relativity involves Velocity (V), and is the calculation of Time Displacement (T):

Einstein calculated E = MC2 / T, which would mean that at the speed of light, an object would attain infinite

volume and infinite mass. Correct me if I am wrong, but even an ordinary mortal like me sees this as ridiculous,

because this would mean that we would attain several quintillion times the volume and mass that exists in the

entire universe (and even that may an infinite understatement). All you have to do is simply ponder it for a moment.

However, reverse the order, where E = T / MC2, and all calculations yield the same results, save one, and that is

that instead of attaining infinite volume and infinite mass at the speed of light, one instead moves into an alternate

universe. What most people do not realize is that the very atoms of our body, at this very moment, are already vibrating at just under the speed of light. Just a tiny push and we would become multidimensional beings. I think

this is why Quantum Physics, which is still nowhere near being a perfect model for planck-scale physics, will,

even so, discover the Far World, or what the ancients called the Underworld or the Hidden World, or most everyone today refers to as the Afterlife or Heaven. Quantum Physics has already demonstrated that nothing in the

universe has ever existed without Consciousness. So, where was Consciousness when the Universe was created

out of literally nothing during a causal Singularity Event? Evidence shows that it existed. And that has been the crux of great debates throughout history.

There is an easy experiment that can prove that Einstein’s Theory of Special Relativity is inverted. Weigh a very

heavy object, and then drop it onto a solid base, such as a concrete floor. Now weight the object again. It will

weigh less… for about 20 minutes; at which time its full weight will finally return. Where did the missing mass go to during those 20 minutes? If Einstein’s calculation had been correct, the object would have actually weighed

more after being dropped, not less.

Page 17: Restoring raster graphics and rubberbands to .net

Page –238–

The following Visual Basic documents are publicly available at: http://www.slideshare.net/DavidRossGoben, and at Google Docs at: http://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout=list&num=50.