Cropping a Bitmap (C++ Builder)

Take the following steps to add code that lets you select an area with a mouse, crop the display to show only that area, and trim the bitmap to match the selected area. (This example uses both cropping and trimming, so that you can see the difference.)

1.

Start with the project that you created in Loading and Displaying an Image.

2.

Add the following declarations to the private section of the Unit1.h file:

bool Cropping; /*The state when the mouse is used for cropping*/
int StartX; /*Starting X position in screen pixels*/
int StartY; /*Starting Y position in screen pixels */
int EndX; /*Ending X position in screen pixels */
int EndY; /*Ending Y position in screen pixels */

3.

Add #include <printers.hpp> to the main unit.

4.

image\btndbtn.gif Select the Button control; then add the control to your main form. Put the control at the top of the form to keep it away from the image.

5.

In the Object Inspector box, change the Button control's Caption property to Select Rectangle, and change the name to RectButton.

6.

Code the Select Rectangle button's Click procedure as follows. In online help, you can copy the block of code and paste it into your application.

void __fastcall TForm1::RectButtonClick(TObject *Sender)
{
   /*Initialize cropping so that you can do it more than once*/
   if( Cropping == True)
   {
      /*Set the clipping area to match the image. */
      Lead1->SetDstClipRect(Lead1->DstLeft, Lead1->DstTop,
      Lead1->DstWidth, Lead1->DstHeight);
      /*Display the image */
      Lead1->ForceRepaint();
   }
   /*Set a form variable to let other events know that you are cropping*/
   Cropping = True;
   /*Set the pointer to a crosshair */
   Lead1->Cursor = crCross;
}

7.

Code the LEAD control's MouseDown procedure as follows. In online help, you can copy the block of code and paste it into your application.

void __fastcall TForm1::Lead1MouseDown (TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
   /*Save the starting position*/
   StartX = X;
   StartY = Y;
   /*Make the rubberband invisible until the mouse moves*/
   Lead1->RubberBandVisible = False;
}

8.

Code the LEAD control's MouseMove procedure as follows. In online help, you can copy the block of code and paste it into your application.

void __fastcall TForm1::Lead1MouseMove (TObject *Sender, TShiftState Shift, int X, int Y)
{
   int rbX;
   int rbY;
   int rbHeight;
   int rbWidth;

   if( (Cropping == True) && (Shift.Contains(ssLeft)))
   {
      /*Get the current mouse position */
      EndX = X;
      EndY = Y;
      /*Determine the origin of the rubberband rectangle,
      regardless of which way the mouse moves. */
      if( EndX > StartX) 
         rbX = StartX ;
      else 
         rbX = EndX;

      if( EndY > StartY) 
         rbY = StartY ;
      else 
         rbY = EndY;
      
      /*Determine the height and width of the rubberband rectangle*/
      rbHeight = abs(StartY - EndY);
      rbWidth = abs(StartX - EndX);
      /*Set the rubberband rectangle*/;
      Lead1->SetRubberBandRect(rbX, rbY, rbWidth, rbHeight);
        /*Alternatively, you could use the following properties to set the rubberband rectangle:
         Lead1->RubberBandHeight = rbHeight;
         Lead1->RubberBandLeft = rbX;
         Lead1->RubberBandTop = rbY;
         Lead1->RubberBandWidth = rbWidth; */

      /*Make the rubberband rectangle visible*/
      Lead1->RubberBandVisible = True;
   }
}

9.

Code the LEAD control's MouseUp procedure as follows. In online help, you can copy the block of code and paste it into your application.

void __fastcall TForm1::Lead1MouseUp (TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
   int CropLeft;
   int CropTop;
   int CropWidth;
   int CropHeight;

   /* Quit if( we are not selecting an area to crop. */
   if( Cropping == False) 
      return;

   /*Get the current mouse position */
   EndX = X;
   EndY = Y;
   /*Get the origin of the rubberband rectangle.
    Allow for different mouse drag directions */
   if( StartX < EndX) 
      CropLeft = StartX ;
   else 
      CropLeft = EndX;
   if( StartY < EndY) 
      CropTop = StartY ;
   else 
      CropTop = EndY;

   /*Get the height and width of the cropped area */
   CropWidth = abs(EndX - StartX);
   CropHeight = abs(EndY - StartY);
   if ((CropWidth < 1) || (CropHeight < 1))
   {
      Lead1->RubberBandVisible= False;
      return;
   }
   /*Crop and repaint the image */
   Lead1->SetDstClipRect(CropLeft, CropTop, CropWidth, CropHeight);
   Lead1->ForceRepaint();
   Lead1->RubberBandVisible = False;
   Lead1->Cursor = crDefault; /*Default */
}

10.

image\btndbtn.gif Select the Button control; then add another control to your main form. Put the control at the top of the form to keep it away from the image.

11.

In the Object Inspector box, change the Button control's Caption property to Trim. and change its Name property to TrimButton. This command button will be used to trim the bitmap in memory and redisplay the bitmap.

12.

Code the Trim button's Click procedure as follows. In online help, you can copy the block of code and paste it into your application.

void __fastcall TForm1::TrimButtonClick(TObject *Sender)
{
   Single XFactor;
   Single YFactor;
   Single NewTop;
   Single NewLeft;
   Single NewWidth;
   Single NewHeight;
   Single HeightFactor;
   Single WidthFactor;
   Single HeightAllowed;
   Single WidthAllowed;

   Screen->Cursor = crHourGlass; /*hourglass */
   /*Use the clipping rectangle's percentage offsets in the image rectangle */
   /*to determine the trimmed rectangle in the bitmap. */
   /*Using percentages allows for the possibility that the image is zoomed. */
   XFactor = Lead1->BitmapWidth / Lead1->DstWidth;
   YFactor = Lead1->BitmapHeight / Lead1->DstHeight;
   NewTop = (Lead1->DstClipTop - Lead1->DstTop) * YFactor;
   NewLeft = (Lead1->DstClipLeft - Lead1->DstLeft) * XFactor;
   NewWidth = Lead1->DstClipWidth * XFactor;
   NewHeight = Lead1->DstClipHeight * YFactor;

   /*Make sure display rectangles are automatically adjusted. */
   Lead1->AutoSetRects = True;
   /*Trim the bitmap. */
   Lead1->Trim(NewLeft, NewTop, NewWidth, NewHeight);
   /*Size and redisplay the control, using the new bitmap size. */
   /*Set the variables used for preserving the aspect ratio. */
   /*Allow for a border of 1/8 of the form size. */
   /*The units of measure do not matter, since we are calculating proportions. */
   HeightFactor = Lead1->BitmapHeight;
   WidthFactor = Lead1->BitmapWidth;
   HeightAllowed = ClientHeight - (ClientHeight / 4);
   WidthAllowed = ClientWidth - (ClientWidth / 4);

   /*Center the LEAD control on the form, preserving the aspect ratio.*/
   /*Check to see if( using the maximum width will make the image too tall. */
   /*Set the dimensions based on the result. */
   if(((WidthAllowed * HeightFactor) / WidthFactor) < HeightAllowed)
   {
      Lead1->Left = ClientWidth / 8;
      Lead1->Width = WidthAllowed;
      Lead1->Height = (Lead1->Width * HeightFactor) / WidthFactor;
      Lead1->Top = (ClientHeight - Lead1->Height) / 2;
   }
   else
   {
      Lead1->Top = ClientHeight / 8;
      Lead1->Height = HeightAllowed;
      Lead1->Width = (Lead1->Height * WidthFactor) / HeightFactor;
      Lead1->Left = (ClientWidth - Lead1->Width) / 2;
   }

   /*Turn off scroll bars to make sure we use the full client area. */
   Lead1->AutoScroll = False;
   /*Set the image display size to match the LEAD control */
   Lead1->SetDstRect(0, 0, Lead1->Width, Lead1->Height);
   Lead1->SetDstClipRect(0, 0, Lead1->Width, Lead1->Height);
   /*Display the image */
   Lead1->ForceRepaint();
   Screen->Cursor = crDefault; /*Default */
}

13.

Run your program to test it.