C# Programming - GDI+

 

In a windows program, the output to an I/O device such as the display or printer requires going through an intermediate object known as the device context (DC). The purpose of DC is to make the windows program device independent so that the program will work with any type of display or printer without requiring a recompilation. The device context has two portions to it as shown below:

 

                                                Windows Program

 

 

 

 

 

 

 

 


                                                Output Device (e.g., display or printer)

 

The context portion of the DC maintains information about the drawing context such as line width, style, color, fill color, font style, font size etc.. The device drive portion of DC contains the actual device driver for a particular output device.

 

In MFC, there is a detailed set of classes that deal with the DC. The general framework that deals with the DC is referred to as the GDI (Graphical Device Interface). In .Net, the GDI interface has been greatly refined and is referred to as GDI+. The .Net GDI+ classes encapsulate some of the important Windows API to create graphical outputs. The set of GDI+ classes is quite comprehensive such that entire books can be written on it. We will try to cover some of the important concepts and the popular GDI+ classes needed in creating windows applications.

 

The System.Drawing namespace contains most of the classes, structs, enums, and delegates that provide the basic drawing functionality. The System.Darwing contains other namespaces for specialized applications such as System.Drawing.Imaging for image processing, System.Drawing.Design for design time controls such as dialog boxes, property sheets etc.., System.Design.Text for conrolling fonts, System.Drawing.Printing for controlling printing and print preview.

 

         When dealing with drawings, there are three important .Net structs that are commonly used. These are Point, Size and Rectangle. The Point has two properties X and Y that are of type integer. The Point also has another similar struct called PointF where the X and Y properties are in float data type. Similarly Size and Rectangle have equivalent structs called SizeF and RectangleF. The Size struct has Width and Height as the two properties. The Rectangle can be specified in terms of Top, Left, Bottom, Right or Top, Left, Width and Height, or Location (Point) and Size. There is also a Region class that can be used to define arbitrarily complex shapes.

         The Color class has some standard colors defined in it as static members e.g., Color.Red. When a precise control over a color is needed, there is a FromArgb function that can be used to specify the combination of red, green and blue pixels, e.g.,

         Color.FromArgb(255,255,0) ; will generate a yellow color.

 

In GDI+, the DC is wrapped in the class System.Drawing.Graphics. All drawing of shapes such as rectangles, lines, text requires you to create the DC first. The typical code that creates a Dc for you is:

Graphics dc = this.CreateGraphics();   // code written inside a form class

 

Windows generates a PAINT message whenever a window needs to be repainted. .Net framework invokes an Paint( ) handler whenever a window needs to be repainted.

 

Example: Create a windows application. Name the project “gditest”. Right click on Form1.cs in the project explorer and choose “view code”. Type the following code in the constructor for form1 class.

    public Form1()

    {

     //

     InitializeComponent();

     this.BackColor = Color.Aqua;

    }

 

Add a Paint event handler by clicking on the lightning symbol above the properties window and double clicking on the Paint event. Type the following code in it. Note that the Paint event handler gets a DC through the PaintEventArgs parameter, so you can simply use, Graphics dc = e.Graphics; to obtain the DC.

 

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

    //---first create a device context

    // Graphics dc = this.CreateGraphics(); normally but Paint

      // is given a DC through the PaintEventArgs parameter

      Graphics dc = e.Graphics;

     

      Pen bluePen = new Pen(Color.Blue, 3);  // 3 pixels wide

      dc.DrawEllipse(bluePen, 30,30,40,50);  //30,30 is top,left, 40=width, 50=height

      Pen redPen = new Pen(Color.Red, 2);

      dc.DrawLine(redPen, 30,30,100,100);

 

      Pen greenPen = new Pen(Color.Green);

      dc.DrawRectangle(greenPen,60,60,50,50);

    }

 

If you run the program by choosing “debug->start without debugging”, you will see the following results.

 

 

Clipping Region: The PaintEventArgs parameter in the Paint handler also contains information about the clipping rectangle. The clipping rectangle is the rectangular area that needs to be repainted (this is similar to the invalidated region in MFC). The clipping rectangle becomes important when there are a large number of objects to be redrawn in the Paint handler. Using the clipping region information, we can choose to redraw only a few of these objects which are being affected.

 

Example:  Modify the Paint handler code to look as,

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      //---first create a device context

      // Graphics dc = this.CreateGraphics(); normally but Paint

      // is given a DC through the PaintEventArgs parameter

      Graphics dc = e.Graphics;

     

      Pen bluePen = new Pen(Color.Blue, 3);  // 3 pixels wide

      dc.DrawEllipse(bluePen, 30,30,40,50);  //30,30 is top,left, 40=width, 50=height

     

      Pen redPen = new Pen(Color.Red, 2);

      dc.DrawLine(redPen, 30,30,100,100);

 

      if ((e.ClipRectangle.Top < 60) && (e.ClipRectangle.Left < 60)

&& (e.ClipRectangle.Bottom > 110) && (e.ClipRectangle.Right > 110))

      {

        Pen greenPen = new Pen(Color.Green);

        dc.DrawRectangle(greenPen,60,60,50,50);

      }

    }

 

Now the green rectangle will appear when you run the program, but if you overlap the above form window such that the top and left of the window covering the above form has greater than 60,60 coordinates, then if you minimize the other window that was covering the above form, the green rectangle will not be redrawn because of the clipping region code.

The clipping region is particularly useful when there are different shapes, and one of the shapes is selected to be moved or deleted. Then as a result of movement or deletion, you should redraw only the moved shape. The clipping region to be redrawn is controlled by the Invalidate call.

 

            Brushes are used to fill a particular region with a color or a pattern. For example, the DC has a FillRectangle method and a FillEllipse method which you can pass the brush and the bounding rectangle to fill the shape with the brush.

 

Example: Modify the Paint handler to fill the rectangle and the ellipse with brushes as shown below.

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      //---first create a device context

      // Graphics dc = this.CreateGraphics(); normally but Paint

      // is given a DC through the PaintEventArgs parameter

      Graphics dc = e.Graphics;

      Rectangle r1 = new Rectangle(30,30,40,40);

      Pen bluePen = new Pen(Color.Blue, 3);  // 3 pixels wide

      dc.DrawEllipse(bluePen, r1);  //30,30 is top,left, 50=height

      Brush yellowBrush = new SolidBrush(Color.Yellow);

      dc.FillEllipse(yellowBrush,r1);

      // alternatively you can use Brushes type

      // which has many Brushes defined as static objects

      // e.g., dc.FillEllipse(Brushes.Yellow,r1)

 

      Pen redPen = new Pen(Color.Red, 2);

      dc.DrawLine(redPen, 30,30,100,100);

      if ((e.ClipRectangle.Top < 60) && (e.ClipRectangle.Left < 60)

&& (e.ClipRectangle.Bottom > 110) && (e.ClipRectangle.Right > 110))

      {

        Rectangle r2 = new Rectangle(60,60,50,50);

        Pen greenPen = new Pen(Color.Green);

        dc.DrawRectangle(greenPen,r2);

        Brush coralBrush = new SolidBrush(Color.Coral);

        dc.FillRectangle(coralBrush,r2);

      }

    }

 

 

         There are three popular coordinate systems in .Net windows programming. These are World coordinates, Page coordinates and Device coordinates. The World coordinates measure the position relative to the (top, left) of the document in pixels (it can be thought of as the logical coordinates under the old GDI concepts). The Page coordinates measure the position relative to the (top,left) of the client area in pixels. The device units are used to specify measurements in inches and millimeters.

 

Scrolling:

            Scrolling in Windows forms is made easy by the AutoScrollPosition property of the form. If no clipping region is involved, then you can simply adjust the DC properly by the following code:

            dc.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);

 

When the clipping region is involved, then you have to adjust the offsets of rectangles by the size of the scroll position obtained from the AutoScrollPosition property.

Example:

            Modify the constructor and the Paint code to properly support scrolling as shown below.

public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

      this.BackColor = Color.Aqua;

      this.AutoScrollMinSize = new Size(250,350);

    }

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      //---first create a device context

      // Graphics dc = this.CreateGraphics(); normally but Paint

      // is given a DC through the PaintEventArgs parameter

      Graphics dc = e.Graphics;

     

      dc.TranslateTransform(this.AutoScrollPosition.X,

        this.AutoScrollPosition.Y);  // without this yellow

      // circle will not scroll properly

      Rectangle r1 = new Rectangle(30,30,140,140);

      Pen bluePen = new Pen(Color.Blue, 3);  // 3 pixels wide

      dc.DrawEllipse(bluePen, r1);  //30,30 is top,left,

      Brush yellowBrush = new SolidBrush(Color.Yellow);

      dc.FillEllipse(yellowBrush,r1);

 

      Pen redPen = new Pen(Color.Red, 2);

      dc.DrawLine(redPen, 30,30,100,100);

 

      Size scrollOffset = new Size(this.AutoScrollPosition);

      if ((e.ClipRectangle.Top +scrollOffset.Width < 160) ||

        (e.ClipRectangle.Left + scrollOffset.Height < 160))

      //  && (e.ClipRectangle.Bottom + scrollOffset.Width > 310)

      //  && (e.ClipRectangle.Right + scrollOffset.Height > 310))

      {

        Rectangle r2 = new Rectangle(160+scrollOffset.Width,

          160+scrollOffset.Height,150,150); 

        Pen greenPen = new Pen(Color.Green);

        dc.DrawRectangle(greenPen,r2);

        Brush coralBrush = new SolidBrush(Color.Coral);

        dc.FillRectangle(coralBrush,r2);

      }

    }

Zooming, Scrolling and Hit Testing:

            Zooming is easily supported in .Net Windows applications by the scale factor supported as a property in DC. The typical zooming code looks as:

      m_Scalef = 2.0; // set in a menu handler for zooming

 

      dc.PageUnit = GraphicsUnit.Pixel; // drawing code

      dc.PageScale = m_Scalef;  // default value of m_Scalef = 1.0

             dc.DrawEllipse(…

 

The zooming does create some complications with respect to scrolling and hit testing to see if the mouse was clicked inside a region. The TranslateTransform and the scroll offsets will be needed just as described in the previous example.

      dc.TranslateTransform(this.AutoScrollPosition.X/m_Scalef,

        this.AutoScrollPosition.Y/m_Scalef); 

 

For hit testing, the mouse position can be obtained from the MouseEventArgs parameter in the MouseDown event handler. This mouse position will need to be adjusted for the scrolling position as,

 

      // e is the parameter containing the mouse position

      Size scrollOffset = new Size(this.AutoScrollPosition);

      mousep[0] = new Point(e.X-scrollOffset.Width, e.Y-scrollOffset.Height);

      dc.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device,mousep);

 

      Pen pen = new Pen(Color.Green,1);

      dc.DrawRectangle(pen,m_r1);

      if (m_r1.Contains(new Rectangle(mousep[0].X, mousep[0].Y,1,1)))

        MessageBox.Show("mouse clicked inside rectangle");

 

Example: Create a new windows application project called testgrfx. Add a menu control to the form. Add a menu item called “zoom” with “zoom in” and “zoom out” as the menu options underneath zoom.

The code for the important parts of the project is shown below.

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Drawing.Drawing2D;

 

namespace testgrfx

{

  /// <summary>

  /// Summary description for Form1.

  /// </summary>

  public class Form1 : System.Windows.Forms.Form

  {

    float m_Scalef;

    Rectangle m_r1;

  ….

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

 

      InitializeComponent();

      m_Scalef = 1.0f;  // for zooming purposes

      m_r1 = new Rectangle(50,50,100,100);

      this.AutoScrollMinSize = new Size(600,700);

    }

 

    /// <summary>

    /// Clean up any resources being used.

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      if( disposing )

      {

        if (components != null)

        {

         components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

 

    private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      Graphics dc = e.Graphics;

      dc.PageUnit = GraphicsUnit.Pixel;

      dc.PageScale = m_Scalef;

      dc.TranslateTransform(this.AutoScrollPosition.X/m_Scalef,

        this.AutoScrollPosition.Y/m_Scalef); 

 

      Pen pn = new Pen(Color.Blue,2);

      dc.DrawEllipse(pn,m_r1);

    }

 

    private void mnuZoomin_Click(object sender, System.EventArgs e)

    {

      m_Scalef = m_Scalef * 2.0f;

      Invalidate();  // to trigger Paint of entire client area

    }

 

    private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)

    {

      Graphics dc = CreateGraphics();

      dc.TranslateTransform(this.AutoScrollPosition.X/m_Scalef,

        this.AutoScrollPosition.Y/m_Scalef); 

      dc.PageUnit = GraphicsUnit.Pixel;

      dc.PageScale = m_Scalef;

 

      Point [] mousep = new Point[1]; 

     // make sure to adjust mouse pos.for scroll position

      Size scrollOffset = new Size(this.AutoScrollPosition);

      mousep[0] = new Point(e.X-scrollOffset.Width, e.Y-scrollOffset.Height);

     

      dc.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device,mousep);

        Pen pen = new Pen(Color.Green,1);

      dc.DrawRectangle(pen,m_r1);

      if (m_r1.Contains(new Rectangle(mousep[0].X, mousep[0].Y,1,1)))

        MessageBox.Show("click inside rectangle");

    }

 

    private void Form1_Load(object sender, System.EventArgs e)

    {

   

    }

 

    private void mnuZoomout_Click(object sender, System.EventArgs e)

    {

      m_Scalef = m_Scalef / 2.0f;

      Invalidate();

    }

  }

}

 

Displaying Text, Fonts:

            The DC provides a DrawString method to display a text in the client area. Brushes are used with fonts to display the text in a particular color.

 

Example: Create a new windows application. Name the project fontex. Type the following code in the Form1 class.

public class Form1 : System.Windows.Forms.Form

  {

    /// <summary>

    /// Required designer variable.

    /// </summary>

    private System.ComponentModel.Container components = null;

   

    private Brush blackBr = Brushes.Black;  

    private Brush redBr = Brushes.Red;

    private Brush royalBlueBr = Brushes.RoyalBlue;

 

    private Font boldTRFont = new Font("Times New Roman",14,FontStyle.Bold);

    private Font italicCOFont = new Font("Courier",12,FontStyle.Italic);

    private Font ARFont = new Font("Arial",10,FontStyle.Regular);

 

 

Add a Paint handler to the form with the following code in it.

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

    {

      Graphics dc = e.Graphics;

      String s1 = "This is a test of Fonts" ;

      String s2 = "This line is in italics";

      String s3 = "This line is in Arial, regular style";

 

      dc.DrawString(s1,boldTRFont,blackBr,new Point(0,20));

      dc.DrawString(s1,italicCOFont,redBr,new Point(0,40));

      dc.DrawString(s1,ARFont,royalBlueBr,new Point(0,60));

    }

 

Collecting Font information from the Common Font Dialog:

            Add a menu control to the form in the fontex example. Add a menu item called “choose” with a menu option “choose font” and “choose color” underneath it. Name the “choose” menu item as mnuChoose, “choose Font” as mnuChooseFont and “choose Color” as mnuChooseColor.

Add the following declarations to the Form1 class as,

    private string sFont = "Setting Fonts through Font Dialog" ;

    private Color fontColor;

    private System.Windows.Forms.MenuItem mnuChooseColor;

    private Font font;

 

Modify the constructor to look as,

public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

      fontColor = Color.Black;

      font = new Font("Arial",8,FontStyle.Regular);

    }

 

 

Add the following line to the existing Paint handler.

dc.DrawString(sFont,font, new SolidBrush(fontColor),new Point(0,0));

 

Type the following code for the two menu event handlers.

 

private void menuChooseFont_Click(object sender, System.EventArgs e)

    {

      FontDialog fontDlg = new FontDialog();

      //fontDlg.Color = Color.Red ;  // default color

      if (fontDlg.ShowDialog() == DialogResult.OK)

      {

        font = fontDlg.Font;

        Invalidate();

      }

    }

 

    private void mnuChooseColor_Click(object sender, System.EventArgs e)

    {

      ColorDialog colorDlg = new ColorDialog();

      if (colorDlg.ShowDialog() == DialogResult.OK)

      {

        fontColor = colorDlg.Color;

        Invalidate();

      }

    }

 

Run the application and experiment with the color and font dialogs.

 

 

Client Area Determination:

     Sometimes our windows application needs to know the size of the client area. In simple situations, the code in a form class can obtain the client area as,

      int cx = this.ClientRectangle.Width;

      int cy = this.ClientRectangle.Height;


C# - Image Processing

 

C# provides the System.Drawing.Imaging namespace that contains the types related to image processing. One of the useful classes in this namespace is called Bitmap which can easily read images from JPEG or GIF files and convert them to bitmaps. The constructor for the Bitmap class takes on an image file and generates the corresponding bitmap for it.

      Bitmap b1 = new Bitmap(“t1.jpg”);

 

Once the image has been converted to a bitmap, you can easily apply different image processing techniques to the individual pixels. For example, if you add a small integer to each RGB byte, you will end up increasing the brightness of the picture. We will develop a small windows application that will allow us to display images and perform different operations on them.

 

When operating on bitmaps, we need to modify the pixel memory directly. This is where old C/C++ style pointers can be very handy. Fortunately C# allows us to deal with memory directly through the unsafe keyword. If a C# code uses an unsafe block, the entire project has to be built by specifying the “Configuration Properties” of the project to “allow unsafe blocks”. We will explain these issues through an example.

 

 

Example: Create a new Windows application. Name the project ImageProc.

Put two picture boxes on the form. Name the left picture box as “picOrig”. Name the right picture box as “picRight”. Set the border style property of the picture boxes to Fixed Single. Set the SizeMode property of the picture boxes to “StretchImage”. Try to size the picture boxes so that their width is larger than their height (similar to the aspect ratio of a 3x5 picture). Put a label “Original Image” underneath the left picture box, and a label “After Image Processing” under the right picture box.

 

Add a main menu to the form. Create a top level menu called “File” with a menu item “Load Image” underneath it. Name the menus as “mnuFile” and “mnuFileLoadImage” respectively.

 

Type the following code for the “Load Image” menu handler.

 

  private void mnuFileLoadImage_Click(object sender, System.EventArgs e)

    {

      OpenFileDialog dialog = new OpenFileDialog();

      dialog.Filter = "jpeg files (*.jpg)|*.jpg";

      if (DialogResult.OK == dialog.ShowDialog())

          this.picOrig.Image = new Bitmap(dialog.FileName);

    }

Run the program and click on the menu to load an image. Choose a jpg type file and see if the image gets loaded properly in the left picture box.

 

Add a C# class to the project (by right clicking on the project name through the project explorer). Name the class MyImageProc. Add the following method to the MyImageProc class to convert to a Gray scale image from a color image.

 

  public static bool CovertToGray(Bitmap b)

    {

      // GDI+ return format is BGR, NOT RGB.

      BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),

        ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

      int stride = bmData.Stride; // bytes in a row 3*b.Width

      System.IntPtr Scan0 = bmData.Scan0;

      unsafe

      {

        byte * p = (byte *)(void *)Scan0;

        byte red, green, blue;

        int nOffset = stride - b.Width*3;

        for(int y=0;y < b.Height;++y)

        {

          for(int x=0; x < b.Width; ++x )

          {

               blue = p[0];

               green = p[1];

               red = p[2];

               p[0] = p[1] = p[2] = (byte)(.299 * red

                    + .587 * green + .114 * blue);

               p += 3;

          }

          p += nOffset;

        }

      }

      b.UnlockBits(bmData);

      return true;

    }

 

Because the above code is using types that are defined in the System.Drawing and System.Drawing.Imaging namespaces, you will need to add the following lines, at the top of the MyImageProc class.

using System.Drawing;

using System.Drawing.Imaging;

 

Add a top level menu called “Image Proc”. Underneath the “Image Proc” menu, add a menu item called “Convert to Gray”. Name the “Image proc” menu as mnuImage, and the “Convert to Gray” menu as mnuImageGray. Set the enabled property of this menu to false. Write the following event handler for the “Convert to Gray” menu item.

 

  private void mnuImageGray_Click(object sender, System.EventArgs e)

    {

      Bitmap copy = new Bitmap((Bitmap) this.picOrig.Image);

      MyImageProc.CovertToGray(copy);

      picRight.Image = null;

      picRight.Image = copy; 

    }

 

 

Modify the File->Load Image handler to enable the “Convert to Gary” menu once an image file has been loaded in the left image box.

 

  private void mnuFileLoadImage_Click(object sender, System.EventArgs e)

    {

      OpenFileDialog dialog = new OpenFileDialog();

      dialog.Filter = "jpeg files (*.jpg)|*.jpg";

      if (DialogResult.OK == dialog.ShowDialog())

      {

          this.picOrig.Image = new Bitmap(dialog.FileName);

          mnuImageGray.Enabled = true;

         

      }

    }

 

 

Now if you try to build the program, you will get an error message as “Unsafe code may only appear if compiling with /unsafe”. To take care of this error, right click on the project name, and choose properties. Then click on the “Configuration Properties” and change the “Allow unsafe code block” settings to “True”, as shown below.

 

 

Now build the project. Run the application and load an image in the left hand picture box. Then click on the “Image Proc” menu item and choose “Convert to Gray”. You should see the left hand picture being converted to a “Gray Scale” picture and displayed in the right hand picture box.

 

 

Add a method called “Brighten” to the MyImageProc class. This method will increase the brightness of each pixel by adding a number to each of the RGB bytes. The code for the Brighten method is shown below.

 

    public static bool Brighten(Bitmap b, int nBrightness)

    {

      // GDI+ return format is BGR, NOT RGB.

      BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),

        ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

      int stride = bmData.Stride;

      System.IntPtr Scan0 = bmData.Scan0;

      unsafe

      {

        int nVal;

        byte * p = (byte *)(void *)Scan0;

        int nOffset = stride - b.Width*3;

        int nWidth = b.Width * 3;

        for(int y=0;y<b.Height;++y)

        {

          for (int x = 0;  x < nWidth; ++x)

          {

               nVal = (int) (p[0] + nBrightness);

               if (nVal < 0) nVal = 0;

               if (nVal > 255) nVal = 255;

               p[0] = (byte)nVal;

               ++p;

          }

          p += nOffset;

        }

      }

      b.UnlockBits(bmData);

      return true;

    }

 

Add another menu item called “Brighten Image” underneath the “Image Proc” menu. Name the “Brighten Image” menu as mnuImageBrighten. Set the enabled property of this menu item to false. Type the following code in the “Brighten Image” handler.

  private void mnuImageBrighten_Click(object sender, System.EventArgs e)

    {

      Bitmap copy = new Bitmap((Bitmap) this.picOrig.Image);

      MyImageProc.Brighten(copy,40); // add 40 to each byte in pixel

      picRight.Image = null;

      picRight.Image = copy; 

    }

 

Modify the File->Load Image handler to enable the mnuImageBrighten by adding the following line to it:

          mnuImageBrighten.Enabled = true;

 

Run the program, and choose an image, then choose the “Brighten Image” menu to brighten the image as shown below.

 

Rather than fixing the amount of brightness, it will be nice to let the user choose the brightness level through another dialog. We can provide a default value of 30 in this dialog. To achieve this, add a “windows form” to the project (by right clicking on the project name and choosing Add). Name the form “BrightnessDlg.cs”. Set the FormBorder property to “Fixed Single”. Also set the “Maximize Box” property to False.

Add a text box to the form. Name the text box, txtBrightness. Add two buttons called Apply and Cancel, with names of cmdApply and cmdCancel respectively. This dialog will look as,

 

The important code in the BrightnessDlg.cs file will look as,

  public class BrightnessDlg : System.Windows.Forms.Form

  {

   

 

    public int nBrightness

    {

      get

      {

        return (Convert.ToInt32(txtBrightness.Text, 10));

      }

      set{txtBrightness.Text = value.ToString();}

    }

 

  private void cmdApply_Click(object sender, System.EventArgs e)

    {

      this.DialogResult = System.Windows.Forms.DialogResult.OK;

      this.Close();

    }

 

  private void cmdCancel_Click(object sender, System.EventArgs e)

    {

      this.DialogResult = System.Windows.Forms.DialogResult.Cancel;

      this.Close();

    }

  }

}

 

Modify the “Brighten Image” event handler as,

 

    private void mnuImageBrighten_Click(object sender, System.EventArgs e)

    {

      BrightnessDlg dlg = new BrightnessDlg();

      dlg.nBrightness = 30;  // default brightness of 30

 

      if (DialogResult.OK == dlg.ShowDialog())

      {

        Bitmap copy = new Bitmap((Bitmap) this.picOrig.Image);

        MyImageProc.Brighten(copy,dlg.nBrightness); 

        picRight.Image = null;

        picRight.Image = copy;

        mnuFileSave.Enabled = true;

      }

    }

 

Add a menu called “Save Right Image” under the File menu. Give it a name of mnuFileSave. Type the following code in the event handler.

  private void mnuFileSave_Click(object sender, System.EventArgs e)

    {

      SaveFileDialog dlg = new SaveFileDialog();

      dlg.Filter = "jpeg files (*.jpg)|*.jpg";

      if (DialogResult.OK == dlg.ShowDialog())

          this.picRight.Image.Save(dlg.FileName, ImageFormat.Jpeg);

    }

 

 

You will need to add the following namespace to the Form1.cs file,

            using System.Drawing.Imaging;

 

Modify the Image->Convert to Gray handler to enable the File->Save menu by adding the following line shown in bold.

 

  private void mnuImageGray_Click(object sender, System.EventArgs e)

    {

      Bitmap copy = new Bitmap((Bitmap) this.picOrig.Image);

      MyImageProc.CovertToGray(copy);

      picRight.Image = null;

      picRight.Image = copy;

      mnuFileSave.Enabled = true;

    }

 

 

Add a menu called “Exit” under the File menu. Give it a name of mnuFileExit. Type the following code in the event handler.

 

  private void mnuFileExit_Click(object sender, System.EventArgs e)

    {

     this.Close();

    }

 

Build and test the program.