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

Community
  • 1
  • 1
user1137313
  • 2,390
  • 9
  • 44
  • 91
  • Just a wild guess, but in your MoveToF and LineToFSP, I'd use Cy coordinates rather than Top and Bottom. – David Schwartz Apr 17 '15 at 00:58
  • 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? – user1137313 Apr 17 '15 at 05:13
  • You are re-inventing the wheel here. You should use the excellent GR32_Lines extension. – David Heffernan Apr 17 '15 at 10:53
  • @DavidHeffernan , do you have some code to back your suggestion? – user1137313 Apr 17 '15 at 13:46
  • @user1137313 No. GR32_Lines has lots of superb examples. – David Heffernan Apr 17 '15 at 13:57
  • 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);` – user1137313 Apr 17 '15 at 19:58
  • 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 me – user1137313 Apr 17 '15 at 20:02

2 Answers2

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.

Wodzu
  • 6,932
  • 10
  • 65
  • 105
  • 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? – user1137313 Apr 17 '15 at 13:44
  • I haven't noticed this behaviour. Please update your question with image that will show the side-effect. – Wodzu Apr 17 '15 at 14:25
  • 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 color – user1137313 Apr 17 '15 at 14:56
  • I also have problems saving this layer as a transparent PNG. I formulated the problem in a new question so you can answer there: http://stackoverflow.com/questions/29705278/graphics32-saving-transparent-drawing-layer-to-png – user1137313 Apr 17 '15 at 17:00
  • 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. – SpeedFreak Apr 18 '15 at 09:25
  • 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. – user1137313 Apr 20 '15 at 01:10
  • @user1137313 I've resolved the issue with blurred line. – Wodzu Apr 20 '15 at 07:26
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
SpeedFreak
  • 876
  • 6
  • 16
  • 1
    `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. – Wodzu Apr 17 '15 at 08:37
  • You are correct. I've removed `StippleStep` from the answer. – SpeedFreak Apr 17 '15 at 10:51