Drawing a thick dotted line on a ImgView32 layer
Asked Answered
P

2

5

I just want to draw a vertical dotted thick line on a layer in ImgView32. I also want my line to be thicker so I draw multiple lines close to one another, because the Canvas.Pen.Width has no effect on the LineTo methods. So my code is as follows:

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;

So the line is intended to be placed in the middle of the new layer. I add the layer using this:

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

For the rest of the code just add my code to the Layers example and you will be able to reproduce my problem.

As far as I read, in order to draw a dotted line there are multiple aprroaches, like Stipple with LineToFSP (used in my code) or PolyPolygonFS with a BuildDashedLine points. But I cannot seem to make any of them to work corectly. Actually the second approach does not do anything... so I stick with my first approach. So it seems like everytime it starts drawing a line it's random the way the dotted line starts. So it's either a pixel, or an empty one. So when I resize the layer the line transforms like in the following images:

before resizing after first resize after more resizing after more resizing

And in fact all I want to achieve is this:

desired result

And of course I want the line to be drawn again when resizing the layer without distorting it (that's why I use the onPaint handler approach). If I just draw a simple line on a layer (using Bitmap.Canvas) and then resize the layer, then the line would get distorted just like stretching a jpeg, so I want to avoid that.

So please tell me how to draw a thick dotted line on a layer in ImgView32 (TGraphics32)

EDIT

After trying the code from the answers I made it work. However there is a side-effect to this layer: when resizing the layer (using the mouse), at some widths the color of the dotted line is dimmed and blurred like bellow:

Before resizing enter image description here After resizing (sometimes). enter image description here

You can reproduce this yourself using the same code.

EDIT

There is another problem with this special layer: saving it to file... I tried to save it as a transparent PNG using 2 approaches but I keep getting a corrupted file. Even if I try to save the layer as Bitmap the same corruption occurs. Please check out this question too:

Graphics32 - saving transparent drawing layer to png

Phlegmatic answered 16/4, 2015 at 22:47 Comment(7)
Just a wild guess, but in your MoveToF and LineToFSP, I'd use Cy coordinates rather than Top and Bottom.Mcgurn
Cy is a variable from another handler where I draw circles and I used it to get the coordinates of the center of the layer. So (Cx,Cy) si the center point of the layer. How would using Cy help me with my problem described above?Phlegmatic
You are re-inventing the wheel here. You should use the excellent GR32_Lines extension.Pouliot
@DavidHeffernan , do you have some code to back your suggestion?Phlegmatic
@Phlegmatic No. GR32_Lines has lots of superb examples.Pouliot
I tried the GR32_Lines however the dotted line does not manifest itself quite so nicely as the direct LineToFSP. This is my code: 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);Phlegmatic
If you will try that code, you will notice that when resizing the layer, in height for example, the dotted line does not stay the same. It sometimes turns into a normal line. So unless there is some catch ... some property that I can set to prevent this from happening, using this approach is not working for mePhlegmatic
K
4

As @SpeedFreak mentioned you need to reset StrippleCounter before each line draw call. You also need to setup up a proper line pattern for your line. This could be done by SetStripple method. The trick is to set up this pattern correctly for the width of your line. If your line is 5 pixels wide then you need a pattern that will consist of 5 black pixels and 5 white pixels.

Try this out, I've removed unnecessary code (updated):

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;

And the result should be like on the picture:

Line example

The reason for which you've got "blurred" line from time to time when resizing layer is because you were operating on floating point values for drawing the line. You need to use integer values. I am guessing that in some cases drawing engine decided to draw a blurred pixel when your line was filling only the part of that pixel.

Hope this helps.

Klansman answered 17/4, 2015 at 7:0 Comment(7)
Your code works great. However do you have any idea on how to avoid the side-effect that is visible when trying to resize the layer horizontally? On some widths the drawing turns from black into a dark-grey. Any idea on why this happens and how to prevent it?Phlegmatic
I haven't noticed this behaviour. Please update your question with image that will show the side-effect.Klansman
Check out the edited question. But you should be able to reproduce the effect yourself. Just try to slowly resize the layer horizontally, using the mouse. You will notice that from time to time, the drawing gets blurred and dimmed in colorPhlegmatic
I also have problems saving this layer as a transparent PNG. I formulated the problem in a new question so you can answer there: #29705778Phlegmatic
My guess is that the grey color is a side effect of another, unrelated problem. I would examine the values the CombineMode, DrawMode and MasterAlpha TBitmap32 properties when the problem occurs.Thedrick
The issue is not that problematic. The real problem is with saving the layers. Please check out the question referred to in the above comment.Phlegmatic
@Phlegmatic I've resolved the issue with blurred line.Klansman
T
3

You need to reset the stipple counter between each line. Otherwise each line will continue the pattern where the previous left off:

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...

You haven't shown how your pattern is set up but judging from your examples there could be a problem there too. I would (now) do it something like this:

Buffer.SetStipple([clBlack32, clBlack32, clBlack32, clBlack32, clBlack32,
  clWhite32, clWhite32, clWhite32, clWhite32, clWhite32]); // Alternating black and white, 5 pixels each
Thedrick answered 17/4, 2015 at 6:37 Comment(2)
StippleStep Is not a correct approach here. This property is responsible for fading effect, By giving value 1/5 you are telling drawing engine to fade color in five steps.Klansman
You are correct. I've removed StippleStep from the answer.Thedrick

© 2022 - 2024 — McMap. All rights reserved.