- 积分
- 24557
- 明经币
- 个
- 注册时间
- 2004-3-17
- 在线时间
- 小时
- 威望
-
- 金钱
- 个
- 贡献
-
- 激情
-
|
楼主 |
发表于 2010-2-3 22:30:00
|
显示全部楼层
二、加强版本的截图
http://through-the-interface.typepad.com/through_the_interface/2009/11/novembers-plugin-of-the-month-screenshot.html
November 16, 2009
Updated version of Screenshot now available
We’ve had a few reports of issues with the Screenshot “Plugin of the Month”. They fall into two main categories:
Attempting to NETLOAD the application DLL from a network share
Within the ReadMe for each of the plugins we’ve documented that each application’s DLL module should be copied to the local file system – preferably inside the AutoCAD Program Files folder – before being loaded by NETLOAD. We recommend this because it essentially stops users from hitting a whole category of .NET Framework-related problems when loading and running the plugins.
If you didn’t heed this advice then you’d probably find that, as soon as the SCREENSHOT command was launched, you received a message such as “FATAL ERROR: Unsupported version of Windows Presentation Foundation.”
Now I don’t have an exhaustive list of reasons it’s best to place .NET DLLs in the AutoCAD root program folder, but my understanding/belief is that it’s down to two main ones:
1. Security
The .NET Framework implements security for different zones, and – up until the .NET Framework 3.5 SP1 – the default security setting for the “Local Intranet” (which affects applications being loaded from network shares) was “Medium Trust”. This level of trust means:
Programs might not be able to access most protected resources such as the registry or security policy settings, or access your local file system without user interaction. Programs will not be able to connect back to their site of origin, resolve domain names, and use all windowing resources.
Our “Plugin of the Month” applications target lower versions of the .NET Framework (we want to avoid forcing people to use the latest version of the framework and want the applications to run – where possible – at least as far back as AutoCAD 2007), and so their default level of trust will make running from a network share a problem.
Now it’s possible to use the Control Panel to configure earlier versions of the .NET Framework to be more tolerant of network-resident DLLs (you can change the trust level for the local intranet zone to be higher), but it’s still not something I’d recommend: I’m pretty sure this is only one aspect of the situation, and it would be dangerous to assume it’s all that’s needed.
2. Assembly Loading
The other main reason for putting modules in AutoCAD’s root program folder is related to the loading of .NET assemblies into AutoCAD’s AppDomain (which is basically what the NETLOAD command does, and this choice of architecture is why there’s no NETUNLOAD command).
While NETLOAD uses Assembly.LoadFrom() to load in .NET assembly DLLs – which does allow you to specify a path other than the current folder – there does appear to be some fragility overall with the location of assemblies and how they reference each other. It’s safest to place assemblies at a location beneath the calling executable (i.e. AutoCAD).
Capturing images with the “force foreground to black” option results in a completely black image
This one is definitely down to me. A big thank you to Harry Kortekaas for very diligently helping me identify the problem.
It actually came down to some poor application logic on my side: I was forgetting to check the “use white background” flag at the right place, and had also inverted the test for being in modelspace vs. paperspace. By chance – in many situations – the white background was picked up correctly from the paperspace, and so the issue wasn’t easily reproducible.
Anyway, a fix has now been integrated into version 1.0.2 of the application, which is now downloadable via Autodesk Labs. I’ve also included an updated version of the code below.
A third type of issue has been reported, but I haven’t yet been able to determine the cause: on one system (meaning: one reported, so far) the file selection dialog and the dynamic input graphics are not being refreshed away in time for the capture to take place.
This is similar to something we’ve been aware of from early on - the fact that the call to Editor.GetFileNameForSave() returns control to the application before the Operating System has had the chance to refresh the screen – so I built a delay (a call to System.Threading.Thread.Sleep()) into the original application to wait for a second (we started at 0.1s and worked our way upwards) which seemed to address it for all the systems upon which the application was tested.
At first it seemed – during the diagnosis of this particular issue – that this delay needed increasing, and so I added it to the per-user application settings (with the default value of 1.0, i.e. a second). This is now configurable via a new command – CONFIGSS – the logic being to keep rarely-used configuration options apart from the common ones accessible via the SCREENSHOT command. In this particular situation, though, no amount of delay seemed to help.
The same person reported an issue with the input box and temporary dimensions displayed during dynamic input not being repainted away on this particular system when the image is placed on the clipboard, so I’ve also introduced a configurable delay there, too. This is also set using CONFIGSS – the default is currently 0 seconds, to replicate the previous behaviour.
I suspect that both issues are actually down to a configuration problem (we’ll hopefully see whether my suspicion is valid), but I’ve left the capability to configure them in the application, in any case. People may also choose to reduce the current delay using CONFIGSS, as one second was on the high end: 0.3 seconds should be enough for the majority of systems. Just think of all the time you can get back! :-)
If anyone else out there has seen something similar to this with the Screenshot application, please do let us know.
The CONFIGSS command has a third setting exposed (although it’s the first one it prompts for), and this was also introduced after receiving feedback from the same source: it’s sometimes preferred to have a boundary zone or buffer around the extents chosen via the “Objects” option, so that the selection isn’t so tight-fitting around the objects. The application now has a default of 10% of the screen extents’ width or height (whichever is larger) that will get added automatically. To go back to the old behaviour you can configure this percentage to zero using CONFIGSS.
Here’s the updated C# code: the compiled version of which (again, version 1.0.2) is now available:- // (C) Copyright 2009 by Autodesk, Inc.
- //
- // Permission to use, copy, modify, and distribute this software in
- // object code form for any purpose and without fee is hereby granted,
- // provided that the above copyright notice appears in all copies and
- // that both that copyright notice and the limited warranty and
- // restricted rights notice below appear in all supporting
- // documentation.
- //
- // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
- // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
- // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
- // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
- // UNINTERRUPTED OR ERROR FREE.
- //
- // Use, duplication, or disclosure by the U.S. Government is subject to
- // restrictions set forth in FAR 52.227-19 (Commercial Computer
- // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
- // (Rights in Technical Data and Computer Software), as applicable.
- //
-
- 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.Printing;
- using System.Drawing.Drawing2D;
- using System.Drawing;
- using System.Runtime.InteropServices;
- using System.Collections;
- using System.Configuration;
- using System;
-
- using DemandLoading;
-
- namespace Screenshot
- {
- public class ScreenshotApplication : IExtensionApplication
- {
- // Define a class for our custom data
-
- public class AppData : ApplicationSettingsBase
- {
- [UserScopedSetting()]
- [DefaultSettingValue("true")]
- public bool Clipboard
- {
- get { return ((bool)this["Clipboard"]); }
- set { this["Clipboard"] = (bool)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("false")]
- public bool Print
- {
- get { return ((bool)this["Print"]); }
- set { this["Print"] = (bool)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("false")]
- public bool WhiteBackground
- {
- get { return ((bool)this["WhiteBackground"]); }
- set { this["WhiteBackground"] = (bool)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("false")]
- public bool BlackForeground
- {
- get { return ((bool)this["BlackForeground"]); }
- set { this["BlackForeground"] = (bool)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("false")]
- public bool Grayscale
- {
- get { return ((bool)this["Grayscale"]); }
- set { this["Grayscale"] = (bool)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("0.1")]
- public double ExtentsScale
- {
- get { return ((double)this["ExtentsScale"]); }
- set { this["ExtentsScale"] = (double)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("1.0")]
- public double FileCaptureDelay
- {
- get { return ((double)this["FileCaptureDelay"]); }
- set { this["FileCaptureDelay"] = (double)value; }
- }
- [UserScopedSetting()]
- [DefaultSettingValue("0.0")]
- public double ClipboardCaptureDelay
- {
- get { return ((double)this["ClipboardCaptureDelay"]); }
- set { this["ClipboardCaptureDelay"] = (double)value; }
- }
- }
-
- // 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 acedGetCurrentColors32(
- out AcColorSettings colorSettings
- );
-
- [DllImport("acad.exe",
- CallingConvention = CallingConvention.Cdecl,
- EntryPoint = "?acedSetCurrentColors@@YAHPAUAcColorSettings@@@Z"
- )]
- static extern bool acedSetCurrentColors32(
- ref AcColorSettings colorSettings
- );
-
- // 64-bit versions of these functions...
-
- [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
- );
-
- // Helper functions that call automatically to 32- or 64-bit
- // versions, as appropriate
-
- static bool acedGetCurrentColors(
- out AcColorSettings colorSettings
- )
- {
- if (IntPtr.Size > 4)
- return acedGetCurrentColors64(out colorSettings);
- else
- return acedGetCurrentColors32(out colorSettings);
- }
-
- static bool acedSetCurrentColors(
- ref AcColorSettings colorSettings
- )
- {
- if (IntPtr.Size > 4)
- return acedSetCurrentColors64(ref colorSettings);
- else
- return acedSetCurrentColors32(ref colorSettings);
- }
-
- // IExtensionApplication protocol
-
- public void Initialize()
- {
- try
- {
- RegistryUpdate.RegisterForDemandLoading();
- }
- catch
- { }
- }
-
- public void Terminate()
- {
- }
-
- [CommandMethod("ADNPLUGINS", "REMOVESS", CommandFlags.Modal)]
- static public void RemoveScreenshot()
- {
- RegistryUpdate.UnregisterForDemandLoading();
- }
-
- [CommandMethod("ADNPLUGINS", "CONFIGSS", CommandFlags.Modal)]
- static public void ConfigureScreenshot()
- {
- // An additional command for some "advanced" configuration
- // options
-
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
-
- // Retrieve our application settings (or create new ones)
-
- AppData ad = new AppData();
- ad.Reload();
-
- if (ad != null)
- {
- // Ask the user for the percentage increase to apply to
- // the extents determined by the Objects option
-
- PromptIntegerOptions pio =
- new PromptIntegerOptions(
- "\nPercentage increase when capturing " +
- "object extents: "
- );
- pio.DefaultValue = (int)(ad.ExtentsScale * 100);
- pio.LowerLimit = 0;
- pio.UpperLimit = 100;
- pio.UseDefaultValue = true;
-
- PromptIntegerResult pir = ed.GetInteger(pio);
- if (pir.Status != PromptStatus.OK)
- return;
-
- ad.ExtentsScale = pir.Value * 0.01;
-
- // Ask the use for the delay to apply after a file
- // has been selected, to allow the OS to redraw their
- // screen graphics
-
- PromptDoubleOptions pdo =
- new PromptDoubleOptions(
- "\nDelay in seconds to allow " +
- "repaint after file selection: "
- );
- pdo.DefaultValue = ad.FileCaptureDelay;
- pdo.AllowNegative = false;
- pdo.UseDefaultValue = true;
-
- PromptDoubleResult pdr = ed.GetDouble(pdo);
- if (pdr.Status != PromptStatus.OK)
- return;
-
- ad.FileCaptureDelay = pdr.Value;
-
- // Ask the user for the delay to apply before a
- // clipboard capture to allow any dynamic
- // input graphics to be undrawn
-
- pdo.Message =
- "\nDelay in seconds to allow " +
- "repaint before clipboard selection: ";
- pdo.DefaultValue = ad.ClipboardCaptureDelay;
-
- pdr = ed.GetDouble(pdo);
- if (pdr.Status != PromptStatus.OK)
- return;
-
- ad.ClipboardCaptureDelay = pdr.Value;
-
- ad.Save();
- }
- }
-
- // Command to capture the main and active drawing windows
- // or a user-selected portion of a drawing
-
- [CommandMethod("ADNPLUGINS", "SCREENSHOT", CommandFlags.Modal)]
- static public void CaptureScreenShot()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
-
- // Retrieve our application settings (or create new ones)
-
- AppData ad = new AppData();
- ad.Reload();
-
- 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
-
- filename = PauseOrFilename(ed, ad);
-
- ScreenShotToFile(
- Application.DocumentManager.
- MdiActiveDocument.Window,
- 30, 26, 10, 10,
- filename,
- ad
- );
- }
- else if (ppr.StringResult == "Application")
- {
- // Capture the entire application window
-
- filename = PauseOrFilename(ed, ad);
-
- 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();
-
- if (psr.Status == PromptStatus.OK)
- {
- // Regenerate to clear any selection highlighting
-
- ed.WriteMessage("\n");
- ed.Regen();
-
- // 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
- );
-
- ApplyScaleToExtents(ad.ExtentsScale,ref pt1,ref pt2);
-
- // Now save this portion of our screen as a raster
- // image
-
- filename = PauseOrFilename(ed, ad);
-
- ScreenShotToFile(pt1, pt2, filename, ad);
- }
- }
- else if (ppr.StringResult == "Settings")
- {
- if (GetSettings(ed, ad))
- ad.Save();
- 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
-
- filename = PauseOrFilename(ed, ad);
-
- ScreenShotToFile(pt1, pt2, filename, ad);
- }
- }
- }
-
- // If using the clipboard let's introduce a pause to allow
- // any dynamic input graphics to be refreshed away.
- // Otherwise get the filename (which will have its own delay)
-
- private static string PauseOrFilename(Editor ed, AppData ad)
- {
- if (ad.Clipboard)
- {
- ed.UpdateScreen();
- System.Threading.Thread.Sleep(
- (int)(1000 * ad.ClipboardCaptureDelay)
- );
- return "";
- }
- else
- return GetFileName(ed, 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();
- }
- }
-
- // Apply a scale to the supplied extents
- // (provided as a potion of the extents, i.e. 10% == 0.1)
-
- private static void ApplyScaleToExtents(
- double factor,
- ref Point pt1,
- ref Point pt2
- )
- {
- // Get the width and height of the selected screen area
-
- int width = Math.Abs(pt2.X - pt1.X);
- int height = Math.Abs(pt2.Y - pt1.Y);
-
- // Decide what the (uniform) border should be: use
- // a portion of either the width or the height,
- // whichever's larger
-
- int border =
- (int)(factor * (width > height ? width : height));
-
- // Adjust the two screen points by this border amount
-
- if (pt1.X < pt2.X)
- {
- pt1.X -= border;
- pt2.X += border;
- }
- else
- {
- pt1.X += border;
- pt2.X -= border;
- }
-
- if (pt1.Y < pt2.Y)
- {
- pt1.Y -= border;
- pt2.Y += border;
- }
- else
- {
- pt1.Y += border;
- pt2.Y -= border;
- }
- }
-
- // Print the current application settings to the command-line
-
- private static void PrintSettings(Editor ed, AppData ad)
- {
- ed.WriteMessage(
- "\nCurrent settings: Output={0}, Print={1}, " +
- "Background={2}, Foreground={3}, Grayscale={4}",
- ad.Clipboard ? "Clipboard" : "File",
- ad.Print ? "Yes" : "No",
- 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/Print/Background/Foreground/Grayscale/Exit]: ",
- "Output Print 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 == "Print")
- {
- // If Print is different, ask whether to
- // send the image to the printer
-
- bool different =
- GetYesOrNo(
- ed,
- "\nSend image to printer once captured",
- ad.Print
- );
- if (different)
- {
- ad.Print = !ad.Print;
- 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, AppData ad)
- {
- 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";
-
- // Set the default save location to be the current drawing
-
- string fn = ed.Document.Database.Filename;
- if (fn.Contains("."))
- {
- int extIdx = fn.LastIndexOf(".");
- if (fn.Substring(extIdx + 1) != "dwt" &&
- fn.Contains("\"))
- {
- int sepIdx = fn.LastIndexOf("\");
- pofo.InitialDirectory =
- fn.Substring(0, sepIdx);
- }
- }
-
- 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.
- // The delay is now a configurable option (in seconds)
-
- ed.UpdateScreen();
-
- System.Threading.Thread.Sleep(
- (int)(1000 * ad.FileCaptureDelay)
- );
- }
-
- 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();
- bool gotSettings = false;
-
- ObjectId vtrId = ObjectId.Null,
- sbId = ObjectId.Null;
-
- bool in3DView = is3D(gsm);
- bool regened = false;
-
- 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);
- gotSettings = true;
-
- // 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.WriteMessage("\n");
- ed.Regen();
- regened = true;
- }
- // 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)
- {
- // If we're needing to convert to a grayscale,
- // whether to create a black foreground or a
- // true grayscale image, we need to find the
- // background colour
-
- System.Drawing.Color col;
-
- // If we remapped it to white, it's easy
-
- if (ad.WhiteBackground)
- {
- col = System.Drawing.Color.White;
- }
- else
- {
- // If not, then it may be more complicated
-
- if (!gotSettings)
- {
- acedGetCurrentColors(out ocs);
- gotSettings = true;
- }
-
- // Get the 3D view background colour, if appropriate
-
- if (in3DView)
- {
- uint bgcol = ocs.dwParallelBkColor;
- col =
- System.Drawing.Color.FromArgb((int)bgcol);
- }
- else
- {
- // If a 2D view, we need to find out whether in
- // modelspace or paperspace, and then get the
- // appropriate colour for its background
-
- bool inModelspace =
- ((short)Application.GetSystemVariable(
- "TILEMODE"
- ) == 1);
- uint bgcol =
- (inModelspace
- ? ocs.dwGfxModelBkColor
- : ocs.dwGfxLayoutBkColor
- );
- col =
- System.Drawing.Color.FromArgb((int)bgcol);
- }
- }
-
- // Run the conversion to a grayscale
-
- processed =
- ConvertToGrayscale(
- bmp,
- col,
- ad.BlackForeground,
- System.Drawing.Color.Black
- );
- }
- else
- {
- processed = bmp;
- }
-
- // Take a copy of the bitmap for printing
-
- using (Bitmap toPrint = processed)
- {
- // Save the screenshot to the specified location
-
- if (!regened)
- ed.WriteMessage("\n");
-
- if (filename != null && filename != "")
- {
- processed.Save(filename, GetFormatForFile(filename));
-
- ed.WriteMessage(
- "Image captured and saved to "{0}".",
- filename
- );
- }
-
- // Copy it to the clipboard
-
- if (ad.Clipboard)
- {
- System.Windows.Forms.Clipboard.SetImage(processed);
- ed.WriteMessage(
- "Image captured to the clipboard."
- );
- }
-
- // Send it to a printer
-
- if (ad.Print)
- {
- PrintDocument pdoc = new PrintDocument();
- pdoc.PrintPage +=
- delegate(object sender, PrintPageEventArgs e)
- {
- int wid = toPrint.Width,
- hgt = toPrint.Height;
-
- // Store the ratio between width and height
-
- double ratio = (double)wid / (double)hgt;
-
- // If the image's width isn't the
- // same as the page...
-
- if (wid != e.MarginBounds.Width)
- {
- // Change the width to fit the page
-
- wid = e.MarginBounds.Width;
-
- // Adjust the height to maintain scale
- // (even if bigger than the page height)
-
- hgt = (int)(wid / ratio);
- }
-
- // If the image's height is bigger than the
- // page...
-
- if (hgt > e.MarginBounds.Height)
- {
- // Change the height to fit the paper
-
- hgt = e.MarginBounds.Height;
-
- // Adjust the width to maintain scale
-
- wid = (int)(ratio * hgt);
- }
-
- // Set the interpolation settings to high
- // quality
-
- e.Graphics.InterpolationMode =
- InterpolationMode.HighQualityBicubic;
-
- // And send the image out to the page
-
- e.Graphics.DrawImage(
- toPrint,
- e.MarginBounds.X,
- e.MarginBounds.Y,
- wid,
- hgt
- );
- };
-
- // Create and show the print dialog
-
- System.Windows.Forms.PrintDialog pdlg =
- new System.Windows.Forms.PrintDialog();
- pdlg.Document = pdoc;
-
- if (pdlg.ShowDialog() ==
- System.Windows.Forms.DialogResult.OK)
- pdoc.Print(); // Print on OK
- }
- }
- }
- }
- if (ad.WhiteBackground)
- {
- if (vtrId != ObjectId.Null || sbId != ObjectId.Null)
- {
- Remove3DBackground(db, tr, vtrId, sbId);
- }
- else
- {
- if (gotSettings)
- {
- acedSetCurrentColors(ref ocs);
- ed.WriteMessage("\n");
- 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 = "ADNPlugin_Screenshot";
-
- 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
-
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
-
- 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);
- }
- }
- }
|
|