Рисуем на десктопе

В статье рассматривается метод прорисовки объектов поверх всех окон на примере программы «конфетти», посредством лишь WinAPI функций, что неизменно влияет на размер приложения и его быстродействие. Кроме встроенных модулей Windows, никаких других задействовать не будем.[attachment=14]
Для начала нужно создать в Delphi новое приложение File – New – Application. Далее закрываем окно Uses1.pas, т.к. писать мы будем на чистом API, и такая легкая и удобная в обращении VCL нам не понадобится. Открываем окно с кодом программы Project – View Source и вставляем туда следующий код:


program Konfetti;

uses
  Windows;

var
  ScreenDC: hDC;
  BrushHandle, OldBrushHandle: HBRUSH;
  PenHandle, OldPenHandle: HPEN;
   color:cardinal;
   x,y,z,t:integer;
   begin
  ScreenDC := GetDC(0);
    randomize;
   z:=0;
repeat
color:=random(16777215);
  

  PenHandle := CreatePen(PS_SOLID, 1, color);
  OldPenHandle := SelectObject(ScreenDC, PenHandle);


  BrushHandle := CreateSolidBrush(color);
  OldBrushHandle := SelectObject(ScreenDC, BrushHandle);


  x:=Random(1027);
  y:=Random(768);
   t:=random(6);
Ellipse(ScreenDC,x,y,x+t,y+t);
                z:=z+1;
           sleep(7);
        until z=4000;
  
  SelectObject(ScreenDC, OldBrushHandle);
  DeleteObject(BrushHandle);

  SelectObject(ScreenDC, OldPenHandle);
  DeleteObject(PenHandle);

    ReleaseDC(0,ScreenDC);
  end.

Теперь запустим программу. Рабочий стол буквально завалило разноцветными конфетти. Разберем по порядку, как мы этого добились:
Для начала мы объявили переменную ScreenDC типа hDC, которая будет указывать на десктоп. Потом переменные BrushHandle, OldBrushHandle которые будут служить указателями на кисть и PenHandle, OldPenHandle, на карандаш, соответственно. Зачем, я расскажу ниже.
Как не трудно догадаться, color будет хранить значение цвета, а x,y,z,t будут служить как раз таки для самой прорисовки конфети.
В самом начале программы, мы получаем контекст непосредственно рабочего стола — ScreenDC := GetDC(0), включаем генерацию случайных (псевдослучайных) чисел, и указываем z (кол-во конфетти) равным нулю.
Запускаем блок repeat, будет повторять весь код, расположенный ниже, пока не выполнится условие until. Для начала создадим кисть и карандаш:


PenHandle := CreatePen(PS_SOLID, 1, color);
OldPenHandle := SelectObject(ScreenDC, PenHandle)

BrushHandle := CreateSolidBrush(color);
OldBrushHandle := SelectObject(ScreenDC, BrushHandle);

Затем уже обозначаем координаты прорисовки объекта, в нашем случае эллипса. Тут надо сделать небольшую заметку. Объект вообще прорисовывается по четырем координатам:


Ellipse(ScreenDC,x1,y1,x2,y2)

Первым параметром у нас идет контекст рабочего стола, это понятно. Координаты x1, y1 отвечают за начало прорисовки объекта, а x2, y2 — координаты, до которых он будет прорисовываться. Допустим, если использовать функцию с такими параметрами:


Ellipse(ScreenDC,500,500,505,505)

то у нас получится круг размером всего лишь в 5 пикселей. Он будет рисоваться слева направо. Если же мы зададим такие координаты Ellipse(ScreenDC,500,500,300,250), у нас он будет прорисовываться наоборот, справа налево, и в итоге получится самый настоящий эллипс огромных размеров. Таким образом, за размер конфетти в нашем коде отвечает переменная t. Сразу после прорисовки, идет запись еще одного объекта в копилку переменной z, и ожидание в течении 7 миллисекунд. Как только число снежинок достигнет 3к штук, операция повторения будет прервана (until z=3000;).
Затем нам нужно в обязательном (ну если не обязательном, то очень рекомендуемом) порядке отчистить память от наших указателей на кисти и рабочий стол:


  SelectObject(ScreenDC, OldBrushHandle);
  DeleteObject(BrushHandle);
  SelectObject(ScreenDC, OldPenHandle);
  DeleteObject(PenHandle);
  ReleaseDC(0,ScreenDC);

Вообще, чтобы нарисовать эллипс нам не нужно было создавать ни кистей, ни карандаша. Но в случае если бы мы не создали карандаш, наш круг (в данном случае красного цвета) был бы обведен черным цветом, что нам совсем не нужно. В том же случае, если бы мы не создали карандаш, круг внутри был бы белым. Если нужно нарисовать просто белый круг, (по идее, мы создавали эллипс, но так как обе координаты конечные его координаты одинаково удалены от начальных, то мы получаем круг), даже не нужно создавать кисть и карандаш. Тогда рисование эллипса сведется всего к нескольким строчкам кода:


programm Ellipse;
uses
Windows;
var
ScreenDC: hDC;
begin
ScreenDC := GetDC(0);
Ellipse(ScreenDC,50,50,88,88);
ReleaseDC(0,ScreenDC);
end.

Можно, например, также сделать прорисовку объектов прямо под курсором, и при перемещении мыши, будет эффект собственноручного рисования на рабочем столе.
Для этого надо объявить дополнительную переменную типа TPoint, которая как раз будет хранить координаты курсора. В получении координат курсора нам поможет API-функция getcursorpos:


program MouseGraf;

uses
  Windows;

var
  ScreenDC: hDC;
  BrushHandle, OldBrushHandle: HBRUSH;
  PenHandle, OldPenHandle: HPEN;
   color:cardinal;
   pos:Tpoint;
   graf:boolean;
   begin

color:=255;
repeat
   ScreenDC := GetDC(0);
    PenHandle := CreatePen(PS_SOLID, 1, color);
  OldPenHandle := SelectObject(ScreenDC, PenHandle);
    BrushHandle := CreateSolidBrush(color);
  OldBrushHandle := SelectObject(ScreenDC, BrushHandle);

    getcursorpos(pos); // получаем координаты мыши
    Ellipse(ScreenDC,pos.X,pos.Y,pos.X+5,pos.Y+5);

  SelectObject(ScreenDC, OldBrushHandle);
  DeleteObject(BrushHandle);
  SelectObject(ScreenDC, OldPenHandle);
  DeleteObject(PenHandle);
  ReleaseDC(0,ScreenDC);
until graf;      
end.

Запустив эту программу, мы наблюдаем следующее: перемещая мышь, мы тем самым прорисовываем линию вдоль траектории перемещения. Кстати, может быть, кто-то захочет автоматизировать этот процесс. Можно сидеть, откинувшись на спинку стула, и смотреть за тем, как мышь сама елозит по Десктопу, выводя какие-нибудь слова. Для этого существует API-функция setcursorpos(x,y). Например, создадим программу, которая будет рисовать букву «П». Код программы приведен ниже. Она не очень отличается от тех, что мы разбирали выше, поэтому не будем пояснять ее действие.


program P_Graff;
uses
  Windows;
var
  ScreenDC: hDC;
  BrushHandle, OldBrushHandle: HBRUSH;
  PenHandle, OldPenHandle: HPEN;
   color:cardinal;
     pos:TPoint;
   begin
  ScreenDC := GetDC(0);
color:=255;
   PenHandle := CreatePen(PS_SOLID, 1, color);
  OldPenHandle := SelectObject(ScreenDC, PenHandle);
   BrushHandle := CreateSolidBrush(color);
  OldBrushHandle := SelectObject(ScreenDC, BrushHandle);
   setcursorpos(500,300);
   getcursorpos(pos);
    repeat
      setcursorpos(pos.x,pos.Y+1);
   getcursorpos(pos);
      Ellipse(ScreenDC,pos.x,pos.y,pos.x+11,pos.y+11);
sleep(15);
  until pos.Y=450;
       setcursorpos(500,300);
      getcursorpos(pos);
      repeat
      setcursorpos(pos.x+1,pos.Y);
   getcursorpos(pos);
      Ellipse(ScreenDC,pos.x,pos.y,pos.x+11,pos.y+11);
sleep(15);
  until pos.X=600;
         setcursorpos(600,300);
      getcursorpos(pos);
      repeat
      setcursorpos(pos.x,pos.Y+1);
   getcursorpos(pos);
      Ellipse(ScreenDC,pos.x,pos.y,pos.x+11,pos.y+11);
sleep(15);
  until pos.y=450;
  // освобождаем память от созданного карандаша и кисти
  SelectObject(ScreenDC, OldBrushHandle);
  DeleteObject(BrushHandle);
  SelectObject(ScreenDC, OldPenHandle);
  DeleteObject(PenHandle);
  // освобождаем память от указателя на рабочий стол
  ReleaseDC(0,ScreenDC);
  end.

Ну вот и все ). Приветствуем критику статьи. Ждем Ваших вопросов и комментариев.