Снимок экрана с использованием индексированного цветового таблица

StackOverflow https://stackoverflow.com/questions/4570533

Вопрос

В Интернете есть многочисленные учебники о том, как запечатлеть и сохранить скриншот, используя C#. Например, я использовал этот сайт Чтобы получить мое решение:

        using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb))
        using (var g = Graphics.FromImage(screenshot))
        {
            g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
            screenshot.Save("screenshot.png", ImageFormat.Png);
        }

Он отлично работает в большинстве программ, но программа, которую я хочу захватить, использует 8 бит, индексированные цветовые стол. Скриншоты этой программы, сделанная с помощью этого кода, странные. У меня вопрос, если кто -то может указать мне в правильном направлении для захвата скриншотов программ с индексированными цветными таблицами в C#?

Чтобы помочь вам помочь мне, я опишу свои выводы и попытки решения ниже.

Экриншоты, снятые этим кодом программы с полным экраном, с использованием 8 -бит, индексированного цветового стола, в основном чернокожие (для ~ 88 %), и в нем есть только 17 других цветов. Я не вижу рисунка в этих 17 цветах. В самой программе используются почти все 256 цветов. Я также надеялся найти 256 цветов на черных снимках экрана, что может указывать на простые отношения один на один, но это не так.

Я также хотел бы отметить, что снимки экрана, сделанные вручную, идеальны (например, когда я вставляю их в MS Paint). По этой причине я попытался получить изображение из system.windows.forms.clipboard.getimage () после снимка экрана вручную, но это возвращает Comobject, и я бы не знал, что с этим делать. Конечно, это должно содержать информацию действительного скриншота, поскольку MS Paint знает, как извлечь ее из этого объекта COM. Как я могу извлечь это сам, предпочтительно в C#?

Но мой главный вопрос: может ли кто -нибудь указать мне в правильном направлении для съемки скриншотов программ с индексированными цветными таблицами с C#?

Это было полезно?

Решение 3

Предлагаемые решения не работали, и я только недавно решил эту проблему. Я сделал снимок экрана, отправив нажмите и выпустив в Windows нажмите клавишу Printscreen, и получил данные из буфера обмена в виде MemoryStream и выяснил, как скриншот был декодирован в этом потоке, и преобразовал его в растровый карту. Не очень привлекательно, но, по крайней мере, это работает ...

Другие советы

Если я правильно понимаю, проблема здесь в том, что вы копируете значения пикселей (индексы палитры), но не сама палитра. Я не нашел способ скопировать палитру с чистым C#, но с P/Invoke и альтернативой с DirectX ... поскольку оба добавляют зависимость к окнам, которые я выбрал P/Invoke, так как это проще и не зависит от DirectX.

У меня есть два метода, чтобы предложить вам ...

Первый метод:

[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
    private static extern IntPtr SelectPalette(
        IntPtr hdc,
        IntPtr htPalette,
        bool bForceBackground);

    [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
    private static extern int RealizePalette(IntPtr hdc);

    private void ScreenShot()
    {
        IntPtr htPalette = Graphics.GetHalftonePalette();
        using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
        {
            using (var graphics = Graphics.FromImage(screenshot))
            {
                IntPtr hdc = graphics.GetHdc();
                SelectPalette(hdc, htPalette, true);
                RealizePalette(hdc);
                graphics.ReleaseHdc(hdc);
                graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
            }
            screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
        }
    }

Этот метод зависит от GetHalftonePalette давая вам правильную палитру. Я никогда не тестировал с палитрами (я слишком молод) ... поэтому я решил написать второй метод, основанный на том, что Windows должен автоматически обрабатывать палитры в некоторых условиях. ПРИМЕЧАНИЕ. Не уверен, что перемещение вызова в CopyFromScreen на начало использования блока лучше или игнорировать призыв к реализепалете (я слишком молод).

Второй метод:

    [System.Runtime.InteropServices.DllImport("gdi32.dll")]
    [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
    static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    private void ScreenShot()
    {
        int width = Screen.PrimaryScreen.Bounds.Width;
        int height = Screen.PrimaryScreen.Bounds.Height;
        using (var screenshot = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
        {
            using (var fromHwnd = Graphics.FromHwnd(new IntPtr(0)))
            using (var graphics = Graphics.FromImage(screenshot))
            {
                IntPtr hdc_screen = fromHwnd.GetHdc();
                IntPtr hdc_screenshot = graphics.GetHdc();
                BitBlt(hdc_screenshot, 0, 0, width, height, hdc_screen, 0, 0, 0x00CC0020); /*SRCCOPY = 0x00CC0020*/
                graphics.ReleaseHdc(hdc_screenshot);
                fromHwnd.ReleaseHdc(hdc_screen);
            }
            screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
        }
    }

Оба метода работают на нормальных условиях. Пожалуйста, проведите тестирование, может быть, вам нужно будет выполнить второй метод с добавлением дублирования исходной палитры (то есть ... когда Windows не обрабатывает их автоматически), но я не уверен, как это Сделайте это (я слишком молод) и надеюсь, что этот метод сработает.

Наконец, если вы хотите позволить себе дополнительную зависимость и хотите, чтобы она отличалась от Windows, возможно, GTK# (есть версия, доступная Form Windows), является хорошим вариантом. Посмотри пожалуйста Как сделать скриншот с моно C#? поскольку он решает аналогичную проблему и предоставляет пример кода для GTK#.

Примечание:Следующий код, кажется, хорошо работает здесь, вы получаете какие -либо исключения по этому коду?

System.Drawing.Image image = Clipboard.GetImage();
image.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);

Это не так, как это работает. Вы захватываете экран, а не непосредственно вывод программы. Настройка видеодаптера имеет значение, он будет 32BPP для любой недавней машины. Может быть, 16 фунтов для старых. Попытка скопировать такой неиндексированный пиксельный формат в растровый карту с палитрой не поддерживается. Алгоритм создания палитры, которая обеспечивает лучшую цветовую точность, является вычислительно нетривиальной.

Только не беспокойтесь, изображение 32BPP будет неразличимым от вывода программы. Если сжимать файл действительно важно, храните его как .gif. Он не будет выглядеть великолепно, Gif Encoder использует сжатие цветового стола.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top