Как увеличить расстояние между точками на линии? - PullRequest
0 голосов
/ 05 апреля 2019

При использовании стандартного пера (PS_DOT) и рисовании линии с ним результат будет таким, как показано на рисунке ниже (увеличенное изображение) enter image description here

У меня есть две проблемы с этой линией, во-первых, она задает более одного пикселя для точки. Во-вторых, точки слишком близко друг к другу для того, что я хотел бы сделать (нарисуйте очень мягкую линию.)

Я мог бы использовать SetPixel, но производительность оставляет желать лучшего.

У меня вопрос: есть ли достаточно быстрый способ нарисовать линию, где можно управлять количеством пикселей, используемых для рисования точки, и расстоянием между точками?

По сути, это более быстрый способ, чем использование SetPixel (который можно было бы использовать для решения проблемы, если бы он не был таким медленным.)

Было бы здорово получить фрагмент кода, показывающий, как это делается на C, C ++ или Delphi.

Спасибо за вашу помощь.

РЕДАКТИРОВАТЬ: Я попробовал ответ IInspectable об использовании ExtCreatePen, и это очень близко. Кажется, что единственный способ получить пиксели / точки вместо черточек - это использовать PS_ALTERNATE, но, когда это используется, невозможно указать интервал.

Для справки, и в случае, если я делаю ошибку, которую не вижу, ниже приведена тестовая программа, которую я написал. То, что я хотел бы, является повторяющейся последовательностью 1 точка (не тире), а затем 2 пробела. Вывод, полученный из тестовой программы, показан (увеличен) ниже: (верхняя строка получается с помощью PS_ALTERNATE, нижняя строка - с массивом, который задает 1 точку, 2 пробела - что дает 2 точки и 2 пробела.)

enter image description here

программа испытаний:

{$APPTYPE        GUI}

{$LONGSTRINGS    OFF}
{$WRITEABLECONST ON}

program _ExtCreatePen;

uses Windows, Messages;

const
  AppName  = 'ExtCreatePen';

{$ifdef VER90} { Delphi 2.0 }
type
  ptrint  = longint;        // NativeInt  for newer versions
  ptruint = dword;          // NativeUint  "    "      "
{$endif}

{-----------------------------------------------------------------------------}

function WndProc (Wnd : HWND; Msg : UINT; wParam, lParam : ptrint)
         : ptrint; stdcall;
const
  PenPattern   : packed array[1..4] of DWORD = (1, 2, 1, 2); { 1 dot, 2 spaces}

  PenBrush     : TLOGBRUSH = (lbStyle:BS_SOLID; lbColor:0; lbHatch:0);
  PenWidth     : DWORD     = 1;

  { !! this doesn't seem to work as expected, expected 1 dot, 2 spaces !!     }

  PenStyle     : DWORD     = PS_COSMETIC or PS_USERSTYLE;
  StyleCount   : DWORD     = high(PenPattern);
  StylePattern : PDWORD    = @PenPattern[low(PenPattern)];

  { this gives 1 dot, 1 space.                                                }

  //PenStyle     : DWORD     = PS_COSMETIC or PS_ALTERNATE;
  //StyleCount   : DWORD     = 0;
  //StylePattern : PDWORD    = nil;

  Pen          : HPEN      = 0;

var
  ps          : TPAINTSTRUCT;
  ClientRect  : TRECT;

begin
  WndProc := 0;

  case Msg of
    WM_CREATE:
    begin
      PenBrush.lbColor := RGB(255, 0, 0);

      Pen := ExtCreatePen(PenStyle,
                          PenWidth,
                          PenBrush,
                          StyleCount,
                          StylePattern);
      exit;
    end;

    WM_PAINT:
    begin
      BeginPaint(Wnd, ps);
        GetClientRect(Wnd, ClientRect);

        SelectObject(ps.hdc, Pen);  { use the pen we created    }

        MoveToEx(ps.hdc, 0, ClientRect.Bottom div 2, nil);
        LineTo(ps.hdc, ClientRect.Right, ClientRect.Bottom div 2);
      EndPaint(Wnd, ps);

      exit;
    end;

    WM_CLOSE: PostMessage(Wnd, WM_DESTROY, 0, 0);

    WM_DESTROY:
       begin
         if Pen <> 0 then DeleteObject(Pen);

         PostQuitMessage(0);

         exit;
       end; { WM_DESTROY }
  end; { case msg }

  WndProc := DefWindowProc (Wnd, Msg, wParam, lParam);
end;

{-----------------------------------------------------------------------------}

function InitAppClass: WordBool;
  { registers the application's window classes                                }
var
  cls : TWndClassEx;

begin
  cls.cbSize          := sizeof(TWndClassEx);           { must be initialized }

  if not GetClassInfoEx (hInstance, AppName, cls) then
  begin
    with cls do
    begin
      { cbSize has already been initialized as required above                 }

      style           := CS_BYTEALIGNCLIENT;
      lpfnWndProc     := @WndProc;                    { window class handler  }
      cbClsExtra      := 0;
      cbWndExtra      := 0;
      hInstance       := system.hInstance;
      hIcon           := LoadIcon (hInstance, IDI_APPLICATION);
      hCursor         := LoadCursor(0, IDC_ARROW);
      hbrBackground   := COLOR_WINDOW + 1;
      lpszMenuName    := nil;
      lpszClassName   := AppName;                     { Window Class name     }
      hIconSm         := 0;
    end; { with }

    InitAppClass := WordBool(RegisterClassEx(cls));
  end
  else InitAppClass := TRUE;
end;

{-----------------------------------------------------------------------------}

function WinMain : integer;
  { application entry point                                                   }
var
  Wnd : hWnd;
  Msg : TMsg;

begin
  if not InitAppClass then Halt (255);  { register application's class        }

  { Create the main application window                                        }

  Wnd := CreateWindowEx(WS_EX_CLIENTEDGE,
                        AppName,                { class name                  }
                        AppName,                { window caption text         }
                        ws_Overlapped       or  { window style                }
                        ws_SysMenu          or
                        ws_MinimizeBox      or
                        ws_ClipSiblings     or
                        ws_ClipChildren     or  { don't affect children       }
                        ws_visible,             { make showwindow unnecessary }
                        20,                     { x pos on screen             }
                        20,                     { y pos on screen             }
                        400,                    { window width                }
                        200,                    { window height               }
                        0,                      { parent window handle        }
                        0,                      { menu handle 0 = use class   }
                        hInstance,              { instance handle             }
                        nil);                   { parameter sent to WM_CREATE }

  if Wnd = 0 then Halt;                         { could not create the window }

  while GetMessage (Msg, 0, 0, 0) do            { wait for message            }
  begin
    TranslateMessage (Msg);                     { key conversions             }
    DispatchMessage  (Msg);                     { send to window procedure    }
  end;

  WinMain := Msg.wParam;                        { terminate with return code  }
end;

begin
  WinMain;
end.

Ответы [ 2 ]

1 голос
/ 08 апреля 2019

В вашем коде нет ничего плохого.Мне понадобилось немного времени, чтобы реализовать пользовательскую пунктирную линию GDI, как вы ее описали.

В GDI пользовательские пунктирные линии отображаются пунктирными линиями, как указано в MSDN.

Если dwPenStyle равно PS_COSMETIC и PS_USERSTYLE, записи в массиве lpStyle указывают длины тире и пробелы в единицах стиля.Единица стиля определяется устройством, где перо используется для рисования линии.

Если dwPenStyle равно PS_GEOMETRIC и PS_USERSTYLE, записи в массиве lpStyle указывают длину тире и пробелы в логических единицах.

Эта функция может быть реализована в GDI +, как упоминалось в начале @Michael Chourdakis

1 голос
/ 06 апреля 2019

Если вам нужно контролировать длину штрихов и пробелов в строке, вызовите функцию ExtCreatePen , чтобы создать перо PS_USERSTYLE. Это позволяет вам указать массив из 16 записей, каждая из которых задает длину тире или пробела.

...