Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 02.10.2015, 16:23
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию Быстрое определение цвета пикселя по 4-ем позициям

Приветствую!

Очень прошу понимающих людей объяснить мне как реализовать поставленную задачу.

Определить цвет пикселя (одного) не проблема, цвет программа определяет мгновенно, а вот если мониторить сразу 4 - проблема.. программа часто не срабатывает при нахождение цвета...

Я делаю так:

Код:
var
  bmp: TBitmap;
  h1: HWND;
  DC: HDC;
  dx,dy,fx,fy,jx,jy,kx,ky: Integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  bmp := TBitmap.Create;
  bmp.Width := 1; bmp.Height := 1;
  bmp.PixelFormat := pf32bit;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ReleaseDC(h1,DC);
end;

procedure TForm1.btn1Click(Sender: TObject);
begin
  h1 := FindWindow(ProgName, nil);
  DC := GetDC(FindWindow(ProgName,''));
  
  //Если программа запущена задаем координаты которые будем проверять. Кстати таймеры все стоят на интервале равным = 1
  if h1 <> 0 then
     begin
       dx:=646;
       dy:=715; 
       fx:=724; 
       fy:=715; 
       jx:=802;
       jy:=715; 
       kx:=843; 
       ky:=715;

       //Включаем таймеры которые будут это делать (каждый будет смотреть свой пиксель)

       tmr1.Enabled:=True;
       tmr2.Enabled:=True;
       tmr3.Enabled:=True;
       tmr4.Enabled:=True;
     end else
     begin
       ShowMessage('Game Not Started');
     end;
end;

//Таких таймеров 4 штуки. Код в них похож, только координаты пикселей разные и сравниваемый цвет.

procedure TForm1.tmr1Timer(Sender: TObject);
var
  c: TColor;
  rr,gg,bb: Integer;
begin
  BitBlt(bmp.Canvas.Handle,0,0,1,1,dc,dx,dy,SRCCOPY);
  //releasedc(h1,dc);
  c := pinteger(bmp.Scanline[0])^;
  c := ((c and $FF) shl 16) or (c and $FF00) or ((c shr 16) and $FF);
  rr:=GetRValue(c);
  gg:=GetGValue(c);
  bb:=GetBValue(c);

  if (rr>80) and (gg>80) and (bb>100) then EmulateKey(h1, Ord('D'));

end;

...
procedure TForm1.tmr2Timer(Sender: TObject);
...
procedure TForm1.tmr3Timer(Sender: TObject);
...
procedure TForm1.tmr4Timer(Sender: TObject);
...

Если один таймер стоит - все работает. Два - более или менее тоже а вот 4 уже то срабатывает то нет...
Как лучше и правильнее сделать? Я буду вам очень благодарен за помощь.

Последний раз редактировалось Alloc, 02.10.2015 в 16:27.
Ответить с цитированием
  #2  
Старый 02.10.2015, 17:16
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Эм... что это за ...фигня? А если тебе понадобится проверять 10 тысяч пикселей, ты сделаешь 10 тысяч таймеров?
Что помешало загнать координаты и цвета в массивы и в одном таймере один раз брать картинку, пробегать в цикле по массиву и проверять сразу все нужные пиксели?
Да и не очень понятно, зачем 1 мс в таймере. Человек видит 24 кадра в секунду, т.е. минимальная различаемая человеком задержка - 40 миллисекунд. В хороших играх FPS обычно делается в районе 60 (16 мс), так обеспечивается большая зрительная гладкость, однако для робота это уже лишено смысла. Ну а быстрее вообще никто не рисует, кроме случаев 3Д-изображения для специальных очков. Какой смысл проверять каждую миллисекунду, если игра перерисовывает не чаще, чем раз в 40?
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 02.10.2015 в 17:22.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (02.10.2015)
  #3  
Старый 02.10.2015, 20:01
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Спасибо за ответ. Я понимаю, для Вас этот код полная жесть, но я только в начале пути познания программирования. Я примерно представляю, что Вы имеете в виду, попытался воспроизвести Ваши советы, но не выходит что то.. Я был бы Вам очень признателен если Вы напишите небольшой пример того, как должен выглядеть массив координат и цветов и как пробегать по нему циклом... Если Вам не трудно, буду очень признателен
Ответить с цитированием
  #4  
Старый 02.10.2015, 21:05
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Ну простейший вариант (если не использовать структуры) - 3 массива
Код:
var x, y: array of integer;
colors: array of Longint;
Заполнять соответственно координатами и цветами. x[0], y[0] - координаты первой точки, colors[0] - цвет первой точки. Ну и потом взять картинку и как-то так.
Код:
for i := 0 to pointsCount-1 do begin
  c := ColorToRGB(bmp.Canvas.Pixels[x[i], y[i]]);
  if abs(GetRValue(c) - GetRValue(colors[i]))< delta and
     abs(GetGValue(c) - GetGValue(colors[i]))< delta and
     abs(GetBValue(c) - GetBValue(colors[i]))< delta then
      EmulateKey(h1, Ord('D'));
end;
Где delta - максимальная мера отличия по каждому цвету.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 02.10.2015 в 21:10.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (02.10.2015)
  #5  
Старый 02.10.2015, 21:18
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Bargest, Большое спасибо за помощь, Вы мне очень помогли. Буду пробовать. А как быть насчет ReleaseDC? Я слышал его обязательно нужно ставить так как может быть утечка памяти. Если его все так и необходимо прописывать, то куда? Тоже в таймер или достаточно в событие Form.Destroy?

Да, и переменные i, pointsCount и delta - объявить как Integer? В этом случае почему то компилятор указывая на знак "<" говорит о "Incompatible types"

Последний раз редактировалось Alloc, 02.10.2015 в 21:43.
Ответить с цитированием
  #6  
Старый 02.10.2015, 23:08
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Цитата:
Если его все так и необходимо прописывать, то куда?
Есть такая шуточная поговорка: 7 раз NEW, 7 раз DELETE. Сколько раз выделил, столько раз и освобождаешь. Если DC создаешь один раз при нажатии кнопки, а удалять его будешь в таймере, то сколько раз удалится то, что создано 1 раз?
Цитата:
Да, и переменные i, pointsCount и delta - объявить как Integer?
ABS возвращает дробное. Это функция "модуль". И сравнивается оно с delta. Значит delta какого типа?
Можно было и справку почитать на этот случай.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (02.10.2015)
  #7  
Старый 02.10.2015, 23:39
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от Bargest
Если DC создаешь один раз при нажатии кнопки, а удалять его будешь в таймере, то сколько раз удалится то, что создано 1 раз?
т.е. если я правильно Вас понял, то можно создать один раз при нажатии на кнопку и отпустить при событии Form.Destroy?
Цитата:
Сообщение от Bargest

Значит delta какого типа?
Variant?))

а что же делать с:

Код:
BitBlt(bmp.Canvas.Handle,0,0,1,1,dc,dx,dy,SRCCOPY);

его придется то же поместить в цикл?

Код:
for i := 0 to pointsCount-1 do
  begin

    BitBlt(bmp.Canvas.Handle,0,0,1,1,dc,x[i],y[i],SRCCOPY);

    c := ColorToRGB(bmp.Canvas.Pixels[x[i], y[i]]);
    if abs(GetRValue(c) - GetRValue(colors[i])) < delta and
       abs(GetGValue(c) - GetGValue(colors[i])) < delta and
       abs(GetBValue(c) - GetBValue(colors[i])) < delta then
       EmulateKey(h1, Ord('D'));
  end;

Последний раз редактировалось Alloc, 02.10.2015 в 23:45.
Ответить с цитированием
  #8  
Старый 03.10.2015, 17:34
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Цитата:
создать один раз при нажатии на кнопку и отпустить при событии Form.Destroy?
В корне не верно. Снова считаем. Что будет, если кнопку не нажать? При закрытии формы удалится то, чего нет. Что будет, если кнопку нажать 100 раз? 99 из выделенных DC не удалятся.
Очевидно, удалять надо:
1) Прямо перед созданием, если DC не нуль. То есть инициализировать DC нулем, и перед выделением проверять: если он не нуль - удалить и занулить. Так обеспечится удаление того, что нагенерируется тыканием на кнопку.
2) При закрытии формы для удаления последнего.
При этом надо подумать на тему: а что будет, если анализируемое окно закроется во время работы нашей программы?
Поэтому наилучший вариант совсем иной - выделять DC при заходе в таймер и освобождать при выходе. А сохранять хендл окна, а не его DC.
Цитата:
его придется то же поместить в цикл?
Этот код копирует 1 пиксель с DC в битмап как отдельное изображение. В чем глубинный смысл сего извращения, я так и не осознал. Я предлагаю скопировать всё DC в битмап до цикла. Соответственно, размер битмапа должен быть как у окна в данный момент.
Или же брать пиксели в цикле прямо с DC, что видится самым логичным решением. Задача ведь требует считать малое число (четыре) отдельных разбросанных пикселей с экрана, а не обрабатывать фотографии. Зачем вся эта фигня с BMP?
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 03.10.2015 в 17:41.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (03.10.2015)
  #9  
Старый 03.10.2015, 19:09
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от Bargest
В корне не верно. Снова считаем. Что будет, если кнопку не нажать?
спасибо, теперь ясно)

Цитата:
Сообщение от Bargest
Этот код копирует 1 пиксель с DC в битмап как отдельное изображение. В чем глубинный смысл сего извращения, я так и не осознал.
Изначально была идея для поиска пикселей использовать ScanLine так как я где то слышал, что это самый быстрый способ... но на деле я что то не вижу разницы между этим и через GetPixel.

Цитата:
Сообщение от Bargest
Я предлагаю скопировать всё DC в битмап до цикла. Соответственно, размер битмапа должен быть как у окна в данный момент.
Т.е. сделать скриншот всего экрана DC? Я попробую, спасибо. Но так или иначе, мне кажется что это мало изменит ситуацию а может даже усугубит? Ведь раньше мы сохраняли всего один пиксель а сейчас целый экран.. Или это не так?

Цитата:
Сообщение от Bargest
Или же брать пиксели в цикле прямо с DC, что видится самым логичным решением. Задача ведь требует считать малое число (четыре) отдельных разбросанных пикселей с экрана, а не обрабатывать фотографии. Зачем вся эта фигня с BMP?

Вы имеете в виду через GetPixel? Я пробовал так делать, так же в таймере определял цвет каждого пикселя, сравнивал и выполнял соответствующее действие.. что то типо такого:
Код:
PixColorA:= GetPixel(DC,dx,dy);
  ra:=GetRValue(PixColorA);
  ga:=GetGValue(PixColorA);
  ba:=GetBValue(PixColorA);

  PixColorB:= GetPixel(DC,fx,fy);
  rb:=GetRValue(PixColorB);
  gb:=GetGValue(PixColorB);
  bb:=GetBValue(PixColorB);

  PixColorC:= GetPixel(DC,jx,jy);
  rc:=GetRValue(PixColorC);
  gc:=GetGValue(PixColorC);
  bc:=GetBValue(PixColorC);

  PixColorD:= GetPixel(DC,kx,ky);
  rd:=GetRValue(PixColorD);
  gd:=GetGValue(PixColorD);
  bd:=GetBValue(PixColorD);

  if (ra>80) and (ga>80) and (ba>100) then EmulateKey(h1, Ord('D'));
  if (rb>36) and (gb>100) and (bb>130) then EmulateKey(h1, Ord('F'));
  if (rc>36) and (gc>100) and (bc>130) then EmulateKey(h1, Ord('J'));
  if (rd>80) and (gd>80) and (bd>100) then EmulateKey(h1, Ord('K'));
но как я писал в своем первом посте, программа часто не срабатывала и сильно тормозила саму игру так как читала эти 4 пикселя с интервалом в 1мс в таймере. Вы говорили выше про 1мс и про 24 кадра но дело в том, что в игре эти предметы которые я пытаюсь поймать делая эту программу двигаются очень быстро и если я поставлю допустим 100мс или даже 40мс в таймере они не определятся.. (ну это конечно в моем случае). Может есть другой способ для этой задачи? Я уже пробовал и в 4 потока запускать, все так же) Уже не знаю... на Вас вся надежда

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


Последний раз редактировалось Alloc, 03.10.2015 в 19:40.
Ответить с цитированием
  #10  
Старый 03.10.2015, 19:30
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Цитата:
Изначально была идея для поиска пикселей использовать ScanLine так как я где то слышал, что это самый быстрый способ...
*Вздох* Ну как ScanLine, созданный для ускорения доступа к строке картинки через получение указателя на её в виде массива может ускорить доступ к одному случайно взятому пикселю?
Цитата:
Ведь раньше мы сохраняли всего один пиксель а сейчас целый экран.. Или это не так?
Да, это будет сильно медленней. Но не думаю, что разница будет заметна. Логичней всего просто брать отдельные пиксели прямо с DC, как я говорил. Единственный плюс BMP в данном случае - относительное удобство работы с пикселями.
Тем не менее, копировать 1 пиксель в BMP (т.е. брать его средствами винды из DC), чтобы потом сделать к нему scanline и взять из него пиксель - это по меньшей мере КРАЙНЕ странно.
Цитата:
Вы говорили выше про 1мс и про 24 кадра <...> и если я поставлю допустим 100мс или даже 40мс в таймере они не определятся..
Элементарная логика подсказывает, что если сделать частоту выборки в 2 раза выше частоты кадров, то попадать будет однозначно в каждый кадр даже с учетом погрешностей. Можно попробовать определить настоящую частоту перерисовки, надо только подумать, чем. На самом деле она может плавать. Но в простой игрушке (как я понимаю из скриншота с другой темы) рисовать чаще 25 раз в секунду нет никакого смысла. Отсюда возникает другой вопрос: а ты вообще уверен, что игра отрисовывает быстрый объект во всех промежуточных положениях?
Вот есть в игре объект. Он движется со скоростью 200 пикселей в секунду. Пусть мы перерисовываем 25 раз в секунду. Значит между двумя кадрами объект переместится на 200/25=8 пикселей. Если часть объекта нужного цвета занимает меньше 8 пикселей, то какой-то пиксель так и не засветится нужным цветом. Какая реальная скорость объекта? Если он пролетает монитор в 1920х1080 по горизонтали за секунду, то между кадрами объект телепортируется на 76 пикселей. Так делается анимация.
Цитата:
программа часто не срабатывала и сильно тормозила саму игру так как читала эти 4 пикселя с интервалом в 1мс в таймере.
В винде приложения "переключаются" примерно раз в 10-15 миллисекунд, насколько я помню. Если они не мудрили (а опять же по тому скрину видно, что вряд ли мудрили), то игра просто не может перемещать объекты чаще. Твой таймер скорее всего просто тормозит комп излишней циклической проверкой одного и того же.

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

EDIT в виду добавления скриншота:
Так очевидно, надо мониторить изменение столбиков пикселей нижних областей. Как только хоть в одном пикселе из столбика произошло переключение цвета с черного на какой-то - нажимать. Если даже тут FPS=60 (что было бы странно), то нет смысла ставить таймер меньше, чем на 8-10мс.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 03.10.2015 в 19:51.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (03.10.2015)
  #11  
Старый 03.10.2015, 19:53
Аватар для Страдалецъ
Страдалецъ Страдалецъ вне форума
Гуру
 
Регистрация: 09.03.2009
Адрес: На курорте, из окна вижу теплое Баренцево море. Бррр.
Сообщения: 4,723
Репутация: 52347
По умолчанию

Я бы вам рекомендовал записать хотя-бы 10 секунд игры в скрины. Для чистоты эксперимента их можно первоначально в массив записать, а уже потом весь массив сохранить на диск. А уже потом проанализировав скрины можно и со скоростью определится и с правильной позицией захвата пикселей.
__________________
Жизнь такова какова она есть и больше никакова.
Помогаю за спасибо.
Ответить с цитированием
Этот пользователь сказал Спасибо Страдалецъ за это полезное сообщение:
Alloc (03.10.2015)
  #12  
Старый 03.10.2015, 20:34
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Поддерживаю. Можно хоть тем же чудесным таймером на 1 мс. Заодно и реальные метки времени прикопать для кадров.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (03.10.2015)
  #13  
Старый 03.10.2015, 20:48
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от Страдалецъ
Я бы вам рекомендовал записать хотя-бы 10 секунд игры в скрины. Для чистоты эксперимента их можно первоначально в массив записать, а уже потом весь массив сохранить на диск. А уже потом проанализировав скрины можно и со скоростью определится и с правильной позицией захвата пикселей.
да, насчет позиций пикселей (координат) я примерно так и сделал.. а вот как "определиться со скоростью"? Взять два кадра допустим и сравнить их, проследить на сколько за один кадр передвинулся объект? Или как?

Цитата:
Сообщение от Bargest
EDIT в виду добавления скриншота:
Так очевидно, надо мониторить изменение столбиков пикселей нижних областей. Как только хоть в одном пикселе из столбика произошло переключение цвета с черного на какой-то - нажимать. Если даже тут FPS=60 (что было бы странно), то нет смысла ставить таймер меньше, чем на 8-10мс.

т.е. ваш вариант в принципе все еще актуален в данной ситуации? Я про массивы из координат и цветов и т.д.? И может быть лучше брать цвет пикселя через GetPixel? Или Вы имели в виду что то другое?

Последний раз редактировалось Alloc, 03.10.2015 в 20:54.
Ответить с цитированием
  #14  
Старый 03.10.2015, 21:33
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Я говорю о том, что сравнивать, скорее всего, придется не один пиксель для обнаружения объекта, а ловить любые изменения в небольшом ряду по направлению движения. В остальном-то смысл тот же.
Но сначала лучше прикинуть скорость. "записывать" в таймере кусочки картинки в массив несколько секунд и потом сохранять с временными метками. Хоть увидишь, что "видит" твоя программа.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (03.10.2015)
  #15  
Старый 03.10.2015, 22:37
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от Bargest
Я говорю о том, что сравнивать, скорее всего, придется не один пиксель для обнаружения объекта, а ловить любые изменения в небольшом ряду по направлению движения.
извините за наглость, но если это не трудно, не могли бы показать как это примерно выглядит?

Цитата:
Сообщение от Bargest
В остальном-то смысл тот же.
Но сначала лучше прикинуть скорость. "записывать" в таймере кусочки картинки в массив несколько секунд и потом сохранять с временными метками. Хоть увидишь, что "видит" твоя программа.
это уже слишком сложно для меня, боюсь я не справлюсь с этим..

Хочу сделать небольшое пояснение.. Дело в том, что как бы по отдельности все работает, определяется все замечательно, и без разницы какой способ использую, приведенный в начале темы или через GetPixel...

Код:
procedure TForm1.tmr1Timer(Sender: TObject);
var
  r,g,b: Integer;
begin
  PixColorA:= GetPixel(DC,ax,ay);
  r:=GetRValue(PixColorA);
  g:=GetGValue(PixColorA);
  b:=GetBValue(PixColorA);

  if (r>80) and (g>80) and (b>100) then EmulateKey(h1, Ord('D'));
end;

а стоит добавить еще пару GetPixel(DC,X,Y) уже для проверки других координат то программа начинает вести себя странно.. то определяет - то нет.. это главная проблема
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 11:48.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025