CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 1 of 1
  1. #1
    Join Date
    Feb 2000
    Location
    Indore, India
    Posts
    1,046

    Windows SDK GDI: How do I fill rectangles, regions and texts in graded colours?

    Q: How do I fill rectangles, regions and texts in graded colours?

    A: Filling colours in a gradient way along one direction is shown in the following knowledge base article. Using the same technique, but extending it a bit, you can make rectangles, regions and texts in graded colours as shown in the attached image.

    Filling rectangles in graded colours is done by calculating the smallest rectangle having the same background colour. Colour at a point is evaluated by linearly interpolating the colours at its four corners.

    Here is the code for drawing a rectangle in graded colours. The code is wrapped into a neat function, arguments of which are device context handle (of type 'HDC'), rectangle object (of type 'RECT') and array to colours at corners (of type 'LPCOLORREF'). Colours at four corners are passed to this and similar functions in the clockwise order starting from left-top (left-top, right-top, right-bottom and left-bottom).


    Code:
    // Fills the rectangle 'rect' in graded colours
    void GradientFill(HDC hdc, RECT rect, LPCOLORREF color_array)
    {
      // Calculates size of single colour bands
      int xStep = (rect.right - rect.left) / 256 + 1;
      int yStep = (rect.bottom - rect.top) / 256 + 1;
      // X loop starts
      for (int iX = 0, X = rect.left; iX < 256; iX++)
      {
        // Calculates end colours of the band in Y direction
        int RGBColor[3][2] = {
          {IPOL(GetRValue(color_array[0]), GetRValue(color_array[1]), iX),
            IPOL(GetRValue(color_array[3]), GetRValue(color_array[2]), iX)
          },
          {IPOL(GetGValue(color_array[0]),  GetGValue(color_array[1]), iX),
            IPOL(GetGValue(color_array[3]), GetGValue(color_array[2]), iX)
          },
          {IPOL(GetBValue(color_array[0]), GetBValue(color_array[1]), iX),
            IPOL(GetBValue(color_array[3]), GetBValue(color_array[2]), iX)
          }
        };
        // Y loop starts
        for (int iY = 0, Y = rect.top; iY < 256; iY++)
        {
          // Calculates the colour of the rectangular band
          COLORREF Color = RGB(IPOL(RGBColor[0][0], RGBColor[0][1], iY),
            IPOL(RGBColor[1][0], RGBColor[1][1],  iY),
            IPOL(RGBColor[2][0], RGBColor[2][1], iY));
          // Creates the brush to fill the rectangle
          HBRUSH hBrush = CreateSolidBrush(Color);
          // Paints the rectangular band with the brush
          RECT Rect = {X, Y, X + xStep, Y + yStep};
          FillRect(hdc, &Rect, hBrush);
          // Deletes the brush
          DeleteObject(hBrush);
          // Updates Y value of the rectangle
          Y += yStep;
          if (Y > rect.bottom)
            Y = rect.bottom;
        }
        // Updates X value of the rectangle
        X += xStep;
        if (X > rect.right)
          X = rect.right;
      }
    }
    Creating a region in graded colours is done in three steps. First is to create the bounding rectangle of the region in graded colours and attach it to a memory DC. This memory DC is BitBlt-ed to the display DC in 'SRCINVERT' mode. Second step is to paint the region in black over this. Third and final step is to BitBlt the memory DC once more in 'SRCINVERT' mode. Whatever is inside the region will be painted in graded colours and whatever is outside will be cancelled due to two BitBlts of 'SRCINVERT' mode.

    Here is the code. Arguments are similar except that the second is the region instead of rectangle in earlier snippet.


    Code:
    // Fills the region 'rgn' in graded colours
    void GradientFill(HDC hdc, HRGN rgn, LPCOLORREF color_array)
    {
      // Creates memory DC
      HDC hMemDC = CreateCompatibleDC(hdc);
      if (hMemDC)  // Memory DC creation successful
      {
        // Gets bounding rectangle of region
        RECT rectRgn;
        GetRgnBox(rgn, &rectRgn);
        // Left top point of applying mask
        int X = rectRgn.left, Y = rectRgn.top;
        // Size of mask
        int Width = rectRgn.right - X, Height = rectRgn.bottom - Y;
        // Creates bitmap for the mask
        HBITMAP hBitmap = CreateCompatibleBitmap(hdc, Width, 
    
    Height);
        if (hBitmap)  // Bitmap created successfully
        {
          // Selects bitmap in memory DC
          HBITMAP hOldBitmap = (HBITMAP) SelectObject(hMemDC, hBitmap);
          // Prepares gradient filled mask and applies to output DC
          OffsetRect(&rectRgn, -rectRgn.left, -rectRgn.top);
          GradientFill(hMemDC, rectRgn, color_array);
          BitBlt(hdc, X, Y, Width, Height, hMemDC, 0, 0, SRCINVERT);
          // Displays region in black in output DC
          FillRgn(hdc, rgn, (HBRUSH) GetStockObject(BLACK_BRUSH));
          // Applies mask to output DC again
          BitBlt(hdc, X, Y, Width, Height, hMemDC, 0, 0, SRCINVERT);
          // De-selects bitmap from memory DC
          SelectObject(hMemDC, hOldBitmap);
          // Deletes bitmap
          DeleteObject(hBitmap);
        }
        // Deletes memory DC
        DeleteDC(hMemDC);
      }
    }
    Writing a text in graded colours is similar to displaying a region in graded colours except the fact that the intermediate step is to display the text in black colour. Arguments, apart from display DC and colour array, are the X and Y positions of the string and the string as 'LPSTR' type.


    Code:
    // Fills the text 'text' in graded colours at (x, y)
    void GradientFill(HDC hdc, int x, int y, LPSTR text, LPCOLORREF color_array)
    {
      // Creates memory DC
      HDC hMemDC = CreateCompatibleDC(hdc);
      if (hMemDC)          // Memory DC creation successful
      {
        // Gets size of the string
        SIZE Size;
        GetTextExtentPoint32(hdc, text, lstrlen(text), &Size);
        // Creates bounding rectangle of the text
        RECT rectText = {x, y, x + Size.cx, y + Size.cy};
        // Creates bitmap for the mask
        HBITMAP hBitmap = CreateCompatibleBitmap(hdc, Size.cx, Size.cy);
        if (hBitmap)      // Bitmap created successfully
        {
          // Selects bitmap in memory DC
          HBITMAP hOldBitmap = (HBITMAP) SelectObject(hMemDC, hBitmap);
          // Prepares gradient filled mask and applies to output DC
          OffsetRect(&rectText, -rectText.left, -rectText.top);
          GradientFill(hMemDC, rectText, color_array);
          BitBlt(hdc, x, y, Size.cx, Size.cy, hMemDC, 0, 0, SRCINVERT);
          // Displays text in black colour in output DC
          // Background mode is transparent
          COLORREF TextColor = SetTextColor(hdc, RGB(0, 0, 0));
          int BkMode = SetBkMode(hdc, TRANSPARENT);
          TextOut(hdc, x, y, text, lstrlen(text));
          SetBkMode(hdc, BkMode);
          SetTextColor(hdc, TextColor);
          // Applies mask to output DC again
          BitBlt(hdc, x, y, Size.cx, Size.cy, hMemDC, 0, 0, SRCINVERT);
          // De-selects bitmap from memory DC
          SelectObject(hMemDC, hOldBitmap);
          // Deletes bitmap
          DeleteObject(hBitmap);
        }
        // Deletes memory DC
        DeleteDC(hMemDC);
      }
    }
    Code for interpolating the colours in between is given as a macro:


    Code:
    // Interpolates intermediate value at N
    // X0 corresponds to N = 0 and X1 to N = 255
    #define IPOL(X0, X1, N) ((X0) + ((X1) - (X0)) * N / 256)

    Attached Images Attached Images  
    Last edited by Andreas Masur; March 12th, 2006 at 06:13 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured