Exchanging curves to/from clipboard using Metafiles

Please discuss general Delphi programming topics here.

Exchanging curves to/from clipboard using Metafiles

Postby SaeedShekari » April 30th, 2008, 11:53 am

Hi all,
I am reading and writing Metafiles from/to windows clipboard to exchange objects with other softwares like CorelDraw. When I copy some Bezier curves from CorelDraw (Simpley by Ctrl+C) then I can exactly paste them into my application as Beziers. But I wonder why, when I create a metafile myself and draw some Beziers in my MetaFileCanvas, Beziers are being pasted as polygons (not Beziers) in CorelDraw! This is while I checked all Windows API drawing function like PolyBezier, PolyBezierTo and PolyDraw (by which I can draw lines and Beziers together). I also checked all the available methods in TMetaFileCanvas component, but the result was the same.

I have faced another problem which is the accuracy! Data (points) miss the accuracy. I set MMHeight and MMWidth properties of MetaFile, but it had no effect. I tried to use SetMapMode function and apply MM_HIMETRIC mode to force a 0.01mm accuracy, It affects the units, but still the points are transferred with an accuracy of 0.254mm!

Any idea where I am making a mistake, or which functions and settings I am missing?

Is there any other way to exchange curves other than MetaFiles in Clipboard? I know I can create my own format in clipboard, but it should be readable by other applications.

Any help will highly be appreciated.

Here is a part of my codes by which I create a Metafile and Copy to clipboard

Code: Select all
MetaFile:= TMetaFile.Create;
MetaFile. Enhanced:= True;
R:= GetSelectedObjectsRect;
MetaFile.Height:= R.Right-R.Left;
MetaFile.width:= R.Bottom - R.Top;
MetaFile.MMWidth:= Abs(InitRect.Right - InitRect.Left) Div 10; //**SEE BELOW
MetaFile.MMHeight:= Abs(InitRect.Top - InitRect.Bottom) Div 10; //**SEE BELOW
MetaCanvas:= TMetafileCanvas.Create(MetaFile, 0);
SetMapMode(MetaFile.Handle, MM_HIMETRIC);
For i:= 0 To Selecteds.Count-1 Do
Begin
//  ….. Draw Some Beziers….
//In two below lines, Controls: Array[1..4] of TPoint
  MoveTo(MetaCanvas.Handle, Controls[0].x, Controls[0].y);
  PolyBezier(MetaCanvas.Handle, Controls, n +1);
End;
MetaCanvas.Free;
MetaFile.SaveToClipboardFormat(Format, DataHandle, PaletteHandle);
Clipboard.Clear;
ClipBoard.Open;
SetClipboardData(CF_ENHMETAFILE, MetaFile.Handle);
ClipBoard.Close;


** My data accuracy is 0.001mm and I use integer type and 1 units means 0.0001mm im my application, for MetaFile I divide it into 10 to drop the accuracy to 0.01, so 1 unit will be 0.01mm
Saeed Shekari
User avatar
SaeedShekari
Active Member
Active Member
 
Posts: 20
Joined: March 8th, 2008, 2:01 pm
Location: UAE

Postby Kambiz » May 1st, 2008, 2:29 am

Regarding accuracy, it depends on the DC that metafile is created from it. In your code, you have used zero as DC (second parameter of TMetafileCanvas constructor) that means the screen DC. Therefore, your metafile resolution is limited to display resolution.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby SaeedShekari » May 1st, 2008, 10:52 am

Hi,

Thank you Kambiz, You are absolutely right. The precision problem was solved using a DC as a reference:

here is the new code:
Code: Select all
hdcRef     := GetDC(MyScreenCanvasHandle);
iWidthMM   := GetDeviceCaps(hdcRef, HORZSIZE);
iHeightMM  := GetDeviceCaps(hdcRef, VERTSIZE);
iWidthPels := GetDeviceCaps(hdcRef, HORZRES);
iHeightPels:= GetDeviceCaps(hdcRef, VERTRES);

R.Left      := Round( (InitRect.Left * iWidthMM  * 100) / iWidthPels );
R.Top      := Round( (InitRect.top * iHeightMM * 100) / iHeightPels );
R.Right    := Round( (InitRect.right * iWidthMM * 100) / iWidthPels );
R.Bottom := Round( (InitRect.bottom * iHeightMM * 100) / iHeightPels );

hdcMeta:= CreateEnhMetaFile(hdcRef, Nil, @R, Nil); //Create a EMF in Memory
SetMapMode(hdcMeta, MM_HIMETRIC);

//Let Objects Draw their curves and data to hdcMeta as usual
For i:= 0 To Selecteds.Count-1 Do
  TMyObject(Selecteds[i]).DrawToMetaFile(hdcMeta);

Meta:= CloseEnhMetaFile(hdcMeta);
Clipboard.Clear;
ClipBoard.Open;
SetClipboardData(CF_ENHMETAFILE, Meta);
ClipBoard.Close;

But the problem with Bezier (which is drawn as polygons) still persists.
As you see in above, I am calling CreateEnhMetaFile API function, I tough maybe TMetaFile component could not create a real EMF, but the result was same! Any idea why Beziers are drawn in Enhanced Metafile as polygons? Is there anything I don’t know?
Below is my code how I draw a Bezier, as you see I am calling PolyDraw API function to draw lines and beziers:
Code: Select all
Procedure TBezierCurve.DrawToMetaFile(MetaCanvas: HDC);
Begin
  //Adding Bezier points into 'PolyPoints' array
  //Adding Bezier point types into 'BnByte' array

  PolyDraw(MetaCanvas, PolyPoints^, BnByte^, cCount);

End;

//Above code was simplified
//Here is the complete code

Procedure TBezierCurve.DrawToMetaFile(MetaCanvas: HDC);
Type
  TPointArr= Array[0..50000] Of TPoint;
  PPointArr= ^TPointArr;
Var
  cCount: Integer;
  BnByte: PByteArray;
  PolyPoints: PPointArr;
  i, n: Integer;
  BN, BNp: TBezierNode;
Begin
  //I am cuculating the necessary point counts which should be created
  cCount:= 0;
  For i:=0 To  BzNodes.Count-1 Do
  Begin
    Bn:= TBezierNode(BzNodes.Items[i]);
    //Checking  if my data point is a line of a bezier
    If (i <> 0) And ((Bn.Kind And BNKILine) <> BNKILine) Then
    Begin
      //If point is bezier then three more points are required for controls
      cCount:= cCount + 3;
      Continue;
    End;
    Inc(cCount);
  End;
 
  //Aloocating memory
  PolyPoints:= AllocMem((cCount) * SizeOf(TPoint));
  BnByte:= AllocMem((cCount) * sizeof(Byte));

  //Now filling arrays with bezier points
  n:= 0;
  For i:=0 To  BzNodes.Count-1 Do
  Begin
    Bn:= TBezierNode(BzNodes.Items[i]);
    If i = 0 Then
    Begin
      BnByte[n]:= PT_MOVETO;
      PolyPoints[n]:= Point(BN.N2.x, BN.N2.y);
      Inc(n);
      Bnp:= Bn;
    End
    Else
    If (Bn.Kind And BNKILine) = BNKILine Then
    Begin
      BnByte[n]:= PT_LINETO;
      PolyPoints[n]:= Point(BN.N2.x, BN.N2.y );
      Inc(n);
      Bnp:= Bn;
    End
    Else
    Begin
      BnByte[n]:= PT_BEZIERTO;
      PolyPoints[n]:= Point(Bnp.N3.x, Bnp.N3.y);
      Inc(n);
      BnByte[n]:= PT_BEZIERTO;
      PolyPoints[n]:= Point(Bn.N1.x, Bn.N1.y);
      Inc(n);
      BnByte[n]:= PT_BEZIERTO;
      PolyPoints[n]:= Point(Bn.N2.x, Bn.N2.y);
      Inc(n);
      Bnp:= Bn;
    End;
  End;
 
  //calling Win API function to draw bezier to MetaFile
  PolyDraw(MetaCanvas, PolyPoints^, BnByte^, cCount);
  FreeMem(PolyPoints);
  FreeMem(BnByte);

End;
Saeed Shekari
User avatar
SaeedShekari
Active Member
Active Member
 
Posts: 20
Joined: March 8th, 2008, 2:01 pm
Location: UAE

Postby Kambiz » May 2nd, 2008, 5:11 pm

Have you tried to create your EMF using GDI+?
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby SaeedShekari » May 3rd, 2008, 6:26 am

I shall try that GDI+. Thanks Kambiz
Saeed Shekari
User avatar
SaeedShekari
Active Member
Active Member
 
Posts: 20
Joined: March 8th, 2008, 2:01 pm
Location: UAE


Return to Delphi Programming

Who is online

Users browsing this forum: No registered users and 3 guests

cron