question

WilliamKandler-2357 avatar image
0 Votes"
WilliamKandler-2357 asked WilliamKandler-2353 answered

ExtFloodFill with custom scaling

I'm filling irregular shapes with solid colors and my desired coord system is centered origin. I can make this work by setting scaleheight/width to max dimension of picturebox and computing offset=maxdimension/2. But this means every point must be specified as x+offest/y+offset; rather messy.

I can set up for 0/0 centered in pic via

dest.ScaleMode = vbInches 'here's the key; picture's scale mode is inches

dest.ScaleWidth = MaxDimension
dest.ScaleLeft = -dest.ScaleWidth / 2

dest.ScaleHeight = MaxDimension
dest.ScaleTop = -dest.ScaleHeight / 2

But extfloodfill cannot locate the proper fill point(fills from wrong point).

windows-api
· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.


Why is scaling needed?

1 Vote 1 ·

the world of circles is a natural for system where the origin is at the center. the resulting code is simpler and smaller.

0 Votes 0 ·

It's prefer to use SetMapMode SetWindowExtEx SetViewportExtEx SetViewportOrgEx to Custom Coordinate Spaces and Transformations.
Hope The Invalidating the Client Area Sample helpful.


0 Votes 0 ·
XiaopoYang-MSFT avatar image
0 Votes"
XiaopoYang-MSFT answered XiaopoYang-MSFT edited

It's prefer to use WinAPI to set the origin at the center.
Such As:

 void SetIsotropic(HDC hdc, int cxClient,int xyClient)
 {
  SetMapMode(hdc,MM_ISOTROPIC);
  SetWindowExtEx(hdc,1000,1000,NULL);
  SetViewportExtEx(hdc, cxClient / 2,-xyClient / 2,NULL);
  SetViewportOrgEx(hdc, cxClient / 2, xyClient / 2, NULL);
 }

The previous code Need more care to use.
And Then perhaps ExtFloodFill will have normal behavior.


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

WilliamKandler-2357 avatar image
0 Votes"
WilliamKandler-2357 answered

my test is a 3000x3000 twips picturebox. Here is what I've tried for a working space of +-2.5 with the origin at 0,0 in the center of the box:

SetMapMode Pic2.hdc, MM_ISOTROPIC
SetWindowExtEx Pic2.hdc, 3000, 3000, 3000
SetViewportExtEx Pic2.hdc, 2.5, -2.5, 2.5
SetViewportOrgEx Pic2.hdc, -2.5, -2.5, PointOf(0, 0)

Pic.ScaleX(X/Y, Pic.ScaleMode, vbPixels) is then used to get the starting point for floodfill.

For -2.5/-2.5 it ought to give 0/0 and for 2.5/2.5 it ought to give 200/200, but actually results in -97/-97 and 97/97.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

XiaopoYang-MSFT avatar image
0 Votes"
XiaopoYang-MSFT answered XiaopoYang-MSFT edited

After Testing, I found There is no problem on ExtFloodFill. As far as the following code concerned, The ExtFloodFill interpreted coordinate correctly.

 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     switch (message)
     {
     case WM_PAINT:
         {
             PAINTSTRUCT ps;
             HDC hdc = BeginPaint(hWnd, &ps);
             // TODO: Add any drawing code that uses hdc here...
  RECT rc{};
    
  GetClientRect(hWnd, &rc);
  SetMapMode(hdc, MM_ANISOTROPIC);
  SetWindowExtEx(hdc, 100, 100, NULL);
  SetViewportExtEx(hdc, rc.right / 2, -rc.bottom / 2, NULL);
  SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);
    
  POINT aptTriangle[] = { 0,50, 100,0,  0,-99, -25,0, 0,50 };
  Polyline(hdc, aptTriangle, ARRAYSIZE(aptTriangle));
    
  HBRUSH hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(0, 0, 255));
  // ' Select it for use in Form1, noting the previous brush.
  HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
  BOOL retval = FALSE;
  // ' Flood fill the area outside the ellipse (use a green boundary)
  retval = ExtFloodFill(hdc, 10, 10, Color::Black, FLOODFILLBORDER);//Picture 1
  //retval = ExtFloodFill(hdc, 0, 51, Color::Black, FLOODFILLBORDER);//Picture 2
    
  // ' Select the previously selected pen and brush.
  SelectObject(hdc, hOldBrush);
  // ' Delete the pen and brush to free up resources.
  retval = DeleteObject(hBrush);
             EndPaint(hWnd, &ps);
         }
         break;
     case WM_DESTROY:
         PostQuitMessage(0);
         break;
     default:
         return DefWindowProc(hWnd, message, wParam, lParam);
     }
     return 0;
 }


114474-image.png
114475-image.png



image.png (38.6 KiB)
image.png (54.0 KiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

WilliamKandler-2357 avatar image
0 Votes"
WilliamKandler-2357 answered XiaopoYang-MSFT commented

I write my question in vb; you answer in incomplete C.

You specify:
SetWindowExtEx(hdc, 100, 100, NULL);
SetViewportExtEx(hdc, rc.right / 2, -rc.bottom / 2, NULL);
SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);

my obj is a 3000x3000 twits picturebox and my user coord. system is -2.5/-2.5 -> 2.5/2.5; so I should say?:

SetWindowExtEx hdc, 3000, 3000, NULL
SetViewportExtEx hdc, 2.5, -2.5, NULL
SetViewportOrgEx hdc, 2.5, 2.5, NULL

You then dispay using:
POINT aptTriangle[] = { 0,50, 100,0, 0,-99, -25,0, 0,50 };
Polyline(hdc, aptTriangle, ARRAYSIZE(aptTriangle));

I can't draw my circle using inches at 0/0 with a radius of 2.5 with this code. that (as I originally stated) requires:

dest.ScaleMode = vbInches

dest.ScaleWidth = MaxDimension
dest.ScaleLeft = -dest.ScaleWidth / 2

dest.ScaleHeight = MaxDimension
dest.ScaleTop = -dest.ScaleHeight / 2


Then you fill via:

retval = ExtFloodFill(hdc, 10, 10, Color::Black, FLOODFILLBORDER)

My equivalent for center fill is:

rtn = ExtFloodFill(dest.hDC, 0,0, 0, FLOODFILLBORDER)

This fills, but not from the CENTER

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

You just need Coordinate Spaces Where Each logical unit is mapped to some inch you need. According to Application-Defined Mapping Modes, The following example shows how to set logical units to 1/64 inch:

 SetMapMode(hDC, MM_ISOTROPIC); 
 SetWindowExtEx(hDC, 64, 64, NULL); 
 SetViewportExtEx(hDC, GetDeviceCaps(hDC, LOGPIXELSX), 
                       GetDeviceCaps(hDC, LOGPIXELSY), NULL); 


0 Votes 0 ·
WilliamKandler-2357 avatar image
0 Votes"
WilliamKandler-2357 answered

Some progress has been made! Using this to setup:

Pic2.ScaleMode = vbInches
SetMapMode Pic2.hDC, MM_ISOTROPIC
SetWindowExtEx Pic2.hDC, 1, 1, Null
SetViewportExtEx Pic2.hDC, GetDeviceCaps(Pic2.hDC, LOGPIXELSX), GetDeviceCaps(Pic2.hDC, LOGPIXELSY), Null

1,1 is to get inches. your code suggested 64,64 to get 1/64; doesn't make sense. I tried .5,.5 and 2,2, no change. my object is (-2..5,-2.5)-(2.5,2.5)

The object is drawn and extfloodfill works BUT is drawn at 4x size and the origin is at 0/0 (upperleft corner)

I added 1 line:

Pic2.ScaleMode = vbInches
SetMapMode Pic2.hDC, MM_ISOTROPIC
SetWindowExtEx Pic2.hDC, 1, 1, Null
SetViewportExtEx Pic2.hDC, GetDeviceCaps(Pic2.hDC, LOGPIXELSX), GetDeviceCaps(Pic2.hDC, LOGPIXELSY), Null
Pic2.Scale (-2.5, -2.5)-(2.5, 2.5)

Object size and positioning now proper but fill stops working

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

XiaopoYang-MSFT avatar image
0 Votes"
XiaopoYang-MSFT answered

The object is not drawn at 4x size

You need SetStretchBltMode and StretchBlt.

the origin is not at 0/0.

You need SetViewportOrgEx.
Such as

 case WM_PAINT:
         {
             PAINTSTRUCT ps;
             HDC hdc = BeginPaint(hWnd, &ps);
             // TODO: Add any drawing code that uses hdc here...
             RECT rc{};
             GetClientRect(hWnd, &rc);
             SetMapMode(hdc, MM_ANISOTROPIC);
             SetWindowExtEx(hdc, 1, 1, NULL);
             int pixel_x = GetDeviceCaps(hdc, LOGPIXELSX);
             int pixel_y = GetDeviceCaps(hdc, LOGPIXELSY);
             SetViewportExtEx(hdc, pixel_x, -pixel_y, NULL);
             SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);
                
             //POINT aptTriangle[] = { 0,50, 100,0,  0,-99, -25,0, 0,50 };
             POINT aptTriangle[] = { 0,1, 1,0,  0,-1, -1,0, 0,1 };
             Polyline(hdc, aptTriangle, ARRAYSIZE(aptTriangle));
    
             HBRUSH hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(0, 0, 255));
             //    ' Select it for use in Form1, noting the previous brush.
             HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
             BOOL retval = FALSE;
             //    ' Flood fill the area outside the ellipse (use a green boundary)
             retval = ExtFloodFill(hdc, 0, 2, Color::Black, FLOODFILLBORDER);//Picture 1
    
             //    ' Select the previously selected pen and brush.
             SelectObject(hdc, hOldBrush);
             //    ' Delete the pen and brush to free up resources.
             retval = DeleteObject(hBrush);
             EndPaint(hWnd, &ps);
         }
         break;

115186-image.png


Note: ExtFloodFill has a int x and y. So ExtFloodFill(hdc, 0, 1.2, Color::Black, FLOODFILLBORDER); will not work.


image.png (13.8 KiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

WilliamKandler-2357 avatar image
0 Votes"
WilliamKandler-2357 answered XiaopoYang-MSFT edited

Yes, floodfill shows that problem, but it is working with fractional starting points.
115409-untitled.png
Below is almost all my code. The disputed part is right up front in the Form_Load proc. Which paints a ring on 2 3000x3000 twip pictureboxes. Pic1, the left, works for positioning(centered), origin location(center), and fills. Pic2 doesn’t work quite right. The ring displays at 2x the size (my earlier comment of 4x was wrong) , its origin is at the upperleft corner, and fill works. I tried both -100, -100 and 100, 100 for the Viewportorg statement. It seemed to have no effect.
.

Dim SideNum As Integer 'counts from 1 to sides as we go around ring

Private Sub Form_Load()
Me.Show

Pic1.ScaleMode = vbInches
size = 5
Pic1.ScaleWidth = size
Pic1.ScaleHeight = size
DrawRing Pic1, PointOf(2.5, 2.5), 12, 2, 1.5
fill Pic1, PointOf(2.5, 2.5), RGB(255, 255, 0)
fill Pic1, PointOf(-2.3, -2.3), RGB(0, 0, 255)

Pic2.ScaleMode = vbInches
SetMapMode Pic2.hDC, MM_ISOTROPIC
SetWindowExtEx Pic2.hDC, 1, 1, Null
SetViewportExtEx Pic2.hDC, GetDeviceCaps(Pic2.hDC, LOGPIXELSX), -GetDeviceCaps(Pic2.hDC,LOGPIXELSY), Null
SetViewportOrgEx hDC, -100, -100, Null
DrawRing Pic2, PointOf(0, 0), 12, 2, 1.5
fill Pic2, PointOf(0, 0), RGB(255, 255, 0)
End Sub

Private Sub DrawRing(dest As PictureBox, Origin As ObjPt, Sides As Integer, Radius As Single, Wall As Single)
Dim SO1 As ObjPt 'gap adjusted segment outer previous
Dim SO2 As ObjPt 'gap adjusted segment outer current
Dim SI1 As ObjPt 'gap adjusted segment inner previous
Dim SI2 As ObjPt 'gap adjusted segment inner current
Dim Ctr As ObjPt
Dim Angle As Single 'current Angle
Dim InitialAngle As Single 'first angle in loop to display ring
Dim Miter As Single
InitialAngle = 0
FinalAngle = 360
SideNum = 0
Miter = 180 / Max(Sides, 1) 'Get apparent Cutting Angle
While Angle <= FinalAngle 'loop around the circle to create segments
SideNum = SideNum + 1
PolarToCartesian Origin, Radius, Angle - 2 Miter, SO1 'get X,Y of prior outer corner
PolarToCartesian Origin, Radius - Wall, Angle - 2
Miter, SI1 'get X,Y of prior inner corner
PolarToCartesian Origin, Radius, Angle, SO2 'get X,Y of current outer corner
PolarToCartesian Origin, Radius - Wall, Angle, SI2 'get X,Y of current inner corner
DrawLineObj dest, "lft", SI1, SO1, 0 'first inter-segment boundary
DrawLineObj dest, "out", SO1, SO2, 0 'outer edge
DrawLineObj dest, "inr", SI1, SI2, 0 'inner edge
DrawLineObj dest, "rgt", SI2, SO2, 0 'final inter-segment boundary
CtrPoint SI1, SO1, SI2, SO2, Ctr
fill dest, Ctr, RGB(255 - 10 SideNum, 0, 0)
Angle = Angle + 2
Miter 'advance to next segment in the circle/riing
Wend
End Sub

Private Sub fill(dest As PictureBox, Ctr As ObjPt, Color As Long)
Dim p As ObjPt
Dim rtn As Long
dest.FillStyle = cSolid
dest.fillcolor = Color 'set fill style and color
ScaleToPixelsObj dest, Ctr, p
rtn = ExtFloodFill(dest.hDC, p.X, p.y, 0, FLOODFILLBORDER) 'do the fill
End Sub

Private Sub ScaleToPixelsObj(Pic As PictureBox, FromObj As ObjPt, ToObj As ObjPt)
With FromObj
ToObj.X = Pic.ScaleX(.X, Pic.ScaleMode, vbPixels)
ToObj.y = Pic.ScaleY(.y, Pic.ScaleMode, vbPixels)
End With
End Sub

Private Function PointOf(X As Single, y As Single) As ObjPt
Dim ans As ObjPt
ans.X = X
ans.y = y
PointOf = ans
End Function



untitled.png (16.2 KiB)
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

The ring displays at 2x the size

Don‘t use Pic2.ScaleMode = vbInches if you use SetMapMode, SetWindowExtEx, SetViewportExtEx and SetViewportOrgEx.
After using SetMapMode, SetWindowExtEx, SetViewportExtEx and SetViewportOrgEx, hDC is in Inch Coordinate System. With Pic2.ScaleMode = vbInches, The Pic2 Also is in Inch ScaleMode. So The ring displays at 2x the size.

its origin is at the upperleft corner

The line SetViewportOrgEx hDC, -100, -100, Null should be the Half Of Client Area Width pixels and Half Of Client Area Height pixels.

0 Votes 0 ·

I found some VB code on Internet. Hope helpful.

Set ScaleHeight to -ScaleHeight to make the Y coordinates increase from bottom to top. Then set ScaleLeft and ScaleTop to position the origin where you want it. This example places the origin 1440 twips in and upp from the lower left corner. Change the values of X0 and Y0 to put the origin someplace else.

 Private Sub picCanvas_Resize()
 Const X0 = 1440
 Const Y0 = 1440
    
     picCanvas.Cls
    
    ' Set scale mode to twips.
     picCanvas.ScaleMode = vbTwips
    
     ' Set ScaleHeight to make Y increase upwards.
     picCanvas.ScaleHeight = -picCanvas.ScaleHeight
    
     ' Make the origin 1 inch (1440 twips)
     ' from the lower left corner.
     picCanvas.ScaleLeft = -X0
     picCanvas.ScaleTop = -picCanvas.ScaleHeight - Y0
    
     ' Draw a box at the origin.
     picCanvas.Line (0, 0)-Step(360, 360), , BF
    
     ' Draw a diamond centered at the origin.,
     picCanvas.Line (-720, 0)-Step(720, 720)
     picCanvas.Line -Step(720, -720)
     picCanvas.Line -Step(-720, -720)
     picCanvas.Line -Step(-720, 720)
 End Sub
0 Votes 0 ·
WilliamKandler-2353 avatar image
1 Vote"
WilliamKandler-2353 answered

I have located and fixed my problem. The scalex and scaley functions do not function properly when changing coordinate systems. They only handle simple scaling., Thus, when floodfill and scale(-2.5,-2.5)-(2.5,2.5) come together and you use

Sub ScaleToPixels(Pic As PictureBox, inp As ObjPt, outv As ObjPt)
With inp
outv.x = Pic.ScaleX(.x, Pic.ScaleMode, vbPixels)
outv.y = Pic.ScaleY(-.y, Pic.ScaleMode, vbPixels)
End With
End Sub

to give floodfill its starting point in pixels, it gives you the scale(-2.5,-2.5)-(2.5,2.5) coordinate system you specified instead of scale(0,0)-(maxx,maxy) that floodfill requires. So you have to something like this.

Sub ScaleToPixels(Pic As PictureBox, inp As ObjPt, outv As ObjPt)
With inp
outv.x = 238 + Pic.ScaleX(.x, Pic.ScaleMode, vbPixels)
outv.y = 238 - Pic.ScaleY(-.y, Pic.ScaleMode, vbPixels)
End With
End Sub

238 is the mid-point pixel for my case.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.