Рисование толстой пунктирной линии на слое 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;
Для остальной части кода просто добавьте мой код в пример Layers, и вы сможете воспроизвести мою проблему.
Насколько я читал, для того, чтобы нарисовать пунктирную линию, существует несколько проблем, таких как Stipple с LineToFSP (используется в моем коде) или PolyPolygonFS с точками BuildDashedLine. Но я не могу заставить кого-то из них работать в основном. На самом деле второй подход ничего не делает... поэтому я придерживаюсь своего первого подхода. Так что кажется, что каждый раз, когда он начинает рисовать линию, он случайным образом, как начинается пунктирная линия. Так что это либо пиксель, либо пустой. Поэтому, когда я изменяю размер слоя, линия преобразуется, как на следующих изображениях:
И на самом деле все, чего я хочу достичь, это:
И, конечно же, я хочу, чтобы линия изменялась при изменении размера слоя без его искажения (поэтому я использую подход обработчика onPaint). Если бы я просто нарисовал простую линию на слое (используя Bitmap.Canvas), а затем изменил размер слоя, то линия исказилась бы, как растяжение jpeg, поэтому я хочу этого избежать.
Поэтому, пожалуйста, скажите мне, как нарисовать толстую пунктирную линию на слое в ImgView32 (TGraphics32)
РЕДАКТИРОВАТЬ
Попробовав код из ответов, я заставил его работать. Однако у этого слоя есть побочный эффект: при изменении размера слоя (с помощью мыши) при некоторой ширине цвет пунктирной линии тускнеет и размывается, как показано ниже:
Перед изменением размераПосле изменения размера (иногда).
Вы можете воспроизвести это самостоятельно, используя тот же код.
РЕДАКТИРОВАТЬ
Есть еще одна проблема с этим специальным слоем: сохранение его в файл... Я пытался сохранить его в виде прозрачного PNG, используя 2 подхода, но я продолжаю получать поврежденный файл. Даже если я попытаюсь сохранить слой как растровое изображение, произойдет такое же повреждение. Пожалуйста, проверьте этот вопрос тоже:
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;
И результат должен быть как на картинке:
Причина, по которой у вас время от времени появляется "размытая" линия при изменении размера слоя, заключается в том, что вы работали со значениями с плавающей запятой для рисования линии. Вам нужно использовать целочисленные значения. Я предполагаю, что в некоторых случаях механизм рисования решил нарисовать размытый пиксель, когда ваша линия заполняет только часть этого пикселя.
Надеюсь это поможет.
Вам нужно сбросить счетчик пунктирной линии между каждой строкой. В противном случае каждая строка будет продолжать шаблон, где предыдущий остановился:
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