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

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 TIECanvas: PolylineTo & PolyPolyline

Note: You must be registered in order to post a reply.
To register, click here. Registration is FREE!

View 
UserName:
Password:
Format  Bold Italicized Underline  Align Left Centered Align Right  Horizontal Rule  Insert Hyperlink   Browse for an image to attach to your post Browse for a zip to attach to your post Insert Code  Insert Quote Insert List
   
Message 

 

Emoji
Smile [:)] Big Smile [:D] Cool [8D] Blush [:I]
Tongue [:P] Evil [):] Wink [;)] Black Eye [B)]
Frown [:(] Shocked [:0] Angry [:(!] Sleepy [|)]
Kisses [:X] Approve [^] Disapprove [V] Question [?]

 
Check here to subscribe to this topic.
   

T O P I C    R E V I E W
Fellafoo Posted - Mar 12 2022 : 17:56:50
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
15   L A T E S T    R E P L I E S    (Newest First)
Fellafoo Posted - May 04 2022 : 08:09:25
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.
xequte Posted - May 01 2022 : 22:40:20
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
Fellafoo Posted - Apr 29 2022 : 05:03:23
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 }
Fellafoo Posted - Apr 06 2022 : 10:20:08
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.
xequte Posted - Apr 05 2022 : 17:37:39
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
Fellafoo Posted - Apr 05 2022 : 10:13:07
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 }
Fellafoo Posted - Apr 04 2022 : 11:49:50
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;
xequte Posted - Apr 03 2022 : 18:32:58
Yes, that works nicely.

Image


+ Mask


=



Nigel
Xequte Software
www.imageen.com


Fellafoo Posted - Apr 03 2022 : 18:12:12
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;
Fellafoo Posted - Apr 03 2022 : 13:03:14
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;
Fellafoo Posted - Apr 01 2022 : 12:03:06
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
Fellafoo Posted - Mar 30 2022 : 11:44:45
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?
xequte Posted - Mar 15 2022 : 19:55:58
Nice work

Nigel
Xequte Software
www.imageen.com
Fellafoo Posted - Mar 15 2022 : 11:57:02
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 }
xequte Posted - Mar 12 2022 : 18:09:37
Hi

Sorry, those methods are not supported at this time.

Nigel
Xequte Software
www.imageen.com