Search code examples
gdiregioncliprad-studio

GDI - Unexpected result for the OffsetRgn() function


I'm using Embarcadero RAD Studio C++ builder XE7.

For a drawing function using the Windows GDI, I need to add a clip region to the device context of a canvas.

By testing my code, I noticed that sometimes the clipping region was smaller than the expected size. I searched why and I found a strange behavior of the OffsetRgn() function which lets me a little puzzled.

To apply the clip region, I use a code similar to the following:

std::unique_ptr<TBitmap> pBitmap(new TBitmap());
pBitmap->PixelFormat = pf32bit;
pBitmap->AlphaFormat = afDefined;
pBitmap->SetSize(60, 7);

TCanvas* pCanvas = pBitmap->Canvas;

::SelectClipRgn(pCanvas->Handle, NULL);

const TRect sourceRect = pCanvas->ClipRect;

HRGN pClipRegion = ::CreateRectRgn(50, -2, 60, 8);

::SelectClipRgn(pCanvas->Handle, pClipRegion);

const TRect intermediateRect = pCanvas->ClipRect;

const int deltaX = pCanvas->ClipRect.Left - 50;
const int deltaY = pCanvas->ClipRect.Top  - (-2);

::OffsetRgn(pClipRegion, -deltaX, -deltaY);

::SelectClipRgn(pCanvas->Handle, pClipRegion);

const TRect finalRect = pCanvas->ClipRect;

NOTE written like this and out of his context, the above code does not really make sense, and I know it's illogical. Please do not judge its quality, this is not the purpose of my question. I gathered several excerpts that I grouped into an executable code putting the problem forward.

The hardcoded values are an example of values I get in my application when the problem occurs. If I execute the above code, I measure:

  • left = 0, top = 0, right = 60, bottom = 7 in sourceRect value
  • left = 50, top = 0, right = 60, bottom = 7 in intermediateRect value
  • left = 50, top = 0, right = 60, bottom = 6 in finalRect

I however expected that the bottom value should also be equals to 7 in finalRect, which is the canvas limit, as I only moved the region and nothing else. So why it's value become suddenly smaller than expected?


Solution

  • So I finally found the substance of the case. Based on this post:

    Why does calling GetRgnBox on the result of GetClipRgn return a very different rect than GetClipRect?

    The clip region is applied in logical units relatively to the canvas origin, whereas the clipping rectangle I tried to apply was measured in pixels from a [0, 0] origin.

    As I incorrectly thought in my code that the origin was always [0, 0] for the both systems, the resulting region could be incorrect in several special cases, causing this strange shifting I sometimes noticed between the clipping really applied and which I expected.

    Measuring the canvas origin with the GetWindowOrgEx() function highlighted the issue.

    However for the above shown case, issue came because the clip region was moved by an offset of -2, taking so the value of -4 on top and 6 on bottom, which is then clipped to fit the canvas bounds while the clip region is applied, resulting to a clipping with value of 0 on top and 6 on bottom.