Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing a thick dotted line on a ImgView32 layer

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 resizingafter first resizeafter more resizingafter 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

like image 383
user1137313 Avatar asked Apr 16 '15 22:04

user1137313


1 Answers

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.

like image 135
Wodzu Avatar answered Sep 21 '22 00:09

Wodzu