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
|
|
|