ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 TIECanvas: PolylineTo & PolyPolyline
 New Topic  Reply to Topic
Author Previous Topic Topic Next Topic  

Fellafoo

USA
52 Posts

Posted - Mar 12 2022 :  17:56:50  Show Profile  Reply
Hello,

Does TIECanvas have an equivalent for Windows.PolylineTo and Windows.PolyPolyline?

If not, does someone have an example of converting these calls to Polyline or LineTo?

Thank You,

MFM

xequte

38610 Posts

Posted - Mar 12 2022 :  18:09:37  Show Profile  Reply
Hi

Sorry, those methods are not supported at this time.

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Mar 15 2022 :  11:57:02  Show Profile  Reply
FWIW, here's the procedure I came up with to pass my existing variables to the IECanvas Polygon procedure.

procedure PolyPolygonForIE(aDC: HDC; aPtArr: PPoint; aPtCount: PInteger; aPlyCount: Integer);
{ Draw a series of closed polygons using an array of Points, Integers (no. of pts. per polygon),
  and an Integer indicating the total number of polygons to draw. }
var
  i, j: Integer;
  iePtsArr: array of TPoint;

  TmpCnv: TCanvas;
  ieCnv: TIECanvas;

begin
  if (aPlyCount = 0) then Exit { Nothing to do }
  else if (aPtCount^ < 3) then Exit; { Not a valid polygon }

  { Create temporary IECanvas from Device Context }
  TmpCnv := TCanvas.Create;
  TmpCnv.Handle := aDC;
  ieCnv := TIECanvas.Create(TmpCnv);
  TmpCnv.Free;

  { Set unique property of ieCanvas }
  ieCnv.Brush.Transparency := 64;

  for i := 0 to Pred(aPlyCount) do begin
    SetLength(iePtsArr, aPtCount^);
    for j := 0 to Pred(aPtCount^) do begin
      iePtsArr[j] := aPtArr^;
      Inc(aPtArr);
    end;
    Inc(aPtCount);
    ieCnv.Polygon(iePtsArr); { Draw polygons from array one at a time }
  end;
  ieCnv.Free;
end; { PolyPolygonForIE }
Go to Top of Page

xequte

38610 Posts

Posted - Mar 15 2022 :  19:55:58  Show Profile  Reply
Nice work

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Mar 30 2022 :  11:44:45  Show Profile  Reply
Thanks Nigel. However, I've found a caveat I'm not sure how to solve yet. When I pass the points array to Windows.PolyPolygon, I believe the order of points (i.e., clockwise versus counter-clockwise) determines whether the polygon is a void.

Since I'm drawing each polygon one at a time with TIECanvas.Polygon, the void areas are drawn in the same color as the filled areas.

How are voids normally handled with ImageEN?
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 01 2022 :  12:03:06  Show Profile  Reply
Update (Polygon Voids):

The Polygon(s) I pass to PolyPolygonForIE are always in the order of 'Parent' first followed by 'Children' (i.e., Voids). So, I think I can modify the procedure to use an intermediate bitmap and mask to 'knock out' the voids.

If I modify the routine where the polygons are drawn, I can set a condition to draw the voids in a different color. For example...


  for i := 0 to Pred(aPlyCount) do begin
    SetLength(iePtsArr, aPtCount^);
    for j := 0 to Pred(aPtCount^) do begin
      iePtsArr[j] := aPtArr^;
      Inc(aPtArr);
    end;
    Inc(aPtCount);
    if (i = 0) then begin
      ieCnv.Polygon(iePtsArr); { Draw polygons from array one at a time }
    end
    else begin
      ieCnv.Brush.Transparency := 255; { Solid }
      ieCnv.Brush.Color := clBlack { Background Color }
    end;
  end;


This has the effect of knocking out the voids but since the voids are not transparent, everything behind the void gets knocked out.

I'd like to draw the voids onto an adjustment mask, then apply that to the intermediate bitmap before alphablending it with the canvas. Unfortunately, everything I've tried so far fails to create 'transparent' holes.

First, I create a TIEBitmap to match the canvas.

  ieBmp := TIEBitmap.Create(DrawFormWidth, DrawFormHeight, ie32RGB);

Then I create a secondary canvas I can draw on transparently in the bitmap.

ieCnv2 := TIECanvas.Create(ieBmp.Canvas);
ieCnv2.Brush.Create(ieBrush2);
ieCnv2.Brush.Color := BrClr;
ieCnv2.Brush.Transparency := BrOp; { 0 - 255 }

Then I create a mask I can draw the voids on.

  ieMask := TIEMask.Create;
  ieBmp.AdjustmentsMask.DrawPolygon(255, @iePtsArr, 1);

Then I assign the mask to the bitmap.

  ieBmp.AdjustmentsMask.Assign(ieMask);

Now I want to knock out the masked areas before alphablending it on to the canvas.

Any advice would be appreciated.

Thank You,

MFM
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 03 2022 :  13:03:14  Show Profile  Reply
FWIW, I've been experimenting with the TImageEnView component and came up with this sequence to create and 'knockout' a polygon.


procedure TForm1.KnockOutPolygon();
{ Create a polygon with a knockout }
var
  ieBmp: TIEBitmap;
begin
  { Create 'Transparent' Layer }
  ieBmp := TIEBitmap.Create(512, 512, clWhite, 0);
  ieView.LayersAddEx(ielkImage, 0, 0, ieBmp.Width, ieBmp.Height, ieBmp, True, False, False);

  { New Layer }
  ieView.LayersAdd(ielkPolyline);
  { Set Fill and Boundary Properties }
  ieView.CurrentLayer.FillColor := clYellow;
  ieView.CurrentLayer.FillOpacity := 128;
  //ieView.CurrentLayer.BorderColor := clRed;
  ieView.CurrentLayer.BorderWidth := 0; { > 0 will change width/height of layer! }
  { Add Polygon using image coordinates }
  TIEPolylineLayer(ieView.CurrentLayer).SetPoints( [ Point(0, 0), Point(256, 256), Point(0, 512) ], True, iepbBitmap );

  ieView.Update();

  { Merge with 'Transparent' Layer }
  ieView.LayersMerge(ieView.LayersCurrent - 1, ieView.LayersCurrent, True);

  { Create 'Transparent' Layer }
  ieBmp := TIEBitmap.Create(512, 512, clWhite, 0);
  //ieView.LayersAddEx(ielkImage, -1, -1, ieBmp.Width, ieBmp.Height, ieBmp, True, False, False);
  ieView.LayersInsertEx(ieView.LayersCurrent + 1, ielkImage, 0{-1}, 0{-1}, ieBmp.Width, ieBmp.Height, ieBmp, True, False, False);

  { Create Knockout Polygon (after current layer) }
  ieView.LayersInsert(ieView.LayersCurrent + 1, ielkPolyline);
  ieView.CurrentLayer.FillColor := clBlack;
  ieView.CurrentLayer.BorderWidth := 0;
  TIEPolylineLayer(ieView.CurrentLayer).SetPoints( [ Point(24, 58), Point(222, 256), Point(24, 454) ], True, iepbBitmap );

  ieView.Update();

  { Merge with 'Transparent' Layer }
  ieView.LayersMerge(ieView.LayersCurrent - 1, ieView.LayersCurrent, True);

  SetCurrentLayerAsMask(); { See Layer Masking Demo }

  ieView.Update();

  { Merge with Polygon Layer }
  ieView.LayersMerge(ieView.LayersCurrent - 1, ieView.LayersCurrent, True);

  ieView.IO.SaveToFile('C:\Users\mark\Desktop\ieView.png');
end;
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 03 2022 :  18:12:12  Show Profile  Reply
More Knockout Experimentation:

Since the Windows.PolyPolygon routine handles knockouts, I thought I might be able to do a work-around by modifying the output from this procedure. Here's what I came up with. Unfortunately, it's not fast enough and the edges of the polygons are 'choppy', but it does work.


procedure PolyPolygonForIE_New(aDC: HDC; aPtArr: PPoint; aPtCount: PInteger; aPlyCount: Integer; BrClr: TColor = 0; BrOp: Byte = 255; BmpBrush: Str255 = '');
var
  TmpCnv: TCanvas;
  ieBmp: TIEBitmap;
  DrawFormWidth, DrawFormHeight: Integer;

begin
  { Create temporary from Device Context }
  TmpCnv := TCanvas.Create;
  TmpCnv.Handle := aDC;
  DrawFormWidth := TmpCnv.ClipRect.Right;
  DrawFormHeight := TmpCnv.ClipRect.Top;

  { Create ie bitmap, same size as Device Context }
  ieBmp := TIEBitmap.Create(DrawFormWidth, DrawFormHeight, ie32RGB);
  ieBmp.Canvas.Brush.Color := BrClr;

  { Draw on ie bitmap canvas using Windos.PolyPolygon }
  PolyPolygon(ieBmp.Canvas.Handle, aPtArr^, aPtCount^, aPlyCount);

  { Set opacity of draw form color to 0 }
  ieBmp.SetTransparentColors(DrawFormColor, DrawFormColor, 0);
  { Set opacity of filled area to user-defined value }
  ieBmp.SetTransparentColors(BrClr, BrClr, BrOp);
  { Merge transparent fill with knockout into background }
  ieBmp.RenderToCanvasWithAlpha(TmpCnv, 0, 0, DrawFormWidth, DrawFormHeight, 0, 0, DrawFormWidth, DrawFormHeight, 255, rfFastLinear, ielNormal, 1);

  ieBmp.Free;
  TmpCnv.Free;
end;
Go to Top of Page

xequte

38610 Posts

Posted - Apr 03 2022 :  18:32:58  Show Profile  Reply
Yes, that works nicely.

Image


+ Mask


=



Nigel
Xequte Software
www.imageen.com


Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 04 2022 :  11:49:50  Show Profile  Reply
I thought this method might be a little quicker but it's not noticeable.


procedure PolyPolygonForIE_New(aDC: HDC; aPtArr: PPoint; aPtCount: PInteger; aPlyCount: Integer; BrClr: TColor = 0; BrOp: Byte = 255; BmpBrush: Str255 = '');

var
  TmpCnv: TCanvas;
  ieBmp: TIEBitmap;
  DrawFormWidth, DrawFormHeight: Integer;
  x, y: Integer;
  ieRGB_Df: TRGB;

begin
  { Create temporary IECanvas from Device Context }
  TmpCnv := TCanvas.Create;
  TmpCnv.Handle := aDC;
  DrawFormWidth := TmpCnv.ClipRect.Right;
  DrawFormHeight := TmpCnv.ClipRect.Top;

  { Create ie bitmap, same size as Device Context }
  ieBmp := TIEBitmap.Create(DrawFormWidth, DrawFormHeight, ie32RGB);
  ieBmp.Canvas.Brush.Color := BrClr;

  { Draw on ie bitmap canvas using Windows.PolyPolygon }
  PolyPolygon(ieBmp.Canvas.Handle, aPtArr^, aPtCount^, aPlyCount);

  { Set opacity of DrawForm to 0 and set opacity of filled area to user-defined value }
  ieRGB_Df := TColor2TRGB(DrawFormColor);
  for y := 0 to Pred(ieBmp.Height) do begin
    for x := 0 to Pred(ieBmp.Width) do begin
      if (ieBmp.Pixels[x, y].r = ieRGB_Df.r) and
         (ieBmp.Pixels[x, y].g = ieRGB_Df.g) and
         (ieBmp.Pixels[x, y].b = ieRGB_Df.b) then
        ieBmp.Alpha[x, y] := 0
      else
        ieBmp.Alpha[x, y] := BrOp;
    end;
  end;

  ieBmp.DrawToCanvasWithAlpha(TmpCnv, 0, 0);

  ieBmp.Free;
  TmpCnv.Free;
end;
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 05 2022 :  10:13:07  Show Profile  Reply
I had to take the long way around the barn, but I think I've finally figured out how I can draw polygons with voids using the Windows.PolyPolygon procedure. Any ideas on how I can improve the performance of this routine would be appreciated.


procedure PolyPolygonForIE_Voids(aDC: HDC; aPtArr: PPoint; aPtCount: PInteger; aPlyCount: Integer; BrClr: TColor = 0; BrOp: Byte = 255; BmpBrush: Str255 = '');
{ Draw transparent polygons WITH voids using Windows PolyPolygon procedure & TIEBitmap AlphaChannel }
var
  TmpCnv: TCanvas;
  aBmp: TBitmap;
  ieBmp: TIEBitmap;

  DrawFormWidth, DrawFormHeight: Integer;

begin
  if (aPlyCount = 0) then Exit { Nothing to do }
  else if (aPtCount^ < 3) then Exit; { Not a valid polygon }

  { Create temporary canvas from Device Context }
  TmpCnv := TCanvas.Create;
  TmpCnv.Handle := aDC;
  DrawFormWidth := TmpCnv.ClipRect.Right;
  DrawFormHeight := TmpCnv.ClipRect.Top;

  { Create Bitmap for Alpha Channel (Mask) }
  aBmp := TBitmap.Create;
  aBmp.Canvas.Brush.Style := bsSolid;
  aBmp.Canvas.Brush.Color := clBlack; { Set color here before width / height }
  aBmp.Width := DrawFormWidth;
  aBmp.Height := DrawFormHeight;

  { Set brush color to grey value from user-defined opacity value }
  aBmp.Canvas.Brush.Color := RGB(BrOp, BrOp, BrOp);
  { Draw polygon with voids using Windows.PolyPolygon (Alpha Image) }
  PolyPolygon(aBmp.Canvas.Handle, aPtArr^, aPtCount^, aPlyCount);

  { Create 'ie' bitmap, same size as Device Context }
  ieBmp := TIEBitmap.Create(DrawFormWidth, DrawFormHeight, ie32RGB); { This method seems fastest }
  { Set compositing mode }
  ieBmp.IECanvas.SetCompositingMode(ieCompositingModeSourceOver, ieCompositingQualityDefault);

  { Assign Mask to ieBmp's Alpha Channel }
  ieBmp.AlphaChannel.Assign(aBmp);
  ieBmp.SyncAlphaChannel(True);

  { Set brush color to user-defined value }
  ieBmp.Canvas.Brush.Color := BrClr;
  { Draw polygon with voids using Windows.PolyPolygon (Source Image) }
  PolyPolygon(ieBmp.Canvas.Handle, aPtArr^, aPtCount^, aPlyCount);

  { Merge result onto our DrawForm canvas }
  ieBmp.DrawToCanvasWithAlpha(TmpCnv, 0, 0);

  aBmp.Free;
  ieBmp.Free;
  TmpCnv.Free;
end; { PolyPolygonForIE_Voids }
Go to Top of Page

xequte

38610 Posts

Posted - Apr 05 2022 :  17:37:39  Show Profile  Reply
Hi

Does it make any performance difference if you skip the use of the TBitmap, and just interact with TIEBitmap.AlphaChannel.Canvas?

Something like (untested):

procedure PolyPolygonForIE_Voids(aDC: HDC; aPtArr: PPoint; aPtCount: PInteger; aPlyCount: Integer; BrClr: TColor = 0; BrOp: Byte = 255; BmpBrush: Str255 = '');
{ Draw transparent polygons WITH voids using Windows PolyPolygon procedure & TIEBitmap AlphaChannel }
var
  TmpCnv: TCanvas;
  ieBmp: TIEBitmap;
  DrawFormWidth, DrawFormHeight: Integer;
begin
  if (aPlyCount = 0) then Exit { Nothing to do }
  else if (aPtCount^ < 3) then Exit; { Not a valid polygon }

  { Create temporary canvas from Device Context }
  TmpCnv := TCanvas.Create;
  TmpCnv.Handle := aDC;
  DrawFormWidth := TmpCnv.ClipRect.Right;
  DrawFormHeight := TmpCnv.ClipRect.Top;

  { Create 'ie' bitmap, same size as Device Context }
  ieBmp := TIEBitmap.Create(DrawFormWidth, DrawFormHeight, ie32RGB); { This method seems fastest }
  { Set compositing mode }
  ieBmp.IECanvas.SetCompositingMode(ieCompositingModeSourceOver, ieCompositingQualityDefault);

  { Draw Mask to ieBmp's Alpha Channel }
  { Set brush color to grey value from user-defined opacity value }
  ieBmp.AlphaChannel.Canvas.Brush.Color := RGB(BrOp, BrOp, BrOp);
  { Draw polygon with voids using Windows.PolyPolygon (Alpha Image) }
  PolyPolygon(ieBmp.AlphaChannel.Canvas.Handle, aPtArr^, aPtCount^, aPlyCount);

  // Should not be needed???
  // ieBmp.SyncAlphaChannel(True);

  { Set brush color to user-defined value }
  ieBmp.Canvas.Brush.Color := BrClr;
  { Draw polygon with voids using Windows.PolyPolygon (Source Image) }
  PolyPolygon(ieBmp.Canvas.Handle, aPtArr^, aPtCount^, aPlyCount);

  { Merge result onto our DrawForm canvas }
  ieBmp.DrawToCanvasWithAlpha(TmpCnv, 0, 0);

  aBmp.Free;
  ieBmp.Free;
  TmpCnv.Free;
end; { PolyPolygonForIE_Voids }


Ideally you would skip TCanvas completely and use only GDI+ methods (better performance, transparency and anti-aliasing support), but unfortunately there is not a simple equivalent PolyPolyline/PolyPolygon in GDI+

You could split your PolyPolygon call into multiple TIECanvas.Polygon calls, but the void handling would be a headache.



Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 06 2022 :  10:20:08  Show Profile  Reply
Thanks Nigel. This works but I had to add a call to ieBmp.AlphaChannel.Fill(0) after creating ieBmp. Adding a call to ieBmp.Fill(0) doesn't seem to be necessary, nor does the call to SyncAlphaChannel. I also added a call to set ieBmp.Canvas.Pen.Width to 0, otherwise a 'thin' line appears when drawing on a white background. We draw vector boundaries separately from the display list to make sure they are 'on top' of fills.

Since we draw one polygon (with or without voids) one at a time, I can easily separate out the voids like this.


  { Convert DataCAD's point array for Polygon procedure }
  for i := 0 to Pred(aPlyCount) do begin { x Polygons }
    SetLength(iePtsArr, aPtCount^); { No. Points in curr. Polygon }
    for j := 0 to Pred(aPtCount^) do begin
      iePtsArr[j] := aPtArr^; { Array of points in curr. Polygon. }
      Inc(aPtArr);
    end;
    Inc(aPtCount);

    if (i = 0) then
      ieCnv.Polygon(iePtsArr) { Draw Parent Polygon }
    else begin
      { Change brush properties here }
      ieCnv.Polygon(iePtsArr) { Draw Voids from array one at a time }
    end;
  end;


However, voids can overlap or occur outside of the parent's boundary so I'd have to figure out how to draw areas of voids outside of the parent 'positively' rather than transparently to be consistent with the way they are handled by PolyPolygon.
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - Apr 29 2022 :  05:03:23  Show Profile  Reply
I've come up with a faster method that solves my original problem of determining how to use Windows.PolyPolygon to draw transparent fills with (or without) voids. I'm now using my call to draw the polygon to create a clipping path/region instead. I can then simply draw a transparent rectangle over the clipping region.

procedure PolyPolygonFills(aDC: HDC; aPtArr: PPoint; aPtCount: PInteger; aPlyCount: Integer; BrClr: TColor = 0; BrOp: Byte = 255; BmpBrush: Str255 = '');
{ Draw transparent rectangle over clipping region using ImageEN }
var
  i, j: Integer;

  ieCnv: TIECanvas;
  Rgn: HRGN;
  ClipRect: TRect;

begin
  if (aPlyCount = 0) then Exit { Nothing to do }
  else if (aPtCount^ < 3) then Exit; { Not a valid polygon }

  BeginPath(aDC);
  PolyPolygon(aDC, aPtArr^, aPtCount^, aPlyCount);
  EndPath(aDC);

  Rgn := PathToRegion(aDC);
  SelectClipRgn(aDC, Rgn);
  GetClipBox(aDC, ClipRect); { Rectangular extents of clipping region / polygon }

  { Create 'ie' canvas from memory canvas for 'transparent' Polygon procedure }
  ieCnv := TIECanvas.Create(MemCnv);
  { Does this help? }
  ieCnv.SmoothingMode := iesmBestPerformance;

  ieCnv.Pen.Color := BrClr;
  ieCnv.Pen.Transparency := BrOp;
  ieCnv.Pen.Width := 0;

  { Set unique brush property for ieCanvas }
  ieCnv.Brush.Color := BrClr;
  ieCnv.Brush.Transparency := BrOp; { 0 - 255 }
  ieCnv.Brush.Style := bsSolid;

  ieCnv.Rectangle(ClipRect); { Draw transparent rectangle over clipping region }

  ieCnv.Free;

  SelectClipRgn(aDC, 0); { Clear region from canvas }
  if (Rgn <> 0) then DeleteObject(Rgn);
end; { PolyPolygonFills }
Go to Top of Page

xequte

38610 Posts

Posted - May 01 2022 :  22:40:20  Show Profile  Reply
Nice. That should give you much better performance too.

How is the quality? Do you need iesmAntialias for anti-aliasing?

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

Fellafoo

USA
52 Posts

Posted - May 04 2022 :  08:09:25  Show Profile  Reply
Yes, much faster. The quality with SmoothingMode set to iesmBestPerformance is certainly acceptable on screen. I'll probably end up exposing this to the user so they can adjust it if they want to.
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
Jump To: