При использовании стандартного пера (PS_DOT) и рисовании линии с ним результат будет таким, как показано на рисунке ниже (увеличенное изображение)
У меня есть две проблемы с этой линией, во-первых, она задает более одного пикселя для точки. Во-вторых, точки слишком близко друг к другу для того, что я хотел бы сделать (нарисуйте очень мягкую линию.)
Я мог бы использовать SetPixel, но производительность оставляет желать лучшего.
У меня вопрос: есть ли достаточно быстрый способ нарисовать линию, где можно управлять количеством пикселей, используемых для рисования точки, и расстоянием между точками?
По сути, это более быстрый способ, чем использование SetPixel (который можно было бы использовать для решения проблемы, если бы он не был таким медленным.)
Было бы здорово получить фрагмент кода, показывающий, как это делается на C, C ++ или Delphi.
Спасибо за вашу помощь.
РЕДАКТИРОВАТЬ: Я попробовал ответ IInspectable об использовании ExtCreatePen, и это очень близко. Кажется, что единственный способ получить пиксели / точки вместо черточек - это использовать PS_ALTERNATE, но, когда это используется, невозможно указать интервал.
Для справки, и в случае, если я делаю ошибку, которую не вижу, ниже приведена тестовая программа, которую я написал. То, что я хотел бы, является повторяющейся последовательностью 1 точка (не тире), а затем 2 пробела. Вывод, полученный из тестовой программы, показан (увеличен) ниже: (верхняя строка получается с помощью PS_ALTERNATE, нижняя строка - с массивом, который задает 1 точку, 2 пробела - что дает 2 точки и 2 пробела.)
программа испытаний:
{$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.