Рисование толстой пунктирной линии на слое ImgView32

Я просто хочу нарисовать вертикальную пунктирную толстую линию на слое в ImgView32. Я также хочу, чтобы моя линия была толще, поэтому я рисую несколько линий близко друг к другу, потому что Canvas.Pen.Width не влияет на методы LineTo. Итак, мой код выглядит следующим образом:

procedure TMainForm.PaintDottedHandler(Sender: TObject;Buffer: TBitmap32);
var
  Cx, Cy,raza: Single;
  W2, H2: Single;
  I,J: Integer;
  points:TArrayOfFloatPoint;
  Center, Radius:TFloatPoint;
const
  CScale = 1 / 200;
begin

  if Sender is TPositionedLayer then
    with TPositionedLayer(Sender).GetAdjustedLocation do
    begin
      W2 := (Right - Left) * 0.5;
      H2 := (Bottom - Top) * 0.5;

      Cx := Left + W2;
      Cy := Top + H2;
      W2 := W2 * CScale;
      H2 := H2 * CScale;
      Buffer.PenColor := clRed32;

      Buffer.MoveToF(Cx-2,Top);
      Buffer.LineToFSP(Cx-2 , Bottom);

      Buffer.MoveToF(Cx-1,Top);
      Buffer.LineToFSP(Cx-1 , Bottom);

      Buffer.MoveToF(Cx,Top);
      Buffer.LineToFSP(Cx , Bottom);

      Buffer.MoveToF(Cx+1,Top);
      Buffer.LineToFSP(Cx+1 , Bottom);

      Buffer.MoveToF(Cx+2,Top);
      Buffer.LineToFSP(Cx+2 , Bottom);
    end;
end;

Таким образом, линия предназначена для размещения в середине нового слоя. Я добавляю слой, используя это:

procedure TMainForm.DottedLine1Click(Sender: TObject);
var
  L: TPositionedLayer;
begin
  L := CreatePositionedLayer;
  L.OnPaint := PaintDottedHandler;
  L.Tag := 2;
  Selection := L;
end;

Для остальной части кода просто добавьте мой код в пример слоев, и вы сможете воспроизвести мою проблему.

Насколько я читал, для рисования пунктирной линии существует несколько подходов, таких как Stipple с LineToFSP (используется в моем коде) или PolyPolygonFS с точками BuildDashedLine. Но я не могу заставить ни один из них работать правильно. На самом деле второй подход ничего не делает... поэтому я придерживаюсь первого подхода. Таким образом, кажется, что каждый раз, когда он начинает рисовать линию, случайным образом начинается пунктирная линия. Так что либо пиксель, либо пустой. Поэтому, когда я изменяю размер слоя, линия трансформируется, как показано на следующих изображениях:

перед изменением размерапосле первого изменения размера после дальнейшего изменения размерапосле изменения размера

И на самом деле все, чего я хочу добиться, это:

желаемый результат

И, конечно же, я хочу, чтобы линия снова рисовалась при изменении размера слоя, не искажая его (поэтому я использую подход обработчика onPaint). Если я просто нарисую простую линию на слое (используя Bitmap.Canvas), а затем изменю размер слоя, то линия будет искажена, как при растяжении jpeg, поэтому я хочу этого избежать.

Итак, подскажите, пожалуйста, как нарисовать толстую пунктирную линию на слое в ImgView32 (TGraphics32)

ИЗМЕНИТЬ

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

Перед изменением размера введите здесь описание изображенияПосле изменения размера (иногда). введите здесь описание изображения

Вы можете воспроизвести это самостоятельно, используя тот же код.

ИЗМЕНИТЬ

Есть еще одна проблема с этим специальным слоем: сохранение его в файл... Я пытался сохранить его как прозрачный PNG, используя 2 подхода, но я продолжаю получать поврежденный файл. Даже если я попытаюсь сохранить слой как растровое изображение, произойдет то же повреждение. Пожалуйста, проверьте и этот вопрос:

Graphics32 - сохранение прозрачного слоя рисования в png


person user1137313    schedule 16.04.2015    source источник
comment
Просто дикая догадка, но в ваших MoveToF и LineToFSP я бы использовал координаты Cy, а не Top и Bottom.   -  person David Schwartz    schedule 17.04.2015
comment
Cy — это переменная из другого обработчика, где я рисую круги, и я использовал ее для получения координат центра слоя. Таким образом, (Cx,Cy) является центральной точкой слоя. Как использование Cy поможет мне решить мою проблему, описанную выше?   -  person user1137313    schedule 17.04.2015
comment
Вы заново изобретаете колесо здесь. Вы должны использовать отличное расширение GR32_Lines.   -  person David Heffernan    schedule 17.04.2015
comment
@DavidHeffernan, у вас есть код, подтверждающий ваше предложение?   -  person user1137313    schedule 17.04.2015
comment
@user1137313 user1137313 Нет. GR32_Lines содержит множество превосходных примеров.   -  person David Heffernan    schedule 17.04.2015
comment
Я попробовал GR32_Lines, однако пунктирная линия не так хорошо проявляется, как прямая LineToFSP. Это мой код: with TLine32.Create do try dashes := MakeArrayOfFloat([3, 3]); EndStyle := esClosed; SetPoints([FixedPoint(Cx-2,Top), FixedPoint(Cx-2,Bottom)]); Draw(Buffer, 3, dashes, clBlack32, clBlack32);   -  person user1137313    schedule 17.04.2015
comment
Если вы попробуете этот код, вы заметите, что при изменении размера слоя, например, по высоте, пунктирная линия не остается прежней. Иногда она превращается в обычную линию. Так что, если нет какого-то улова... какое-то свойство, которое я могу установить, чтобы этого не произошло, использование этого подхода у меня не работает.   -  person user1137313    schedule 17.04.2015


Ответы (2)


Как упоминал @SpeedFreak, вам нужно сбросить StrippleCounter перед каждым вызовом отрисовки линии. Вам также необходимо настроить правильный шаблон линии для вашей линии. Это можно сделать методом SetStripple. Хитрость заключается в том, чтобы правильно настроить этот шаблон для ширины вашей линии. Если ваша линия имеет ширину 5 пикселей, вам нужен шаблон, который будет состоять из 5 черных пикселей и 5 белых пикселей.

Попробуйте это, я удалил ненужный код (обновлено):

procedure TMainForm.PaintDottedHandler(Sender: TObject; Buffer: TBitmap32);
var
  R: TRect;
  Cx: Integer;
begin
  if Sender is TPositionedLayer then
  begin
    // Five black pixels, five white pixels since width of the line is 5px
    Buffer.SetStipple([clBlack32, clBlack32, clBlack32, clBlack32, clBlack32,
      clWhite32, clWhite32, clWhite32, clWhite32, clWhite32]);
    // We mest operate on integer values to avoid blurred line.
    R := MakeRect(TPositionedLayer(Sender).GetAdjustedLocation);
    Cx := R.Left + (R.Right - R.Left) div 2;

    Buffer.StippleCounter := 0;
    Buffer.MoveToF(Cx-2, R.Top);
    Buffer.LineToFSP(Cx-2 , R.Bottom);

    Buffer.StippleCounter := 0;
    Buffer.MoveToF(Cx-1, R.Top);
    Buffer.LineToFSP(Cx-1 , R.Bottom);

    Buffer.StippleCounter := 0;
    Buffer.MoveToF(Cx, R.Top);
    Buffer.LineToFSP(Cx , R.Bottom);

    Buffer.StippleCounter := 0;
    Buffer.MoveToF(Cx+1, R.Top);
    Buffer.LineToFSP(Cx+1 , R.Bottom);

    Buffer.StippleCounter := 0;
    Buffer.MoveToF(Cx+2, R.Top);
    Buffer.LineToFSP(Cx+2 , R.Bottom);
  end;
end;

И должно получиться как на картинке:

Пример строки

Причина, по которой у вас время от времени появляется «размытая» линия при изменении размера слоя, заключается в том, что вы оперировали значениями с плавающей запятой для рисования линии. Вам нужно использовать целочисленные значения. Я предполагаю, что в некоторых случаях движок рисования решил нарисовать размытый пиксель, когда ваша линия заполняла только часть этого пикселя.

Надеюсь это поможет.

person Wodzu    schedule 17.04.2015
comment
Ваш код отлично работает. Однако есть ли у вас какие-либо идеи о том, как избежать побочного эффекта, который виден при попытке изменить размер слоя по горизонтали? На некоторых ширинах рисунок из черного превращается в темно-серый. Любая идея о том, почему это происходит и как это предотвратить? - person user1137313; 17.04.2015
comment
Я не заметил такого поведения. Пожалуйста, обновите свой вопрос с изображением, которое покажет побочный эффект. - person Wodzu; 17.04.2015
comment
Проверьте отредактированный вопрос. Но вы должны быть в состоянии воспроизвести эффект самостоятельно. Просто попробуйте медленно изменить размер слоя по горизонтали с помощью мыши. Вы заметите, что время от времени рисунок становится размытым и тусклым. - person user1137313; 17.04.2015
comment
У меня также есть проблемы с сохранением этого слоя в виде прозрачного PNG. Я сформулировал проблему в новом вопросе, чтобы вы могли ответить там: stackoverflow.com/questions/29705278/ - person user1137313; 17.04.2015
comment
Я предполагаю, что серый цвет является побочным эффектом другой, не связанной с этим проблемы. Я бы проверил значения свойств CombineMode, DrawMode и MasterAlpha TBitmap32 при возникновении проблемы. - person SpeedFreak; 18.04.2015
comment
Вопрос не такой уж и проблемный. Настоящая проблема заключается в сохранении слоев. Пожалуйста, ознакомьтесь с вопросом, упомянутым в комментарии выше. - person user1137313; 20.04.2015
comment
@ user1137313 Я решил проблему с размытой линией. - person Wodzu; 20.04.2015

Вам необходимо сбросить счетчик шагов между каждым линия. В противном случае каждая строка будет продолжать шаблон с того места, где остановилась предыдущая:

Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx-2,Top);
Buffer.LineToFSP(Cx-2 , Bottom);

Buffer.StippleCounter := 0;
Buffer.MoveToF(Cx-1,Top);
Buffer.LineToFSP(Cx-1 , Bottom);
...etc...

Вы не показали, как настроен ваш шаблон, но оцениваете из ваших примеров там тоже может быть проблема. Я бы (сейчас) сделал это примерно так:

Buffer.SetStipple([clBlack32, clBlack32, clBlack32, clBlack32, clBlack32,
  clWhite32, clWhite32, clWhite32, clWhite32, clWhite32]); // Alternating black and white, 5 pixels each
person SpeedFreak    schedule 17.04.2015
comment
StippleStep Здесь не правильный подход. Это свойство отвечает за эффект затухания. Задав значение 1/5, вы сообщаете движку рисования, что цвет исчезнет за пять шагов. - person Wodzu; 17.04.2015
comment
Ты прав. Я удалил StippleStep из ответа. - person SpeedFreak; 17.04.2015