Recent

Author Topic: Contribution (GTK2): RectInRegion, PtInRegion and FillRgn  (Read 19327 times)

vcunha

  • New member
  • *
  • Posts: 9
Contribution (GTK2): RectInRegion, PtInRegion and FillRgn
« on: October 22, 2012, 03:41:17 pm »
Sorry for my bad English.

I am converting a system for drawing entity relationship diagram which was initially developed in Delphi to Lazarus / Free Pascal (I hope to post soon on Wiki applications developed in Lazarus.).

Taking part in manual work, everything was going swimmingly in the Windows but on Linux there was something wrong. I understand that the problems were related to two functions: RectInRegion and PtInRegion. In Linux, these underlying functions always returns false.

After much research I managed the implementation that works satisfactorily both in Windows and on Linux (tested on Ubuntu 12.04 and 12.10) (I can not test on MacOS, but would be happy to know that also works).

I realized that if we disregard the existence of directives compilation, or are withdrawn. The result is the same on Windows, you might lose in performance, but barely noticeable.

Follow implementations. I hope I have contributed in some way to the community.

Code: [Select]
function PtInRegion(RGN: HRGN; X, Y: Integer) : Boolean;
{$IFDEF MSWINDOWS}
begin
  Result := Windows.PtInRegion(RGN, X, Y);
{$ELSE}
var
  APoint   : TPoint;
  ARect : TRect;
begin
  GetRgnBox(RGN, @ARect);
  APoint.X := X;
  APoint.Y := Y;
  Result := LclIntf.PtInRect(ARect, APoint);
{$ENDIF}
end;

Code: [Select]
function RectInRegion(RGN: HRGN; ARect: TRect): Boolean;
{$IFDEF MSWINDOWS}
begin
  Result := Windows.RectInRegion(RGN, ARect);
{$ELSE}
var
  RectRgn,
  EmptyRgn: HRGN;
begin
  RectRgn := CreateRectRgnIndirect(ARect);
  try
    EmptyRgn := CreateEmptyRegion;
    try
      Result := CombineRgn(EmptyRgn, RectRgn, RGN, RGN_AND) <> NULLREGION;
    finally
      DeleteObject(EmptyRgn);
    end;
  finally
    DeleteObject(RectRgn);
  end;
{$ENDIF}
end;

Greetings.
« Last Edit: October 30, 2012, 08:51:27 pm by vcunha »

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: My contribution: RectInRegion and PtInRegion
« Reply #1 on: October 22, 2012, 05:05:22 pm »
Thanks a kit vcunha for contributing.

Could you post that as a patch to the bugtracker (see link above) so it doesn't get forgotten and developers can have a look at it and imlement it?
(A developer may happen to see this post but that is by no means a certainty. The bugtracker is closely monitored however)

Thanks a lot,
BigChimp
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4712
  • I like bugs.
Re: My contribution: RectInRegion and PtInRegion
« Reply #2 on: October 22, 2012, 06:53:45 pm »
Thanks from me, too, but your code needs some work.

First, there already is function PtInRegion although it is not implemented for GTK2 for some reason.

Second, LCL code should not have platform specific IFDEFs. Platform specific code must go to WidgetSet libraries. See how PtInRegion is done, it only calls a virtual WidgetSet.PtInRegion.

Code: [Select]
function PtInRegion(RGN: HRGN; X, Y: Integer) : Boolean;
begin
  Result := WidgetSet.PtInRegion(RGN,X,Y);
end;

which returns False by default :

Code: [Select]
function TWidgetSet.PtInRegion(RGN: HRGN; X, Y: Integer): Boolean;
begin
  Result := false;
end;

For example QT bindings implement it like this:

Code: [Select]
function TQtWidgetSet.PtInRegion(RGN: HRGN; X, Y: Integer): Boolean;
begin
  Result := False;
  if not IsValidGDIObject(RGN) then
    exit;
  Result := TQtRegion(RGN).containsPoint(X, Y);
end;

If you could add the missing GTK2 code and the whole RectInRegion function, it would be great.
You must either find a GTK2 API call or use more generic code, but calling LclIntf code from the widgetset code is banned (although this rule has been bended, too).
LclIntf.PtInRect is a short function and its could be copied if needed.

Searching the LCL sources can be educating. Most likely similar functionality is already implemented in some other function.
And yes, a patch in bug tracker is preferred.

Thanks and regards,
Juha
« Last Edit: October 22, 2012, 06:57:51 pm by JuhaManninen »
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

vcunha

  • New member
  • *
  • Posts: 9
Re: My contribution: RectInRegion and PtInRegion
« Reply #3 on: October 23, 2012, 01:44:30 pm »
Thanks a kit vcunha for contributing.

Could you post that as a patch to the bugtracker (see link above) so it doesn't get forgotten and developers can have a look at it and imlement it?
(A developer may happen to see this post but that is by no means a certainty. The bugtracker is closely monitored however)

Thanks a lot,
BigChimp

Thanks BigChimp.

I had not thought of That, my goal was to offer something for Those Who were having some difficulty by using Also these functions.

But you are right, I'll post in the bugtracker.

Thanks a lot,

VCunha

vcunha

  • New member
  • *
  • Posts: 9
Re: My contribution: RectInRegion and PtInRegion
« Reply #4 on: October 23, 2012, 02:06:24 pm »
Thanks from me, too...

Thanks JuhaManninen

..., but your code needs some work.

That's the idea. I am a linux user for many years. However, I am programmer in C / C + +, Delphi, but only on windows platform. So multiplatform applications used only Java.. I give up Delphi, but not Pascal.

I'm maturing as a developer seeking to multiplatform and contribute something to the community. And you're helping me, pointing me in other directions I can take as a path.

First, there already is function PtInRegion although it is not implemented for GTK2 for some reason.

I tried to implement it in another way, Why was not responding the same way on different platforms. Through research, I noticed that several people had the same difficulties when working on the issue of porting or converting your application. The way it appears, managed to get a common answer for both platforms.

Second, LCL code should not have platform specific IFDEFs. Platform specific code must go to WidgetSet libraries. See how PtInRegion is done, it only calls a virtual WidgetSet.PtInRegion.

The IFDEFs platform-specific, are there because I copied the code directly from my application here, after having had a satisfactory result. I confess that at first I was not thinking specifically in LCL.

But if we make the following code in both windows in gtk as:

Code: [Select]
function PtInRegion(RGN: HRGN; X, Y: Integer) : Boolean;
var
  APoint   : TPoint;
  ARect : TRect;
begin
  GetRgnBox(RGN, @ARect);
  APoint.X := X;
  APoint.Y := Y;
  Result := LclIntf.PtInRect(ARect, APoint);
end;

have the same result. You do not need directives compilation is only a mention for those who have a preference for using the API directly.

Code: [Select]
function TWidgetSet.PtInRegion(RGN: HRGN; X, Y: Integer): Boolean;
begin
  Result := false;
end;

I confess that gave me a fright. Because I did not understand, if the code was not yet implemented, or if your call proceeded otherwise, because it is where a virtual function call.

There are differences of Delphi and did not expect anything else. Particularly loved the way the code was organized in Lazarus / Free Pascal excited to have new experience.

You must either find a GTK2 API call or use more generic code,...

I have searched for it and I will bring to the community.

...but calling LclIntf code from the widgetset code is banned (although this rule has been bended, too).

If possible, talk more about it. I really did not know that.

LclIntf.PtInRect is a short function and its could be copied if needed.

You know, reuse of code, but you are right the function is very short.

Searching the LCL sources can be educating. Most likely similar functionality is already implemented in some other function.
And yes, a patch in bug tracker is preferred.

I'll do that.

Thank you for helping me on this journey.

VCunha.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4712
  • I like bugs.
Re: My contribution: RectInRegion and PtInRegion
« Reply #5 on: October 23, 2012, 10:53:54 pm »
The IFDEFs platform-specific, are there because I copied the code directly from my application here, after having had a satisfactory result. I confess that at first I was not thinking specifically in LCL.

Yes, I made wrong assumptions first. Anyway, your points about the code are valid.
IMO PtInRegion could indeed be a platform independent function in LCLIntf (actually in winapih.inc and winapi.inc).
However now it is a widgetset bindings function and the other widgetsets use an API call to implement it.
I implemented the missing GTK2 version by copying your code there. Maybe someone comes up with a GTK2 API call for the same task, we can replace it then.
See r39155 in Lazarus trunk.

I also copied your platform independent RectInRegion code there. Let's see if I get feedback from LCL experts...
See r39156, and please test it.

Quote
...but calling LclIntf code from the widgetset code is banned (although this rule has been bended, too).
If possible, talk more about it. I really did not know that.

Uhhh... I don't know the LCL nor the bindings code very well, I should be careful when teaching it to others.
PtInRect and other functions in LclIntf are used and can be used in bindings code. It is like a widgetset API call but is implemented as platform-neutral code.
My bad.
What I had in mind was the separation between LCL widgets like TButton and binding widgets like TGtk2WSButton. It should be a one way dependency. LCL can refer to the binding code but not the other way around.
TButton seems to follow this rule but some other widgets break it. For example TGtk2WSMenuItem refers back to TMenuItem.
The dependencies could be cleaned with some effort and callback funcs etc., but it is not a high priority.

My biggest contribution to LCL was the menu shortcut drawing for sequentially combined shortcuts, like Ctrl-Q, K, following each other. They did not show in menus. The drawing had to be done in widgetset binding code (Mac still doesn't have it).
Mostly I have worked with Lazarus IDE features and the Delphi converter.

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: My contribution: RectInRegion and PtInRegion
« Reply #6 on: October 23, 2012, 11:32:44 pm »
Note that the code posted does not actually do what the function name implies.

I think it should at least have a "TODO" comment to that effect,
to reduce user confusion.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4712
  • I like bugs.
Re: My contribution: RectInRegion and PtInRegion
« Reply #7 on: October 23, 2012, 11:42:34 pm »
Note that the code posted does not actually do what the function name implies.

Which function? Or both?

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: My contribution: RectInRegion and PtInRegion
« Reply #8 on: October 24, 2012, 05:01:23 am »
Look at the code yourself -- PtInRegion function should check if point belongs to a region,
while the code checks region's bounding box instead.

RectInRegion function should check if rectangle lies inside a region,
while the code checks if it intersects the region.

I am actually not sure what is better -- functions failing outright, so it is immediately obvious to the developer that they are not implemented, or functions which sometimes work, but sometimes fail, so the developer might be confused and forced into a non-pleasant debugging session.

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: My contribution: RectInRegion and PtInRegion
« Reply #9 on: October 24, 2012, 09:20:18 am »
RectInRegion function should check if rectangle lies inside a region,
while the code checks if it intersects the region.

Not sure if this modification would work or not, really need a demo to check.  But posting just in case it's any use.

Code: [Select]
...
try
    Result := CombineRgn(EmptyRgn, RectRgn, RGN, RGN_AND) <> NULLREGION;
      //our rect, might still not be fulling inside, but if
      //we XOR our result here with original Rect, it should be NULL
      //if it's fully inside..
      if Result then
        Result := CombineRgn(EmptyRgn, EmptyRgn, RectRgn, RGN_XOR) = NULLREGION;
    finally 
...

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4712
  • I like bugs.
Re: My contribution: RectInRegion and PtInRegion
« Reply #10 on: October 24, 2012, 11:00:41 am »
Look at the code yourself -- PtInRegion function should check if point belongs to a region,
while the code checks region's bounding box instead.

RectInRegion function should check if rectangle lies inside a region,
while the code checks if it intersects the region.

I am actually not sure what is better -- functions failing outright, so it is immediately obvious to the developer that they are not implemented, or functions which sometimes work, but sometimes fail, so the developer might be confused and forced into a non-pleasant debugging session.

Ok, I will do proper testing later today when I get home.
Does anyone know of a GTK2 API function for implementing PtInRegion?
I admit I am now in a region I don't know very well.

Juha
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: My contribution: RectInRegion and PtInRegion
« Reply #11 on: October 24, 2012, 11:13:19 am »
Windows regions are a number of not intersecting rectangles that cover the complete area of the region ptinregion should be as simple as a loop of ptInRect. I have no idea what the region on gtk2 is though.

Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: My contribution: RectInRegion and PtInRegion
« Reply #12 on: October 24, 2012, 11:24:27 am »
Quote
Does anyone know of a GTK2 API function for implementing PtInRegion?

This might not be super efficient, but I think you could just use the RectInRegion,.
From what I can remember from Windows API, a rectangle right & bottom are exclusive coordinates, eg.  Rect = x1 >= * & x2 < *  , y1 >= * & y2 < *, so you would just add 1, or else you would have an empty rect.  Basically making a 1 by 1 pixel rectangle.

So maybe this ->
Code: [Select]
function PtInRegion(RGN: HRGN; X, Y: Integer) : Boolean;
{$IFDEF MSWINDOWS}
begin
  Result := Windows.PtInRegion(RGN, X, Y);
{$ELSE}
begin
  result := RectInRegion(RGN,rect(X,Y,X+1,Y+1));
{$ENDIF}
end;

Again not tested.

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: My contribution: RectInRegion and PtInRegion
« Reply #13 on: October 24, 2012, 12:01:53 pm »
Ok, knocked up a quick Demo.
The modifications seem to work fine.
I've taken out the #IFDEFS so that I could still check in Windows.

vcunha

  • New member
  • *
  • Posts: 9
Re: My contribution: RectInRegion and PtInRegion
« Reply #14 on: October 24, 2012, 01:03:59 pm »
Look at the code yourself -- PtInRegion function should check if point belongs to a region,
while the code checks region's bounding box instead.

RectInRegion function should check if rectangle lies inside a region,
while the code checks if it intersects the region.

I am actually not sure what is better -- functions failing outright, so it is immediately obvious to the developer that they are not implemented, or functions which sometimes work, but sometimes fail, so the developer might be confused and forced into a non-pleasant debugging session.

Ok, I will do proper testing later today when I get home.
Does anyone know of a GTK2 API function for implementing PtInRegion?
I admit I am now in a region I don't know very well.

Juha

When you told me about seeking similar roles in GTK2, I started to do this.

The functions that we are talking in this post: http://msdn.microsoft.com/en-us/library/windows/desktop/dd162915%28v=vs.85%29.aspx

I found a function in GTK2 (http://developer.gnome.org/gdk/stable/gdk-Points-Rectangles-and-Regions.html#gdk-Points-Rectangles-and-Regions.synopsis) that can do to my understanding of the role.

definitions
-> Gdk_region_rect_in (): Tests whether a rectangle is Within the region.
-> RectInRegion: Determines whether any part of the specified rectangle is Within the boundaries of the region.


Now for PtInRegion
-> Gdk_region_point_in (): Finds Out if the point is in the region.
-> PtInRegion: Determines whether the specified point is inside the specified region.

The question is which functions GDK expect a region of type GdkRegion and Win API receives a region of type HRGN.

In Win32, the GTK2 provides the following function in C language to make this conversion: (http://nesl.ee.ucla.edu/fw/han/old_machine_backup/overo-oe/tmp/work/i686-linux/gtk+-native-2.20.0-r8.0/gtk+-2.20.0/gdk/win32/gdkgc-win32.c).

Quote
HRGN _gdk_win32_gdkregion_to_hrgn (GdkRegion *region, gint x_origin, gint y_origin)
{
  HRGN hrgn;
  RGNDATA *rgndata;
  RECT *rect;
  GdkRegionBox *boxes = region->rects;
  guint nbytes = sizeof (RGNDATAHEADER) + (sizeof (RECT) * region->numRects);
  int i;

  rgndata = g_malloc (nbytes);
  rgndata->rdh.dwSize = sizeof (RGNDATAHEADER);
  rgndata->rdh.iType = RDH_RECTANGLES;
  rgndata->rdh.nCount = rgndata->rdh.nRgnSize = 0;
  SetRect (&rgndata->rdh.rcBound, G_MAXSHORT, G_MAXSHORT, G_MINSHORT, G_MINSHORT);

  for (i = 0; i < region->numRects; i++)
 {
      rect = ((RECT *) rgndata->Buffer) + rgndata->rdh.nCount++;

      rect->left       = CLAMP (boxes.x1 + x_origin, G_MINSHORT, G_MAXSHORT);
      rect->right     = CLAMP (boxes.x2 + x_origin, G_MINSHORT, G_MAXSHORT);
      rect->top       = CLAMP (boxes.y1 + y_origin, G_MINSHORT, G_MAXSHORT);
      rect->bottom = CLAMP (boxes.y2 + y_origin, G_MINSHORT, G_MAXSHORT);

      if (rect->left < rgndata->rdh.rcBound.left)
   rgndata->rdh.rcBound.left = rect->left;
      if (rect->right > rgndata->rdh.rcBound.right)
   rgndata->rdh.rcBound.right = rect->right;
      if (rect->top < rgndata->rdh.rcBound.top)
   rgndata->rdh.rcBound.top = rect->top;
      if (rect->bottom > rgndata->rdh.rcBound.bottom)
   rgndata->rdh.rcBound.bottom = rect->bottom;
    }
  if ((hrgn = ExtCreateRegion (NULL, nbytes, rgndata)) == NULL)
    WIN32_API_FAILED ("ExtCreateRegion");

  g_free (rgndata);

  return (hrgn);
}

I'm trying to convert this function either: gdkregion to HRGN <-> HRGN to gdkregion.

VCunha

 

TinyPortal © 2005-2018