明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
12
返回列表 发新帖

[Kean专集] Kean专题(3)—Graphics_System

   关闭 [复制链接]
 楼主| 发表于 2009-9-12 20:08 | 显示全部楼层
八、从主窗口和绘图窗口截图September 09, 2009
Taking screenshots of AutoCAD’s main and drawing windows using .NET
I received a comment on this previous post:
    Hi Kean, I tried to develop a routine based on this post but I found 2 things that I'd like to solve if possible. Your routine is just not usable for larger drawings (takes way to long). Also, your routine does not work properly in paper space. Please, don't get me wrong, I appreciate all the work you've put into this blog. I just desperately need to come up with a solution for snapshots that work on large drawings, that work in paper/model space and that use the current viewstyle settings (except the background color). Isn't there a way to somehow mimic the GetGsView function that is fast and works in paper space as well but has a downfall of creating a 3D view in the actual drawing whenever a 2D Wireframe visual style is set? Any help is really appreciated.
The previous post was really focused on using AutoCAD’s graphics system to generate a bitmap of the contained model. Something that may work better for certain specific scenarios is to capture graphics at the operating system level: essentially taking a screenshot (as opposed to what I previously called a snapshot – although frankly the difference is really which graphics system you ask to generate the graphics). The code I’ve adapted for use within AutoCAD is shown here.
Here’s the C# code:
  1. using acApp = Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.Runtime;
  3. using System.Drawing.Imaging;
  4. using System.Drawing;
  5. namespace ScreenshotTest
  6. {
  7.   public class Commands
  8.   {
  9.     [CommandMethod("CSS")]
  10.     static public void CaptureScreenShot()
  11.     {
  12.       ScreenShotToFile(
  13.         acApp.Application.MainWindow,
  14.         "c:\\main-window.png",
  15.         0, 0, 0, 0
  16.       );
  17.       ScreenShotToFile(
  18.         acApp.Application.DocumentManager.MdiActiveDocument.Window,
  19.         "c:\\doc-window.png",
  20.         30, 26, 10, 10
  21.       );
  22.     }
  23.     private static void ScreenShotToFile(
  24.       Autodesk.AutoCAD.Windows.Window wd,
  25.       string filename,
  26.       int top, int bottom, int left, int right
  27.     )
  28.     {
  29.       Point pt = wd.Location;
  30.       Size sz = wd.Size;
  31.       pt.X += left;
  32.       pt.Y += top;
  33.       sz.Height -= top + bottom;
  34.       sz.Width -= left + right;
  35.       // Set the bitmap object to the size of the screen
  36.       Bitmap bmp =
  37.         new Bitmap(
  38.           sz.Width,
  39.           sz.Height,
  40.           PixelFormat.Format32bppArgb
  41.         );
  42.       using (bmp)
  43.       {
  44.         // Create a graphics object from the bitmap
  45.         using (Graphics gfx = Graphics.FromImage(bmp))
  46.         {
  47.           // Take a screenshot of our window
  48.           gfx.CopyFromScreen(
  49.             pt.X, pt.Y, 0,0, sz,
  50.             CopyPixelOperation.SourceCopy
  51.           );
  52.           // Save the screenshot to the specified location
  53.           bmp.Save(filename, ImageFormat.Png);
  54.         }
  55.       }
  56.     }
  57.   }
  58. }
The code is pretty simple: it defines a command called CSS which creates two screenshots – one of the entire application window and one of the active document. The function that does the hard work – ScreenShotToFile() takes some arguments to allow cropping of the created image, as – for instance - the drawing window also contains some graphics that most people wouldn’t consider to be part of the drawing. If you want to see the reason for adding these four parameters, try passing zero to each of them in second call to the function (just as we do for the first), and look at the resulting image in c:\doc-window.png.
You’ll need to add references to a few additional .NET assemblies: System.Drawing and probably PresentationCore if you’re working with AutoCAD 2010.
Now let’s see what we get when we run our CSS command in an editing session. Two files get created – c:\main-window.png:

And c:\doc-window.png:

While this approach may not work for every situation, it’s another option to consider, depending on your requirements.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2009-9-24 14:21 | 显示全部楼层
九、在文档中按选择区域截图
September 23, 2009
Taking a screenshot of a user-selected portion of a drawing using .NET
Following on from this recent post – and inspired by a question we received recently from a developer – I decided to extend the previous code to allow a user to select a portion of a drawing they would like to save to a file or place on the clipboard. This is actually a really useful tool for me when I’m writing this blog, so there was certainly a degree of self-interest involved. :-)
The technique shown in today’s post is actually pretty handy for other situations: it’s quite common to want to transform a point from drawing coordinates (which may well be in a specific User Coordinate System (UCS)) into screen coordinates, especially when wanting to display a window or a custom context menu at the cursor location.
To transform from UCS to screen coordinates we actually need three steps (and thanks to Adam Nagy, from DevTech EMEA, for creating code I borrowed from to get this working):
   1. Transform the selected point by the current UCS matrix to get WCS
          * This can be achieved using the .NET interface
   2. Transform the WCS point into Windows client coordinates
          * For this we need to P/Invoke the ObjectARX function acedCoordFromWorldToPixel()
   3. Transform the client coordinates into screen coordinates
          * For this we need to P/Invoke the Win32 API function ClientToScreen()
We need to do this for both corners of our user-selected window, of course. Once we have the corners in screen coordinates, the code from last time can be used: with a few modifications to support selection of top-right/bottom-left corners, rather than the assumed top-left/bottom-right, as well as adding support for placing the bitmap on the clipboard, rather than saving it to a file.
Here’s the updated C# code with our new WSS command:
  1. using acApp = Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.EditorInput;
  3. using Autodesk.AutoCAD.Geometry;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System.Drawing.Imaging;
  6. using System.Drawing;
  7. using System.Runtime.InteropServices;
  8. using System.Windows.Interop;
  9. using System;
  10. namespace ScreenshotTest
  11. {
  12.   public class Commands
  13.   {
  14.     // For the coordinate tranformation we need...
  15.     // An ObjectARX function:
  16.     [DllImport(
  17.       "acad.exe",
  18.       EntryPoint="?acedCoordFromWorldToPixel@@YAHHQBNAAVCPoint@@@Z"
  19.     )]
  20.     static extern int acedCoordFromWorldToPixel(
  21.       int windnum,
  22.       double[] ptIn,
  23.       out Point ptOut
  24.     );
  25.     // And a Win32 function:
  26.     [DllImport("user32.dll")]
  27.     static extern bool ClientToScreen(IntPtr hWnd, ref Point pt);
  28.     // Command to capture the main and active drawing windows
  29.     [CommandMethod("CSS")]
  30.     static public void CaptureScreenShot()
  31.     {
  32.       ScreenShotToFile(
  33.         acApp.Application.MainWindow,
  34.         0, 0, 0, 0,
  35.         "c:\\main-window.png",
  36.         false
  37.       );
  38.       ScreenShotToFile(
  39.         acApp.Application.DocumentManager.MdiActiveDocument.Window,
  40.         30, 26, 10, 10,
  41.         "c:\\doc-window.png",
  42.         false
  43.       );
  44.     }
  45.     // Command to capture a user-selected portion of a drawing
  46.     [CommandMethod("WSS")]
  47.     static public void WindowScreenShot()
  48.     {
  49.       acApp.Document doc =
  50.         acApp.Application.DocumentManager.MdiActiveDocument;
  51.       Editor ed = doc.Editor;
  52.       // Ask the user for the screen window to capture
  53.       PromptPointResult ppr =
  54.         ed.GetPoint("\nSelect first point of capture window: ");
  55.       if (ppr.Status != PromptStatus.OK)
  56.         return;
  57.       Point3d first = ppr.Value;
  58.       ppr =
  59.         ed.GetCorner(
  60.           "\nSelect second point of capture window: ",
  61.           first
  62.         );
  63.       if (ppr.Status != PromptStatus.OK)
  64.         return;
  65.       Point3d second = ppr.Value;
  66.       // Generate screen coordinate points based on the
  67.       // drawing points selected
  68.       Point pt1, pt2;
  69.       // First we get the viewport number
  70.       short vp =
  71.         (short)acApp.Application.GetSystemVariable("CVPORT");
  72.       // Get the current UCS matrix (would be indentify if in WCS)
  73.       Matrix3d ucsMat =
  74.         ed.CurrentUserCoordinateSystem;
  75.       // And then the handle to the current drawing window
  76.       IntPtr hWnd = doc.Window.Handle;
  77.       // Now calculate the selected corners in screen coordinates
  78.       pt1 = ScreenFromDrawingPoint(ucsMat, hWnd, first, vp);
  79.       pt2 = ScreenFromDrawingPoint(ucsMat, hWnd, second, vp);
  80.       // Now save this portion of our screen as a raster image
  81.       ScreenShotToFile(pt1, pt2, null, true);
  82.     }
  83.     // Perform our three tranformations to get from UCS (or WCS)
  84.     // to screen coordinates
  85.     private static Point ScreenFromDrawingPoint(
  86.       Matrix3d ucsMat,
  87.       IntPtr hWnd,
  88.       Point3d ucsPt,
  89.       short vpNum
  90.     )
  91.     {
  92.       Point res;
  93.       Point3d wcsPt = ucsPt.TransformBy(ucsMat);
  94.       acedCoordFromWorldToPixel(vpNum, wcsPt.ToArray(), out res);
  95.       ClientToScreen(hWnd, ref res);
  96.       return res;
  97.     }
  98.     // Save the display of an AutoCAD window as a raster file
  99.     // and/or an image on the clipboard
  100.     private static void ScreenShotToFile(
  101.       Autodesk.AutoCAD.Windows.Window wd,
  102.       int top, int bottom, int left, int right,
  103.       string filename,
  104.       bool clipboard
  105.     )
  106.     {
  107.       Point pt = wd.Location;
  108.       Size sz = wd.Size;
  109.       pt.X += left;
  110.       pt.Y += top;
  111.       sz.Height -= top + bottom;
  112.       sz.Width -= left + right;
  113.       SaveScreenPortion(pt, sz, filename, clipboard);
  114.     }
  115.     // Save a screen window between two corners as a raster file
  116.     // and/or an image on the clipboard
  117.     private static void ScreenShotToFile(
  118.       Point pt1,
  119.       Point pt2,
  120.       string filename,
  121.       bool clipboard
  122.     )
  123.     {
  124.       // Create the top left corner from the two corners
  125.       // provided (by taking the min of both X and Y values)
  126.       Point pt =
  127.         new Point(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y));
  128.       // Determine the size by subtracting X & Y values and
  129.       // taking the absolute value of each
  130.       Size sz =
  131.         new Size(Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
  132.       SaveScreenPortion(pt, sz, filename, clipboard);
  133.     }
  134.     // Save a portion of the screen display as a raster file
  135.     // and/or an image on the clipboard
  136.     private static void SaveScreenPortion(
  137.       Point pt,
  138.       Size sz,
  139.       string filename,
  140.       bool clipboard
  141.     )
  142.     {
  143.       // Set the bitmap object to the size of the window
  144.       Bitmap bmp =
  145.         new Bitmap(
  146.           sz.Width,
  147.           sz.Height,
  148.           PixelFormat.Format32bppArgb
  149.         );
  150.       using (bmp)
  151.       {
  152.         // Create a graphics object from the bitmap
  153.         using (Graphics gfx = Graphics.FromImage(bmp))
  154.         {
  155.           // Take a screenshot of our window
  156.           gfx.CopyFromScreen(
  157.             pt.X, pt.Y, 0, 0, sz,
  158.             CopyPixelOperation.SourceCopy
  159.           );
  160.           // Save the screenshot to the specified location
  161.           if (filename != null && filename != "")
  162.             bmp.Save(filename, ImageFormat.Png);
  163.           // Copy it to the clipboard
  164.           if (clipboard)
  165.             System.Windows.Forms.Clipboard.SetImage(bmp);
  166.         }
  167.       }
  168.     }
  169.   }
  170. }
To get the code working, you may need to add an additional assembly reference to System.Windows.Forms.dll, which gives us access to the Clipboard. I should also mention that I’m running this code on AutoCAD 2010 on a 32-bit OS: if the DllImport statement to P/Invoke our ObjectARX function, you may need to find and declare the appropriate function signature.
To put our WSS command through its paces, let’s load a drawing and set a random UCS (I do this by changing the view using 3DORBIT and using the UCS command, setting it to the current View) and on top of that changing the view to be from a another random angle not aligned with this new UCS:

Now we NETLOAD our assembly and run the WSS command, selecting the two corners of our window:

And we can see that the resultant image is on the clipboard by pasting it somewhere (we could very easily adjust the code to save to a file, if we so chose) :

Update:
Paavo pointed out in a comment that I had missed a managed API method to replace the P/Invoke of acedCoordFromWorldToPixel(), which makes the code much cleaner and more portable across versions: Editor.PointToScreen(). We still have to P/Invoke ClientToScreen(), but at least that’s undecorated/unmangled.
Here’s the revised C# code, which I’ve included in its entirety because it has ended up meaning a change both to the ScreenFromDrawingPoint() function and the code that calls it: we need the editor to perform our transformation so we might as well use it within the function to retrieve the matrix representing the current UCS. Which simplifies things somewhat.
  1. using acApp = Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.EditorInput;
  3. using Autodesk.AutoCAD.Geometry;
  4. using Autodesk.AutoCAD.Runtime;
  5. using System.Drawing.Imaging;
  6. using System.Drawing;
  7. using System.Runtime.InteropServices;
  8. using System.Windows.Interop;
  9. using System;
  10. namespace ScreenshotTest
  11. {
  12.   public class Commands
  13.   {
  14.     // For the coordinate tranformation we need...
  15.     // A Win32 function:
  16.     [DllImport("user32.dll")]
  17.     static extern bool ClientToScreen(IntPtr hWnd, ref Point pt);
  18.     // Command to capture the main and active drawing windows
  19.     [CommandMethod("CSS")]
  20.     static public void CaptureScreenShot()
  21.     {
  22.       ScreenShotToFile(
  23.         acApp.Application.MainWindow,
  24.         0, 0, 0, 0,
  25.         "c:\\main-window.png",
  26.         false
  27.       );
  28.       ScreenShotToFile(
  29.         acApp.Application.DocumentManager.MdiActiveDocument.Window,
  30.         30, 26, 10, 10,
  31.         "c:\\doc-window.png",
  32.         false
  33.       );
  34.     }
  35.     // Command to capture a user-selected portion of a drawing
  36.     [CommandMethod("WSS")]
  37.     static public void WindowScreenShot()
  38.     {
  39.       acApp.Document doc =
  40.         acApp.Application.DocumentManager.MdiActiveDocument;
  41.       Editor ed = doc.Editor;
  42.       // Ask the user for the screen window to capture
  43.       PromptPointResult ppr =
  44.         ed.GetPoint("\nSelect first point of capture window: ");
  45.       if (ppr.Status != PromptStatus.OK)
  46.         return;
  47.       Point3d first = ppr.Value;
  48.       ppr =
  49.         ed.GetCorner(
  50.           "\nSelect second point of capture window: ",
  51.           first
  52.         );
  53.       if (ppr.Status != PromptStatus.OK)
  54.         return;
  55.       Point3d second = ppr.Value;
  56.       // Generate screen coordinate points based on the
  57.       // drawing points selected
  58.       Point pt1, pt2;
  59.       // First we get the viewport number
  60.       short vp =
  61.         (short)acApp.Application.GetSystemVariable("CVPORT");
  62.       // Then the handle to the current drawing window
  63.       IntPtr hWnd = doc.Window.Handle;
  64.       // Now calculate the selected corners in screen coordinates
  65.       pt1 = ScreenFromDrawingPoint(ed, hWnd, first, vp);
  66.       pt2 = ScreenFromDrawingPoint(ed, hWnd, second, vp);
  67.       // Now save this portion of our screen as a raster image
  68.       ScreenShotToFile(pt1, pt2, null, true);
  69.     }
  70.     // Perform our three tranformations to get from UCS (or WCS)
  71.     // to screen coordinates
  72.     private static Point ScreenFromDrawingPoint(
  73.       Editor ed,
  74.       IntPtr hWnd,
  75.       Point3d ucsPt,
  76.       short vpNum
  77.     )
  78.     {     
  79.       Point3d wcsPt =
  80.         ucsPt.TransformBy(
  81.           ed.CurrentUserCoordinateSystem
  82.         );
  83.       Point res = ed.PointToScreen(wcsPt, vpNum);
  84.       ClientToScreen(hWnd, ref res);
  85.       return res;
  86.     }
  87.     // Save the display of an AutoCAD window as a raster file
  88.     // and/or an image on the clipboard
  89.     private static void ScreenShotToFile(
  90.       Autodesk.AutoCAD.Windows.Window wd,
  91.       int top, int bottom, int left, int right,
  92.       string filename,
  93.       bool clipboard
  94.     )
  95.     {
  96.       Point pt = wd.Location;
  97.       Size sz = wd.Size;
  98.       pt.X += left;
  99.       pt.Y += top;
  100.       sz.Height -= top + bottom;
  101.       sz.Width -= left + right;
  102.       SaveScreenPortion(pt, sz, filename, clipboard);
  103.     }
  104.     // Save a screen window between two corners as a raster file
  105.     // and/or an image on the clipboard
  106.     private static void ScreenShotToFile(
  107.       Point pt1,
  108.       Point pt2,
  109.       string filename,
  110.       bool clipboard
  111.     )
  112.     {
  113.       // Create the top left corner from the two corners
  114.       // provided (by taking the min of both X and Y values)
  115.       Point pt =
  116.         new Point(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y));
  117.       // Determine the size by subtracting X & Y values and
  118.       // taking the absolute value of each
  119.       Size sz =
  120.         new Size(Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
  121.       SaveScreenPortion(pt, sz, filename, clipboard);
  122.     }
  123.     // Save a portion of the screen display as a raster file
  124.     // and/or an image on the clipboard
  125.     private static void SaveScreenPortion(
  126.       Point pt,
  127.       Size sz,
  128.       string filename,
  129.       bool clipboard
  130.     )
  131.     {
  132.       // Set the bitmap object to the size of the window
  133.       Bitmap bmp =
  134.         new Bitmap(
  135.           sz.Width,
  136.           sz.Height,
  137.           PixelFormat.Format32bppArgb
  138.         );
  139.       using (bmp)
  140.       {
  141.         // Create a graphics object from the bitmap
  142.         using (Graphics gfx = Graphics.FromImage(bmp))
  143.         {
  144.           // Take a screenshot of our window
  145.           gfx.CopyFromScreen(
  146.             pt.X, pt.Y, 0, 0, sz,
  147.             CopyPixelOperation.SourceCopy
  148.           );
  149.           // Save the screenshot to the specified location
  150.           if (filename != null && filename != "")
  151.             bmp.Save(filename, ImageFormat.Png);
  152.           // Copy it to the clipboard
  153.           if (clipboard)
  154.             System.Windows.Forms.Clipboard.SetImage(bmp);
  155.         }
  156.       }
  157.     }
  158.   }
  159. }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-3-29 23:11 , Processed in 0.222731 second(s), 17 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表