Выбрать главу

Алгоритм работы прост: по экрану двигаются частицы, за каждой из которых тянется след. Срок жизни любой частицы ограничен, новые точки появляются в месте расположения курсора:

const

MaxParticles = 100000; // Верхнее ограничение по количеству точек type

TParticle = record // Тип для описания отдельной точки

X : Integer; // Координаты точки на экране

Y : Integer;

Angle : Single; // Угол направления движения

Speed : Integer; // Скорость движения

Decay : Single; // Время жизни

HalfLife : Single; // Срок существования

// Величина сдвига для угла, движение по спирали

AngleAdjustment : Single;

end;

var // Глобальные переменные модуля

ParticleCount : Integer = 10000; // Текущее количество точек

Particle : Array [0..MaxParticles] of TParticle; // Массив частиц

mouseX, mouseY : Integer; // Координаты курсора

// Растровый массив, хранит цвет для всех пикселов экрана

Pict : Array [0..ScreenWidth - 1, 0..ScreenHeight - 1] of Byte;

BlurFactor : Integer = 1; // Задает величину размытости следа

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

for Index := 0 to MaxParticles do

with Particle [Index] do begin

Speed := 1 + round (random (3)) ;

Angle : = random * 2 * Pi;

X := random (ScreenWidth - 1) + 1;

Y := random (ScreenHeight - 1) + 1;

Decay := random;

HalfLife := random / 20;

AngleAdjustment := random / 20;

end;

При каждом обновлении экрана отслеживаются новые позиции частиц и усредняются цвета пикселов, подобно предыдущему примеру:

for Index := 0 to ParticleCount do

with Particle [Index] do begin

Decay := Decay - HalfLife; // Уменьшить время жизни

// Срок существования прошел, появляется новая точка

if Decay <= 0 then begin

Decay := 1;

X := mouseX; // В позиции курсора

Y := mouseY;

end;

Angle := Angle + AngleAdjustment; // Движение по спирали

If Angle >= 2 * Pi then Angle := 0; //От переполнения

X := X + round (cos(Angle) * Speed); // Новая позиция

Y := Y + round (sin(Angle) * Speed);

// Точка, ушедшая за границу экрана

if (X > ScreenWidth - 2) or (X < 2) then begin

X := mouseX; // Переместить в позицию курсора

Y : = mouseY;

Angle := random * 2 * Pi;

end

else if (Y > ScreenHeight - 2) or (Y < 2) then begin

X := mouseX;

Y := mouseY;

Angle := random '* 2 * Pi;

end;

// "Отображение" точки

Pict [X, Y] := Speed * 16 + 186;

end;

// Эффект размытости for Index := 1 to BlurFactor do for X := 2 to ScreenWidth - 2 do

for Y := 2 to (ScreenHeight - 2) do begin

// Усреднение значения девяти соседних элементов Accum := 0;

Accum := Accum + Pict [X, Y] +

Pict[X, Y + 1] + Pict[X, Y - 1] +

Pict[X + 1, Y] + Pict[X - 1, Y] +

Pict[X + 1, Y + 1] + Pict[X - 1, Y - 1] +

Pict[X + 1, Y - 1] + Pict[X - 1, Y + 1];

Accum := Accum div 9; // Усреднение значений

// соседних пикселов

Pict [X, Y] :=' Accum;

end;

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

for Index := 0 to ScreenWidth - 1 do begin

Pict[Index, 0] := 127;

Pict[Index, ScreenHeight - 1] := 127;

Pict[Index, 1] := 127;

Pict[Index, ScreenHeight - 2] := 127;

end;

for Index := 0 to ScreenHeight - 1 do begin

PictfO, Index] := 127;

Pict[ScreenWidth - 1, Index] := 127;

Pict[l, Index] := 127;

Pict[ScreenWidth - 2, Index] := 127;

end;

С помощью клавиш <Ноте> и <End> можно менять количество частиц, а с помощью клавиш <Page Up> и <Page Down> - управлять степенью усреднения пикселов.

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

ZeroMemory (desc. IpSurface, desc.lPitch * ScreenHeight * (ScreenBitDepth div 8) ) ;

Также здесь нельзя использовать значение ширины экрана вместо lPitch, т. к. из-за выравнивания памяти это могут быть разные значения. Ширина поверхности "подгоняется" к границам параграфов, т. е. должна быть кратна 4-м байтам.

Массивы в видеопамять приходится переносить медленным способом - поэлементно. Одна ячейка массива занимает байт, при разрешении экрана в 16 разрядов на пиксел массив скопируется только в первую половину памяти поверхности. Если же вы в своем приложении не собираетесь менять разрешение, то вполне можете копировать массив целиком, одной командой CopyMemory.

Поскольку значения в массиве pict лежат в пределах диапазона типа Byte, то для 16-битного режима картинка получится не очень выразительной и отображается оттенками одного цвета.

Сохранение растровых изображений

Наверняка перед вами рано или поздно встанет задача сохранения получающихся картинок. Если вы попытаетесь их скопировать в буфер обмена для дальнейшей вставки в рисунок графического редактора, то обнаружите проблему с 256-цветными приложениями. Картинки будут искажаться, поскольку палитра таких изображений будет отличной от палитры рисунка.

Я приведу простейшее решение проблемы, основанное на использовании объекта класса TBitmap. В предыдущем примере обработчик формы нажатия клавиши приведите к следующему виду:

procedure TfrmDD. FormKeyDown (Sender: TObject; var Key: Word

Shift: TShiftState) ; var

BitMap : TBitmap; // Для записи картинок в файл begin

case Key of

VK NEXT : BlurFactor := BlurFactor + 1;

VK_PRIOR : begin

BlurFactor := BlurFactor - 1;

if BlurFactor < 1 then BlurFactor := 1;

end;

VK_HOME : begin

Inc (ParticleCount, 1000);

if ParticleCount > MaxParticles then ParticleCount := MaxParticles;

end;

VK_END : begin

Dec {ParticleCount, 1000);

if ParticleCount < 2000 then ParticleCount := 2000;

end;

// По нажатию пробела содержимое экрана сохраняется в файле

VK_SPACE : begin

BitMap := TBitmap.Create;

BitMap.PixelFormat := pf24bit; // Разрядность задаем 24

BitMap.Height := ClientHeight;

BitMap.Width := ClientWidth;

// Копируем в BitMap содержимое экрана

BitBlt(BitMap.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,

Canvas.Handle, 0, 0, SRCCOPY);

BitMap.SaveToFile ('l.bmp'); // Записываем в файл

end;

VK_ESCAPE,

VK_F12 : Close;

end;

end;

Записываются 24-битные файлы, и информация о цвете не теряется в любом случае.

Доступ к пикселам в 16-битном режиме

В таком режиме информация о цвете пиксела разделяется на три цветовые составляющие, но шестнадцать на три нацело не делится, поэтому разработчики вынуждены прибегать к неравномерному распределению. Наиболее распространенной является схема 5-6-5. В этом формате первые пять битов хранят значение красного оттенка, следующие шесть битов отводятся под зеленую составляющую, ну и последние пять битов заняты оттенком синего. Всего получается 65 536 (216) различных цветов. Из них по 32 градации красного и синего, 64 градации зеленого.

Схема 5-6-5 является самой распространенной. Поэтому для начала будем опираться именно на нее. Как быть в случае другого формата, рассмотрим позднее.

Для примера возьмем цвет, образованный следующими значениями составляющих:

* красный, 5 бит: 00011; зеленый, 6 бит: 001011; синий, 5 бит: 00101.