AutoCAD中的ScreenShot工具

当我在Kean的博客上看到这个东西的时候,第一个使我感到好奇的地方是:怎么截取到屏幕上的图呢?快速浏览了一下代码后彻底醒悟了,原来.NET里面的Graphics对象就有个函数能简单实现——就是CopyFromScreen,它能复制选定屏幕范围的图像,然后图像被自动保存在Graphics对象最初设定的图片格式文件(比如位图)中。这么简单,真是汗言!其它的事情就很简单了,比如在AutoCAD中怎么获取用户选定的区域然后转换成屏幕坐标范围,这些用AutoCAD API就能实现。

下面是转载自Kean的博客:

Design iteration #2 of Screenshot, November's Plugin of the Month

Thanks to all those who provided feedback on this recent post. I really appreciate people taking the time to provide feedback – both positive and negative – as I want to ensure this tool is used by more than just a few AutoCAD users. Today's version extends the last one to include a few more "Settings":

  • Existing settings
    • Output
      • Allows the user to choose whether the captured image is copied to the clipboard or saved to a raster file
    • Background
      • Allows the user to force the background colour to white
        • Hopefully useful for people using a non-quite background but want to create images for documentation
  • New settings
    • Foreground
      • Allows the user to force the foreground colour (and that means everything not the same colour as the background, at least for now) to black
        • This can be used in combination with "Background" to create a pure black & white image
    • Grayscale
      • Allows the user to force the resultant image to be grayscale (can be used in conjunction with Foreground and/or Background, but may therefore not have any visible effects)

At Mark Johnston's suggestion (and thanks for your help with testing these options, Mark!) I also added a message to indicate when the capture has occurred (and where the resultant image has been placed). And thanks to Viru Aithal, our DevTech India Manager, for providing some code to change the background colour of a 3D view.

In terms of future enhancements… I'm chewing on an option to hide the navigations aids (e.g. the UCS Icon and the ViewCube). Visibility of the UCS Icon should be simple enough to control via the active viewport, but disabling the ViewCube is likely to be more complex: I haven't yet seen a .NET equivalent for the acdbGetShowCompass() ObjectARX function – P/Invoking it is possible, I expect – and the other option would involve calling the NAVVCUBE command, but as it needs to run synchronously I'd have to use SendCommand() via COM. Either approach adds complexity and/or version dependency issues, so I'm starting to veer away from this direction. Please do post any thoughts you might have as comments, of course.

Here's the updated C# code:

using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; using Autodesk.AutoCAD.GraphicsSystem; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.Colors; using System.Drawing.Imaging; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Interop; using System.Collections; using System; namespace ScreenshotTest { public class Commands { // Specify a key under which we want // to store our custom data const string myKey = "AdnPluginScreenshot"; // Define a class for our custom data public class AppData { public bool clipboard; public bool whitebackground; public bool blackforeground; public bool grayscale; public AppData() { clipboard = true; whitebackground = false; blackforeground = false; grayscale = false; } } // A struct for communicating colours to/from AutoCAD public struct AcColorSettings { public UInt32 dwGfxModelBkColor; public UInt32 dwGfxLayoutBkColor; public UInt32 dwParallelBkColor; public UInt32 dwBEditBkColor; public UInt32 dwCmdLineBkColor; public UInt32 dwPlotPrevBkColor; public UInt32 dwSkyGradientZenithColor; public UInt32 dwSkyGradientHorizonColor; public UInt32 dwGroundGradientOriginColor; public UInt32 dwGroundGradientHorizonColor; public UInt32 dwEarthGradientAzimuthColor; public UInt32 dwEarthGradientHorizonColor; public UInt32 dwModelCrossHairColor; public UInt32 dwLayoutCrossHairColor; public UInt32 dwParallelCrossHairColor; public UInt32 dwPerspectiveCrossHairColor; public UInt32 dwBEditCrossHairColor; public UInt32 dwParallelGridMajorLines; public UInt32 dwPerspectiveGridMajorLines; public UInt32 dwParallelGridMinorLines; public UInt32 dwPerspectiveGridMinorLines; public UInt32 dwParallelGridAxisLines; public UInt32 dwPerspectiveGridAxisLines; public UInt32 dwTextForeColor; public UInt32 dwTextBkColor; public UInt32 dwCmdLineForeColor; public UInt32 dwAutoTrackingVecColor; public UInt32 dwLayoutATrackVecColor; public UInt32 dwParallelATrackVecColor; public UInt32 dwPerspectiveATrackVecColor; public UInt32 dwBEditATrackVecColor; public UInt32 dwModelASnapMarkerColor; public UInt32 dwLayoutASnapMarkerColor; public UInt32 dwParallelASnapMarkerColor; public UInt32 dwPerspectiveASnapMarkerColor; public UInt32 dwBEditASnapMarkerColor; public UInt32 dwModelDftingTooltipColor; public UInt32 dwLayoutDftingTooltipColor; public UInt32 dwParallelDftingTooltipColor; public UInt32 dwPerspectiveDftingTooltipColor; public UInt32 dwBEditDftingTooltipColor; public UInt32 dwModelDftingTooltipBkColor; public UInt32 dwLayoutDftingTooltipBkColor; public UInt32 dwParallelDftingTooltipBkColor; public UInt32 dwPerspectiveDftingTooltipBkColor; public UInt32 dwBEditDftingTooltipBkColor; public UInt32 dwModelLightGlyphs; public UInt32 dwLayoutLightGlyphs; public UInt32 dwParallelLightGlyphs; public UInt32 dwPerspectiveLightGlyphs; public UInt32 dwBEditLightGlyphs; public UInt32 dwModelLightHotspot; public UInt32 dwLayoutLightHotspot; public UInt32 dwParallelLightHotspot; public UInt32 dwPerspectiveLightHotspot; public UInt32 dwBEditLightHotspot; public UInt32 dwModelLightFalloff; public UInt32 dwLayoutLightFalloff; public UInt32 dwParallelLightFalloff; public UInt32 dwPerspectiveLightFalloff; public UInt32 dwBEditLightFalloff; public UInt32 dwModelLightStartLimit; public UInt32 dwLayoutLightStartLimit; public UInt32 dwParallelLightStartLimit; public UInt32 dwPerspectiveLightStartLimit; public UInt32 dwBEditLightStartLimit; public UInt32 dwModelLightEndLimit; public UInt32 dwLayoutLightEndLimit; public UInt32 dwParallelLightEndLimit; public UInt32 dwPerspectiveLightEndLimit; public UInt32 dwBEditLightEndLimit; public UInt32 dwModelCameraGlyphs; public UInt32 dwLayoutCameraGlyphs; public UInt32 dwParallelCameraGlyphs; public UInt32 dwPerspectiveCameraGlyphs; public UInt32 dwModelCameraFrustrum; public UInt32 dwLayoutCameraFrustrum; public UInt32 dwParallelCameraFrustrum; public UInt32 dwPerspectiveCameraFrustrum; public UInt32 dwModelCameraClipping; public UInt32 dwLayoutCameraClipping; public UInt32 dwParallelCameraClipping; public UInt32 dwPerspectiveCameraClipping; public int nModelCrosshairUseTintXYZ; public int nLayoutCrosshairUseTintXYZ; public int nParallelCrosshairUseTintXYZ; public int nPerspectiveCrosshairUseTintXYZ; public int nBEditCrossHairUseTintXYZ; public int nModelATrackVecUseTintXYZ; public int nLayoutATrackVecUseTintXYZ; public int nParallelATrackVecUseTintXYZ; public int nPerspectiveATrackVecUseTintXYZ; public int nBEditATrackVecUseTintXYZ; public int nModelDftingTooltipBkUseTintXYZ; public int nLayoutDftingTooltipBkUseTintXYZ; public int nParallelDftingTooltipBkUseTintXYZ; public int nPerspectiveDftingTooltipBkUseTintXYZ; public int nBEditDftingTooltipBkUseTintXYZ; public int nParallelGridMajorLineTintXYZ; public int nPerspectiveGridMajorLineTintXYZ; public int nParallelGridMinorLineTintXYZ; public int nPerspectiveGridMinorLineTintXYZ; public int nParallelGridAxisLineTintXYZ; public int nPerspectiveGridAxisLineTintXYZ; }; // For the coordinate tranformation we need... // A Win32 function: [DllImport("user32.dll")] static extern bool ClientToScreen(IntPtr hWnd, ref Point pt); // And to access the colours in AutoCAD, we need ObjectARX... [DllImport("acad.exe", CallingConvention=CallingConvention.Cdecl, EntryPoint="?acedGetCurrentColors@@YAHPAUAcColorSettings@@@Z" )] static extern bool acedGetCurrentColors( out AcColorSettings colorSettings ); [DllImport("acad.exe", CallingConvention=CallingConvention.Cdecl, EntryPoint="?acedSetCurrentColors@@YAHPAUAcColorSettings@@@Z" )] static extern bool acedSetCurrentColors( ref AcColorSettings colorSettings ); // 64-bit versions of these functions, for when we support that [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acedGetCurrentColors@@YAHPEAUAcColorSettings@@@Z" )] static extern bool acedGetCurrentColors64( out AcColorSettings colorSettings ); [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint="?acedSetCurrentColors@@YAHPEAUAcColorSettings@@@Z" )] static extern bool acedSetCurrentColors64( ref AcColorSettings colorSettings ); // Command to capture the main and active drawing windows // or a user-selected portion of a drawing [CommandMethod("SCREENSHOT")] static public void CaptureScreenShot() { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Hashtable ud = doc.UserData; AppData ad; ad = ud[myKey] as AppData; if (ad == null) { object obj = ud[myKey]; if (obj == null) { // MyData object not found - first time run ad = new AppData(); ud.Add(myKey, ad); } else { // Found something different instead ed.WriteMessage( "Found an object of type /"" + obj.GetType().ToString() + "/" instead of MyData."); } } if (ad != null) { string filename = ""; bool settingschosen; PromptPointResult ppr; do { settingschosen = false; // Ask the user for the screen window to capture PrintSettings(ed, ad); PromptPointOptions ppo = new PromptPointOptions( "/nSelect first point of capture window or " + "[Document/Application/Objects/Settings]: ", "Document Application Objects Settings" ); // Get the first point of the capture window, // or a keyword ppr = ed.GetPoint(ppo); if (ppr.Status == PromptStatus.Keyword) { if (ppr.StringResult == "Document") { // Capture the active document window if (!ad.clipboard) filename = GetFileName(ed); ScreenShotToFile( Application.DocumentManager. MdiActiveDocument.Window, 30, 26, 10, 10, filename, ad ); } else if (ppr.StringResult == "Application") { // Capture the entire application window if (!ad.clipboard) filename = GetFileName(ed); ScreenShotToFile( Application.MainWindow, 0, 0, 0, 0, filename, ad ); } else if (ppr.StringResult == "Objects") { // Ask the user to select a number of entities PromptSelectionResult psr = ed.GetSelection(); // Generate screen coordinate points based on the // drawing points selected // First we get the viewport number short vp = (short)Application.GetSystemVariable("CVPORT"); // Then the handle to the current drawing window IntPtr hWnd = doc.Window.Handle; // Get the screen extents of the selected entities Point pt1, pt2; GetExtentsOfSelection( ed, doc, hWnd, vp, psr.Value, out pt1, out pt2 ); // Now save this portion of our screen as a raster // image if (!ad.clipboard) filename = GetFileName(ed); ScreenShotToFile(pt1, pt2, filename, ad); } else if (ppr.StringResult == "Settings") { if (GetSettings(ed, ad)) ud[myKey] = ad; settingschosen = true; } } } while (settingschosen); // Loop if settings were modified if (ppr.Status == PromptStatus.OK) { // Now we're ready to select the second point Point3d first = ppr.Value; ppr = ed.GetCorner( "/nSelect second point of capture window: ", first ); if (ppr.Status != PromptStatus.OK) return; Point3d second = ppr.Value; // Generate screen coordinate points based on the // drawing points selected Point pt1, pt2; // First we get the viewport number short vp = (short)Application.GetSystemVariable("CVPORT"); // Then the handle to the current drawing window IntPtr hWnd = doc.Window.Handle; // Now calculate the selected corners in screen coordinates pt1 = ScreenFromDrawingPoint(ed, hWnd, first, vp, true); pt2 = ScreenFromDrawingPoint(ed, hWnd, second, vp, true); // Now save this portion of our screen as a raster image if (!ad.clipboard) filename = GetFileName(ed); ScreenShotToFile(pt1, pt2, filename, ad); } } } // Iterate through a selection-set and get the overall extents // of the various objects relative to the screen // (this is imperfect: our extents in WCS may not translate to // the extents on the screen. A more thorough approach would be // to get a number of points from an object and check each) private static void GetExtentsOfSelection( Editor ed, Document doc, IntPtr hWnd, short vp, SelectionSet ss, out Point min, out Point max ) { // Create minimum and maximum points for the "on screen" // extents of our objects min = new Point(); max = new Point(); // Know which is the first pass through bool first = true; // Some variables to store transformation results Point pt1 = new Point(), pt2 = new Point(); Transaction tr = doc.TransactionManager.StartTransaction(); using (tr) { foreach (SelectedObject so in ss) { DBObject obj = tr.GetObject(so.ObjectId, OpenMode.ForRead); Entity ent = obj as Entity; if (ent != null) { // Get the WCS extents of each object Extents3d ext = ent.GeometricExtents; // Calculate the extent corners in screen coordinates // (this may not be the true screen extents, but we'll // hope it's good enough) pt1 = ScreenFromDrawingPoint( ed, hWnd, ext.MinPoint, vp, false ); pt2 = ScreenFromDrawingPoint( ed, hWnd, ext.MaxPoint, vp, false ); // The points may not be ordered, so get the min and max // values for both X and Y from both points int minX = Math.Min(pt1.X, pt2.X); int minY = Math.Min(pt1.Y, pt2.Y); int maxX = Math.Max(pt1.X, pt2.X); int maxY = Math.Max(pt1.Y, pt2.Y); // On the first run through, just get the points if (first) { min = new Point(minX, minY); max = new Point(maxX, maxY); first = false; } else { // On subsequent runs through, we need to compare if (minX < min.X) min.X = minX; if (minY < min.Y) min.Y = minY; if (maxX > max.X) max.X = maxX; if (maxY > max.Y) max.Y = maxY; } } } tr.Commit(); } } // Print the current application settings to the command-line private static void PrintSettings(Editor ed, AppData ad) { ed.WriteMessage( "/nCurrent settings: Output={0}, Background={1}, " + "Foreground={2}, Grayscale={3}", ad.clipboard ? "Clipboard" : "File", ad.whitebackground ? "ForceToWhite" : "Normal", ad.blackforeground ? "ForceToBlack" : "Normal", ad.grayscale ? "On" : "Off" ); } // Ask the user to modify the application settings private static bool GetSettings(Editor ed, AppData ad) { // At our top-level settings prompt, make the default // to exit back up PromptKeywordOptions pko = new PromptKeywordOptions( "/nSetting to change " + "[Output/Background/Foreground/Grayscale/Exit]: ", "Output Background Foreground Grayscale Exit" ); pko.Keywords.Default = "Exit"; PromptResult pr; bool settingschanged = false; do { // Start by printing the current settings PrintSettings(ed, ad); pr = ed.GetKeywords(pko); if (pr.Status == PromptStatus.OK) { if (pr.StringResult == "Output") { // If Output is selected, ask whether to put the // image on the clipboard or save to file PromptKeywordOptions pko2 = new PromptKeywordOptions( "/nSave to file or place on the clipboard " + "[File/Clipboard]: ", "File Clipboard" ); // The default depends on our current settings pko2.Keywords.Default = (ad.clipboard ? "Clipboard" : "File"); PromptResult pr2 = ed.GetKeywords(pko2); if (pr2.Status == PromptStatus.OK) { // Change the settings, as needed bool clipboard = (pr2.StringResult == "Clipboard"); if (ad.clipboard != clipboard) { ad.clipboard = clipboard; settingschanged = true; } } } else if (pr.StringResult == "Background") { // If Background is different, ask whether to // force the background colour to white // (we could allow selection of a colour, // but that's out of scope, for now) bool different = GetYesOrNo( ed, "/nForce background color to white", ad.whitebackground ); if (different) { ad.whitebackground = !ad.whitebackground; settingschanged = true; } } else if (pr.StringResult == "Foreground") { // If Foreground is different, ask whether to // force the foreground colour to black // (we could allow selection of a colour, // but that's out of scope, for now) bool different = GetYesOrNo( ed, "/nForce foreground color to black", ad.blackforeground ); if (different) { ad.blackforeground = !ad.blackforeground; settingschanged = true; } } else if (pr.StringResult == "Grayscale") { // If Grayscale is different, ask whether to // force the foreground pixels to be gray bool different = GetYesOrNo( ed, "/nConvert image to grayscale", ad.grayscale ); if (different) { ad.grayscale = !ad.grayscale; settingschanged = true; } } } } while( pr.Status == PromptStatus.OK && pr.StringResult != "Exit" ); // Loop until Exit or cancel return settingschanged; } // Ask the user to enter yes or no to a particular question, // setting the default option appropriately private static bool GetYesOrNo( Editor ed, string prompt, bool defval ) { bool changed = false; PromptKeywordOptions pko = new PromptKeywordOptions(prompt + " [Yes/No]: ", "Yes No"); // The default depends on our current settings pko.Keywords.Default = (defval ? "Yes" : "No"); PromptResult pr = ed.GetKeywords(pko); if (pr.Status == PromptStatus.OK) { // Change the settings, as needed bool newval = (pr.StringResult == "Yes"); if (defval != newval) { changed = true; } } return changed; } // Ask the user to select a location to save our file to private static string GetFileName(Editor ed) { string filename = ""; // The entries here will drive the behaviour of the // GetFormatForFile() function PromptSaveFileOptions pofo = new PromptSaveFileOptions( "/nSelect image location: " ); pofo.Filter = "Bitmap (*.bmp)|*.bmp|" + "GIF (*.gif)|*.gif|" + "JPEG (*.jpg)|*.jpg|" + "PNG (*.png)|*.png|" + "TIFF (*.tif)|*.tif"; PromptFileNameResult pfnr = ed.GetFileNameForSave(pofo); if (pfnr.Status == PromptStatus.OK) { filename = pfnr.StringResult; // If a file was selected, wait for some time to allow // the "file already exists" dialog to disappear // (100 msec = 1/10th of a second - may need tweaking) System.Threading.Thread.Sleep(100); } return filename; } // Perform our tranformations to get from UCS // (or WCS) to screen coordinates private static Point ScreenFromDrawingPoint( Editor ed, IntPtr hWnd, Point3d pt, short vpNum, bool useUcs ) { // Transform from UCS to WCS, if needed Point3d wcsPt = (useUcs ? pt.TransformBy(ed.CurrentUserCoordinateSystem) : pt ); // Then get the screen coordinates within the client // and translate these for the overall screen Point res = ed.PointToScreen(wcsPt, vpNum); ClientToScreen(hWnd, ref res); return res; } // Save the display of an AutoCAD window as a raster file // and/or an image on the clipboard private static void ScreenShotToFile( Autodesk.AutoCAD.Windows.Window wd, int top, int bottom, int left, int right, string filename, AppData ad ) { Point pt = wd.Location; Size sz = wd.Size; pt.X += left; pt.Y += top; sz.Height -= top + bottom; sz.Width -= left + right; SaveScreenPortion(pt, sz, filename, ad); } // Save a screen window between two corners as a raster file // and/or an image on the clipboard private static void ScreenShotToFile( Point pt1, Point pt2, string filename, AppData ad ) { // Create the top left corner from the two corners // provided (by taking the min of both X and Y values) Point pt = new Point(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)); // Determine the size by subtracting X & Y values and // taking the absolute value of each Size sz = new Size(Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y)); SaveScreenPortion(pt, sz, filename, ad); } // Save a portion of the screen display as a raster file // and/or an image on the clipboard private static void SaveScreenPortion( Point pt, Size sz, string filename, AppData ad ) { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; Manager gsm = doc.GraphicsManager; Transaction tr = db.TransactionManager.StartTransaction(); using (tr) { AcColorSettings ocs = new AcColorSettings(); ObjectId vtrId = ObjectId.Null, sbId = ObjectId.Null; bool in3DView = is3D(gsm); if (ad.whitebackground) { if (in3DView) { Set3DBackground( ed, db, tr, new EntityColor(255, 255, 255), out vtrId, out sbId ); } else { // Get the current system colours acedGetCurrentColors(out ocs); // Take a copy - we'll leave the original to reset // the values later on, once we've finished AcColorSettings cs = ocs; // Make both background colours white (the 3D // background isn't currently being picked up) cs.dwGfxModelBkColor = 16777215; cs.dwGfxLayoutBkColor = 16777215; //cs.dwParallelBkColor = 16777215; // Set the modified colours acedSetCurrentColors(ref cs); ed.Regen(); } // Update the screen to reflect the changes ed.UpdateScreen(); } // Set the bitmap object to the size of the window Bitmap bmp = new Bitmap( sz.Width, sz.Height, PixelFormat.Format32bppArgb ); using (bmp) { // Create a graphics object from the bitmap using (Graphics gfx = Graphics.FromImage(bmp)) { // Take a screenshot of our window gfx.CopyFromScreen( pt.X, pt.Y, 0, 0, sz, CopyPixelOperation.SourceCopy ); Bitmap processed; if (ad.blackforeground || ad.grayscale) { System.Drawing.Color col; if (in3DView) { if (ad.whitebackground) { col = System.Drawing.Color.White; } else { acedGetCurrentColors(out ocs); uint bgcol = ocs.dwParallelBkColor; col = System.Drawing.Color.FromArgb((int)bgcol); } } else { bool inModelspace = ((short)Application.GetSystemVariable( "TILEMODE" ) == 0); uint bgcol = (inModelspace ? ocs.dwGfxModelBkColor : ocs.dwGfxLayoutBkColor ); col = System.Drawing.Color.FromArgb((int)bgcol); } processed = ConvertToGrayscale( bmp, col, ad.blackforeground, System.Drawing.Color.Black ); } else { processed = bmp; } // Save the screenshot to the specified location if (filename != null && filename != "") { processed.Save(filename, GetFormatForFile(filename)); ed.WriteMessage( "/nImage captured and saved to /"{0}/".", filename ); } // Copy it to the clipboard if (ad.clipboard) { System.Windows.Forms.Clipboard.SetImage(processed); ed.WriteMessage( "/nImage captured to the clipboard." ); } } } if (ad.whitebackground) { if (vtrId != ObjectId.Null || sbId != ObjectId.Null) { Remove3DBackground(db, tr, vtrId, sbId); } else { acedSetCurrentColors(ref ocs); ed.Regen(); } ed.UpdateScreen(); } tr.Commit(); } } // Check whether the active viewport is 3D private static bool is3D(Manager gsm) { short vp = (short)Application.GetSystemVariable("CVPORT"); View v = gsm.GetGsView(vp, false); using (v) { return (v != null); } } // Return the image format to use for a particular filename private static ImageFormat GetFormatForFile(string filename) { // If all else fails, let's create a PNG // (might also choose to throw an exception) ImageFormat imf = ImageFormat.Png; if (filename.Contains(".")) { // Get the filename's extension (what follows the last ".") string ext = filename.Substring(filename.LastIndexOf(".") + 1); // Get the first three characters of the extension if (ext.Length > 3) ext = ext.Substring(0, 3); // Choose the format based on the extension (in lowercase) switch (ext.ToLower()) { case "bmp": imf = ImageFormat.Bmp; break; case "gif": imf = ImageFormat.Gif; break; case "jpg": imf = ImageFormat.Jpeg; break; case "tif": imf = ImageFormat.Tiff; break; case "wmf": imf = ImageFormat.Wmf; break; default: imf = ImageFormat.Png; break; } } return imf; } // Set the background colour of a 3D view private static void Set3DBackground( Editor ed, Database db, Transaction tr, EntityColor ec, out ObjectId vtrId, out ObjectId sbId ) { // We're be returning IDs of the Viewport Table Record // and of the background itself vtrId = ObjectId.Null; sbId = ObjectId.Null; ed.UpdateTiledViewportsInDatabase(); ViewportTable vt = (ViewportTable)tr.GetObject( db.ViewportTableId, OpenMode.ForRead ); if (vt.Has("*Active")) { // Let's get the Viewport Table Record vtrId = vt["*Active"]; DBDictionary nod = (DBDictionary)tr.GetObject( db.NamedObjectsDictionaryId, OpenMode.ForRead ); // And create the background dictionary, if none exists ObjectId bkdId = ObjectId.Null; DBDictionary bkDict = null; const string dictKey = "ACAD_BACKGROUND"; const string bkKey = "MYBACKGROUND"; if (nod.Contains(dictKey)) { bkdId = nod.GetAt(dictKey); bkDict = (DBDictionary)tr.GetObject(bkdId, OpenMode.ForWrite); } else { bkDict = new DBDictionary(); nod.UpgradeOpen(); bkdId = nod.SetAt(dictKey, bkDict); tr.AddNewlyCreatedDBObject(bkDict, true); } // Get or create our background object if (bkDict.Contains(bkKey)) { sbId = bkDict.GetAt(bkKey); } else { SolidBackground sb = new SolidBackground(); sb.Color = ec; sbId = bkDict.SetAt(bkKey, sb); tr.AddNewlyCreatedDBObject(sb, true); } // And set it to the viewport ViewportTableRecord vtr = (ViewportTableRecord)tr.GetObject( vtrId, OpenMode.ForWrite ); vtr.Background = sbId; } } // Remove the previously set 3D background colour private static void Remove3DBackground( Database db, Transaction tr, ObjectId vtrId, ObjectId sbId ) { // First remove it from the viewport if (vtrId != ObjectId.Null) { ViewportTableRecord vtr = (ViewportTableRecord)tr.GetObject( vtrId, OpenMode.ForWrite ); vtr.Background = ObjectId.Null; } // And then erase the object itself (although // I suspect this is redundant) if (sbId != ObjectId.Null) { SolidBackground sb = (SolidBackground)tr.GetObject( sbId, OpenMode.ForRead, true ); if (!sb.IsErased) { sb.UpgradeOpen(); sb.Erase(); } } } // Return a grayscale version of a provided bitmap, // with the option of forcing non-background pixels to // be black public static Bitmap ConvertToGrayscale( Bitmap src, System.Drawing.Color bgcol, bool force, System.Drawing.Color fgcol ) { // From http://www.bobpowell.net/grayscale.htm Bitmap bmp = new Bitmap(src.Width, src.Height); for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { System.Drawing.Color c = src.GetPixel(x, y); int lum = (force && !SameColors(c, bgcol) ? 0 : (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11) // 0.299R + 0.587G + 0.114B ); bmp.SetPixel( x, y, (lum == 0 ? fgcol : System.Drawing.Color.FromArgb(lum, lum, lum)) ); } } return bmp; } // Return whether two colour can be considered equivalent // in terms of RGB values private static bool SameColors( System.Drawing.Color a, System.Drawing.Color b ) { // Ignore Alpha channel, just compare RGB return (a.R == b.R && a.G == b.G && a.B == b.B); } } }

To get a feel for the command-line prompts, here's the code in action, with the user-inputs in red:

Command: SCREENSHOT

Current settings: Output=Clipboard, Background=Normal

Select first point of capture window or [Document/Application/Objects/Settings]: S

Current settings: Output=Clipboard, Background=Normal

Setting to change [Output/Background/Exit] : O

Save to file or place on the clipboard [File/Clipboard] : F

Current settings: Output=File, Background=Normal

Setting to change [Output/Background/Exit] : B

Force background color to white [Yes/No] : Y

Current settings: Output=File, Background=ForceToWhite

Setting to change [Output/Background/Exit] :

Current settings: Output=File, Background=ForceToWhite

Select first point of capture window or [Document/Application/Objects/Settings]: D

Regenerating model.

Regenerating model.

Command:

If you get the chance, please give the code a try and let me know what more you'd like to see the SCREENSHOT command do.

Here are the results of running SCREENSHOT with various options, starting with a black drawing background (which is quite common-place):

AutoCAD中的ScreenShot工具_第1张图片

   

Background = Normal

Foreground = Normal

Grayscale = Off

AutoCAD中的ScreenShot工具_第2张图片

   

Background = ForceToWhite

Foreground = Normal

Grayscale = Off 

AutoCAD中的ScreenShot工具_第3张图片

   

Background = ForceToWhite

Foreground = ForceToBlack

Grayscale = On/Off 

AutoCAD中的ScreenShot工具_第4张图片

   

Background = ForceToWhite

Foreground = Normal

Grayscale = On 

AutoCAD中的ScreenShot工具_第5张图片

   

Background = Normal

Foreground = Normal

Grayscale = On 

AutoCAD中的ScreenShot工具_第6张图片

   

Background = Normal

Foreground = ForceToBlack

Grayscale = On/Off  

The last one is clearly less than useful, but it's there for completeness. :-)

Just to show it works, here are a few images taken in a 3D view (shown in the same sequence as the above 2D images):

AutoCAD中的ScreenShot工具_第7张图片AutoCAD中的ScreenShot工具_第8张图片AutoCAD中的ScreenShot工具_第9张图片AutoCAD中的ScreenShot工具_第10张图片AutoCAD中的ScreenShot工具_第11张图片AutoCAD中的ScreenShot工具_第12张图片

The clipping of the 3D window when using the "Document" option seems slightly different in 3D (as you can see from the additional line at the bottom of the screen), which is something I'll address, at some point.

Anyway, please do keep the feedback coming (although as I'm on vacation – and away from a PC - for the rest of the week you may not see me respond until next week).

你可能感兴趣的:(AutoCAD,.NET,酷酷的工具)