Exemple #1
0
        public void Start(int framesPerSecond)
        {
            string filename;

            if (recordingWindow != null)
            {
                string windowTitle = Regex.Replace(recordingWindow.Text, @"[^\x20\d\w]", "");
                if (string.IsNullOrEmpty(windowTitle))
                {
                    windowTitle = "greenshot-recording";
                }
                filename = Path.Combine(conf.OutputFilePath, windowTitle + ".avi");
            }
            else
            {
                filename = Path.Combine(conf.OutputFilePath, "greenshot-recording.avi");
            }
            if (File.Exists(filename))
            {
                try {
                    File.Delete(filename);
                } catch {}
            }
            LOG.InfoFormat("Capturing to {0}", filename);

            if (recordingWindow != null)
            {
                LOG.InfoFormat("Starting recording Window '{0}', {1}", recordingWindow.Text, recordingWindow.ClientRectangle);
                recordingSize = recordingWindow.ClientRectangle.Size;
            }
            else
            {
                LOG.InfoFormat("Starting recording rectangle {0}", recordingRectangle);
                recordingSize = recordingRectangle.Size;
            }
            if (recordingSize.Width % 8 > 0)
            {
                LOG.InfoFormat("Correcting width to be factor 8, {0} => {1}", recordingSize.Width, recordingSize.Width + (8 - (recordingSize.Width % 8)));
                recordingSize = new Size(recordingSize.Width + (8 - (recordingSize.Width % 8)), recordingSize.Height);
            }
            if (recordingSize.Height % 8 > 0)
            {
                LOG.InfoFormat("Correcting Height to be factor 8, {0} => {1}", recordingSize.Height, recordingSize.Height + (8 - (recordingSize.Height % 8)));
                recordingSize = new Size(recordingSize.Width, recordingSize.Height + (8 - (recordingSize.Height % 8)));
            }
            this.framesPerSecond = framesPerSecond;
            // "P/Invoke" Solution for capturing the screen
            hWndDesktop = User32.GetDesktopWindow();
            // get te hDC of the target window
            hDCDesktop = User32.GetWindowDC(hWndDesktop);
            // Make sure the last error is set to 0
            Win32.SetLastError(0);

            // create a device context we can copy to
            hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);
            // Check if the device context is there, if not throw an error with as much info as possible!
            if (hDCDest == IntPtr.Zero)
            {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", recordingSize);
                // Cleanup
                User32.ReleaseDC(hWndDesktop, hDCDesktop);
                // throw exception
                throw exceptionToThrow;
            }

            // Create BitmapInfoHeader for CreateDIBSection
            BitmapInfoHeader bitmapInfoHeader = new BitmapInfoHeader(recordingSize.Width, recordingSize.Height, 32);

            // Make sure the last error is set to 0
            Win32.SetLastError(0);

            // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
            hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bitmapInfoHeader, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);

            if (hDIBSection == IntPtr.Zero)
            {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", recordingSize);
                exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
                exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());

                // clean up
                GDI32.DeleteDC(hDCDest);
                User32.ReleaseDC(hWndDesktop, hDCDesktop);

                // Throw so people can report the problem
                throw exceptionToThrow;
            }
            // Create a GDI Bitmap so we can use GDI and GDI+ operations on the same memory
            GDIBitmap = new Bitmap(recordingSize.Width, recordingSize.Height, 32, PixelFormat.Format32bppArgb, bits0);
            // select the bitmap object and store the old handle
            hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);
            stop       = false;

            aviWriter = new AVIWriter();
            // Comment the following 2 lines to make the user select it's own codec
            //aviWriter.Codec = "msvc";
            //aviWriter.Quality = 99;

            aviWriter.FrameRate = framesPerSecond;
            aviWriter.Open(filename, recordingSize.Width, recordingSize.Height);

            // Start update check in the background
            backgroundTask = new Thread(new ThreadStart(CaptureFrame));
            backgroundTask.IsBackground = true;
            backgroundTask.Start();
        }
Exemple #2
0
        /// <summary>
        /// This method will use User32 code to capture the specified captureBounds from the screen
        /// </summary>
        /// <param name="captureBounds">Rectangle with the bounds to capture</param>
        /// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns>
        public static Bitmap CaptureRectangle(Rectangle captureBounds)
        {
            Bitmap returnBitmap = null;

            if (captureBounds.Height <= 0 || captureBounds.Width <= 0)
            {
                LOG.Warn("Nothing to capture, ignoring!");
                return(null);
            }
            else
            {
                LOG.Debug("CaptureRectangle Called!");
            }

            // .NET GDI+ Solution, according to some post this has a GDI+ leak...
            // See http://connect.microsoft.com/VisualStudio/feedback/details/344752/gdi-object-leak-when-calling-graphics-copyfromscreen
            // Bitmap capturedBitmap = new Bitmap(captureBounds.Width, captureBounds.Height);
            // using (Graphics graphics = Graphics.FromImage(capturedBitmap)) {
            //	graphics.CopyFromScreen(captureBounds.Location, Point.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt);
            // }
            // capture.Image = capturedBitmap;
            // capture.Location = captureBounds.Location;

            // "P/Invoke" Solution for capturing the screen
            IntPtr hWndDesktop = User32.GetDesktopWindow();
            // get te hDC of the target window
            IntPtr hDCDesktop = User32.GetWindowDC(hWndDesktop);

            // Make sure the last error is set to 0
            Win32.SetLastError(0);

            // create a device context we can copy to
            IntPtr hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);

            // Check if the device context is there, if not throw an error with as much info as possible!
            if (hDCDest == IntPtr.Zero)
            {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
                // Cleanup
                User32.ReleaseDC(hWndDesktop, hDCDesktop);
                // throw exception
                throw exceptionToThrow;
            }

            // Create BitmapInfoHeader for CreateDIBSection
            BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24);

            // Make sure the last error is set to 0
            Win32.SetLastError(0);

            // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
            IntPtr bits0;             // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
            IntPtr hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);

            if (hDIBSection == IntPtr.Zero)
            {
                // Get Exception before the error is lost
                Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds);
                exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
                exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());

                // clean up
                GDI32.DeleteDC(hDCDest);
                User32.ReleaseDC(hWndDesktop, hDCDesktop);

                // Throw so people can report the problem
                throw exceptionToThrow;
            }
            else
            {
                // select the bitmap object and store the old handle
                IntPtr hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);

                // bitblt over (make copy)
                GDI32.BitBlt(hDCDest, 0, 0, captureBounds.Width, captureBounds.Height, hDCDesktop, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);

                // restore selection (old handle)
                GDI32.SelectObject(hDCDest, hOldObject);
                // clean up
                GDI32.DeleteDC(hDCDest);
                User32.ReleaseDC(hWndDesktop, hDCDesktop);

                // get a .NET image object for it
                // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try...
                bool success = false;
                ExternalException exception = null;
                for (int i = 0; i < 3; i++)
                {
                    try {
                        // assign image to Capture, the image will be disposed there..
                        returnBitmap = Bitmap.FromHbitmap(hDIBSection);
                        success      = true;
                        break;
                    } catch (ExternalException ee) {
                        LOG.Warn("Problem getting bitmap at try " + i + " : ", ee);
                        exception = ee;
                    }
                }
                if (!success)
                {
                    LOG.Error("Still couldn't create Bitmap!");
                    throw exception;
                }
                // free up the Bitmap object
                GDI32.DeleteObject(hDIBSection);
            }
            return(returnBitmap);
        }
        /// <summary>
        /// This method will use User32 code to capture the specified captureBounds from the screen
        /// </summary>
        /// <param name="captureBounds">Rectangle with the bounds to capture</param>
        /// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns>
        public static Bitmap CaptureRectangle(Rectangle captureBounds)
        {
            Bitmap returnBitmap = null;

            if (captureBounds.Height <= 0 || captureBounds.Width <= 0)
            {
                LOG.Warn("Nothing to capture, ignoring!");
                return(null);
            }
            LOG.Debug("CaptureRectangle Called!");

            // .NET GDI+ Solution, according to some post this has a GDI+ leak...
            // See http://connect.microsoft.com/VisualStudio/feedback/details/344752/gdi-object-leak-when-calling-graphics-copyfromscreen
            // Bitmap capturedBitmap = new Bitmap(captureBounds.Width, captureBounds.Height);
            // using (Graphics graphics = Graphics.FromImage(capturedBitmap)) {
            //	graphics.CopyFromScreen(captureBounds.Location, Point.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt);
            // }
            // capture.Image = capturedBitmap;
            // capture.Location = captureBounds.Location;

            using (SafeWindowDCHandle desktopDCHandle = SafeWindowDCHandle.fromDesktop()) {
                if (desktopDCHandle.IsInvalid)
                {
                    // Get Exception before the error is lost
                    Exception exceptionToThrow = CreateCaptureException("desktopDCHandle", captureBounds);
                    // throw exception
                    throw exceptionToThrow;
                }

                // create a device context we can copy to
                using (SafeCompatibleDCHandle safeCompatibleDCHandle = GDI32.CreateCompatibleDC(desktopDCHandle)) {
                    // Check if the device context is there, if not throw an error with as much info as possible!
                    if (safeCompatibleDCHandle.IsInvalid)
                    {
                        // Get Exception before the error is lost
                        Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds);
                        // throw exception
                        throw exceptionToThrow;
                    }
                    // Create BITMAPINFOHEADER for CreateDIBSection
                    BITMAPINFOHEADER bmi = new BITMAPINFOHEADER(captureBounds.Width, captureBounds.Height, 24);

                    // Make sure the last error is set to 0
                    Win32.SetLastError(0);

                    // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
                    IntPtr bits0;                     // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
                    using (SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDCHandle, ref bmi, BITMAPINFOHEADER.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0)) {
                        if (safeDibSectionHandle.IsInvalid)
                        {
                            // Get Exception before the error is lost
                            Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds);
                            exceptionToThrow.Data.Add("hdcDest", safeCompatibleDCHandle.DangerousGetHandle().ToInt32());
                            exceptionToThrow.Data.Add("hdcSrc", desktopDCHandle.DangerousGetHandle().ToInt32());

                            // Throw so people can report the problem
                            throw exceptionToThrow;
                        }
                        // select the bitmap object and store the old handle
                        using (safeCompatibleDCHandle.SelectObject(safeDibSectionHandle)) {
                            // bitblt over (make copy)
                            GDI32.BitBlt(safeCompatibleDCHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDCHandle, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
                        }

                        // get a .NET image object for it
                        // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try...
                        bool success = false;
                        ExternalException exception = null;
                        for (int i = 0; i < 3; i++)
                        {
                            try {
                                // Collect all screens inside this capture
                                List <Screen> screensInsideCapture = new List <Screen>();
                                foreach (Screen screen in Screen.AllScreens)
                                {
                                    if (screen.Bounds.IntersectsWith(captureBounds))
                                    {
                                        screensInsideCapture.Add(screen);
                                    }
                                }
                                // Check all all screens are of an equal size
                                bool offscreenContent;
                                using (Region captureRegion = new Region(captureBounds)) {
                                    // Exclude every visible part
                                    foreach (Screen screen in screensInsideCapture)
                                    {
                                        captureRegion.Exclude(screen.Bounds);
                                    }
                                    // If the region is not empty, we have "offscreenContent"
                                    using (Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow())) {
                                        offscreenContent = !captureRegion.IsEmpty(screenGraphics);
                                    }
                                }
                                // Check if we need to have a transparent background, needed for offscreen content
                                if (offscreenContent)
                                {
                                    using (Bitmap tmpBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle())) {
                                        // Create a new bitmap which has a transparent background
                                        returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution);
                                        // Content will be copied here
                                        using (Graphics graphics = Graphics.FromImage(returnBitmap)) {
                                            // For all screens copy the content to the new bitmap
                                            foreach (Screen screen in Screen.AllScreens)
                                            {
                                                Rectangle screenBounds = screen.Bounds;
                                                // Make sure the bounds are offsetted to the capture bounds
                                                screenBounds.Offset(-captureBounds.X, -captureBounds.Y);
                                                graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel);
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    // All screens, which are inside the capture, are of equal size
                                    // assign image to Capture, the image will be disposed there..
                                    returnBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle());
                                }
                                // We got through the capture without exception
                                success = true;
                                break;
                            } catch (ExternalException ee) {
                                LOG.Warn("Problem getting bitmap at try " + i + " : ", ee);
                                exception = ee;
                            }
                        }
                        if (!success)
                        {
                            LOG.Error("Still couldn't create Bitmap!");
                            if (exception != null)
                            {
                                throw exception;
                            }
                        }
                    }
                }
            }
            return(returnBitmap);
        }
Exemple #4
0
        /// <summary>
        /// FreeImage method
        /// </summary>
        /// <param name="bmp"></param>

        /*
         * public static void FI_ConvertSave(Bitmap bmp)
         * {
         *  if (bmp != null)
         *  {
         *      using (FreeImageAPI.FreeImageBitmap fiBitmap = FreeImageAPI.FreeImageBitmap.FromHbitmap(bmp.GetHbitmap()))
         *      {
         *          if (fiBitmap.ColorDepth > 24)
         *          {
         *              fiBitmap.ConvertColorDepth(FreeImageAPI.FREE_IMAGE_COLOR_DEPTH.FICD_08_BPP);
         *          }
         *          for (int i = 16; i < 256; i++)
         *              fiBitmap.Palette.Data[i] = new FreeImageAPI.RGBQUAD(Color.White);
         *
         *          FreeImageAPI.Palette pl = new FreeImageAPI.Palette(256);
         *          pl.CreateGrayscalePalette();
         *          //quantize using the NeuQuant neural-net quantization algorithm
         *          fiBitmap.Quantize(FreeImageAPI.FREE_IMAGE_QUANTIZE.FIQ_NNQUANT, 16, pl);
         *
         *
         *
         *          fiBitmap.Save("test_FreeImageOutput.png", FreeImageAPI.FREE_IMAGE_FORMAT.FIF_PNG, FreeImageAPI.FREE_IMAGE_SAVE_FLAGS.PNG_Z_BEST_COMPRESSION);
         *          //bmp = fiBitmap.ToBitmap();
         *          //ms = new MemoryStream();
         *          //fiBitmap.Save(ms, FreeImageAPI.FREE_IMAGE_FORMAT.FIF_PNG, FreeImageAPI.FREE_IMAGE_SAVE_FLAGS.PNG_Z_DEFAULT_COMPRESSION);
         *      }
         *      bmp.Dispose();
         *  }
         * }
         */

        // From wischik.com
        static Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)
        {
            if (bpp != 1 && bpp != 8)
            {
                throw new System.ArgumentException("1 or 8", "bpp");
            }

            // Plan: built into Windows GDI is the ability to convert
            // bitmaps from one format to another. Most of the time, this
            // job is actually done by the graphics hardware accelerator card
            // and so is extremely fast. The rest of the time, the job is done by
            // very fast native code.
            // We will call into this GDI functionality from C#. Our plan:
            // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
            // (2) Create a GDI monochrome hbitmap
            // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
            // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)

            int    w   = b.Width;
            int    h   = b.Height;
            IntPtr hbm = b.GetHbitmap(); // this is step (1)

            //
            // Step (2): create the monochrome bitmap.
            // "BITMAPINFO" is an interop-struct which we define below.
            // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
            GDI32.BITMAPINFOHEADER bmi = new GDI32.BITMAPINFOHEADER();
            bmi.biSize          = 40; // the size of the BITMAPHEADERINFO struct
            bmi.biWidth         = w;
            bmi.biHeight        = h;
            bmi.biPlanes        = 1;                     // "planes" are confusing. We always use just 1. Read MSDN for more info.
            bmi.biBitCount      = (ushort)bpp;           // ie. 1bpp or 8bpp
            bmi.biCompression   = (uint)GdiFlags.BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
            bmi.biSizeImage     = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);
            bmi.biXPelsPerMeter = 1000000;               // not really important
            bmi.biYPelsPerMeter = 1000000;               // not really important
            // Now for the colour table.
            uint ncols = (uint)1 << bpp;                 // 2 colours for 1bpp; 256 colours for 8bpp

            bmi.biClrUsed      = ncols;
            bmi.biClrImportant = ncols;
            bmi.cols           = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
            if (bpp == 1)
            {
                bmi.cols[0] = GDI32.MAKERGB(0, 0, 0); bmi.cols[1] = GDI32.MAKERGB(255, 255, 255);
            }
            else
            {
                for (int i = 0; i < ncols; i++)
                {
                    bmi.cols[i] = GDI32.MAKERGB(i, i, i);
                }
            }
            // For 8bpp we've created an palette with just greyscale colours.
            // You can set up any palette you want here. Here are some possibilities:
            // greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
            bmi.biClrUsed = 216; bmi.biClrImportant = 216; int[] colv = new int[6] {
                0, 51, 102, 153, 204, 255
            };
            for (int i = 0; i < 216; i++)
            {
                bmi.cols[i] = GDI32.MAKERGB(colv[i / 36], colv[(i / 6) % 6], colv[i % 6]);
            }
            // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
            //          for (int i=0; i<216; i++) bmi.cols[i]=GDI32.MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
            // optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
            //
            // Now create the indexed bitmap "hbm0"
            IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
            IntPtr hbm0 = GDI32.CreateDIBSection(IntPtr.Zero, ref bmi, (uint)GdiFlags.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
            //
            // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
            // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
            IntPtr sdc = USER32.GetDC(IntPtr.Zero);       // First we obtain the DC for the screen
            // Next, create a DC for the original hbitmap
            IntPtr hdc = GDI32.CreateCompatibleDC(sdc);

            GDI32.SelectObject(hdc, hbm);
            // and create a DC for the monochrome hbitmap
            IntPtr hdc0 = GDI32.CreateCompatibleDC(sdc);

            GDI32.SelectObject(hdc0, hbm0);
            // Now we can do the BitBlt:
            GDI32.BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, TernaryRasterOperations.SRCCOPY);
            // Step (4): convert this monochrome hbitmap back into a Bitmap:
            System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
            //
            // Finally some cleanup.
            GDI32.DeleteDC(hdc);
            GDI32.DeleteDC(hdc0);
            USER32.ReleaseDC(IntPtr.Zero, sdc);
            GDI32.DeleteObject(hbm);
            GDI32.DeleteObject(hbm0);
            //
            return(b0);
        }