WPF RenderTargetBitmap downscaling TextRenderMode to GreyScale

前端 未结 1 1704
别跟我提以往
别跟我提以往 2020-12-12 01:26

RenderTargetBitmap removes downgrades a RichtextBox\'s TextRenderingMode to GreyScale. So a captured PNG looks poor quality and doesnt match the WPF control

If I use

相关标签:
1条回答
  • 2020-12-12 02:29

    You can use the same technique to render your window into the bitmap as windows using when taking a screen shot with Alt + Print Screen. The idea is to get a window handle and then render it to a bitmap using BitBlt system call.

    Below is an example (you would need to reference the System.Drawing.dll assembly in your project to make it work):

    public static void SaveToBitmapNative(Window window, FrameworkElement element, string fileName)
    {
        WindowInteropHelper helper = new WindowInteropHelper(window);
    
        // detect the window client area position if rendering a child element
        double incX = 0, incY = 0;
        if (window != element)
        {
            System.Drawing.Point pos = new System.Drawing.Point(0, 0);
            ClientToScreen(helper.Handle, ref pos);
            incX = pos.X - (int)window.Left;
            incY = pos.Y - (int)window.Top;
        }
    
        // transform child position to window coordinates
        GeneralTransform transform = element.TransformToVisual(window);
        Point point = transform.Transform(new Point(0, 0));
        Rect rect = new Rect(point.X + incX, point.Y + incY, element.ActualWidth, element.ActualHeight);
    
        // render window into bitmap
        using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
            (int)rect.Width, (int)rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
        {
            using (System.Drawing.Graphics memoryGraphics = System.Drawing.Graphics.FromImage(bitmap))
            {
                IntPtr dc = memoryGraphics.GetHdc();
                IntPtr windowDC = GetWindowDC(helper.Handle);
    
                BitBlt(dc, 0, 0, (int)rect.Width, (int)rect.Height,
                        windowDC, (int)rect.Left, (int)rect.Top, TernaryRasterOperations.SRCCOPY);
    
                memoryGraphics.ReleaseHdc(dc);
                ReleaseDC(helper.Handle, windowDC);
            }
            // save bitmap to file
            bitmap.Save(fileName);
        }
    }
    
    [DllImport("gdi32.dll")]
    static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, TernaryRasterOperations rop);
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr ptr);
    [DllImport("user32.dll")]
    static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
    [DllImport("user32.dll")]
    static extern bool ClientToScreen(IntPtr hWnd, ref System.Drawing.Point lpPoint);
    
    public enum TernaryRasterOperations : uint
    {
        SRCCOPY = 0x00CC0020,
        SRCPAINT = 0x00EE0086,
        SRCAND = 0x008800C6,
        SRCINVERT = 0x00660046,
        SRCERASE = 0x00440328,
        NOTSRCCOPY = 0x00330008,
        NOTSRCERASE = 0x001100A6,
        MERGECOPY = 0x00C000CA,
        MERGEPAINT = 0x00BB0226,
        PATCOPY = 0x00F00021,
        PATPAINT = 0x00FB0A09,
        PATINVERT = 0x005A0049,
        DSTINVERT = 0x00550009,
        BLACKNESS = 0x00000042,
        WHITENESS = 0x00FF0062
    }
    

    here's how you can call this

    private void saveButton_Click(object sender, RoutedEventArgs e)
    {
        // saves the entire window into the bitmap
        SaveToBitmapNative(this, this, "c:\\test0.png");
        // saves a child control (RichTextBox) into the bitmap
        SaveToBitmapNative(this, richTextBox, "c:\\test1.png"); 
    }
    

    Note: this would not work for layered windows updated via UpdateLayeredWindow function.

    hope this helps, regards

    0 讨论(0)
提交回复
热议问题