Растеризовать текстовый блок wpf в растровое изображение с помощью контекста рисования

Моя программа является своего рода копией MS Paint и Pickpick. и одна из функций — растеризация выбранного объекта, такого как текстовый блок или фигура.

Что касается выбираемого объекта, для изменения размера и перемещения с помощью декоративного элемента у него есть 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...)

Вот так

Было несложно преобразовать форму для рисования в контексте рисования вместо рендеринга на 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 Вертикальное выравнивание с DrawingContext.DrawText
, но проблема заключается в том, что Textblock имеет многострочный или словесный перенос.
скриншот моей программы введите здесь описание изображения

 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, поэтому в зависимости от изменения пользователем свойства текстового блока оно будет сильно различаться. Я думаю, что невозможно преобразовать каждую переменную. Итак, я думаю об альтернативных способах рисования.

  1. Рисуйте с помощью GDI+ вместо рисования в контексте рисования. (все еще неуверенно)
  2. Используйте контекст рисования, пока пользователь редактирует текст. (так что это будет то же самое до растеризации и наоборот)
  3. Любой способ напрямую преобразовать/захватить текстовый блок в изображение или геометрию? (это был бы лучший способ, если бы это было возможно.) Например, чтобы получить источник изображения с эффектом шейдера, мне это понравилось. так.. наверное есть способ. Как я могу получить объект источника с эффектом < /а>

Вы также можете обратиться к этой программе с http://ngwin.com/picpick
скриншот выбора введите здесь описание изображения

Есть идеи получше? Заранее спасибо.


person Mark Choi    schedule 13.02.2018    source источник


Ответы (1)


Я сделал это!
Я мог захватить конкретный элемент управления с помощью RenderTargetBimap. Поскольку ContentControl является частью Visual Element.
CustomControl унаследовано управление от ContentControl.

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

неправильное поведение будет исправлено
Кроме того, мне пришлось иметь дело с углом. Потому что я не мог напрямую захватить управление под углом. но моя идея

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

Теперь все кажется в порядке. введите здесь описание изображения

person Mark Choi    schedule 16.02.2018