Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.11.20;
Скачать: CL | DM;

Вниз

Скриншоты по сетке   Найти похожие ветки 

 
Gamer   (2005-07-29 22:38) [0]

Здравствуйте.
Посоветуйте как можно реализовать передачу скриншотов по сети с максимально возможной скоростью. У меня получается довольно медленно. И еще вопрос как сделать тоже самое (передачу) с положением мыши. То есть это должно работать вместе. У меня есть наброски проги которая это делает. Но как я уже говорил - очень медленно.


 
kami ©   (2005-07-30 00:48) [1]

1. Сжимать чем угодно - лишь бы побыстрее полный экран
2. Разбить экран на участки и сравнивать "старые" и "новые" части изображения, после чего опять-таки сжимать с информацией для восстановления и передавать.

Нашел с курсором (с) не моё:
procedure MakeScreenshot (Bmp: TBitmap; CaptureCursor: Boolean = True);
var
DC: HDC;
ACursor: HICON;
Pt: TPoint;
CurInfo: tagCURSORINFO;
IcoInfo: _ICONINFO;
begin
Bmp.Width := Screen.Width;
Bmp.Height := Screen.Height;
DC := GetDC (GetDesktopWindow);
try
  BitBlt (
    Bmp.Canvas.Handle,
    0,0,Bmp.Width,Bmp.Height,
    DC,
    0,0,
    SRCCOPY);
  // -- êóðñîð
  if CaptureCursor then
  begin
    CurInfo.cbSize := SizeOf(CurInfo);
    GetCursorInfo(CurInfo);
    ACursor := CurInfo.hCursor;
    Pt := CurInfo.ptScreenPos;
    GetIconInfo (ACursor,IcoInfo);
    DrawIcon(
      Bmp.Canvas.Handle,
      Pt.X-IcoInfo.xHotspot,
      Pt.Y-IcoInfo.yHotspot,
      ACursor
    )
  end;
finally
  ReleaseDC (GetDesktopWindow,DC);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Bmp: TBitmap;
rect:TRect;
begin
rect.left:=0;
rect.Top:=0;
rect.Bottom:=600;
rect.Right:=800;
Bmp := TBitmap.Create;
try
  // ñíèìàåì èçîáðàæåíèå ýêðàíà ñ êóðñîðîì
  MakeScreenshot(Bmp,True);
  // è ñîõðàíÿåì åãî â ôàéë äèðåêòîðèè ïðîãðàììû
  img.Canvas.StretchDraw(rect,bmp);
finally
  Bmp.Free;
  img.Refresh;
end;


 
kami ©   (2005-07-30 00:58) [2]

Под "информацией для восстановления" имел ввиду размеры и положение изменившейся части экрана.

Сравнение битмапов на предмет хоть одного отличающегося пикселя:

function TForm1.BitmapsEqual(Bmp1, Bmp2: TBitMap): Boolean;
{ Автор Магнитоныч (c)}
var H, H2, LineLength, y: Integer;
  DS: TDIBSection;
function EqualLine(Line: Integer): Boolean;
begin
  Result := CompareMem(@bmp1.ScanLine[Line]^,
                       @bmp2.ScanLine[Line]^, LineLength);
end;

begin
Result := False;
{сравнение базовых параметров}
 if GetObject(bmp1.Handle, SizeOf(DS), @DS) > 0
  then LineLength := DS.dsBm.bmWidthBytes
  else Exit; //error

H := Bmp1.Height - 1;
if H = 0 then
  Result := EqualLine(0)
else begin
  Result := True;
  H2 := ((H+1) div 2) - 1;
  { Сравниваем строки. Цикл проходит одновременно сверху
     и снизу, сходясь в центре - так больше вероятность скорейшего
     обнаружения отличающихся пикселей }
  for y := 0 to h2 do
    if not(EqualLine(y))or not(EqualLine(h-y)) then
       begin
            Result := False;
            Exit;
       end;
  { Если высота не кратна двум, сравниваем средние строки,
     которые пропустили в цикле. Это сделано, чтобы дважды
     не вызывать CompareMem для одной строки. }
  if (H mod 2) = 0 then
    Result := EqualLine(H2 + 1);
end;
end;


Из оригинального кода убрано сравнение габаритов битмапов и (кажется) глубины цвета.


 
Gamer   (2005-07-30 16:16) [3]

kami ©
Спасибо за столь полные ответы. Задам еще два вопроса.
Если я буду передавать только изменившиеся строки, то это будет намного быстее?
Когда я удалил Exit; после нахождения неравных строчек (чтобы записать все разные строки) то получилось что процедура, которая делает скриншот и сравнивает его с предыдущим, выполняется ~2,2 сек. Можно ли как нибудь ускорить этот процесс?


 
kami ©   (2005-07-30 17:41) [4]

1. Да, если еще сжимать изменившиеся (не обязательно строки, можно брать более крупные еденицы - у меня, например, максимум скорости получался при разбиении экрана на части по 3 строки).Сжимать можно ZLib`ом
2. Ты не правильно понял назначение BitmapsEqual: в нее не нужно "пихать" полностью старый и новый экраны. В своей процедуре создай 2 временных битмапа с нужными габаритами, в один скопируй часть "старого" изображения, в другой - нового. После этого их и сравнивай.

ЗЫ. Если и этого будет мало - есть пример, рабочий 100%,  могу выложить. Но пока постарайся сам :)


 
kami ©   (2005-07-30 17:48) [5]


> это будет намного быстее

Сравни, что лучше: передать 4-8Kb сжатой информации о изменившихся участках, или несколько метров полного скриншота.

Единственная ситуация, когда получается медленнее - при полном изменении экрана (в основном только из-за потерь времени на сжатие)


 
Gamer   (2005-07-30 23:27) [6]

kami ©  
Попробую использовать ZLib.


 
Gamer   (2005-08-03 15:35) [7]

kami ©  
Сделал так как Вы написали. Делю екран на квадраты и сжимаю. Но интерестно посмотреть Ваш пример. Насколько он большой?


 
kami ©   (2005-08-03 21:23) [8]

Хе. Я еще мал и очень глуп, чтоб меня на вы называть, да и уверен, что у тебя получилось ничуть не хуже, а даже гораздо лучше. И тоже очеть интересно посмотреть реализацию :)
{ не претендует на истину в какой-либо инстанции, можно и НУЖНО сделать гораздо быстрее}
unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, JPEG, ShellAPI, ZLibEx;
const
  divider=256;// на сколько частей делим экран
type
 TForm1 = class(TForm)
   Image1: TImage;
   bbInit: TBitBtn;
   bbLetDoIt: TBitBtn;
   Label1: TLabel;
   procedure FormCreate(Sender: TObject);
   procedure bbInitClick(Sender: TObject);
   procedure bbLetDoItClick(Sender: TObject);
   procedure FormKeyDown(Sender: TObject; var Key: Word;
     Shift: TShiftState);
   procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
 public
   mcomp,mdecomp:TMemoryStream;
   img2:TBitMap;//"новый" экран
   PartImg1:array[0..divider-1] of TBitMap;//ВСЕ части старого экрана
   PartImg2:TBitMap;//часть нового экрана
   Rect:TRect;
   DC:HDC;
   Flag:Boolean;// флаг окончания "скриншотирования"
   CompressStream:TMemoryStream;//поток для сжатой измененной части экрана
   DataStream:TMemoryStream;//аккумулятор сжатых измененных частей
   PartStream:TMemoryStream;//несжатый поток измененной части экрана
   img:TBitmap;//часть экрана на приемной стороне
   FillImg:TBitmap;//восстановленное изображение на приемной стороне
   function BitmapsEqual(Bmp1, Bmp2: TBitMap):Boolean;
   function CopyImgToPart(yPos:word;const Source:tbitmap;var PartIMG:TBitMap):Boolean;
   function DoSend(yPos:integer;var PartImg:TBitMap):Boolean;
   procedure DecompressToImage;
 end;

var
 Form1:TForm1;
implementation
{$R *.DFM}
//============================================================
//         передающая часть
//============================================================
//BitMapEquial пропускаю-уже есть в ветке
function  TForm1.CopyImgToPart(yPos:word;const Source:tbitmap;var PartImg:TBitMap):Boolean;
begin
 Result:=BitBLT(PartImg.Canvas.Handle,0,0,REct.Right,PartImg.Height,Source.Canvas.handle,0,yPos*PartImg.Height,SRCCOPY);
end;

function TForm1.DoSend(yPos:integer;var PartImg:TBitMap):Boolean;
var
 s:string;
begin
{Процедура вызывается только,если Img1[i], то есть часть экрана,
сохраненная в памяти отличается от соотв. части на экране}
 CopyImgToPart(0,PartImg,PartImg1[ypos]);//сохранили измененную часть экрана
 {сохраняем в поток. После сохранения всех отличающихся участков будет пересылка
 общего потока DataStream главному компьютеру}
 PartStream.clear;
 PartIMG.SaveToStream(PartStream);
 PartStream.Position:=0;
 CompressStream.Clear;
 ZCompressStream(PartStream,CompressStream,zcDefault);
 CompressStream.Position:=0;
 {теперь в CompressStream-сжатая часть экрана}
 s:=Format("MyTesting Part:=%15d VertLines:=%15d MyPartSize:=%15d",[yPos,PartImg.Height,CompressStream.Size]);
 DataStream.WriteBuffer(s[1],Length(s));{положили в общий поток измененных частей заголовок}
 DataStream.CopyFrom(CompressStream,CompressStream.Size);{и саму сжатую часть экрана}
end;

procedure TForm1.bbLetDoItClick(Sender:TObject);
var
i:integer;
j,k,n:integer;
max:integer;
begin
 n:=0;
 max:=0;
 While not Flag do
   begin
     k:=GetTickCount;
     DataStream.Clear;
     BitBLT(img2.Canvas.Handle,0,0,Rect.Right,Rect.Bottom,dc,0,0,SRCCopy);
     for i:=0 to divider-1 do
       begin
         CopyImgToPart(i,img2,PartImg2);
         if not BitMapsEqual(PartImg2,PartImg1[i]) then
           DoSend(i,PartImg2);
       end;
     {Здесь в DataStream содержатся все измененные части, то есть
     измененные PartImg1 уже сжатые и с информацией  для восстановления}
     j:=GetTickCount;
     if DataStream.Size>max then
       max:=DataStream.Size;
     Label1.Caption:="Кадр "+IntToStr(n)+" Протикало тиков  "+IntToStr(j-k)+
       " Размер данных "+IntToStr(DataStream.Size)+" max="+IntToStr(max);
     inc(n);
     DecompressToImage;//вместо этой процедуры в реальном приложении
     //нужно отправить кому-нибудь DataStream, а на приемной стороне
     //уже вызывать DecompressToImage после приема потока
     ProcessMessages;
   end;
end;

//============================================================
//      приемная часть
//============================================================
procedure TForm1.DecompressToImage;
var
 s:string;
 part,vertlines,partsize:integer;
begin
 {итак,в DataStream содержатся заголовки сжатых частей и сами части}
 {будем считать,что разрешение экрана подчиненного компа нам известно,
 так как первый скриншот должен пересылаться полностью.(здесь не отражено)}
 DataStream.Position:=0;
 SetLength(s,87);
 while DataStream.Position<DataStream.Size do
   begin
     mcomp.Clear;
     mdecomp.Clear;
     DataStream.Read(s[1],86);
     part:=StrToInt(Copy(s,17,15));
     VertLines:=StrToInt(Copy(s,44,15));
     PartSize:=StrToInt(Copy(s,72,15));
     img.Width:=1024;
     img.Height:=VertLines;
     mcomp.CopyFrom(DataStream,PartSize);
     mcomp.Position:=0;
     ZDecompressStream(mcomp,mdecomp);
     mdecomp.Position:=0;
     img.LoadFromStream(mdecomp);
     bitblt(FillImg.Canvas.Handle,0,part*VertLines,1024,VertLines,img.Canvas.Handle,0,0,SRCCopy);
   end;
 {раскомментировать следующую строку для отображения результатов}
 //Image1.Canvas.StretchDraw(Image1.ClientRect,FillImg);
end;

procedure TForm1.FormKeyDown(Sender:TObject;var Key:Word;Shift:TShiftState);
begin
 if Key=vk_Escape then
   Flag:=True;
end;

procedure TForm1.FormCloseQuery(Sender:TObject;var CanClose:Boolean);
begin
 Flag:=True;
 CanClose:=True;
end;

procedure TForm1.FormCreate(Sender:TObject);
var
 i:integer;
begin
 Windows.GetClientRect(GetDeskTopWindow,Rect);
 img2:=TBitMap.Create;
 PartImg2:=TBitMap.Create;
 FillImg:=TBitMap.Create;// а если создавать и удалять его локально,
 //в DecompressToImage, то будут видны только измененные части изображения.
 FillImg.Width:=Rect.Right
 FillImg.Height:=Rect.Bottom;

 mcomp:=TMemoryStream.Create;
 mdecomp:=TMemoryStream.Create;

 img:=TBitMap.Create;
 for i:=0 to divider-1 do// глупости, конечно, можно оперировать
                         // не частями старого экрана, а им всем
   begin
     PartImg1[i]:=TBitMap.Create;
     PartImg1[i].Width:=Rect.Right;
     PartImg1[i].Height:=Rect.Bottom div divider;{это не правильно-экран может не поделиться на 256 частей, и тогда нижний "кусок" не будет просчитываться}
   end;
 CompressStream:=TMemoryStream.Create;
 PartStream:=TMemoryStream.Create;
 DataStream:=TMemoryStream.create;

 img2.Width:=Rect.Right;
 PartImg2.Width:=Rect.Right;

 img2.Height:=Rect.Bottom;
 PartImg2.Height:=Rect.Bottom div divider;
 dc:=GetDC(GetDesktopWindow);
end;
// FormClose пропускаю-ничего интересного :)
procedure TForm1.bbInitClick(Sender: TObject);
begin
 //по идее-нужно перенести экран на PartImg1, но лень писать - сами напишете
 Flag:=False;
end;
end.


 
Gamer   (2005-08-04 22:49) [9]

kami ©  
Свой код выставлю через пару дней.


 
Gamer   (2005-08-08 01:04) [10]

kami ©  

Обещанный код. Ограничился только функцией деления на квадраты. Так как сжимаю таким же способом.


var
b1,b2:tbitmap;
t,x,w,y,h:integer;
stepx,stepy{ширина,высота квадрата},sw,sh{рaзмtр экрана}:integer;
yw,xw:boolean;
begin
t:=gettickcount;
sw:=screen.Width;
sh:=screen.Height;
stepx:=sw;
stepy:=5;
b1:=tbitmap.Create;
b2:=tbitmap.Create;
x:=-stepx;
y:=-stepy;
if stepy<sh then begin
 yw:=true;
 while yw=true do begin
   xw:=true;
   h:=stepy;
   y:=y+stepy;
   if y+stepy>sh then begin
     h:=y+stepy-sh;
     yw:=false;
   end;
   if y+stepy=sh then yw:=false;
   while xw=true do begin
     w:=stepx;
     x:=x+stepx;
     if x+stepx>sw then begin
       w:=x+stepx-sw;
       xw:=false;
     end;
     if x+stepx=sw then begin
       xw:=false;
     end;
     b1.width:=w;
     b2.width:=w;
     b1.height:=h;
     b2.height:=h;
     bitblt(b1.canvas.Handle,0,0,w,h,i1.picture.Bitmap.canvas.Handle,x,y,SRCCOPY);
     bitblt(b2.canvas.Handle,0,0,w,h,i2.picture.Bitmap.canvas.Handle,x,y,SRCCOPY);
     if BitmapsEqual(B1, B2)=false then filecreate("c:\temp\"+inttostr(x)+"."+inttostr(y));;
 
     b1.SaveToFile("c:\temp\1 "+inttostr(x)+"."+inttostr(y)+" "+inttostr(w)+"."+inttostr(h)+".bmp");
     b2.SaveToFile("c:\temp\2 "+inttostr(x)+"."+inttostr(y)+" "+inttostr(w)+"."+inttostr(h)+".bmp");
     if xw=false then x:=-stepx;
   end;
 end;


Компоненты:
i1: TImage;  //Старый скриншот
i2: TImage;  //Новый  скриншот
Хотя разницы между ними нет.
Проверяет квадратами stepx*stepy. Если что не ясно написано - спрашивай.

И еще вопрос: Ты используешь TThreadBlocking сервер?


 
Slym ©   (2005-08-08 09:04) [11]

А в файлы то зачем? Это же тормоз! Мемори стреам пользуй


 
kami ©   (2005-08-08 20:09) [12]


> Ты используешь TThreadBlocking сервер?

нет, ctNonBlocking.

2. Неужто, действительно сохраняешь в файлы?
3. Поясни, пожалуйста, смысл этого:
> if BitmapsEqual(B1, B2)=false then filecreate("c:\temp\"+inttostr(x)+"."+inttostr(y));;


 
Наиль ©   (2005-08-08 23:18) [13]

Для ленивых есть способ проще.
Один битмап накладывается на другой с CopyMode:=SrcInvert.
Совпадающие участки дадут чёрный цвет.
Далее преобразовать в JPG как описано в
http://www.delphimaster.ru/articles/catcher.html

Преимущество одно: меньше кода - меньше ошибок.


 
Наиль ©   (2005-08-08 23:26) [14]

Беру свои слова обратно. CopyMode=cmSrcInvert проводит операцию XOR. Восстановление изображения проводится также методом накладывания. Но JPEG искажает "чёрный рисунок" поэтому восстановление не всегда возможно.


 
LoGeen   (2005-08-08 23:28) [15]

Я и работаю с MemoryStream. Файлы создавал для отладки.
Т.е. filecreate("c:\temp\"+inttostr(x)+"."+inttostr(y)); чтобы узнать количество разных отрезков.
А
b1.SaveToFile("c:\temp\1 "+inttostr(x)+"."+inttostr(y)+" "+inttostr(w)+"."+inttostr(h)+".bmp");
b2.SaveToFile("c:\temp\2 "+inttostr(x)+"."+inttostr(y)+" "+inttostr(w)+"."+inttostr(h)+".bmp");

для того чтобы увидеть сами отрезки.


 
Gamer   (2005-08-08 23:37) [16]

Извините, верхнее [15] сообщение было от меня.



Страницы: 1 вся ветка

Текущий архив: 2005.11.20;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.042 c
1-1130312278
sergg
2005-10-26 11:37
2005.11.20
Измение размера двумерного VarArray


2-1131015442
|imp|
2005-11-03 13:57
2005.11.20
Как получить список процессов?


4-1126964773
NikNet
2005-09-17 17:46
2005.11.20
У меня есть HDC как мне нарисовать иконку на ней?


14-1130375544
TakeIt
2005-10-27 05:12
2005.11.20
Что такое новая система шаблонов кода в Delphi 2006 ?


4-1126864296
Still Swamp
2005-09-16 13:51
2005.11.20
Необходимо из сервиса запустить интерактивный EXE.