通过drawingcontext将wpf文本块栅格化为位图

时间:2018-02-13 07:38:10

标签: c# wpf image bitmap drawingcontext

我的程序是MS paint和Pickpick的复制版本。 其中一个功能是栅格化所选对象,如文本块或形状。

关于可选对象,为了调整大小并使用adorner移动, 它有1个ContentControl,包含1个文本块+ 1个形状。

ContentControl (able to resize, rotate, move)
└─> Textblock (bold, italic, V-align, H-align, word wrap...)
└─> Shape (can be a triangle, rectangle etc...)

Like this

转换为使用绘图上下文绘制形状并不难,而不是在Canvas中渲染。

var SH = CC.GetShape();
var TB = CC.GetTextBlock();
var visual = new DrawingVisual();

Geometry geo = null;
System.Windows.Media.Pen pen = null;
System.Windows.Media.Brush brush = null;
if (SH != null)
{
    geo = SH.RenderedGeometry;  // shape to geo
    if (geo == null)
        return;
    pen = new System.Windows.Media.Pen(SH.Stroke, SH.StrokeThickness);
    brush = SH.Fill;
}

using (var dc = visual.RenderOpen())
{
    // Draw the background first
    dc.DrawImage(first, new Rect(0, 0, first.Width, first.Height));
    dc.PushTransform(new TranslateTransform(left, top));
    // Draw the shape
    if (SH != null && geo != null)
        dc.DrawGeometry(brush, pen, geo);
}

但是在使用绘图上下文绘制Textblock时, 我在下面提到了链接来计算Textblock的位置 Vertical alignment with DrawingContext.DrawText
 但问题是当Textblock包含多行或单词时。
我的计划的屏幕截图 enter image description here

 if (TB.Text.Equals(string.Empty) == false)
 {
      var typeface = new Typeface(CC.txtSetting.fontFamily,
      CC.txtSetting.fontStyle,
      CC.txtSetting.fontWeight,
      FontStretches.Normal);

      var formattedText = new FormattedText(TB.Text
                 , CultureInfo.CurrentCulture
                 , FlowDirection.LeftToRight
                 , typeface
                 , CC.txtSetting.fontSize
                 , new SolidColorBrush(CC.txtSetting.fontColor));

      double centerX = CC.ActualWidth / 2;
      double centerY = CC.ActualHeight / 2;

      double txtPositionX = 0.0f;
      double txtPositionY = 0.0f;
      if (TB.TextAlignment == TextAlignment.Left)
      {
          txtPositionX = 1.0f;
      }
      else if (TB.TextAlignment == TextAlignment.Center)
      {
          txtPositionX = centerX - formattedText.WidthIncludingTrailingWhitespace / 2;
      }
      else if (TB.TextAlignment == TextAlignment.Right)
      {
          txtPositionX = CC.Width - 
formattedText.WidthIncludingTrailingWhitespace - 1.0f;
      }

      if (TB.VerticalAlignment == VerticalAlignment.Top)
      {
          txtPositionY = 1.0f;
      }
      else if (TB.VerticalAlignment == VerticalAlignment.Center)
      {
          txtPositionY = centerY - formattedText.Height / 2;
      }
      else if (TB.VerticalAlignment == VerticalAlignment.Bottom)
      {
          txtPositionY = CC.Height - formattedText.Height - 1.0f;
      }

      var ptLocation = new System.Windows.Point(txtPositionX, txtPositionY);
                dc.DrawText(formattedText, ptLocation);
  }

此外,文本块由ContentControl包装,因此根据用户更改textblock的属性,它会有很大的变化。 我想似乎不可能转换每个变量。 所以,我正在考虑另外的绘画方式。

  1. 使用GDI +绘图而不是使用绘图上下文绘图。 (仍然不确定)
  2. 在用户编辑文本时使用绘图上下文。 (所以它在光栅化之前是相同的,反之亦然)
  3. 任何直接将Textblock转换/捕获为图像或几何图形的方法? (如果可能的话,这将是最好的方式。) 例如,要获得着色器效果应用图像源,我确实喜欢这样。所以......可能就是这样。 How can I get the object of effect-applied source
  4. 您也可以从http://ngwin.com/picpick参考此计划
    picpick的截图 enter image description here

    有更好的想法吗?提前谢谢。

1 个答案:

答案 0 :(得分:0)

我做到了! 我可以使用RenderTargetBimap捕获特定控件。由于ContentControlVisual元素的一部分。
CustomControlContentControl的继承控件。

    public static BitmapSource ControlToBitmap(CustomControl control)
    {
        int W = (int)control.ActualWidth;
        int H = (int)control.ActualHeight;
        RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
         W, H,
         96d, 96d, PixelFormats.Pbgra32);

        // needed otherwise the image output is black
        control.Measure(new System.Windows.Size(W, H));
        control.Arrange(new Rect(new System.Windows.Size(W, H)));

        renderBitmap.Render(control);

        var BS = RenderTargetBitmapToBitmap(renderBitmap);
        return BS;
    }

the wrong behaviour to be fixed
另外,我不得不处理这个角度。因为我无法直接捕捉角度控制。但我的想法是

  1. 首先备份角度值。
  2. 并将控件恢复为非旋转(RotateTransform = 0.0
  3. 将非旋转控件捕获到位图。
  4. 然后再次旋转捕获的位图。
  5. 将两个位图合并为一个。

    public static void OverlayControl(ImageSource first, CustomControl CC)
    {
        if (CC == null)
            return;
    
        var visual = new DrawingVisual();
    
        double left = Canvas.GetLeft(CC);
        double top = Canvas.GetTop(CC);
    
        // Get control's angle.
        double rotationInDegrees = 0.0f;
        RotateTransform rotation = CC.RenderTransform as RotateTransform;
        if (rotation != null) // Make sure the transform is actually a RotateTransform
        {
            rotationInDegrees = rotation.Angle; // back up this to temp var.
            rotation.Angle = 0.0f; // Set this to 0.0 to capture properly.
        }
    
        var second = ControlToBitmap(CC);
    
    
        using (var dc = visual.RenderOpen())
        {
            // Draw the background image frist.
            dc.DrawImage(first, new Rect(0, 0, first.Width, first.Height));
    
            // Push angle if the control has rotated.
            if (rotationInDegrees != 0.0f)
                dc.PushTransform(new RotateTransform(rotationInDegrees, left + (CC.Width / 2), top + (CC.Height / 2)));
    
            // transfrom as much as control moved from the origin.
            dc.PushTransform(new TranslateTransform(left, top));
            // Draw the second image. (captured image from the control)
            dc.DrawImage(second, new Rect(0, 0, second.Width, second.Height));
    
            // pop transforms
            dc.Pop();
        }
    
        var rtb = new RenderTargetBitmap((int)first.Width, (int)first.Height,
                                    96, 96, PixelFormats.Default);
        rtb.Render(visual);
        // Set as a one combined image.
        MainWindow.VM.RenderedImage = rtb;
    }
    
  6. 现在,一切似乎都没问题。 enter image description here