
https://youtu.be/hdOmqhATKIY
(Это уже тест на реальном устройстве под управлением ОС Андроид )
Неплохо, но скорость все равно не очень радует, так что нужно будет пробовать сделать "OpenGL версию".
Модератор: Модераторы

можешь дальше исправлять и получить выхлоп в этих местах в два а то и более раза.
1. Разумеется, я прекрасно понимаю, что причина выравнивания в оптимизации, но текущий код я пока вообще не оптимизировал (возможно, об этом позаботились его изначальные авторы (основу брал в примере из Hiasm, хотя, возможно, что это была далеко не первая его реализация), но сейчас я особо снова в этот код не вникал), к тому же вычисления с плавающей точкой требуют оптимизаций, отличных от целочисленных ухищрений.Seenkao писал(а): 05.07.2026 14:41:49можешь дальше исправлять и получить выхлоп в этих местах в два а то и более раза.
Почитать можешь здесь, при чём всю тему. Полезное пишут.
У меня работает только laz4android 2.0.12...Seenkao писал(а): 05.07.2026 21:02:10 Tools -> [LAMW] Android Module Wizard -> Build Released Signed...
А вообще, создавая новый проект, он вроде без отладки идёт.
Идет то может быть без отладки но apk собирает только такие......создавая новый проект, он вроде без отладки идёт.
Упс! Действительно есть такое. Спасибо! ПопробуюTools -> [LAMW] Android Module Wizard -> Build Released Signed...

Я уже кидал тебе ссылку. Дополнительно в файле androidwidget найди строчку (функцию) procedure TAndroidWidget.SetBounds(NewLeft, NewTop, NewWidth, NewHeight: integer); и замени её код:
Код: Выделить всё
procedure TAndroidWidget.SetBounds(NewLeft, NewTop, NewWidth, NewHeight: integer);
begin
// 18/06/2026: Lazarus 4.x up bug fix
// In Lazarus 4.0 and above, if we add a component by double clicking,
// it doesn't appear because it has a width=0, height=0 size.
if (csDesigning in ComponentState) then
begin
if NewWidth=0 then NewWidth:=100;
if NewHeight=0 then NewHeight:=40;
end;
if (Left=NewLeft) and (Top=NewTop) and (Width=NewWidth) and (Height=NewHeight) then Exit;
FLeft:=NewLeft;
FTop:=NewTop;
FWidth:=NewWidth;
FHeight:=NewHeight;
if (csDesigning in ComponentState) then
if Assigned(Parent) then
Parent.Invalidate
end;Спасибо я попробовал но что-то у меня пошло сильно не так ( пришлось откатываться к архивной копии впрочем возможно это жара так повлияла ( пару раз успел заметить заметил что монитор ресурсов показывал резкий разгон и скачек температуры встроенной видеокарты (читай ядра CPU) выше 100 градусов после чего система разумеется моментально влетала в перегрузку ( похоже эмулятор кривой))Seenkao писал(а): 05.07.2026 21:32:02 Я уже кидал тебе ссылку. Дополнительно в файле androidwidget найди строчку (функцию) procedure TAndroidWidget.SetBounds(NewLeft, NewTop, NewWidth, NewHeight: integer); и замени её код:
код есть? Может по ZenGL попробую запустить и посмотреть результат?
Код: Выделить всё
unit WaveEffectsBin;
{$mode delphi}
{$POINTERMATH ON}
interface
uses
Classes, SysUtils,Graphics, Math;
type
// Структура для хранения состояния волн
TWaveState = record
Height: Single;
Speed: Single;
end;
TWaveStateArray= array of TWaveState;
// ☆☆☆ УНИВЕРСАЛЬНАЯ ПРОЦЕДУРА РАБОТЫ С МАССИВОМ ПИКСЕЛЕЙ ☆☆☆
procedure WaveProcessorFXBin(
Pixels: PByte; // указатель на данные пикселей (RGBA)
Width, Height: Integer; // размеры изображения
var Waves:TWaveStateArray; // массив волн
GridSize: Integer; // размер сетки
X, Y: Integer; // -1 = только симуляция
Radius: Integer; // 0 = только симуляция
HeightWave: Single; // 0 = только симуляция
Viscosity, WavesSpeed, LightIntensity, Depth: Single;
Background: PByte = nil; // указатель на фон (nil = градиент)
BgWidth: Integer = 0; BgHeight: Integer = 0 // размеры фона
);
// Вспомогательные функции
procedure InitWaves(var Waves: TWaveStateArray; GridSize: Integer);
procedure ClearWaves(var Waves:TWaveStateArray; GridSize: Integer);
procedure GenerateGradient(Pixels: PByte; Width, Height: Integer);
procedure InitWavesRandom(var Waves: TWaveStateArray; GridSize: Integer; Amplitude: Single);
implementation
// ☆☆☆ ИНИЦИАЛИЗАЦИЯ ВОЛН (СЛУЧАЙНЫЕ ВОЗМУЩЕНИЯ) ☆☆☆
procedure InitWavesRandom(var Waves: TWaveStateArray; GridSize: Integer; Amplitude: Single);
var
i: Integer;
begin
for i := 0 to GridSize * GridSize - 1 do
begin
// Случайные возмущения с плавным переходом на границах
Waves[i].Height := (Random - 0.5) * Amplitude * 2;
Waves[i].Speed := (Random - 0.5) * Amplitude * 0.1;
end;
// Границы затухают
for i := 0 to GridSize - 1 do
begin
Waves[i].Height := Waves[i].Height * 0.5;
Waves[i].Speed := Waves[i].Speed * 0.5;
Waves[(GridSize - 1) * GridSize + i].Height :=
Waves[(GridSize - 1) * GridSize + i].Height * 0.5;
Waves[(GridSize - 1) * GridSize + i].Speed :=
Waves[(GridSize - 1) * GridSize + i].Speed * 0.5;
Waves[i * GridSize].Height := Waves[i * GridSize].Height * 0.5;
Waves[i * GridSize].Speed := Waves[i * GridSize].Speed * 0.5;
Waves[i * GridSize + GridSize - 1].Height :=
Waves[i * GridSize + GridSize - 1].Height * 0.5;
Waves[i * GridSize + GridSize - 1].Speed :=
Waves[i * GridSize + GridSize - 1].Speed * 0.5;
end;
end;
// ☆☆☆ СОЗДАНИЕ ГРАДИЕНТА ☆☆☆
procedure GenerateGradient(Pixels: PByte; Width, Height: Integer);
var
X, Y: Integer;
idx: Integer;
R, G, B: Byte;
RowPtr: PByte;
begin
if Pixels = nil then Exit;
for Y := 0 to Height - 1 do
begin
RowPtr := Pixels + Y * Width * 4;
for X := 0 to Width - 1 do
begin
idx := X * 4;
B := Round(30 + 40 * (Y / Height));
G := Round(80 + 100 * (Y / Height));
R := Round(180 + 55 * (1 - (Y / Height)));
RowPtr[idx] := 255; // Alpha
RowPtr[idx + 1] := R; // Red
RowPtr[idx + 2] := G; // Green
RowPtr[idx + 3] := B; // Blue
end;
end;
end;
// ☆☆☆ ИНИЦИАЛИЗАЦИЯ ВОЛН ☆☆☆
procedure InitWaves(var Waves: TWaveStateArray; GridSize: Integer);
var
i: Integer;
begin
for i := 0 to GridSize * GridSize - 1 do
begin
Waves[i].Height := 0;
Waves[i].Speed := 1;
end;
end;
procedure ClearWaves(var Waves:TWaveStateArray; GridSize: Integer);
begin
InitWaves(Waves, GridSize);
end;
// ☆☆☆ СОЗДАНИЕ ВОЛНЫ ☆☆☆
procedure MakeRipple(var Waves: array of TWaveState; GridSize: Integer;
CX, CY, Radius: Integer; HeightWave: Single);
var
X, Y, DX, DY, Dist, RadiusSq: Integer;
Profile: Single;
idx: Integer;
begin
if GridSize < 1 then Exit;
if (CX < 0) or (CY < 0) or (CX >= GridSize) or (CY >= GridSize) then Exit;
RadiusSq := Radius * Radius;
for X := Max(0, CX - Radius) to Min(GridSize - 1, CX + Radius) do
begin
DX := X - CX;
for Y := Max(0, CY - Radius) to Min(GridSize - 1, CY + Radius) do
begin
DY := Y - CY;
Dist := DX * DX + DY * DY;
if Dist <= RadiusSq then
begin
Profile := 1.0 - Sqrt(Dist) / Radius;
Profile := Profile * Profile * (3.0 - 2.0 * Profile);
idx := Y * GridSize + X;
Waves[idx].Height := Waves[idx].Height + HeightWave * Profile;
end;
end;
end;
end;
// ☆☆☆ СИМУЛЯЦИЯ ВОЛН ☆☆☆
procedure SimulateWaves(var Waves: array of TWaveState; GridSize: Integer;
Viscosity, WavesSpeed: Single);
var
X, Y, idx: Integer;
Ddx, Ddy, Viscosity1: Single;
begin
if GridSize < 3 then Exit;
if Length(Waves) < GridSize * GridSize then Exit;
Viscosity1 := 1.0 - Viscosity;
// Обновляем скорости
for Y := 1 to GridSize - 2 do
for X := 1 to GridSize - 2 do
begin
idx := Y * GridSize + X;
Ddx := (Waves[idx + 1].Height - Waves[idx].Height) -
(Waves[idx].Height - Waves[idx - 1].Height);
Ddy := (Waves[idx + GridSize].Height - Waves[idx].Height) -
(Waves[idx].Height - Waves[idx - GridSize].Height);
Waves[idx].Speed := Waves[idx].Speed + (Ddx + Ddy) / WavesSpeed;
end;
// Обновляем высоты
for Y := 1 to GridSize - 2 do
for X := 1 to GridSize - 2 do
begin
idx := Y * GridSize + X;
Waves[idx].Height := (Waves[idx].Height + Waves[idx].Speed) * Viscosity1;
end;
// Границы (затухание)
for X := 1 to GridSize - 2 do
begin
Waves[X].Height := Waves[GridSize + X].Height * 0.95;
Waves[(GridSize - 1) * GridSize + X].Height :=
Waves[(GridSize - 2) * GridSize + X].Height * 0.95;
end;
for Y := 1 to GridSize - 2 do
begin
Waves[Y * GridSize].Height := Waves[Y * GridSize + 1].Height * 0.95;
Waves[Y * GridSize + GridSize - 1].Height :=
Waves[Y * GridSize + GridSize - 2].Height * 0.95;
end;
end;
// ☆☆☆ РЕНДЕРИНГ ВОЛН В МАССИВ ПИКСЕЛЕЙ ☆☆☆
procedure RenderWavesToPixels(
var Waves: array of TWaveState;
GridSize: Integer;
Pixels: PByte;
Width, Height: Integer;
Background: PByte;
BgWidth, BgHeight: Integer;
LightIntensity, Depth: Single);
var
X, Y: Integer;
GridX, GridY: Integer;
Dx, Dy, HeightFactor, WaveHeight: Single;
Light: Integer;
XMap, YMap: Integer;
R, G, B: Integer;
idx, bgIdx: Integer;
RowPtr, BgRowPtr: PByte;
BgX, BgY: Integer;
begin
if Pixels = nil then Exit;
for Y := 0 to Height - 1 do
begin
RowPtr := Pixels + Y * Width * 4;
for X := 0 to Width - 1 do
begin
// Преобразуем координаты пикселя в координаты сетки
GridX := (X * GridSize) div Width;
GridY := (Y * GridSize) div Height;
if GridX >= GridSize then GridX := GridSize - 1;
if GridY >= GridSize then GridY := GridSize - 1;
idx := GridY * GridSize + GridX;
WaveHeight := Waves[idx].Height;
// Вычисляем наклон
if GridX + 1 < GridSize then
Dx := Waves[GridY * GridSize + GridX + 1].Height - WaveHeight
else
Dx := 0;
if GridY + 1 < GridSize then
Dy := Waves[(GridY + 1) * GridSize + GridX].Height - WaveHeight
else
Dy := 0;
// Фактор глубины (искажение)
HeightFactor := WaveHeight + Depth;
if HeightFactor < 1.0 then HeightFactor := 1.0;
// Смещение пикселя (эффект преломления)
XMap := X + Round(Dx * HeightFactor * 0.5);
YMap := Y + Round(Dy * HeightFactor * 0.5);
if XMap < 0 then XMap := 0;
if XMap >= Width then XMap := Width - 1;
if YMap < 0 then YMap := 0;
if YMap >= Height then YMap := Height - 1;
// Вычисляем освещение
Light := Round((Dx + Dy) * LightIntensity * 0.5);
Light := Max(-128, Min(127, Light));
// Добавляем блик на высоких волнах
if Abs(WaveHeight) > 3.0 then
Light := Light + Round(Abs(WaveHeight)) * 2;
// Получаем пиксель из фона
if Background <> nil then
begin
// Если фон другого размера — масштабируем координаты
if (BgWidth > 0) and (BgHeight > 0) then
begin
BgX := (XMap * BgWidth) div Width;
BgY := (YMap * BgHeight) div Height;
if BgX >= BgWidth then BgX := BgWidth - 1;
if BgY >= BgHeight then BgY := BgHeight - 1;
BgRowPtr := Background + (BgY * BgWidth + BgX) * 4;
end
else
begin
BgRowPtr := Background + (YMap * Width + XMap) * 4;
end;
R := BgRowPtr[1] + Light;
G := BgRowPtr[2] + Light;
B := BgRowPtr[3] + Light;
end
else
begin
// Если фона нет — рисуем волны на чёрном
R := 100 + Light;
G := 150 + Light;
B := 255 + Light;
end;
R := Max(0, Min(255, R));
G := Max(0, Min(255, G));
B := Max(0, Min(255, B));
// Записываем результат
idx := X * 4;
if Background <> nil then
RowPtr[idx] := BgRowPtr[0] // Alpha из фона
else
RowPtr[idx] := 255; // Alpha = 255
RowPtr[idx + 1] := R;
RowPtr[idx + 2] := G;
RowPtr[idx + 3] := B;
end;
end;
end;
// ☆☆☆ ГЛАВНАЯ УНИВЕРСАЛЬНАЯ ПРОЦЕДУРА ☆☆☆
procedure WaveProcessorFXBin(
Pixels: PByte;
Width, Height: Integer;
var Waves:TWaveStateArray;
GridSize: Integer;
X, Y: Integer;
Radius: Integer;
HeightWave: Single;
Viscosity, WavesSpeed, LightIntensity, Depth: Single;
Background: PByte = nil;
BgWidth: Integer = 0; BgHeight: Integer = 0);
var
NeedFreeBackground: Boolean;
GradientPixels: array of Byte;
begin
if Pixels = nil then Exit;
if GridSize <= 0 then GridSize := 40;
// Проверяем и инициализируем массив волн
if Length(Waves) <> GridSize * GridSize then
SetLength(Waves, GridSize * GridSize);
// Инициализация волн при первом вызове
if Waves[0].Height = 0 then
InitWaves(Waves, GridSize);
NeedFreeBackground := False;
// Если фона нет — создаём градиент
if Background = nil then
begin
SetLength(GradientPixels, Width * Height * 4);
GenerateGradient(@GradientPixels[0], Width, Height);
Background := @GradientPixels[0];
BgWidth := Width;
BgHeight := Height;
NeedFreeBackground := True;
end;
// Если заданы координаты — создаём волну
if (X >= 0) and (Y >= 0) and (Radius > 0) and (HeightWave > 0) then
MakeRipple(Waves, GridSize, X, Y, Radius, HeightWave);
// Симуляция физики волн
SimulateWaves(Waves, GridSize, Viscosity, WavesSpeed);
// Рендеринг в пиксели
RenderWavesToPixels(
Waves, GridSize,
Pixels, Width, Height,
Background, BgWidth, BgHeight,
LightIntensity, Depth
);
// Освобождаем временный градиент
if NeedFreeBackground then
SetLength(GradientPixels, 0);
end;
end.
Код: Выделить всё
{-------------------------------------}
Const MB:Boolean=false;
Xo: Integer=-10;
Yo: Integer=-10;
procedure TAndroidModule1.jImageView1TouchDown(Sender: TObject; Touch: TMouch);
var
X,Y,X1, Y1: Integer;
Radius: Integer;
Height: Single;
begin
if not FIsRunning then exit;
X:=Trunc(Touch.Pt.X); Y:=Trunc(Touch.Pt.Y);
If mb and ( Sqrt(Sqr(X-Xo)+Sqr(y-Yo)) >3 ) then begin
Xo:=X; Yo:=Y;
end;
mb:=True;
X1:= Trunc(( X*FGridSize )/fWidth);
Y1:= Trunc(( Y*FGridSize )/fHeight);
Radius := 5 + Random(15);
Height := FRandomIntensity * (0.5 + Random * 0.5);
MakeRippleAt(X1, Y1, 10, 12.0);
end;
procedure TAndroidModule1.jImageView1TouchMove(Sender: TObject; Touch: TMouch);
var
X,Y: Integer;
begin
X:=Trunc(Touch.Pt.X); Y:=Trunc(Touch.Pt.Y);
If mb and ( Sqrt(Sqr(X-Xo)+Sqr(y-Yo)) >3 ) then begin
jImageView1TouchDown(Sender, Touch);
end;
end;
procedure TAndroidModule1.jImageView1TouchUp(Sender: TObject; Touch: TMouch);
begin
if mb then mb:=False;
end;