August 2014

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            








« Free AutoCAD API webcast: 0 to 3D in 60 Minutes | Main | Free webcast on AutoCAD VBA to .NET Migration »

October 12, 2009

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

    }

  }

}

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

Standard

 

Background = Normal

Foreground = Normal

Grayscale = Off

 

 

White

 

Background = ForceToWhite

Foreground = Normal

Grayscale = Off

 

BlackAndWhite

 

Background = ForceToWhite

Foreground = ForceToBlack

Grayscale = On/Off

 

Grayscale

 

Background = ForceToWhite

Foreground = Normal

Grayscale = On

 

BlackAndGray

 

Background = Normal

Foreground = Normal

Grayscale = On

 

Black

 

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

Standard - 3D White - 3D BlackAndWhite - 3D GrayAndWhite - 3D Grayscale - 3D BlackAndGray - 3D

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

blog comments powered by Disqus

Feed/Share

10 Random Posts